如何比较两个UIImage对象

use*_*119 59 ios

我正在开发一个应用程序.因为我在imageviews更改UIImageview图像之前使用.SO 我需要在UIimageobejct中获取该图像并与另一个UIImage对象进行比较以找到它们是否为sam.所以请告诉我如何做到这一点.

Sim*_*mon 122

一种方法是首先将它们转换为图像数据,然后进行比较.

- (BOOL)image:(UIImage *)image1 isEqualTo:(UIImage *)image2
{
    NSData *data1 = UIImagePNGRepresentation(image1);
    NSData *data2 = UIImagePNGRepresentation(image2);

    return [data1 isEqual:data2];
}
Run Code Online (Sandbox Code Playgroud)

  • 这更好地作为一个类别实现 (7认同)
  • 比较是吃饭的时间. (7认同)

Mar*_*ner 20

一个Swift实现@ Simon的答案:

func image(image1: UIImage, isEqualTo image2: UIImage) -> Bool {
    let data1: NSData = UIImagePNGRepresentation(image1)!
    let data2: NSData = UIImagePNGRepresentation(image2)!
    return data1.isEqual(data2)
}
Run Code Online (Sandbox Code Playgroud)

或者根据@nhgrif的建议扩展UIImage:

import UIKit

extension UIImage {

    func isEqualToImage(image: UIImage) -> Bool {
        let data1: NSData = UIImagePNGRepresentation(self)!
        let data2: NSData = UIImagePNGRepresentation(image)!
        return data1.isEqual(data2)
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 现在我唯一的评论是,不是强行解包和崩溃,如果要么是'nil`,我们应该返回`false`.`guard let data1 = UIImagePNGRepresentation(self),data2 = UIImagePNGRepresentation(image)else {return false} return data1 == data2` (2认同)

小智 8

当两者都使用时[UIImage imageNamed:],我们可以使用isEqual:,否则我们可以比较数据.


Aus*_*tin 7

我首选的 (Swift) 解决方案

import UIKit

func ==(lhs: UIImage, rhs: UIImage) -> Bool
{
  guard let data1 = UIImagePNGRepresentation(lhs),
            data2 = UIImagePNGRepresentation(rhs)
    else { return false }

  return data1.isEqual(data2)
}
Run Code Online (Sandbox Code Playgroud)


Gre*_*ton 7

将Mark Tickner的解决方案更新为Swift 4

import UIKit

extension UIImage {

    func isEqualToImage(_ image: UIImage) -> Bool {
        let data1 = self.pngData()
        let data2 = image.pngData()
        return data1 == data2
    }

}
Run Code Online (Sandbox Code Playgroud)

这两个变量可能是过大的,但它们可能有助于向新手解释。可以缩短为:

import UIKit

extension UIImage {

    func isEqualToImage(_ image: UIImage) -> Bool {
        return self.pngData() == image.pngData()
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 我已经这样做了,但它对我不起作用。这就是我告诉你的原因。你能帮我解决这个问题吗? (2认同)

Vla*_*lex 6

正确的答案取决于"你想做什么样的比较?".

  1. 最简单的方法就是进行数据比较.
  2. 如果你想知道图像是否是从一个本地文件创建的 - 你可以使用-isEqual :(但是有一种危险的方式,因为我不确定,如果由于某种原因图像缓存清除会发生什么).
  3. 困难的方法是提供每像素比较(当然,系统会花更多的时间在它上面).由于法律原因,我无法提供我们公司图书馆的代码:(

但是你可以在这里查看facebook的ios-snapshot-test-case项目中的好例子:链接到所需文件.您可以使用性能测试来测量处理时间.

对于伟大的正义,我将从下面复制代码:

- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
{
  NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");

  CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
  CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));

  // The images have the equal size, so we could use the smallest amount of bytes because of byte padding
  size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
  size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
  void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
  void *imagePixels = calloc(1, referenceImageSizeBytes);

  if (!referenceImagePixels || !imagePixels) {
    free(referenceImagePixels);
    free(imagePixels);
    return NO;
  }

  CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
                                                             referenceImageSize.width,
                                                             referenceImageSize.height,
                                                             CGImageGetBitsPerComponent(self.CGImage),
                                                             minBytesPerRow,
                                                             CGImageGetColorSpace(self.CGImage),
                                                             (CGBitmapInfo)kCGImageAlphaPremultipliedLast
                                                             );
  CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
                                                    imageSize.width,
                                                    imageSize.height,
                                                    CGImageGetBitsPerComponent(image.CGImage),
                                                    minBytesPerRow,
                                                    CGImageGetColorSpace(image.CGImage),
                                                    (CGBitmapInfo)kCGImageAlphaPremultipliedLast
                                                    );

  if (!referenceImageContext || !imageContext) {
    CGContextRelease(referenceImageContext);
    CGContextRelease(imageContext);
    free(referenceImagePixels);
    free(imagePixels);
    return NO;
  }

  CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
  CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);

