iOS開発者のコミュニティはプロジェクトの作成方法について議論していますが、MVVMを使用するかVIPERを使用するかを決定しようとしている間、プロジェクトをソリッド化するか、そこにジェットタービンを追加しようとしている間、私はこれから脱却し、 Hypeチャートから別のテクノロジーがどのように機能するかを検討します-駆動開発 。
2017年、機械学習は誇大広告のスケジュールのトップでした。 そして、理由は明らかです。
- さらにオープンなデータセットが登場しました。
- 適切なハードウェアが登場しました。 クラウドソリューションを含む。
- この分野の技術は、生産プロジェクトに適用され始めました。
機械学習は幅広いトピックです。顔認識に焦点を当て、クリスマス前にどのテクノロジーがあったかを把握しようとします キリスト CoreML、およびAppleフレームワークのリリース後に登場したもの。
顔認識理論
顔認識は、パターン認識理論の実用化の一部です。 これは、識別と分類の2つのサブタスクで構成されています( ここでは、違いについて詳しく説明します )。 識別は、Facebook、iPhotoなどの最新のサービスで積極的に使用されています。 顔認識は、iPhone XのFaceIDから始まって、軍用機器のターゲット設定で終わるまで、あらゆる場所で使用されます。
後頭葉と側頭葉の境界にある脳の領域-紡錘状回のおかげで、人は他の人の顔を認識します。 私たちは4か月からさまざまな人々を認識します。 脳が識別のために滲出させる重要な特徴は、目、鼻、口、眉毛です。 また、人間の脳は顔全体を半分まで復元し、顔の一部だけで人を判別できます。 脳は、見ているすべての顔を平均し、この平均バージョンとの違いを見つけます。 したがって、白人種族の人々には、モンゴロイド種族に属するすべての人が一面にいるようです。 そして、モンゴロイドがヨーロッパ人を区別することは困難です。 内部認識は頭部の顔のスペクトル範囲に合わせて調整されているため、スペクトルの一部にデータがない場合、顔は同じものと見なされます。
顔認識の問題は40年以上にわたって解決されてきました。 彼らが含まれます:
- ビデオストリーム内の複数の顔の検索と認識。
- 顔、髪型、あごひげ、めがね、年齢、顔の回転の変化に対する抵抗。
- 人の識別のためのデータのスケーラビリティ。
- リアルタイムで作業します。
画像とその選択で顔を見つけるための最適なアルゴリズムの1つは、 方向勾配のヒストグラムです 。
他のアルゴリズムがあります。 Viola-Jonesアルゴリズムを使用して、顔のあるゾーンがどのように検索されるかについて詳しく説明します 。 精度が低く、顔をひねるとうまく機能しません。
テクノロジーおよびパターン認識ソリューションの簡単なツアー
パターン認識のアルゴリズムを含む多くのソリューションがあります。 iOSで使用される一般的なライブラリのリスト:
図1. DLIBライブラリー構造
- 長所:
-オープンソースソリューション。開発に参加し、現在の傾向を見ることができます。
-C ++で書かれています。 cocoapodsの形式でiOSをサポートしています:pod 'dlib'。
-C ++ライブラリとして統合することもできます。 Windows、Linux、MacOSで動作します。 Objective-C ++でラッパーを記述することにより、迅速なアプリケーションで作業できます。 - 短所:
-接続されたライブラリの大きなサイズ。 ポッドとして40メガバイト。
-高いエントリのしきい値。 多数の内部アルゴリズム。それぞれがObjective-Cでラッパーを作成します。
図2. OpenCVライブラリの構造
OpenCV (オープンソースコンピュータービジョンライブラリ)
- 長所:
-サポートに定期的に参加している最大のコミュニティ。
-C ++で書かれています。 cocoapodsの形式でiOSをサポートしています:pod 'OpenCV'。 - 短所:
-高いエントリのしきい値。
-接続されたライブラリの大きなサイズ。 ポッドとして77メガバイト、C ++ライブラリとして180メガバイト。
図3. CoreML構造
- 長所:
-アプリケーションへの簡単な統合。
-他のフレームワーク(Keras、Caffe、scikit-learn)のいくつかの異なるモデルをサポートする便利なコンバーターが含まれています。
-小さいサイズのボックスソリューション。
-GPUを搭載。 - 短所:
-CoreMLの一部であるため、他の既存のフレームワークの限られた数のモデルタイプをサポートします。
-最も一般的な機械学習ソリューションの1つであるTensorFlowのサポートはありません。 自作のコンバーターに多くの時間を費やす必要があります。
-高レベルの抽象化です。 すべての実装が閉じられているため、制御が不可能です。
-iOS 11以降。
パターン認識の問題の解決策を提供する有料プラットフォームがあります。 ほとんどが独自のアルゴリズムとテクノロジーを開発しています。 もちろん、これらの技術は積極的に開発され、軍によって使用されているため、一部のソリューションは分類されており、オープンソースがありません。
ランドマークとは
図4.顔の構造の視覚表示。
ランドマークを決定する目的は、顔のポイントを見つけることです。 アルゴリズムの最初のステップは、画像内の顔の位置を決定することです。 場所を受け取った後、人々は主要な輪郭を探します :
- 顔の輪郭。
- 左目。
- 右目。
- 左眉。
- 右眉。
- 左瞳孔。
- 右の瞳孔。
- 鼻。
- 唇。
これらの各輪郭は、平面上の点の配列です。
図5. dlib 68のランドマーク
写真では、顔の構造をはっきりと見ることができます。 また、選択したライブラリによって、ランドマークの数は異なります。 4つのランドマーク、16、64、124などのソリューションを開発しました。
マスクを構築するためのドロネー三角形分割
実用的な部分に移りましょう。 取得したランドマークに基づいて、顔に簡単なマスクを作成してみましょう。 予想される結果は、次の形式のマスクになります。
図6. Delaunay三角形分割アルゴリズムを視覚化するマスク。
Delaunay三角形分割は、平面上の点Sのセットの三角形分割です。三角形の場合、Sからのすべての点は、頂点である点を除き、三角形の外接円の外側にあります。 1934年にソビエトの数学者ボリスドローネによって最初に記述されました。
図7. Delaunayの三角形分割の例。 各ポイントから、ユークリッドメトリックで最も近い2つを通過する円が生成されます。
アルゴリズムの実用的な実装
カメラの顔にDelaunay三角形分割アルゴリズムを実装します。
手順1.内部には、2次元空間のポイントの配列を取り、三角形の配列を返すラッパーが表示されます。
public final class Triangle { public var vertex1: Vertex public var vertex2: Vertex public var vertex3: Vertex public init(vertex1: Vertex, vertex2: Vertex, vertex3: Vertex) { self.vertex1 = vertex1 self.vertex2 = vertex2 self.vertex3 = vertex3 } }
また、頂点はCGPointのラッパーであり、さらに特定のランドマークの番号が含まれています。
public final class Vertex { public let point: CGPoint // . 0 67. 68 dlib. 65 vision public let identifier: Int public init(point: CGPoint, id: Int) { self.point = point self.identifier = id } }
ステップ2.フェースにポリゴンを描画することに進みましょう。 カメラの電源を入れて、画面にカメラ画像を表示します。
final class ViewController: UIViewController { private var session: AVCaptureSession? private let faceDetection = VNDetectFaceRectanglesRequest() private let faceLandmarks = VNDetectFaceLandmarksRequest() private let faceLandmarksDetectionRequest = VNSequenceRequestHandler() private let faceDetectionRequest = VNSequenceRequestHandler() private lazy var previewLayer: AVCaptureVideoPreviewLayer? = { guard let session = self.session else { return nil } var previewLayer = AVCaptureVideoPreviewLayer(session: session) previewLayer.videoGravity = .resizeAspectFill return previewLayer }() private lazy var triangleView: TriangleView = { TriangleView(frame: view.bounds) }() private var frontCamera: AVCaptureDevice? = { AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: .front) }() override func viewDidLoad() { super.viewDidLoad() sessionPrepare() session?.startRunning() guard let previewLayer = previewLayer else { return } view.layer.addSublayer(previewLayer) view.insertSubview(triangleView, at: Int.max) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() previewLayer?.frame = view.frame } private func sessionPrepare() { session = AVCaptureSession() guard let session = session, let captureDevice = frontCamera else { return } do { let deviceInput = try AVCaptureDeviceInput(device: captureDevice) session.beginConfiguration() if session.canAddInput(deviceInput) { session.addInput(deviceInput) } let output = AVCaptureVideoDataOutput() output.videoSettings = [ String(kCVPixelBufferPixelFormatTypeKey): Int(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) ] output.alwaysDiscardsLateVideoFrames = true if session.canAddOutput(output) { session.addOutput(output) } session.commitConfiguration() let queue = DispatchQueue(label: "output.queue") output.setSampleBufferDelegate(self, queue: queue) print("setup delegate") } catch { print("can't setup session") } } }
ステップ3.次に、カメラからフレームを取得します
図8.カメラから受信したフレームの例
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } guard let attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate) as? [String: Any] else { return } let ciImage = CIImage(cvImageBuffer: pixelBuffer, options: attachments) // leftMirrored for front camera let ciImageWithOrientation = ciImage.oriented(forExifOrientation: Int32(UIImageOrientation.leftMirrored.rawValue)) detectFace(on: ciImageWithOrientation) } }
ステップ4.フレーム上の顔を探しています
fileprivate func detectFace(on image: CIImage) { try? faceDetectionRequest.perform([faceDetection], on: image) if let results = faceDetection.results as? [VNFaceObservation] { if !results.isEmpty { faceLandmarks.inputFaceObservations = results detectLandmarks(on: image) } } }
ステップ5.顔のランドマークを探す
図9.顔に見つかったランドマークの例。
private func detectLandmarks(on image: CIImage) { try? faceLandmarksDetectionRequest.perform([faceLandmarks], on: image) guard let landmarksResults = faceLandmarks.results as? [VNFaceObservation] else { return } for observation in landmarksResults { if let boundingBox = faceLandmarks.inputFaceObservations?.first?.boundingBox { let faceBoundingBox = boundingBox.scaled(to: UIScreen.main.bounds.size) var maparr = [Vertex]() for (index, element) in convertPointsForFace(observation.landmarks?.allPoints, faceBoundingBox).enumerated() { let point = CGPoint(x: (Double(UIScreen.main.bounds.size.width - element.point.x)), y: (Double(UIScreen.main.bounds.size.height - element.point.y))) maparr.append(Vertex(point: point, id: index)) } triangleView.recalculate(vertexes: maparr) } } } private func convertPointsForFace(_ landmark: VNFaceLandmarkRegion2D?, _ boundingBox: CGRect) -> [Vertex] { guard let points = landmark?.normalizedPoints else { return [] } let faceLandmarkPoints = points.map { (point: CGPoint) -> Vertex in let pointX = point.x * boundingBox.width + boundingBox.origin.x let pointY = point.y * boundingBox.height + boundingBox.origin.y return Vertex(point: CGPoint(x: Double(pointX), y: Double(pointY)), id: 0) } return faceLandmarkPoints }
ステップ6.次に、マスクを上に描画します。 Delaunayアルゴリズムから取得した三角形を取得し、レイヤーの形で描画します。
図10.最終結果は、顔の最もシンプルなマスクです。
SwiftでのDelaunay三角形分割アルゴリズムの完全な実装はこちらです。
また、洗練されたユーザー向けの最適化のヒントもいくつかあります。 毎回新しいレイヤーを描画するのは費用のかかる操作です。 Delaunayアルゴリズムを使用して三角形の座標を絶えず計算することもコストがかかります。 そのため、カメラを見る高解像度で高品質の顔を撮影し、この写真でDelaunay三角形分割アルゴリズムを実行します。 結果の三角形はテキストファイルに保存され、この三角形を使用して座標を変更します。
マスクとは
MSQRD、Snapchat、VK、さらにはAvito-すべてマスクを使用します。
図11. snapchatのマスクの例
マスクの最も単純なバージョンの実装は簡単です。 高くなったランドマークを取得します。 適用するマスクを選択し、その上にランドマークを配置します。 この場合、単純な2D投影があり、より複雑な3Dマスクがあります。 それらの場合、ポイントの変換が計算され、マスクの頂点がフレームに変換されます。 そのため、耳を担当するランドマークがマスクの耳を担当します。 次に、顔のランドマークの新しい位置を追跡し、マスクを変更します。
この領域では、マスクを作成するときに解決される困難なタスクがあります。 たとえば、レンダリングの複雑さ。 ランドマークのジャンプの瞬間は、タスクをさらに複雑にします。これは、この場合、マスクが歪められ、予測できない動作をするためです。 また、携帯電話のカメラからフレームをキャプチャすることは、光、影、鋭いけいれんなどの素早い変更を含む混oticとしたプロセスであるため、タスクは非常に時間がかかります。 別の課題は、複雑なマスクの作成です。
簡単な問題を楽しませたり解決したりする方法は興味深いです。 しかし、他の分野と同様に、難しいタスクを解決したい場合は、学習に時間を費やす必要があります。
次の記事で
パターン認識、人、ナンバープレート、性別、年齢の問題を解決することがますます一般的になっています。 この市場のIT企業は、このような問題をユーザーに徐々にかつ目に見えない形で解決するためのテクノロジーを導入しています。 中国は 、この分野で最初になるために、今後数年間で機械学習に1500億を投資します。
次の記事では、選択した人物によって特定の人物を識別し、識別前にファジー写真をフィルタリングする方法を説明します。