├── .gitignore ├── ImagePreparation ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── ImagePreparation.entitlements ├── Model │ ├── IMPSet.swift │ └── Annotations.swift ├── AppConfiguration.swift ├── AppDelegate.swift ├── MLCreate.py ├── Tools │ ├── NSImage+PhotoRect.swift │ ├── NSImage+Tools.swift │ └── FileHelper.swift ├── Info.plist ├── Document.swift ├── View │ └── DrawView.swift ├── ViewController │ ├── ViewController.swift │ └── ViewController+Actions.swift ├── MLCreate.ipynb └── Base.lproj │ └── Main.storyboard ├── Podfile ├── ImagePreparation.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── vobu.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── ImagePreparationTests ├── Info.plist └── ImagePreparationTests.swift ├── ImagePreparationUITests ├── Info.plist └── ImagePreparationUITests.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | xcshareddata 2 | xcuserdata 3 | .DS_Store 4 | docset/ 5 | lib/ 6 | Pods 7 | *.xcworkspace 8 | Podfile.lock 9 | -------------------------------------------------------------------------------- /ImagePreparation/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | 3 | use_frameworks! 4 | 5 | target 'ImagePreparation' do 6 | 7 | pod 'SSZipArchive' 8 | 9 | end 10 | 11 | -------------------------------------------------------------------------------- /ImagePreparation/ImagePreparation.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ImagePreparation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ImagePreparation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ImagePreparation/Model/IMPSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WorkArea.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 Volker Bublitz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct IMPSet { 12 | let workFolder: URL 13 | let imageFolder: URL 14 | var annotations: Annotations 15 | } 16 | -------------------------------------------------------------------------------- /ImagePreparation.xcodeproj/xcuserdata/vobu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ImagePreparation.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ImagePreparation/AppConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Config.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 Volker Bublitz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct AppConfiguration { 12 | static let normalizedBaseSizeInPixels:CGFloat = 500.0 13 | static let defaultLabel = "Unknown" 14 | static let appName = "ImagePreparation" 15 | static let mlExportDirName = "MLExport" 16 | } 17 | -------------------------------------------------------------------------------- /ImagePreparation/Model/Annotations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Annotations.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 16.09.18. 6 | // Copyright © 2018 Volker Bublitz. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct Annotations: Codable { 12 | var path:[String] 13 | var annotations:[Annotation] 14 | } 15 | 16 | struct Annotation: Codable { 17 | var coordinates:Coordinates 18 | var label:String 19 | } 20 | 21 | struct Coordinates: Codable { 22 | var width:Double 23 | var height:Double 24 | var x:Double 25 | var y:Double 26 | } 27 | -------------------------------------------------------------------------------- /ImagePreparation/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | func applicationDidFinishLaunching(_ aNotification: Notification) { 15 | // Insert code here to initialize your application 16 | } 17 | 18 | func applicationWillTerminate(_ aNotification: Notification) { 19 | FileHelper.cleanApplicationSupport() 20 | } 21 | 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /ImagePreparationTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ImagePreparationUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ImagePreparation/MLCreate.py: -------------------------------------------------------------------------------- 1 | import turicreate as tc 2 | import json 3 | 4 | # # Name your model 5 | modelName = "MyModel" 6 | 7 | # # Build train JSON SFrame 8 | with open('annotations.json') as j: 9 | annotations = json.load(j) 10 | 11 | annotationData = tc.SFrame(annotations) 12 | data = tc.load_images('images/') 13 | data = data.join(annotationData) 14 | trainData, testData = data.random_split(0.8) 15 | 16 | # # Check ground truth 17 | trainData['image_with_ground_truth'] = tc.object_detector.util.draw_bounding_boxes(trainData['image'], trainData['annotations']) 18 | # trainData.explore() 19 | 20 | # # Train the model 21 | model = tc.object_detector.create(trainData, feature="image", annotations="annotations", max_iterations=20) 22 | model.save(modelName + '.model') 23 | 24 | # # Predictions 25 | predictions = model.predict(testData, confidence_threshold=0.0, verbose=True) 26 | # predictions.explore() 27 | metrics = model.evaluate(testData) 28 | print('mAP: {:.1%}'.format(metrics['mean_average_precision_50'])) 29 | # metrics 30 | 31 | # # Export model 32 | model.export_coreml(modelName + '.mlmodel') 33 | -------------------------------------------------------------------------------- /ImagePreparation/Tools/NSImage+PhotoRect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage+PhotoRect.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 16.09.18. 6 | // Copyright © 2018 Volker Bublitz. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSImageView { 12 | 13 | func photoReduction() -> CGFloat { 14 | guard let size = self.image?.size else { 15 | return 1.0 16 | } 17 | let iFrame = self.bounds 18 | let xRatio = NSWidth(iFrame)/size.width 19 | let yRatio = NSHeight(iFrame)/size.height 20 | return min(xRatio, yRatio) 21 | } 22 | 23 | func photoRectInImageView() -> NSRect { 24 | guard let size = self.image?.size else { 25 | return NSRect.zero 26 | } 27 | let iBounds = self.bounds 28 | let reduction = self.photoReduction() 29 | let width = size.width * reduction 30 | let height = size.height * reduction 31 | let x = (iBounds.size.width - width)/2.0 32 | let y = (iBounds.size.height - height)/2.0 33 | return NSRect(x: x, y: y, width: width, height: height) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ImagePreparationTests/ImagePreparationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePreparationTests.swift 3 | // ImagePreparationTests 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ImagePreparation 11 | 12 | class ImagePreparationTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ImagePreparation/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 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # turi-annotate-od 2 | Simple Mac App to create annotations and prepare images for Object Detection training with Turi Create 3 | 4 | Purpose 5 | -------------- 6 | 7 | When you need to prepare your images and add annotations for training with [Turi Create](https://github.com/apple/turicreate), you can use this little Mac App ("ImagePreparation"). 8 | 9 | ## Installation 10 | 11 | Just clone this project and run ```pod install```. 12 | Open the created workspace in Xcode 10 on your Mac and build and run the ImagePreparation target. 13 | 14 | ## Usage 15 | 16 | After starting up, you'll see an empty window. You can import your images now by selecting ```File -> Import Pictures...```. 17 | For each imported picture you can now draw a bounding box and enter a label in the text field. You can save and open your ongoing work 18 | as 'mlp' files. When you're ready to go, you can select ```File -> Export ML Data...```. 19 | This will export your work in a folder called MLExport. In addition, it will add a python script and [Jupyter Notebook](http://jupyter.org) file. 20 | 21 | Note: I'm using [Anaconda](https://www.anaconda.com) with Python 3.6 and have set up a virtual environment where i installed version 5.0 of Turi Create. You can 22 | start Jupyter Notebook from your export folder and use the included script to train your model. 23 | 24 | -------------------------------------------------------------------------------- /ImagePreparationUITests/ImagePreparationUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePreparationUITests.swift 3 | // ImagePreparationUITests 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ImagePreparationUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ImagePreparation/Tools/NSImage+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage+Resize.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 16.09.18. 6 | // Copyright © 2018 Volker Bublitz. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSImage { 12 | 13 | func resizedImage(w: CGFloat, h: CGFloat) -> NSImage { 14 | let destSize = NSMakeSize(w, h) 15 | let newImage = NSImage(size: destSize) 16 | newImage.lockFocus() 17 | self.draw(in: NSMakeRect(0, 0, destSize.width, destSize.height), from: NSMakeRect(0, 0, self.size.width, self.size.height), operation: .destinationOver, fraction: CGFloat(1)) 18 | newImage.unlockFocus() 19 | return newImage 20 | } 21 | 22 | func writeToFile(file: URL, usingType type: NSBitmapImageRep.FileType) { 23 | let properties = [NSBitmapImageRep.PropertyKey.compressionFactor: 1.0] 24 | guard 25 | let imageData = tiffRepresentation, 26 | let imageRep = NSBitmapImageRep(data: imageData), 27 | let fileData = imageRep.representation(using: type, properties: properties) else { 28 | return 29 | } 30 | try? fileData.write(to: file) 31 | } 32 | 33 | func pixelSize() -> NSSize { 34 | guard let h = representations.first?.pixelsHigh, 35 | let w = representations.first?.pixelsWide else { 36 | return size 37 | } 38 | return NSSize(width: w, height: h) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /ImagePreparation/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | mlp 13 | 14 | CFBundleTypeIconFile 15 | 16 | CFBundleTypeName 17 | DocumentType 18 | CFBundleTypeOSTypes 19 | 20 | ???? 21 | 22 | CFBundleTypeRole 23 | Editor 24 | NSDocumentClass 25 | $(PRODUCT_MODULE_NAME).Document 26 | 27 | 28 | CFBundleExecutable 29 | $(EXECUTABLE_NAME) 30 | CFBundleIconFile 31 | 32 | CFBundleIdentifier 33 | $(PRODUCT_BUNDLE_IDENTIFIER) 34 | CFBundleInfoDictionaryVersion 35 | 6.0 36 | CFBundleName 37 | $(PRODUCT_NAME) 38 | CFBundlePackageType 39 | APPL 40 | CFBundleShortVersionString 41 | 1.0 42 | CFBundleVersion 43 | 1 44 | LSMinimumSystemVersion 45 | $(MACOSX_DEPLOYMENT_TARGET) 46 | NSHumanReadableCopyright 47 | Copyright © 2018 vobu. All rights reserved. 48 | NSMainStoryboardFile 49 | Main 50 | NSPrincipalClass 51 | NSApplication 52 | 53 | 54 | -------------------------------------------------------------------------------- /ImagePreparation/Tools/FileHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileHelper.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class FileHelper: NSObject { 12 | 13 | static func workImagesUrl(workFolder: URL) -> URL { 14 | let imageFolder = workFolder.appendingPathComponent("images") 15 | assureDirectoryIsAvailable(imageFolder) 16 | return imageFolder 17 | } 18 | 19 | static func annotationsUrl(workFolder: URL) -> URL { 20 | return workFolder.appendingPathComponent("annotations.json") 21 | } 22 | 23 | static func workFolderURL() -> URL { 24 | let uuid = UUID().uuidString 25 | guard let workFolder = applicationSupportUrl()?.appendingPathComponent(uuid) else { 26 | fatalError("unable to work with application support files") 27 | } 28 | assureDirectoryIsAvailable(workFolder) 29 | return workFolder 30 | } 31 | 32 | static func cleanApplicationSupport() { 33 | guard let dir = applicationSupportUrl(), 34 | let urls = try? FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil, options: []) else { 35 | return 36 | } 37 | urls.forEach { (url) in 38 | try? FileManager.default.removeItem(at: url) 39 | } 40 | } 41 | 42 | private static func assureDirectoryIsAvailable(_ url: URL) { 43 | let fm = FileManager.default 44 | if !fm.fileExists(atPath: url.path) { 45 | do { 46 | try fm.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) 47 | } 48 | catch let error { 49 | fatalError("Stopping application - no access to working directory " + error.localizedDescription) 50 | } 51 | } 52 | } 53 | 54 | private static func applicationSupportUrl() -> URL? { 55 | if let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first { 56 | let appDirectory = url.appendingPathComponent(Bundle.main.bundleIdentifier ?? AppConfiguration.appName) 57 | return appDirectory 58 | } 59 | return nil 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ImagePreparation/Document.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Document.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SSZipArchive 11 | 12 | class Document: NSDocument { 13 | 14 | var impSet:IMPSet 15 | 16 | override init() { 17 | let workFolder = FileHelper.workFolderURL() 18 | let imageFolder = FileHelper.workImagesUrl(workFolder: workFolder) 19 | impSet = IMPSet(workFolder: workFolder, imageFolder: imageFolder, annotations: Annotations(path: [], annotations: [])) 20 | super.init() 21 | } 22 | 23 | override class var autosavesInPlace: Bool { 24 | return true 25 | } 26 | 27 | override func makeWindowControllers() { 28 | // Returns the Storyboard that contains your Document window. 29 | let storyboard = NSStoryboard(name: "Main", bundle: nil) 30 | let windowController = storyboard.instantiateController(withIdentifier: "Document Window Controller") as! NSWindowController 31 | self.addWindowController(windowController) 32 | // Set represented object of ViewController 33 | if let viewController: ViewController = windowController.contentViewController as! ViewController? { 34 | viewController.representedObject = self 35 | viewController.select(index: 0) 36 | } 37 | } 38 | 39 | override func write(to url: URL, ofType typeName: String) throws { 40 | try JSONEncoder().encode(impSet.annotations).write(to: FileHelper.annotationsUrl(workFolder: impSet.workFolder)) 41 | SSZipArchive.createZipFile(atPath: url.path, withContentsOfDirectory: impSet.workFolder.path) 42 | } 43 | 44 | override func read(from url: URL, ofType typeName: String) throws { 45 | SSZipArchive.unzipFile(atPath: url.path, toDestination: impSet.workFolder.path) 46 | guard FileManager.default.fileExists(atPath: impSet.imageFolder.path) else { 47 | try? FileManager.default.removeItem(at: impSet.workFolder) 48 | throw NSError(domain: NSCocoaErrorDomain, code: NSFileReadUnsupportedSchemeError, userInfo: nil) 49 | } 50 | let jsonUrl = FileHelper.annotationsUrl(workFolder: impSet.workFolder) 51 | let annotations:Annotations = try JSONDecoder().decode(Annotations.self, from: Data(contentsOf: jsonUrl)) 52 | impSet.annotations = annotations 53 | } 54 | 55 | override func close() { 56 | super.close() 57 | try? FileManager.default.removeItem(at: impSet.workFolder) 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ImagePreparation/View/DrawView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DrawView.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 16.09.18. 6 | // Copyright © 2018 Volker Bublitz. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | protocol DrawViewDelegate { 12 | func drawView(_ drawView: DrawView, didDrawRect rect: NSRect) 13 | func drawViewShouldStartDrawing(_ drawView: DrawView) -> Bool 14 | } 15 | 16 | class DrawView: NSView { 17 | 18 | var delegate: DrawViewDelegate? 19 | var startingPoint = NSPoint.zero 20 | var endPoint = NSPoint.zero 21 | 22 | var path: NSBezierPath = NSBezierPath() 23 | var rect: NSRect = NSRect.zero 24 | 25 | func drawMLRect(rect: NSRect) { 26 | startingPoint = rect.origin 27 | endPoint = NSPoint(x: rect.origin.x + rect.size.width, y: rect.origin.y + rect.size.height) 28 | updateMLRect() 29 | } 30 | 31 | override func mouseDown(with event: NSEvent) { 32 | guard delegate?.drawViewShouldStartDrawing(self) ?? false else { 33 | return 34 | } 35 | startingPoint = convert(event.locationInWindow, from: nil) 36 | updateMLRect() 37 | } 38 | 39 | override func mouseDragged(with event: NSEvent) { 40 | guard delegate?.drawViewShouldStartDrawing(self) ?? false else { 41 | return 42 | } 43 | endPoint = convert(event.locationInWindow, from: nil) 44 | updateMLRect() 45 | } 46 | 47 | override func mouseUp(with event: NSEvent) { 48 | guard delegate?.drawViewShouldStartDrawing(self) ?? false else { 49 | return 50 | } 51 | let originX = min(startingPoint.x, endPoint.x) 52 | let originY = min(startingPoint.y, endPoint.y) 53 | let width = abs(startingPoint.x - endPoint.x) 54 | let height = abs(startingPoint.y - endPoint.y) 55 | delegate?.drawView(self, didDrawRect: NSRect(x: originX, y: originY, width: width, height: height)) 56 | } 57 | 58 | override func draw(_ dirtyRect: NSRect) { 59 | super.draw(dirtyRect) 60 | 61 | NSColor.black.set() 62 | 63 | path.lineJoinStyle = .round 64 | path.lineCapStyle = .round 65 | path.lineWidth = 2.0 66 | path.stroke() 67 | } 68 | 69 | private func updateMLRect() { 70 | path = NSBezierPath() 71 | path.move(to: startingPoint) 72 | path.line(to: NSPoint(x: startingPoint.x, y: endPoint.y)) 73 | path.line(to: NSPoint(x: endPoint.x, y: endPoint.y)) 74 | path.line(to: NSPoint(x: endPoint.x, y: startingPoint.y)) 75 | path.line(to: NSPoint(x: startingPoint.x, y: startingPoint.y)) 76 | needsDisplay = true 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ImagePreparation/ViewController/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController, DrawViewDelegate { 12 | 13 | var document:Document? 14 | private var currentIndex = 0 15 | 16 | @IBOutlet weak var textField: NSTextField! 17 | @IBOutlet weak var imageView: NSImageView! 18 | @IBOutlet weak var drawView: DrawView! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | drawView.delegate = self 23 | 24 | // Do any additional setup after loading the view. 25 | } 26 | 27 | override var representedObject: Any? { 28 | didSet { 29 | document = representedObject as? Document 30 | } 31 | } 32 | 33 | private func updateUI() { 34 | guard let path = document?.impSet.annotations.path[currentIndex], 35 | let realP = realPath(mlPath: path), 36 | let image = NSImage(contentsOfFile: realP), 37 | let annotation = document?.impSet.annotations.annotations[currentIndex] else { 38 | return 39 | } 40 | imageView.image = image 41 | textField.stringValue = annotation.label 42 | 43 | let imageRect = self.imageView.photoRectInImageView() 44 | let pixelSize = image.pixelSize() 45 | 46 | let scale = Double(pixelSize.width / imageRect.size.width) 47 | let oX = annotation.coordinates.x / scale 48 | let oY = (Double(pixelSize.height) - annotation.coordinates.y) / scale 49 | let oWidth = annotation.coordinates.width / scale 50 | let oHeight = annotation.coordinates.height / scale 51 | 52 | let rectX = Double(imageRect.origin.x) + (oX - oWidth / 2.0) 53 | let rectY = Double(imageRect.origin.y) + (oY - oHeight / 2.0) 54 | let rect = NSRect(x: rectX, y: rectY, width: oWidth, height: oHeight) 55 | 56 | self.drawView.drawMLRect(rect: rect) 57 | } 58 | 59 | private func realPath(mlPath: String) -> String? { 60 | guard let basePath = document?.impSet.workFolder.path else { 61 | return nil 62 | } 63 | return basePath + "/" + mlPath 64 | } 65 | 66 | //MARK: - Actions 67 | 68 | @IBAction func nextClicked(_ sender: Any) { 69 | updateLabel() 70 | guard currentIndex < (document?.impSet.annotations.path.count ?? 0) - 1 else { 71 | currentIndex = 0 72 | updateUI() 73 | return 74 | } 75 | currentIndex = currentIndex + 1 76 | updateUI() 77 | } 78 | 79 | @IBAction func previousClicked(_ sender: Any) { 80 | updateLabel() 81 | guard currentIndex > 0 else { 82 | currentIndex = (document?.impSet.annotations.path.count ?? 1) - 1 83 | updateUI() 84 | return 85 | } 86 | currentIndex = currentIndex - 1 87 | updateUI() 88 | } 89 | 90 | private func updateLabel() { 91 | if document?.impSet.annotations.annotations.count ?? 0 > currentIndex { 92 | document?.impSet.annotations.annotations[currentIndex].label = textField.stringValue 93 | } 94 | } 95 | 96 | // MARK: - Delegates 97 | 98 | func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool { 99 | document?.impSet.annotations.annotations[currentIndex].label = textField.stringValue 100 | return true 101 | } 102 | 103 | func drawViewShouldStartDrawing(_ drawView: DrawView) -> Bool { 104 | return document?.impSet.annotations.path.count ?? 0 > currentIndex 105 | } 106 | 107 | func drawView(_ drawView: DrawView, didDrawRect rect: NSRect) { 108 | updateLabel() 109 | guard let path = document?.impSet.annotations.path[currentIndex], 110 | let realP = realPath(mlPath: path), 111 | let image = NSImage(contentsOfFile: realP) else { 112 | return 113 | } 114 | let imageRect = imageView.photoRectInImageView() 115 | let pX = Double(rect.origin.x - imageRect.origin.x) 116 | let pY = Double(rect.origin.y - imageRect.origin.y) 117 | let pixelSize = image.pixelSize() 118 | let scale = Double(pixelSize.width / imageRect.size.width) 119 | let mlX = pX * scale 120 | let mlY = pY * scale 121 | let mlWidth = Double(rect.width) * scale 122 | let mlHeight = Double(rect.height) * scale 123 | let mlCenterX = mlX + mlWidth / 2.0 124 | let mlCenterY = mlY + mlHeight / 2.0 125 | document?.impSet.annotations.annotations[currentIndex].coordinates.height = mlHeight 126 | document?.impSet.annotations.annotations[currentIndex].coordinates.width = mlWidth 127 | document?.impSet.annotations.annotations[currentIndex].coordinates.x = mlCenterX 128 | document?.impSet.annotations.annotations[currentIndex].coordinates.y = Double(pixelSize.height) - mlCenterY 129 | } 130 | 131 | func select(index: Int) { 132 | guard document?.impSet.annotations.path.count ?? 0 > index else { 133 | return 134 | } 135 | currentIndex = index 136 | updateUI() 137 | } 138 | 139 | } 140 | 141 | -------------------------------------------------------------------------------- /ImagePreparation/ViewController/ViewController+Actions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController+Actions.swift 3 | // ImagePreparation 4 | // 5 | // Created by Volker Bublitz on 22.09.18. 6 | // Copyright © 2018 vobu. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension ViewController { 12 | 13 | @IBAction func importImages(_ sender: Any?) { 14 | guard let window = self.view.window else { 15 | return 16 | } 17 | let openPanel = folderOpenPanel() 18 | openPanel.beginSheetModal(for: window) { (response) in 19 | switch response { 20 | case NSApplication.ModalResponse.OK: 21 | self.importImages(fromUrl: openPanel.urls.first) 22 | default: 23 | break 24 | } 25 | } 26 | } 27 | 28 | @IBAction func exportML(_ sender: Any?) { 29 | guard let window = self.view.window else { 30 | return 31 | } 32 | let openPanel = folderOpenPanel() 33 | openPanel.canCreateDirectories = true 34 | openPanel.beginSheetModal(for: window) { (response) in 35 | switch response { 36 | case NSApplication.ModalResponse.OK: 37 | self.exportML(toUrl: openPanel.urls.first) 38 | default: 39 | break 40 | } 41 | } 42 | } 43 | 44 | private func exportML(toUrl url: URL?) { 45 | guard let url = url, 46 | let impSet = document?.impSet else { 47 | return 48 | } 49 | document?.save(self) 50 | let encoder = JSONEncoder() 51 | encoder.outputFormatting = .prettyPrinted 52 | try? encoder.encode(impSet.annotations).write(to: FileHelper.annotationsUrl(workFolder: impSet.workFolder)) 53 | var exportUrl = url.appendingPathComponent(AppConfiguration.mlExportDirName) 54 | var count = 2 55 | while FileManager.default.fileExists(atPath: exportUrl.path) { 56 | exportUrl = url.appendingPathComponent(String(format: "%@_%li", AppConfiguration.mlExportDirName, count)) 57 | count = count + 1 58 | } 59 | try? FileManager.default.copyItem(at: impSet.workFolder, to: exportUrl) 60 | copy(resource: "MLCreate", pathExtension: "ipynb", exportURL: exportUrl) 61 | copy(resource: "MLCreate", pathExtension: "py", exportURL: exportUrl) 62 | } 63 | 64 | private func copy(resource: String, pathExtension: String, exportURL: URL) { 65 | guard let url = Bundle.main.url(forResource: resource, withExtension: pathExtension) else { 66 | return 67 | } 68 | let targetUrl = exportURL.appendingPathComponent(resource).appendingPathExtension(pathExtension) 69 | try? FileManager.default.copyItem(at: url, to: targetUrl) 70 | } 71 | 72 | private func importImages(fromUrl url: URL?) { 73 | guard let url = url, 74 | let _ = document?.impSet else { 75 | return 76 | } 77 | addNormalizedImages(url: url) 78 | if document?.impSet.annotations.path.count ?? 0 > 0 { 79 | select(index: 0) 80 | } 81 | } 82 | 83 | private func addNormalizedImages(url u: URL) { 84 | guard let urls = try? FileManager.default.contentsOfDirectory(at: u, includingPropertiesForKeys: nil, options: []) else { 85 | return 86 | } 87 | 88 | var annotationList:[Annotation] = [] 89 | var mlPaths:[String] = [] 90 | 91 | urls.forEach { (fileUrl) in 92 | guard let targetImageUrl = document?.impSet.imageFolder.appendingPathComponent(fileUrl.lastPathComponent), 93 | let image = NSImage(contentsOf: fileUrl), 94 | image.size.width > 0, image.size.height > 0, 95 | Double(image.size.width) < Double.infinity, 96 | Double(image.size.height) < Double.infinity else { 97 | return 98 | } 99 | let width = image.size.width 100 | let height = image.size.height 101 | let scale = max(AppConfiguration.normalizedBaseSizeInPixels / width, 102 | AppConfiguration.normalizedBaseSizeInPixels / height) 103 | let targetSize = NSSize(width: width * scale, height: height * scale) 104 | let resized = image.resizedImage(w: targetSize.width, h: targetSize.height) 105 | resized.writeToFile(file: targetImageUrl, usingType: .jpeg) 106 | let mlPath:String = targetImageUrl.pathComponents.suffix(2).joined(separator: "/") 107 | mlPaths.append(mlPath) 108 | let pixelSize = resized.pixelSize() 109 | let coordinates = Coordinates(width: Double(pixelSize.width), height: Double(pixelSize.height), 110 | x: Double(pixelSize.width) / 2.0, y: Double(pixelSize.height) / 2.0) 111 | let annotation = Annotation(coordinates: coordinates, label: AppConfiguration.defaultLabel) 112 | annotationList.append(annotation) 113 | } 114 | document?.impSet.annotations.annotations.append(contentsOf: annotationList) 115 | document?.impSet.annotations.path.append(contentsOf: mlPaths) 116 | } 117 | 118 | private func folderOpenPanel() -> NSOpenPanel { 119 | let openPanel = NSOpenPanel() 120 | openPanel.canChooseDirectories = true 121 | openPanel.canChooseFiles = false 122 | return openPanel 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ImagePreparation/MLCreate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import turicreate as tc" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 2, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import json" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# Name your model" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 3, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "modelName = \"MyModel\"" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "# Build train JSON SFrame" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 4, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "with open('annotations.json') as j:\n", 51 | " annotations = json.load(j)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 5, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "annotationData = tc.SFrame(annotations)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 6, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "data = tc.load_images('images/')" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 7, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "data = data.join(annotationData)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 8, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "trainData, testData = data.random_split(0.8)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "# Check ground truth" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 9, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "trainData['image_with_ground_truth'] = \\\n", 104 | " tc.object_detector.util.draw_bounding_boxes(trainData['image'], trainData['annotations'])" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 10, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "data": { 114 | "text/html": [ 115 | "
Materializing SFrame
" 116 | ], 117 | "text/plain": [ 118 | "Materializing SFrame" 119 | ] 120 | }, 121 | "metadata": {}, 122 | "output_type": "display_data" 123 | } 124 | ], 125 | "source": [ 126 | "trainData.explore()" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "# Train the model" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 11, 139 | "metadata": {}, 140 | "outputs": [ 141 | { 142 | "name": "stdout", 143 | "output_type": "stream", 144 | "text": [ 145 | "Setting 'batch_size' to 32\n", 146 | "Using CPU to create model\n", 147 | "NOTE: If available, an AMD GPU can be leveraged on macOS 10.14+ for faster model creation\n", 148 | "+--------------+--------------+--------------+\n", 149 | "| Iteration | Loss | Elapsed Time |\n", 150 | "+--------------+--------------+--------------+\n", 151 | "| 1 | 4.544 | 10.3 |\n", 152 | "| 3 | 4.558 | 29.6 |\n", 153 | "| 5 | 4.566 | 48.6 |\n", 154 | "| 7 | 4.557 | 67.9 |\n", 155 | "| 9 | 4.558 | 87.4 |\n", 156 | "| 11 | 4.478 | 106.9 |\n", 157 | "| 13 | 4.485 | 126.2 |\n", 158 | "| 15 | 4.459 | 146.0 |\n", 159 | "| 17 | 4.378 | 165.6 |\n", 160 | "| 19 | 4.315 | 184.9 |\n", 161 | "| 21 | 4.288 | 204.5 |\n", 162 | "| 23 | 4.246 | 224.1 |\n", 163 | "| 25 | 4.206 | 244.0 |\n", 164 | "| 27 | 4.173 | 263.3 |\n", 165 | "| 29 | 4.119 | 282.7 |\n", 166 | "| 30 | 4.066 | 291.4 |\n", 167 | "+--------------+--------------+--------------+\n" 168 | ] 169 | } 170 | ], 171 | "source": [ 172 | "model = tc.object_detector.create(trainData, feature=\"image\", annotations=\"annotations\", max_iterations=30)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 12, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "model.save(modelName + '.model')" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "# Predictions" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 13, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "name": "stdout", 198 | "output_type": "stream", 199 | "text": [ 200 | "Predicting 1/14\n", 201 | "Predicting 14/14\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "predictions = model.predict(testData, confidence_threshold=0.0, verbose=True)" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 14, 212 | "metadata": {}, 213 | "outputs": [ 214 | { 215 | "data": { 216 | "text/html": [ 217 | "
Materializing SFrame
" 218 | ], 219 | "text/plain": [ 220 | "Materializing SFrame" 221 | ] 222 | }, 223 | "metadata": {}, 224 | "output_type": "display_data" 225 | } 226 | ], 227 | "source": [ 228 | "predictions.explore()" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 15, 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "name": "stdout", 238 | "output_type": "stream", 239 | "text": [ 240 | "Predicting 1/14\n", 241 | "Predicting 14/14\n" 242 | ] 243 | } 244 | ], 245 | "source": [ 246 | "metrics = model.evaluate(testData)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 16, 252 | "metadata": {}, 253 | "outputs": [ 254 | { 255 | "name": "stdout", 256 | "output_type": "stream", 257 | "text": [ 258 | "mAP: 30.6%\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "print('mAP: {:.1%}'.format(metrics['mean_average_precision_50']))" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": 17, 269 | "metadata": {}, 270 | "outputs": [ 271 | { 272 | "data": { 273 | "text/plain": [ 274 | "{'average_precision_50': {'Feder': 0.30599078264015506},\n", 275 | " 'mean_average_precision_50': 0.30599078264015506}" 276 | ] 277 | }, 278 | "execution_count": 17, 279 | "metadata": {}, 280 | "output_type": "execute_result" 281 | } 282 | ], 283 | "source": [ 284 | "metrics" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "# Export model" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 18, 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [ 300 | "model.export_coreml(modelName + '.mlmodel')" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": null, 306 | "metadata": {}, 307 | "outputs": [], 308 | "source": [] 309 | } 310 | ], 311 | "metadata": { 312 | "kernelspec": { 313 | "display_name": "Python 3", 314 | "language": "python", 315 | "name": "python3" 316 | }, 317 | "language_info": { 318 | "codemirror_mode": { 319 | "name": "ipython", 320 | "version": 3 321 | }, 322 | "file_extension": ".py", 323 | "mimetype": "text/x-python", 324 | "name": "python", 325 | "nbconvert_exporter": "python", 326 | "pygments_lexer": "ipython3", 327 | "version": "3.6.5" 328 | } 329 | }, 330 | "nbformat": 4, 331 | "nbformat_minor": 2 332 | } 333 | -------------------------------------------------------------------------------- /ImagePreparation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DA24DA9D21569932006E3FE0 /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA24DA9C21569932006E3FE0 /* FileHelper.swift */; }; 11 | DA24DA9F2156C838006E3FE0 /* ViewController+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA24DA9E2156C838006E3FE0 /* ViewController+Actions.swift */; }; 12 | DA24DAA5215781E4006E3FE0 /* MLCreate.ipynb in Resources */ = {isa = PBXBuildFile; fileRef = DA24DAA3215781E3006E3FE0 /* MLCreate.ipynb */; }; 13 | DA24DAA6215781E4006E3FE0 /* MLCreate.py in Resources */ = {isa = PBXBuildFile; fileRef = DA24DAA4215781E3006E3FE0 /* MLCreate.py */; }; 14 | DA4E7D4A21568CA500A8F73C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D4921568CA500A8F73C /* AppDelegate.swift */; }; 15 | DA4E7D4C21568CA500A8F73C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D4B21568CA500A8F73C /* ViewController.swift */; }; 16 | DA4E7D4E21568CA500A8F73C /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D4D21568CA500A8F73C /* Document.swift */; }; 17 | DA4E7D5021568CA700A8F73C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA4E7D4F21568CA700A8F73C /* Assets.xcassets */; }; 18 | DA4E7D5321568CA700A8F73C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA4E7D5121568CA700A8F73C /* Main.storyboard */; }; 19 | DA4E7D5F21568CA700A8F73C /* ImagePreparationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D5E21568CA700A8F73C /* ImagePreparationTests.swift */; }; 20 | DA4E7D6A21568CA700A8F73C /* ImagePreparationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D6921568CA700A8F73C /* ImagePreparationUITests.swift */; }; 21 | DA4E7D7D21568D8D00A8F73C /* DrawView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D7C21568D8D00A8F73C /* DrawView.swift */; }; 22 | DA4E7D8121568DA200A8F73C /* NSImage+PhotoRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D7F21568DA200A8F73C /* NSImage+PhotoRect.swift */; }; 23 | DA4E7D8221568DA200A8F73C /* NSImage+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D8021568DA200A8F73C /* NSImage+Tools.swift */; }; 24 | DA4E7D8421568DB100A8F73C /* Annotations.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D8321568DB100A8F73C /* Annotations.swift */; }; 25 | DA4E7D8621568DB800A8F73C /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D8521568DB700A8F73C /* AppConfiguration.swift */; }; 26 | DA4E7D882156915300A8F73C /* IMPSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4E7D872156915300A8F73C /* IMPSet.swift */; }; 27 | E4979CFF6DEEFC3A2B714BE0 /* Pods_ImagePreparation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37519BFE4C9B578E38132C2A /* Pods_ImagePreparation.framework */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | DA4E7D5B21568CA700A8F73C /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = DA4E7D3E21568CA500A8F73C /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = DA4E7D4521568CA500A8F73C; 36 | remoteInfo = ImagePreparation; 37 | }; 38 | DA4E7D6621568CA700A8F73C /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = DA4E7D3E21568CA500A8F73C /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = DA4E7D4521568CA500A8F73C; 43 | remoteInfo = ImagePreparation; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 1C6ADD0C5EF32D27108AB089 /* Pods-ImagePreparation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImagePreparation.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ImagePreparation/Pods-ImagePreparation.debug.xcconfig"; sourceTree = ""; }; 49 | 37519BFE4C9B578E38132C2A /* Pods_ImagePreparation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImagePreparation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | ACEE2D56CD1B20C25B5E668A /* Pods-ImagePreparation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImagePreparation.release.xcconfig"; path = "Pods/Target Support Files/Pods-ImagePreparation/Pods-ImagePreparation.release.xcconfig"; sourceTree = ""; }; 51 | DA24DA9C21569932006E3FE0 /* FileHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileHelper.swift; sourceTree = ""; }; 52 | DA24DA9E2156C838006E3FE0 /* ViewController+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+Actions.swift"; sourceTree = ""; }; 53 | DA24DAA3215781E3006E3FE0 /* MLCreate.ipynb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MLCreate.ipynb; sourceTree = ""; }; 54 | DA24DAA4215781E3006E3FE0 /* MLCreate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MLCreate.py; sourceTree = ""; }; 55 | DA4E7D4621568CA500A8F73C /* ImagePreparation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImagePreparation.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | DA4E7D4921568CA500A8F73C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 57 | DA4E7D4B21568CA500A8F73C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 58 | DA4E7D4D21568CA500A8F73C /* Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; 59 | DA4E7D4F21568CA700A8F73C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 60 | DA4E7D5221568CA700A8F73C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 61 | DA4E7D5421568CA700A8F73C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | DA4E7D5521568CA700A8F73C /* ImagePreparation.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ImagePreparation.entitlements; sourceTree = ""; }; 63 | DA4E7D5A21568CA700A8F73C /* ImagePreparationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImagePreparationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | DA4E7D5E21568CA700A8F73C /* ImagePreparationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePreparationTests.swift; sourceTree = ""; }; 65 | DA4E7D6021568CA700A8F73C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | DA4E7D6521568CA700A8F73C /* ImagePreparationUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImagePreparationUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | DA4E7D6921568CA700A8F73C /* ImagePreparationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePreparationUITests.swift; sourceTree = ""; }; 68 | DA4E7D6B21568CA700A8F73C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | DA4E7D7C21568D8D00A8F73C /* DrawView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawView.swift; sourceTree = ""; }; 70 | DA4E7D7F21568DA200A8F73C /* NSImage+PhotoRect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSImage+PhotoRect.swift"; sourceTree = ""; }; 71 | DA4E7D8021568DA200A8F73C /* NSImage+Tools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSImage+Tools.swift"; sourceTree = ""; }; 72 | DA4E7D8321568DB100A8F73C /* Annotations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Annotations.swift; sourceTree = ""; }; 73 | DA4E7D8521568DB700A8F73C /* AppConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = ""; }; 74 | DA4E7D872156915300A8F73C /* IMPSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IMPSet.swift; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | DA4E7D4321568CA500A8F73C /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | E4979CFF6DEEFC3A2B714BE0 /* Pods_ImagePreparation.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | DA4E7D5721568CA700A8F73C /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | DA4E7D6221568CA700A8F73C /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | /* End PBXFrameworksBuildPhase section */ 101 | 102 | /* Begin PBXGroup section */ 103 | 6F4ECD81E2E1840690BD7416 /* Frameworks */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 37519BFE4C9B578E38132C2A /* Pods_ImagePreparation.framework */, 107 | ); 108 | name = Frameworks; 109 | sourceTree = ""; 110 | }; 111 | C3030F0A78F27F5F4D90A9A1 /* Pods */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 1C6ADD0C5EF32D27108AB089 /* Pods-ImagePreparation.debug.xcconfig */, 115 | ACEE2D56CD1B20C25B5E668A /* Pods-ImagePreparation.release.xcconfig */, 116 | ); 117 | name = Pods; 118 | sourceTree = ""; 119 | }; 120 | DA4E7D3D21568CA500A8F73C = { 121 | isa = PBXGroup; 122 | children = ( 123 | DA4E7D4821568CA500A8F73C /* ImagePreparation */, 124 | DA4E7D5D21568CA700A8F73C /* ImagePreparationTests */, 125 | DA4E7D6821568CA700A8F73C /* ImagePreparationUITests */, 126 | DA4E7D4721568CA500A8F73C /* Products */, 127 | C3030F0A78F27F5F4D90A9A1 /* Pods */, 128 | 6F4ECD81E2E1840690BD7416 /* Frameworks */, 129 | ); 130 | sourceTree = ""; 131 | }; 132 | DA4E7D4721568CA500A8F73C /* Products */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | DA4E7D4621568CA500A8F73C /* ImagePreparation.app */, 136 | DA4E7D5A21568CA700A8F73C /* ImagePreparationTests.xctest */, 137 | DA4E7D6521568CA700A8F73C /* ImagePreparationUITests.xctest */, 138 | ); 139 | name = Products; 140 | sourceTree = ""; 141 | }; 142 | DA4E7D4821568CA500A8F73C /* ImagePreparation */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | DA4E7D5121568CA700A8F73C /* Main.storyboard */, 146 | DA4E7D4921568CA500A8F73C /* AppDelegate.swift */, 147 | DA4E7D4D21568CA500A8F73C /* Document.swift */, 148 | DA4E7D8521568DB700A8F73C /* AppConfiguration.swift */, 149 | DA4E7D7B21568D6900A8F73C /* ViewController */, 150 | DA4E7D7A21568D6500A8F73C /* View */, 151 | DA4E7D7921568D5C00A8F73C /* Model */, 152 | DA4E7D7E21568D9000A8F73C /* Tools */, 153 | DA4E7D4F21568CA700A8F73C /* Assets.xcassets */, 154 | DA4E7D5421568CA700A8F73C /* Info.plist */, 155 | DA24DAA3215781E3006E3FE0 /* MLCreate.ipynb */, 156 | DA24DAA4215781E3006E3FE0 /* MLCreate.py */, 157 | DA4E7D5521568CA700A8F73C /* ImagePreparation.entitlements */, 158 | ); 159 | path = ImagePreparation; 160 | sourceTree = ""; 161 | }; 162 | DA4E7D5D21568CA700A8F73C /* ImagePreparationTests */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | DA4E7D5E21568CA700A8F73C /* ImagePreparationTests.swift */, 166 | DA4E7D6021568CA700A8F73C /* Info.plist */, 167 | ); 168 | path = ImagePreparationTests; 169 | sourceTree = ""; 170 | }; 171 | DA4E7D6821568CA700A8F73C /* ImagePreparationUITests */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | DA4E7D6921568CA700A8F73C /* ImagePreparationUITests.swift */, 175 | DA4E7D6B21568CA700A8F73C /* Info.plist */, 176 | ); 177 | path = ImagePreparationUITests; 178 | sourceTree = ""; 179 | }; 180 | DA4E7D7921568D5C00A8F73C /* Model */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | DA4E7D8321568DB100A8F73C /* Annotations.swift */, 184 | DA4E7D872156915300A8F73C /* IMPSet.swift */, 185 | ); 186 | path = Model; 187 | sourceTree = ""; 188 | }; 189 | DA4E7D7A21568D6500A8F73C /* View */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | DA4E7D7C21568D8D00A8F73C /* DrawView.swift */, 193 | ); 194 | path = View; 195 | sourceTree = ""; 196 | }; 197 | DA4E7D7B21568D6900A8F73C /* ViewController */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | DA4E7D4B21568CA500A8F73C /* ViewController.swift */, 201 | DA24DA9E2156C838006E3FE0 /* ViewController+Actions.swift */, 202 | ); 203 | path = ViewController; 204 | sourceTree = ""; 205 | }; 206 | DA4E7D7E21568D9000A8F73C /* Tools */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | DA4E7D7F21568DA200A8F73C /* NSImage+PhotoRect.swift */, 210 | DA4E7D8021568DA200A8F73C /* NSImage+Tools.swift */, 211 | DA24DA9C21569932006E3FE0 /* FileHelper.swift */, 212 | ); 213 | path = Tools; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXGroup section */ 217 | 218 | /* Begin PBXNativeTarget section */ 219 | DA4E7D4521568CA500A8F73C /* ImagePreparation */ = { 220 | isa = PBXNativeTarget; 221 | buildConfigurationList = DA4E7D6E21568CA700A8F73C /* Build configuration list for PBXNativeTarget "ImagePreparation" */; 222 | buildPhases = ( 223 | 703C6BACDD9327458467302B /* [CP] Check Pods Manifest.lock */, 224 | DA4E7D4221568CA500A8F73C /* Sources */, 225 | DA4E7D4321568CA500A8F73C /* Frameworks */, 226 | DA4E7D4421568CA500A8F73C /* Resources */, 227 | FEBFB7FC06554A23FD79FF61 /* [CP] Embed Pods Frameworks */, 228 | ); 229 | buildRules = ( 230 | ); 231 | dependencies = ( 232 | ); 233 | name = ImagePreparation; 234 | productName = ImagePreparation; 235 | productReference = DA4E7D4621568CA500A8F73C /* ImagePreparation.app */; 236 | productType = "com.apple.product-type.application"; 237 | }; 238 | DA4E7D5921568CA700A8F73C /* ImagePreparationTests */ = { 239 | isa = PBXNativeTarget; 240 | buildConfigurationList = DA4E7D7121568CA700A8F73C /* Build configuration list for PBXNativeTarget "ImagePreparationTests" */; 241 | buildPhases = ( 242 | DA4E7D5621568CA700A8F73C /* Sources */, 243 | DA4E7D5721568CA700A8F73C /* Frameworks */, 244 | DA4E7D5821568CA700A8F73C /* Resources */, 245 | ); 246 | buildRules = ( 247 | ); 248 | dependencies = ( 249 | DA4E7D5C21568CA700A8F73C /* PBXTargetDependency */, 250 | ); 251 | name = ImagePreparationTests; 252 | productName = ImagePreparationTests; 253 | productReference = DA4E7D5A21568CA700A8F73C /* ImagePreparationTests.xctest */; 254 | productType = "com.apple.product-type.bundle.unit-test"; 255 | }; 256 | DA4E7D6421568CA700A8F73C /* ImagePreparationUITests */ = { 257 | isa = PBXNativeTarget; 258 | buildConfigurationList = DA4E7D7421568CA700A8F73C /* Build configuration list for PBXNativeTarget "ImagePreparationUITests" */; 259 | buildPhases = ( 260 | DA4E7D6121568CA700A8F73C /* Sources */, 261 | DA4E7D6221568CA700A8F73C /* Frameworks */, 262 | DA4E7D6321568CA700A8F73C /* Resources */, 263 | ); 264 | buildRules = ( 265 | ); 266 | dependencies = ( 267 | DA4E7D6721568CA700A8F73C /* PBXTargetDependency */, 268 | ); 269 | name = ImagePreparationUITests; 270 | productName = ImagePreparationUITests; 271 | productReference = DA4E7D6521568CA700A8F73C /* ImagePreparationUITests.xctest */; 272 | productType = "com.apple.product-type.bundle.ui-testing"; 273 | }; 274 | /* End PBXNativeTarget section */ 275 | 276 | /* Begin PBXProject section */ 277 | DA4E7D3E21568CA500A8F73C /* Project object */ = { 278 | isa = PBXProject; 279 | attributes = { 280 | LastSwiftUpdateCheck = 0940; 281 | LastUpgradeCheck = 0940; 282 | ORGANIZATIONNAME = vobu; 283 | TargetAttributes = { 284 | DA4E7D4521568CA500A8F73C = { 285 | CreatedOnToolsVersion = 9.4; 286 | LastSwiftMigration = 1000; 287 | }; 288 | DA4E7D5921568CA700A8F73C = { 289 | CreatedOnToolsVersion = 9.4; 290 | LastSwiftMigration = 1000; 291 | TestTargetID = DA4E7D4521568CA500A8F73C; 292 | }; 293 | DA4E7D6421568CA700A8F73C = { 294 | CreatedOnToolsVersion = 9.4; 295 | LastSwiftMigration = 1000; 296 | TestTargetID = DA4E7D4521568CA500A8F73C; 297 | }; 298 | }; 299 | }; 300 | buildConfigurationList = DA4E7D4121568CA500A8F73C /* Build configuration list for PBXProject "ImagePreparation" */; 301 | compatibilityVersion = "Xcode 9.3"; 302 | developmentRegion = en; 303 | hasScannedForEncodings = 0; 304 | knownRegions = ( 305 | en, 306 | Base, 307 | ); 308 | mainGroup = DA4E7D3D21568CA500A8F73C; 309 | productRefGroup = DA4E7D4721568CA500A8F73C /* Products */; 310 | projectDirPath = ""; 311 | projectRoot = ""; 312 | targets = ( 313 | DA4E7D4521568CA500A8F73C /* ImagePreparation */, 314 | DA4E7D5921568CA700A8F73C /* ImagePreparationTests */, 315 | DA4E7D6421568CA700A8F73C /* ImagePreparationUITests */, 316 | ); 317 | }; 318 | /* End PBXProject section */ 319 | 320 | /* Begin PBXResourcesBuildPhase section */ 321 | DA4E7D4421568CA500A8F73C /* Resources */ = { 322 | isa = PBXResourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | DA4E7D5021568CA700A8F73C /* Assets.xcassets in Resources */, 326 | DA4E7D5321568CA700A8F73C /* Main.storyboard in Resources */, 327 | DA24DAA5215781E4006E3FE0 /* MLCreate.ipynb in Resources */, 328 | DA24DAA6215781E4006E3FE0 /* MLCreate.py in Resources */, 329 | ); 330 | runOnlyForDeploymentPostprocessing = 0; 331 | }; 332 | DA4E7D5821568CA700A8F73C /* Resources */ = { 333 | isa = PBXResourcesBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | ); 337 | runOnlyForDeploymentPostprocessing = 0; 338 | }; 339 | DA4E7D6321568CA700A8F73C /* Resources */ = { 340 | isa = PBXResourcesBuildPhase; 341 | buildActionMask = 2147483647; 342 | files = ( 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | /* End PBXResourcesBuildPhase section */ 347 | 348 | /* Begin PBXShellScriptBuildPhase section */ 349 | 703C6BACDD9327458467302B /* [CP] Check Pods Manifest.lock */ = { 350 | isa = PBXShellScriptBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | ); 354 | inputPaths = ( 355 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 356 | "${PODS_ROOT}/Manifest.lock", 357 | ); 358 | name = "[CP] Check Pods Manifest.lock"; 359 | outputPaths = ( 360 | "$(DERIVED_FILE_DIR)/Pods-ImagePreparation-checkManifestLockResult.txt", 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | shellPath = /bin/sh; 364 | 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"; 365 | showEnvVarsInLog = 0; 366 | }; 367 | FEBFB7FC06554A23FD79FF61 /* [CP] Embed Pods Frameworks */ = { 368 | isa = PBXShellScriptBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | ); 372 | inputPaths = ( 373 | "${SRCROOT}/Pods/Target Support Files/Pods-ImagePreparation/Pods-ImagePreparation-frameworks.sh", 374 | "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework", 375 | ); 376 | name = "[CP] Embed Pods Frameworks"; 377 | outputPaths = ( 378 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | shellPath = /bin/sh; 382 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ImagePreparation/Pods-ImagePreparation-frameworks.sh\"\n"; 383 | showEnvVarsInLog = 0; 384 | }; 385 | /* End PBXShellScriptBuildPhase section */ 386 | 387 | /* Begin PBXSourcesBuildPhase section */ 388 | DA4E7D4221568CA500A8F73C /* Sources */ = { 389 | isa = PBXSourcesBuildPhase; 390 | buildActionMask = 2147483647; 391 | files = ( 392 | DA4E7D8621568DB800A8F73C /* AppConfiguration.swift in Sources */, 393 | DA4E7D4C21568CA500A8F73C /* ViewController.swift in Sources */, 394 | DA4E7D7D21568D8D00A8F73C /* DrawView.swift in Sources */, 395 | DA4E7D8421568DB100A8F73C /* Annotations.swift in Sources */, 396 | DA4E7D8121568DA200A8F73C /* NSImage+PhotoRect.swift in Sources */, 397 | DA4E7D4A21568CA500A8F73C /* AppDelegate.swift in Sources */, 398 | DA4E7D8221568DA200A8F73C /* NSImage+Tools.swift in Sources */, 399 | DA24DA9F2156C838006E3FE0 /* ViewController+Actions.swift in Sources */, 400 | DA4E7D4E21568CA500A8F73C /* Document.swift in Sources */, 401 | DA4E7D882156915300A8F73C /* IMPSet.swift in Sources */, 402 | DA24DA9D21569932006E3FE0 /* FileHelper.swift in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | DA4E7D5621568CA700A8F73C /* Sources */ = { 407 | isa = PBXSourcesBuildPhase; 408 | buildActionMask = 2147483647; 409 | files = ( 410 | DA4E7D5F21568CA700A8F73C /* ImagePreparationTests.swift in Sources */, 411 | ); 412 | runOnlyForDeploymentPostprocessing = 0; 413 | }; 414 | DA4E7D6121568CA700A8F73C /* Sources */ = { 415 | isa = PBXSourcesBuildPhase; 416 | buildActionMask = 2147483647; 417 | files = ( 418 | DA4E7D6A21568CA700A8F73C /* ImagePreparationUITests.swift in Sources */, 419 | ); 420 | runOnlyForDeploymentPostprocessing = 0; 421 | }; 422 | /* End PBXSourcesBuildPhase section */ 423 | 424 | /* Begin PBXTargetDependency section */ 425 | DA4E7D5C21568CA700A8F73C /* PBXTargetDependency */ = { 426 | isa = PBXTargetDependency; 427 | target = DA4E7D4521568CA500A8F73C /* ImagePreparation */; 428 | targetProxy = DA4E7D5B21568CA700A8F73C /* PBXContainerItemProxy */; 429 | }; 430 | DA4E7D6721568CA700A8F73C /* PBXTargetDependency */ = { 431 | isa = PBXTargetDependency; 432 | target = DA4E7D4521568CA500A8F73C /* ImagePreparation */; 433 | targetProxy = DA4E7D6621568CA700A8F73C /* PBXContainerItemProxy */; 434 | }; 435 | /* End PBXTargetDependency section */ 436 | 437 | /* Begin PBXVariantGroup section */ 438 | DA4E7D5121568CA700A8F73C /* Main.storyboard */ = { 439 | isa = PBXVariantGroup; 440 | children = ( 441 | DA4E7D5221568CA700A8F73C /* Base */, 442 | ); 443 | name = Main.storyboard; 444 | sourceTree = ""; 445 | }; 446 | /* End PBXVariantGroup section */ 447 | 448 | /* Begin XCBuildConfiguration section */ 449 | DA4E7D6C21568CA700A8F73C /* Debug */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | ALWAYS_SEARCH_USER_PATHS = NO; 453 | CLANG_ANALYZER_NONNULL = YES; 454 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 455 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 456 | CLANG_CXX_LIBRARY = "libc++"; 457 | CLANG_ENABLE_MODULES = YES; 458 | CLANG_ENABLE_OBJC_ARC = YES; 459 | CLANG_ENABLE_OBJC_WEAK = YES; 460 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 461 | CLANG_WARN_BOOL_CONVERSION = YES; 462 | CLANG_WARN_COMMA = YES; 463 | CLANG_WARN_CONSTANT_CONVERSION = YES; 464 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 465 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 466 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 467 | CLANG_WARN_EMPTY_BODY = YES; 468 | CLANG_WARN_ENUM_CONVERSION = YES; 469 | CLANG_WARN_INFINITE_RECURSION = YES; 470 | CLANG_WARN_INT_CONVERSION = YES; 471 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 472 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 473 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 474 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 475 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 476 | CLANG_WARN_STRICT_PROTOTYPES = YES; 477 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 478 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 479 | CLANG_WARN_UNREACHABLE_CODE = YES; 480 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 481 | CODE_SIGN_IDENTITY = "Mac Developer"; 482 | COPY_PHASE_STRIP = NO; 483 | DEBUG_INFORMATION_FORMAT = dwarf; 484 | ENABLE_STRICT_OBJC_MSGSEND = YES; 485 | ENABLE_TESTABILITY = YES; 486 | GCC_C_LANGUAGE_STANDARD = gnu11; 487 | GCC_DYNAMIC_NO_PIC = NO; 488 | GCC_NO_COMMON_BLOCKS = YES; 489 | GCC_OPTIMIZATION_LEVEL = 0; 490 | GCC_PREPROCESSOR_DEFINITIONS = ( 491 | "DEBUG=1", 492 | "$(inherited)", 493 | ); 494 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 495 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 496 | GCC_WARN_UNDECLARED_SELECTOR = YES; 497 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 498 | GCC_WARN_UNUSED_FUNCTION = YES; 499 | GCC_WARN_UNUSED_VARIABLE = YES; 500 | MACOSX_DEPLOYMENT_TARGET = 10.13; 501 | MTL_ENABLE_DEBUG_INFO = YES; 502 | ONLY_ACTIVE_ARCH = YES; 503 | SDKROOT = macosx; 504 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 505 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 506 | }; 507 | name = Debug; 508 | }; 509 | DA4E7D6D21568CA700A8F73C /* Release */ = { 510 | isa = XCBuildConfiguration; 511 | buildSettings = { 512 | ALWAYS_SEARCH_USER_PATHS = NO; 513 | CLANG_ANALYZER_NONNULL = YES; 514 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 515 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 516 | CLANG_CXX_LIBRARY = "libc++"; 517 | CLANG_ENABLE_MODULES = YES; 518 | CLANG_ENABLE_OBJC_ARC = YES; 519 | CLANG_ENABLE_OBJC_WEAK = YES; 520 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 521 | CLANG_WARN_BOOL_CONVERSION = YES; 522 | CLANG_WARN_COMMA = YES; 523 | CLANG_WARN_CONSTANT_CONVERSION = YES; 524 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 525 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 526 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 527 | CLANG_WARN_EMPTY_BODY = YES; 528 | CLANG_WARN_ENUM_CONVERSION = YES; 529 | CLANG_WARN_INFINITE_RECURSION = YES; 530 | CLANG_WARN_INT_CONVERSION = YES; 531 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 532 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 533 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 534 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 535 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 536 | CLANG_WARN_STRICT_PROTOTYPES = YES; 537 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 538 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 539 | CLANG_WARN_UNREACHABLE_CODE = YES; 540 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 541 | CODE_SIGN_IDENTITY = "Mac Developer"; 542 | COPY_PHASE_STRIP = NO; 543 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 544 | ENABLE_NS_ASSERTIONS = NO; 545 | ENABLE_STRICT_OBJC_MSGSEND = YES; 546 | GCC_C_LANGUAGE_STANDARD = gnu11; 547 | GCC_NO_COMMON_BLOCKS = YES; 548 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 549 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 550 | GCC_WARN_UNDECLARED_SELECTOR = YES; 551 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 552 | GCC_WARN_UNUSED_FUNCTION = YES; 553 | GCC_WARN_UNUSED_VARIABLE = YES; 554 | MACOSX_DEPLOYMENT_TARGET = 10.13; 555 | MTL_ENABLE_DEBUG_INFO = NO; 556 | SDKROOT = macosx; 557 | SWIFT_COMPILATION_MODE = wholemodule; 558 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 559 | }; 560 | name = Release; 561 | }; 562 | DA4E7D6F21568CA700A8F73C /* Debug */ = { 563 | isa = XCBuildConfiguration; 564 | baseConfigurationReference = 1C6ADD0C5EF32D27108AB089 /* Pods-ImagePreparation.debug.xcconfig */; 565 | buildSettings = { 566 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 567 | CODE_SIGN_IDENTITY = "Mac Developer"; 568 | CODE_SIGN_STYLE = Automatic; 569 | COMBINE_HIDPI_IMAGES = YES; 570 | DEVELOPMENT_TEAM = 96UYVTQDND; 571 | INFOPLIST_FILE = ImagePreparation/Info.plist; 572 | LD_RUNPATH_SEARCH_PATHS = ( 573 | "$(inherited)", 574 | "@executable_path/../Frameworks", 575 | ); 576 | PRODUCT_BUNDLE_IDENTIFIER = de.poolzeit.ImagePreparation; 577 | PRODUCT_NAME = "$(TARGET_NAME)"; 578 | PROVISIONING_PROFILE_SPECIFIER = ""; 579 | SWIFT_VERSION = 4.2; 580 | }; 581 | name = Debug; 582 | }; 583 | DA4E7D7021568CA700A8F73C /* Release */ = { 584 | isa = XCBuildConfiguration; 585 | baseConfigurationReference = ACEE2D56CD1B20C25B5E668A /* Pods-ImagePreparation.release.xcconfig */; 586 | buildSettings = { 587 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 588 | CODE_SIGN_IDENTITY = "Mac Developer"; 589 | CODE_SIGN_STYLE = Automatic; 590 | COMBINE_HIDPI_IMAGES = YES; 591 | DEVELOPMENT_TEAM = 96UYVTQDND; 592 | INFOPLIST_FILE = ImagePreparation/Info.plist; 593 | LD_RUNPATH_SEARCH_PATHS = ( 594 | "$(inherited)", 595 | "@executable_path/../Frameworks", 596 | ); 597 | PRODUCT_BUNDLE_IDENTIFIER = de.poolzeit.ImagePreparation; 598 | PRODUCT_NAME = "$(TARGET_NAME)"; 599 | PROVISIONING_PROFILE_SPECIFIER = ""; 600 | SWIFT_VERSION = 4.2; 601 | }; 602 | name = Release; 603 | }; 604 | DA4E7D7221568CA700A8F73C /* Debug */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 608 | BUNDLE_LOADER = "$(TEST_HOST)"; 609 | CODE_SIGN_STYLE = Automatic; 610 | COMBINE_HIDPI_IMAGES = YES; 611 | DEVELOPMENT_TEAM = 96UYVTQDND; 612 | INFOPLIST_FILE = ImagePreparationTests/Info.plist; 613 | LD_RUNPATH_SEARCH_PATHS = ( 614 | "$(inherited)", 615 | "@executable_path/../Frameworks", 616 | "@loader_path/../Frameworks", 617 | ); 618 | PRODUCT_BUNDLE_IDENTIFIER = de.poolzeit.ImagePreparationTests; 619 | PRODUCT_NAME = "$(TARGET_NAME)"; 620 | SWIFT_VERSION = 4.2; 621 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImagePreparation.app/Contents/MacOS/ImagePreparation"; 622 | }; 623 | name = Debug; 624 | }; 625 | DA4E7D7321568CA700A8F73C /* Release */ = { 626 | isa = XCBuildConfiguration; 627 | buildSettings = { 628 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 629 | BUNDLE_LOADER = "$(TEST_HOST)"; 630 | CODE_SIGN_STYLE = Automatic; 631 | COMBINE_HIDPI_IMAGES = YES; 632 | DEVELOPMENT_TEAM = 96UYVTQDND; 633 | INFOPLIST_FILE = ImagePreparationTests/Info.plist; 634 | LD_RUNPATH_SEARCH_PATHS = ( 635 | "$(inherited)", 636 | "@executable_path/../Frameworks", 637 | "@loader_path/../Frameworks", 638 | ); 639 | PRODUCT_BUNDLE_IDENTIFIER = de.poolzeit.ImagePreparationTests; 640 | PRODUCT_NAME = "$(TARGET_NAME)"; 641 | SWIFT_VERSION = 4.2; 642 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImagePreparation.app/Contents/MacOS/ImagePreparation"; 643 | }; 644 | name = Release; 645 | }; 646 | DA4E7D7521568CA700A8F73C /* Debug */ = { 647 | isa = XCBuildConfiguration; 648 | buildSettings = { 649 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 650 | CODE_SIGN_STYLE = Automatic; 651 | COMBINE_HIDPI_IMAGES = YES; 652 | DEVELOPMENT_TEAM = 96UYVTQDND; 653 | INFOPLIST_FILE = ImagePreparationUITests/Info.plist; 654 | LD_RUNPATH_SEARCH_PATHS = ( 655 | "$(inherited)", 656 | "@executable_path/../Frameworks", 657 | "@loader_path/../Frameworks", 658 | ); 659 | PRODUCT_BUNDLE_IDENTIFIER = de.poolzeit.ImagePreparationUITests; 660 | PRODUCT_NAME = "$(TARGET_NAME)"; 661 | SWIFT_VERSION = 4.2; 662 | TEST_TARGET_NAME = ImagePreparation; 663 | }; 664 | name = Debug; 665 | }; 666 | DA4E7D7621568CA700A8F73C /* Release */ = { 667 | isa = XCBuildConfiguration; 668 | buildSettings = { 669 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 670 | CODE_SIGN_STYLE = Automatic; 671 | COMBINE_HIDPI_IMAGES = YES; 672 | DEVELOPMENT_TEAM = 96UYVTQDND; 673 | INFOPLIST_FILE = ImagePreparationUITests/Info.plist; 674 | LD_RUNPATH_SEARCH_PATHS = ( 675 | "$(inherited)", 676 | "@executable_path/../Frameworks", 677 | "@loader_path/../Frameworks", 678 | ); 679 | PRODUCT_BUNDLE_IDENTIFIER = de.poolzeit.ImagePreparationUITests; 680 | PRODUCT_NAME = "$(TARGET_NAME)"; 681 | SWIFT_VERSION = 4.2; 682 | TEST_TARGET_NAME = ImagePreparation; 683 | }; 684 | name = Release; 685 | }; 686 | /* End XCBuildConfiguration section */ 687 | 688 | /* Begin XCConfigurationList section */ 689 | DA4E7D4121568CA500A8F73C /* Build configuration list for PBXProject "ImagePreparation" */ = { 690 | isa = XCConfigurationList; 691 | buildConfigurations = ( 692 | DA4E7D6C21568CA700A8F73C /* Debug */, 693 | DA4E7D6D21568CA700A8F73C /* Release */, 694 | ); 695 | defaultConfigurationIsVisible = 0; 696 | defaultConfigurationName = Release; 697 | }; 698 | DA4E7D6E21568CA700A8F73C /* Build configuration list for PBXNativeTarget "ImagePreparation" */ = { 699 | isa = XCConfigurationList; 700 | buildConfigurations = ( 701 | DA4E7D6F21568CA700A8F73C /* Debug */, 702 | DA4E7D7021568CA700A8F73C /* Release */, 703 | ); 704 | defaultConfigurationIsVisible = 0; 705 | defaultConfigurationName = Release; 706 | }; 707 | DA4E7D7121568CA700A8F73C /* Build configuration list for PBXNativeTarget "ImagePreparationTests" */ = { 708 | isa = XCConfigurationList; 709 | buildConfigurations = ( 710 | DA4E7D7221568CA700A8F73C /* Debug */, 711 | DA4E7D7321568CA700A8F73C /* Release */, 712 | ); 713 | defaultConfigurationIsVisible = 0; 714 | defaultConfigurationName = Release; 715 | }; 716 | DA4E7D7421568CA700A8F73C /* Build configuration list for PBXNativeTarget "ImagePreparationUITests" */ = { 717 | isa = XCConfigurationList; 718 | buildConfigurations = ( 719 | DA4E7D7521568CA700A8F73C /* Debug */, 720 | DA4E7D7621568CA700A8F73C /* Release */, 721 | ); 722 | defaultConfigurationIsVisible = 0; 723 | defaultConfigurationName = Release; 724 | }; 725 | /* End XCConfigurationList section */ 726 | }; 727 | rootObject = DA4E7D3E21568CA500A8F73C /* Project object */; 728 | } 729 | -------------------------------------------------------------------------------- /ImagePreparation/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 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 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 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | --------------------------------------------------------------------------------