├── .github └── workflows │ └── pythonpackage.yml ├── README ├── tpHash.h └── tpHash.m /.github/workflows/pythonpackage.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | max-parallel: 4 11 | matrix: 12 | python-version: [2.7, 3.5, 3.6, 3.7] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install -r requirements.txt 24 | - name: Lint with flake8 25 | run: | 26 | pip install flake8 27 | # stop the build if there are Python syntax errors or undefined names 28 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 29 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 30 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 31 | - name: Test with pytest 32 | run: | 33 | pip install pytest 34 | pytest 35 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 相似图像Hash算法OC版本 2 | 3 | 本代码主要是实现了 http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html 一文记载的一种相似图像Hash的算法,具体细节请看此文。 4 | 5 | 关键函数为 6 | + (uint64_t)ptHash:(UIImage*)image; //生成64位的图像Hash 7 | + (int)hamdist:(uint64_t)x with:(uint64_t) y; //比较两个Hash值的汉明距离 8 | 9 | 这个算法有一定局限性,仅适用于一张图片的多种不同分辨率,采样率,或者极为相似的图像的比较。 10 | 实现主要参考了文中提到的Wote的Py版本,在此表示感谢。 -------------------------------------------------------------------------------- /tpHash.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TpHash : NSObject 4 | 5 | + (uint64_t)ptHash:(UIImage*)image; //生成64位的图像Hash 6 | + (int)hamdist:(uint64_t)x with:(uint64_t) y; //比较两个Hash值的汉明距离 7 | + (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize; 8 | + (uint8_t *) convertToGreyscale64Array:(UIImage *)i; 9 | @end 10 | -------------------------------------------------------------------------------- /tpHash.m: -------------------------------------------------------------------------------- 1 | #import "TpHash.h" 2 | 3 | @implementation TpHash 4 | 5 | +(uint64_t)ptHash:(UIImage*)image { 6 | 7 | image = [self scaleImage:image toSize:CGSizeMake(8,8)]; 8 | uint8_t* imgArray = [self convertToGreyscale64Array:image]; 9 | int sum = 0; 10 | for (int i = 0; i < 64; i++) { 11 | 12 | sum += imgArray[i]; 13 | } 14 | uint8_t avg = sum/64; 15 | uint64_t ret = 0; 16 | for (int i = 0; i < 64; i++) { 17 | 18 | if(imgArray[i]>=avg) { 19 | 20 | ret++; 21 | } 22 | ret <<= 1; 23 | } 24 | return ret; 25 | } 26 | 27 | +(int)hamdist:(uint64_t)x with:(uint64_t) y 28 | { 29 | unsigned dist = 0, val = x ^ y; 30 | 31 | while(val) 32 | { 33 | ++dist; 34 | val &= val - 1; 35 | } 36 | 37 | return dist; 38 | } 39 | 40 | +(UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize { 41 | 42 | UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); 43 | [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; 44 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 45 | UIGraphicsEndImageContext(); 46 | return newImage; 47 | } 48 | 49 | 50 | +(uint8_t *) convertToGreyscale64Array:(UIImage *)i { 51 | 52 | int kRed = 1; 53 | int kGreen = 2; 54 | int kBlue = 4; 55 | 56 | int colors = kGreen; 57 | int m_width = i.size.width; 58 | int m_height = i.size.height; 59 | 60 | uint32_t *rgbImage = (uint32_t *) malloc(m_width * m_height * sizeof(uint32_t)); 61 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 62 | CGContextRef context = CGBitmapContextCreate(rgbImage, m_width, m_height, 8, m_width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); 63 | CGContextSetInterpolationQuality(context, kCGInterpolationHigh); 64 | CGContextSetShouldAntialias(context, NO); 65 | CGContextDrawImage(context, CGRectMake(0, 0, m_width, m_height), [i CGImage]); 66 | CGContextRelease(context); 67 | CGColorSpaceRelease(colorSpace); 68 | 69 | uint8_t *m_imageData = (uint8_t *) malloc(m_width * m_height); 70 | for(int y = 0; y < m_height; y++) { 71 | for(int x = 0; x < m_width; x++) { 72 | uint32_t rgbPixel=rgbImage[y*m_width+x]; 73 | uint32_t sum=0,count=0; 74 | if (colors & kRed) {sum += (rgbPixel>>24)&255; count++;} 75 | if (colors & kGreen) {sum += (rgbPixel>>16)&255; count++;} 76 | if (colors & kBlue) {sum += (rgbPixel>>8)&255; count++;} 77 | m_imageData[y*m_width+x]=sum/count/4; 78 | } 79 | } 80 | free(rgbImage); 81 | return m_imageData; 82 | } 83 | 84 | 85 | @end 86 | --------------------------------------------------------------------------------