OpenCVでリアルタイム輪郭検出
前回作ったアプリに、以前トライしたOpenCVを使った輪郭検出の機能を追加してみました。
やったこと
今回はImageController.mmでUIImageを生成する部分を変更して、cv::Matを生成 -> 輪郭検出 -> UIImageに変換しています。
※ViewController.swiftは前回と同じなので省略します。
ImageController.mm
#import "ocvCameraImage-Bridging-Header.h" #import < opencv2 / opencv.hpp > #import "opencv2 / imgcodecs / ios.h" @interface ImageController() @property (nonatomic)CVImageBufferRef ibrImageBuffer; @property (nonatomic)uint8_t *baseAddress; @property (nonatomic)size_t sztWidth; @property (nonatomic)size_t sztHeight; @property (nonatomic)CGContextRef cnrContext; @property (nonatomic, strong)UIImage *imgCreatedImage; @end @implementation ImageController - (UIImage *) createImageFromBuffer:(CMSampleBufferRef) sbrBuffer { _ibrImageBuffer = CMSampleBufferGetImageBuffer(sbrBuffer); // ピクセルバッファのベースアドレスをロックする. CVPixelBufferLockBaseAddress(_ibrImageBuffer, 0); // ベースアドレスの取得. _baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(_ibrImageBuffer, 0); // サイズの取得. _sztWidth = CVPixelBufferGetWidth(_ibrImageBuffer); _sztHeight = CVPixelBufferGetHeight(_ibrImageBuffer); ------------ここから変更--------------- // Mat画像の生成 cv::Mat matEdge((int)_sztHeight, (int)_sztWidth, CV_8UC4, (void*)_baseAddress); // 90°回転. cv::transpose(matEdge, matEdge); // 左右反転. cv::flip(matEdge, matEdge, 1); // グレイスケール化. cv::cvtColor(matEdge, matEdge, cv::COLOR_BGR2GRAY); std::vector< std::vector < cv::Point > > vctContours; std::vector< cv::Vec4i > vctHierarchy; // Canny アルゴリズムを使ったエッジ検出. Canny(matEdge, matEdge, 100, 100, 3); // 輪郭を取得する cv::findContours(matEdge, vctContours, vctHierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); // 輪郭を表示する matEdge = cv::Mat::zeros(matEdge.size(), CV_8UC3); cv::drawContours(matEdge, vctContours, -1, cv::Scalar(255, 255, 255), 1); // UIImageへの変換. _imgCreatedImage = MatToUIImage(matEdge); // 解放. vctContours.clear(); vctHierarchy.clear(); matEdge.release(); --------------------------------------- // ベースアドレスのロックを解除. CVPixelBufferUnlockBaseAddress(_ibrImageBuffer, 0); return _imgCreatedImage; }
線を引いた間の部分が変更点です。
cv::Matへの変換部分などは、なんだか前回よりすっきりした気がします。
AVFoundationで取得できる画像が90°反転している問題は、「cv::transpose」と「cv::flip」で対処しています(画面の回転に対応できるよう、変更が必要ですが)。
なお、前回のコードで生成していたUIImageを「UIImageToMat」でcv::Matに変換 -> エッジ検出 -> 「MatToUIImage」でUIImageに再変換しようとすると、実行中にエラーで止まってしまいました。
UIImageの生成またはUIImage <--> cv::Matの変換が重い、メモリの解放ができてないなどの問題があるのかもしれません。
また、輪郭を描画する「cv::drawContours」で、以前と同じくランダムな色を使う以下のコードを使うと、カメラが時々固まっていました。
int intContourCount = (int)vctContours.size() - 1; for( int i = intContourCount; 0 <= i; i--) { // 輪郭に付ける色の指定.0~255の範囲で値を生成. sclColor = cv::Scalar(rngColor.uniform(0, 255), rngColor.uniform(0,255), rngColor.uniform(0,255) ); // 輪郭の番号を指定して、色を付ける cv::drawContours(matDrawnContour, vctContours, i, sclColor, 1); }
毎フレーム実行するにはキツいのかもしれません。
これについては今後の課題ということで。