Core ML框架詳細(xì)解析(三) —— 利用Vision和Core ML對圖像進(jìn)行分類

版本記錄

版本號 時間
V1.0 2017.10.29

前言

目前世界上科技界的所有大佬一致認(rèn)為人工智能是下一代科技革命,蘋果作為科技界的巨頭,當(dāng)然也會緊跟新的科技革命的步伐,其中ios API 就新出了一個框架Core ML。ML是Machine Learning的縮寫,也就是機(jī)器學(xué)習(xí),這正是現(xiàn)在很火的一個技術(shù),它也是人工智能最核心的內(nèi)容。感興趣的可以看我寫的下面幾篇。
1. Core ML框架詳細(xì)解析(一) —— Core ML基本概覽
2. Core ML框架詳細(xì)解析(二) —— 獲取模型并集成到APP中

一個簡單示例

使用VisionCore ML可以對圖像進(jìn)行分類,下面看一個簡單例子。

下面我們就看一下代碼。

1. ImageClassificationViewController.swift
import UIKit
import CoreML
import Vision
import ImageIO

class ImageClassificationViewController: UIViewController {
    // MARK: - IBOutlets
    
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var cameraButton: UIBarButtonItem!
    @IBOutlet weak var classificationLabel: UILabel!
    
    // MARK: - Image Classification
    
    /// - Tag: MLModelSetup
    lazy var classificationRequest: VNCoreMLRequest = {
        do {
            /*
             Use the Swift class `MobileNet` Core ML generates from the model.
             To use a different Core ML classifier model, add it to the project
             and replace `MobileNet` with that model's generated Swift class.
             */
            let model = try VNCoreMLModel(for: MobileNet().model)
            
            let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in
                self?.processClassifications(for: request, error: error)
            })
            request.imageCropAndScaleOption = .centerCrop
            return request
        } catch {
            fatalError("Failed to load Vision ML model: \(error)")
        }
    }()
    
    /// - Tag: PerformRequests
    func updateClassifications(for image: UIImage) {
        classificationLabel.text = "Classifying..."
        
        let orientation = CGImagePropertyOrientation(image.imageOrientation)
        guard let ciImage = CIImage(image: image) else { fatalError("Unable to create \(CIImage.self) from \(image).") }
        
        DispatchQueue.global(qos: .userInitiated).async {
            let handler = VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
            do {
                try handler.perform([self.classificationRequest])
            } catch {
                /*
                 This handler catches general image processing errors. The `classificationRequest`'s
                 completion handler `processClassifications(_:error:)` catches errors specific
                 to processing that request.
                 */
                print("Failed to perform classification.\n\(error.localizedDescription)")
            }
        }
    }
    
    /// Updates the UI with the results of the classification.
    /// - Tag: ProcessClassifications
    func processClassifications(for request: VNRequest, error: Error?) {
        DispatchQueue.main.async {
            guard let results = request.results else {
                self.classificationLabel.text = "Unable to classify image.\n\(error!.localizedDescription)"
                return
            }
            // The `results` will always be `VNClassificationObservation`s, as specified by the Core ML model in this project.
            let classifications = results as! [VNClassificationObservation]
        
            if classifications.isEmpty {
                self.classificationLabel.text = "Nothing recognized."
            } else {
                // Display top classifications ranked by confidence in the UI.
                let topClassifications = classifications.prefix(2)
                let descriptions = topClassifications.map { classification in
                    // Formats the classification for display; e.g. "(0.37) cliff, drop, drop-off".
                   return String(format: "  (%.2f) %@", classification.confidence, classification.identifier)
                }
                self.classificationLabel.text = "Classification:\n" + descriptions.joined(separator: "\n")
            }
        }
    }
    
    // MARK: - Photo Actions
    
    @IBAction func takePicture() {
        // Show options for the source picker only if the camera is available.
        guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
            presentPhotoPicker(sourceType: .photoLibrary)
            return
        }
        
        let photoSourcePicker = UIAlertController()
        let takePhoto = UIAlertAction(title: "Take Photo", style: .default) { [unowned self] _ in
            self.presentPhotoPicker(sourceType: .camera)
        }
        let choosePhoto = UIAlertAction(title: "Choose Photo", style: .default) { [unowned self] _ in
            self.presentPhotoPicker(sourceType: .photoLibrary)
        }
        
        photoSourcePicker.addAction(takePhoto)
        photoSourcePicker.addAction(choosePhoto)
        photoSourcePicker.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        
        present(photoSourcePicker, animated: true)
    }
    
    func presentPhotoPicker(sourceType: UIImagePickerControllerSourceType) {
        let picker = UIImagePickerController()
        picker.delegate = self
        picker.sourceType = sourceType
        present(picker, animated: true)
    }
}

extension ImageClassificationViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    // MARK: - Handling Image Picker Selection

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
        picker.dismiss(animated: true)
        
        // We always expect `imagePickerController(:didFinishPickingMediaWithInfo:)` to supply the original image.
        let image = info[UIImagePickerControllerOriginalImage] as! UIImage
        imageView.image = image
        updateClassifications(for: image)
    }
}
2. CGImagePropertyOrientation+UIImageOrientation.swift
import UIKit
import ImageIO

