2017-06-06 1 views
17

Ich versuche, Apples Beispiel Core ML-Modelle, die auf der WWDC 2017 vorgeführt wurden ordnungsgemäß funktionieren. Ich benutze das GoogLeNet, um Bilder zu klassifizieren (siehe Apple Machine Learning Page). Das Modell nimmt einen CVPixelBuffer als Eingabe. Ich habe ein Bild namens imageSample.jpg, das ich für diese Demo verwende. Mein Code ist unten:Convert Image in CVPixelBuffer für Machine Learning Swift

 var sample = UIImage(named: "imageSample")?.cgImage 
     let bufferThree = getCVPixelBuffer(sample!) 

     let model = GoogLeNetPlaces() 
     guard let output = try? model.prediction(input: GoogLeNetPlacesInput.init(sceneImage: bufferThree!)) else { 
      fatalError("Unexpected runtime error.") 
     } 

     print(output.sceneLabel) 

Ich bekomme immer den unerwarteten Laufzeitfehler in der Ausgabe und nicht eine Bildklassifizierung. Mein Code, um das Bild zu konvertieren unter:

func getCVPixelBuffer(_ image: CGImage) -> CVPixelBuffer? { 
     let imageWidth = Int(image.width) 
     let imageHeight = Int(image.height) 

     let attributes : [NSObject:AnyObject] = [ 
      kCVPixelBufferCGImageCompatibilityKey : true as AnyObject, 
      kCVPixelBufferCGBitmapContextCompatibilityKey : true as AnyObject 
     ] 

     var pxbuffer: CVPixelBuffer? = nil 
     CVPixelBufferCreate(kCFAllocatorDefault, 
          imageWidth, 
          imageHeight, 
          kCVPixelFormatType_32ARGB, 
          attributes as CFDictionary?, 
          &pxbuffer) 

     if let _pxbuffer = pxbuffer { 
      let flags = CVPixelBufferLockFlags(rawValue: 0) 
      CVPixelBufferLockBaseAddress(_pxbuffer, flags) 
      let pxdata = CVPixelBufferGetBaseAddress(_pxbuffer) 

      let rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 
      let context = CGContext(data: pxdata, 
            width: imageWidth, 
            height: imageHeight, 
            bitsPerComponent: 8, 
            bytesPerRow: CVPixelBufferGetBytesPerRow(_pxbuffer), 
            space: rgbColorSpace, 
            bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) 

      if let _context = context { 
       _context.draw(image, in: CGRect.init(x: 0, y: 0, width: imageWidth, height: imageHeight)) 
      } 
      else { 
       CVPixelBufferUnlockBaseAddress(_pxbuffer, flags); 
       return nil 
      } 

      CVPixelBufferUnlockBaseAddress(_pxbuffer, flags); 
      return _pxbuffer; 
     } 

     return nil 
    } 

ich diesen Code aus einer früheren Stackoverflow Post bekam (letzte Antwort here). Ich erkenne, dass der Code möglicherweise nicht korrekt ist, aber ich habe keine Ahnung, wie ich das selbst machen könnte. Ich glaube, dass dies der Abschnitt ist, der den Fehler enthält. Das Modell ruft den folgenden Eingabetyp auf: Image<RGB,224,224>

+0

Ich habe ein Beispielprojekt mit komplettem Code erstellt, den Sie hier finden: https://hackernoon.com/swift-tutorial-native-mach ine-learning-and-machine-vision-in-ios-11-11e1e88aa397 –

Antwort

29

Sie müssen nicht mehrere Bildfehler machen, um ein Core ML-Modell mit einem Bild zu verwenden - das neue Vision framework kann das für Sie erledigen.

import Vision 
import CoreML 

let model = try VNCoreMLModel(for: MyCoreMLGeneratedModelClass().model) 
let request = VNCoreMLRequest(model: model, completionHandler: myResultsMethod) 
let handler = VNImageRequestHandler(url: myImageURL) 
handler.perform([request]) 

func myResultsMethod(request: VNRequest, error: Error?) { 
    guard let results = request.results as? [VNClassificationObservation] 
     else { fatalError("huh") } 
    for classification in results { 
     print(classification.identifier, // the scene label 
       classification.confidence) 
    } 

} 

Die WWDC17 session on Vision sollte ein bisschen mehr Informationen haben - es ist morgen Nachmittag.

+0

Funktioniert wie ein Charme (mit einigen Modifikationen), danke. Mir war nicht klar, dass Vision eine bestimmte Art von Anfrage nach Modellen hatte, die Informationen von einer Bildeingabe ausgaben. Ich denke, ich hätte besser auf die Dokumentation achten sollen ... –

+0

Für die ursprüngliche Frage ist 'VNImageRequestHandler (cgImage: CGImage)' besser geeignet. – chengsam

+0

@chengsam Nicht wirklich - die ursprüngliche Frage beginnt von einer Ressource auf der Festplatte. Das Einlesen als "UIImage", das Umwandeln in "CGImage" und das Übergeben an Vision verliert Metadaten schon lange, aber die Übergabe der Ressourcen-URL behält diese Metadaten für Vision bereit. – rickster

