diff --git a/CHANGELOG.md b/CHANGELOG.md index 81759acf7..2fe9acd26 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/hutool-core/src/main/java/cn/hutool/core/img/BackgroundRemoval.java b/hutool-core/src/main/java/cn/hutool/core/img/BackgroundRemoval.java new file mode 100644 index 000000000..0988d32f0 --- /dev/null +++ b/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; + +/** + *

图片背景识别处理、背景替换、背景设置为矢量图

+ *

根据一定规则算出图片背景色的RGB值,进行替换

+ *

2020-05-21 16:36

+ * + * @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 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 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 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; + } +} \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java index ca5c47fbd..8caa47738 100644 --- a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java +++ b/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); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java index f1702ac31..401010512 100644 --- a/hutool-core/src/test/java/cn/hutool/core/img/ImgUtilTest.java +++ b/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); + } } diff --git a/hutool-system/pom.xml b/hutool-system/pom.xml index 003558f0a..ef4bb4a1c 100644 --- a/hutool-system/pom.xml +++ b/hutool-system/pom.xml @@ -26,7 +26,7 @@ com.github.oshi oshi-core - 4.5.2 + 5.1.0 provided diff --git a/hutool-system/src/main/java/cn/hutool/system/oshi/CpuInfo.java b/hutool-system/src/main/java/cn/hutool/system/oshi/CpuInfo.java new file mode 100644 index 000000000..f2141ef4c --- /dev/null +++ b/hutool-system/src/main/java/cn/hutool/system/oshi/CpuInfo.java @@ -0,0 +1,131 @@ +package cn.hutool.system.oshi; + +import java.text.DecimalFormat; + +/** + *

+ *

2020-05-21 14:19

+ * + * @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 + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/hutool-system/src/main/java/cn/hutool/system/oshi/OshiUtil.java b/hutool-system/src/main/java/cn/hutool/system/oshi/OshiUtil.java index 4aeff7ec3..5497b328e 100644 --- a/hutool-system/src/main/java/cn/hutool/system/oshi/OshiUtil.java +++ b/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 getDiskStores() { return hardware.getDiskStores(); } /** * 获取网络相关信息,可能多块网卡 * @return 网络相关信息 - * @since 5.3.5 + * @since 5.3.6 */ - public static NetworkIF[] getNetworkIFs(){ + public static List 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; + } } diff --git a/hutool-system/src/test/java/cn/hutool/system/OshiTest.java b/hutool-system/src/test/java/cn/hutool/system/OshiTest.java index ebc638156..7bfee6a89 100644 --- a/hutool-system/src/test/java/cn/hutool/system/OshiTest.java +++ b/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); + } }