【问题标题】:OpenCV image Comparison And Similarity in AndroidAndroid中的OpenCV图像比较和相似性
【发布时间】:2018-02-06 13:10:01
【问题描述】:

我是OpenCV 学习者。我正在尝试图像比较。我用过 OpenCV 2.4.13.3 我有这两张图片1.jpgcam1.jpg

当我在 openCV 中使用以下命令时

File sdCard = Environment.getExternalStorageDirectory();
String path1, path2;
path1 = sdCard.getAbsolutePath() + "/1.jpg";
path2 = sdCard.getAbsolutePath() + "/cam1.jpg";

FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.BRIEF);
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);

Mat img1 = Highgui.imread(path1);
Mat img2 = Highgui.imread(path2);

Mat descriptors1 = new Mat();
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(img1, keypoints1);
extractor.compute(img1, keypoints1, descriptors1);

//second image
// Mat img2 = Imgcodecs.imread(path2);
Mat descriptors2 = new Mat();
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(img2, keypoints2);
extractor.compute(img2, keypoints2, descriptors2);


//matcher image descriptors
MatOfDMatch matches = new MatOfDMatch();
matcher.match(descriptors1,descriptors2,matches);

// Filter matches by distance
MatOfDMatch filtered = filterMatchesByDistance(matches);

int total = (int) matches.size().height;
int Match= (int) filtered.size().height;
Log.d("LOG", "total:" + total + " Match:"+Match);

方法 filterMatchesByDistance

    static MatOfDMatch filterMatchesByDistance(MatOfDMatch matches){
    List<DMatch> matches_original = matches.toList();
    List<DMatch> matches_filtered = new ArrayList<DMatch>();

    int DIST_LIMIT = 30;
    // Check all the matches distance and if it passes add to list of filtered matches
    Log.d("DISTFILTER", "ORG SIZE:" + matches_original.size() + "");
    for (int i = 0; i < matches_original.size(); i++) {
        DMatch d = matches_original.get(i);
        if (Math.abs(d.distance) <= DIST_LIMIT) {
            matches_filtered.add(d);
        }
    }
    Log.d("DISTFILTER", "FIL SIZE:" + matches_filtered.size() + "");

    MatOfDMatch mat = new MatOfDMatch();
    mat.fromList(matches_filtered);
    return mat;
}

日志

total:122 Match:30

从日志中我们可以看到匹配是 30。
但正如我们所见,两张图片都具有相同的视觉元素 (in)。
如何使用 openCV 获得 match=90?
如果有人可以帮助编写代码 sn-p,那就太好了。
如果使用 opencv 是不可能的,那么其他是什么 我们可以寻找替代方案吗?

【问题讨论】:

  • 这感觉像是在寻找有关 OpenCV 和/或图像识别的教程的问题。我正在链接到不同的 SE 小组,在该小组中阅读以下帖子:dsp.stackexchange.com/questions/17846/… 可能会为进一步研究提供适当的背景。
  • 为什么不更改阈值DIST_LIMIT?如果 30 个就足够了,获得更多匹配项的目的是什么?
  • 这对于 ORB/SIFT 描述符来说看起来是一个具有挑战性的图像......它基本上有 2 种颜色,并且大多数描述符(如果不是全部)将在边缘周围,这与很多相似其他边缘,所以你会得到很多误报(最佳匹配比你想象的更远),当按距离过滤时,你只会得到最接近它的那些。也许你必须尝试另一种方法,更像是模式匹配等等
  • github.com/opencv/opencv/blob/master/samples/cpp/… 你检查过这个吗?为您的图像提供良好的效果。
  • 我同意@api55,特征检测/匹配不适合这类图像,因为它们不会产生足够的唯一关键点。您应该对pattern matching 有更多的运气,不幸的是它不适合旋转。另外,请不要专门要求代码,SO不是编码服务。

标签: android image opencv image-processing


【解决方案1】:

但我们可以看到,两张图片都有相同的视觉元素(in)。

所以,我们不应该比较整个图像,而应该比较“相同的视觉元素”。如果您不比较“模板”和“相机”图像本身,而是以相同的方式处理(例如转换为二进制黑白)“模板”和“相机”图像,则可以进一步提高 Match 值。例如,尝试在(“模板”和“相机”)图像上找到蓝色(模板徽标的背景)正方形并比较该正方形(感兴趣区域)。代码可能是这样的:

Bitmap bmImageTemplate = <get your template image Bitmap>;
Bitmap bmTemplate = findLogo(bmImageTemplate);  // process template image

Bitmap bmImage = <get your camera image Bitmap>;
Bitmap bmLogo = findLogo(bmImage); // process camera image same way

compareBitmaps(bmTemplate, bmLogo);

在哪里