  CGContextRelease(referenceImageContext);
  CGContextRelease(imageContext);

  BOOL imageEqual = YES;

  // Do a fast compare if we can
  if (tolerance == 0) {
    imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
  } else {
    // Go through each pixel in turn and see if it is different
    const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;

    FBComparePixel *p1 = referenceImagePixels;
    FBComparePixel *p2 = imagePixels;

    NSInteger numDiffPixels = 0;
    for (int n = 0; n < pixelCount; ++n) {
      // If this pixel is different, increment the pixel diff count and see
      // if we have hit our limit.
      if (p1->raw != p2->raw) {
        numDiffPixels ++;

        CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
        if (percent > tolerance) {
          imageEqual = NO;
          break;
        }
      }

      p1++;
      p2++;
    }
  }

  free(referenceImagePixels);
  free(imagePixels);

  return imageEqual;
}
Run Code Online (Sandbox Code Playgroud)


Abd*_*eed 5

雨燕3

有两种方法。喜欢:-

1)使用 isEqual() 函数。

 self.image?.isEqual(UIImage(named: "add-image"))
Run Code Online (Sandbox Code Playgroud)

2)使用可访问性标识符

将accessibilityIdentifier设置为图像名称

myImageView.image?.accessibilityIdentifier = "add-image"
Run Code Online (Sandbox Code Playgroud)

然后使用以下代码。

extension UIImageView
{
func getFileName() -> String? {
// First set accessibilityIdentifier of image before calling.
let imgName = self.image?.accessibilityIdentifier
return imgName
}
}
Run Code Online (Sandbox Code Playgroud)

最后,方法的调用方式来识别

myImageView.getFileName()
Run Code Online (Sandbox Code Playgroud)


Cod*_*der 5

将图像转换为JPG / PNG或依赖于可访问性标识符是一项昂贵的操作,或者脆弱且容易失败。

在这里,我遵循Apple在以下链接中提供的建议:

isEqual(:)方法是确定两个图像是否包含相同图像数据的唯一可靠方法。即使使用相同的缓存图像数据初始化它们,您创建的图像对象也可能彼此不同。确定其相等性的唯一方法是使用isEqual( :)方法,该方法比较实际的图像数据。清单1展示了比较图像的正确和不正确的方法。

为简化起见,我创建了以下扩展进行比较,从而避免了转换第一个图像的问题:

import UIKit

