如何在iPhone上比较两张图片是否相似,且相似度达到一定百分比?

18

我基本上想要从iPhone或iPad 2相机中获取两张图片并将它们相互比较,以查看它们是否非常相似。显然由于光照等因素,图像永远不会完全相同,所以我希望检查大约90%的兼容性。

我在这里看到的所有类似问题都不是针对iOS的,或者是用于在图像中定位对象。我只想看看两张图片是否相似。

谢谢。

2个回答

10
作为一个快速简单的算法,我建议在每个图像中迭代约1%的像素,并直接将它们相互比较或保持一个运行平均值,然后在最后比较两个平均颜色值。您可以查看this answer,了解如何确定图像中给定位置的像素颜色的想法。您可能希望对其进行优化以更好地适应您的用例(重复查询相同的图像),但它应该提供一个很好的起点。然后,您可以使用大致如下的算法:
float numDifferences = 0.0f;
float totalCompares = width * height / 100.0f;
for (int yCoord = 0; yCoord < height; yCoord += 10) {
    for (int xCoord = 0; xCoord < width; xCoord += 10) {
        int img1RGB[] = [image1 getRGBForX:xCoord andY: yCoord];
        int img2RGB[] = [image2 getRGBForX:xCoord andY: yCoord];
        if (abs(img1RGB[0] - img2RGB[0]) > 25 || abs(img1RGB[1] - img2RGB[1]) > 25 || abs(img1RGB[2] - img2RGB[2]) > 25) {
            //one or more pixel components differs by 10% or more
            numDifferences++;
        }
    }
}

if (numDifferences / totalCompares <= 0.1f) {
    //images are at least 90% identical 90% of the time
}
else {
    //images are less than 90% identical 90% of the time
}

@SolidSnake4444 - img1GRB/img2RGB数组将由对getRGBForX:andY:的调用填充。它们将包含给定坐标处像素通道值(即红色分量的int,绿色的int和蓝色的int)。在比较中使用数组索引只是为了简洁起见。如果您喜欢,您可以轻松地执行类似于int img1Red = img1RGB [0];int img1Green = img1RGB [1];等操作。 - aroth
所以你正在添加三个整数值来表示颜色,然后从第二张图像的值之和中减去它,然后比较它是否大于25?如果我理解正确,为什么要与25进行比较? - SolidSnake4444
@SolidSnake4444 - 我正在比较三个整数颜色值,而不是将它们相加。我从第一个像素中获取红色、绿色和蓝色分量值,并将它们与第二个像素的红色、绿色和蓝色值(分别)进行比较。为了进行比较,我进行简单的减法,然后将结果与25进行比较。为什么是25?因为最大可能的差异是255,而您说任何在10%范围内的差异都应该算作匹配。而255的10%是25。因此,如果差异小于25,则该组件被视为匹配(在10%范围内),否则将被计为差异。 - aroth
1
你从哪里获取到宽度、高度和 getRGBForX 的? - Jesse Onolemen
大家好,有人在吗?请帮我一下。 - Dinesh Gurrapu
显示剩余5条评论

3

基于aroth的想法,这是我的完整实现。它检查一些随机像素是否相同。对于我所需要的功能来说,它运行得非常流畅。

- (bool)isTheImage:(UIImage *)image1 apparentlyEqualToImage:(UIImage *)image2 accordingToRandomPixelsPer1:(float)pixelsPer1
{
    if (!CGSizeEqualToSize(image1.size, image2.size))
    {
        return false;
    }

    int pixelsWidth = CGImageGetWidth(image1.CGImage);
    int pixelsHeight = CGImageGetHeight(image1.CGImage);

    int pixelsToCompare = pixelsWidth * pixelsHeight * pixelsPer1;

    uint32_t pixel1;
    CGContextRef context1 = CGBitmapContextCreate(&pixel1, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst);
    uint32_t pixel2;
    CGContextRef context2 = CGBitmapContextCreate(&pixel2, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst);

    bool isEqual = true;

    for (int i = 0; i < pixelsToCompare; i++)
    {
        int pixelX = arc4random() % pixelsWidth;
        int pixelY = arc4random() % pixelsHeight;

        CGContextDrawImage(context1, CGRectMake(-pixelX, -pixelY, pixelsWidth, pixelsHeight), image1.CGImage);
        CGContextDrawImage(context2, CGRectMake(-pixelX, -pixelY, pixelsWidth, pixelsHeight), image2.CGImage);

        if (pixel1 != pixel2)
        {
            isEqual = false;
            break;
        }
    }
    CGContextRelease(context1);
    CGContextRelease(context2);

    return isEqual;
}

用法:

[self isTheImage:image1 apparentlyEqualToImage:image2
accordingToRandomPixelsPer1:0.001]; // Use a value between 0.0001 and 0.005

根据我的性能测试,0.005(像素的0.5%)是您应该使用的最大值。如果需要更高的精度,请使用此方法比较整个图像。0.001似乎是一个安全、高效的值。对于大型图像(如0.5到2百万像素之间),我使用0.0001(0.01%),它非常快速和可靠,从未出错。
当然,错误率将取决于您使用的图像类型。我正在使用UIWebView截图,0.0001表现很好,但如果您比较真实的照片(甚至比较一个随机像素),可能可以使用更少的值。如果您处理非常相似的计算机设计图像,则绝对需要更高的精度。
请注意:我始终比较ARGB图像,不考虑alpha通道。如果您的情况不完全一样,您可能需要进行调整。

这看起来很有前途。我会在周末研究一下,看看它是否能按照我需要的方式工作。根据你所说的,我认为它会。 - SolidSnake4444
1
现在我重新阅读了你的问题,我不确定它是否适用于你想要的。即使两张照片在人眼中看起来相似,所有像素可能都是不同的。计算平均颜色似乎是一个更好的方法。 - cprcrack
你有一个代码示例吗?关于如何到达照片的像素或RGB级别,我似乎很难理解如何编写代码。 - SolidSnake4444
@cprcrack 在视图加载时获取 image1、image2 时出现了未声明的标识符错误。您能否分享更多的代码,以便更容易理解? - Saad Chaudhry
我已经实现了这段代码,我在这里遇到的问题是,该算法完美地找到了来自WhatsApp或Facebook并保存在我的相册中的相似图像,但当我从我的相机拍摄相似的照片时(我有20张用于测试),它找不到任何一张。此外,我必须为每个手机单独设置参数(这是错误的方法),我该如何改进这段代码以解决我面临的问题? - Omkar Jadhav
@cprcrack,我注意到你的算法也适用于较小的图像,所以我猜这就是它比较WhatsApp接收到的已经压缩过的图像的原因。但是当出现更大尺寸的图像(例如相机拍摄的图像)时,它就无法处理了。 - Omkar Jadhav

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