├── SimilarImageHunter
├── SimilarImageHunter
│ ├── find_image_without_extension
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── 绿太阳 + 放大镜.png
│ │ │ ├── 绿太阳 + 放大镜-1.png
│ │ │ └── Contents.json
│ ├── find_image_with_extension
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── main.m
│ ├── SimilarImageHunter.entitlements
│ ├── NSImage+Processor.h
│ ├── ImageComparator.h
│ ├── AppDelegate.m
│ ├── Info.plist
│ ├── NSImage+Processor.m
│ ├── ImageComparator.m
│ ├── ViewController.m
│ └── Base.lproj
│ │ └── Main.storyboard
└── SimilarImageHunter.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── GreenSun.sketch
├── images
└── 01.png
├── .gitignore
├── README.md
└── LICENSE
/SimilarImageHunter/SimilarImageHunter/find_image_without_extension:
--------------------------------------------------------------------------------
1 | find $1
2 |
--------------------------------------------------------------------------------
/GreenSun.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulingtianxia/SimilarImageHunter/HEAD/GreenSun.sketch
--------------------------------------------------------------------------------
/images/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulingtianxia/SimilarImageHunter/HEAD/images/01.png
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/Assets.xcassets/AppIcon.appiconset/绿太阳 + 放大镜.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulingtianxia/SimilarImageHunter/HEAD/SimilarImageHunter/SimilarImageHunter/Assets.xcassets/AppIcon.appiconset/绿太阳 + 放大镜.png
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/Assets.xcassets/AppIcon.appiconset/绿太阳 + 放大镜-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulingtianxia/SimilarImageHunter/HEAD/SimilarImageHunter/SimilarImageHunter/Assets.xcassets/AppIcon.appiconset/绿太阳 + 放大镜-1.png
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/find_image_with_extension:
--------------------------------------------------------------------------------
1 | find $1 -name '*.[Jj][Pp][Ee][Gg]' -or -name '*.[Pp][Nn][Gg]' -or -name '*.[Ii][Cc][Oo]' -or -name '*.[Tt][Ii][Ff][Ff]' -or -name '*.[Pp][Dd][Ff]' -or -name '*.[Jj][Pp]2' -or -name '*.[Ee][Xx][Rr]'
2 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : NSViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : NSObject
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, const char * argv[]) {
12 | return NSApplicationMain(argc, argv);
13 | }
14 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/SimilarImageHunter.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/NSImage+Processor.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSImage+Processor.h
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #define KEY_ASPECT_RATIO @"aspect ratio"
12 | #define KEY_PIXELVECTOR @"pixel vector"
13 | @interface NSImage (Processor)
14 | - (NSDictionary *)abstractVector;
15 | @end
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | #Pods/
27 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/ImageComparator.h:
--------------------------------------------------------------------------------
1 | //
2 | // ImageComparator.h
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/13.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ImageComparator : NSObject
12 | @property (nonatomic) BOOL checkExtension;
13 | - (NSArray *)collectImagePathsInRootPath:(NSString *)rootPath;
14 | - (double)similarityBetweenSourceImage:(NSImage *)sourceImage sourceFile:(NSString *)sourceFile toTargetImage:(NSImage *)targetImage targetFile:(NSString *)targetFile;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
18 | // Insert code here to initialize your application
19 | }
20 |
21 | - (void)applicationWillTerminate:(NSNotification *)aNotification {
22 | // Insert code here to tear down your application
23 | }
24 |
25 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
26 | {
27 | return YES;
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SimilarImageHunter
2 |
3 | Mac OS X 上的小工具,用来查找相似内容的图片。它会将 Target Path 中的图片与 Source Path 中的图片两两比较,在 Target Path 中捕获到与 Source Path 中每张图片最相似的图片。(建立 Source Path 到 Target Path 的映射)
4 |
5 | 
6 |
7 | ## 使用方法
8 |
9 | 1. 点击第一行的 Select 按钮选择 Source Path
10 | 2. 点击第二行的 Select 按钮选择 Target Path
11 | 3. 点击 Hunt 按钮进行捕获相似图片
12 | 4. 在树形表中点击箭头可以展开详细映射信息
13 | 5. 双击图片路径可以打开对应图片
14 | 6. 在 Hunt 的过程中随时可以按 Cancel 按钮取消当前任务
15 | 7. 按 Clear 按钮清空当前页面所有信息
16 | 8. 勾选 "Check Extension" 则会在查找图片时检查文件后缀名,加快筛选速度
17 | 9. 勾选 "Ignore Duplicate File" 则可忽略重复路径文件。
18 |
19 | 如果目标路径中有多张图片相似度相同且最大,这些图片都会被列出来。树形列表第一列的父节点内容为原始路径中的图片,子节点为目标路径中匹配到的最佳相似内容图片。列表第二列为相似度。
20 |
21 | ## 实现原理
22 |
23 | http://yulingtianxia.com/blog/2016/01/17/search-for-similar-images/
24 |
25 | ## Release
26 |
27 | ### Mac App Store
28 |
29 | https://itunes.apple.com/cn/app/similarimagehunter/id1122434637?mt=12
30 |
31 | ### GitHub Release
32 |
33 | [SimilarImageHunter](https://github.com/yulingtianxia/SimilarImageHunter/releases/)
34 |
35 | ## LICENSE
36 |
37 | The MIT License.
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 杨萧玉
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "512x512",
45 | "idiom" : "mac",
46 | "filename" : "绿太阳 + 放大镜.png",
47 | "scale" : "1x"
48 | },
49 | {
50 | "size" : "512x512",
51 | "idiom" : "mac",
52 | "filename" : "绿太阳 + 放大镜-1.png",
53 | "scale" : "2x"
54 | }
55 | ],
56 | "info" : {
57 | "version" : 1,
58 | "author" : "xcode"
59 | }
60 | }
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.2
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 4
25 | LSApplicationCategoryType
26 | public.app-category.utilities
27 | LSMinimumSystemVersion
28 | $(MACOSX_DEPLOYMENT_TARGET)
29 | NSHumanReadableCopyright
30 | Copyright © 2016年 杨萧玉. All rights reserved.
31 | NSMainStoryboardFile
32 | Main
33 | NSPrincipalClass
34 | NSApplication
35 |
36 |
37 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/NSImage+Processor.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSImage+Processor.m
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import "NSImage+Processor.h"
10 |
11 | #define Mask8(x) ( (x) & 0xFF )
12 | #define A(x) ( Mask8(x) )
13 | #define B(x) ( Mask8(x >> 8 ) )
14 | #define G(x) ( Mask8(x >> 16) )
15 | #define R(x) ( Mask8(x >> 24) )
16 | #define RGBAMake(r, g, b, a) ( Mask8(a) | Mask8(b) << 8 | Mask8(g) << 16 | Mask8(r) << 24 )
17 |
18 | @implementation NSImage (Processor)
19 |
20 | - (NSDictionary *)abstractVector {
21 | // 1. Get pixels of image
22 | NSData *imageData = self.TIFFRepresentation;
23 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
24 | CGImageRef inputCGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
25 | NSUInteger width = CGImageGetWidth(inputCGImage);
26 | NSUInteger height = CGImageGetHeight(inputCGImage);
27 |
28 | NSUInteger bytesPerPixel = 4;
29 | NSUInteger bytesPerRow = bytesPerPixel * width;
30 | NSUInteger bitsPerComponent = 8;
31 |
32 | UInt32 * pixels;
33 | pixels = (UInt32 *) calloc(height * width, sizeof(UInt32));
34 |
35 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
36 | CGContextRef context = CGBitmapContextCreate(pixels, width, height,
37 | bitsPerComponent, bytesPerRow, colorSpace,
38 | kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big);
39 |
40 | CGContextDrawImage(context, CGRectMake(0, 0, width, height), inputCGImage);
41 |
42 | CGColorSpaceRelease(colorSpace);
43 | CGContextRelease(context);
44 |
45 | // 2. Iterate and calculate!
46 | NSMutableDictionary *pixelBucket = [NSMutableDictionary dictionary];
47 | UInt32 *currentPixel = pixels;
48 | for (NSUInteger j = 0; j < height; j++) {
49 | for (NSUInteger i = 0; i < width; i++) {
50 | UInt32 color = *currentPixel;
51 | NSUInteger fingerprint = RGBAMake([self downsampleComponent:R(color)],
52 | [self downsampleComponent:G(color)],
53 | [self downsampleComponent:B(color)],
54 | [self downsampleX:i y:j w:width h:height]);
55 | pixelBucket[@(fingerprint)] = @(pixelBucket[@(fingerprint)].intValue + 1);
56 | currentPixel++;
57 | }
58 | }
59 |
60 | free(pixels);
61 |
62 | [pixelBucket enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
63 | pixelBucket[key] = @(obj.doubleValue/(height * width));
64 | }];
65 |
66 | NSDictionary *bucket = @{KEY_ASPECT_RATIO:@((float)width/height),KEY_PIXELVECTOR:[pixelBucket copy]};
67 | return bucket;
68 | }
69 |
70 | - (UInt32)downsampleComponent:(UInt8)component
71 | {
72 | return (UInt32)component / 32;
73 | }
74 |
75 | - (UInt32)downsampleX:(NSInteger)x y:(NSInteger)y w:(NSInteger)width h:(NSInteger)height
76 | {
77 | NSInteger rowCount = MIN(4, height);
78 | NSInteger countPerRow = MIN(4, width);
79 | NSInteger hStep = width / countPerRow;
80 | NSInteger vStep = height / rowCount;
81 | NSInteger row = y / vStep;
82 | NSInteger col = x / hStep;
83 | return (UInt32)(row * countPerRow + col);
84 | }
85 |
86 | @end
87 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/ImageComparator.m:
--------------------------------------------------------------------------------
1 | //
2 | // ImageComparator.m
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/13.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import "ImageComparator.h"
10 | #import "NSImage+Processor.h"
11 |
12 | @interface ImageComparator ()
13 | @property (nonatomic,nonnull) NSMutableDictionary *vectorCache;
14 | @end
15 | @implementation ImageComparator
16 |
17 | - (instancetype)init
18 | {
19 | self = [super init];
20 | if (self) {
21 | _vectorCache = [NSMutableDictionary dictionary];
22 | _checkExtension = YES;
23 | }
24 | return self;
25 | }
26 |
27 | - (NSArray *)collectImagePathsInRootPath:(NSString *)rootPath
28 | {
29 | NSTask *imagePathsCollector;
30 | imagePathsCollector = [[NSTask alloc] init];
31 | imagePathsCollector.launchPath = @"/bin/bash";
32 | NSString *shellPath;
33 |
34 | if (self.checkExtension) {
35 | shellPath = [NSString stringWithFormat:@"%@%@",[NSBundle mainBundle].resourcePath,@"/find_image_with_extension"];
36 | }
37 | else {
38 | shellPath = [NSString stringWithFormat:@"%@%@",[NSBundle mainBundle].resourcePath,@"/find_image_without_extension"];
39 | }
40 | imagePathsCollector.arguments = @[shellPath,rootPath];
41 | NSPipe *pipe;
42 | pipe = [NSPipe pipe];
43 | [imagePathsCollector setStandardOutput: pipe]; //设置输出参数
44 |
45 | NSFileHandle *file;
46 | file = [pipe fileHandleForReading]; // 句柄
47 | [imagePathsCollector launch];
48 | NSData *data;
49 | data = [file readDataToEndOfFile]; // 读取数据
50 |
51 | NSString *string = [[NSString alloc] initWithData: data
52 | encoding: NSUTF8StringEncoding];
53 | NSArray *paths = [string componentsSeparatedByString:@"\n"];
54 | return paths;
55 | // NSMutableArray *results = [NSMutableArray array];
56 | // for (NSString *path in paths) {
57 | // if ([self checkImageFile:path]) {
58 | // [results addObject:path];
59 | // }
60 | // }
61 | // return [results copy];
62 | }
63 |
64 | //- (BOOL)checkImageFile:(NSString *)filePath
65 | //{
66 | // __block BOOL dataCheck = NO;
67 | // NSTask *imageChecker = [[NSTask alloc] init];
68 | // imageChecker.launchPath = @"/usr/bin/file";
69 | // imageChecker.arguments = @[filePath];
70 | // NSPipe *pipe = [NSPipe pipe];
71 | // imageChecker.standardOutput = pipe;
72 | // NSFileHandle *file = [pipe fileHandleForReading]; // 句柄
73 | // [imageChecker launch];
74 | // NSData *data = [file readDataToEndOfFile]; // 读取数据
75 | // NSString *string = [[NSString alloc] initWithData: data
76 | // encoding: NSUTF8StringEncoding];
77 | // [[string componentsSeparatedByString:@" "] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
78 | // if ([obj isEqualToString:@"image"]) {
79 | // *stop = YES;
80 | // dataCheck = YES;
81 | // }
82 | // }];
83 | //
84 | // BOOL extensionCheck = [filePath.pathExtension isEqualToString:@"pdf"] || [filePath.pathExtension isEqualToString:@"PDF"];
85 | // return dataCheck||extensionCheck;
86 | //}
87 |
88 | - (double)similarityBetweenSourceImage:(NSImage *)sourceImage sourceFile:(NSString *)sourceFile toTargetImage:(NSImage *)targetImage targetFile:(NSString *)targetFile
89 | {
90 | double result;
91 | double weightOfAspectRatio = 0.3;
92 |
93 | NSDictionary *sourceVector = self.vectorCache[sourceFile];
94 | if (!sourceVector) {
95 | sourceVector = [sourceImage abstractVector];
96 | self.vectorCache[sourceFile] = sourceVector;
97 | }
98 |
99 | NSDictionary *targetVector = self.vectorCache[targetFile];
100 | if (!targetVector) {
101 | targetVector = [targetImage abstractVector];
102 | self.vectorCache[targetFile] = targetVector;
103 | }
104 |
105 | double sourceAspectRatio = ((NSNumber *)sourceVector[KEY_ASPECT_RATIO]).doubleValue;
106 | double targetAspectRatio = ((NSNumber *)targetVector[KEY_ASPECT_RATIO]).doubleValue;
107 | double similarityOfAspectRatio = 1 - fabs(sourceAspectRatio - targetAspectRatio) / sourceAspectRatio;
108 |
109 | NSDictionary *sourcePixelVector = sourceVector[KEY_PIXELVECTOR];
110 | NSDictionary *targetPixelVector = targetVector[KEY_PIXELVECTOR];
111 |
112 | //向量余弦距离相似性
113 | __block double similarityOfPixelVector = 0;
114 | __block double targetRank = 0;
115 | __block double sourceRank = 0;
116 | [sourcePixelVector enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
117 | NSNumber *targetObj = targetPixelVector[key];
118 | if (targetObj) {
119 | similarityOfPixelVector += obj.doubleValue * targetObj.doubleValue;
120 | }
121 | sourceRank += obj.doubleValue * obj.doubleValue;
122 | }];
123 |
124 | sourceRank = sqrt(sourceRank);
125 |
126 | [targetPixelVector enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
127 | targetRank += obj.doubleValue * obj.doubleValue;
128 | }];
129 |
130 | targetRank = sqrt(targetRank);
131 |
132 | similarityOfPixelVector = similarityOfPixelVector / (sourceRank * targetRank);
133 |
134 | result = similarityOfAspectRatio*weightOfAspectRatio + similarityOfPixelVector*(1-weightOfAspectRatio);
135 |
136 | return result;
137 | }
138 |
139 | @end
140 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // SimilarImageHunter
4 | //
5 | // Created by 杨萧玉 on 16/1/12.
6 | // Copyright © 2016年 杨萧玉. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "ImageComparator.h"
11 |
12 | #define SOURCE_PATH @"SOURCE_PATH"
13 | #define CHILDREN @"children"
14 | #define TARGET_PATH @"targetpath"
15 | #define SIMILARITY @"similarity"
16 |
17 | @interface ViewController ()
18 | @property (nonnull,nonatomic) ImageComparator *comparator;
19 | @property (weak) IBOutlet NSTextField *sourcePathTF;
20 | @property (weak) IBOutlet NSTextField *targetPathTF;
21 | @property (weak) IBOutlet NSButton *huntBtn;
22 | @property (weak) IBOutlet NSOutlineView *resultTable;
23 | @property (weak) IBOutlet NSButton *clearBtn;
24 | @property (weak) IBOutlet NSButton *cancelBtn;
25 | @property (weak) IBOutlet NSButton *checkExtensionBtn;
26 | @property (weak) IBOutlet NSButton *ignoreDuplicateFileBtn;
27 |
28 | @property (nonatomic) BOOL *cancelledPtr;
29 | @property (nonnull,nonatomic) NSMutableArray *> *resultData;
30 | @end
31 |
32 | @implementation ViewController
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 | self.comparator = [ImageComparator new];
37 | self.resultTable.delegate = self;
38 | self.resultTable.dataSource = self;
39 | }
40 |
41 | - (void)setRepresentedObject:(id)representedObject {
42 | [super setRepresentedObject:representedObject];
43 |
44 | // Update the view, if already loaded.
45 | }
46 |
47 | #pragma mark - buttons
48 |
49 | - (IBAction)selectSourcePath:(NSButton *)sender {
50 | NSOpenPanel* openDlg = [NSOpenPanel openPanel];
51 | openDlg.prompt = @"Select";
52 | openDlg.canChooseFiles = YES;
53 | openDlg.canChooseDirectories = YES;
54 | openDlg.allowsMultipleSelection = NO;
55 |
56 | if ([openDlg runModal] == NSFileHandlingPanelOKButton)
57 | {
58 | NSURL *fileURL = [openDlg URL];
59 | self.sourcePathTF.stringValue = fileURL.path;
60 | }
61 | }
62 |
63 | - (IBAction)selectTargetPath:(NSButton *)sender {
64 | NSOpenPanel* openDlg = [NSOpenPanel openPanel];
65 | openDlg.prompt = @"Select";
66 | openDlg.canChooseFiles = YES;
67 | openDlg.canChooseDirectories = YES;
68 | openDlg.allowsMultipleSelection = NO;
69 |
70 | if ([openDlg runModal] == NSFileHandlingPanelOKButton)
71 | {
72 | NSURL *fileURL = [openDlg URL];
73 | self.targetPathTF.stringValue = fileURL.path;
74 | }
75 | }
76 |
77 | - (IBAction)checkExtionsion:(NSButton *)sender {
78 | self.comparator.checkExtension = (sender.state == NSOnState);
79 | }
80 |
81 | - (IBAction)huntClick:(NSButton *)sender {
82 | sender.enabled = NO;
83 | __block BOOL cancelled = NO;
84 | self.resultData = [NSMutableArray array];
85 | [self.resultTable reloadData];
86 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
87 |
88 | NSArray *sourcePaths = [self.comparator collectImagePathsInRootPath:self.sourcePathTF.stringValue];
89 | NSArray *targetPaths = [self.comparator collectImagePathsInRootPath:self.targetPathTF.stringValue];
90 | NSMutableArray *invalidFiles = [NSMutableArray array];
91 | for (NSString *sourcePath in sourcePaths) {
92 | if (cancelled) {
93 | break;
94 | }
95 | NSMutableDictionary *similarityMap = [NSMutableDictionary dictionary];
96 | NSImage *sourceImage = [[NSImage alloc] initWithContentsOfFile:sourcePath];
97 | if (!sourceImage) {
98 | [invalidFiles addObject:sourcePath];
99 | continue;
100 | }
101 | for (NSString *obj in targetPaths) {
102 | if (cancelled) {
103 | break;
104 | }
105 | if (self.ignoreDuplicateFileBtn.state == NSOnState && [sourcePath isEqualToString:obj]) {
106 | continue;
107 | }
108 | if ([invalidFiles containsObject:obj]) {
109 | continue;
110 | }
111 | NSImage *targetImage = [[NSImage alloc] initWithContentsOfFile:obj];
112 | if (!targetImage) {
113 | [invalidFiles addObject:obj];
114 | continue;
115 | }
116 | NSNumber *similarity = @([self.comparator similarityBetweenSourceImage:sourceImage sourceFile:sourcePath toTargetImage:targetImage targetFile:obj]);
117 | similarityMap[obj]=similarity;
118 | }
119 | __block NSNumber *max = @0;
120 | __block NSString *similarist = @"";
121 | [similarityMap enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
122 | if (obj.doubleValue > max.doubleValue) {
123 | max = obj;
124 | similarist = key;
125 | }
126 | }];
127 | if (![similarist isEqualToString:@""]) {
128 | NSMutableArray *resultPaths = [NSMutableArray array];
129 | [similarityMap enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
130 | if (obj.doubleValue == max.doubleValue) {
131 | [resultPaths addObject:key];
132 | }
133 | }];
134 | NSMutableArray *> *children = [NSMutableArray array];
135 | for (NSString *path in resultPaths) {
136 | [children addObject:@{TARGET_PATH:path, SIMILARITY:[NSString stringWithFormat:@"%.02f%%",max.doubleValue*100]}];
137 | }
138 | [self.resultData addObject:@{SOURCE_PATH:sourcePath, CHILDREN:[children copy]}];
139 | }
140 | }
141 | dispatch_async(dispatch_get_main_queue(), ^{
142 | sender.enabled = YES;
143 | sender.highlighted = NO;
144 | if (!cancelled) {
145 | [self.resultTable reloadData];
146 | }
147 | });
148 | });
149 | self.cancelledPtr = &cancelled;
150 | }
151 |
152 | - (IBAction)clearClick:(NSButton *)sender {
153 | self.sourcePathTF.stringValue = @"";
154 | self.targetPathTF.stringValue = @"";
155 | [self.resultData removeAllObjects];
156 | [self.resultTable reloadData];
157 | }
158 |
159 | - (IBAction)cancelClick:(NSButton *)sender {
160 | if (self.cancelledPtr) {
161 | *self.cancelledPtr = YES;
162 | }
163 | self.huntBtn.enabled = YES;
164 | }
165 |
166 | #pragma mark - NSOutlineViewDataSource
167 |
168 | - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
169 | {
170 | if (item == nil) {
171 | return self.resultData.count;
172 | }
173 |
174 | if ([item isKindOfClass:[NSDictionary class]]) {
175 | return [[item objectForKey:CHILDREN] count];
176 | }
177 |
178 | return 0;
179 | }
180 |
181 | - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
182 | {
183 | if (item == nil) {
184 | return self.resultData[index];
185 | }
186 |
187 | if ([item isKindOfClass:[NSDictionary class]]) {
188 | return [[item objectForKey:CHILDREN] objectAtIndex:index];
189 | }
190 |
191 | return nil;
192 | }
193 |
194 | - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
195 | {
196 | if ([item isKindOfClass:[NSDictionary class]]) {
197 | return YES;
198 | }else {
199 | return NO;
200 | }
201 | }
202 |
203 | - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
204 | {
205 | if ([[tableColumn identifier] isEqualToString:CHILDREN]) {
206 | return [item objectForKey:SIMILARITY];
207 | }else{
208 | if ([item isKindOfClass:[NSDictionary class]]) {
209 | NSString *sourcePath = [item objectForKey:SOURCE_PATH];
210 | if (sourcePath) {
211 | return sourcePath;
212 | }
213 | return [item objectForKey:TARGET_PATH];
214 | }
215 | }
216 |
217 | return nil;
218 | }
219 |
220 | - (IBAction)doubleClickOutlineAction:(NSOutlineView *)sender {
221 | NSDictionary *item = [sender itemAtRow:[sender clickedRow]];
222 | NSString *sourcePath = [item objectForKey:SOURCE_PATH];
223 | if (sourcePath) {
224 | [[NSWorkspace sharedWorkspace] openFile:sourcePath];
225 | }
226 | NSString *targetPath = [item objectForKey:TARGET_PATH];
227 | if (targetPath) {
228 | [[NSWorkspace sharedWorkspace] openFile:targetPath];
229 | }
230 | }
231 |
232 | @end
233 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | A434F4AB1C460B550078080A /* find_image_with_extension in Resources */ = {isa = PBXBuildFile; fileRef = A434F4AA1C460B550078080A /* find_image_with_extension */; };
11 | A494D3C91C454CB800F6269D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A494D3C81C454CB800F6269D /* AppDelegate.m */; };
12 | A494D3CC1C454CB800F6269D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A494D3CB1C454CB800F6269D /* main.m */; };
13 | A494D3CF1C454CB800F6269D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A494D3CE1C454CB800F6269D /* ViewController.m */; };
14 | A494D3D11C454CB800F6269D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A494D3D01C454CB800F6269D /* Assets.xcassets */; };
15 | A494D3D41C454CB800F6269D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A494D3D21C454CB800F6269D /* Main.storyboard */; };
16 | A494D3DD1C4552BC00F6269D /* NSImage+Processor.m in Sources */ = {isa = PBXBuildFile; fileRef = A494D3DC1C4552BC00F6269D /* NSImage+Processor.m */; };
17 | A494D3E01C455D9C00F6269D /* ImageComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = A494D3DF1C455D9C00F6269D /* ImageComparator.m */; };
18 | A4CC35381D0AC43A00A400F0 /* find_image_without_extension in Resources */ = {isa = PBXBuildFile; fileRef = A4CC35371D0AC43A00A400F0 /* find_image_without_extension */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | A434F4AA1C460B550078080A /* find_image_with_extension */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = find_image_with_extension; sourceTree = ""; };
23 | A494D3C41C454CB800F6269D /* SimilarImageHunter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimilarImageHunter.app; sourceTree = BUILT_PRODUCTS_DIR; };
24 | A494D3C71C454CB800F6269D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
25 | A494D3C81C454CB800F6269D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
26 | A494D3CB1C454CB800F6269D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
27 | A494D3CD1C454CB800F6269D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
28 | A494D3CE1C454CB800F6269D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
29 | A494D3D01C454CB800F6269D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
30 | A494D3D31C454CB800F6269D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
31 | A494D3D51C454CB900F6269D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | A494D3DB1C4552BC00F6269D /* NSImage+Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+Processor.h"; sourceTree = ""; };
33 | A494D3DC1C4552BC00F6269D /* NSImage+Processor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+Processor.m"; sourceTree = ""; };
34 | A494D3DE1C455D9C00F6269D /* ImageComparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageComparator.h; sourceTree = ""; };
35 | A494D3DF1C455D9C00F6269D /* ImageComparator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageComparator.m; sourceTree = ""; };
36 | A4CC35361D095AA800A400F0 /* SimilarImageHunter.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = SimilarImageHunter.entitlements; sourceTree = ""; };
37 | A4CC35371D0AC43A00A400F0 /* find_image_without_extension */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = find_image_without_extension; sourceTree = ""; };
38 | /* End PBXFileReference section */
39 |
40 | /* Begin PBXFrameworksBuildPhase section */
41 | A494D3C11C454CB800F6269D /* Frameworks */ = {
42 | isa = PBXFrameworksBuildPhase;
43 | buildActionMask = 2147483647;
44 | files = (
45 | );
46 | runOnlyForDeploymentPostprocessing = 0;
47 | };
48 | /* End PBXFrameworksBuildPhase section */
49 |
50 | /* Begin PBXGroup section */
51 | A494D3BB1C454CB800F6269D = {
52 | isa = PBXGroup;
53 | children = (
54 | A494D3C61C454CB800F6269D /* SimilarImageHunter */,
55 | A494D3C51C454CB800F6269D /* Products */,
56 | );
57 | sourceTree = "";
58 | };
59 | A494D3C51C454CB800F6269D /* Products */ = {
60 | isa = PBXGroup;
61 | children = (
62 | A494D3C41C454CB800F6269D /* SimilarImageHunter.app */,
63 | );
64 | name = Products;
65 | sourceTree = "";
66 | };
67 | A494D3C61C454CB800F6269D /* SimilarImageHunter */ = {
68 | isa = PBXGroup;
69 | children = (
70 | A4CC35361D095AA800A400F0 /* SimilarImageHunter.entitlements */,
71 | A494D3C71C454CB800F6269D /* AppDelegate.h */,
72 | A494D3C81C454CB800F6269D /* AppDelegate.m */,
73 | A494D3CD1C454CB800F6269D /* ViewController.h */,
74 | A494D3CE1C454CB800F6269D /* ViewController.m */,
75 | A494D3D01C454CB800F6269D /* Assets.xcassets */,
76 | A494D3D21C454CB800F6269D /* Main.storyboard */,
77 | A494D3D51C454CB900F6269D /* Info.plist */,
78 | A494D3CA1C454CB800F6269D /* Supporting Files */,
79 | A494D3DB1C4552BC00F6269D /* NSImage+Processor.h */,
80 | A494D3DC1C4552BC00F6269D /* NSImage+Processor.m */,
81 | A494D3DE1C455D9C00F6269D /* ImageComparator.h */,
82 | A494D3DF1C455D9C00F6269D /* ImageComparator.m */,
83 | A434F4AA1C460B550078080A /* find_image_with_extension */,
84 | A4CC35371D0AC43A00A400F0 /* find_image_without_extension */,
85 | );
86 | path = SimilarImageHunter;
87 | sourceTree = "";
88 | };
89 | A494D3CA1C454CB800F6269D /* Supporting Files */ = {
90 | isa = PBXGroup;
91 | children = (
92 | A494D3CB1C454CB800F6269D /* main.m */,
93 | );
94 | name = "Supporting Files";
95 | sourceTree = "";
96 | };
97 | /* End PBXGroup section */
98 |
99 | /* Begin PBXNativeTarget section */
100 | A494D3C31C454CB800F6269D /* SimilarImageHunter */ = {
101 | isa = PBXNativeTarget;
102 | buildConfigurationList = A494D3D81C454CB900F6269D /* Build configuration list for PBXNativeTarget "SimilarImageHunter" */;
103 | buildPhases = (
104 | A494D3C01C454CB800F6269D /* Sources */,
105 | A494D3C11C454CB800F6269D /* Frameworks */,
106 | A494D3C21C454CB800F6269D /* Resources */,
107 | );
108 | buildRules = (
109 | );
110 | dependencies = (
111 | );
112 | name = SimilarImageHunter;
113 | productName = SimilarImageHunter;
114 | productReference = A494D3C41C454CB800F6269D /* SimilarImageHunter.app */;
115 | productType = "com.apple.product-type.application";
116 | };
117 | /* End PBXNativeTarget section */
118 |
119 | /* Begin PBXProject section */
120 | A494D3BC1C454CB800F6269D /* Project object */ = {
121 | isa = PBXProject;
122 | attributes = {
123 | LastUpgradeCheck = 1010;
124 | ORGANIZATIONNAME = "杨萧玉";
125 | TargetAttributes = {
126 | A494D3C31C454CB800F6269D = {
127 | CreatedOnToolsVersion = 7.2;
128 | DevelopmentTeam = D3RCVUP6VH;
129 | SystemCapabilities = {
130 | com.apple.Sandbox = {
131 | enabled = 1;
132 | };
133 | };
134 | };
135 | };
136 | };
137 | buildConfigurationList = A494D3BF1C454CB800F6269D /* Build configuration list for PBXProject "SimilarImageHunter" */;
138 | compatibilityVersion = "Xcode 3.2";
139 | developmentRegion = English;
140 | hasScannedForEncodings = 0;
141 | knownRegions = (
142 | en,
143 | Base,
144 | );
145 | mainGroup = A494D3BB1C454CB800F6269D;
146 | productRefGroup = A494D3C51C454CB800F6269D /* Products */;
147 | projectDirPath = "";
148 | projectRoot = "";
149 | targets = (
150 | A494D3C31C454CB800F6269D /* SimilarImageHunter */,
151 | );
152 | };
153 | /* End PBXProject section */
154 |
155 | /* Begin PBXResourcesBuildPhase section */
156 | A494D3C21C454CB800F6269D /* Resources */ = {
157 | isa = PBXResourcesBuildPhase;
158 | buildActionMask = 2147483647;
159 | files = (
160 | A494D3D11C454CB800F6269D /* Assets.xcassets in Resources */,
161 | A494D3D41C454CB800F6269D /* Main.storyboard in Resources */,
162 | A4CC35381D0AC43A00A400F0 /* find_image_without_extension in Resources */,
163 | A434F4AB1C460B550078080A /* find_image_with_extension in Resources */,
164 | );
165 | runOnlyForDeploymentPostprocessing = 0;
166 | };
167 | /* End PBXResourcesBuildPhase section */
168 |
169 | /* Begin PBXSourcesBuildPhase section */
170 | A494D3C01C454CB800F6269D /* Sources */ = {
171 | isa = PBXSourcesBuildPhase;
172 | buildActionMask = 2147483647;
173 | files = (
174 | A494D3E01C455D9C00F6269D /* ImageComparator.m in Sources */,
175 | A494D3CF1C454CB800F6269D /* ViewController.m in Sources */,
176 | A494D3DD1C4552BC00F6269D /* NSImage+Processor.m in Sources */,
177 | A494D3CC1C454CB800F6269D /* main.m in Sources */,
178 | A494D3C91C454CB800F6269D /* AppDelegate.m in Sources */,
179 | );
180 | runOnlyForDeploymentPostprocessing = 0;
181 | };
182 | /* End PBXSourcesBuildPhase section */
183 |
184 | /* Begin PBXVariantGroup section */
185 | A494D3D21C454CB800F6269D /* Main.storyboard */ = {
186 | isa = PBXVariantGroup;
187 | children = (
188 | A494D3D31C454CB800F6269D /* Base */,
189 | );
190 | name = Main.storyboard;
191 | sourceTree = "";
192 | };
193 | /* End PBXVariantGroup section */
194 |
195 | /* Begin XCBuildConfiguration section */
196 | A494D3D61C454CB900F6269D /* Debug */ = {
197 | isa = XCBuildConfiguration;
198 | buildSettings = {
199 | ALWAYS_SEARCH_USER_PATHS = NO;
200 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
201 | CLANG_CXX_LIBRARY = "libc++";
202 | CLANG_ENABLE_MODULES = YES;
203 | CLANG_ENABLE_OBJC_ARC = YES;
204 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
205 | CLANG_WARN_BOOL_CONVERSION = YES;
206 | CLANG_WARN_COMMA = YES;
207 | CLANG_WARN_CONSTANT_CONVERSION = YES;
208 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
209 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
210 | CLANG_WARN_EMPTY_BODY = YES;
211 | CLANG_WARN_ENUM_CONVERSION = YES;
212 | CLANG_WARN_INFINITE_RECURSION = YES;
213 | CLANG_WARN_INT_CONVERSION = YES;
214 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
215 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
216 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
218 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
219 | CLANG_WARN_STRICT_PROTOTYPES = YES;
220 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
221 | CLANG_WARN_UNREACHABLE_CODE = YES;
222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
223 | CODE_SIGN_IDENTITY = "-";
224 | COPY_PHASE_STRIP = NO;
225 | DEBUG_INFORMATION_FORMAT = dwarf;
226 | ENABLE_STRICT_OBJC_MSGSEND = YES;
227 | ENABLE_TESTABILITY = YES;
228 | GCC_C_LANGUAGE_STANDARD = gnu99;
229 | GCC_DYNAMIC_NO_PIC = NO;
230 | GCC_NO_COMMON_BLOCKS = YES;
231 | GCC_OPTIMIZATION_LEVEL = 0;
232 | GCC_PREPROCESSOR_DEFINITIONS = (
233 | "DEBUG=1",
234 | "$(inherited)",
235 | );
236 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
237 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
238 | GCC_WARN_UNDECLARED_SELECTOR = YES;
239 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
240 | GCC_WARN_UNUSED_FUNCTION = YES;
241 | GCC_WARN_UNUSED_VARIABLE = YES;
242 | MACOSX_DEPLOYMENT_TARGET = 10.10;
243 | MTL_ENABLE_DEBUG_INFO = YES;
244 | ONLY_ACTIVE_ARCH = YES;
245 | SDKROOT = macosx;
246 | };
247 | name = Debug;
248 | };
249 | A494D3D71C454CB900F6269D /* Release */ = {
250 | isa = XCBuildConfiguration;
251 | buildSettings = {
252 | ALWAYS_SEARCH_USER_PATHS = NO;
253 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
254 | CLANG_CXX_LIBRARY = "libc++";
255 | CLANG_ENABLE_MODULES = YES;
256 | CLANG_ENABLE_OBJC_ARC = YES;
257 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
258 | CLANG_WARN_BOOL_CONVERSION = YES;
259 | CLANG_WARN_COMMA = YES;
260 | CLANG_WARN_CONSTANT_CONVERSION = YES;
261 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
262 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
263 | CLANG_WARN_EMPTY_BODY = YES;
264 | CLANG_WARN_ENUM_CONVERSION = YES;
265 | CLANG_WARN_INFINITE_RECURSION = YES;
266 | CLANG_WARN_INT_CONVERSION = YES;
267 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
268 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
269 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
271 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
272 | CLANG_WARN_STRICT_PROTOTYPES = YES;
273 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
274 | CLANG_WARN_UNREACHABLE_CODE = YES;
275 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
276 | CODE_SIGN_IDENTITY = "-";
277 | COPY_PHASE_STRIP = NO;
278 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
279 | ENABLE_NS_ASSERTIONS = NO;
280 | ENABLE_STRICT_OBJC_MSGSEND = YES;
281 | GCC_C_LANGUAGE_STANDARD = gnu99;
282 | GCC_NO_COMMON_BLOCKS = YES;
283 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
284 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
285 | GCC_WARN_UNDECLARED_SELECTOR = YES;
286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
287 | GCC_WARN_UNUSED_FUNCTION = YES;
288 | GCC_WARN_UNUSED_VARIABLE = YES;
289 | MACOSX_DEPLOYMENT_TARGET = 10.10;
290 | MTL_ENABLE_DEBUG_INFO = NO;
291 | SDKROOT = macosx;
292 | };
293 | name = Release;
294 | };
295 | A494D3D91C454CB900F6269D /* Debug */ = {
296 | isa = XCBuildConfiguration;
297 | buildSettings = {
298 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
299 | CODE_SIGN_ENTITLEMENTS = SimilarImageHunter/SimilarImageHunter.entitlements;
300 | CODE_SIGN_IDENTITY = "Mac Developer";
301 | COMBINE_HIDPI_IMAGES = YES;
302 | INFOPLIST_FILE = SimilarImageHunter/Info.plist;
303 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
304 | MACOSX_DEPLOYMENT_TARGET = 10.11;
305 | PRODUCT_BUNDLE_IDENTIFIER = com.yulingtianxia.SimilarImageHunter;
306 | PRODUCT_NAME = "$(TARGET_NAME)";
307 | };
308 | name = Debug;
309 | };
310 | A494D3DA1C454CB900F6269D /* Release */ = {
311 | isa = XCBuildConfiguration;
312 | buildSettings = {
313 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
314 | CODE_SIGN_ENTITLEMENTS = SimilarImageHunter/SimilarImageHunter.entitlements;
315 | CODE_SIGN_IDENTITY = "Mac Developer";
316 | COMBINE_HIDPI_IMAGES = YES;
317 | INFOPLIST_FILE = SimilarImageHunter/Info.plist;
318 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
319 | MACOSX_DEPLOYMENT_TARGET = 10.11;
320 | PRODUCT_BUNDLE_IDENTIFIER = com.yulingtianxia.SimilarImageHunter;
321 | PRODUCT_NAME = "$(TARGET_NAME)";
322 | };
323 | name = Release;
324 | };
325 | /* End XCBuildConfiguration section */
326 |
327 | /* Begin XCConfigurationList section */
328 | A494D3BF1C454CB800F6269D /* Build configuration list for PBXProject "SimilarImageHunter" */ = {
329 | isa = XCConfigurationList;
330 | buildConfigurations = (
331 | A494D3D61C454CB900F6269D /* Debug */,
332 | A494D3D71C454CB900F6269D /* Release */,
333 | );
334 | defaultConfigurationIsVisible = 0;
335 | defaultConfigurationName = Release;
336 | };
337 | A494D3D81C454CB900F6269D /* Build configuration list for PBXNativeTarget "SimilarImageHunter" */ = {
338 | isa = XCConfigurationList;
339 | buildConfigurations = (
340 | A494D3D91C454CB900F6269D /* Debug */,
341 | A494D3DA1C454CB900F6269D /* Release */,
342 | );
343 | defaultConfigurationIsVisible = 0;
344 | defaultConfigurationName = Release;
345 | };
346 | /* End XCConfigurationList section */
347 | };
348 | rootObject = A494D3BC1C454CB800F6269D /* Project object */;
349 | }
350 |
--------------------------------------------------------------------------------
/SimilarImageHunter/SimilarImageHunter/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
798 |
808 |
818 |
828 |
838 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
--------------------------------------------------------------------------------