CocoaImageHashing
Hey there and welcome to CocoaImageHashing, a framework helping you with perceptual hashing.
About Perceptual hashing
Perceptual hashing is the application of an algorithm to create a fingerprint for a multimedia format (in this case pictures). Perceptual hash functions have a useful property: small, semantically negligible changes on the input data (e.g. changing contrast, size, format, compression, rotation) produce only small changes on the function output.
This makes perceptual hashing functions useful for:
- finding duplicate images or de-dupliation
- finding similar images or matching
- sorting an image set wrt a base image
This framework was written for macOS, iOS, watchOS and tvOS. This means that basic image transformations are executed through CoreGraphics. There are no external dependencies, so getting the framework integrated into your project is straight-forward.
This framework provides three hashing functions with the following properties:
Name | Performance | Quality |
---|---|---|
aHash | good | bad |
dHash | excellent | good |
pHash | bad | excellent |
Performance
Depending on the hashing algorithm, perceptual hashing generally can yield very good performance. This library was built primarily for ease of use, but hashing performance was critical, too. Some utility functions have data parallelism built in and fingerprint calculation makes use of current CPU instruction pipelines by unrolling and inlining all tight loops of used hashing algorithms.
How To Get Started
Installation with CocoaPods
Integrating this framework with Cocoapods is straightforward.
Just declare this dependency in your Podfile:
pod 'CocoaImageHashing', :git => 'https://github.com/ameingast/cocoaimagehashing.git'
Installation with Carthage
To use Carthage (a more lightweight but more hands on package manager) just create a Cartfile
with
github "ameingast/cocoaimagehashing" ~> 1.9.0
Then follow the steps in the Carthage guide basically (for iOS):
- run
carthage update
- drag the framework from Carthage/Build into Linked Frameworks on the General tab
- add
carthage copy-frameworks
to aRun Scripts
phase
and you're done. The steps for Mac are very similar.
Using the Framework
API
The entrypoint for the framework is the class OSImageHashing.
It provides APIs for:
- perceptual hashing of images in NSData, NSImage and UIImage format in different complexities depending on the used hashing algorithm or desired outcome quality
- comparing calulcated fingerprints in O(1) time and space
- measuring distance between calculated fingerprints in O(1) time and space
- concurrently finding similar elements in NSArrays in O(n^2) time and O(n) space
- concurrently finding similar elements in data streams in O(n^2) time and (almost) O(1) space
- sorting NSArrays based on image similarity
The CocoaImageHashing API is described in detail in this file.
Types
The framework uses the following types in its API:
Name | Bitsize | Description |
---|---|---|
OSHashType | 64 | The result or fingerprint of a perceptual hashing function |
OSHashDistanceType | 64 | The distance between to fingerprints |
OSImageHashingProviderId | 16 | The API representation of a hashing algorithm |
OSImageHashingQuality | 16 | The API representation of a hashing algorithm described by its hashing quality |
More detailed information on types is available here.
Examples
Comparing two images for similarity:
import CocoaImageHashing
let firstImageData = Data()
let secondImageData = Data()
let result = OSImageHashing.sharedInstance().compareImageData(firstImageData,
to: secondImageData,
with: .pHash)
print("Match", result)
#import <CocoaImageHashing/CocoaImageHashing.h>
@interface HashExample : NSObject
@end
@implementation HashExample
- (void)imageSimilarity
{
NSData *firstImageData = [NSData new];
NSData *secondImageData = [NSData new]
BOOL result = [[OSImageHashing sharedInstance] compareImageData:firstImageData to:secondImageData];
NSLog(@"Images match: %@", result ? @"Yes" : @"No");
}
@end
Measuring the distance between two fingerprints
import CocoaImageHashing
let lhsData = OSImageHashing.sharedInstance().hashImageData(Data(), with: .pHash)
let rhsData = OSImageHashing.sharedInstance().hashImageData(Data(), with: .pHash)
let result = OSImageHashing.sharedInstance().hashDistance(lhsData, to: rhsData, with: .pHash)
print("Distance", result)
#import <CocoaImageHashing/CocoaImageHashing.h>
@interface DistanceExample : NSObject
@end
@implementation DistanceExample
- (void)measureDistance
{
NSData *firstImageData = [NSData new];
NSData *secondImageData = [NSData new]
OSHashDistanceType distance = [[OSImageHashing sharedInstance] hashDistance:firstImageData to:secondImageData];
NSLog(@"Hash distance: %@", @(distance));
}
@end
Finding similar images
import CocoaImageHashing
var imageData = [Data(), Data(), Data()]
let similarImages = imageHashing.similarImages(withProvider: .pHash) {
if imageData.count > 0 {
let data = imageData.removeFirst()
return OSTuple<NSString, NSData>(first: name as NSString,
andSecond: data as NSData)
} else {
return nil
}
}
print("Similar Images", similarImages)
#import <CocoaImageHashing/CocoaImageHashing.h>
@interface DuplicationExample : NSObject
@end
@implementation DuplicationExample
- (void)findDuplicates
{
NSData *firstImageData = [NSData new];
NSData *secondImageData = [NSData new]
NSData *thirdImageData = [NSData new];
NSMutableArray<OSTuple<OSImageId *, NSData *> *> *data = [NSMutableArray new];
NSUInteger i = 0;
for (NSData *data in @[ firstImageData, secondImageData, thirdImageData ]) {
OSTuple<OSImageId *, NSData *> *tuple = [OSTuple tupleWithFirst:[NSString stringWithFormat:@"%@", @(i++)] andSecond:data];
[data addObject:tuple];
}
NSArray<OSTuple<OSImageId *, OSImageId *> *> *similarImageIdsAsTuples = [[OSImageHashing sharedInstance] similarImagesWithHashingQuality:OSImageHashingQualityHigh forImages:images];
NSLog(@"Similar image ids: %@", similarImageIdsAsTuples);
}
@end
Sorting an NSArray containing image data
import CocoaImageHashing
let baseImage = Data()
let images = [Data(), Data(), Data()]
let sortedImages = imageHashing.sortedArray(usingImageSimilartyComparator: baseImage,
for: images,
for: .pHash)
print("Sorted images", sortedImages)
#import <CocoaImageHashing/CocoaImageHashing.h>
@interface SortingExample : NSObject
@end
@implementation SortingExample
- (void)sortImageData
{
NSData *baseImage = [NSData new];
NSData *firstImageData = [NSData new];
NSData *secondImageData = [NSData new]
NSData *thirdImageData = [NSData new];
NSArray<NSData *> *images = @[ firstImageData, secondImageData, thirdImageData ];
NSArray<NSData *> *sortedImages = [[OSImageHashing sharedInstance] sortedArrayUsingImageSimilartyComparator:baseImage forArray:images];
NSLog(@"Sorted images: %@", sortedImages);
}
@end
More examples can be found in the test suite and the project playground.
Contact and Contributions
Please submit bug reports and improvements through pull-requests or tickets on github.
This project uses conservative compiler settings. Please be sure that no compiler warnings occur before sending patchesor pull requests upstream.
If you like this library, please consider donating. Thank you!
Copyright and Licensensing
Copyright (c) 2015, Andreas Meingast [email protected].
The framework is published under a BSD style license. For more information, please see the LICENSE file.