iOSでOpenCV

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

#１、iOS版OpenCVパッケージの組み込み
###１、[OpenCVの公式サイト](https://opencv.org/releases/)からiOSパックをダウンロード。
![スクリーンショット 2020-10-19 7.22.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7f1a447c-baf3-ca3b-cf41-db3866a37d27.png)
最新バージョンでOKです。

###２、解凍した opencv２.frameworkを、Xcodeプロジェクトにフォルダごとドラッグ&ドロップ。
![スクリーンショット 2020-10-19 9.25.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/eabd6098-a377-d17d-f2d4-9c11d825b652.png)

###３、必要なファイルを作る。
![スクリーンショット 2020-10-19 8.03.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/19c4b5ab-5b54-a870-66a7-ed9fd2af3b98.png)
計３ファイル作ります。OpenCV-iOSはObjective-C++で記述するためこれらのファイルが必要です。

####*・File１、OpenCVManager.mm*
Objective-C++でOpenCVの機能を書くためのファイルです。
NewファイルからObjective-Cファイルを選択し、OpenCVManagerという名前で作成します。
![スクリーンショット 2020-10-19 7.42.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7fb784db-c80c-d298-7039-2f4bbc39fd9b.png) ![スクリーンショット 2020-10-19 7.43.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/46ae08e4-dfa4-63a6-d9e9-d6027c2ef98e.png)
Bridging Headerファイルを作るかXcodeがきいてくれるので、Createを選択します。
![スクリーンショット 2020-10-19 7.44.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7c94be94-77da-dd13-dd8d-04a9af12e61e.png)
**OpenCVManager.mの拡張子をmからmmに変えます。**
c++を書くときはmmにするそうです。

####*・File２、{プロジェクト名}-Bridging-Header.h*
Objective-CとSwiftをブリッジするためのファイル
Swiftから呼ぶObjective-C関数を記述します。
あとで、OpenCVManager.mmの関数名と引数と返り値を記述します。

####*・File３、OpenCVManager.h*
ヘッダーファイル。中身は書き換えません。
![スクリーンショット 2020-10-19 7.59.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/98d95a9c-f3fc-6a85-fd8f-e2b2f657da97.png)

####４、とりあえず使ってみる方法（グレイ変換）
![pexels-nadi-lindsay-5503185.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/a7faa671-0091-5482-987d-b29fa5c2f959.jpeg)　![スクリーンショット 2020-10-19 9.56.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/da5f2d16-17be-48e3-768d-d2de326f084d.png)
上記で作成したファイルに以下を記述します。
（＃の部分も記述します）

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

```objective_c: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とのブリッジ。メソッドを記述**

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

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

@interface OpenCVManager : NSObject

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

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

@end

```

**・Swiftからの呼び出し**

```swift

let grayImage = OpenCVManager.gray(uiImage)
```

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

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

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

####・BGR
![pexels-nadi-lindsay-5503185.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/a7faa671-0091-5482-987d-b29fa5c2f959.jpeg)　![スクリーンショット 2020-10-19 10.48.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/c2042792-1413-66ea-1b3e-38bf1acc2f7b.png)


```objective_c: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);
}
```

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

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

####・キャニー・エッジ
![pexels-nadi-lindsay-5503185.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/a7faa671-0091-5482-987d-b29fa5c2f959.jpeg)　![スクリーンショット 2020-10-19 11.06.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/27a1ab7a-7e57-9ef1-f0a5-ae438cf9c3b7.png)

```objective_c: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);
}
```

```objective_c:{プロジェクト名}-Bridging-Header.h
+ (UIImage *) canny:(UIImage *)image;
```

```swift
let cannyImage = OpenCVManager.Canny(uiImage)
```

####・findTransformECC（２画像の位置合わせ計算）＆warpAffine（画像アフィン変換）

```objective_c:OpenCVManager.mm
+ (UIImage *) transform:(UIImage *)image second:(UIImage *)simage {
    cv::Mat img_Mat;  // １枚目の画像用の行列を用意
    cv::Mat simg_Mat;  // ２枚目の画像用の行列を用意
    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);  // ホモグラフィ変換は３*３行列
    else
        warp_matrix = cv::Mat::eye(2, 3, CV_32F);  // アフィン変換は2*３行列
    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);
}
```

```objective_c:{プロジェクト名}-Bridging-Header.h
+ (UIImage *) transform:(UIImage *)image second:(UIImage *)simage;
```

```swift
let transformedImage = OpenCVManager.transform(firstImage, second: secondImage)
```
####・２値化（白と黒に閾値で分ける）
![IMG_2812のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/88bba44c-0b9f-8a2d-6e02-0ea7204fc858.png)![名称未設定2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/45da9e70-d4b2-791a-5c54-2c10edae626c.png)



```objective_c: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);
}
```


```objective_c:{プロジェクト名}-Bridging-Header.h
+ (UIImage *) thresh:(UIImage *)image;
```

```swift
let binarizedImage = OpenCVManager.thresh(uiImage)
```

####・ぼかし
![IMG_2812のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/88bba44c-0b9f-8a2d-6e02-0ea7204fc858.png)![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/d9bc7980-0344-5fc9-1c05-113a9c65201a.png)




```objective_c: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);
}
```


```objective_c:{プロジェクト名}-Bridging-Header.h
+ (UIImage *) blur:(UIImage *)image;
```

```swift
let blurredImage = OpenCVManager.blur(uiImage)
```

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

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

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)