From 16f55e7516e2218c0d9d6a609ce6e35ab5c9b12e Mon Sep 17 00:00:00 2001
From: ZhouYixun <291028775@qq.com>
Date: Wed, 19 Jan 2022 20:38:17 +0800
Subject: [PATCH] removecv
---
pom.xml | 280 ++++++++++++++-
.../agent/automation/AndroidStepHandler.java | 119 ++++---
.../agent/automation/IOSStepHandler.java | 118 ++++---
.../org/cloud/sonic/agent/cv/AKAZEFinder.java | 318 ++++++++++++++++++
.../org/cloud/sonic/agent/cv/SIFTFinder.java | 103 ++++++
.../sonic/agent/cv/SimilarityChecker.java | 70 ++++
.../org/cloud/sonic/agent/cv/TemMatcher.java | 61 ++++
src/main/resources/application.yml | 2 +-
8 files changed, 939 insertions(+), 132 deletions(-)
create mode 100644 src/main/java/org/cloud/sonic/agent/cv/AKAZEFinder.java
create mode 100644 src/main/java/org/cloud/sonic/agent/cv/SIFTFinder.java
create mode 100644 src/main/java/org/cloud/sonic/agent/cv/SimilarityChecker.java
create mode 100644 src/main/java/org/cloud/sonic/agent/cv/TemMatcher.java
diff --git a/pom.xml b/pom.xml
index 21f338b9..f1dd853f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,28 +102,292 @@
org.bytedeco
- ffmpeg-platform
- 4.2.2-1.5.3
+ openblas
+ 0.3.9-1.5.3
org.bytedeco
- javacpp-platform
- 1.5.3
+ opencv
+ 4.3.0-1.5.3
+
- dev
+ windows-x86
+
+ windows-x86
+
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ tesseract
+ 4.1.1-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ leptonica
+ 1.79.0-1.5.3
+ ${profileActive}
+
+
+
+
+
+ windows-x86_64
+
+ windows-x86_64
+
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+
+
+
+ linux-arm64
+
+ linux-arm64
+
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+
+
+
+ linux-armhf
- dev
+ linux-armhf
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+
+
+ linux-ppc64le
+
+ linux-ppc64le
+
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+
+
+
+ linux-x86
+
+ linux-x86
+
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+
+
+
+ linux-x86_64
+
+ linux-x86_64
+
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
+
+
- prod
+ macosx-x86_64
- prod
+ macosx-x86_64
+
+
+ org.bytedeco
+ ffmpeg
+ 4.2.2-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ javacpp
+ 1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ opencv
+ 4.3.0-1.5.3
+ ${profileActive}
+
+
+ org.bytedeco
+ openblas
+ 0.3.9-1.5.3
+ ${profileActive}
+
+
diff --git a/src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java b/src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java
index 0f9393bb..e9584f9e 100644
--- a/src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java
+++ b/src/main/java/org/cloud/sonic/agent/automation/AndroidStepHandler.java
@@ -5,6 +5,10 @@
import com.android.ddmlib.IDevice;
import org.cloud.sonic.agent.bridge.android.AndroidDeviceBridgeTool;
import org.cloud.sonic.agent.bridge.android.AndroidDeviceThreadPool;
+import org.cloud.sonic.agent.cv.AKAZEFinder;
+import org.cloud.sonic.agent.cv.SIFTFinder;
+import org.cloud.sonic.agent.cv.SimilarityChecker;
+import org.cloud.sonic.agent.cv.TemMatcher;
import org.cloud.sonic.agent.interfaces.ErrorType;
import org.cloud.sonic.agent.interfaces.ResultDetailStatus;
import org.cloud.sonic.agent.interfaces.StepType;
@@ -54,10 +58,6 @@
*/
public class AndroidStepHandler {
public LogTool log = new LogTool();
- private RestTemplate restTemplate = SpringTool.getBean(RestTemplate.class);
- private Environment environment = SpringTool.getBean(Environment.class);
- private String baseUrl = "http://" + environment.getProperty("sonic.server.host")
- + ":" + environment.getProperty("sonic.server.folder-port") + "/api/folder";
private AndroidDriver androidDriver;
private JSONObject globalParams = new JSONObject();
//包版本
@@ -884,40 +884,56 @@ public void clickByImg(HandleDes handleDes, String des, String pathValue) throws
return;
}
}
- File localCap = getScreenToLocal();
- FindResult findResult;
- FileSystemResource resource1 = new FileSystemResource(file);
- FileSystemResource resource2 = new FileSystemResource(localCap);
- MultiValueMap param = new LinkedMultiValueMap<>();
- param.add("file1", resource1);
- param.add("file2", resource2);
- param.add("type", "finder");
+ FindResult findResult = null;
try {
- ResponseEntity responseEntity =
- restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class);
- if (responseEntity.getBody().getInteger("code") == 2000) {
- findResult = responseEntity.getBody().getJSONObject("data").toJavaObject(FindResult.class);
+ SIFTFinder siftFinder = new SIFTFinder();
+ findResult = siftFinder.getSIFTFindResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "SIFT图像算法出错,切换算法中...",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ log.sendStepLog(StepType.INFO, "SIFT算法无法定位图片,切换AKAZE算法中...",
+ "");
+ try {
+ AKAZEFinder akazeFinder = new AKAZEFinder();
+ findResult = akazeFinder.getAKAZEFindResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "AKAZE图像算法出错,切换模版匹配算法中...",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ log.sendStepLog(StepType.INFO, "AKAZE算法无法定位图片,切换模版匹配算法中...",
+ "");
+ try {
+ TemMatcher temMatcher = new TemMatcher();
+ findResult = temMatcher.getTemMatchResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "模版匹配算法出错",
+ "");
+ }
if (findResult != null) {
- try {
- log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
- findResult.getUrl());
- TouchAction ta = new TouchAction(androidDriver);
- ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform();
- } catch (Exception e) {
- log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", "");
- handleDes.setE(e);
- }
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ handleDes.setE(new Exception("图片定位失败!"));
}
- } else if (responseEntity.getBody().getInteger("code") == 4003) {
- handleDes.setE(new Exception("图像匹配失败!"));
- } else {
- handleDes.setE(new Exception("点击失败!cv服务出错!"));
}
- } catch (Exception e) {
- handleDes.setE(new Exception("点击失败!cv服务访问出错!"));
- } finally {
- file.delete();
- localCap.delete();
+ }
+ if (findResult != null) {
+ try {
+ TouchAction ta = new TouchAction(androidDriver);
+ ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform();
+ } catch (Exception e) {
+ log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", "");
+ handleDes.setE(e);
+ }
}
}
@@ -986,34 +1002,13 @@ public void checkImage(HandleDes handleDes, String des, String pathValue, double
if (pathValue.startsWith("http")) {
file = DownImageTool.download(pathValue);
}
- File localCap = getScreenToLocal();
- FileSystemResource resource1 = new FileSystemResource(file);
- FileSystemResource resource2 = new FileSystemResource(localCap);
- MultiValueMap param = new LinkedMultiValueMap<>();
- param.add("file1", resource1);
- param.add("file2", resource2);
- param.add("type", "checker");
- try {
- ResponseEntity responseEntity =
- restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class);
- if (responseEntity.getBody().getInteger("code") == 2000) {
- double score = responseEntity.getBody().getDouble("data");
- handleDes.setStepDes("检测" + des + "图片相似度");
- handleDes.setDetail("相似度为" + score * 100 + "%");
- if (score == 0) {
- handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!"));
- } else if (score < (matchThreshold / 100)) {
- handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100));
- }
- } else {
- handleDes.setE(new Exception("图片相似度检测出错!cv服务出错!"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- handleDes.setE(new Exception("图片相似度检测出错!cv服务访问出错!"));
- } finally {
- file.delete();
- localCap.delete();
+ double score = SimilarityChecker.getSimilarMSSIMScore(file, getScreenToLocal(), true);
+ handleDes.setStepDes("检测" + des + "图片相似度");
+ handleDes.setDetail("相似度为" + score * 100 + "%");
+ if (score == 0) {
+ handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!"));
+ } else if (score < (matchThreshold / 100)) {
+ handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100));
}
}
diff --git a/src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java b/src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java
index e03831aa..5851752f 100644
--- a/src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java
+++ b/src/main/java/org/cloud/sonic/agent/automation/IOSStepHandler.java
@@ -4,6 +4,10 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.agent.bridge.ios.TIDeviceTool;
+import org.cloud.sonic.agent.cv.AKAZEFinder;
+import org.cloud.sonic.agent.cv.SIFTFinder;
+import org.cloud.sonic.agent.cv.SimilarityChecker;
+import org.cloud.sonic.agent.cv.TemMatcher;
import org.cloud.sonic.agent.interfaces.ErrorType;
import org.cloud.sonic.agent.interfaces.ResultDetailStatus;
import org.cloud.sonic.agent.interfaces.StepType;
@@ -62,10 +66,6 @@
*/
public class IOSStepHandler {
public LogTool log = new LogTool();
- private RestTemplate restTemplate = SpringTool.getBean(RestTemplate.class);
- private Environment environment = SpringTool.getBean(Environment.class);
- private String baseUrl = "http://" + environment.getProperty("sonic.server.host")
- + ":" + environment.getProperty("sonic.server.folder-port") + "/api/folder";
private IOSDriver iosDriver;
private JSONObject globalParams = new JSONObject();
private String testPackage = "";
@@ -580,40 +580,56 @@ public void clickByImg(HandleDes handleDes, String des, String pathValue) throws
return;
}
}
- File localCap = getScreenToLocal();
- FindResult findResult;
- FileSystemResource resource1 = new FileSystemResource(file);
- FileSystemResource resource2 = new FileSystemResource(localCap);
- MultiValueMap param = new LinkedMultiValueMap<>();
- param.add("file1", resource1);
- param.add("file2", resource2);
- param.add("type", "finder");
+ FindResult findResult = null;
try {
- ResponseEntity responseEntity =
- restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class);
- if (responseEntity.getBody().getInteger("code") == 2000) {
- findResult = responseEntity.getBody().getJSONObject("data").toJavaObject(FindResult.class);
+ SIFTFinder siftFinder = new SIFTFinder();
+ findResult = siftFinder.getSIFTFindResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "SIFT图像算法出错,切换算法中...",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ log.sendStepLog(StepType.INFO, "SIFT算法无法定位图片,切换AKAZE算法中...",
+ "");
+ try {
+ AKAZEFinder akazeFinder = new AKAZEFinder();
+ findResult = akazeFinder.getAKAZEFindResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "AKAZE图像算法出错,切换模版匹配算法中...",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ log.sendStepLog(StepType.INFO, "AKAZE算法无法定位图片,切换模版匹配算法中...",
+ "");
+ try {
+ TemMatcher temMatcher = new TemMatcher();
+ findResult = temMatcher.getTemMatchResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "模版匹配算法出错",
+ "");
+ }
if (findResult != null) {
- try {
- log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
- findResult.getUrl());
- TouchAction ta = new TouchAction(iosDriver);
- ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform();
- } catch (Exception e) {
- log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", "");
- handleDes.setE(e);
- }
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ handleDes.setE(new Exception("图片定位失败!"));
}
- } else if (responseEntity.getBody().getInteger("code") == 4003) {
- handleDes.setE(new Exception("图像匹配失败!"));
- } else {
- handleDes.setE(new Exception("点击失败!cv服务出错!"));
}
- } catch (Exception e) {
- handleDes.setE(new Exception("点击失败!cv服务访问出错!"));
- } finally {
- file.delete();
- localCap.delete();
+ }
+ if (findResult != null) {
+ try {
+ TouchAction ta = new TouchAction(iosDriver);
+ ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform();
+ } catch (Exception e) {
+ log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", "");
+ handleDes.setE(e);
+ }
}
}
@@ -664,33 +680,13 @@ public void checkImage(HandleDes handleDes, String des, String pathValue, double
if (pathValue.startsWith("http")) {
file = DownImageTool.download(pathValue);
}
- File localCap = getScreenToLocal();
- FileSystemResource resource1 = new FileSystemResource(file);
- FileSystemResource resource2 = new FileSystemResource(localCap);
- MultiValueMap param = new LinkedMultiValueMap<>();
- param.add("file1", resource1);
- param.add("file2", resource2);
- param.add("type", "checker");
- try {
- ResponseEntity responseEntity =
- restTemplate.postForEntity(baseUrl + "/upload/cv", param, JSONObject.class);
- if (responseEntity.getBody().getInteger("code") == 2000) {
- double score = responseEntity.getBody().getDouble("data");
- handleDes.setStepDes("检测" + des + "图片相似度");
- handleDes.setDetail("相似度为" + score * 100 + "%");
- if (score == 0) {
- handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!"));
- } else if (score < (matchThreshold / 100)) {
- handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100));
- }
- } else {
- handleDes.setE(new Exception("图片相似度检测出错!cv服务出错!"));
- }
- } catch (Exception e) {
- handleDes.setE(new Exception("图片相似度检测出错!cv服务访问出错!"));
- } finally {
- file.delete();
- localCap.delete();
+ double score = SimilarityChecker.getSimilarMSSIMScore(file, getScreenToLocal(), true);
+ handleDes.setStepDes("检测" + des + "图片相似度");
+ handleDes.setDetail("相似度为" + score * 100 + "%");
+ if (score == 0) {
+ handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!"));
+ } else if (score < (matchThreshold / 100)) {
+ handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100));
}
}
diff --git a/src/main/java/org/cloud/sonic/agent/cv/AKAZEFinder.java b/src/main/java/org/cloud/sonic/agent/cv/AKAZEFinder.java
new file mode 100644
index 00000000..5c344611
--- /dev/null
+++ b/src/main/java/org/cloud/sonic/agent/cv/AKAZEFinder.java
@@ -0,0 +1,318 @@
+package org.cloud.sonic.agent.cv;
+
+import org.cloud.sonic.agent.automation.FindResult;
+import org.cloud.sonic.agent.tools.UploadTools;
+import org.bytedeco.opencv.opencv_core.*;
+import org.bytedeco.opencv.opencv_features2d.AKAZE;
+import org.bytedeco.opencv.opencv_flann.Index;
+import org.bytedeco.opencv.opencv_flann.IndexParams;
+import org.bytedeco.opencv.opencv_flann.LshIndexParams;
+import org.bytedeco.opencv.opencv_flann.SearchParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.bytedeco.opencv.global.opencv_calib3d.CV_RANSAC;
+import static org.bytedeco.opencv.global.opencv_calib3d.findHomography;
+import static org.bytedeco.opencv.global.opencv_core.*;
+import static org.bytedeco.opencv.global.opencv_flann.FLANN_DIST_HAMMING;
+import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
+import static org.bytedeco.opencv.global.opencv_imgproc.*;
+
+public class AKAZEFinder {
+ private final Logger logger = LoggerFactory.getLogger(AKAZEFinder.class);
+ IplImage objectImage = null;
+ AKAZE detector = AKAZE.create();
+ double distanceThreshold = 0.6;
+ int matchesMin = 4;
+ double ransacReprojThreshold = 1.0;
+ boolean useFLANN = false;
+ KeyPointVector objectKeypoints = null, imageKeypoints = null;
+ Mat objectDescriptors = null, imageDescriptors = null;
+ Mat indicesMat, distsMat;
+ Index flannIndex = null;
+ IndexParams indexParams = null;
+ SearchParams searchParams = null;
+ Mat pt1 = null, pt2 = null, mask = null, H = null;
+ ArrayList ptpairs = null;
+
+ public void init() {
+ objectKeypoints = new KeyPointVector();
+ objectDescriptors = new Mat();
+ detector.detectAndCompute(cvarrToMat(objectImage),
+ new Mat(), objectKeypoints, objectDescriptors, false);
+
+ int total = (int) objectKeypoints.size();
+ if (useFLANN) {
+ indicesMat = new Mat(total, 2, CV_32SC1);
+ distsMat = new Mat(total, 2, CV_32FC1);
+ flannIndex = new Index();
+ indexParams = new LshIndexParams(12, 20, 2); // using LSH Hamming distance
+ searchParams = new SearchParams(64, 0, true); // maximum number of leafs checked
+ searchParams.deallocate(false); // for some reason FLANN seems to do it for us
+ }
+ pt1 = new Mat(total, 1, CV_32FC2);
+ pt2 = new Mat(total, 1, CV_32FC2);
+ mask = new Mat(total, 1, CV_8UC1);
+ H = new Mat(3, 3, CV_64FC1);
+ ptpairs = new ArrayList(2 * objectDescriptors.rows());
+ logger.info("模版图一共有" + total + "个特征点");
+ }
+
+ public double[] find(IplImage image) {
+ if (objectDescriptors.rows() < matchesMin) {
+ return null;
+ }
+ imageKeypoints = new KeyPointVector();
+ imageDescriptors = new Mat();
+ detector.detectAndCompute(cvarrToMat(image),
+ new Mat(), imageKeypoints, imageDescriptors, false);
+ if (imageDescriptors.rows() < matchesMin) {
+ return null;
+ }
+
+ int total = (int) imageKeypoints.size();
+ logger.info("原图一共有" + total + "个特征点");
+
+ int w = objectImage.width();
+ int h = objectImage.height();
+ double[] srcCorners = {0, 0, w, 0, w, h, 0, h};
+ double[] dstCorners = locatePlanarObject(objectKeypoints, objectDescriptors,
+ imageKeypoints, imageDescriptors, srcCorners);
+ return dstCorners;
+ }
+
+ private static final int[] bits = new int[256];
+
+ static {
+ for (int i = 0; i < bits.length; i++) {
+ for (int j = i; j != 0; j >>= 1) {
+ bits[i] += j & 0x1;
+ }
+ }
+ }
+
+ private int compareDescriptors(ByteBuffer d1, ByteBuffer d2, int best) {
+ int totalCost = 0;
+ assert d1.limit() - d1.position() == d2.limit() - d2.position();
+ while (d1.position() < d1.limit()) {
+ totalCost += bits[(d1.get() ^ d2.get()) & 0xFF];
+ if (totalCost > best)
+ break;
+ }
+ return totalCost;
+ }
+
+ private int naiveNearestNeighbor(ByteBuffer vec, ByteBuffer modelDescriptors) {
+ int neighbor = -1;
+ int d, dist1 = Integer.MAX_VALUE, dist2 = Integer.MAX_VALUE;
+ int size = vec.limit() - vec.position();
+
+ for (int i = 0; i * size < modelDescriptors.capacity(); i++) {
+ ByteBuffer mvec = (ByteBuffer) modelDescriptors.position(i * size).limit((i + 1) * size);
+ d = compareDescriptors((ByteBuffer) vec.reset(), mvec, dist2);
+ if (d < dist1) {
+ dist2 = dist1;
+ dist1 = d;
+ neighbor = i;
+ } else if (d < dist2) {
+ dist2 = d;
+ }
+ }
+ if (dist1 < distanceThreshold * dist2)
+ return neighbor;
+ return -1;
+ }
+
+ private void findPairs(Mat objectDescriptors, Mat imageDescriptors) {
+ int size = imageDescriptors.cols();
+ ByteBuffer objectBuf = objectDescriptors.createBuffer();
+ ByteBuffer imageBuf = imageDescriptors.createBuffer();
+
+ for (int i = 0; i * size < objectBuf.capacity(); i++) {
+ ByteBuffer descriptor = (ByteBuffer) objectBuf.position(i * size).limit((i + 1) * size).mark();
+ int nearestNeighbor = naiveNearestNeighbor(descriptor, imageBuf);
+ if (nearestNeighbor >= 0) {
+ ptpairs.add(i);
+ ptpairs.add(nearestNeighbor);
+ }
+ }
+ }
+
+ private void flannFindPairs(Mat objectDescriptors, Mat imageDescriptors) {
+ int length = objectDescriptors.rows();
+ flannIndex.build(imageDescriptors, indexParams, FLANN_DIST_HAMMING);
+ flannIndex.knnSearch(objectDescriptors, indicesMat, distsMat, 2, searchParams);
+ IntBuffer indicesBuf = indicesMat.createBuffer();
+ IntBuffer distsBuf = distsMat.createBuffer();
+ for (int i = 0; i < length; i++) {
+ if (distsBuf.get(2 * i) < distanceThreshold * distsBuf.get(2 * i + 1)) {
+ ptpairs.add(i);
+ ptpairs.add(indicesBuf.get(2 * i));
+ }
+ }
+ }
+
+ private double[] locatePlanarObject(KeyPointVector objectKeypoints, Mat objectDescriptors,
+ KeyPointVector imageKeypoints, Mat imageDescriptors, double[] srcCorners) {
+ ptpairs.clear();
+ if (useFLANN) {
+ flannFindPairs(objectDescriptors, imageDescriptors);
+ } else {
+ findPairs(objectDescriptors, imageDescriptors);
+ }
+ int n = ptpairs.size() / 2;
+ logger.info("筛选后共有" + n + "个吻合点");
+ if (n < matchesMin) {
+ return null;
+ }
+
+ pt1.resize(n);
+ pt2.resize(n);
+ mask.resize(n);
+ FloatBuffer pt1Idx = pt1.createBuffer();
+ FloatBuffer pt2Idx = pt2.createBuffer();
+ for (int i = 0; i < n; i++) {
+ Point2f p1 = objectKeypoints.get(ptpairs.get(2 * i)).pt();
+ pt1Idx.put(2 * i, p1.x());
+ pt1Idx.put(2 * i + 1, p1.y());
+ Point2f p2 = imageKeypoints.get(ptpairs.get(2 * i + 1)).pt();
+ pt2Idx.put(2 * i, p2.x());
+ pt2Idx.put(2 * i + 1, p2.y());
+ }
+
+ H = findHomography(pt1, pt2, CV_RANSAC, ransacReprojThreshold, mask, 2000, 0.995);
+ if (H.empty() || countNonZero(mask) < matchesMin) {
+ return null;
+ }
+
+ double[] h = (double[]) H.createIndexer(false).array();
+ double[] dstCorners = new double[srcCorners.length];
+ for (int i = 0; i < srcCorners.length / 2; i++) {
+ double x = srcCorners[2 * i], y = srcCorners[2 * i + 1];
+ double Z = 1 / (h[6] * x + h[7] * y + h[8]);
+ double X = (h[0] * x + h[1] * y + h[2]) * Z;
+ double Y = (h[3] * x + h[4] * y + h[5]) * Z;
+ dstCorners[2 * i] = X;
+ dstCorners[2 * i + 1] = Y;
+ }
+ return dstCorners;
+ }
+
+ public static Scalar randColor() {
+ int b, g, r;
+ b = ThreadLocalRandom.current().nextInt(0, 255);
+ g = ThreadLocalRandom.current().nextInt(0, 255);
+ r = ThreadLocalRandom.current().nextInt(0, 255);
+ return new Scalar(b, g, r, 0);
+ }
+
+ public FindResult getAKAZEFindResult(File temFile, File beforeFile) throws IOException {
+ IplImage object = cvLoadImage(temFile.getAbsolutePath(), IMREAD_GRAYSCALE);
+ IplImage image = cvLoadImage(beforeFile.getAbsolutePath(), IMREAD_GRAYSCALE);
+ logger.info("原图宽:" + image.width());
+ logger.info("原图高:" + image.height());
+ if (object == null || image == null) {
+ logger.error("读取图片失败!");
+ temFile.delete();
+ beforeFile.delete();
+ return null;
+ }
+
+ IplImage correspond = IplImage.create(image.width() + object.width(), image.height(), 8, 1);
+ cvSetImageROI(correspond, cvRect(0, 0, image.width(), correspond.height()));
+ cvCopy(image, correspond);
+ cvSetImageROI(correspond, cvRect(image.width(), 0, object.width(), object.height()));
+ cvCopy(object, correspond);
+ cvResetImageROI(correspond);
+
+ objectImage = object;
+ useFLANN = true;
+ ransacReprojThreshold = 3;
+ init();
+
+ long start = System.currentTimeMillis();
+ double[] dst_corners = find(image);
+ FindResult findResult = new FindResult();
+ findResult.setTime((int) (System.currentTimeMillis() - start));
+ logger.info("特征匹配时间: " + (System.currentTimeMillis() - start) + " ms");
+
+ IplImage correspondColor = IplImage.create(correspond.width(), correspond.height(), 8, 3);
+ cvCvtColor(correspond, correspondColor, CV_GRAY2BGR);
+ cvSetImageROI(correspondColor, cvRect(0, 0, image.width(), correspondColor.height()));
+ cvCopy(cvLoadImage(beforeFile.getAbsolutePath(), IMREAD_COLOR), correspondColor);
+ cvSetImageROI(correspondColor, cvRect(image.width(), 0, object.width(), object.height()));
+ cvCopy(cvLoadImage(temFile.getAbsolutePath(), IMREAD_COLOR), correspondColor);
+ cvResetImageROI(correspondColor);
+
+ if (dst_corners != null) {
+ int resultX = 0;
+ int resultY = 0;
+ for (int i = 0; i < 4; i++) {
+ int j = (i + 1) % 4;
+ int x1 = (int) Math.round(dst_corners[2 * i]);
+ int y1 = (int) Math.round(dst_corners[2 * i + 1]);
+ int x2 = (int) Math.round(dst_corners[2 * j]);
+ int y2 = (int) Math.round(dst_corners[2 * j + 1]);
+ line(cvarrToMat(correspondColor), new Point(x1, y1),
+ new Point(x2, y2),
+ Scalar.RED, 2, CV_AA, 0);
+ if (i == 0) {
+ resultX = (x1 + x2) / 2;
+ }
+ if (i == 1) {
+ resultY = (y1 + y2) / 2;
+ }
+ }
+ if (resultX == 0 && resultY == 0) {
+ temFile.delete();
+ beforeFile.delete();
+ return null;
+ }
+ findResult.setX(resultX);
+ findResult.setY(resultY);
+ logger.info("结果坐标为(" + resultX + "," + resultY + ")");
+ }
+
+ if (objectKeypoints != null) {
+ for (int i = 0; i < objectKeypoints.size(); i++) {
+ KeyPoint r = objectKeypoints.get(i);
+ Point center = new Point(Math.round(r.pt().x()) + image.width(), Math.round(r.pt().y()));
+ int radius = Math.round(r.size() / 2);
+ circle(cvarrToMat(correspondColor), center, radius, randColor(), 1, CV_AA, 0);
+ }
+ }
+
+ if (imageKeypoints != null) {
+ for (int i = 0; i < imageKeypoints.size(); i++) {
+ KeyPoint r = imageKeypoints.get(i);
+ Point center = new Point(Math.round(r.pt().x()), Math.round(r.pt().y()));
+ int radius = Math.round(r.size() / 2);
+ circle(cvarrToMat(correspondColor), center, radius, randColor(), 1, CV_AA, 0);
+ }
+ }
+
+ for (int i = 0; i < ptpairs.size(); i += 2) {
+ Point2f pt1 = objectKeypoints.get(ptpairs.get(i)).pt();
+ Point2f pt2 = imageKeypoints.get(ptpairs.get(i + 1)).pt();
+ line(cvarrToMat(correspondColor), new Point(Math.round(pt1.x()) + image.width(), Math.round(pt1.y())),
+ new Point(Math.round(pt2.x()), Math.round(pt2.y())),
+ randColor(), 1, CV_AA, 0);
+ }
+ long time = Calendar.getInstance().getTimeInMillis();
+ String fileName = "test-output" + File.separator + time + ".jpg";
+ cvSaveImage(fileName, correspondColor);
+ findResult.setUrl(UploadTools.upload(new File(fileName), "imageFiles"));
+ temFile.delete();
+ beforeFile.delete();
+ return findResult;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/cloud/sonic/agent/cv/SIFTFinder.java b/src/main/java/org/cloud/sonic/agent/cv/SIFTFinder.java
new file mode 100644
index 00000000..2f058e74
--- /dev/null
+++ b/src/main/java/org/cloud/sonic/agent/cv/SIFTFinder.java
@@ -0,0 +1,103 @@
+package org.cloud.sonic.agent.cv;
+
+import org.bytedeco.opencv.opencv_core.*;
+import org.bytedeco.opencv.opencv_features2d.FlannBasedMatcher;
+import org.bytedeco.opencv.opencv_xfeatures2d.SIFT;
+import org.cloud.sonic.agent.automation.FindResult;
+import org.cloud.sonic.agent.tools.UploadTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+import static org.bytedeco.opencv.global.opencv_features2d.drawMatchesKnn;
+import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
+import static org.bytedeco.opencv.global.opencv_imgcodecs.imwrite;
+import static org.bytedeco.opencv.global.opencv_imgproc.*;
+
+public class SIFTFinder {
+ private final Logger logger = LoggerFactory.getLogger(SIFTFinder.class);
+
+ public FindResult getSIFTFindResult(File temFile, File beforeFile) throws Exception {
+ Mat image01 = imread(beforeFile.getAbsolutePath());
+ Mat image02 = imread(temFile.getAbsolutePath());
+
+ Mat image1 = new Mat();
+ Mat image2 = new Mat();
+ cvtColor(image01, image1, COLOR_BGR2GRAY);
+ cvtColor(image02, image2, COLOR_BGR2GRAY);
+
+ KeyPointVector keyPointVector1 = new KeyPointVector();
+ KeyPointVector keyPointVector2 = new KeyPointVector();
+ Mat image11 = new Mat();
+ Mat image22 = new Mat();
+
+ long start = System.currentTimeMillis();
+ SIFT sift = SIFT.create();
+ sift.detectAndCompute(image1, image1, keyPointVector1, image11);
+ sift.detectAndCompute(image2, image2, keyPointVector2, image22);
+
+ FlannBasedMatcher flannBasedMatcher = new FlannBasedMatcher();
+ DMatchVectorVector matchPoints = new DMatchVectorVector();
+
+ flannBasedMatcher.knnMatch(image11, image22, matchPoints, 2);
+ logger.info("处理前共有匹配数:" + matchPoints.size());
+ DMatchVectorVector goodMatches = new DMatchVectorVector();
+
+ List xs = new ArrayList<>();
+ List ys = new ArrayList<>();
+ for (long i = 0; i < matchPoints.size(); i++) {
+ if (matchPoints.get(i).size() >= 2) {
+ DMatch match1 = matchPoints.get(i).get(0);
+ DMatch match2 = matchPoints.get(i).get(1);
+
+ if (match1.distance() <= 0.6 * match2.distance()) {
+ xs.add((int) keyPointVector1.get(match1.queryIdx()).pt().x());
+ ys.add((int) keyPointVector1.get(match1.queryIdx()).pt().y());
+ goodMatches.push_back(matchPoints.get(i));
+ }
+ }
+ }
+ logger.info("处理后匹配数:" + goodMatches.size());
+ if (goodMatches.size() <= 4) {
+ temFile.delete();
+ beforeFile.delete();
+ return null;
+ }
+ FindResult findResult = new FindResult();
+ findResult.setTime((int) (System.currentTimeMillis() - start));
+ Mat result = new Mat();
+
+ drawMatchesKnn(image01, keyPointVector1, image02, keyPointVector2, goodMatches, result);
+
+ int resultX = majorityElement(xs);
+ int resultY = majorityElement(ys);
+ findResult.setX(resultX);
+ findResult.setY(resultY);
+ logger.info("结果坐标为(" + resultX + "," + resultY + ")");
+ circle(result, new Point(resultX, resultY), 5, Scalar.RED, 10, CV_AA, 0);
+ long time = Calendar.getInstance().getTimeInMillis();
+ String fileName = "test-output" + File.separator + time + ".jpg";
+ imwrite(fileName, result);
+ findResult.setUrl(UploadTools.upload(new File(fileName), "imageFiles"));
+ temFile.delete();
+ beforeFile.delete();
+ return findResult;
+ }
+
+ public static int majorityElement(List nums) {
+ double j;
+ Collections.sort(nums);
+ int size = nums.size();
+ if (size % 2 == 1) {
+ j = nums.get((size - 1) / 2);
+ } else {
+ j = (nums.get(size / 2 - 1) + nums.get(size / 2) + 0.0) / 2;
+ }
+ return (int) j;
+ }
+}
diff --git a/src/main/java/org/cloud/sonic/agent/cv/SimilarityChecker.java b/src/main/java/org/cloud/sonic/agent/cv/SimilarityChecker.java
new file mode 100644
index 00000000..f6a2d7f3
--- /dev/null
+++ b/src/main/java/org/cloud/sonic/agent/cv/SimilarityChecker.java
@@ -0,0 +1,70 @@
+package org.cloud.sonic.agent.cv;
+
+import org.bytedeco.opencv.global.opencv_core;
+import org.bytedeco.opencv.opencv_core.Mat;
+import org.bytedeco.opencv.opencv_core.Scalar;
+import org.bytedeco.opencv.opencv_core.Size;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import static org.bytedeco.opencv.global.opencv_core.*;
+import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
+import static org.bytedeco.opencv.global.opencv_imgproc.GaussianBlur;
+
+public class SimilarityChecker {
+ private final Logger logger = LoggerFactory.getLogger(SimilarityChecker.class);
+
+ public static double getSimilarMSSIMScore(File file1, File file2, Boolean isDelete) {
+ synchronized (SimilarityChecker.class) {
+ Mat i1 = imread(file1.getAbsolutePath());
+ Mat i2 = imread(file2.getAbsolutePath());
+ if (i1.size().get() != i2.size().get()) {
+ return 0;
+ }
+ double C1 = 6.5025, C2 = 58.5225;
+ int d = opencv_core.CV_32F;
+ Mat I1 = new Mat();
+ Mat I2 = new Mat();
+ i1.convertTo(I1, d);
+ i2.convertTo(I2, d);
+ Mat I2_2 = I2.mul(I2).asMat();
+ Mat I1_2 = I1.mul(I1).asMat();
+ Mat I1_I2 = I1.mul(I2).asMat();
+ Mat mu1 = new Mat();
+ Mat mu2 = new Mat();
+ GaussianBlur(I1, mu1, new Size(11, 11), 1.5);
+ GaussianBlur(I2, mu2, new Size(11, 11), 1.5);
+ Mat mu1_2 = mu1.mul(mu1).asMat();
+ Mat mu2_2 = mu2.mul(mu2).asMat();
+ Mat mu1_mu2 = mu1.mul(mu2).asMat();
+ Mat sigma1_2 = new Mat();
+ Mat sigma2_2 = new Mat();
+ Mat sigma12 = new Mat();
+ GaussianBlur(I1_2, sigma1_2, new Size(11, 11), 1.5);
+ sigma1_2 = subtract(sigma1_2, mu1_2).asMat();
+ GaussianBlur(I2_2, sigma2_2, new Size(11, 11), 1.5);
+ sigma2_2 = subtract(sigma2_2, mu2_2).asMat();
+ GaussianBlur(I1_I2, sigma12, new Size(11, 11), 1.5);
+ sigma12 = subtract(sigma12, mu1_mu2).asMat();
+ Mat t1, t2, t3;
+ t1 = add(multiply(2, mu1_mu2), Scalar.all(C1)).asMat();
+ t2 = add(multiply(2, sigma12), Scalar.all(C2)).asMat();
+ t3 = t1.mul(t2).asMat();
+ t1 = add(add(mu1_2, mu2_2), Scalar.all(C1)).asMat();
+ t2 = add(add(sigma1_2, sigma2_2), Scalar.all(C2)).asMat();
+ t1 = t1.mul(t2).asMat();
+ Mat ssim_map = new Mat();
+ divide(t3, t1, ssim_map);
+ Scalar mSsim = mean(ssim_map);
+ if (isDelete) {
+ file1.delete();
+ file2.delete();
+ }
+ return new BigDecimal((mSsim.get(0) + mSsim.get(1) + mSsim.get(2)) / 3).setScale(2, RoundingMode.DOWN).doubleValue();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/cloud/sonic/agent/cv/TemMatcher.java b/src/main/java/org/cloud/sonic/agent/cv/TemMatcher.java
new file mode 100644
index 00000000..ecaf382d
--- /dev/null
+++ b/src/main/java/org/cloud/sonic/agent/cv/TemMatcher.java
@@ -0,0 +1,61 @@
+package org.cloud.sonic.agent.cv;
+
+import org.bytedeco.javacpp.DoublePointer;
+import org.bytedeco.opencv.opencv_core.*;
+import org.cloud.sonic.agent.automation.FindResult;
+import org.cloud.sonic.agent.tools.UploadTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.bytedeco.opencv.global.opencv_core.*;
+import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
+import static org.bytedeco.opencv.global.opencv_imgproc.*;
+
+public class TemMatcher {
+ private final Logger logger = LoggerFactory.getLogger(TemMatcher.class);
+
+ public FindResult getTemMatchResult(File temFile, File beforeFile) throws IOException {
+ try {
+ Mat sourceColor = imread(beforeFile.getAbsolutePath());
+ Mat sourceGrey = new Mat(sourceColor.size(), CV_8UC1);
+ cvtColor(sourceColor, sourceGrey, COLOR_BGR2GRAY);
+ Mat template = imread(temFile.getAbsolutePath(), IMREAD_GRAYSCALE);
+ Size size = new Size(sourceGrey.cols() - template.cols() + 1, sourceGrey.rows() - template.rows() + 1);
+ Mat result = new Mat(size, CV_32FC1);
+
+ long start = System.currentTimeMillis();
+ matchTemplate(sourceGrey, template, result, TM_CCORR_NORMED);
+ DoublePointer minVal = new DoublePointer();
+ DoublePointer maxVal = new DoublePointer();
+ Point min = new Point();
+ Point max = new Point();
+ minMaxLoc(result, minVal, maxVal, min, max, null);
+ rectangle(sourceColor, new Rect(max.x(), max.y(), template.cols(), template.rows()), randColor(), 2, 0, 0);
+ FindResult findResult = new FindResult();
+ findResult.setTime((int) (System.currentTimeMillis() - start));
+ long time = Calendar.getInstance().getTimeInMillis();
+ String fileName = "test-output" + File.separator + time + ".jpg";
+ imwrite(fileName, sourceColor);
+ findResult.setX(max.x() + template.cols() / 2);
+ findResult.setY(max.y() + template.rows() / 2);
+ findResult.setUrl(UploadTools.upload(new File(fileName), "imageFiles"));
+ return findResult;
+ } finally {
+ temFile.delete();
+ beforeFile.delete();
+ }
+ }
+
+ public static Scalar randColor() {
+ int b, g, r;
+ b = ThreadLocalRandom.current().nextInt(0, 255 + 1);
+ g = ThreadLocalRandom.current().nextInt(0, 255 + 1);
+ r = ThreadLocalRandom.current().nextInt(0, 255 + 1);
+ return new Scalar(b, g, r, 0);
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 97ee7be7..9473ce6c 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,4 +1,4 @@
spring:
version: @project.version@
profiles:
- active: @profileActive@
\ No newline at end of file
+ active: dev,@profileActive@
\ No newline at end of file