How to compare two UIImage objects

Ios

Ios Problem Overview


I am developing one application.In that i am using the imageviews.SO before changeing the UIImageview image I need to take that image in UIimage obejct and compare with another UIImage object for finding both are sam or not. So please tell me how to do that one.

Ios Solutions


Solution 1 - Ios

One way is to convert them to image data first, and then compare that.

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

    return [data1 isEqual:data2];
}

Solution 2 - Ios

A Swift implementation of @Simon's answer:

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

Or by extending UIImage based on @nhgrif's suggestion:

import UIKit

extension UIImage {

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

}

Solution 3 - Ios

Updated Mark Tickner's solution to Swift 4

import UIKit

extension UIImage {

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

}

The two variables are probably overkill, but they might help explain to someone new to this. Could shorten to:

import UIKit

extension UIImage {

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

}

Solution 4 - Ios

when both using [UIImage imageNamed:], we can use isEqual:, otherwise we could compare the data.

Solution 5 - Ios

My preferred (Swift) solution

import UIKit

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

Solution 6 - Ios

The right answer depends on "What kind of comparison you want to do?".

  1. The easiest way is just to compare data.
  2. If you want to know whether image were created from one local file – you can use -isEqual: (but there is an dangerous way, because I'm not sure, what happens if image cache clear for some reason).
  3. The hard way is provide per-pixel comparison (surely, system will spend more time on it). I can't provide the code from our company's library because of legal reason :(

But you can check good example on facebook's ios-snapshot-test-case project here: link right to the needed file. You can use performance tests to measure process time.

For the Great Justice I'll copy code from there below:

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

Solution 7 - Ios

Converting the images to JPG / PNG, or relying on accessibility identifiers is either an expensive operation, or fragile & prone to failure.

Here, I follow the suggestion provided by Apple at the following link:

> The isEqual(:) method is the only reliable way to determine whether > two images contain the same image data. The image objects you create > may be different from each other, even when you initialize them with > the same cached image data. The only way to determine their equality > is to use the isEqual(:) method, which compares the actual image > data. Listing 1 illustrates the correct and incorrect ways to compare > images.

To simplify things, I create the following extension for doing comparisons, so that I can avoid the issue of converting the first image:

import UIKit

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

With this, I can now setup an example to compare it on a pair of images:

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

Solution 8 - Ios

I did some changes to Mark's answer and used Data and elementsEqual instead NSData and 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)
    }
}

Solution 9 - Ios

Swift 4.x version of Facebook's comparison algorithm:

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

Solution 10 - Ios

> Swift 3

There is two ways. Like:-

  1. Use isEqual() function.

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

  2. Use accessibilityIdentifier

Set the accessibilityIdentifier as image Name

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

Then Use the following code.

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

Finally, The calling way of method to identify

myImageView.getFileName()

Solution 11 - Ios

Swift 5.x

You can just use

let image: UIImage!
let anotherImage: UIImage!

image == anotherImage

Solution 12 - Ios

Compare the image size initially

For a less expensive method, compare the image size initially. Even if there is a small change inside an image, the size will be different.

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

Successfully tested in comparing images from same source (same dimension, format etc).

Solution 13 - Ios

This method works great:

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

Solution 14 - Ios

Short-hand and Swifty solution

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

Solution 15 - Ios

I needed to detect differences within the video feed frames and threshold that difference in order to decide to do something within my code. You can use a similar pixel comparison to look at the pngData of the UIImage.

see my answer here

Solution 16 - Ios

i not sure about comparing the UIImage data as it would be prettry expensive. What you could do is subclass Uimage and add tag property your self and then compare tag before changing the image. assign

Solution 17 - Ios

if one image name is known to you,it will help you....

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

Solution 18 - Ios

Well you can also the 'tag' property to identify the objects at latter stages of your program. Just set the integer value and you are good to go

Solution 19 - Ios

We need to use isEqualToData method in Objective-C. The Apple document states that

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1498119View Question on Stackoverflow
Solution 1 - IosSimonView Answer on Stackoverflow
Solution 2 - IosMark TicknerView Answer on Stackoverflow
Solution 3 - IosGreg HilstonView Answer on Stackoverflow
Solution 4 - IosxeonwellView Answer on Stackoverflow
Solution 5 - IosAustinView Answer on Stackoverflow
Solution 6 - IosVladlexView Answer on Stackoverflow
Solution 7 - IosCodeBenderView Answer on Stackoverflow
Solution 8 - IosMatheus CamposView Answer on Stackoverflow
Solution 9 - IosVladView Answer on Stackoverflow
Solution 10 - IosAbdul HameedView Answer on Stackoverflow
Solution 11 - IosNike KovView Answer on Stackoverflow
Solution 12 - IosAnulal SView Answer on Stackoverflow
Solution 13 - IosmaxwellView Answer on Stackoverflow
Solution 14 - IosOleksandr DanylenkoView Answer on Stackoverflow
Solution 15 - Iosuser3325025View Answer on Stackoverflow
Solution 16 - Iosuser3445228View Answer on Stackoverflow
Solution 17 - IosemrazView Answer on Stackoverflow
Solution 18 - IosAzerueView Answer on Stackoverflow
Solution 19 - IosOhmyView Answer on Stackoverflow