swiftにopencvを入れて画像処理を行う

# やりたいこと
xcodeでswiftプロジェクトを作成し、swift内にopenCVを入れる事で画像処理を楽にする。
iPhoneカメラで撮影した画像を2値化したり、エッジ検出する
※いつものように個人macでの実装なので業務関係ない
## open cvのダウンロード
以前ダウンロードまでは実施したので、この記事では実装をしていく。
インストールしてない人は、以下記事参考


https://qiita.com/web05891254/items/d4f331a360f759e5a459


## Object-cからopen cvを呼び出す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645607/838f1110-89c4-682c-f922-f75595e3bda0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645607/748adb14-b397-6a94-8059-522525a0672f.png)

Nextでファイル作成を実行するとポップアップが表示する。
Object-Cを実行するために必要なbridging header fileをXcodeが自動生成してくれる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645607/0bb7212e-44bb-7edf-a43c-ac53311f50ed.png)

Object-C++ファイルにする為に、OpenCVSample.mファイルを.mmに変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645607/771e5c15-b1a9-1515-f99b-caa09fa3d463.png)

```obj-c: ImageCheckAi-Bridging-Header.h
#import "OpenCVSample.h"

```

```obj-c: OpenCVSample.h
#import <UIKit/UIKit.h>

@interface OpenCVSample : NSObject
+(UIImage *)GrayScale:(UIImage *)image;
@end
```

最初は撮影した画像をグレースケールに変換する
```obj-c++:OpenCVSample.mm
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>

#import "OpenCVSample.h"

@implementation OpenCVSample

+(UIImage *)GrayScale:(UIImage *)image{
    // convert image to mat
    cv::Mat mat;
    UIImageToMat(image, mat);

    // convert mat to gray scale
    //ここでグレースケール化しています。
    cv::Mat gray;
    cv::cvtColor(mat, gray, cv::COLOR_BGR2GRAY);

    // convert to image
    UIImage * grayImg = MatToUIImage(gray);

    return grayImg;
}

@end
```



## swiftからObjective-Cを実行
ViewControllerに撮影、撮影後の表示をUIImagePickerControllerで実装

storybordなどでカメラボタンを配置してください。今回は割愛してオブジェクトが配置された前提にします。
```swift:ViewController.swift
//フレームワークをインポートする
import UIKit


//ビュー用のクラス
class ViewController: UIViewController, UIImagePickerControllerDelegate,UINavigationControllerDelegate{

    @IBOutlet weak var imageView: UIImageView!
    let imagePicker = UIImagePickerController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //画像の初期設定みたいなもの
        //tyoeで.cameraを.photoLibraryに変更にすれば写真フォルダから参照する
        imagePicker.delegate = self
        imagePicker.sourceType = .camera
        imagePicker.allowsEditing = true
    }
    
    //カメラでuse select選択すると関数起動
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        //画像情報定義
        if let userPickedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
            imageView.image = userPickedImage
            
            guard let ciimage = CIImage(image:userPickedImage) else {
                fatalError("エラーじゃ取得できぬ")
            }
            //画像を引数にdetect関数起動
            detect(image: ciimage)
        }
        //ピッカー解除
        imagePicker.dismiss(animated: true, completion: nil)
    }
    
    //画像処理関数
    func detect(image:CIImage) {
        
        let thresholdImage = imageView.image
        let gray = OpenCVSample.grayScale(thresholdImage)
        
        //最終表示画像
        imageView.image = gray
    }
//ピッカー解除
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        imagePicker.dismiss(animated: true,completion: nil)
    }
    @IBAction func cameraTapped(_ sender: UIBarButtonItem) {
        present(imagePicker, animated: true, completion: nil)
    }
    
}

```

## 実機へビルドして動作確認
iPhoneで撮影した画像がグレースケール化されている事が分かる
画像が回転してしまっているので、別途記述する必要はあり。
![F20EB62F-F237-427B-A8A4-D18B12FCAC0A.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645607/51d120ec-1178-2708-96fc-f4ed34932860.png)





### 画像を2値化してみる



先程記述したobj-c++ファイルを修正する。
以下二値化部分切り取り
```obj-c++
//引数(入力画像、出力画像、輝度閾値、輝度最大値、タイプ）
 cv::threshold(gray, binary, 110, 255,  cv::THRESH_BINARY);
```

```obj-c++:OpenCVSample.mm
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>

#import "OpenCVSample.h"

@implementation OpenCVSample

+(UIImage *)GrayScale:(UIImage *)image{
    // convert image to mat
    cv::Mat mat;
    UIImageToMat(image, mat);

    // convert mat to gray scale
    cv::Mat gray;
    cv::cvtColor(mat, gray, cv::COLOR_BGR2GRAY);

    //2値化処理
    cv::Mat binary;
    cv::threshold(gray, binary, 110, 255,  cv::THRESH_BINARY);
    
    // 引数の画像をbinaryに変更する
    UIImage * grayImg = MatToUIImage(binary);

    return grayImg;
}

@end
```

2値化できた
![6E66DDFE-EF62-4604-AB3F-D4DDBF3C49A5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645607/3bc6e88a-f306-ca50-f1d6-414901930aca.png)


# まとめ
今回は単純2値化のみしか実装してないが、適応的閾値処理を活用して影を2値化時に検出しない様にする必要はあります。
画像の回転を元に戻すなどありますが、参考になれば幸いです

以下参考にさせていただいた記事


https://qiita.com/fushikky/items/e53fab1a48d43ff94006