UIImageとIplImageの相互変換


iOSアプリでOpenCVを使うとき、表示はUIImageが楽だけどIplImage(cv::mat)じゃないと処理できない！みたいなことあると思います。
注意としては解放を忘れないこと。特にIplImageのリリースを忘れると謎のメモリリークと戦うはめになります（戦いました…）。

なんだか変換に変換を繰り返すので、もっといい方法知っている方いらっしゃればご教授頂けると幸いです。

## UIImage* を IplImage* に変換する
こんなメソッドで実現できます。個人的には、Util用のクラス（ファイル）を用意してクラスメソッドにしておくと便利だと思います。OpenCVで処理する部分はC++で書くので、拡張子は"mm"としています。

```objective-c:method.mm
+ (IplImage *)createIplImageFromUIImage:(UIImage *)uiImage
{
    CGImageRef imageRef = uiImage.CGImage;
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    IplImage *iplimage = cvCreateImage(cvSize(uiImage.size.width, uiImage.size.height), IPL_DEPTH_8U, 4);
    
    // IplImageからCGBitmapContextを作成
    CGContextRef contextRef = CGBitmapContextCreate(
                                                    iplimage->imageData,
                                                    iplimage->width,
                                                    iplimage->height,
                                                    iplimage->depth,
                                                    iplimage->widthStep,
                                                    colorSpace,
                                                    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault);
    
    // CGBitmapContextに描画
    CGContextDrawImage(contextRef, CGRectMake(0, 0, uiImage.size.width, uiImage.size.height), imageRef);
    
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpace);
    
    // 出力するIplImageを作成
	// OpenCVではデフォルトの色配列がBGRになっているので合わせます
    IplImage *outputImage = cvCreateImage(cvGetSize(iplimage), IPL_DEPTH_8U, 3);
    cvCvtColor(iplimage, outputImage, CV_RGBA2BGR);
    
	// 解放を忘れない
	cvReleaseImage(&iplimage);
    
    return outputImage;
}
```

使うときはこんな感じです。

```objective-c:use.mm
#import "Util.h"

UIImage *inputImage = [なにか用意];
IplImage *getImage = [Util createIplImageFromUIImage:inputImage];
```

## IplImage* を UIImage* に変換する
処理が終わったら表示したいのでUIImage*のほうが都合がいいので戻します。

```objective-c:method.mm
+ (UIImage *)UIImageFromIplImage:(IplImage *)inputImage
{
    CGColorSpaceRef colorSpace;
    if (inputImage->nChannels == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    }
    else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
        
		// 先ほどの逆ですが、OpenCVの色配列はBGRになっているのでRGBに変換
        cvCvtColor(inputImage, inputImage, CV_BGR2RGB);
    }
    
    // IplImageからNSDataを作成
    NSData *data = [NSData dataWithBytes:inputImage->imageData length:inputImage->imageSize];
    
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    
    // CGImageを作成
    CGImageRef imageRef = CGImageCreate(inputImage->width,
                                        inputImage->height,
                                        inputImage->depth,
                                        inputImage->depth * inputImage->nChannels,
                                        inputImage->widthStep,
                                        colorSpace,
                                        kCGImageAlphaNone|kCGBitmapByteOrderDefault,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault
                                        );
    
    // UIImageをimageRefから作成
    UIImage *outputImage = [UIImage imageWithCGImage:imageRef];
    
	// しつこいようですが解放を忘れない
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
    
    return outputImage;
}
```

## おまけ：Iplimage* と cv::Mat の相互変換
比較的新しいバージョンのOpenCVだとIplImageではなくcv::Matが使われるようになっていたりするので、この変換です。

```c++:method.cpp
IplImage *iplImage = ...;
cv::Mat  *matImage = ...;

// IplImage を cv::Mat に変換
matImage = cv::cvarrToMat(iplImage);

// なんやかんや編集して
matImage.at<int>(y, x) = ...;

// cv::Mat を IplImage に変換
*iplImage = matImage;
```