質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Objective-C

Objective-Cはオブジェクト指向型のプログラミング言語のひとつです。C言語をベースにSmalltalkが取り入れられています。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

0回答

1090閲覧

OpenCVで検出した色の面積を求めたい

m-mega

総合スコア56

Objective-C

Objective-Cはオブジェクト指向型のプログラミング言語のひとつです。C言語をベースにSmalltalkが取り入れられています。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2022/10/20 10:24

編集2022/10/25 11:47

前提

SwiftUIとOpenCVを使って、スマホのカメラ映像(プレビュー画像)から特定色の輪郭を検出するプログラムを作成しました。

輪郭を検出してプレビューとして表示するところまではできたのですが、検出した輪郭の「面積」を求めるところで詰まってしまっています。

下図はアプリの実行画面で、持ち手が青色のハサミを映している様子です。
アプリの実行画面(持ち手が青色のハサミを映している様子)
アプリの「run」ボタンを押すとレンダリングを開始し、「stop」ボタンを押すとレンダリングを停止します。

実現したいこと

  • OpenCVで検出した輪郭の面積を求めたい

発生している問題・エラーメッセージ

輪郭の面積を求めるために使う「Imgproc.contourArea()」関数にMat型のデータを渡す必要があるのですが、取得した輪郭のデータからMat型のデータを作成する方法が分かりません。

試したこと

輪郭の情報を取得するために「Imgproc.findContours()」関数を使用しています。
findContoursのドキュメント
各輪郭は「contours」に格納されます。printで中身(一部抜粋)を確認すると下図のようになっています。
イメージ説明

輪郭の面積を計算するために「Imgproc.contourArea()」関数を使用しています。
Imgproc.contourAreaのドキュメント
輪郭の面積を求めるcontourAreaにはMat型(○×○の行列型)のデータを渡す必要があるので、「findContours」で得られた輪郭のデータをMat型に変換する必要があると考えています。

OpenCVをSwiftで使用するためのドキュメントを参考に、Matに変換する関数をいくつか試してみたのですが、うまくいっていない状態です。

該当のソースコード

【ContentView.swift】

Swift

