我們的目的: 在圖片中找出口罩的位置,當(dāng)然你也可以找出其他你想要檢測出的東西,這里只拿口罩舉例
老規(guī)矩, 看下效果
這只是13張圖片的訓(xùn)練的效果,我懶得找圖了,用的就是訓(xùn)練數(shù)據(jù)測試的,效果還行,最后會解釋為什么有的圖片檢測不到
(一)準(zhǔn)備
- 訓(xùn)練的樣本 (帶有口罩的圖片)
- https://cloud.annotations.ai/ 這個是IBM在線標(biāo)注的服務(wù),自己去注冊免費的,還要注冊一個云存儲啥的,自己搞定就完事了
- Xcode with Create ML 這才是核心
(一)標(biāo)注
-
(1)首先我們打開 https://cloud.annotations.ai/(注冊等忽略),進(jìn)入你的工作面板
-
(2) 輸入項目名字 選擇標(biāo)注類型
我們選右邊的小企鵝,這個是用來做地標(biāo)檢測的(就是 landmarks),不知道咋翻,左邊那個小企鵝是做圖片分類的,沒有位置信息啥的
-
(3)開始標(biāo)注
我們將要標(biāo)注的圖片直接拖進(jìn)網(wǎng)頁就行了
用鼠標(biāo)標(biāo)注好后.我們可以在右側(cè)將這個標(biāo)注命名,比如 mask
-
(4)導(dǎo)出標(biāo)注文件
這里我們導(dǎo)出 Create ML 所需要的文件格式,基本上就是下面這個樣子,帶一個json文件
(二) 開始訓(xùn)練
-
(1) 打開Xcode -> Open Developer Tools -> Create ML
我們選擇 Object Detector 目標(biāo)檢測器(多個小企鵝),同樣左邊的適用一個小企鵝的標(biāo)注
-
(2) 開始訓(xùn)練
我們將剛才導(dǎo)出的Create ML的標(biāo)注圖片的文件夾直接拖進(jìn)來, 然后點擊左上角的Train 就可以
-------------------- A Few Moments Later -------------------
等到迭代的數(shù)量達(dá)到max (1000)后, loss(損失函數(shù)) 也比較低的時候 ,就證明我們的模型已經(jīng)訓(xùn)練好了, Create ML 會自己結(jié)束的,我們只有等就行了.
這個時間很長 電腦放在那里就可以了
(三) 測試模型
點擊右邊的Output , 然后將我們的測試數(shù)據(jù)集拖進(jìn)來,切換一下,就可以看到我們模型的測試結(jié)果了.
PS : 模型的效果 取決于 網(wǎng)絡(luò)的結(jié)構(gòu)(這個我們改不了), 訓(xùn)練樣本的數(shù)量和質(zhì)量,標(biāo)注的準(zhǔn)確性
如何提高訓(xùn)練的效率
- 建議訓(xùn)練樣本在訓(xùn)練前,我們用python 或者其他什么語言隨便你, 將我們的訓(xùn)練樣本處理一遍,比如處理好樣本的尺寸、質(zhì)量等,過濾掉質(zhì)量差的圖片
- 還有一個思路就是, 初代模型 來處理訓(xùn)練樣本, 雖然說初代模型可能效果不好,但是也可以做到目標(biāo)的大致定位,在得到大致的位置之后,我們再做圖片的尺寸和質(zhì)量處理,這樣我們的訓(xùn)練樣本會更好
---------------- 開始貼代碼 ---------------
(四) 代碼事例
- (1) 新建一個工程 在storyboard 里拖一個 UIImageView 和一個 UIButton 就可以了
-
(2) 把訓(xùn)練好的model 拖到工程中
找到你的Create ML 工程 然后顯示包內(nèi)容 就能找到你的model 了
lazy var detectorRequest: VNCoreMLRequest = {
do {
let model = try VNCoreMLModel(for: faceMask().model)
let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in
self?.detectionResult(request: request, error: error)
})
request.imageCropAndScaleOption = .scaleFit
return request
} catch {
fatalError("Failed to load Vision ML model: \(error)")
}
}()
@IBAction func choosePic(_ sender: Any) {
choosePic()
}
func detectionResult(request:VNRequest,error:Error?) -> Void {
DispatchQueue.main.async {
guard let resutls = request.results else {
print("啥也沒有")
return
}
let detections = resutls as! [VNRecognizedObjectObservation]
self.drawMaskRect(detections: detections)
}
}
func drawMaskRect(detections:[VNRecognizedObjectObservation]) -> Void {
guard let image = self.content?.image else {
return
}
let imageSize = image.size
let scale: CGFloat = 0
UIGraphicsBeginImageContextWithOptions(imageSize, false, scale)
image.draw(at: CGPoint.zero)
for detection in detections {
processed image, with the origin at the image's lower-left corner.
let boundingBox = detection.boundingBox
let rectangle = CGRect(x: boundingBox.minX*image.size.width, y: (1-boundingBox.minY-boundingBox.height)*image.size.height, width: boundingBox.width*image.size.width, height: boundingBox.height*image.size.height)
UIColor(red: 0, green: 1, blue: 0, alpha: 0.4).setFill()
UIRectFillUsingBlendMode(rectangle, CGBlendMode.normal)
}
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.content?.image = newImage
}
func detectImage(image:UIImage) -> Void {
let orientation = CGImagePropertyOrientation(rawValue: UInt32(image.imageOrientation.rawValue))
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.detectorRequest])
} catch {
print("Failed to perform detection.\n\(error.localizedDescription)")
}
}
}
func choosePic() {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let camera = UIAlertAction(title: "相機(jī)", style: .default) { (action) in
if (UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera)){
let pickerVC = UIImagePickerController()
pickerVC.delegate = self
pickerVC.allowsEditing = true
pickerVC.sourceType = .camera
self .present(pickerVC, animated: true, completion: nil)
}
}
let album = UIAlertAction(title: "相冊", style: .default) { (action) in
if (UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.photoLibrary)){
let pickerVC = UIImagePickerController()
pickerVC.delegate = self
pickerVC.allowsEditing = true
pickerVC.sourceType = .photoLibrary
self .present(pickerVC, animated: true, completion: nil)
}
}
let cancel = UIAlertAction(title: "取消", style: .cancel, handler: nil)
alert.addAction(camera)
alert.addAction(album)
alert.addAction(cancel)
self.present(alert, animated: true, completion: nil)
}
}
extension ViewController : UIImagePickerControllerDelegate ,UINavigationControllerDelegate{
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[.originalImage] as? UIImage else {
return
}
self.content.image = image
self.detectImage(image: image)
}
}
遇到的問題
- (1)檢測結(jié)果不準(zhǔn), 這個問題處理半天,不知道為什么結(jié)果 跟在Create ML里測試的結(jié)果不一樣, 而且非常離譜,在真機(jī)上也一樣
解決辦法: 升級Xcode,我不知道是Xcode 的原因 還是iOS 系統(tǒng)的原因,升級Xcode 到11.3.1后 模擬器應(yīng)該是13.0 這回結(jié)果就對了, 沒升級手機(jī)系統(tǒng)太慢了,只在模擬器上測試了
看下模擬器的效果
效果還可以吧, 別拍照檢測了 , 數(shù)據(jù)量少大部分檢測不出來,我這是運氣好,建議直接用訓(xùn)練的樣本來測試, 我試了用相機(jī)去拍樣本,還是檢測不到,要拍的尺寸差不多才能檢測的到,這也就是上面為什么說訓(xùn)練樣本要預(yù)處理,你的輸入數(shù)據(jù)最好都是一樣的
怎么將圖片放到模擬器的?
打開模擬器的相冊 ,直接把圖片拖進(jìn)去就可以了
擴(kuò)展
以上只是一種機(jī)器學(xué)習(xí)的應(yīng)用, 我們可以看到Create ML 的模版還有很多分類,不僅可以檢測地標(biāo), 還可以進(jìn)行圖片分類、語音分類、動作分類缎罢、文字分類等等, 有了這些模版,我們就可以做很多東西的識別