├── .DS_Store ├── .gitignore ├── Podfile ├── TestingMLAllImages.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── alexe.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── alexe.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── TestingMLAllImages.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── alexe.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist └── TestingMLAllImages ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── MobileNet.mlmodel ├── TestingMLAllImages.xcdatamodeld ├── .xccurrentversion └── TestingMLAllImages.xcdatamodel │ └── contents ├── UIImage+CVPixelBuffer.swift ├── ViewController.swift └── loading-bar.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farawayCC/ML-image-similarity/8e78e4eb2452799a7fd30dff5229a19c00086ab8/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Podfile.lock 3 | Pods 4 | *.xcuserstate 5 | *.pbxproj 6 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'TestingMLAllImages' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | pod 'OpalImagePicker' 8 | end 9 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 258A4FF9DC741B0A07CF63FD /* Pods_TestingMLAllImages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA8EA2D371856AC6D1C8D452 /* Pods_TestingMLAllImages.framework */; }; 11 | BA42112721F0C38700326022 /* loading-bar.png in Resources */ = {isa = PBXBuildFile; fileRef = BA42112621F0C38700326022 /* loading-bar.png */; }; 12 | BA87BC8E21EC85350002D72D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA87BC8D21EC85350002D72D /* AppDelegate.swift */; }; 13 | BA87BC9021EC85350002D72D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA87BC8F21EC85350002D72D /* ViewController.swift */; }; 14 | BA87BC9321EC85350002D72D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BA87BC9121EC85350002D72D /* Main.storyboard */; }; 15 | BA87BC9621EC85350002D72D /* TestingMLAllImages.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BA87BC9421EC85350002D72D /* TestingMLAllImages.xcdatamodeld */; }; 16 | BA87BC9821EC85370002D72D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BA87BC9721EC85370002D72D /* Assets.xcassets */; }; 17 | BA87BC9B21EC85370002D72D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BA87BC9921EC85370002D72D /* LaunchScreen.storyboard */; }; 18 | BA87BCA321EC88510002D72D /* MobileNet.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = BA87BCA221EC88510002D72D /* MobileNet.mlmodel */; }; 19 | BAEE10732229460800227EF3 /* UIImage+CVPixelBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAEE10722229460800227EF3 /* UIImage+CVPixelBuffer.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | BA42112621F0C38700326022 /* loading-bar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "loading-bar.png"; sourceTree = ""; }; 24 | BA87BC8A21EC85350002D72D /* TestingMLAllImages.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestingMLAllImages.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | BA87BC8D21EC85350002D72D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | BA87BC8F21EC85350002D72D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | BA87BC9221EC85350002D72D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | BA87BC9521EC85350002D72D /* TestingMLAllImages.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TestingMLAllImages.xcdatamodel; sourceTree = ""; }; 29 | BA87BC9721EC85370002D72D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | BA87BC9A21EC85370002D72D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | BA87BC9C21EC85370002D72D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | BA87BCA221EC88510002D72D /* MobileNet.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = MobileNet.mlmodel; sourceTree = ""; }; 33 | BAEE10722229460800227EF3 /* UIImage+CVPixelBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+CVPixelBuffer.swift"; sourceTree = ""; }; 34 | D45A5AB2934828B1FA3193DD /* Pods-TestingMLAllImages.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TestingMLAllImages.debug.xcconfig"; path = "Target Support Files/Pods-TestingMLAllImages/Pods-TestingMLAllImages.debug.xcconfig"; sourceTree = ""; }; 35 | FA89F5B21B3052A2DD469522 /* Pods-TestingMLAllImages.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TestingMLAllImages.release.xcconfig"; path = "Target Support Files/Pods-TestingMLAllImages/Pods-TestingMLAllImages.release.xcconfig"; sourceTree = ""; }; 36 | FA8EA2D371856AC6D1C8D452 /* Pods_TestingMLAllImages.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TestingMLAllImages.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | BA87BC8721EC85350002D72D /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 258A4FF9DC741B0A07CF63FD /* Pods_TestingMLAllImages.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 1ED0E4EE49164D67242145CB /* Pods */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | D45A5AB2934828B1FA3193DD /* Pods-TestingMLAllImages.debug.xcconfig */, 55 | FA89F5B21B3052A2DD469522 /* Pods-TestingMLAllImages.release.xcconfig */, 56 | ); 57 | path = Pods; 58 | sourceTree = ""; 59 | }; 60 | 3BD088A6F1B0EACE54C2A262 /* Frameworks */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | FA8EA2D371856AC6D1C8D452 /* Pods_TestingMLAllImages.framework */, 64 | ); 65 | name = Frameworks; 66 | sourceTree = ""; 67 | }; 68 | BA87BC8121EC85350002D72D = { 69 | isa = PBXGroup; 70 | children = ( 71 | BA87BC8C21EC85350002D72D /* TestingMLAllImages */, 72 | BA87BC8B21EC85350002D72D /* Products */, 73 | 1ED0E4EE49164D67242145CB /* Pods */, 74 | 3BD088A6F1B0EACE54C2A262 /* Frameworks */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | BA87BC8B21EC85350002D72D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | BA87BC8A21EC85350002D72D /* TestingMLAllImages.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | BA87BC8C21EC85350002D72D /* TestingMLAllImages */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | BA87BC8D21EC85350002D72D /* AppDelegate.swift */, 90 | BA87BC8F21EC85350002D72D /* ViewController.swift */, 91 | BA87BC9121EC85350002D72D /* Main.storyboard */, 92 | BAEE10722229460800227EF3 /* UIImage+CVPixelBuffer.swift */, 93 | BA87BC9721EC85370002D72D /* Assets.xcassets */, 94 | BA87BCA221EC88510002D72D /* MobileNet.mlmodel */, 95 | BA87BC9921EC85370002D72D /* LaunchScreen.storyboard */, 96 | BA42112621F0C38700326022 /* loading-bar.png */, 97 | BA87BC9C21EC85370002D72D /* Info.plist */, 98 | BA87BC9421EC85350002D72D /* TestingMLAllImages.xcdatamodeld */, 99 | ); 100 | path = TestingMLAllImages; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | BA87BC8921EC85350002D72D /* TestingMLAllImages */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = BA87BC9F21EC85370002D72D /* Build configuration list for PBXNativeTarget "TestingMLAllImages" */; 109 | buildPhases = ( 110 | 262D65C4C17A97344E419F0D /* [CP] Check Pods Manifest.lock */, 111 | BA87BC8621EC85350002D72D /* Sources */, 112 | BA87BC8721EC85350002D72D /* Frameworks */, 113 | BA87BC8821EC85350002D72D /* Resources */, 114 | BBE3896CFDE14A93C2B5C320 /* [CP] Embed Pods Frameworks */, 115 | ); 116 | buildRules = ( 117 | ); 118 | dependencies = ( 119 | ); 120 | name = TestingMLAllImages; 121 | productName = TestingMLAllImages; 122 | productReference = BA87BC8A21EC85350002D72D /* TestingMLAllImages.app */; 123 | productType = "com.apple.product-type.application"; 124 | }; 125 | /* End PBXNativeTarget section */ 126 | 127 | /* Begin PBXProject section */ 128 | BA87BC8221EC85350002D72D /* Project object */ = { 129 | isa = PBXProject; 130 | attributes = { 131 | LastSwiftUpdateCheck = 1000; 132 | LastUpgradeCheck = 1000; 133 | ORGANIZATIONNAME = "Alexey Klygin"; 134 | TargetAttributes = { 135 | BA87BC8921EC85350002D72D = { 136 | CreatedOnToolsVersion = 10.0; 137 | }; 138 | }; 139 | }; 140 | buildConfigurationList = BA87BC8521EC85350002D72D /* Build configuration list for PBXProject "TestingMLAllImages" */; 141 | compatibilityVersion = "Xcode 9.3"; 142 | developmentRegion = en; 143 | hasScannedForEncodings = 0; 144 | knownRegions = ( 145 | en, 146 | Base, 147 | ); 148 | mainGroup = BA87BC8121EC85350002D72D; 149 | productRefGroup = BA87BC8B21EC85350002D72D /* Products */; 150 | projectDirPath = ""; 151 | projectRoot = ""; 152 | targets = ( 153 | BA87BC8921EC85350002D72D /* TestingMLAllImages */, 154 | ); 155 | }; 156 | /* End PBXProject section */ 157 | 158 | /* Begin PBXResourcesBuildPhase section */ 159 | BA87BC8821EC85350002D72D /* Resources */ = { 160 | isa = PBXResourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | BA87BC9B21EC85370002D72D /* LaunchScreen.storyboard in Resources */, 164 | BA87BC9821EC85370002D72D /* Assets.xcassets in Resources */, 165 | BA87BC9321EC85350002D72D /* Main.storyboard in Resources */, 166 | BA42112721F0C38700326022 /* loading-bar.png in Resources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXResourcesBuildPhase section */ 171 | 172 | /* Begin PBXShellScriptBuildPhase section */ 173 | 262D65C4C17A97344E419F0D /* [CP] Check Pods Manifest.lock */ = { 174 | isa = PBXShellScriptBuildPhase; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | ); 178 | inputFileListPaths = ( 179 | ); 180 | inputPaths = ( 181 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 182 | "${PODS_ROOT}/Manifest.lock", 183 | ); 184 | name = "[CP] Check Pods Manifest.lock"; 185 | outputFileListPaths = ( 186 | ); 187 | outputPaths = ( 188 | "$(DERIVED_FILE_DIR)/Pods-TestingMLAllImages-checkManifestLockResult.txt", 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | shellPath = /bin/sh; 192 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 193 | showEnvVarsInLog = 0; 194 | }; 195 | BBE3896CFDE14A93C2B5C320 /* [CP] Embed Pods Frameworks */ = { 196 | isa = PBXShellScriptBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | ); 200 | inputFileListPaths = ( 201 | ); 202 | inputPaths = ( 203 | "${PODS_ROOT}/Target Support Files/Pods-TestingMLAllImages/Pods-TestingMLAllImages-frameworks.sh", 204 | "${BUILT_PRODUCTS_DIR}/OpalImagePicker/OpalImagePicker.framework", 205 | ); 206 | name = "[CP] Embed Pods Frameworks"; 207 | outputFileListPaths = ( 208 | ); 209 | outputPaths = ( 210 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpalImagePicker.framework", 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | shellPath = /bin/sh; 214 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TestingMLAllImages/Pods-TestingMLAllImages-frameworks.sh\"\n"; 215 | showEnvVarsInLog = 0; 216 | }; 217 | /* End PBXShellScriptBuildPhase section */ 218 | 219 | /* Begin PBXSourcesBuildPhase section */ 220 | BA87BC8621EC85350002D72D /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | BA87BCA321EC88510002D72D /* MobileNet.mlmodel in Sources */, 225 | BA87BC9621EC85350002D72D /* TestingMLAllImages.xcdatamodeld in Sources */, 226 | BAEE10732229460800227EF3 /* UIImage+CVPixelBuffer.swift in Sources */, 227 | BA87BC9021EC85350002D72D /* ViewController.swift in Sources */, 228 | BA87BC8E21EC85350002D72D /* AppDelegate.swift in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXSourcesBuildPhase section */ 233 | 234 | /* Begin PBXVariantGroup section */ 235 | BA87BC9121EC85350002D72D /* Main.storyboard */ = { 236 | isa = PBXVariantGroup; 237 | children = ( 238 | BA87BC9221EC85350002D72D /* Base */, 239 | ); 240 | name = Main.storyboard; 241 | sourceTree = ""; 242 | }; 243 | BA87BC9921EC85370002D72D /* LaunchScreen.storyboard */ = { 244 | isa = PBXVariantGroup; 245 | children = ( 246 | BA87BC9A21EC85370002D72D /* Base */, 247 | ); 248 | name = LaunchScreen.storyboard; 249 | sourceTree = ""; 250 | }; 251 | /* End PBXVariantGroup section */ 252 | 253 | /* Begin XCBuildConfiguration section */ 254 | BA87BC9D21EC85370002D72D /* Debug */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | CLANG_ANALYZER_NONNULL = YES; 259 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_ENABLE_OBJC_WEAK = YES; 265 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 266 | CLANG_WARN_BOOL_CONVERSION = YES; 267 | CLANG_WARN_COMMA = YES; 268 | CLANG_WARN_CONSTANT_CONVERSION = YES; 269 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 271 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 272 | CLANG_WARN_EMPTY_BODY = YES; 273 | CLANG_WARN_ENUM_CONVERSION = YES; 274 | CLANG_WARN_INFINITE_RECURSION = YES; 275 | CLANG_WARN_INT_CONVERSION = YES; 276 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | CODE_SIGN_IDENTITY = "iPhone Developer"; 287 | COPY_PHASE_STRIP = NO; 288 | DEBUG_INFORMATION_FORMAT = dwarf; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | ENABLE_TESTABILITY = YES; 291 | GCC_C_LANGUAGE_STANDARD = gnu11; 292 | GCC_DYNAMIC_NO_PIC = NO; 293 | GCC_NO_COMMON_BLOCKS = YES; 294 | GCC_OPTIMIZATION_LEVEL = 0; 295 | GCC_PREPROCESSOR_DEFINITIONS = ( 296 | "DEBUG=1", 297 | "$(inherited)", 298 | ); 299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 301 | GCC_WARN_UNDECLARED_SELECTOR = YES; 302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 303 | GCC_WARN_UNUSED_FUNCTION = YES; 304 | GCC_WARN_UNUSED_VARIABLE = YES; 305 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 306 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 307 | MTL_FAST_MATH = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = iphoneos; 310 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 311 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 312 | }; 313 | name = Debug; 314 | }; 315 | BA87BC9E21EC85370002D72D /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_ENABLE_OBJC_WEAK = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | CODE_SIGN_IDENTITY = "iPhone Developer"; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 350 | ENABLE_NS_ASSERTIONS = NO; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu11; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 361 | MTL_ENABLE_DEBUG_INFO = NO; 362 | MTL_FAST_MATH = YES; 363 | SDKROOT = iphoneos; 364 | SWIFT_COMPILATION_MODE = wholemodule; 365 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 366 | VALIDATE_PRODUCT = YES; 367 | }; 368 | name = Release; 369 | }; 370 | BA87BCA021EC85370002D72D /* Debug */ = { 371 | isa = XCBuildConfiguration; 372 | baseConfigurationReference = D45A5AB2934828B1FA3193DD /* Pods-TestingMLAllImages.debug.xcconfig */; 373 | buildSettings = { 374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 375 | CODE_SIGN_STYLE = Automatic; 376 | DEVELOPMENT_TEAM = JCUHTJANQ3; 377 | INFOPLIST_FILE = TestingMLAllImages/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = ( 379 | "$(inherited)", 380 | "@executable_path/Frameworks", 381 | ); 382 | PRODUCT_BUNDLE_IDENTIFIER = Farcon.TestingMLAllImages; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | SWIFT_VERSION = 4.2; 385 | TARGETED_DEVICE_FAMILY = "1,2"; 386 | }; 387 | name = Debug; 388 | }; 389 | BA87BCA121EC85370002D72D /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | baseConfigurationReference = FA89F5B21B3052A2DD469522 /* Pods-TestingMLAllImages.release.xcconfig */; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | CODE_SIGN_STYLE = Automatic; 395 | DEVELOPMENT_TEAM = JCUHTJANQ3; 396 | INFOPLIST_FILE = TestingMLAllImages/Info.plist; 397 | LD_RUNPATH_SEARCH_PATHS = ( 398 | "$(inherited)", 399 | "@executable_path/Frameworks", 400 | ); 401 | PRODUCT_BUNDLE_IDENTIFIER = Farcon.TestingMLAllImages; 402 | PRODUCT_NAME = "$(TARGET_NAME)"; 403 | SWIFT_VERSION = 4.2; 404 | TARGETED_DEVICE_FAMILY = "1,2"; 405 | }; 406 | name = Release; 407 | }; 408 | /* End XCBuildConfiguration section */ 409 | 410 | /* Begin XCConfigurationList section */ 411 | BA87BC8521EC85350002D72D /* Build configuration list for PBXProject "TestingMLAllImages" */ = { 412 | isa = XCConfigurationList; 413 | buildConfigurations = ( 414 | BA87BC9D21EC85370002D72D /* Debug */, 415 | BA87BC9E21EC85370002D72D /* Release */, 416 | ); 417 | defaultConfigurationIsVisible = 0; 418 | defaultConfigurationName = Release; 419 | }; 420 | BA87BC9F21EC85370002D72D /* Build configuration list for PBXNativeTarget "TestingMLAllImages" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | BA87BCA021EC85370002D72D /* Debug */, 424 | BA87BCA121EC85370002D72D /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | /* End XCConfigurationList section */ 430 | 431 | /* Begin XCVersionGroup section */ 432 | BA87BC9421EC85350002D72D /* TestingMLAllImages.xcdatamodeld */ = { 433 | isa = XCVersionGroup; 434 | children = ( 435 | BA87BC9521EC85350002D72D /* TestingMLAllImages.xcdatamodel */, 436 | ); 437 | currentVersion = BA87BC9521EC85350002D72D /* TestingMLAllImages.xcdatamodel */; 438 | path = TestingMLAllImages.xcdatamodeld; 439 | sourceTree = ""; 440 | versionGroupType = wrapper.xcdatamodel; 441 | }; 442 | /* End XCVersionGroup section */ 443 | }; 444 | rootObject = BA87BC8221EC85350002D72D /* Project object */; 445 | } 446 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcodeproj/project.xcworkspace/xcuserdata/alexe.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farawayCC/ML-image-similarity/8e78e4eb2452799a7fd30dff5229a19c00086ab8/TestingMLAllImages.xcodeproj/project.xcworkspace/xcuserdata/alexe.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TestingMLAllImages.xcodeproj/xcuserdata/alexe.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcodeproj/xcuserdata/alexe.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TestingMLAllImages.xcscheme 8 | 9 | orderHint 10 | 3 11 | 12 | TestingMLAllImages.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 3 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TestingMLAllImages.xcworkspace/xcuserdata/alexe.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TestingMLAllImages/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TestingMLAllImages 4 | // 5 | // Created by Alexey Klygin on 14/01/2019. 6 | // Copyright © 2019 Alexey Klygin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | 18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 19 | // Override point for customization after application launch. 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(_ application: UIApplication) { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 26 | } 27 | 28 | func applicationDidEnterBackground(_ application: UIApplication) { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(_ application: UIApplication) { 34 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | func applicationDidBecomeActive(_ application: UIApplication) { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | func applicationWillTerminate(_ application: UIApplication) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | // Saves changes in the application's managed object context before the application terminates. 44 | self.saveContext() 45 | } 46 | 47 | // MARK: - Core Data stack 48 | 49 | lazy var persistentContainer: NSPersistentContainer = { 50 | /* 51 | The persistent container for the application. This implementation 52 | creates and returns a container, having loaded the store for the 53 | application to it. This property is optional since there are legitimate 54 | error conditions that could cause the creation of the store to fail. 55 | */ 56 | let container = NSPersistentContainer(name: "TestingMLAllImages") 57 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 58 | if let error = error as NSError? { 59 | // Replace this implementation with code to handle the error appropriately. 60 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 61 | 62 | /* 63 | Typical reasons for an error here include: 64 | * The parent directory does not exist, cannot be created, or disallows writing. 65 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 66 | * The device is out of space. 67 | * The store could not be migrated to the current model version. 68 | Check the error message to determine what the actual problem was. 69 | */ 70 | fatalError("Unresolved error \(error), \(error.userInfo)") 71 | } 72 | }) 73 | return container 74 | }() 75 | 76 | // MARK: - Core Data Saving support 77 | 78 | func saveContext () { 79 | let context = persistentContainer.viewContext 80 | if context.hasChanges { 81 | do { 82 | try context.save() 83 | } catch { 84 | // Replace this implementation with code to handle the error appropriately. 85 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 86 | let nserror = error as NSError 87 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)") 88 | } 89 | } 90 | } 91 | 92 | } 93 | 94 | -------------------------------------------------------------------------------- /TestingMLAllImages/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /TestingMLAllImages/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TestingMLAllImages/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /TestingMLAllImages/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 99 | 105 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /TestingMLAllImages/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryUsageDescription 6 | Please, hit OK button :) 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TestingMLAllImages/MobileNet.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farawayCC/ML-image-similarity/8e78e4eb2452799a7fd30dff5229a19c00086ab8/TestingMLAllImages/MobileNet.mlmodel -------------------------------------------------------------------------------- /TestingMLAllImages/TestingMLAllImages.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | TestingMLAllImages.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /TestingMLAllImages/TestingMLAllImages.xcdatamodeld/TestingMLAllImages.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /TestingMLAllImages/UIImage+CVPixelBuffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+CVPixelBuffer.swift 3 | // TestingMLAllImages 4 | // 5 | // Created by Alexey Klygin on 01/03/2019. 6 | // Copyright © 2019 Alexey Klygin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | public func pixelBuffer(width: Int, height: Int) -> CVPixelBuffer? { 13 | var maybePixelBuffer: CVPixelBuffer? 14 | let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, 15 | kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] 16 | let status = CVPixelBufferCreate(kCFAllocatorDefault, 17 | width, 18 | height, 19 | kCVPixelFormatType_32ARGB, 20 | attrs as CFDictionary, 21 | &maybePixelBuffer) 22 | 23 | guard status == kCVReturnSuccess, let pixelBuffer = maybePixelBuffer else { 24 | return nil 25 | } 26 | 27 | CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) 28 | let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer) 29 | 30 | guard let context = CGContext(data: pixelData, 31 | width: width, 32 | height: height, 33 | bitsPerComponent: 8, 34 | bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), 35 | space: CGColorSpaceCreateDeviceRGB(), 36 | bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) 37 | else { 38 | return nil 39 | } 40 | 41 | context.translateBy(x: 0, y: CGFloat(height)) 42 | context.scaleBy(x: 1, y: -1) 43 | 44 | UIGraphicsPushContext(context) 45 | self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) 46 | UIGraphicsPopContext() 47 | CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) 48 | 49 | return pixelBuffer 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TestingMLAllImages/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TestingMLAllImages 4 | // 5 | // Created by Alexey Klygin on 14/01/2019. 6 | // Copyright © 2019 Alexey Klygin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreML 11 | import Vision 12 | import ImageIO 13 | import Photos 14 | import OpalImagePicker 15 | 16 | 17 | /// Общий сценарий: Выбираем фото с помощью OpalImagePickerController, хранящиеся в images фото во viewDidAppear() превращаем в объекты структуры UserPhoto, тут же предсказывая для каждого элемента что на картинке (можно попробовать запустить предсказания параллельно). Запускаем перерисовку таблицы 18 | 19 | ///TODO: Помимо распараллеливания предсказаний, можно не хранить фото после выбора, а только ссылки на них в память 20 | 21 | struct UserPhoto { 22 | var image: UIImage 23 | var descriprions: Set 24 | var groupNumber: Int 25 | var isLastInGroup: Bool 26 | } 27 | 28 | class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OpalImagePickerControllerDelegate { 29 | 30 | //MARK: Outlets 31 | 32 | let model = MobileNet() 33 | typealias Prediction = (String, Double) 34 | 35 | var userPhotos = [UserPhoto]() 36 | var images = [CIImage]() 37 | var consumedTimes = [Double]() 38 | var groupNumber = 0 39 | var needToUpdate = false 40 | 41 | @IBOutlet var tableView: UITableView! 42 | @IBOutlet weak var totalObjects: UILabel! 43 | @IBOutlet weak var timeDifference: UILabel! 44 | @IBOutlet weak var currentStatusLabel: UILabel! 45 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 46 | 47 | //MARK: Main 48 | 49 | override func viewDidLoad() { 50 | super.viewDidLoad() 51 | tableView.delegate = self 52 | tableView.dataSource = self 53 | } 54 | 55 | override func viewDidAppear(_ animated: Bool) { 56 | super.viewDidAppear(animated) 57 | 58 | currentStatusLabel.text = "Loading photos..." 59 | activityIndicator.startAnimating() 60 | 61 | if needToUpdate { 62 | // Populate TableView with data 63 | for image in images { 64 | let exactDate = Date() 65 | currentStatusLabel.text = "Loading photos..." 66 | let predictions = predictUsingCoreML(image: UIImage(ciImage: image), predictionsCount: 2) 67 | 68 | userPhotos.append( 69 | UserPhoto( 70 | image: UIImage(ciImage: image), 71 | descriprions: convertPredictionsToSet(predictions: predictions), 72 | groupNumber: 0, 73 | isLastInGroup: false)) 74 | let endDate = Date().timeIntervalSince(exactDate) 75 | consumedTimes.append(endDate) 76 | let avgTime = Double(round(1000*calculateAverageTime(arr: consumedTimes))/1000) 77 | timeDifference.text = String(avgTime) + " sec per image" 78 | } 79 | 80 | // Deciding duplicates 81 | let groupsOfSimilarPhotos = getGroupsOfSimilarPhotos(userPhotos) 82 | userPhotos = [UserPhoto]() 83 | // Каждому элементу присваиваем групповой индекс 84 | for group in groupsOfSimilarPhotos { 85 | for i in 0.. 0) ? "Done" : "Waiting for photos") 101 | activityIndicator.stopAnimating() 102 | } 103 | 104 | //MARK: ML 105 | 106 | /* 107 | This uses the Core ML-generated MobileNet class directly. 108 | Downside of this method is that we need to convert the UIImage to a 109 | CVPixelBuffer object ourselves. Core ML does not resize the image for 110 | you, so it needs to be 224x224 because that's what the model expects. 111 | */ 112 | func predictUsingCoreML(image: UIImage, predictionsCount: Int) -> [Prediction] { 113 | if let pixelBuffer = image.pixelBuffer(width: 224, height: 224), 114 | let prediction = try? model.prediction(image: pixelBuffer) { 115 | return top(predictionsCount, prediction.classLabelProbs) 116 | } 117 | return [Prediction]() 118 | } 119 | 120 | 121 | //MARK: TableViewDelegate 122 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 123 | return userPhotos.count 124 | } 125 | 126 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 127 | let cell:MyCustomCell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! MyCustomCell 128 | let currentItem = userPhotos[indexPath.row] 129 | cell.myImageView.image = currentItem.image 130 | if currentItem.groupNumber % 2 == 0 { 131 | cell.backgroundColor = UIColor.white 132 | } else { 133 | cell.backgroundColor = UIColor.init(displayP3Red: 205/255, green: 205/255, blue: 205/255, alpha: 1) 134 | } 135 | cell.myCellLabel?.text = currentItem.descriprions.count == 0 ? "Loading..." : currentItem.descriprions.joined(separator: ", ") 136 | return cell 137 | } 138 | 139 | //MARK: Image Picker 140 | 141 | @IBAction func pickImagesPressed(_ sender: Any) { 142 | let imagePicker = OpalImagePickerController() 143 | imagePicker.allowedMediaTypes = Set([PHAssetMediaType.image]) 144 | imagePicker.maximumSelectionsAllowed = 100 145 | imagePicker.imagePickerDelegate = self 146 | present(imagePicker, animated: true, completion: nil) 147 | } 148 | 149 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset]) { 150 | needToUpdate = true 151 | var index = 0 152 | 153 | //Refresh data 154 | userPhotos = [UserPhoto]() 155 | images = [CIImage]() 156 | 157 | for asset in assets { 158 | images.append(getAssetThumbnail(asset: asset).0) 159 | totalObjects.text = "Object \(index+1) / \(assets.count)" 160 | index += 1 161 | } 162 | tableView.reloadData() 163 | presentedViewController?.dismiss(animated: true, completion: nil) 164 | } 165 | 166 | 167 | //MARK: Supp funcs 168 | 169 | private func getGroupsOfSimilarPhotos(_ userPhotos: [UserPhoto]) -> [[UserPhoto]] { 170 | var photosToCheck = userPhotos 171 | var similars = [[UserPhoto]]() 172 | while let leftHandPhoto = photosToCheck.first { 173 | var photosToIterate = photosToCheck 174 | photosToIterate.removeFirst() 175 | var hasIntersections = [UserPhoto]() 176 | while let rightHandPhoto = photosToIterate.first { 177 | if leftHandPhoto.descriprions.intersection(rightHandPhoto.descriprions).count > 0 { 178 | if hasIntersections.isEmpty { 179 | hasIntersections.append(leftHandPhoto) 180 | } 181 | hasIntersections.append(rightHandPhoto) 182 | } 183 | photosToIterate.removeFirst() 184 | } 185 | similars = hasIntersections.isEmpty ? similars: similars + [hasIntersections] 186 | photosToCheck.removeFirst() 187 | } 188 | print(similars) 189 | return similars 190 | } 191 | 192 | func calculateAverageTime(arr: [Double]) -> Double { 193 | var total = 0.0 194 | for value in arr { 195 | total += value 196 | } 197 | if arr.count != 0 { 198 | return total/Double(arr.count) 199 | } else { 200 | return 0; 201 | } 202 | } 203 | 204 | func convertPredictionsToSet(predictions: [Prediction]) -> Set { 205 | var someSet = Set() 206 | for predict in predictions { 207 | someSet.insert(predict.0) 208 | } 209 | return someSet 210 | } 211 | 212 | func top(_ k: Int, _ prob: [String: Double]) -> [Prediction] { 213 | precondition(k <= prob.count) 214 | 215 | return Array(prob.map { x in (x.key, x.value) } 216 | .sorted(by: { a, b -> Bool in a.1 > b.1 }) 217 | .prefix(through: k - 1)) 218 | } 219 | 220 | func getAssetThumbnail(asset: PHAsset) -> (CIImage, Int) { 221 | let manager = PHImageManager.default() 222 | let option = PHImageRequestOptions() 223 | var resultImage = CIImage() 224 | var orientationRaw = 0 225 | option.isSynchronous = true 226 | manager.requestImageData(for: asset, options: option) { (data, str, orient, someUselessVariable) in 227 | resultImage = CIImage(data: data ?? Data()) ?? CIImage() 228 | orientationRaw = orient.rawValue 229 | } 230 | return (resultImage, orientationRaw) 231 | } 232 | } 233 | 234 | 235 | class MyCustomCell: UITableViewCell { 236 | @IBOutlet weak var myImageView: UIImageView! 237 | @IBOutlet weak var myCellLabel: UILabel! 238 | @IBOutlet weak var isDuplicatedLabel: UILabel! 239 | } 240 | -------------------------------------------------------------------------------- /TestingMLAllImages/loading-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farawayCC/ML-image-similarity/8e78e4eb2452799a7fd30dff5229a19c00086ab8/TestingMLAllImages/loading-bar.png --------------------------------------------------------------------------------