如何比较两个 UIImage 对象

我正在开发一个应用程序。因为我使用的是 imageviews.SO,在改变 UIImageview图像之前,我需要在 UIimage目标中获取该图像,并与另一个 UIImage对象进行比较,以找出两者是否都是 sam。所以请告诉我该怎么做。

60085 次浏览

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

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


return [data1 isEqual:data2];
}

您也可以使用‘ tag’属性来识别程序后期阶段的对象。只要设置整数值就可以了

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

我不确定是否要比较 UIImage 数据,因为它的成本太高了。您可以做的是子类化 Uimage 并添加自己的标记属性,然后在更改图像之前比较标记。分配

首先比较图像的大小

对于一个不太昂贵的方法,首先比较图像的大小。即使图像内部有一个很小的变化,大小也会有所不同。

NSData *data1 = UIImagePNGRepresentation(image1);
NSData *data2 = UIImagePNGRepresentation(image2);


if(data1.length == data2.length) {
// if required, compare the data to confirm it
if(data1 isEqual:data2) {
// images are exactly same
} else {
// even though size is same images are different
}
} else {
// images are different.
}

成功测试比较来自同一来源(相同尺寸,格式等)的图像。

快速实现@Simon 的回答:

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

或者根据@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)
}


}

我的首选(迅捷)解决方案

import UIKit


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


return data1.isEqual(data2)
}

正确的答案取决于“你想做什么样的比较?”。

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

但是你可以在这里查看 facebook ios 快照测试案例项目的一个好例子: 链接到所需的文件。 您可以使用性能测试来度量处理时间。

为了大正义,我将从下面拷贝代码:

- (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;
}

Swift 3

有两种方法。比如:-

1)使用 isequals ()函数。

 self.image?.isEqual(UIImage(named: "add-image"))

2)使用 AccessibilityIdentifier

将 accessibilityIdentifier 设置为 image Name

myImageView.image?.accessibilityIdentifier = "add-image"

然后使用以下代码。

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

方法的调用方式识别

myImageView.getFileName()

如果您知道一个图像名称,它将帮助您... ..。

CGImageRef cgImage = [imageView.image CGImage];
if (cgImage == [UIImage imageNamed:@"imagename.png"].CGImage) {
// True Statement
}

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

在这里,我遵循苹果在 以下连结会议上提出的建议:

Isequals (< em > :)方法是确定是否 两个图像包含相同的图像数据 可能彼此不同,即使在初始化它们时使用 相同的缓存图像数据。确定它们相等的唯一方法 是使用 isequals ( :)方法,该方法比较实际的图像 清单1说明了正确和不正确的比较方法 影像。

为了简化操作,我创建了以下用于进行比较的扩展,这样就可以避免转换第一个图像的问题:

import UIKit


extension UIImage {
func isEqual(to image: UIImage) -> Bool {
return isEqual(image)
}
}

有了这个,我现在可以设置一个例子来比较它对一对图像:

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

这种方法很有效:

func isEqualImages(image1: UIImage, image2: UIImage) -> Bool {
let data1: Data? = UIImagePNGRepresentation(image1)
let data2: Data? = UIImagePNGRepresentation(image2)
return data1 == data2
}

我对 Mark 的答案做了一些修改,使用 Data 和 element 来代替 NSData 和 isequals。

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)
}
}

迅捷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
}

更新了 Mark Tickner 对 Swift 4的解决方案

import UIKit


extension UIImage {


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


}

这两个变量可能有点过了,但它们可能有助于向一些新手解释这个问题。可以缩写为:

import UIKit


extension UIImage {


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


}

斯威夫特5. x

你可以用

let image: UIImage!
let anotherImage: UIImage!


image == anotherImage

简捷快捷的解决方案

extension UIImage: Equatable {
static func ==(lhs: UIImage, rhs: UIImage) -> Bool {
return lhs.pngData() == rhs.pngData()
}
}

我需要检测视频提要帧内的差异和阈值的差异,以便决定在我的代码中做一些事情。可以使用类似的像素比较来查看 UIImage 的 pngData。

看我的答案

我们需要在 Objective-C 中使用 isEqualToData 方法

Two data objects are equal if they hold the same number of bytes, and if the bytes at the same position in the objects are the same.

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


return [data1 isEqualToData:data2];
}