【教程】Autojs使用OpenCV进行SIFT/BRISK等算法进行图像匹配

转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn]

此代码可以替代内置的images.findImage函数使用,但可能会误匹配,如果是对匹配结果要求比较高的,还是得谨慎使用。

javascript 复制代码
runtime.images.initOpenCvIfNeeded();
importClass(java.util.ArrayList);
importClass(java.util.List);
importClass(java.util.LinkedList);
importClass(org.opencv.imgproc.Imgproc);
importClass(org.opencv.imgcodecs.Imgcodecs);
importClass(org.opencv.core.Core);
importClass(org.opencv.core.Mat);
importClass(org.opencv.core.MatOfDMatch);
importClass(org.opencv.core.MatOfKeyPoint);
importClass(org.opencv.core.MatOfRect);
importClass(org.opencv.core.Size);
importClass(org.opencv.features2d.DescriptorMatcher);
importClass(org.opencv.features2d.Features2d);
importClass(org.opencv.features2d.SIFT);
importClass(org.opencv.features2d.ORB);
importClass(org.opencv.features2d.BRISK);
importClass(org.opencv.features2d.AKAZE);
importClass(org.opencv.features2d.BFMatcher);
importClass(org.opencv.core.MatOfPoint2f);
importClass(org.opencv.calib3d.Calib3d);
importClass(org.opencv.core.CvType);
importClass(org.opencv.core.Point);
importClass(org.opencv.core.Scalar);
importClass(org.opencv.core.MatOfByte);

/*
 * 用法示例:
 * var image1 = captureScreen();
 * var image2 = images.read('xxxx');
 * match(image1, image2);
 */

