|
|
@ -1,8 +1,4 @@ |
|
|
|
package com.iflytop.colortitration.app.service; |
|
|
|
|
|
|
|
import lombok.RequiredArgsConstructor; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
package com.iflytop.colortitration.app.common.utils; |
|
|
|
|
|
|
|
import javax.imageio.ImageIO; |
|
|
|
import java.awt.*; |
|
|
@ -13,22 +9,17 @@ import java.io.File; |
|
|
|
import java.io.IOException; |
|
|
|
|
|
|
|
/** |
|
|
|
* 图像分析 |
|
|
|
* 工具类:图像分析,可扩展多种图像处理与分析方法,方法均为静态调用 |
|
|
|
*/ |
|
|
|
@Slf4j |
|
|
|
@Service |
|
|
|
@RequiredArgsConstructor |
|
|
|
public class ImageAnalysisService { |
|
|
|
public class ImageAnalysisUtil { |
|
|
|
/** |
|
|
|
* RGB 空间最大距离 |
|
|
|
*/ |
|
|
|
private static final double MAX_DISTANCE = Math.sqrt(3 * 255 * 255); |
|
|
|
|
|
|
|
/** |
|
|
|
* 泡沫过滤:亮度与均值偏差倍数 |
|
|
|
*/ |
|
|
|
private static final double BUBBLE_STD_MULTIPLIER = 1.5; |
|
|
|
|
|
|
|
/** |
|
|
|
* 默认高斯平滑内核(3x3) |
|
|
|
*/ |
|
|
@ -38,6 +29,10 @@ public class ImageAnalysisService { |
|
|
|
1 / 16f, 2 / 16f, 1 / 16f |
|
|
|
}; |
|
|
|
|
|
|
|
// 私有构造,避免实例化 |
|
|
|
private ImageAnalysisUtil() { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 使用百分比容差(0–100%)比较图像文件指定区域的平均颜色与目标颜色是否在容差范围内。 |
|
|
|
* |
|
|
@ -48,7 +43,7 @@ public class ImageAnalysisService { |
|
|
|
* @return 如果区域平均颜色与目标颜色距离 <= 百分比转换后的绝对距离,则返回 true;否则返回 false |
|
|
|
* @throws IOException 如果读取图像文件失败 |
|
|
|
*/ |
|
|
|
public boolean isRegionColorWithinTolerance(File imageFile, Color targetColor, double tolerancePercent, Rectangle region) throws IOException { |
|
|
|
public static boolean isRegionColorWithinTolerance(File imageFile, Color targetColor, double tolerancePercent, Rectangle region) throws IOException { |
|
|
|
BufferedImage image = loadImage(imageFile); |
|
|
|
Rectangle validRegion = getValidRegion(image, region); |
|
|
|
|
|
|
@ -83,7 +78,7 @@ public class ImageAnalysisService { |
|
|
|
* @return 如果区域平均颜色与目标颜色距离 <= 百分比转换后的绝对距离,则返回 true;否则返回 false |
|
|
|
* @throws IOException 如果读取图像文件失败 |
|
|
|
*/ |
|
|
|
public boolean analyzeRegionColorMatch(File imageFile, Color targetColor, double tolerancePercent, Rectangle region) throws IOException { |
|
|
|
public static boolean analyzeRegionColorMatch(File imageFile, Color targetColor, double tolerancePercent, Rectangle region) throws IOException { |
|
|
|
BufferedImage image = loadImage(imageFile); |
|
|
|
BufferedImage smoothed = smoothImage(image); |
|
|
|
Rectangle validRegion = getValidRegion(smoothed, region); |
|
|
@ -143,7 +138,7 @@ public class ImageAnalysisService { |
|
|
|
* @return 如果检测到符合特征的区域,返回 true;否则返回 false |
|
|
|
* @throws IOException 读取图像失败 |
|
|
|
*/ |
|
|
|
public boolean isTestTubePresent(File backgroundFile, File currentFile, Rectangle region, double diffThresholdPercent, int minArea, double minAspectRatio) throws IOException { |
|
|
|
public static boolean isTestTubePresent(File backgroundFile, File currentFile, Rectangle region, double diffThresholdPercent, int minArea, double minAspectRatio) throws IOException { |
|
|
|
BufferedImage bg = smoothImage(loadImage(backgroundFile)); |
|
|
|
BufferedImage curr = smoothImage(loadImage(currentFile)); |
|
|
|
|
|
|
@ -187,7 +182,7 @@ public class ImageAnalysisService { |
|
|
|
return aspect >= minAspectRatio; |
|
|
|
} |
|
|
|
|
|
|
|
// --------------- 私有 --------------- |
|
|
|
// --------------- 私有辅助方法 --------------- |
|
|
|
|
|
|
|
/** |
|
|
|
* 读取图像文件并返回 BufferedImage 对象 |
|
|
@ -196,7 +191,7 @@ public class ImageAnalysisService { |
|
|
|
* @return BufferedImage 图像对象 |
|
|
|
* @throws IOException 如果读取失败抛出异常 |
|
|
|
*/ |
|
|
|
private BufferedImage loadImage(File imageFile) throws IOException { |
|
|
|
private static BufferedImage loadImage(File imageFile) throws IOException { |
|
|
|
BufferedImage img = ImageIO.read(imageFile); |
|
|
|
if (img == null) { |
|
|
|
throw new IOException("无法从文件读取图像: " + imageFile); |
|
|
@ -212,7 +207,7 @@ public class ImageAnalysisService { |
|
|
|
* @return Rectangle 有效区域 |
|
|
|
* @throws IllegalArgumentException 如果区域超出图像范围 |
|
|
|
*/ |
|
|
|
private Rectangle getValidRegion(BufferedImage image, Rectangle region) { |
|
|
|
private static Rectangle getValidRegion(BufferedImage image, Rectangle region) { |
|
|
|
Rectangle bounds = new Rectangle(0, 0, image.getWidth(), image.getHeight()); |
|
|
|
Rectangle valid = bounds.intersection(region); |
|
|
|
if (valid.isEmpty()) { |
|
|
@ -227,7 +222,7 @@ public class ImageAnalysisService { |
|
|
|
* @param src 源图像 |
|
|
|
* @return BufferedImage 平滑后的图像 |
|
|
|
*/ |
|
|
|
private BufferedImage smoothImage(BufferedImage src) { |
|
|
|
private static BufferedImage smoothImage(BufferedImage src) { |
|
|
|
Kernel kernel = new Kernel(3, 3, GAUSS_KERNEL); |
|
|
|
return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null).filter(src, null); |
|
|
|
} |
|
|
@ -239,7 +234,7 @@ public class ImageAnalysisService { |
|
|
|
* @param c2 颜色二 |
|
|
|
* @return double 欧氏距离 |
|
|
|
*/ |
|
|
|
private double computeDistance(Color c1, Color c2) { |
|
|
|
private static double computeDistance(Color c1, Color c2) { |
|
|
|
return Math.sqrt( |
|
|
|
Math.pow(c1.getRed() - c2.getRed(), 2) + |
|
|
|
Math.pow(c1.getGreen() - c2.getGreen(), 2) + |
|
|
@ -254,7 +249,7 @@ public class ImageAnalysisService { |
|
|
|
* @return double 绝对距离阈值 |
|
|
|
* @throws IllegalArgumentException 如果百分比不在 0–100 范围内 |
|
|
|
*/ |
|
|
|
private double convertTolerance(double percent) { |
|
|
|
private static double convertTolerance(double percent) { |
|
|
|
if (percent < 0 || percent > 100) { |
|
|
|
throw new IllegalArgumentException("tolerancePercent 范围应在 0–100 之间"); |
|
|
|
} |