Looly 5 年之前
父节点
当前提交
e9400766a9

+ 2 - 0
CHANGELOG.md

@@ -19,6 +19,8 @@
 * 【core   】     增加FontUtil,可定义pressText是否从中间(issue#I1HSWU@Gitee)
 * 【http   】     SoapClient支持自定义请求头(issue#I1I0AO@Gitee)
 * 【script 】     ScriptUtil增加evalInvocable和invoke方法(issue#I1HHCP@Gitee)
+* 【core   】     ImgUtil增加去除背景色的方法(pr#124@Gitee)
+* 【system 】     OshiUtil增加获取CPU使用率的方法(pr#124@Gitee)
 
 ### Bug修复
 * 【core   】     修复SimpleCache死锁问题(issue#I1HOKB@Gitee)

+ 354 - 0
hutool-core/src/main/java/cn/hutool/core/img/BackgroundRemoval.java

@@ -0,0 +1,354 @@
+package cn.hutool.core.img;
+
+import cn.hutool.core.io.FileTypeUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+
+import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>图片背景识别处理、背景替换、背景设置为矢量图</p>
+ * <p>根据一定规则算出图片背景色的RGB值,进行替换</p>
+ * <p>2020-05-21 16:36</p>
+ *
+ * @author Dai Yuanchuan
+ **/
+public class BackgroundRemoval {
+
+	/**
+	 * 目前暂时支持的图片类型数组
+	 * 其他格式的不保证结果
+	 */
+	public static String[] IMAGES_TYPE = {"jpg", "png"};
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param inputPath  要处理图片的路径
+	 * @param outputPath 输出图片的路径
+	 * @param tolerance  容差值[根据图片的主题色,加入容差值,值的范围在0~255之间]
+	 * @return 返回处理结果 true:图片处理完成 false:图片处理失败
+	 */
+	public static boolean backgroundRemoval(String inputPath, String outputPath, int tolerance) {
+		return backgroundRemoval(new File(inputPath), new File(outputPath), tolerance);
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param input     需要进行操作的图片
+	 * @param output    最后输出的文件
+	 * @param tolerance 容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理结果 true:图片处理完成 false:图片处理失败
+	 */
+	public static boolean backgroundRemoval(File input, File output, int tolerance) {
+		return backgroundRemoval(input, output, null, tolerance);
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param input     需要进行操作的图片
+	 * @param output    最后输出的文件
+	 * @param override  指定替换成的背景颜色 为null时背景为透明
+	 * @param tolerance 容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理结果 true:图片处理完成 false:图片处理失败
+	 */
+	public static boolean backgroundRemoval(File input, File output, Color override, int tolerance) {
+		if (fileTypeValidation(input, IMAGES_TYPE)) {
+			return false;
+		}
+		try {
+			// 获取图片左上、中上、右上、右中、右下、下中、左下、左中、8个像素点rgb的16进制值
+			BufferedImage bufferedImage = ImageIO.read(input);
+			// 图片输出的格式为 png
+			return ImageIO.write(backgroundRemoval(bufferedImage, override, tolerance), "png", output);
+		} catch (IOException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param bufferedImage 需要进行处理的图片流
+	 * @param override      指定替换成的背景颜色 为null时背景为透明
+	 * @param tolerance     容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理好的图片流
+	 */
+	public static BufferedImage backgroundRemoval(BufferedImage bufferedImage, Color override, int tolerance) {
+		// 容差值 最大255 最小0
+		tolerance = Math.min(255, Math.max(tolerance, 0));
+		// 绘制icon
+		ImageIcon imageIcon = new ImageIcon(bufferedImage);
+		BufferedImage image = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(),
+				BufferedImage.TYPE_4BYTE_ABGR);
+		// 绘图工具
+		Graphics graphics = image.getGraphics();
+		graphics.drawImage(imageIcon.getImage(), 0, 0, imageIcon.getImageObserver());
+		// 需要删除的RGB元素
+		String[] removeRgb = getRemoveRgb(bufferedImage);
+		// 获取图片的大概主色调
+		String mainColor = getMainColor(bufferedImage);
+		int alpha = 0;
+		for (int y = image.getMinY(); y < image.getHeight(); y++) {
+			for (int x = image.getMinX(); x < image.getWidth(); x++) {
+				// 获取像素的16进制
+				int rgb = image.getRGB(x, y);
+				String hex = ImgUtil.toHex((rgb & 0xff0000) >> 16, (rgb & 0xff00) >> 8, (rgb & 0xff));
+				boolean isTrue = ArrayUtil.contains(removeRgb, hex) ||
+						areColorsWithinTolerance(hexToRgb(mainColor), new Color(Integer.parseInt(hex.substring(1), 16)), tolerance);
+				if (isTrue) {
+					rgb = override == null ? ((alpha + 1) << 24) | (rgb & 0x00ffffff) : override.getRGB();
+				}
+				image.setRGB(x, y, rgb);
+			}
+		}
+		graphics.drawImage(image, 0, 0, imageIcon.getImageObserver());
+		return image;
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param outputStream 需要进行处理的图片字节数组流
+	 * @param override     指定替换成的背景颜色 为null时背景为透明
+	 * @param tolerance    容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理好的图片流
+	 */
+	public static BufferedImage backgroundRemoval(ByteArrayOutputStream outputStream, Color override, int tolerance) {
+		try {
+			return backgroundRemoval(ImageIO.read(new ByteArrayInputStream(outputStream.toByteArray())), override, tolerance);
+		} catch (IOException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 获取要删除的 RGB 元素
+	 * 分别获取图片左上、中上、右上、右中、右下、下中、左下、左中、8个像素点rgb的16进制值
+	 *
+	 * @param image 图片流
+	 * @return String数组 包含 各个位置的rgb数值
+	 */
+	private static String[] getRemoveRgb(BufferedImage image) {
+		// 获取图片流的宽和高
+		int width = image.getWidth() - 1;
+		int height = image.getHeight() - 1;
+		// 左上
+		int leftUpPixel = image.getRGB(1, 1);
+		String leftUp = ImgUtil.toHex((leftUpPixel & 0xff0000) >> 16, (leftUpPixel & 0xff00) >> 8, (leftUpPixel & 0xff));
+		// 上中
+		int upMiddlePixel = image.getRGB(width / 2, 1);
+		String upMiddle = ImgUtil.toHex((upMiddlePixel & 0xff0000) >> 16, (upMiddlePixel & 0xff00) >> 8, (upMiddlePixel & 0xff));
+		// 右上
+		int rightUpPixel = image.getRGB(width, 1);
+		String rightUp = ImgUtil.toHex((rightUpPixel & 0xff0000) >> 16, (rightUpPixel & 0xff00) >> 8, (rightUpPixel & 0xff));
+		// 右中
+		int rightMiddlePixel = image.getRGB(width, height / 2);
+		String rightMiddle = ImgUtil.toHex((rightMiddlePixel & 0xff0000) >> 16, (rightMiddlePixel & 0xff00) >> 8, (rightMiddlePixel & 0xff));
+		// 右下
+		int lowerRightPixel = image.getRGB(width, height);
+		String lowerRight = ImgUtil.toHex((lowerRightPixel & 0xff0000) >> 16, (lowerRightPixel & 0xff00) >> 8, (lowerRightPixel & 0xff));
+		// 下中
+		int lowerMiddlePixel = image.getRGB(width / 2, height);
+		String lowerMiddle = ImgUtil.toHex((lowerMiddlePixel & 0xff0000) >> 16, (lowerMiddlePixel & 0xff00) >> 8, (lowerMiddlePixel & 0xff));
+		// 左下
+		int leftLowerPixel = image.getRGB(1, height);
+		String leftLower = ImgUtil.toHex((leftLowerPixel & 0xff0000) >> 16, (leftLowerPixel & 0xff00) >> 8, (leftLowerPixel & 0xff));
+		// 左中
+		int leftMiddlePixel = image.getRGB(1, height / 2);
+		String leftMiddle = ImgUtil.toHex((leftMiddlePixel & 0xff0000) >> 16, (leftMiddlePixel & 0xff00) >> 8, (leftMiddlePixel & 0xff));
+		// 需要删除的RGB元素
+		return new String[]{leftUp, upMiddle, rightUp, rightMiddle, lowerRight, lowerMiddle, leftLower, leftMiddle};
+	}
+
+	/**
+	 * 十六进制颜色码转RGB颜色值
+	 *
+	 * @param hex 十六进制颜色码
+	 * @return 返回 RGB颜色值
+	 */
+	public static Color hexToRgb(String hex) {
+		return new Color(Integer.parseInt(hex.substring(1), 16));
+	}
+
+
+	/**
+	 * 判断颜色是否在容差范围内
+	 * 对比两个颜色的相似度,判断这个相似度是否小于 tolerance 容差值
+	 *
+	 * @param color1    颜色1
+	 * @param color2    颜色2
+	 * @param tolerance 容差值
+	 * @return 返回true:两个颜色在容差值之内 false: 不在
+	 */
+	public static boolean areColorsWithinTolerance(Color color1, Color color2, int tolerance) {
+		return areColorsWithinTolerance(color1, color2, new Color(tolerance, tolerance, tolerance));
+	}
+
+	/**
+	 * 判断颜色是否在容差范围内
+	 * 对比两个颜色的相似度,判断这个相似度是否小于 tolerance 容差值
+	 *
+	 * @param color1    颜色1
+	 * @param color2    颜色2
+	 * @param tolerance 容差色值
+	 * @return 返回true:两个颜色在容差值之内 false: 不在
+	 */
+	public static boolean areColorsWithinTolerance(Color color1, Color color2, Color tolerance) {
+		return (color1.getRed() - color2.getRed() < tolerance.getRed() && color1
+				.getRed() - color2.getRed() > -tolerance.getRed())
+				&& (color1.getBlue() - color2.getBlue() < tolerance
+				.getBlue() && color1.getBlue() - color2.getBlue() > -tolerance
+				.getBlue())
+				&& (color1.getGreen() - color2.getGreen() < tolerance
+				.getGreen() && color1.getGreen()
+				- color2.getGreen() > -tolerance.getGreen());
+	}
+
+	/**
+	 * 获取图片大概的主题色
+	 * 循环所有的像素点,取出出现次数最多的一个像素点的RGB值
+	 *
+	 * @param input 图片文件路径
+	 * @return 返回一个图片的大概的色值 一个16进制的颜色码
+	 */
+	public static String getMainColor(String input) {
+		return getMainColor(new File(input));
+	}
+
+	/**
+	 * 获取图片大概的主题色
+	 * 循环所有的像素点,取出出现次数最多的一个像素点的RGB值
+	 *
+	 * @param input 图片文件
+	 * @return 返回一个图片的大概的色值 一个16进制的颜色码
+	 */
+	public static String getMainColor(File input) {
+		try {
+			return getMainColor(ImageIO.read(input));
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return "";
+	}
+
+	/**
+	 * 获取图片大概的主题色
+	 * 循环所有的像素点,取出出现次数最多的一个像素点的RGB值
+	 *
+	 * @param bufferedImage 图片流
+	 * @return 返回一个图片的大概的色值 一个16进制的颜色码
+	 */
+	public static String getMainColor(BufferedImage bufferedImage) {
+		if (bufferedImage == null) {
+			throw new IllegalArgumentException("图片流是空的");
+		}
+
+		// 存储图片的所有RGB元素
+		List<String> list = new ArrayList<>();
+		for (int y = bufferedImage.getMinY(); y < bufferedImage.getHeight(); y++) {
+			for (int x = bufferedImage.getMinX(); x < bufferedImage.getWidth(); x++) {
+				int pixel = bufferedImage.getRGB(x, y);
+				list.add(((pixel & 0xff0000) >> 16) + "-" + ((pixel & 0xff00) >> 8) + "-" + (pixel & 0xff));
+			}
+		}
+
+		Map<String, Integer> map = new HashMap<>(list.size());
+		for (String string : list) {
+			Integer integer = map.get(string);
+			if (integer == null) {
+				integer = 1;
+			} else {
+				integer++;
+			}
+			map.put(string, integer);
+		}
+		String max = "";
+		long num = 0;
+		for (Map.Entry<String, Integer> entry : map.entrySet()) {
+			String key = entry.getKey();
+			Integer temp = entry.getValue();
+			if (StrUtil.isBlank(max) || temp > num) {
+				max = key;
+				num = temp;
+			}
+		}
+		String[] strings = max.split("-");
+		// rgb 的数量只有3个
+		int rgbLength = 3;
+		if (strings.length == rgbLength) {
+			return ImgUtil.toHex(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]),
+					Integer.parseInt(strings[2]));
+		}
+		return "";
+	}
+
+	// -------------------------------------------------------------------------- private
+
+	/**
+	 * 文件类型验证
+	 * 根据给定文件类型数据,验证给定文件类型.
+	 *
+	 * @param input      需要进行验证的文件
+	 * @param imagesType 文件包含的类型数组
+	 * @return 返回布尔值 false:给定文件的文件类型在文件数组中  true:给定文件的文件类型 不在给定数组中。
+	 */
+	private static boolean fileTypeValidation(File input, String[] imagesType) {
+		if (!input.exists()) {
+			throw new IllegalArgumentException("给定文件为空");
+		}
+		// 获取图片类型
+		String type = FileTypeUtil.getType(input);
+		// 类型对比
+		if (!ArrayUtil.contains(imagesType, type)) {
+			throw new IllegalArgumentException(StrUtil.format("文件类型{}不支持", type));
+		}
+		return false;
+	}
+}

+ 110 - 7
hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java

@@ -1765,13 +1765,23 @@ public class ImgUtil {
 	 * @since 4.1.14
 	 */
 	public static String toHex(Color color) {
-		String R = Integer.toHexString(color.getRed());
-		R = R.length() < 2 ? ('0' + R) : R;
-		String G = Integer.toHexString(color.getGreen());
-		G = G.length() < 2 ? ('0' + G) : G;
-		String B = Integer.toHexString(color.getBlue());
-		B = B.length() < 2 ? ('0' + B) : B;
-		return '#' + R + G + B;
+		return toHex(color.getRed(), color.getGreen(), color.getBlue());
+	}
+
+	/**
+	 * RGB颜色值转换成十六进制颜色码
+	 *
+	 * @param r 红(R)
+	 * @param g 绿(G)
+	 * @param b 蓝(B)
+	 * @return 返回字符串形式的 十六进制颜色码 如
+	 */
+	public static String toHex(int r, int g, int b) {
+		// rgb 小于 255
+		if(r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255){
+			throw new IllegalArgumentException("RGB must be 0~255!");
+		}
+		return String.format("#%02X%02X%02X", r, g, b);
 	}
 
 	/**
@@ -1910,4 +1920,97 @@ public class ImgUtil {
 				rectangle.y + (Math.abs(backgroundHeight - rectangle.height) / 2)//
 		);
 	}
+
+	// ------------------------------------------------------------------------------------------------------ 背景图换算
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param inputPath  要处理图片的路径
+	 * @param outputPath 输出图片的路径
+	 * @param tolerance  容差值[根据图片的主题色,加入容差值,值的范围在0~255之间]
+	 * @return 返回处理结果 true:图片处理完成 false:图片处理失败
+	 */
+	public static boolean backgroundRemoval(String inputPath, String outputPath, int tolerance) {
+		return BackgroundRemoval.backgroundRemoval(inputPath, outputPath, tolerance);
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param input     需要进行操作的图片
+	 * @param output    最后输出的文件
+	 * @param tolerance 容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理结果 true:图片处理完成 false:图片处理失败
+	 */
+	public static boolean backgroundRemoval(File input, File output, int tolerance) {
+		return BackgroundRemoval.backgroundRemoval(input, output, tolerance);
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param input     需要进行操作的图片
+	 * @param output    最后输出的文件
+	 * @param override  指定替换成的背景颜色 为null时背景为透明
+	 * @param tolerance 容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理结果 true:图片处理完成 false:图片处理失败
+	 */
+	public static boolean backgroundRemoval(File input, File output, Color override, int tolerance) {
+		return BackgroundRemoval.backgroundRemoval(input, output, override, tolerance);
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param bufferedImage 需要进行处理的图片流
+	 * @param override      指定替换成的背景颜色 为null时背景为透明
+	 * @param tolerance     容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理好的图片流
+	 */
+	public static BufferedImage backgroundRemoval(BufferedImage bufferedImage, Color override, int tolerance) {
+		return BackgroundRemoval.backgroundRemoval(bufferedImage, override, tolerance);
+	}
+
+	/**
+	 * 背景移除
+	 * 图片去底工具
+	 * 将 "纯色背景的图片" 还原成 "透明背景的图片"
+	 * 将纯色背景的图片转成矢量图
+	 * 取图片边缘的像素点和获取到的图片主题色作为要替换的背景色
+	 * 再加入一定的容差值,然后将所有像素点与该颜色进行比较
+	 * 发现相同则将颜色不透明度设置为0,使颜色完全透明.
+	 *
+	 * @param outputStream 需要进行处理的图片字节数组流
+	 * @param override     指定替换成的背景颜色 为null时背景为透明
+	 * @param tolerance    容差值[根据图片的主题色,加入容差值,值的取值范围在0~255之间]
+	 * @return 返回处理好的图片流
+	 */
+	public static BufferedImage backgroundRemoval(ByteArrayOutputStream outputStream, Color override, int tolerance) {
+		return BackgroundRemoval.backgroundRemoval(outputStream, override, tolerance);
+	}
 }

+ 23 - 0
hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java

@@ -1,6 +1,7 @@
 package cn.hutool.core.img;
 
 import cn.hutool.core.io.FileUtil;
+import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -10,6 +11,7 @@ import java.awt.Font;
 import java.awt.Image;
 import java.awt.Rectangle;
 import java.awt.image.BufferedImage;
+import java.io.File;
 import java.io.IOException;
 
 public class ImgUtilTest {
@@ -109,4 +111,25 @@ public class ImgUtilTest {
 		BufferedImage image = ImgUtil.copyImage(ImgUtil.read("f:/pic/test.png"), BufferedImage.TYPE_INT_RGB);
 		ImgUtil.write(image, FileUtil.file("f:/pic/test_dest.jpg"));
 	}
+
+	@Test
+	public void toHexTest(){
+		final String s = ImgUtil.toHex(Color.RED);
+		Assert.assertEquals("#FF0000", s);
+	}
+
+	@Test
+	@Ignore
+	public void backgroundRemovalTest() {
+		// 图片 背景 换成 透明的
+		ImgUtil.backgroundRemoval(
+				"d:/test/617180969474805871.jpg",
+				"d:/test/2.jpg", 10);
+
+		// 图片 背景 换成 红色的
+		ImgUtil.backgroundRemoval(new File(
+				"d:/test/617180969474805871.jpg"),
+				new File("d:/test/3.jpg"),
+				new Color(200, 0, 0), 10);
+	}
 }

+ 1 - 1
hutool-system/pom.xml

@@ -26,7 +26,7 @@
 		<dependency>
 			<groupId>com.github.oshi</groupId>
 			<artifactId>oshi-core</artifactId>
-			<version>4.5.2</version>
+			<version>5.1.0</version>
 			<scope>provided</scope>
 		</dependency>
 	</dependencies>

+ 131 - 0
hutool-system/src/main/java/cn/hutool/system/oshi/CpuInfo.java

@@ -0,0 +1,131 @@
+package cn.hutool.system.oshi;
+
+import java.text.DecimalFormat;
+
+/**
+ * <p></p>
+ * <p>2020-05-21 14:19</p>
+ *
+ * @author Dai Yuanchuan
+ **/
+public class CpuInfo {
+
+	/**
+	 * cpu核心数
+	 */
+	private Integer cpuNum;
+
+	/**
+	 * CPU总的使用率
+	 */
+	private double toTal;
+
+	/**
+	 * CPU系统使用率
+	 */
+	private double sys;
+
+	/**
+	 * CPU用户使用率
+	 */
+	private double used;
+
+	/**
+	 * CPU当前等待率
+	 */
+	private double wait;
+
+	/**
+	 * CPU当前空闲率
+	 */
+	private double free;
+
+	/**
+	 * CPU型号信息
+	 */
+	private String cpuModel;
+
+	public CpuInfo() {
+	}
+
+	public CpuInfo(Integer cpuNum, double toTal, double sys, double used, double wait, double free, String cpuModel) {
+		this.cpuNum = cpuNum;
+		this.toTal = toTal;
+		this.sys = sys;
+		this.used = used;
+		this.wait = wait;
+		this.free = free;
+		this.cpuModel = cpuModel;
+	}
+
+	public Integer getCpuNum() {
+		return cpuNum;
+	}
+
+	public void setCpuNum(Integer cpuNum) {
+		this.cpuNum = cpuNum;
+	}
+
+	public double getToTal() {
+		return toTal;
+	}
+
+	public void setToTal(double toTal) {
+		this.toTal = toTal;
+	}
+
+	public double getSys() {
+		return sys;
+	}
+
+	public void setSys(double sys) {
+		this.sys = sys;
+	}
+
+	public double getUsed() {
+		return used;
+	}
+
+	public void setUsed(double used) {
+		this.used = used;
+	}
+
+	public double getWait() {
+		return wait;
+	}
+
+	public void setWait(double wait) {
+		this.wait = wait;
+	}
+
+	public double getFree() {
+		return free;
+	}
+
+	public void setFree(double free) {
+		this.free = free;
+	}
+
+	public String getCpuModel() {
+		return cpuModel;
+	}
+
+	public void setCpuModel(String cpuModel) {
+		this.cpuModel = cpuModel;
+	}
+
+	@Override
+	public String toString() {
+		DecimalFormat format = new DecimalFormat("#.00");
+		return "CpuInfo{" +
+				"cpu核心数=" + cpuNum +
+				", CPU总的使用率=" + toTal +
+				", CPU系统使用率=" + sys +
+				", CPU用户使用率=" + used +
+				", CPU当前等待率=" + wait +
+				", CPU当前空闲率=" + free +
+				", CPU利用率=" + Double.parseDouble(format.format((100 - getFree()))) +
+				", CPU型号信息='" + cpuModel + '\'' +
+				'}';
+	}
+}

+ 65 - 3
hutool-system/src/main/java/cn/hutool/system/oshi/OshiUtil.java

@@ -9,6 +9,10 @@ import oshi.hardware.HardwareAbstractionLayer;
 import oshi.hardware.NetworkIF;
 import oshi.hardware.Sensors;
 import oshi.software.os.OperatingSystem;
+import oshi.util.Util;
+
+import java.text.DecimalFormat;
+import java.util.List;
 
 /**
  * Oshi库封装的工具类,通过此工具类,可获取系统、硬件相关信息
@@ -95,19 +99,77 @@ public class OshiUtil {
 	 * 获取磁盘相关信息,可能有多个磁盘(包括可移动磁盘等)
 	 * 
 	 * @return 磁盘相关信息
+	 * @since 5.3.6
 	 */
-	public static HWDiskStore[] getDiskStores() {
+	public static List<HWDiskStore> getDiskStores() {
 		return hardware.getDiskStores();
 	}
 
 	/**
 	 * 获取网络相关信息,可能多块网卡
 	 * @return 网络相关信息
-	 * @since 5.3.5
+	 * @since 5.3.6
 	 */
-	public static NetworkIF[] getNetworkIFs(){
+	public static List<NetworkIF> getNetworkIFs(){
 		return hardware.getNetworkIFs();
 	}
 
+	// ------------------------------------------------------------------ cpu
+
+	/**
+	 * 获取系统CPU 系统使用率、用户使用率、利用率等等 相关信息
+	 *
+	 * @return 系统 CPU 使用率 等信息
+	 */
+	public static CpuInfo getCpuInfo() {
+		return getCpuInfo(1000);
+	}
+
+	/**
+	 * 获取系统CPU 系统使用率、用户使用率、利用率等等 相关信息
+	 *
+	 * @param waitingTime 设置等待时间
+	 * @return 系统 CPU 使用率 等信息
+	 */
+	public static CpuInfo getCpuInfo(long waitingTime) {
+		return getCpuInfo(OshiUtil.getProcessor(), waitingTime);
+	}
 
+	/**
+	 * 获取系统CPU 系统使用率、用户使用率、利用率等等 相关信息
+	 *
+	 * @param processor {@link CentralProcessor}
+	 * @param waitingTime 设置等待时间
+	 * @return 系统 CPU 使用率 等信息
+	 */
+	private static CpuInfo getCpuInfo(CentralProcessor processor, long waitingTime) {
+		CpuInfo cpuInfo = new CpuInfo();
+		// CPU信息
+		long[] prevTicks = processor.getSystemCpuLoadTicks();
+		// 这里必须要设置延迟
+		Util.sleep(waitingTime);
+		long[] ticks = processor.getSystemCpuLoadTicks();
+		long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
+		long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
+		long softIrq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
+		long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
+		long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
+		long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
+		long ioWait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
+		long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
+		long totalCpu = Math.max(user + nice + cSys + idle + ioWait + irq + softIrq + steal, 0);
+		final DecimalFormat format = new DecimalFormat("#.00");
+		cpuInfo.setCpuNum(processor.getLogicalProcessorCount());
+		cpuInfo.setToTal(totalCpu);
+		cpuInfo.setSys(Double.parseDouble(format.format(cSys <= 0 ? 0 : (100d * cSys / totalCpu))));
+		cpuInfo.setUsed(Double.parseDouble(format.format(user <= 0 ? 0 : (100d * user / totalCpu))));
+		if (totalCpu == 0) {
+			cpuInfo.setWait(0);
+		} else {
+			cpuInfo.setWait(Double.parseDouble(format.format(100d * ioWait / totalCpu)));
+		}
+		cpuInfo.setFree(Double.parseDouble(format.format(idle <= 0 ? 0 : (100d * idle / totalCpu))));
+		cpuInfo.setCpuModel(processor.toString());
+		return cpuInfo;
+	}
 }

+ 8 - 2
hutool-system/src/test/java/cn/hutool/system/OshiTest.java

@@ -1,10 +1,10 @@
 package cn.hutool.system;
 
+import cn.hutool.system.oshi.CpuInfo;
+import cn.hutool.system.oshi.OshiUtil;
 import org.junit.Assert;
 import org.junit.Test;
 
-import cn.hutool.system.oshi.OshiUtil;
-
 public class OshiTest {
 	
 	@Test
@@ -12,4 +12,10 @@ public class OshiTest {
 		long total = OshiUtil.getMemory().getTotal();
 		Assert.assertTrue(total > 0);
 	}
+
+	@Test
+	public void getCupInfo() {
+		CpuInfo cpuInfo = OshiUtil.getCpuInfo();
+		Assert.assertNotNull(cpuInfo);
+	}
 }