From 109ebcaf864d0c0b8d50d4341c03d80ac0932b80 Mon Sep 17 00:00:00 2001 From: Michael Berg Date: Sun, 16 Sep 2018 13:01:24 +0200 Subject: [PATCH 1/5] Add Controllers for Camera and ImagePicker testing --- .../NSFWDetector.xcodeproj/project.pbxproj | 21 +- Example/NSFWDetector/AppDelegate.swift | 4 +- .../NSFWDetector/Base.lproj/LaunchScreen.xib | 14 +- .../NSFWDetector/Base.lproj/Main.storyboard | 220 +++++++++++++++++- .../NSFWDetector/CameraViewController.swift | 100 ++++++++ .../NSFWDetector/ImageViewController.swift | 43 ++++ Example/NSFWDetector/Info.plist | 2 + Example/NSFWDetector/ViewController.swift | 58 +++-- NSFWDetector/Classes/NSFWDetector.swift | 35 ++- 9 files changed, 448 insertions(+), 49 deletions(-) create mode 100644 Example/NSFWDetector/CameraViewController.swift create mode 100644 Example/NSFWDetector/ImageViewController.swift diff --git a/Example/NSFWDetector.xcodeproj/project.pbxproj b/Example/NSFWDetector.xcodeproj/project.pbxproj index 497a451..796483d 100644 --- a/Example/NSFWDetector.xcodeproj/project.pbxproj +++ b/Example/NSFWDetector.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; + 72CF15A121275D8500DA5C8D /* CameraViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72CF15A021275D8500DA5C8D /* CameraViewController.swift */; }; + 72CF15A5212C446800DA5C8D /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72CF15A4212C446800DA5C8D /* ImageViewController.swift */; }; A9B23BA8992A955A0357EFE1 /* Pods_NSFWDetector_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 671CDE7C580EF5140827DAF9 /* Pods_NSFWDetector_Example.framework */; }; D28DAC1F00B7BD001654B36A /* Pods_NSFWDetector_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BCAED1201A5A40E0325D6BF /* Pods_NSFWDetector_Tests.framework */; }; /* End PBXBuildFile section */ @@ -29,9 +31,9 @@ /* Begin PBXFileReference section */ 06B646A7AD50939DC189C2A7 /* Pods-NSFWDetector_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NSFWDetector_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-NSFWDetector_Example/Pods-NSFWDetector_Example.debug.xcconfig"; sourceTree = ""; }; - 0ABE73541A5F6034F52AD67A /* NSFWDetector.podspec */ = {isa = PBXFileReference; includeInIndex = 1; name = NSFWDetector.podspec; path = ../NSFWDetector.podspec; sourceTree = ""; }; + 0ABE73541A5F6034F52AD67A /* NSFWDetector.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = NSFWDetector.podspec; path = ../NSFWDetector.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 0B9B2661F1AC51A0AEAE4A33 /* Pods-NSFWDetector_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NSFWDetector_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-NSFWDetector_Tests/Pods-NSFWDetector_Tests.release.xcconfig"; sourceTree = ""; }; - 0F4D0ED7A8EE2FD5B16185E5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; name = README.md; path = ../README.md; sourceTree = ""; }; + 0F4D0ED7A8EE2FD5B16185E5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 2BCAED1201A5A40E0325D6BF /* Pods_NSFWDetector_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NSFWDetector_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 576E1996DC60639A1A212C32 /* Pods-NSFWDetector_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NSFWDetector_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-NSFWDetector_Example/Pods-NSFWDetector_Example.release.xcconfig"; sourceTree = ""; }; 607FACD01AFB9204008FA782 /* NSFWDetector_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NSFWDetector_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -45,7 +47,9 @@ 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 671CDE7C580EF5140827DAF9 /* Pods_NSFWDetector_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NSFWDetector_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D901F12ADFFD08DAE8156031 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; + 72CF15A021275D8500DA5C8D /* CameraViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraViewController.swift; sourceTree = ""; }; + 72CF15A4212C446800DA5C8D /* ImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewController.swift; sourceTree = ""; }; + D901F12ADFFD08DAE8156031 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; F12B89091D89F1EF999CDAF2 /* Pods-NSFWDetector_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NSFWDetector_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-NSFWDetector_Tests/Pods-NSFWDetector_Tests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -95,6 +99,8 @@ children = ( 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 607FACD71AFB9204008FA782 /* ViewController.swift */, + 72CF15A021275D8500DA5C8D /* CameraViewController.swift */, + 72CF15A4212C446800DA5C8D /* ImageViewController.swift */, 607FACD91AFB9204008FA782 /* Main.storyboard */, 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, @@ -208,10 +214,11 @@ attributes = { LastSwiftUpdateCheck = 0830; LastUpgradeCheck = 0830; - ORGANIZATIONNAME = CocoaPods; + ORGANIZATIONNAME = LOVOO; TargetAttributes = { 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; + DevelopmentTeam = KT48JMSBT8; LastSwiftMigration = 0900; }; 607FACE41AFB9204008FA782 = { @@ -324,6 +331,8 @@ files = ( 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, + 72CF15A5212C446800DA5C8D /* ImageViewController.swift in Sources */, + 72CF15A121275D8500DA5C8D /* CameraViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -469,7 +478,9 @@ baseConfigurationReference = 06B646A7AD50939DC189C2A7 /* Pods-NSFWDetector_Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = KT48JMSBT8; INFOPLIST_FILE = NSFWDetector/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; @@ -484,7 +495,9 @@ baseConfigurationReference = 576E1996DC60639A1A212C32 /* Pods-NSFWDetector_Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = KT48JMSBT8; INFOPLIST_FILE = NSFWDetector/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; diff --git a/Example/NSFWDetector/AppDelegate.swift b/Example/NSFWDetector/AppDelegate.swift index e2e24d2..76637b1 100644 --- a/Example/NSFWDetector/AppDelegate.swift +++ b/Example/NSFWDetector/AppDelegate.swift @@ -2,8 +2,8 @@ // AppDelegate.swift // NSFWDetector // -// Created by absoluteheike on 08/12/2018. -// Copyright (c) 2018 absoluteheike. All rights reserved. +// Created by Michael Berg on 08/12/2018. +// Copyright (c) 2018 LOVOO. All rights reserved. // import UIKit diff --git a/Example/NSFWDetector/Base.lproj/LaunchScreen.xib b/Example/NSFWDetector/Base.lproj/LaunchScreen.xib index 69a132c..2a5ac66 100644 --- a/Example/NSFWDetector/Base.lproj/LaunchScreen.xib +++ b/Example/NSFWDetector/Base.lproj/LaunchScreen.xib @@ -1,12 +1,10 @@ - + - - - + @@ -16,14 +14,14 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/NSFWDetector/CameraViewController.swift b/Example/NSFWDetector/CameraViewController.swift new file mode 100644 index 0000000..7c09e33 --- /dev/null +++ b/Example/NSFWDetector/CameraViewController.swift @@ -0,0 +1,100 @@ +// +// CameraViewController.swift +// NSFWDetector_Example +// +// Created by Michael Berg on 17.08.18. +// Copyright © 2018 LOVOO. All rights reserved. +// + +import UIKit +import AVKit +import NSFWDetector + +class CameraViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { + + private var subsequentPositiveDetections = 0 + + @IBOutlet weak var visualEffectsView: UIVisualEffectView! + @IBOutlet weak var nsfwLabel: UILabel! + + @IBOutlet weak var alarmView: UIView! + + override func viewDidLoad() { + super.viewDidLoad() + + self.visualEffectsView.layer.cornerRadius = 10.0 + self.visualEffectsView.clipsToBounds = true + + setupCaptureSession() + } + + func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { + + guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { + return + } + + NSFWDetector.shared.check(cvPixelbuffer: pixelBuffer, qos: .userInteractive, completion: { result in + + switch result { + case let .success(nsfwConfidence: confidence): + self.didDetectNSFW(confidence: confidence) + case .error: + break + } + }) + } + + private func didDetectNSFW(confidence: Float) { + if confidence > 0.8 { + self.subsequentPositiveDetections += 1 + + guard self.subsequentPositiveDetections > 3 else { + return + } + + self.alarmView.isHidden = false + } else { + self.subsequentPositiveDetections = 0 + + self.alarmView.isHidden = true + } + + self.nsfwLabel.text = String(format: "%.1f %% porn", confidence * 100.0) + } +} + +extension CameraViewController { + + func setupCaptureSession() { + let captureSession = AVCaptureSession() + captureSession.sessionPreset = AVCaptureSession.Preset.photo + + guard + let frontCamera = self.frontCamera, + let input = try? AVCaptureDeviceInput(device: frontCamera) + else { return } + + captureSession.addInput(input) + + let cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + cameraPreviewLayer.videoGravity = .resizeAspectFill + + self.view.layer.insertSublayer(cameraPreviewLayer, at: 0) + cameraPreviewLayer.frame = self.view.bounds + + let videoOutput = AVCaptureVideoDataOutput() + + videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "sample buffer delegate")) + videoOutput.recommendedVideoSettings(forVideoCodecType: .jpeg, assetWriterOutputFileType: .mp4) + + captureSession.addOutput(videoOutput) + captureSession.sessionPreset = .high + captureSession.startRunning() + } + + private var frontCamera: AVCaptureDevice? { + + return AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInMicrophone, .builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera], mediaType: .video, position: .front).devices.first + } +} diff --git a/Example/NSFWDetector/ImageViewController.swift b/Example/NSFWDetector/ImageViewController.swift new file mode 100644 index 0000000..98f383f --- /dev/null +++ b/Example/NSFWDetector/ImageViewController.swift @@ -0,0 +1,43 @@ +// +// ImageViewController.swift +// NSFWDetector_Example +// +// Created by Michael Berg on 21.08.18. +// Copyright © 2018 LOVOO. All rights reserved. +// + +import UIKit +import NSFWDetector +import QuartzCore + +class ImageViewController: UIViewController { + + @IBOutlet private weak var imageView: UIImageView! + @IBOutlet private weak var blurView: UIVisualEffectView! + @IBOutlet private weak var nsfwLabel: UILabel! + + var image: UIImage? + + override func viewDidLoad() { + super.viewDidLoad() + + self.blurView.layer.cornerRadius = 10.0 + self.blurView.clipsToBounds = true + + guard let image = self.image else { + self.nsfwLabel.text = "No Image selected." + return + } + + self.imageView.image = image + + NSFWDetector.shared.check(image: image) { result in + switch result { + case .error: + self.nsfwLabel.text = "Detection failed" + case let .success(nsfwConfidence: confidence): + self.nsfwLabel.text = String(format: "%.1f %% porn", confidence * 100.0) + } + } + } +} diff --git a/Example/NSFWDetector/Info.plist b/Example/NSFWDetector/Info.plist index eb18faa..9ab8ae8 100644 --- a/Example/NSFWDetector/Info.plist +++ b/Example/NSFWDetector/Info.plist @@ -2,6 +2,8 @@ + NSCameraUsageDescription + Demo App needs acces to the camera to present live tracking ability CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/Example/NSFWDetector/ViewController.swift b/Example/NSFWDetector/ViewController.swift index 12426d9..e6464b7 100644 --- a/Example/NSFWDetector/ViewController.swift +++ b/Example/NSFWDetector/ViewController.swift @@ -2,8 +2,8 @@ // ViewController.swift // NSFWDetector // -// Created by absoluteheike on 08/12/2018. -// Copyright (c) 2018 absoluteheike. All rights reserved. +// Created by Michael Berg on 08/12/2018. +// Copyright (c) 2018 LOVOO. All rights reserved. // import UIKit @@ -11,31 +11,49 @@ import NSFWDetector class ViewController: UIViewController { + @IBOutlet weak var photosButton: UIButton! + @IBOutlet weak var cameraButton: UIButton! + + private var selectedImage: UIImage? + override func viewDidLoad() { super.viewDidLoad() - if #available(iOS 12.0, *) { + self.photosButton.layer.cornerRadius = 12.0 + self.cameraButton.layer.cornerRadius = self.photosButton.layer.cornerRadius + } + + @IBAction func showImagePicker(_ sender: Any) { + let picker = UIImagePickerController() + + picker.delegate = self + picker.sourceType = .photoLibrary + + self.present(picker, animated: true, completion: nil) + } + + @IBAction func prepareForUnwind(segue: UIStoryboardSegue) { } +} - let image = UIImage() +extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { + guard let image = info[UIImagePickerControllerEditedImage] as? UIImage ?? info[UIImagePickerControllerOriginalImage] as? UIImage else { + return + } - guard #available(iOS 12.0, *), let detector = NSFWDetector.shared else { - return - } - detector.check(image: image, completion: { result in + self.selectedImage = image + picker.dismiss(animated: true) { + self.performSegue(withIdentifier: "ImageViewer", sender: self) + } + } - switch result { - case let .success(nsfwConfidence: confidence): - if confidence > 0.9 { - // 😱🙈😏 - } else { - // ¯\_(ツ)_/¯ - } - default: - break - } - }) + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard segue.identifier == "ImageViewer", let imageViewController = segue.destination.childViewControllers.first as? ImageViewController else { + return } + + imageViewController.image = self.selectedImage + self.selectedImage = nil } } - diff --git a/NSFWDetector/Classes/NSFWDetector.swift b/NSFWDetector/Classes/NSFWDetector.swift index 5048108..7079a57 100644 --- a/NSFWDetector/Classes/NSFWDetector.swift +++ b/NSFWDetector/Classes/NSFWDetector.swift @@ -16,9 +16,9 @@ public class NSFWDetector { private let model: VNCoreMLModel - public required init?() { + public required init() { guard let model = try? VNCoreMLModel(for: NSFW().model) else { - return nil + fatalError("NSFW should always be a valid model") } self.model = model } @@ -29,24 +29,39 @@ public class NSFWDetector { /// - success: Detection was successful. `nsfwConfidence`: 0.0 for safe content - 1.0 for hardcore porn ;) public enum DetectionResult { case error(Error) - case success(nsfwConfidence: VNConfidence) + case success(nsfwConfidence: Float) } public func check(image: UIImage, qos: DispatchQoS.QoSClass = .default, completion: @escaping (_ result: DetectionResult) -> Void) { // Create a requestHandler for the image - let _requestHandler: VNImageRequestHandler? + let requestHandler: VNImageRequestHandler? if let cgImage = image.cgImage { - _requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:]) + requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:]) } else if let ciImage = image.ciImage { - _requestHandler = VNImageRequestHandler(ciImage: ciImage, options: [:]) + requestHandler = VNImageRequestHandler(ciImage: ciImage, options: [:]) } else { - _requestHandler = nil + requestHandler = nil } - guard let requestHandler = _requestHandler else { - completion(.error(NSError(domain: "either cgImage nor ciImage must be set inside of UIImage", code: 0, userInfo: nil))) + self.check(requestHandler, qos: qos, completion: completion) + } + + public func check(cvPixelbuffer: CVPixelBuffer, qos: DispatchQoS.QoSClass = .default, completion: @escaping (_ result: DetectionResult) -> Void) { + + let requestHandler = VNImageRequestHandler(cvPixelBuffer: cvPixelbuffer, options: [:]) + + self.check(requestHandler, qos: qos, completion: completion) + } +} + +@available(iOS 12.0, *) +private extension NSFWDetector { + func check(_ requestHandler: VNImageRequestHandler?, qos: DispatchQoS.QoSClass, completion: @escaping (_ result: DetectionResult) -> Void) { + + guard let requestHandler = requestHandler else { + completion(.error(NSError(domain: "either cgImage nor ciImage must be set inside of UIImage", code: 0, userInfo: nil))) return } @@ -64,7 +79,7 @@ public class NSFWDetector { completion(.success(nsfwConfidence: observation.confidence)) } }) - + /// Start the actual detection DispatchQueue.global(qos: qos).async { From 1275f209d8e8377703bb630e090181242eb85732 Mon Sep 17 00:00:00 2001 From: Michael Berg Date: Sun, 16 Sep 2018 17:09:22 +0200 Subject: [PATCH 2/5] Remove GCD from NSFWDetector --- NSFWDetector/Classes/NSFWDetector.swift | 31 +++++++++---------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/NSFWDetector/Classes/NSFWDetector.swift b/NSFWDetector/Classes/NSFWDetector.swift index 7079a57..5697afa 100644 --- a/NSFWDetector/Classes/NSFWDetector.swift +++ b/NSFWDetector/Classes/NSFWDetector.swift @@ -32,7 +32,7 @@ public class NSFWDetector { case success(nsfwConfidence: Float) } - public func check(image: UIImage, qos: DispatchQoS.QoSClass = .default, completion: @escaping (_ result: DetectionResult) -> Void) { + public func check(image: UIImage, completion: @escaping (_ result: DetectionResult) -> Void) { // Create a requestHandler for the image let requestHandler: VNImageRequestHandler? @@ -44,21 +44,21 @@ public class NSFWDetector { requestHandler = nil } - self.check(requestHandler, qos: qos, completion: completion) + self.check(requestHandler, completion: completion) } - public func check(cvPixelbuffer: CVPixelBuffer, qos: DispatchQoS.QoSClass = .default, completion: @escaping (_ result: DetectionResult) -> Void) { + public func check(cvPixelbuffer: CVPixelBuffer, completion: @escaping (_ result: DetectionResult) -> Void) { let requestHandler = VNImageRequestHandler(cvPixelBuffer: cvPixelbuffer, options: [:]) - self.check(requestHandler, qos: qos, completion: completion) + self.check(requestHandler, completion: completion) } } @available(iOS 12.0, *) private extension NSFWDetector { - func check(_ requestHandler: VNImageRequestHandler?, qos: DispatchQoS.QoSClass, completion: @escaping (_ result: DetectionResult) -> Void) { + func check(_ requestHandler: VNImageRequestHandler?, completion: @escaping (_ result: DetectionResult) -> Void) { guard let requestHandler = requestHandler else { completion(.error(NSError(domain: "either cgImage nor ciImage must be set inside of UIImage", code: 0, userInfo: nil))) @@ -68,28 +68,19 @@ private extension NSFWDetector { /// The request that handles the detection completion let request = VNCoreMLRequest(model: self.model, completionHandler: { (request, error) in guard let observations = request.results as? [VNClassificationObservation], let observation = observations.first(where: { $0.identifier == "NSFW" }) else { - DispatchQueue.main.async { - completion(.error(NSError(domain: "Detection failed: No NSFW Observation found", code: 0, userInfo: nil))) - } + completion(.error(NSError(domain: "Detection failed: No NSFW Observation found", code: 0, userInfo: nil))) return } - DispatchQueue.main.async { - completion(.success(nsfwConfidence: observation.confidence)) - } + completion(.success(nsfwConfidence: observation.confidence)) }) /// Start the actual detection - DispatchQueue.global(qos: qos).async { - - do { - try requestHandler.perform([request]) - } catch { - DispatchQueue.main.async { - completion(.error(NSError(domain: "Detection failed: No NSFW Observation found", code: 0, userInfo: nil))) - } - } + do { + try requestHandler.perform([request]) + } catch { + completion(.error(NSError(domain: "Detection failed: No NSFW Observation found", code: 0, userInfo: nil))) } } } From 85ea19280b300a426946d42ae3e2e8348bf14fc8 Mon Sep 17 00:00:00 2001 From: Michael Berg Date: Sun, 16 Sep 2018 17:09:45 +0200 Subject: [PATCH 3/5] Add NSFW View to Camera Sample --- .../NSFWDetector/Base.lproj/Main.storyboard | 40 +++++++++---- .../NSFWDetector/CameraViewController.swift | 57 +++++++++++++++---- Example/NSFWDetector/Info.plist | 4 +- 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/Example/NSFWDetector/Base.lproj/Main.storyboard b/Example/NSFWDetector/Base.lproj/Main.storyboard index 8fa2687..648ca04 100644 --- a/Example/NSFWDetector/Base.lproj/Main.storyboard +++ b/Example/NSFWDetector/Base.lproj/Main.storyboard @@ -93,12 +93,31 @@ - + + + + + + + + + + + + + + +