[Objective-C] 画像処理についてのまとめ


画像描画関連のまとめ

##CGContextを使って画像描画

雰囲気としては、JavaScriptのCanvasを使ったことがある人であれば、あのイメージです。
画像を加工するフローとしては以下。

1. CGContextのサイズを決めて開く
2. CGContextを取得
3. （必要であれば）CGContextのtransformを設定
4. Draw／Render系メソッドを使ってCGContextに画像を描画
5. CGContextから描画済の画像を取得
6. CGContextを閉じる

###サンプルコード

```objc
//コンテキストを開く
CGSize size = [描画したい画像サイズ];
UIGraphicsBeginImageContext(size);

//コンテキストを得る
CGContextRef context = UIGraphicsGetCurrentContext();

//コンテキストの状態を保存
CGContextSaveGState(context);

//コンテキストのtransformを操作（必要であれば）
//例ではX軸で画像を反転
CGContextTranslateCTM(context, size.width, 0);
CGContextScaleCTM(context, -1.0f, 1.0f);

//UIImageの`drawInRect:`メソッドでコンテキストにレンダリング
[aImage drawInRect:CGRectMake(0, 0, size.width, size.height)];

//コンテキストに描画済の画像を得る
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();

//コンテキストを閉じる
UIGraphicsEndImageContext();

// コンテキストを解放
CGContextRestoreGState(context);
```

`CGContextTranslateCTM`などのtransformを変更する関数の意味は、設定後にレンダリングされる画像の位置を変更する効果がある。（以下の画像参照）
コンテキストに、`CGContextTranslateCTM(ctx, 100, 100)`を実行してから画像をレンダリングすると、画像の左上（原点）がコンテキストの`(100, 100)`の位置でレンダリングされる。

![sample.png](https://qiita-image-store.s3.amazonaws.com/0/5616/b833bf38-ecdb-ab6c-cc0f-4f3d829dd862.png "sample.png")

---------------------------

##画像をリサイズする

画像をリサイズするにはまず、Core Graphicsを利用してリサイズ後の画像をレンダリングしなおして取得します。
簡単に手順を書くと

1. `UIGraphicsBeginImageContext([size])`関数を呼び出し、リサイズしたいサイズのコンテキストを開始
2. UIImageインスタンスの`drawInRect:`メソッドを使って現在のコンテキストに画像をレンダリング
3. `UIGraphicsGetImageFromCurrentImageContext()`関数を使って、現在のコンテキストからレンダリングされた画像を取得

という流れになります。

###サンプルコード

```objc
// リサイズ画像のサイズ
CGSize resized_size = CGSizeMake(targetWidth, targetHeight); // リサイズしたいサイズを決める

// コンテキストを開始
UIGraphicsBeginImageContext(resized_size);

// 現在のコンテキストに画像をレンダリング
[aImage drawInRect:CGRectMake(0, 0, resized_size.width, resized_size.height)];

// 現在のコンテキストから、レンダリングされた画像を取得
image = UIGraphicsGetImageFromCurrentContext();

// コンテキストを終了
UIGraphicsEndImageContext();
```


---------------------------

##Retinaディスプレイ対応デバイスで見ると画像がぼやける（荒くなる）ときの対処

上記の方法で画像を生成すると、`UIGraphicsBeginImageContext()`関数の引数に渡したサイズの画像になります。このサイズに、UIViewや画像のサイズをそのまま渡すと、Retinaで見た場合に荒くなる問題があります。
理由としては、プログラム上でやりとりしているサイズはRetinaディスプレイのサイズになっていないからです。

この問題に対処するためには、`UIGraphicsBeginImageContextWithOptions()`関数を利用します。
定義は以下の通り。

```define.m
void UIGraphicsBeginImageContextWithOptions(
   CGSize size,
   BOOL opaque,
   CGFloat scale
);
```

Referenceから引用すると、それぞれの引数の意味は以下になります。

>
##Parameters
* **size**  
The size (measured in points) of the new bitmap context. This represents the size of the image returned by the UIGraphicsGetImageFromCurrentImageContext function. To get the size of the bitmap in pixels, you must multiply the width and height values by the value in the scale parameter.
* **opaque**  
A Boolean flag indicating whether the bitmap is opaque. If you know the bitmap is fully opaque, specify YES to ignore the alpha channel and optimize the bitmap’s storage. Specifying NO means that the bitmap must include an alpha channel to handle any partially transparent pixels.
* **scale**  
The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.

`size`は画像コンテキストのサイズ。生成したい画像のサイズですね。
注意点としては、Retina対応のためのサイズを指定「しない」ということです。

`opaque`は「不透明」のフラグ。完全に不透明な画像の場合は`YES`を指定するとパフォーマンスいいよ、ってことが書いてあります。

`scale`、これがRetina対応のために有効なオプションになります。
ここに`2`を指定すれば倍サイズとなり、めでたくRetina対応された画像が生成できます。
ただ、リファレンスを見ると`0.0`を指定するとmain screenの値を使用すると書いてあるので、基本的には`0.0`を指定しておくとよさそうです。

---------------------------

##UIVIewをUIImageに変換する

UIViewのlayerをCGContextに描画することで、UIViewの状態をUIImage化します。

```objc
// OpaqueueをNOにすると透過したキャプチャが取れる
UIGraphicsBeginImageContextWithOptions(aView.frame.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();

[aView.layer renderInContext:context];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
```

---------------------------

##UIImageにCIFilterを使う

まずはCIImageに変換し、そこからfilterを適用します。

```objc
// NSDataからUIImageを生成
UIImage *aImage = [[UIImage alloc] initWithData:imageData];

// UIImageをCIImageに変換
CIImage *filteredImage = [[CIImage alloc] initWithCGImage:aImage.CGImage];

// CIFilterを作成
CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
[filter setValue:filteredImage forKey:@"inputImage"];

// フィルタ後の画像を取得
filteredImage = filter.outputImage;

// CIImageをUIImageに変換する
CIContext *ciContext = [CIContext contextWithOptions:nil];
CGImageRef imageRef = [ciContext createCGImage:filteredImage fromRect:[filteredImage extent]];
UIImage *outputImage = [UIImage imageWithCGImage:imageRef scale:1.0f orientation:UIImageOrientationUp];
CGImageRelease(imageRef);

UIImageView *aView = [[UIImageView alloc] initWithImage:outputImage];
[wself.view addSubview:aView];
```

---------------------------

##参考記事

* [自前で描画した内容がUIImageで ぼやける時の処置](http://d.akiroom.com/2012-03/cgcontext-uiimage-blur/)
* [UIKit Function Reference](https://developer.apple.com/library/ios/documentation/uikit/reference/UIKitFunctionReference/Reference/reference.html)
* [CGContext Reference](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html)