private Bitmap findLogo(Bitmap sourceBitmap) {
    Bitmap roiBitmap = null;
    Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC3);
    Utils.bitmapToMat(sourceBitmap, sourceMat);
    Mat roiTmp = sourceMat.clone();

    final Mat hsvMat = new Mat();
    sourceMat.copyTo(hsvMat);

    // convert mat to HSV format for Core.inRange()
    Imgproc.cvtColor(hsvMat, hsvMat, Imgproc.COLOR_RGB2HSV);

    Scalar lowerb = new Scalar(85, 50, 40);         // lower color border for BLUE
    Scalar upperb = new Scalar(135, 255, 255);      // upper color border for BLUE
    Core.inRange(hsvMat, lowerb, upperb, roiTmp);   // select only blue pixels

    // find contours
    List<MatOfPoint> contours = new ArrayList<>();
    List<Rect> squares = new ArrayList<>();
    Imgproc.findContours(roiTmp, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

    // find appropriate bounding rectangles
    for (MatOfPoint contour : contours) {
        MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
        RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);

        double rectangleArea = boundingRect.size.area();

        // test min ROI area in pixels
        if (rectangleArea > 400) {
            Point rotated_rect_points[] = new Point[4];
            boundingRect.points(rotated_rect_points);

            Rect rect = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
            double aspectRatio = rect.width > rect.height ?
                    (double) rect.height / (double) rect.width : (double) rect.width / (double) rect.height;
            if (aspectRatio >= 0.9) {
                squares.add(rect);
            }
        }
    }

    Mat logoMat = extractSquareMat(roiTmp, getBiggestSquare(squares));

    roiBitmap = Bitmap.createBitmap(logoMat.cols(), logoMat.rows(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(logoMat, roiBitmap);
    return roiBitmap;
}

方法extractSquareMat() 只是从整个图像中提取感兴趣区域(徽标)

public static Mat extractSquareMat(Mat sourceMat, Rect rect) {
    Mat squareMat = null;
    int padding = 50;

    if (rect != null) {
        Rect truncatedRect = new Rect((int) rect.tl().x + padding, (int) rect.tl().y + padding,
                rect.width - 2 * padding, rect.height - 2 * padding);
        squareMat = new Mat(sourceMat, truncatedRect);
    }

    return squareMat ;
}

compareBitmaps() 只是您的代码的包装器:

private void compareBitmaps(Bitmap bitmap1, Bitmap bitmap2) {
    Mat mat1 = new Mat(bitmap1.getWidth(), bitmap1.getHeight(), CvType.CV_8UC3);
    Utils.bitmapToMat(bitmap1, mat1);

    Mat mat2 = new Mat(bitmap2.getWidth(), bitmap2.getHeight(), CvType.CV_8UC3);
    Utils.bitmapToMat(bitmap2, mat2);

    compareMats(mat1, mat2);
}

您的代码作为方法:

private void compareMats(Mat img1, Mat img2) {
    FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
    DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.BRIEF);
    DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);

    Mat descriptors1 = new Mat();
    MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
    detector.detect(img1, keypoints1);
    extractor.compute(img1, keypoints1, descriptors1);

    //second image
    // Mat img2 = Imgcodecs.imread(path2);
    Mat descriptors2 = new Mat();
    MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
    detector.detect(img2, keypoints2);
    extractor.compute(img2, keypoints2, descriptors2);


    //matcher image descriptors
    MatOfDMatch matches = new MatOfDMatch();
    matcher.match(descriptors1,descriptors2,matches);

    // Filter matches by distance
    MatOfDMatch filtered = filterMatchesByDistance(matches);

    int total = (int) matches.size().height;
    int Match= (int) filtered.size().height;
    Log.d("LOG", "total:" + total + " Match:" + Match);
}

static MatOfDMatch filterMatchesByDistance(MatOfDMatch matches){
    List<DMatch> matches_original = matches.toList();
    List<DMatch> matches_filtered = new ArrayList<DMatch>();

    int DIST_LIMIT = 30;
    // Check all the matches distance and if it passes add to list of filtered matches
    Log.d("DISTFILTER", "ORG SIZE:" + matches_original.size() + "");
    for (int i = 0; i < matches_original.size(); i++) {
        DMatch d = matches_original.get(i);
        if (Math.abs(d.distance) <= DIST_LIMIT) {
            matches_filtered.add(d);
        }
    }
    Log.d("DISTFILTER", "FIL SIZE:" + matches_filtered.size() + "");

    MatOfDMatch mat = new MatOfDMatch();
    mat.fromList(matches_filtered);
    return mat;
}

从您的问题结果中保存的调整大小(缩放 50%)图像的结果是:

D/DISTFILTER: ORG SIZE:237
D/DISTFILTER: FIL SIZE:230
D/LOG: total:237 Match:230

注意!这是一个快速而肮脏的示例,只是为了演示仅适用于给定模板的方法。

附: getBiggestSquare() 可以这样(按区域比较):

public static Rect getBiggestSquare(List<Rect> squares) {
    Rect biggestSquare = null;

    if (squares != null && squares.size() >= 1) {
        Rect square;
        double maxArea;
        int ixMaxArea = 0;

        square = squares.get(ixMaxArea);
        maxArea = square.area();

        for (int ix = 1; ix < squares.size(); ix++) {
            square = squares.get(ix);
            if (square.area() > maxArea) {
                maxArea = square.area();
                ixMaxArea = ix;
            }
        }

        biggestSquare = squares.get(ixMaxArea);
    }

    return biggestSquare;
}

【讨论】:

  • 这仅适用于蓝色徽标。其他标志呢?我有 10-12 个标志。
  • 很可能可以为所有案例创建 ROI 提取规则,并在循环中一一测试徽标相似性。但是,如果您有许多具有复杂形状的徽标(图案)(例如带有渐变的图片),则更好的方法是使用基于机器学习的方法。看看thisthat 教程。
  • getBiggestSquare 方法是什么?
  • @AndriiOmelchenko 请显示函数:getBiggestSquare()
  • @CodeKadiya 请看 P.S.
猜你喜欢
  • 2012-11-03
  • 1970-01-01
  • 2020-04-02
  • 1970-01-01
  • 2013-01-29
  • 2016-09-19
  • 2011-08-09
  • 1970-01-01
相关资源
最近更新 更多