1import SwiftUI 2import AVFoundation 3import opencv2 4 5struct ContentView: View { 6 let videoCapture = VideoCapture() 7 @State var image: UIImage? = nil 8 var body: some View { 9 VStack { 10 if let image = image { 11 Image(uiImage: image) 12 .resizable() 13 .scaledToFit() 14 } 15 else { 16 Spacer() 17 } 18 HStack { 19 Button("run") { 20 videoCapture.run { sampleBuffer in 21 if let convertImage = UIImageFromSampleBuffer(sampleBuffer) { 22 let src = Mat(uiImage: convertImage) // UIImageをMatに変換 23 Imgproc.cvtColor(src: src, dst: src, code: ColorConversionCodes.COLOR_RGB2BGR) // BGRに変換 24 Imgproc.cvtColor(src: src, dst: src, code: ColorConversionCodes.COLOR_BGR2HSV) // HSVに変換 25 Core.inRange(src: src, lowerb: Scalar(50, 50, 50), upperb: Scalar(255, 255, 255), dst: src) // HSVの検出範囲を指定 26 Imgproc.blur(src: src, dst: src, ksize: Size2i(width: 5, height: 5)) // フィルタでMatをぼかす 27 Imgproc.Canny(image: src, edges: src, threshold1: 360.0, threshold2: 360.0) // Canny法で輪郭を検出する 28 // 輪郭の情報を取得する 29 let contours: NSMutableArray = [] 30 let hierarchy = Mat.zeros(Size(width: 5, height: 5), type: CvType.CV_8UC1) 31 Imgproc.findContours(image: src, contours: contours, hierarchy: hierarchy, mode: RetrievalModes.RETR_EXTERNAL, method: ContourApproximationModes.CHAIN_APPROX_SIMPLE) 32 // 輪郭の面積を求める 33 for contour in contours { 34 print(contour) 35 Imgproc.contourArea(contour: contour) // Matに変換する必要がある 36 } 37 let img = src.toUIImage() // MatをUIImageに変換 38 DispatchQueue.main.async { 39 self.image = img 40 } 41 } 42 } 43 } 44 Button("stop") { 45 videoCapture.stop() 46 } 47 } 48 .font(.largeTitle) 49 } 50 } 51 52 func UIImageFromSampleBuffer(_ sampleBuffer: CMSampleBuffer) -> UIImage? { 53 guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } 54 let ciImage = CIImage(cvPixelBuffer: pixelBuffer) 55 let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer)) 56 guard let image = CIContext().createCGImage(ciImage, from: imageRect) else { return nil } 57 58 return UIImage(cgImage: image) 59 } 60 61} 62 63struct ContentView_Previews: PreviewProvider { 64 static var previews: some View { 65 ContentView() 66 } 67} 68
【VideoCapture.swift】

Swift

1import Foundation 2import AVFoundation 3 4class VideoCapture: NSObject { 5 let captureSession = AVCaptureSession() 6 var handler: ((CMSampleBuffer) -> Void)? 7 8 override init() { 9 super.init() 10 setup() 11 } 12 13 func setup() { 14 captureSession.beginConfiguration() 15 let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) 16 guard 17 let deviceInput = try? AVCaptureDeviceInput(device: device!), 18 captureSession.canAddInput(deviceInput) 19 else { return } 20 captureSession.addInput(deviceInput) 21 22 let videoDataOutput = AVCaptureVideoDataOutput() 23 videoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "mydispatchqueue")) 24 videoDataOutput.alwaysDiscardsLateVideoFrames = true 25 26 guard captureSession.canAddOutput(videoDataOutput) else { return } 27 captureSession.addOutput(videoDataOutput) 28 29 // アウトプットの画像を縦向きに変更(標準は横) 30 for connection in videoDataOutput.connections { 31 if connection.isVideoOrientationSupported { 32 connection.videoOrientation = .portrait 33 } 34 } 35 36 captureSession.commitConfiguration() 37 } 38 39 func run(_ handler: @escaping (CMSampleBuffer) -> Void) { 40 if !captureSession.isRunning { 41 self.handler = handler 42 captureSession.startRunning() 43 } 44 } 45 46 func stop() { 47 if captureSession.isRunning { 48 captureSession.stopRunning() 49 } 50 } 51} 52 53extension VideoCapture: AVCaptureVideoDataOutputSampleBufferDelegate { 54 func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 55 if let handler = handler { 56 handler(sampleBuffer) 57 } 58 } 59} 60
【swiftui_avfoundationApp.swift】

Swift

1import SwiftUI 2 3@main 4struct swiftui_avfoundationApp: App { 5 var body: some Scene { 6 WindowGroup { 7 ContentView() 8 } 9 } 10}

補足情報①(FW/ツールのバージョンなど)

  • iOS:15.7
  • Xcode:13.1
  • OpenCV:opencv-4.6.0-ios-framework

OpenCV4.6.0ダウンロードサイト

補足情報②(Android版について)

以前に、Androidでも同様の機能を持ったアプリを作成しました。
その際は、下記のプログラムで輪郭の面積を出すことができていました。

kotlin

1// 認識した色に応じた処理を実行 2... 3 private fun detectColor(img:Mat):Boolean { 4 Core.inRange(img, Scalar(0.0, 0.0, 0.0) 5 , Scalar(255.0, 255.0, 255.0) 6 , img) 7 // 色の輪郭の面積に応じた処理を実行 8 return compareContourColor(detectContour(img)) 9 } 10 11 private fun detectContour(img:Mat): Int { 12 // ガウシアンフィルタで画像をぼかす 13 Imgproc.GaussianBlur(img, img, Size(5.0, 5.0), 5.0) 14 // 輪郭を取得 15 Imgproc.Canny(img, img, 10.0, 360.0) 16 // 輪郭の情報を取得 17 val contours: List<MatOfPoint> = ArrayList() 18 val hierarchy = Mat.zeros(Size(5.0, 5.0), CvType.CV_8UC1) 19 Imgproc.findContours(img, 20 contours, // 輪郭情報を格納 21 hierarchy, // 階層構造を格納 22 Imgproc.RETR_EXTERNAL, // 輪郭抽出モードを指定 23 Imgproc.CHAIN_APPROX_SIMPLE // 輪郭の表示方法を指定 24 ) 25 return contours.size 26 } 27 28 private fun compareContourColor(size:Int):Boolean { 29 ... // 取得した輪郭面積に応じた処理を実行 30 } 31 32 // カメラ映像の各フレームに対する処理 33 override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame): Mat? { 34 // カメラプレビューの各フレームをフルカラーで取得 35 mMat = inputFrame.rgba() 36 Imgproc.cvtColor(mMat, mMatChange, Imgproc.COLOR_RGBA2BGR) // MatをRGBAからBGRに変換 37 Imgproc.cvtColor(mMatChange, mMatChange, Imgproc.COLOR_BGR2HSV) // MatをBGRからHSVに変換 38 detectColor(mMatChange!!) // 輪郭面積に応じた処理を実行 39 return mMat // プレビュー画像を返す 40 } 41...

参考サイト

OpenCVのSwift用ドキュメント
SwiftUIでAVFoundationを使ってフレームバッファを取得する
OpenCVのサンプルをSwiftUIだけで作成する

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

1T2R3M4

2022/10/21 06:28

https://ja.stackoverflow.com/questions/91720 以下ご対応ください。 https://teratail.com/help#posted-otherservice teratailでは、マルチポスト※の推奨はしていません。 やむを得ず複数のサイトに質問を投稿された場合は、質問内容にマルチポストをする理由を書き、他のサイトの投稿へのリンクを貼ってください。 また、解決した際には必ずteratail及びすべての投稿に解決した旨と、どのように解決したかを記載してください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問