/cocoaimagehashing

Perceptual Image Hashing for macOS, iOS, tvOS and watchOS

Primary LanguageObjective-COtherNOASSERTION

CocoaImageHashing

build Status carthage compatible codecov license platforms donate

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 a Run 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 ameingast@gmail.com.

The framework is published under a BSD style license. For more information, please see the LICENSE file.