extension CGImagePropertyOrientation {
    /**
     Converts a `UIImageOrientation` to a corresponding
     `CGImagePropertyOrientation`. The cases for each
     orientation are represented by different raw values.
     
     - Tag: ConvertOrientation
     */
    init(_ orientation: UIImageOrientation) {
        switch orientation {
        case .up: self = .up
        case .upMirrored: self = .upMirrored
        case .down: self = .down
        case .downMirrored: self = .downMirrored
        case .left: self = .left
        case .leftMirrored: self = .leftMirrored
        case .right: self = .right
        case .rightMirrored: self = .rightMirrored
        }
    }
}

這里使用的模型就是MobileNet.mlmodel,下面看一下詳細(xì)信息以及在github上的地址。

MobileNets are based on a streamlined architecture that have depth-wise separable convolutions to build light weight deep neural networks. Trained on ImageNet with categories such as trees, animals, food, vehicles, person etc. MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications https://github.com/shicai/MobileNet-Caffe

這里我直接拍了兩張照片進(jìn)行識別,結(jié)果是可以正確的識別出鍵盤,但是沒有識別出卷紙。具體如下圖所示。

開始界面
識別1
識別2

詳細(xì)說明

1. 總體概括

使用Core ML框架,您可以使用訓(xùn)練好的機(jī)器學(xué)習(xí)模型對輸入數(shù)據(jù)進(jìn)行分類。 Vision框架與Core ML合作,將分類模型應(yīng)用于圖像,并對這些圖像進(jìn)行預(yù)處理,使機(jī)器學(xué)習(xí)任務(wù)更輕松,更可靠。

此示例應(yīng)用程序使用開源MobileNet模型(幾種available classification models之一),來對1000個分類類別來識別圖像,如下面的示例截圖所示。

2. Preview the Sample App - 預(yù)覽示例應(yīng)用程序

要查看此示例應(yīng)用程序的操作,構(gòu)建并運(yùn)行項(xiàng)目,然后使用示例應(yīng)用程序工具欄中的按鈕拍攝照片或從照片庫中選擇圖像。 然后,示例應(yīng)用程序使用Vision將Core ML模型應(yīng)用于所選擇的圖像,并顯示所得到的分類標(biāo)簽以及指示每個分類的置信水平的數(shù)字。 它按照模型分配給每個的置信分?jǐn)?shù)的順序顯示前兩個分類。

3. Set Up Vision with a Core ML Model - 用Core ML模型設(shè)置視覺

Core ML自動生成Swift類 - 在此示例中,MobileNet類可輕松訪問ML模型。 要使用模型設(shè)置Vision請求,請創(chuàng)建該類的實(shí)例,并使用其model屬性創(chuàng)建VNCoreMLRequest
對象。 運(yùn)行請求后,使用請求對象的完成處理程序來指定從模型接收結(jié)果的方法。

// Listing 1

let model = try VNCoreMLModel(for: MobileNet().model)
            
let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in
    self?.processClassifications(for: request, error: error)
})
request.imageCropAndScaleOption = .centerCrop
return request

ML模型以固定的寬高比處理輸入圖像,但輸入圖像可以具有任意的縱橫比,因此Vision必須縮放或裁剪圖像以適合。 為獲得最佳效果,請設(shè)置請求的imageCropAndScaleOption
屬性以匹配模型訓(xùn)練的圖像布局。 對于available classification models模型,VNImageCropAndScaleOptionCenterCrop
選項(xiàng)是適當(dāng)?shù)?,除非另有說明。

4. Run the Vision Request - 運(yùn)行視覺請求

使用要處理的圖像創(chuàng)建VNImageRequestHandler
對象,并將請求傳遞給該對象的performRequests:error:
方法。 此方法同步運(yùn)行 - 使用后臺隊(duì)列,以便在請求執(zhí)行時主隊(duì)列不被阻塞。

// Listing 2

DispatchQueue.global(qos: .userInitiated).async {
    let handler = VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
    do {
        try handler.perform([self.classificationRequest])
    } catch {
        /*
         This handler catches general image processing errors. The `classificationRequest`'s
         completion handler `processClassifications(_:error:)` catches errors specific
         to processing that request.
         */
        print("Failed to perform classification.\n\(error.localizedDescription)")
    }
}

大多數(shù)模型都對已經(jīng)正確定向顯示的圖像進(jìn)行了訓(xùn)練。 為了確保以任意方向正確處理輸入圖像,請將圖像的方向傳遞給圖像請求處理程序。 (此示例應(yīng)用程序向CGImagePropertyOrientation
類型添加了一個初始化程序init(_:),用于從 UIImageOrientation
方向值轉(zhuǎn)換。)

5. Handle Image Classification Results - 處理圖像分類結(jié)果

Vision請求的完成處理程序指示請求是成功還是導(dǎo)致錯誤。 如果成功,其results
屬性包含描述ML模型識別的可能分類的VNClassificationObservation
對象。

//Listing 3

func processClassifications(for request: VNRequest, error: Error?) {
    DispatchQueue.main.async {
        guard let results = request.results else {
            self.classificationLabel.text = "Unable to classify image.\n\(error!.localizedDescription)"
            return
        }
        // The `results` will always be `VNClassificationObservation`s, as specified by the Core ML model in this project.
        let classifications = results as! [VNClassificationObservation]

后記

未完,待續(xù)~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容