Generate hash from UIImage
Solution 1
I wound up using the following code to accomplish the task. Note that this requires that you import <CommonCrypto/CommonDigest.h>
:
unsigned char result[CC_MD5_DIGEST_LENGTH];
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(inImage)];
CC_MD5([imageData bytes], [imageData length], result);
NSString *imageHash = [NSString stringWithFormat:
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
Solution 2
A less than optimal solution:
[ UIImagePNGRepresentation( uiImage1 ) isEqualToData:
UIImagePNGRepresentation( uiImage2 ) ];
This basically compares the PNG encoded data of the 2 images. Since image similarity is a complex subject, better and faster solutions can be devised based on what exactly the end goal is (i.e. are you looking to compare images, pixel by pixel, or just approximate similarity, which could use a downsampled version of the source image, etc).
Solution 3
UIImage Hashing
Swift Code for hashing a UIImage with the SHA256 algorithm in swift 4.2. There are other algorithms you can use, which are may faster or generate less duplicates but this one is good enough for most usecases.
Just drop the code somewhere in your Project and use it like this: yourUIImage.sha256()
extension UIImage{
public func sha256() -> String{
if let imageData = cgImage?.dataProvider?.data as? Data {
return hexStringFromData(input: digest(input: imageData as NSData))
}
return ""
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
Bridging Header
By the way you need a Bridging Header that imports CommonCrypto. If you don't have one follow these steps:
- Create new File -> Header File -> Save as
BridgingHeader
- In Build Settings -> Objective-C Bridging Header -> add
ProjectName/BridgingHeader.h
- Put
#import <CommonCrypto/CommonHMAC.h>
in your Header File
Duplicate Probability
The duplicate probability is low, but you can upgrade to SHA512 relatively easy if you got huge datasets:
The Duplicate Probability is about:
Solution 4
more elegant code below
+(NSString *)MD5HexDigest:(NSData *)input {
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(input.bytes, (unsigned int)input.length, result);
NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
for (int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
[ret appendFormat:@"%02x",result[i]];
}
return ret;
}
Solution 5
This updated snippet should work. Thanks to andreas's answer for the outline.
// Usage
image.sha256
// extensions
import CommonCrypto
extension UIImage {
var sha256: String {
guard let imageData = jpegData(compressionQuality: 1) else { return "" }
return imageData.digest.hexString
}
}
extension Data {
var digest: Data {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(bytes, UInt32(count), &hash)
return Data(bytes: hash, count: digestLength)
}
var hexString: String {
let hexString = map { String(format: "%02.2hhx", $0) }.joined()
return hexString
}
}
Comments
-
Reed Olsen about 2 years
I'm trying to compare two UIImages from the file system to see if they are the same. Obviously, I can't use NSObject's hash method, since this returns a hash of the object, and not the actual image data.
I found code generate an MD5 hash from a string, but I haven't discovered how to implement it for a UIImage.
How should I go about hashing a UIImage? Or is my method for comparing to images totally off?
-
david about 12 yearsMaybe a stupid question but does that require the "I added cryptography" check in the App Store submission?
-
Reed Olsen about 12 yearsI can't speak for all cases, however, when I submitted my app I didn't check that checkbox. I just used the hash to compare two images, not for any sort of data encryption.
-
Lou Zell about 12 yearsFor me, this generated a different hash on each pass. Using [imageData bytes] as the first argument to CC_MD5 seems to fix it.
-
Aaron Brager about 11 yearsYou should use
CC_MD5_DIGEST_LENGTH
instead of16
in case the value ever changes. -
SDJMcHattie over 9 yearsNow that you've used the CC_MD5_DIGEST_LENGTH for the array length, you should also update the way you format the string to be adaptable. I would suggest this code: NSString *imageHash = @""; for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { imageHash = [NSString stringWithFormat:@"%@%02X", imageHash, result[i]]; }
-
David Berry over 9 yearsNote that this mechanism is somewhat inefficient for comparing UIImage from the file system as it reads it, rasterizes it, then reencodes it. Better to just go to the file itself and hash the file.
-
Imran over 8 yearsSo how should we go for approximate similarity say upto 90% similarity.I tried the RGB approach but its a very time taking process when you have about 1000 images in your gallery and you need to compare with one another.
-
Bryan P over 8 yearsYes it would user2924482, it converts the file to the png equivalent data of the uiimage you will pass, it doesn't matter what kind of file type it is