extension UIImage {
    func isEqual(to image: UIImage) -> Bool {
        return isEqual(image)
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这个,我现在可以设置一个示例在一对图像上进行比较:

let imageA = UIImage(named: "a")!
let imageB = UIImage(named: "b")!
let imageC = UIImage(named: "a")!

print(imageA.isEqual(to: imageA)) // true
print(imageA.isEqual(to: imageC)) // true
print(imageA.isEqual(to: imageB)) // false
Run Code Online (Sandbox Code Playgroud)

  • 尽管有Apple的文档,但我发现`isEqual`不能正确比较Obj-C中两个UIImage实例的数据。对于两个包含相同数据的UIImage实例,它返回false。我发现将UIImage实例转换为PNG并比较原始数据(如Simon的回答所示)是我发现与Obj-C正确进行比较的唯一方法。 (4认同)

小智 5

我对Mark的答案进行了一些更改,并使用Data和elementsEqual代替了NSData和isEqual。

extension UIImage {
    func isEqual(to image: UIImage) -> Bool {
        guard let data1: Data = UIImagePNGRepresentation(self),
            let data2: Data = UIImagePNGRepresentation(image) else {
                return false
        }
        return data1.elementsEqual(data2)
    }
}
Run Code Online (Sandbox Code Playgroud)


Vla*_*lad 5

Swift 4.x 版 Facebook 的比较算法:

/// Value in range 0...100 %
typealias Percentage = Float

// See: https://github.com/facebookarchive/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/Categories/UIImage%2BCompare.m
private func compare(tolerance: Percentage, expected: Data, observed: Data) throws -> Bool {
    guard let expectedUIImage = UIImage(data: expected), let observedUIImage = UIImage(data: observed) else {
        throw Error.unableToGetUIImageFromData
    }
    guard let expectedCGImage = expectedUIImage.cgImage, let observedCGImage = observedUIImage.cgImage else {
        throw Error.unableToGetCGImageFromData
    }
    guard let expectedColorSpace = expectedCGImage.colorSpace, let observedColorSpace = observedCGImage.colorSpace else {
        throw Error.unableToGetColorSpaceFromCGImage
    }
    if expectedCGImage.width != observedCGImage.width || expectedCGImage.height != observedCGImage.height {
        throw Error.imagesHasDifferentSizes
    }
    let imageSize = CGSize(width: expectedCGImage.width, height: expectedCGImage.height)
    let numberOfPixels = Int(imageSize.width * imageSize.height)

    // Checking that our `UInt32` buffer has same number of bytes as image has.
    let bytesPerRow = min(expectedCGImage.bytesPerRow, observedCGImage.bytesPerRow)
    assert(MemoryLayout<UInt32>.stride == bytesPerRow / Int(imageSize.width))

    let expectedPixels = UnsafeMutablePointer<UInt32>.allocate(capacity: numberOfPixels)
    let observedPixels = UnsafeMutablePointer<UInt32>.allocate(capacity: numberOfPixels)

    let expectedPixelsRaw = UnsafeMutableRawPointer(expectedPixels)
    let observedPixelsRaw = UnsafeMutableRawPointer(observedPixels)

    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
    guard let expectedContext = CGContext(data: expectedPixelsRaw, width: Int(imageSize.width), height: Int(imageSize.height),
                                          bitsPerComponent: expectedCGImage.bitsPerComponent, bytesPerRow: bytesPerRow,
                                          space: expectedColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
        expectedPixels.deallocate()
        observedPixels.deallocate()
        throw Error.unableToInitializeContext
    }
    guard let observedContext = CGContext(data: observedPixelsRaw, width: Int(imageSize.width), height: Int(imageSize.height),
                                          bitsPerComponent: observedCGImage.bitsPerComponent, bytesPerRow: bytesPerRow,
                                          space: observedColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
        expectedPixels.deallocate()
        observedPixels.deallocate()
        throw Error.unableToInitializeContext
    }

    expectedContext.draw(expectedCGImage, in: CGRect(origin: .zero, size: imageSize))
    observedContext.draw(observedCGImage, in: CGRect(origin: .zero, size: imageSize))

    let expectedBuffer = UnsafeBufferPointer(start: expectedPixels, count: numberOfPixels)
    let observedBuffer = UnsafeBufferPointer(start: observedPixels, count: numberOfPixels)

    var isEqual = true
    if tolerance == 0 {
        isEqual = expectedBuffer.elementsEqual(observedBuffer)
    } else {
        // Go through each pixel in turn and see if it is different
        var numDiffPixels = 0
        for pixel in 0 ..< numberOfPixels where expectedBuffer[pixel] != observedBuffer[pixel] {
            // If this pixel is different, increment the pixel diff count and see if we have hit our limit.
            numDiffPixels += 1
            let percentage = 100 * Float(numDiffPixels) / Float(numberOfPixels)
            if percentage > tolerance {
                isEqual = false
                break
            }
        }
    }

    expectedPixels.deallocate()
    observedPixels.deallocate()

    return isEqual
}
Run Code Online (Sandbox Code Playgroud)