|
|
@ -90,47 +90,7 @@ public class ImageAnalysisUtil { |
|
|
|
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); |
|
|
|
|
|
|
|
int width = validRegion.width; |
|
|
|
int height = validRegion.height; |
|
|
|
int area = width * height; |
|
|
|
double[] brightness = new double[area]; |
|
|
|
int idx = 0; |
|
|
|
for (int x = validRegion.x; x < validRegion.x + width; x++) { |
|
|
|
for (int y = validRegion.y; y < validRegion.y + height; y++) { |
|
|
|
Color c = new Color(smoothed.getRGB(x, y), true); |
|
|
|
brightness[idx++] = (c.getRed() + c.getGreen() + c.getBlue()) / 3.0; |
|
|
|
} |
|
|
|
} |
|
|
|
double sum = 0; |
|
|
|
for (double v : brightness) sum += v; |
|
|
|
double mean = sum / area; |
|
|
|
double var = 0; |
|
|
|
for (double v : brightness) var += Math.pow(v - mean, 2); |
|
|
|
double std = Math.sqrt(var / area); |
|
|
|
|
|
|
|
long sumR = 0, sumG = 0, sumB = 0; |
|
|
|
int validCount = 0; |
|
|
|
idx = 0; |
|
|
|
for (int x = validRegion.x; x < validRegion.x + width; x++) { |
|
|
|
for (int y = validRegion.y; y < validRegion.y + height; y++) { |
|
|
|
if (Math.abs(brightness[idx++] - mean) <= BUBBLE_STD_MULTIPLIER * std) { |
|
|
|
Color c = new Color(smoothed.getRGB(x, y), true); |
|
|
|
sumR += c.getRed(); |
|
|
|
sumG += c.getGreen(); |
|
|
|
sumB += c.getBlue(); |
|
|
|
validCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Color avgColor = validCount == 0 |
|
|
|
? new Color(0, 0, 0) |
|
|
|
: new Color( |
|
|
|
(int) (sumR / validCount), |
|
|
|
(int) (sumG / validCount), |
|
|
|
(int) (sumB / validCount) |
|
|
|
); |
|
|
|
Color avgColor = computeFilteredAverageColor(smoothed, region); |
|
|
|
double distance = computeDistance(avgColor, targetColor); |
|
|
|
return distance <= convertTolerance(tolerancePercent); |
|
|
|
} |
|
|
@ -191,6 +151,21 @@ public class ImageAnalysisUtil { |
|
|
|
return aspect >= minAspectRatio; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 返回指定图像文件区域的平均颜色的16进制字符串 |
|
|
|
* 使用高斯平滑 + 泡沫过滤处理后计算区域平均颜色,格式 "#RRGGBB" |
|
|
|
* |
|
|
|
* @param imageFile 待分析的图像文件 |
|
|
|
* @param region 指定区域(x, y, width, height) |
|
|
|
* @return 区域平均颜色的16进制代码 |
|
|
|
* @throws IOException 如果读取图像文件失败 |
|
|
|
*/ |
|
|
|
public static String getRegionAverageHexColor(File imageFile, Rectangle region) throws IOException { |
|
|
|
BufferedImage image = loadImage(imageFile); |
|
|
|
BufferedImage smoothed = smoothImage(image); |
|
|
|
Color avg = computeFilteredAverageColor(smoothed, region); |
|
|
|
return String.format("#%02X%02X%02X", avg.getRed(), avg.getGreen(), avg.getBlue()); |
|
|
|
} |
|
|
|
// --------------- 私有辅助方法 --------------- |
|
|
|
|
|
|
|
/** |
|
|
@ -252,6 +227,54 @@ public class ImageAnalysisUtil { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 先对指定区域像素的亮度进行统计,然后使用亮度均值和标准差过滤泡沫噪声,再计算剩余像素的平均颜色。 |
|
|
|
* |
|
|
|
* @param img 已平滑的图像对象 |
|
|
|
* @param region 分析区域 |
|
|
|
* @return 过滤后的区域平均颜色 |
|
|
|
*/ |
|
|
|
private static Color computeFilteredAverageColor(BufferedImage img, Rectangle region) { |
|
|
|
int w = region.width, h = region.height, area = w * h; |
|
|
|
double[] bright = new double[area]; |
|
|
|
int idx = 0; |
|
|
|
for (int x = region.x; x < region.x + w; x++) |
|
|
|
for (int y = region.y; y < region.y + h; y++) { |
|
|
|
bright[idx++] = getGray(img.getRGB(x, y)); |
|
|
|
} |
|
|
|
double sum = 0; |
|
|
|
for (double v : bright) sum += v; |
|
|
|
double mean = sum / area; |
|
|
|
double var = 0; |
|
|
|
for (double v : bright) var += Math.pow(v - mean, 2); |
|
|
|
double std = Math.sqrt(var / area); |
|
|
|
long sumR = 0, sumG = 0, sumB = 0; |
|
|
|
int cnt = 0; |
|
|
|
idx = 0; |
|
|
|
for (int x = region.x; x < region.x + w; x++) |
|
|
|
for (int y = region.y; y < region.y + h; y++) { |
|
|
|
if (Math.abs(bright[idx++] - mean) <= BUBBLE_STD_MULTIPLIER * std) { |
|
|
|
Color c = new Color(img.getRGB(x, y), true); |
|
|
|
sumR += c.getRed(); |
|
|
|
sumG += c.getGreen(); |
|
|
|
sumB += c.getBlue(); |
|
|
|
cnt++; |
|
|
|
} |
|
|
|
} |
|
|
|
return cnt == 0 ? new Color(0, 0, 0) : new Color((int) (sumR / cnt), (int) (sumG / cnt), (int) (sumB / cnt)); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 计算单像素的灰度值(简单平均法)。 |
|
|
|
* |
|
|
|
* @param rgb 像素的 RGB 整数值 |
|
|
|
* @return 灰度值 |
|
|
|
*/ |
|
|
|
private static double getGray(int rgb) { |
|
|
|
Color c = new Color(rgb, true); |
|
|
|
return (c.getRed() + c.getGreen() + c.getBlue()) / 3.0; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 将百分比容差转换为在 RGB 空间中的绝对距离阈值 |
|
|
|
* |
|
|
|
* @param percent 容差百分比(0–100) |
|
|
|