Android中的OpenCV图像比较

Posted

技术标签:

【中文标题】Android中的OpenCV图像比较【英文标题】:OpenCV image comparison in Android 【发布时间】:2013-01-29 00:35:52 【问题描述】:

[编辑] 我设计了一些用于图像比较的代码。匹配的部分仍然有点缺陷,我希望得到一些帮助。该项目可以在-GitHub找到。

我有这两张图片 Img1Img2

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

Mat img1 = Highgui.imread("mnt/sdcard/IMG-20121228.jpg");
Mat img2 = Highgui.imread("mnt/sdcard/IMG-20121228-1.jpg");

try
    double l2_norm = Core.norm( img1, img2 );
    tv.setText(l2_norm+"");
 catch(Exception e) 
    //image is not a duplicate

我得到 l2_norm 的双精度值。对于重复的图像对,此双精度值会有所不同。但如果图像不同,则会引发异常。这是我识别重复图像的方式吗?或者有没有更好的方法?我已经广泛搜索并找不到一个真正令人信服的答案。我想要关于如何比较两个图像并根据图像获得truefalse 的布尔值的代码和解释。

编辑

Scalar blah= Core.sumElems(img2);
    Scalar blah1=Core.sumElems(img1);

    if(blah.equals(blah1))
    
        tv.setText("same image");
    
    

我已经尝试过了,但 if 条件永远不会满足。我假设存在一些差异,但 Scalar 没有 compare 函数。我该怎么办?

编辑

try
    Scalar blah= Core.sumElems(img2);
    Scalar blah1=Core.sumElems(img1);
    String b=blah.toString();
    String b1=blah1.toString();
    System.out.println(b+" "+b1);
    double comp=b.compareTo(b1);
    tv.setText(""+comp);
    

这种方法又是有缺陷的。虽然它可以用来比较准确度不错的图像,但当图像大小不同时它就失败了。

当图像尺寸不同并且我打印标量值时,我会得到:

[9768383.0, 1.0052889E7, 1.0381814E7, 0.0] [1.5897384E7, 1.6322252E7, 1.690251E7, 0.0]

第二个和第三个数字之间的差异虽然不大,但与比较相同尺寸的图像时相比是相当大的。然而,第一个数字的变化最大。

比较两张图片内容的最佳最快方法是什么?

[编辑]

我正在使用我找到的代码here。

我不知道如何初始化MatOfKeyPoint 变量keypointslogoKeypoints。这是我的代码 sn-p:

           FeatureDetector detector = FeatureDetector.create(FeatureDetector.SURF);
        //FeatureDetector detector = FeatureDetector.create(FeatureDetector.FAST);
        //Imgproc.cvtColor(img1, img1, Imgproc.COLOR_RGBA2RGB);
        //Imgproc.cvtColor(img2, img2, Imgproc.COLOR_RGBA2RGB);

        DescriptorExtractor SurfExtractor = DescriptorExtractor
        .create(DescriptorExtractor.SURF);


        //extract keypoints
        MatOfKeyPoint keypoints, logoKeypoints;
        long time= System.currentTimeMillis();
        detector.detect(img1, keypoints);
        Log.d("LOG!", "number of query Keypoints= " + keypoints.size());
        detector.detect(img2, logoKeypoints);
        Log.d("LOG!", "number of logo Keypoints= " + logoKeypoints.size());
        Log.d("LOG!", "keypoint calculation time elapsed" + (System.currentTimeMillis() -time));

        //Descript keypoints
        long time2 = System.currentTimeMillis();
        Mat descriptors = new Mat();
        Mat logoDescriptors = new Mat();
        Log.d("LOG!", "logo type" + img2.type() + "  intype" + img1.type());
        SurfExtractor.compute(img1, keypoints, descriptors);
        SurfExtractor.compute(img2, logoKeypoints, logoDescriptors);
        Log.d("LOG!", "Description time elapsed" + (System.currentTimeMillis()- time2));

我显然无法将变量keypointslogoKeypoints 初始化为空,因为那时我会收到空​​指针异常。如何初始化它们?

【问题讨论】:

本 OpenCV 教程有望提供有关该主题的一些信息; goo.gl/gwN6e. try-catch 和 if-else 一样!如果抛出异常(catch 块),则完全出错了! @SatelliteSD - 我知道。这就是为什么我要问是否有更好的方法。 我不相信你需要,MatOfKeyPoint keypoints, logoKeypoints;很好。方法detector.detect(img1, keypoints);然后将使用找到的特征填充您的关键点。否则试试 MatOfKeyPoint keypoints= new MatOfKeyPoint(); @Emile 您能否提供正确的图像关键点比较代码? 【参考方案1】:

您应该明白,这不是一个简单的问题,您可以遵循不同的概念。我只会指出两个没有源代码的解决方案。

    直方图比较:您可以将两个图像都转换为灰度,在 [0,...,255] 范围内制作直方图。每个像素值都将被计算在内。然后使用两个直方图进行比较。如果像素强度的分布等于或高于某个阈值(可能是所有像素的 90%),您可以将此图像视为重复图像。但是:这是最简单的解决方案之一,如果任何图片的分布均等,则它是不稳定的。 Interest-Point-Detectors/-Descriptors:看看 SIFT/SURF 图像检测器和描述符。检测器将尝试确定图像中强度的唯一关键点。将在此位置 I(x,y) 处计算描述符。具有蛮力方法和欧几里德距离的普通匹配器可以使用它们的描述符匹配这些图像。如果图像是重复的,则给定匹配的比率应该非常高。这个解决方案很好实施,并且可能有足够的关于这个主题的教程。

我希望这会有所帮助。如果您有任何问题,请询问。

[UPDATE-1] C++ 教程:http://morf.lv/modules.php?name=tutorials&lasit=2#.UR-ewKU3vCk

一些 JavaCV 教程:http://code.google.com/p/javacv/w/list

[UPDATE-2] 这是使用默认参数的 SIFT-Detector 和 SIFT-Descriptor 的示例。 RANSAC 单应性阈值为 65,重投影误差(epsilon)为 10,启用交叉验证。您可以尝试计算匹配的。如果 Inliner-Outlier-Ratio 太高,您可能会将此对视为重复项。 例如:这些图像在 IMG1 中产生 180 个关键点,在 IMG2 中产生 198 个关键点。匹配的描述符有 163 个,其中只有 3 个是异常值。所以这给出了一个非常好的比率,这仅意味着这些图像可能是重复的。

[UPDATE-3] 我不明白你为什么可以初始化 MatOfKeypoints。 I've read the API 并且有一个公共构造函数。并且:您可以使用要分析的图像的垫子。这是非常好的。 =)

MatOfKeyPoint reference = new MatOfKeyPoint(matOfReferenceImage);

对于匹配,请使用BRUTEFORCE_SL2 Descriptor-Matcher,因为您需要 SURF 或 SIFT 的欧几里得距离。

【讨论】:

能否添加教程链接? 我添加了一些额外的材料。【参考方案2】:

使用cv2.absDiff 计算图片之间的差异,使用cv2.sumElems 获得所有像素差异的总和。

然后发明一个阈值来判断两张图像是否相似。

【讨论】:

你能解释一下吗?我必须为 absDiff 设置一个阈值。所以,这是有缺陷的。或者您可以告诉我最安全的阈值是多少? 首先,抱歉,您将不得不使用 cv.sum,因为 sumelems 似乎只是 python。其次,您需要 sum 的返回值的阈值!您可以将其包装在一个方法中,该方法根据 sum 的返回值和您的阈值返回 true 或 false。 你必须自己尝试一下。由于 sum 返回(对于我相信的每个通道 ARGB)一个代表 2 张图像偏差的值(由 absDiff 计算),因此 0 将是完美匹配。 可以输入确切的语法吗?很抱歉打扰了。刚接触 OpenCV 时,很难找到正确的语法。 opencv.willowgarage.com/documentation/cpp/… 和 docs.opencv.org/modules/core/doc/operations_on_arrays.html#sum【参考方案3】:

你可以试试下面的代码:

Mat img1 = Highgui.imread("mnt/sdcard/IMG-20121228.jpg");
Mat img2 = Highgui.imread("mnt/sdcard/IMG-20121228-1.jpg");
Mat result = new Mat();

Core.compare(img1,img2,result,Core.CMP_NE);

int val = Core.countNonZero(result);

if(val == 0) 
    //Duplicate Image
 else 
    //Different Image

这里的代码比较函数将比较两个图像,然后如果图像之间不存在相似性,则特定矩阵值将为 255,所有其他值将为零。然后您可以计算非零值的数量以确定图像是否相等。这仅适用于完全相同的图像。

如果你想比较忽略光效的图像,我建议你先生成边缘图像(使用 OpenCV 的 canny 函数),然后比较图像。

希望这个答案对你有帮助!!

【讨论】:

以上是关于Android中的OpenCV图像比较的主要内容,如果未能解决你的问题,请参考以下文章

深度图 - 带有 OpenCV 的 Android 中的立体图像

如何使用opencv比较存储在文件中的两个图像

避免 ImageView 中的图像缩放

将 YUV_420_888 中的图像从 Android 发送到 OpenCV Mat 中的 JNI 的最有效方法

如何在android手机中接收opencv图像

Android 中的 OpenCV 模板匹配示例