function match(img1, img2, method) {
  console.time("匹配耗时");
  // 指定特征点算法SIFT
  var match_alg = null;
  if(method == 'sift') {
    match_alg = SIFT.create();
  }else if(method == 'orb') {
    match_alg = ORB.create();
  }else if(method == 'brisk') {
    match_alg = BRISK.create();
  }else {
    match_alg = AKAZE.create();
  }
  

  var bigTrainImage = Imgcodecs.imdecode(new MatOfByte(images.toBytes(img1)), Imgcodecs.IMREAD_UNCHANGED);
  var smallTrainImage = Imgcodecs.imdecode(new MatOfByte(images.toBytes(img2)), Imgcodecs.IMREAD_UNCHANGED);

  // 转灰度图
  // console.log("转灰度图");
  var big_trainImage_gray = new Mat(bigTrainImage.rows(), bigTrainImage.cols(), CvType.CV_8UC1);
  var small_trainImage_gray = new Mat(smallTrainImage.rows(), smallTrainImage.cols(), CvType.CV_8UC1);
  Imgproc.cvtColor(bigTrainImage, big_trainImage_gray, Imgproc.COLOR_BGR2GRAY);
  Imgproc.cvtColor(smallTrainImage, small_trainImage_gray, Imgproc.COLOR_BGR2GRAY);

  // 获取图片的特征点
  // console.log("detect");
  var big_keyPoints = new MatOfKeyPoint();
  var small_keyPoints = new MatOfKeyPoint();
  match_alg.detect(bigTrainImage, big_keyPoints);
  match_alg.detect(smallTrainImage, small_keyPoints);

  // 提取图片的特征点
  // console.log("compute");
  var big_trainDescription = new Mat(big_keyPoints.rows(), 128, CvType.CV_32FC1);
  var small_trainDescription = new Mat(small_keyPoints.rows(), 128, CvType.CV_32FC1);
  match_alg.compute(big_trainImage_gray, big_keyPoints, big_trainDescription);
  match_alg.compute(small_trainImage_gray, small_keyPoints, small_trainDescription);

  // console.log("matcher.train");
  var matcher = new BFMatcher();
  matcher.clear();
  var train_desc_collection = new ArrayList();
  train_desc_collection.add(big_trainDescription);
  // vector<Mat>train_desc_collection(1, trainDescription);
  matcher.add(train_desc_collection);
  matcher.train();

  // console.log("knnMatch");
  var matches = new ArrayList();
  matcher.knnMatch(small_trainDescription, matches, 2);

  //对匹配结果进行筛选,依据distance进行筛选
  // console.log("对匹配结果进行筛选");
  var goodMatches = new ArrayList();
  var nndrRatio = 0.8;

  var len = matches.size();
  for (var i = 0; i < len; i++) {
    var matchObj = matches.get(i);
    var dmatcharray = matchObj.toArray();
    var m1 = dmatcharray[0];
    var m2 = dmatcharray[1];
    if (m1.distance <= m2.distance * nndrRatio) {
      goodMatches.add(m1);
    }
  }

  var matchesPointCount = goodMatches.size();
  //当匹配后的特征点大于等于 4 个,则认为模板图在原图中,该值可以自行调整
  if (matchesPointCount >= 4) {
    log("模板图在原图匹配成功!");
    var templateKeyPoints = small_keyPoints;
    var originalKeyPoints = big_keyPoints;

    var templateKeyPointList = templateKeyPoints.toList();
    var originalKeyPointList = originalKeyPoints.toList();
    var objectPoints = new LinkedList();
    var scenePoints = new LinkedList();
    var goodMatchesList = goodMatches;

    var len = goodMatches.size();
    for (var i = 0; i < len; i++) {
      var goodMatch = goodMatches.get(i);
      objectPoints.addLast(templateKeyPointList.get(goodMatch.queryIdx).pt);
      scenePoints.addLast(originalKeyPointList.get(goodMatch.trainIdx).pt);
    }

    var objMatOfPoint2f = new MatOfPoint2f();
    objMatOfPoint2f.fromList(objectPoints);
    var scnMatOfPoint2f = new MatOfPoint2f();
    scnMatOfPoint2f.fromList(scenePoints);
    //使用 findHomography 寻找匹配上的关键点的变换
    var homography = Calib3d.findHomography(objMatOfPoint2f, scnMatOfPoint2f, Calib3d.RANSAC, 3);

    /**
     * 透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。
     */
    var templateCorners = new Mat(4, 1, CvType.CV_32FC2);
    var templateTransformResult = new Mat(4, 1, CvType.CV_32FC2);
    var templateImage = smallTrainImage;
    var doubleArr = util.java.array("double", 2);
    doubleArr[0] = 0;
    doubleArr[1] = 0;
    templateCorners.put(0, 0, doubleArr);
    doubleArr[0] = templateImage.cols();
    doubleArr[1] = 0;
    templateCorners.put(1, 0, doubleArr);
    doubleArr[0] = templateImage.cols();
    doubleArr[1] = templateImage.rows();
    templateCorners.put(2, 0, doubleArr);
    doubleArr[0] = 0;
    doubleArr[1] = templateImage.rows();
    templateCorners.put(3, 0, doubleArr);
    //使用 perspectiveTransform 将模板图进行透视变以矫正图象得到标准图片
    Core.perspectiveTransform(templateCorners, templateTransformResult, homography);

    //矩形四个顶点
    var pointA = templateTransformResult.get(0, 0);
    var pointB = templateTransformResult.get(1, 0);
    var pointC = templateTransformResult.get(2, 0);
    var pointD = templateTransformResult.get(3, 0);

    var y0 = Math.round(pointA[1])>0?Math.round(pointA[1]):0;
    var y1 = Math.round(pointC[1])>0?Math.round(pointC[1]):0;
    var x0 = Math.round(pointD[0])>0?Math.round(pointD[0]):0;
    var x1 = Math.round(pointB[0])>0?Math.round(pointB[0]):0;
    console.timeEnd("匹配耗时");
    return {x: x0, y: y0};
  } else {
    console.timeEnd("匹配耗时");
    log("模板图不在原图中!");
    return null;
  }
}
相关推荐
xiaoshiguang39 分钟前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡10 分钟前
【C语言】判断回文
c语言·学习·算法
别NULL12 分钟前
机试题——疯长的草
数据结构·c++·算法
TT哇16 分钟前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
yuanbenshidiaos2 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习2 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
程序员_三木2 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo2 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展