
3 changed files with 380 additions and 1 deletions
@ -0,0 +1,238 @@ |
|||||
|
package com.yxt.supervise.monitor.biz.util; |
||||
|
|
||||
|
import org.bytedeco.javacpp.BytePointer; |
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
|
||||
|
import static org.bytedeco.javacpp.opencv_core.*; |
||||
|
import static org.bytedeco.javacpp.opencv_imgcodecs.imread; |
||||
|
import static org.bytedeco.javacpp.opencv_imgcodecs.imwrite; |
||||
|
import static org.bytedeco.javacpp.opencv_imgproc.*; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @Author yzj |
||||
|
* @Date 2023-05-31 |
||||
|
* @Version 1.0 |
||||
|
*/ |
||||
|
public class ImageCompareUtil { |
||||
|
private static Logger Log = LoggerFactory.getLogger(ImageCompareUtil.class); |
||||
|
|
||||
|
|
||||
|
public static void compareImage(String targetImageUrl, String baseImageUrl) { |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 读取图片到数组 |
||||
|
*/ |
||||
|
Mat targetImage = imread(targetImageUrl); |
||||
|
Mat baseImage = imread(baseImageUrl); |
||||
|
Log.info("read image success"); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 首先对比的两个图片宽度要一致,否则不能对比 |
||||
|
*/ |
||||
|
if (targetImage.size().width() == baseImage.size().width()) { |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 基本算法 |
||||
|
* 1、判断高度是否一致,如果不一致,需要截取到高度一致 |
||||
|
* 2、截取算法 |
||||
|
* a、因为图片有通用的顶部bar和底部bar,需要先找到底部bar。 |
||||
|
* b、截取长图片的部分,然后和底部bar拼接,就完成了图片截取。 |
||||
|
* c、这里设置一个默认的宽度,然后对比,找到相同部分,就是底部bar。 |
||||
|
*/ |
||||
|
|
||||
|
if (targetImage.size().height() != baseImage.size().height()) { |
||||
|
|
||||
|
if (targetImage.size().height() > baseImage.size().height()) { |
||||
|
targetImage = dealLongImage(targetImage.clone(), baseImage.clone()); |
||||
|
} else { |
||||
|
baseImage = dealLongImage(baseImage.clone(), targetImage.clone()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 进行图片差异对比 |
||||
|
*/ |
||||
|
Mat imageDiff = compareImage(targetImage, baseImage); |
||||
|
|
||||
|
double total = 100; |
||||
|
|
||||
|
double nonZeroPercent = 100 * (double) countNonZero(imageDiff) / (imageDiff.size().height() * imageDiff.size().width()); |
||||
|
|
||||
|
System.out.println("相似度:" + (total - nonZeroPercent)); |
||||
|
|
||||
|
/** |
||||
|
* 展示图片,将标准图,对比图,差异图,拼接成一张大图。 |
||||
|
* 其中差异图会用绿色标出差异的部分。 |
||||
|
*/ |
||||
|
// set3ImageTo1("", targetImage, baseImage, showDiff(imageDiff, baseImage), "xxxx.jpg" );
|
||||
|
|
||||
|
|
||||
|
imageDiff.release(); |
||||
|
baseImage.release(); |
||||
|
targetImage.release(); |
||||
|
|
||||
|
} else { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 2、截取算法 |
||||
|
* a、因为图片有通用的顶部bar和底部bar,需要先找到底部bar。 |
||||
|
* b、截取长图片的部分,然后和底部bar拼接,就完成了图片截取。 |
||||
|
* c、这里设置一个默认的宽度,然后对比,找到相同部分,就是底部bar。 |
||||
|
* |
||||
|
* @return bar的高度 |
||||
|
*/ |
||||
|
public static int interceptBarHeight(Mat longImage, Mat shortImage) { |
||||
|
|
||||
|
/** |
||||
|
* 设置的默认高度。 |
||||
|
*/ |
||||
|
int imageSearchMaxHeight = 400; |
||||
|
Mat subImageLong = new Mat(longImage, new Rect(0, longImage.size().height() - imageSearchMaxHeight, longImage.size().width(), imageSearchMaxHeight)); |
||||
|
Mat subImageShort = new Mat(shortImage, new Rect(0, shortImage.size().height() - imageSearchMaxHeight, shortImage.size().width(), imageSearchMaxHeight)); |
||||
|
|
||||
|
Mat imageDiff = compareImage(subImageLong, subImageShort); |
||||
|
|
||||
|
for (int row = imageDiff.size().height() - 1; row > -1; row--) { |
||||
|
for (int col = 0; col < imageDiff.size().width(); col++) { |
||||
|
BytePointer bytePointer = imageDiff.ptr(row, col); |
||||
|
if (bytePointer.get(0) != 0) { |
||||
|
imageDiff.release(); |
||||
|
return imageSearchMaxHeight - row; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return imageSearchMaxHeight; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 这里将两张图片作为参数传入, |
||||
|
* 获取到共同的底部之后。对长图进行截取, |
||||
|
* 然后将顶部和底部拼接在一起就ok了。 |
||||
|
* |
||||
|
* @param longImage |
||||
|
* @param shortImage |
||||
|
* @return |
||||
|
*/ |
||||
|
public static Mat dealLongImage(Mat longImage, Mat shortImage) { |
||||
|
|
||||
|
int diffHeight = longImage.size().height() - shortImage.size().height(); |
||||
|
int barHeight = interceptBarHeight(longImage, shortImage); |
||||
|
|
||||
|
Mat dealedLongImage = new Mat(longImage, new Rect(0, 0, longImage.size().width(), shortImage.size().height() - barHeight)); |
||||
|
|
||||
|
Mat imageBar = new Mat(longImage, new Rect(0, longImage.size().height() - barHeight, longImage.size().width(), barHeight)); |
||||
|
|
||||
|
Mat dealedLongImageNew = dealedLongImage.clone(); |
||||
|
|
||||
|
/** |
||||
|
* 将头部和底部bar拼接在一起。 |
||||
|
*/ |
||||
|
vconcat(dealedLongImage, imageBar, dealedLongImageNew); |
||||
|
imageBar.release(); |
||||
|
dealedLongImage.release(); |
||||
|
return dealedLongImageNew; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static Mat compareImage(Mat targetImage, Mat baseImage) { |
||||
|
|
||||
|
Mat targetImageClone = targetImage.clone(); |
||||
|
Mat baseImageColne = baseImage.clone(); |
||||
|
Mat imgDiff1 = targetImage.clone(); |
||||
|
Mat imgDiff = targetImage.clone(); |
||||
|
|
||||
|
/** |
||||
|
* 首先将图片转成灰度图, |
||||
|
*/ |
||||
|
cvtColor(targetImage, targetImageClone, COLOR_BGR2GRAY); |
||||
|
cvtColor(baseImage, baseImageColne, COLOR_BGR2GRAY); |
||||
|
|
||||
|
/** |
||||
|
* 两个矩阵相减,获得差异图。 |
||||
|
*/ |
||||
|
subtract(targetImageClone, baseImageColne, imgDiff1); |
||||
|
subtract(baseImageColne, targetImageClone, imgDiff); |
||||
|
|
||||
|
/** |
||||
|
* 按比重进行叠加。 |
||||
|
*/ |
||||
|
addWeighted(imgDiff, 1, imgDiff1, 1, 0, imgDiff); |
||||
|
|
||||
|
/** |
||||
|
* 图片二值化,大于24的为1,小于24的为0 |
||||
|
*/ |
||||
|
threshold(imgDiff, imgDiff, 24, 255, THRESH_BINARY); |
||||
|
erode(imgDiff, imgDiff, new Mat()); |
||||
|
dilate(imgDiff, imgDiff, new Mat()); |
||||
|
return imgDiff; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private static void set3ImageTo1(String logTag, Mat imageSrc, Mat imageBaseSrc, Mat imageDest, String mergePicResult) { |
||||
|
|
||||
|
if (imageSrc.size().width() == imageDest.size().width() && imageBaseSrc.size().height() == imageDest.size().height()) { |
||||
|
Mat img = imageSrc.clone(); |
||||
|
Mat imgBase = imageBaseSrc.clone(); |
||||
|
Mat imgDest = imageDest.clone(); |
||||
|
Mat imgLine = new Mat(imgBase.size().height(), 1, CV_8UC3, new Scalar(0, 0, 0, 255)); |
||||
|
Mat largeImg2 = new Mat(); |
||||
|
Mat largeImg3 = new Mat(); |
||||
|
Mat largeImg4 = new Mat(); |
||||
|
Mat largeImg5 = new Mat(); |
||||
|
/** |
||||
|
* 横向拼接。 |
||||
|
*/ |
||||
|
hconcat(img, imgLine, largeImg2); |
||||
|
hconcat(largeImg2, imgBase, largeImg3); |
||||
|
hconcat(largeImg3, imgLine, largeImg4); |
||||
|
hconcat(largeImg4, imgDest, largeImg5); |
||||
|
|
||||
|
imwrite(mergePicResult, largeImg5); |
||||
|
|
||||
|
img.release(); |
||||
|
imgBase.release(); |
||||
|
imgDest.release(); |
||||
|
imgLine.release(); |
||||
|
largeImg2.release(); |
||||
|
largeImg3.release(); |
||||
|
largeImg4.release(); |
||||
|
largeImg5.release(); |
||||
|
} else { |
||||
|
Log.info(logTag + " pictures merge failed"); |
||||
|
imwrite(mergePicResult, imageDest); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
private static Mat showDiff(Mat imgDiff, Mat imgBase) { |
||||
|
|
||||
|
MatVector rgbFrame = new MatVector(); |
||||
|
Mat imgDest = imgBase.clone(); |
||||
|
subtract(rgbFrame.get(2), imgDiff, rgbFrame.get(2)); |
||||
|
subtract(rgbFrame.get(0), imgDiff, rgbFrame.get(0)); |
||||
|
addWeighted(rgbFrame.get(1), 1, imgDiff, 1, 0, rgbFrame.get(1)); |
||||
|
merge(rgbFrame, imgDest); |
||||
|
return imgDest; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
|
||||
|
String targetImageUrl = "C:\\Users\\Administrator\\Desktop\\5.png"; |
||||
|
String baseImageUrl = "C:\\Users\\Administrator\\Desktop\\6.png"; |
||||
|
|
||||
|
compareImage(targetImageUrl, baseImageUrl); |
||||
|
// interceptBarHeight(targetImageUrl,baseImageUrl);
|
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,130 @@ |
|||||
|
package com.yxt.supervise.monitor.biz.util; |
||||
|
|
||||
|
import javax.imageio.ImageIO; |
||||
|
import java.awt.image.BufferedImage; |
||||
|
import java.io.File; |
||||
|
|
||||
|
/** |
||||
|
* @Author yzj |
||||
|
* @Date 2023-05-31 |
||||
|
* @Version 1.0 |
||||
|
*/ |
||||
|
|
||||
|
public class ImageNotWCompareUtil { |
||||
|
public static String[][] getPX(String args) { |
||||
|
int[] rgb = new int[3]; |
||||
|
|
||||
|
File file = new File(args); |
||||
|
BufferedImage bi = null; |
||||
|
try { |
||||
|
bi = ImageIO.read(file); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
|
||||
|
int width = bi.getWidth(); |
||||
|
int height = bi.getHeight(); |
||||
|
int minx = bi.getMinX(); |
||||
|
int miny = bi.getMinY(); |
||||
|
String[][] list = new String[width][height]; |
||||
|
for (int i = minx; i < width; i++) { |
||||
|
for (int j = miny; j < height; j++) { |
||||
|
int pixel = bi.getRGB(i, j); |
||||
|
rgb[0] = (pixel & 0xff0000) >> 16; |
||||
|
rgb[1] = (pixel & 0xff00) >> 8; |
||||
|
rgb[2] = (pixel & 0xff); |
||||
|
list[i][j] = rgb[0] + "," + rgb[1] + "," + rgb[2]; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
return list; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static void compareImage(String imgPath1, String imgPath2) { |
||||
|
String[] images = {imgPath1, imgPath2}; |
||||
|
if (images.length == 0) { |
||||
|
System.out.println("Usage >java BMPLoader ImageFile.bmp"); |
||||
|
System.exit(0); |
||||
|
} |
||||
|
|
||||
|
// 分析图片相似度 begin
|
||||
|
String[][] list1 = getPX(images[0]); |
||||
|
String[][] list2 = getPX(images[1]); |
||||
|
int xiangsi = 0; |
||||
|
int busi = 0; |
||||
|
int i = 0, j = 0; |
||||
|
for (String[] strings : list1) { |
||||
|
if ((i + 1) == list1.length) { |
||||
|
continue; |
||||
|
} |
||||
|
for (int m = 0; m < strings.length; m++) { |
||||
|
try { |
||||
|
String[] value1 = list1[i][j].toString().split(","); |
||||
|
String[] value2 = list2[i][j].toString().split(","); |
||||
|
int k = 0; |
||||
|
for (int n = 0; n < value2.length; n++) { |
||||
|
if (Math.abs(Integer.parseInt(value1[k]) - Integer.parseInt(value2[k])) < 5) { |
||||
|
xiangsi++; |
||||
|
} else { |
||||
|
busi++; |
||||
|
} |
||||
|
} |
||||
|
} catch (RuntimeException e) { |
||||
|
continue; |
||||
|
} |
||||
|
j++; |
||||
|
} |
||||
|
i++; |
||||
|
} |
||||
|
|
||||
|
list1 = getPX(images[1]); |
||||
|
list2 = getPX(images[0]); |
||||
|
i = 0; |
||||
|
j = 0; |
||||
|
for (String[] strings : list1) { |
||||
|
if ((i + 1) == list1.length) { |
||||
|
continue; |
||||
|
} |
||||
|
for (int m = 0; m < strings.length; m++) { |
||||
|
try { |
||||
|
String[] value1 = list1[i][j].toString().split(","); |
||||
|
String[] value2 = list2[i][j].toString().split(","); |
||||
|
int k = 0; |
||||
|
for (int n = 0; n < value2.length; n++) { |
||||
|
if (Math.abs(Integer.parseInt(value1[k]) - Integer.parseInt(value2[k])) < 5) { |
||||
|
xiangsi++; |
||||
|
} else { |
||||
|
busi++; |
||||
|
} |
||||
|
} |
||||
|
} catch (RuntimeException e) { |
||||
|
continue; |
||||
|
} |
||||
|
j++; |
||||
|
} |
||||
|
i++; |
||||
|
} |
||||
|
String baifen = ""; |
||||
|
try { |
||||
|
baifen = ((Double.parseDouble(xiangsi + "") / Double.parseDouble((busi + xiangsi) + "")) + ""); |
||||
|
baifen = baifen.substring(baifen.indexOf(".") + 1, baifen.indexOf(".") + 3); |
||||
|
} catch (Exception e) { |
||||
|
baifen = "0"; |
||||
|
} |
||||
|
if (baifen.length() <= 0) { |
||||
|
baifen = "0"; |
||||
|
} |
||||
|
if (busi == 0) { |
||||
|
baifen = "100"; |
||||
|
} |
||||
|
|
||||
|
System.out.println("相似像素数量:" + xiangsi + " 不相似像素数量:" + busi + " 相似率:" + Integer.parseInt(baifen) + "%"); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
ImageNotWCompareUtil.compareImage("E:\\downLoad\\宇信通\\same\\7.jpg", "E:\\downLoad\\宇信通\\same\\8.jpg"); |
||||
|
// ImageNotWService.compareImage("E:\\downLoad\\宇信通\\same\\3.png", "E:\\downLoad\\宇信通\\same\\1.png");
|
||||
|
} |
||||
|
} |
Loading…
Reference in new issue