Как сравнить два объекта UIImage
Я разрабатываю одно приложение. В котором я использую imageviews
.SO, прежде чем менять UIImageview
изображение мне нужно взять это изображение в UIimage
объективировать и сравнивать с другим UIImage
Объектом для нахождения обоих являются Сэм или нет. Поэтому, пожалуйста, скажите мне, как это сделать.
13 ответов
Один из способов - сначала преобразовать их в данные изображения, а затем сравнить их.
- (BOOL)image:(UIImage *)image1 isEqualTo:(UIImage *)image2
{
NSData *data1 = UIImagePNGRepresentation(image1);
NSData *data2 = UIImagePNGRepresentation(image2);
return [data1 isEqual:data2];
}
Быстрая реализация ответа @Simon:
func image(image1: UIImage, isEqualTo image2: UIImage) -> Bool {
let data1: NSData = UIImagePNGRepresentation(image1)!
let data2: NSData = UIImagePNGRepresentation(image2)!
return data1.isEqual(data2)
}
Или путем расширения UIImage на основе предложения @nhgrif:
import UIKit
extension UIImage {
func isEqualToImage(image: UIImage) -> Bool {
let data1: NSData = UIImagePNGRepresentation(self)!
let data2: NSData = UIImagePNGRepresentation(image)!
return data1.isEqual(data2)
}
}
Обновленное решение Марка Тикнера до 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()
}
}
Когда оба используют [UIImage imageNamed:]
, мы можем использовать isEqual:
в противном случае мы могли бы сравнить данные.
Правильный ответ зависит от того, "Какое сравнение вы хотите сделать?".
- Самый простой способ - просто сравнить данные.
- Если вы хотите узнать , были ли изображения созданы из одного локального файла - вы можете использовать -isEqual: (но есть опасный способ, потому что я не уверен, что произойдет, если кэш изображений по какой-то причине очистится).
- Сложный способ - обеспечить сопоставление по пикселям (конечно, система будет тратить на это больше времени). Я не могу предоставить код из библиотеки нашей компании по юридической причине:(
Но вы можете проверить хороший пример на 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;
}
Мое предпочтительное (Swift) решение
import UIKit
func ==(lhs: UIImage, rhs: UIImage) -> Bool
{
guard let data1 = UIImagePNGRepresentation(lhs),
data2 = UIImagePNGRepresentation(rhs)
else { return false }
return data1.isEqual(data2)
}
Преобразование изображений в JPG / PNG или использование идентификаторов специальных возможностей - это либо дорогостоящая операция, либо хрупкая и подверженная сбоям.
Здесь я следую предложению Apple по следующей ссылке:
Метод isEqual (:) - это единственный надежный способ определить, содержат ли два изображения одинаковые данные изображения. Создаваемые вами объекты изображения могут отличаться друг от друга, даже если вы инициализируете их одними и теми же кэшированными данными изображения. Единственный способ определить их равенство - использовать метод isEqual (:), который сравнивает фактические данные изображения. В листинге 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
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
}
Я сделал некоторые изменения в ответе Марка и использовал 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)
}
}
Свифт 3
Есть два пути. Подобно:-
1) Используйте функцию isEqual().
self.image?.isEqual(UIImage(named: "add-image"))
2) Используйте accessibilityIdentifier
Установите accessibilityIdentifier как Имя изображения
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()
Swift 5.x
Вы можете просто использовать
let image: UIImage!
let anotherImage: UIImage!
image == anotherImage
Сравните размер изображения изначально
Для менее дорогого метода сначала сравните размер изображения. Даже если внутри изображения есть небольшое изменение, размер будет другим.
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.
}
Успешно протестировано при сравнении изображений из одного источника (того же размера, формата и т. Д.).
Этот метод прекрасно работает:
func isEqualImages(image1: UIImage, image2: UIImage) -> Bool {
let data1: Data? = UIImagePNGRepresentation(image1)
let data2: Data? = UIImagePNGRepresentation(image2)
return data1 == data2
}
Мне нужно было обнаружить различия в кадрах видеопотока и установить порог этой разницы, чтобы решить что-то сделать в моем коде. Вы можете использовать аналогичное сравнение пикселей, чтобы посмотреть pngData UIImage.
см. мой ответ здесь
Быстрое и быстрое решение
extension UIImage: Equatable {
static func ==(lhs: UIImage, rhs: UIImage) -> Bool {
return lhs.pngData() == rhs.pngData()
}
}
Swift5
func checkequal(img1: UIImage, imag2:UIImage) -> Bool {
let data1 = img1.pngData()
let data2 = imag2.pngData()
return data1 == data2
}
Я не уверен в сравнении данных UIImage, так как это будет очень дорого. Что вы можете сделать, это создать подкласс Uimage и добавить свойство тега самостоятельно, а затем сравнить тег перед изменением изображения. назначать
Нам нужно использовать метод isEqualToData в Objective-C. В документе Apple говорится, что
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];
}
Если вам известно одно имя изображения, оно поможет вам....
CGImageRef cgImage = [imageView.image CGImage];
if (cgImage == [UIImage imageNamed:@"imagename.png"].CGImage) {
// True Statement
}
Также вы можете использовать свойство tag для идентификации объектов на более поздних этапах вашей программы. Просто установите целочисленное значение, и все готово