我们的目的: 在图片中找出口罩的位置,当然你也可以找出其他你想要检测出的东西,这里只拿口罩举例
老规矩, 看下效果
这只是13张图片的训练的效果,我懒得找图了,用的就是训练数据测试的,效果还行,最后会解释为什么有的图片检测不到
(一)准备
- 训练的样本 (带有口罩的图片)
- https://cloud.annotations.ai/ 这个是IBM在线标注的服务,自己去注册免费的,还要注册一个云存储啥的,自己搞定就完事了
- Xcode with Create ML 这才是核心
(一)标注
-
(1)首先我们打开 https://cloud.annotations.ai/(注册等忽略),进入你的工作面板
-
(2) 输入项目名字 选择标注类型
我们选右边的小企鹅,这个是用来做地标检测的(就是 landmarks),不知道咋翻,左边那个小企鹅是做图片分类的,没有位置信息啥的
-
(3)开始标注
我们将要标注的图片直接拖进网页就行了
用鼠标标注好后.我们可以在右侧将这个标注命名,比如 mask
-
(4)导出标注文件
这里我们导出 Create ML 所需要的文件格式,基本上就是下面这个样子,带一个json文件
(二) 开始训练
-
(1) 打开Xcode -> Open Developer Tools -> Create ML
我们选择 Object Detector 目标检测器(多个小企鹅),同样左边的适用一个小企鹅的标注
-
(2) 开始训练
我们将刚才导出的Create ML的标注图片的文件夹直接拖进来, 然后点击左上角的Train 就可以
-------------------- A Few Moments Later -------------------
等到迭代的数量达到max (1000)后, loss(损失函数) 也比较低的时候 ,就证明我们的模型已经训练好了, Create ML 会自己结束的,我们只有等就行了.
这个时间很长 电脑放在那里就可以了
(三) 测试模型
点击右边的Output , 然后将我们的测试数据集拖进来,切换一下,就可以看到我们模型的测试结果了.
PS : 模型的效果 取决于 网络的结构(这个我们改不了), 训练样本的数量和质量,标注的准确性
如何提高训练的效率
- 建议训练样本在训练前,我们用python 或者其他什么语言随便你, 将我们的训练样本处理一遍,比如处理好样本的尺寸、质量等,过滤掉质量差的图片
- 还有一个思路就是, 初代模型 来处理训练样本, 虽然说初代模型可能效果不好,但是也可以做到目标的大致定位,在得到大致的位置之后,我们再做图片的尺寸和质量处理,这样我们的训练样本会更好
---------------- 开始贴代码 ---------------
(四) 代码事例
- (1) 新建一个工程 在storyboard 里拖一个 UIImageView 和一个 UIButton 就可以了
-
(2) 把训练好的model 拖到工程中
找到你的Create ML 工程 然后显示包内容 就能找到你的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: "相机", 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)检测结果不准, 这个问题处理半天,不知道为什么结果 跟在Create ML里测试的结果不一样, 而且非常离谱,在真机上也一样
解决办法: 升级Xcode,我不知道是Xcode 的原因 还是iOS 系统的原因,升级Xcode 到11.3.1后 模拟器应该是13.0 这回结果就对了, 没升级手机系统太慢了,只在模拟器上测试了
看下模拟器的效果
效果还可以吧, 别拍照检测了 , 数据量少大部分检测不出来,我这是运气好,建议直接用训练的样本来测试, 我试了用相机去拍样本,还是检测不到,要拍的尺寸差不多才能检测的到,这也就是上面为什么说训练样本要预处理,你的输入数据最好都是一样的
怎么将图片放到模拟器的?
打开模拟器的相册 ,直接把图片拖进去就可以了
扩展
以上只是一种机器学习的应用, 我们可以看到Create ML 的模版还有很多分类,不仅可以检测地标, 还可以进行图片分类、语音分类、动作分类、文字分类等等, 有了这些模版,我们就可以做很多东西的识别