Как сравнить два объекта 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:в противном случае мы могли бы сравнить данные.

Правильный ответ зависит от того, "Какое сравнение вы хотите сделать?".

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

Мое предпочтительное (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 для идентификации объектов на более поздних этапах вашей программы. Просто установите целочисленное значение, и все готово

Другие вопросы по тегам