search
LoginSignup
11
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Appleのフレームワークにはないコンピュータビジョンのさまざまな機能がつかえます。

1、iOS版OpenCVパッケージの組み込み

1、OpenCVの公式サイトからiOSパックをダウンロード。

スクリーンショット 2020-10-19 7.22.46.png
最新バージョンでOKです。

2、解凍した opencv2.frameworkを、Xcodeプロジェクトにフォルダごとドラッグ&ドロップ。

スクリーンショット 2020-10-19 9.25.09.png

3、必要なファイルを作る。

スクリーンショット 2020-10-19 8.03.29.png
計3ファイル作ります。OpenCV-iOSはObjective-C++で記述するためこれらのファイルが必要です。

・File1、OpenCVManager.mm

Objective-C++でOpenCVの機能を書くためのファイルです。
NewファイルからObjective-Cファイルを選択し、OpenCVManagerという名前で作成します。
スクリーンショット 2020-10-19 7.42.52.png スクリーンショット 2020-10-19 7.43.27.png
Bridging Headerファイルを作るかXcodeがきいてくれるので、Createを選択します。
スクリーンショット 2020-10-19 7.44.11.png
OpenCVManager.mの拡張子をmからmmに変えます。
c++を書くときはmmにするそうです。

・File2、{プロジェクト名}-Bridging-Header.h

Objective-CとSwiftをブリッジするためのファイル
Swiftから呼ぶObjective-C関数を記述します。
あとで、OpenCVManager.mmの関数名と引数と返り値を記述します。

・File3、OpenCVManager.h

ヘッダーファイル。中身は書き換えません。
スクリーンショット 2020-10-19 7.59.47.png

4、とりあえず使ってみる方法(グレイ変換)

