vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

Swift + OpenCV

前回作成したサンプルを、そのままSwiftに置き換えてみたメモ。

準備

とりあえずXcode6 Beta6をインストールしたところ、Xcode自体は起動するものの、なぜかSimulatorが起動せず、エラーに。

ネットで調べつつ色々試したものの、結局Macを再起動したら問題なく動作するようになりました。

プロジェクトの作成はObjective-Cと同じで、言語をSwiftにするだけです。

opencv2.frameworkも同じように追加します。

SwiftObjective-Cの振り分け

Storyboardを使ったUI部分や、UIImageを使用する部分はSwiftで記述することにします。

しかし、SwiftにはCやC++との互換性がないため、C++を使用するOpenCVを扱う部分はObjective-Cで記述します。

Swift

Delegate、ViewControllerのファイルが.hと.mの2つではなく、.swift一つになったことを除けばほぼObjective-Cと同じように作成できます。

Delegateは特に触っていないので、ViewControllerについてのみまとめます。

ViewController.swift

import UIKit

class ViewController: UIViewController {
                            
    @IBOutlet weak var imgImageView: UIImageView!
    @IBOutlet weak var btnColor: UIButton!
    @IBOutlet weak var btnGray: UIButton!
    @IBOutlet weak var btnThreshold: UIButton!
    @IBOutlet weak var btnSobel: UIButton!
    @IBOutlet weak var btnSave: UIButton!
    
    var imgCurrent:UIImage?
    let strImageUrl:NSString = "https://joindiaspora.s3.amazonaws.com/uploads/images/scaled_full_1081a16e63283b49bfab.jpg"
    // OpenCV操作クラス
    let imgController:ImageController = ImageController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        var imgSource:UIImage = UIImage(data: NSData(contentsOfURL: NSURL(string: strImageUrl)))
        
        // 初期化及び画像の操作
        imgController.initOpenCv(imgSource)
        // デフォルトでColor画像を表示する
        self.setImgColorAsCurrent()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    func setImgColorAsCurrent()
    {
        imgCurrent = imgController.getImgColor()
        self.updateImageViewer()
    }
    func updateImageViewer()
    {
        imgImageView.image = imgCurrent
    }
    @IBAction func btnColorTouched(sender: UIButton) {
        imgCurrent = imgController.getImgColor()
        self.updateImageViewer()
    }
    @IBAction func btnGrayTouched(sender: UIButton) {
        imgCurrent = imgController.getImgGray()
        self.updateImageViewer()
    }

    @IBAction func btnThresholdTouched(sender: UIButton) {
        imgCurrent = imgController.getImgThreshold()
        self.updateImageViewer()
    }
    @IBAction func btnSobelTouched(sender: UIButton) {
        imgCurrent = imgController.getImgSobel()
        self.updateImageViewer()
    }
    @IBAction func btnSaveTouched(sender: UIButton)
    {
        // Albumに画像を保存する
        UIImageWriteToSavedPhotosAlbum(imgCurrent, self, Selector("onSavedImage:error:contextInfo:"), nil)
    }
    func onSavedImage(image: UIImage!, error: NSErrorPointer, contextInfo: UnsafePointer<()>)
    {
        let alrShowMessage:UIAlertView = UIAlertView(title: "Finished", message: "保存しました", delegate: nil, cancelButtonTitle: "OK")
        
        alrShowMessage.show()
    }
}

変数

var 変数名: 型名 で変更可能な変数が、let 変数名: 型名 で変更不可能な変数を作成できます。

この際、デフォルトの値を設定しないで[var img: UIImage]のようにするとエラーとなります。

エラーがクラス名のところで表示されるので驚きましたが...。

[var img: UIImage?]または[var img: UIImage!]とすることでエラーを解消できます。

画像の保存

Objective-Cと同じくUIImageWriteToSavedPhotosAlbumで保存ができるのですが、保存後実行するメソッドを設定していた@Selectorがありません。

上記コード内では[Selector("onSavedImage:error:contextInfo:")]としていますが、単純に["onSavedImage:error:contextInfo:"]としても問題ありませんでした。

Objective-Cについては次回。

参考

Swift + OpenCV 2

前回の続きです。

Objective-C

File>New>FileでObjective-Cのファイルを追加すると、実装ファイルの他に通常の「クラス名.h」ではなく「プロジェクト名-Bridging-Header.h」というファイルが作成されます。

Swiftと連携するためのファイルとのことですが、記述する内容は変更ありません。

swImageViewer-Bridging-Header.h

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

@interface ImageController : NSObject
- (void)initOpenCv:(UIImage *)imgSource;
- (UIImage *)getImgColor;
- (UIImage *)getImgGray;
- (UIImage *)getImgThreshold;
- (UIImage *)getImgSobel;
@end

ImageController.mm

実装ファイルも同じですね。viewDidLoadで実行していた画像の操作を、initOpenCvで行っています。

#import "swImageViewer-Bridging-Header.h"
#import < opencv2 / opencv.hpp >
#import "opencv2 / highgui / ios.h"

@interface ImageController()
@property (strong, nonatomic) UIImage *imgColor;
@property (strong, nonatomic) UIImage *imgGray;
@property (strong, nonatomic) UIImage *imgThreshold;
@property (strong, nonatomic) UIImage *imgSobel;
@end
@implementation ImageController
- (void)initOpenCv:(UIImage *)imgSource
{
    cv::Mat matColor;
    // UIImageをcv::Matに変換する
    UIImageToMat(imgSource, matColor);
    // cv::MatをUIImageに変換
    _imgColor = MatToUIImage(matColor);
    
    // グレイスケール
    cv::Mat matGray;
    cv::cvtColor(matColor, matGray, CV_BGR2GRAY);
    // cv::MatをUIImageに変換
    _imgGray = MatToUIImage(matGray);
    
    // 白黒の2値化処理
    cv::Mat matThreshold;
    cv::threshold(matGray, matThreshold, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU);
    // cv::MatをUIImageに変換
    _imgThreshold = MatToUIImage(matThreshold);
    
    // Sobelフィルタによる輪郭検出
    cv::Mat matSobel;
    cv::Sobel(matThreshold, matSobel, CV_16S, 1, 0, 3);
    cv::convertScaleAbs(matSobel, matSobel);
    // cv::MatをUIImageに変換
    _imgSobel = MatToUIImage(matSobel);
}
- (UIImage *)getImgColor
{
    return _imgColor;
}
- (UIImage *)getImgGray
{
    return _imgGray;
}
- (UIImage *)getImgThreshold
{
    return _imgThreshold;
}
- (UIImage *)getImgSobel
{
    return _imgSobel;
}
@end

参考

終わりに

現状思ったよりもObjective-Cと共通するところが多く、Swiftに書き換えるときの手助けになっている気がします。

ただ、OpenCVやopenFrameworksなどCやC++を使うアプリの場合、少なくとも当面はObjective-Cで良いのかな、という印象。

何にしろまだBetaということで、アップデートを楽しみにしつつマイペースに触っていこうかな、というところです。