安卓中的OpenCV图像比较

20

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

我有这两个图片 Img1Img2:

enter image description here enter image description here

当我在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]

第二个和第三个数字的变化虽然不大,但与比较同一大小的图像相比仍然很大。然而,第一个数字受到最大的变化。

有什么最好最快的方法来比较两个图像的内容?

[编辑]

我正在使用我在这里找到的代码。

我无法弄清楚如何初始化MatOfKeyPoint变量keypointslogoKeypoints。 这是我的代码片段:

           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初始化为null,因为我会收到一个空指针异常。那么我该如何初始化它们呢?


这个OpenCV教程希望能够提供一些关于该主题的信息;http://goo.gl/gwN6e。 - harism
2
try-catch不同于if-else!如果抛出异常(catch块),则表示出现了严重问题! - sschrass
1
我认为你不需要这样做:MatOfKeyPoint keypoints, logoKeypoints; 就可以了。使用 detector.detect(img1, keypoints); 方法,就可以将找到的特征点填充到 keypoints 中。否则,可以尝试使用 MatOfKeyPoint keypoints = new MatOfKeyPoint(); - Emile
@Emile,你能贴出图像关键点比较的正确代码吗? - Karthik Balakrishnan
您还需要帮助吗?有什么问题吗? - Rui Marques
显示剩余4条评论
3个回答

30
您需要理解这不是一个简单的问题,有不同的概念可以遵循。我将仅指出两种没有源代码的解决方案。
1. 直方图比较:您可以将两个图像转换为灰度,并在[0,...,255]范围内制作直方图。每个像素值都将被计算。然后使用两个直方图进行比较。如果像素强度的分布相等或高于某个阈值(也许是所有像素的90%),则可以将这些图像视为重复。但是:这是最简单的解决方案之一,如果任何图片具有相等的分布,则不稳定。
2. 兴趣点检测器/描述符:查看SIFT/SURF图像检测器和描述符。检测器将尝试确定图像中强度的唯一关键点。描述符将在此位置I(x,y)上计算。使用暴力法和欧几里得距离的普通匹配器可以使用它们的描述符来匹配这些图像。如果图像是重复的,则给定匹配的比率应该非常高。这种解决方案易于实现,并且可能有足够的关于此主题的教程。
希望这有所帮助。如果您有问题,请问。

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

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

[更新-2] 这里有一个使用默认参数的SIFT检测器和SIFT描述符的示例。RANSAC阈值为65,重投影误差(epsilon)为10,启用交叉验证。您可以尝试计算匹配项。如果内点外点比率过高,则可以将此对视为重复项。 Matching img1 and img2 using SIFT-detector and SIFT-descriptor 例如:这些图像在IMG1中产生180个关键点,在IMG2中产生198个关键点。匹配的描述符有163个,其中只有3个是异常值。因此,这给出了一个非常好的比率,这可能意味着这些图像可能是重复的。

[更新-3] 我不明白为什么可以初始化MatOfKeypoints。我已经阅读了API并且有一个公共构造函数。而且:您可以使用要分析的图像的Mat。这非常好。=)

MatOfKeyPoint reference = new MatOfKeyPoint(matOfReferenceImage);

如果需要使用SURF或SIFT算法进行匹配,请使用BRUTEFORCE_SL2 Descriptor-Matcher,因为您需要欧几里得距离。


请问您能否添加教程链接? - Karthik Balakrishnan
1
我添加了一些额外的材料。 - Mr.Mountain

1
使用cv2.absDiff计算图片之间的差异,使用cv2.sumElems获取所有像素差异的总和。然后创造一个阈值,通过该阈值来判断两张图片是否相似。

你能解释一下吗?我必须为absDiff设置一个阈值。所以,这是有缺陷的。或者你可以告诉我最安全的阈值是多少? - Karthik Balakrishnan
首先,很抱歉您必须使用cv.sum,因为sumelems似乎只适用于Python。其次,您需要针对sum的返回值设置阈值!您可以将其包装在一个方法中,该方法基于sum的返回值和您的阈值返回true或false。 - sschrass
那里你将不得不自己尝试。由于sum返回(对于每个通道ARGB,我相信)表示两个图像之间偏差的值(通过absDiff计算),0将是完美匹配。 - sschrass
请问能否提供完整的语法?很抱歉打扰了。对于刚接触OpenCV的人来说,很难找到正确的语法。 - Karthik Balakrishnan
http://opencv.willowgarage.com/documentation/cpp/core_operations_on_arrays.html#absdiff 和 http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#sum - sschrass
你能为我的代码中的img1和img2编写sumElems的代码吗?我已经写了Scalar blah= Core.sumElems(img2);,显然是不正确的。你能创建一个返回总和值的函数吗? - Karthik Balakrishnan

-1
你可以尝试以下代码:
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函数),然后再比较图像。

希望这个答案能帮到您!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接