pexels-nadi-lindsay-5503185.jpg スクリーンショット 2020-10-19 9.56.51.png
上記で作成したファイルに以下を記述します。
(#の部分も記述します)

・Objective-CでのOpenCVフレームワークのインポートとメソッドの記述

OpenCVManager.mm

#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#import <opencv2/highgui.hpp>
#import <opencv2/imgcodecs/ios.h>
#import "OpenCVManager.h"
#endif
#import <Foundation/Foundation.h>

@implementation OpenCVManager : NSObject
 // ** ここにUIImageを配列にする処理と、OpenCVメソッドをObjective-C関数として書きます。 ** //
 // ** Hello Worldとして、グレイ変換を書いています。 ** //

+ (UIImage*)gray:(UIImage*)image {
   cv::Mat img_Mat;  // 配列(Matrix)を用意
   UIImageToMat(image, img_Mat); // UIImageを配列(Matrix)へ変換
   cv::cvtColor(img_Mat, img_Mat, cv::COLOR_BGR2GRAY); // OpenCVメソッドで処理
   return MatToUIImage(img_Mat); // 配列(Matrix)をUIImageに戻す
}

@end

・Swiftとのブリッジ。メソッドを記述

{プロジェクト名}-Bridging-Header.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "OpenCVManager.h"

@interface OpenCVManager : NSObject

//+ or - (返り値 *)関数名:(引数の型 *)引数名;
//+ : クラスメソッド
//- : インスタンスメソッド

+ (UIImage *)gray:(UIImage *)image;

@end

・Swiftからの呼び出し


let grayImage = OpenCVManager.gray(uiImage)

(*上記はOpenCVManegerのクラスメソッドを直接呼んでいます。OpenCVManegerクラスをインスタンス化して使用する場合は、mmファイルとブリッジングヘッダーのメソッドのアタマの"+"を"-"に変更し、Swift側でインスタンス化します。)

// インスタンス化して使う場合
let cv = OpenCVManager() 
let grayImage = cv.gray(uiImage)

2、OpenCVフィルター

CIFilterでおなじフィルターがある場合もありますが、エッジの取り方など度合いがちがいますので、好みにあわせて使えます。
上記のグレイ変換と同じ場所(OpenCVManager.mmと{プロジェクト名}-Bridging-Header.h)に同様にメソッドを記述して使います。

・BGR

pexels-nadi-lindsay-5503185.jpg スクリーンショット 2020-10-19 10.48.33.png

OpenCVManager.mm
+ (UIImage *) bgr:(UIImage *)image{
    cv::Mat img_Mat;
    UIImageToMat(image, img_Mat);
    cv::cvtColor(img_Mat, img_Mat, cv::COLOR_RGB2BGR); //BGR変換
    return MatToUIImage(img_Mat);
}
{プロジェクト名}-Bridging-Header.h

+ (UIImage *) bgr:(UIImage *)image;
let bgrImage = OpenCVManager.bgr(uiImage)

・キャニー・エッジ

pexels-nadi-lindsay-5503185.jpg スクリーンショット 2020-10-19 11.06.45.png

OpenCVManager.mm

+ (UIImage *) canny:(UIImage *)image{
    cv::Mat img_Mat;
    UIImageToMat(image, img_Mat);
    cv::Mat processed; // 宛先をソースと同じにするとエラーが出たので、宛先のMatをつくっています
    cv::Canny(img_Mat, processed, 10, 100); // 引数は(ソース、宛先、閾値の下限、閾値の上限)
    return MatToUIImage(processed);
}
{プロジェクト名}-Bridging-Header.h
+ (UIImage *) canny:(UIImage *)image;
let cannyImage = OpenCVManager.Canny(uiImage)

・findTransformECC(2画像の位置合わせ計算)&warpAffine(画像アフィン変換)

OpenCVManager.mm
+ (UIImage *) transform:(UIImage *)image second:(UIImage *)simage {
    cv::Mat img_Mat;  // 1枚目の画像用の行列を用意
    cv::Mat simg_Mat;  // 2枚目の画像用の行列を用意
    cv::Mat im1_gray, im2_gray;  // グレイスケール用の行列を用意

    UIImageToMat(image, img_Mat);  // UIImageから行列に変換
    UIImageToMat(simage, simg_Mat);  // UIImageから行列に変換 
    cv::cvtColor(img_Mat, im1_gray, cv::COLOR_BGR2GRAY);  // グレイスケールに変換
    cv::cvtColor(simg_Mat, im2_gray, cv::COLOR_BGR2GRAY);  // グレイスケールに変換
    std::cout << "Width : " << img_Mat.cols << std::endl;

    const int warp_mode = cv::MOTION_EUCLIDEAN;  // アフィン変換の番号(イント)を設定
    cv::Mat warp_matrix;
    if ( warp_mode == cv::MOTION_HOMOGRAPHY )  // ホモグラフィ変換も可能
        warp_matrix = cv::Mat::eye(3, 3, CV_32F);  // ホモグラフィ変換は3*3行列
    else
        warp_matrix = cv::Mat::eye(2, 3, CV_32F);  // アフィン変換は2*3行列
    else
    int number_of_iterations = 50; // 多くすればするほど正確な変換ができ、計算コストが高い。大きな変換から計算するため、だいたいの回転・スケール、移動ならこれぐらいの計算回数でいいかと。
    double termination_eps = 1e-10; // どの程度まで差分値小さくなったら終了するか(たぶん)

    cv::TermCriteria criteria (cv::TermCriteria::COUNT+cv::TermCriteria::EPS, number_of_iterations, termination_eps); // すいません。これ理解せず別ページのデフォルト使ってます。たぶん、計算回数の閾値を変換用にしている?
    cv::Mat processed;
    findTransformECC(
                     im2_gray,
                     im1_gray,
                     warp_matrix,
                     warp_mode,
                     criteria
                 );
    std::cout << warp_matrix << std::endl;  // 変換行列をコンソールにプリントして確認

    cv::warpAffine(simg_Mat, processed, warp_matrix, cv::Size(256,256)); // 出力画像のサイズ

    return MatToUIImage(processed);
}
{プロジェクト名}-Bridging-Header.h
+ (UIImage *) transform:(UIImage *)image second:(UIImage *)simage;
let transformedImage = OpenCVManager.transform(firstImage, second: secondImage)

・2値化(白と黒に閾値で分ける)

IMG_2812のコピー.png名称未設定2.png

OpenCVManager.mm
+ (UIImage *) thresh:(UIImage *)image{
    cv::Mat img_Mat;
    cv::Mat gray_img;
    cv::Mat processed;
    UIImageToMat(image, img_Mat);
    cv::cvtColor(img_Mat, gray_img, cv::COLOR_BGR2GRAY);
    cv::threshold(gray_img, processed, 100, 255, cv::THRESH_BINARY); // 100以上の値が白になる。他は黒。THRESH_BINARY_INVに指定すると、白黒反転
    return MatToUIImage(processed);
}
{プロジェクト名}-Bridging-Header.h
+ (UIImage *) thresh:(UIImage *)image;
let binarizedImage = OpenCVManager.thresh(uiImage)

・ぼかし

IMG_2812のコピー.png名称未設定.png

OpenCVManager.mm
+ (UIImage *) blur:(UIImage *)image{
    cv::Mat img_Mat;
    UIImageToMat(image, img_Mat);
    cv::Mat processed;
    cv::blur(img_Mat, processed, cv::Size(10,10)); // サイズを大きくすると、よりぼける
    return MatToUIImage(processed);
}
{プロジェクト名}-Bridging-Header.h
+ (UIImage *) blur:(UIImage *)image;
let blurredImage = OpenCVManager.blur(uiImage)

🐣


フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。

Twitter
Medium

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
11
Help us understand the problem. What are the problem?