9

Sie können eine reine CoreML verwenden, aber Sie sollten ein Bild (224.224)

DispatchQueue.global(qos: .userInitiated).async { 
     // Resnet50 expects an image 224 x 224, so we should resize and crop the source image 
     let inputImageSize: CGFloat = 224.0 
     let minLen = min(image.size.width, image.size.height) 
     let resizedImage = image.resize(to: CGSize(width: inputImageSize * image.size.width/minLen, height: inputImageSize * image.size.height/minLen)) 
     let cropedToSquareImage = resizedImage.cropToSquare() 

     guard let pixelBuffer = cropedToSquareImage?.pixelBuffer() else { 
      fatalError() 
     } 
     guard let classifierOutput = try? self.classifier.prediction(image: pixelBuffer) else { 
      fatalError() 
     } 

     DispatchQueue.main.async { 
      self.title = classifierOutput.classLabel 
     } 
    } 

// ... 

extension UIImage { 

    func resize(to newSize: CGSize) -> UIImage { 
     UIGraphicsBeginImageContextWithOptions(CGSize(width: newSize.width, height: newSize.height), true, 1.0) 
     self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)) 
     let resizedImage = UIGraphicsGetImageFromCurrentImageContext()! 
     UIGraphicsEndImageContext() 

     return resizedImage 
    } 

    func cropToSquare() -> UIImage? { 
     guard let cgImage = self.cgImage else { 
      return nil 
     } 
     var imageHeight = self.size.height 
     var imageWidth = self.size.width 

     if imageHeight > imageWidth { 
      imageHeight = imageWidth 
     } 
     else { 
      imageWidth = imageHeight 
     } 

     let size = CGSize(width: imageWidth, height: imageHeight) 

     let x = ((CGFloat(cgImage.width) - size.width)/2).rounded() 
     let y = ((CGFloat(cgImage.height) - size.height)/2).rounded() 

     let cropRect = CGRect(x: x, y: y, width: size.height, height: size.width) 
     if let croppedCgImage = cgImage.cropping(to: cropRect) { 
      return UIImage(cgImage: croppedCgImage, scale: 0, orientation: self.imageOrientation) 
     } 

     return nil 
    } 

    func pixelBuffer() -> CVPixelBuffer? { 
     let width = self.size.width 
     let height = self.size.height 
     let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, 
        kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary 
     var pixelBuffer: CVPixelBuffer? 
     let status = CVPixelBufferCreate(kCFAllocatorDefault, 
             Int(width), 
             Int(height), 
             kCVPixelFormatType_32ARGB, 
             attrs, 
             &pixelBuffer) 

     guard let resultPixelBuffer = pixelBuffer, status == kCVReturnSuccess else { 
      return nil 
     } 

     CVPixelBufferLockBaseAddress(resultPixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) 
     let pixelData = CVPixelBufferGetBaseAddress(resultPixelBuffer) 

     let rgbColorSpace = CGColorSpaceCreateDeviceRGB() 
     guard let context = CGContext(data: pixelData, 
             width: Int(width), 
             height: Int(height), 
             bitsPerComponent: 8, 
             bytesPerRow: CVPixelBufferGetBytesPerRow(resultPixelBuffer), 
             space: rgbColorSpace, 
             bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) else { 
             return nil 
     } 

     context.translateBy(x: 0, y: height) 
     context.scaleBy(x: 1.0, y: -1.0) 

     UIGraphicsPushContext(context) 
     self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) 
     UIGraphicsPopContext() 
     CVPixelBufferUnlockBaseAddress(resultPixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) 

     return resultPixelBuffer 
    } 
} 

Die erwartete Bildgröße für die Eingänge Größe ändern Sie in der mimodel Datei finden: enter image description here

Eine Demo Projekt, das sowohl reine CoreML- als auch Vision-Varianten verwendet, finden Sie hier: https://github.com/handsomecode/iOS11-Demos/tree/coreml_vision/CoreML/CoreMLDemo

+0

Ich dachte, ich hätte in der Vision-Sitzung (oder vielleicht einer der anderen ML-Sitzungen) gehört, dass du das Bild nicht skalieren musst ... Vielleicht irre ich mich aber. – pinkeerach

+3

@pinketerach: Sie müssen die Größe des Bildes nicht ändern, wenn Sie die Vision API ('VNCoreMLRequest', wie in meiner Antwort) verwenden, da Vision den Bildverarbeitungsteil für Sie übernimmt. Wenn Sie Core ML direkt (ohne Vision) verwenden, müssen Sie die Größe des Bildes ändern und neu formatieren (je nachdem, welches Modell Sie verwenden möchten) und es selbst in einen "CVPixelBuffer" konvertieren. – rickster

+0

@mauryat Ihr Beispielprojekt tut nichts. Es gibt wirklich keinen Code. – zumzum