Swiftで画像をめちゃくちゃ簡単にあつかえるライブラリ

Swift で画像のピクセルを操作しようとすると面倒ですよね？大抵 _CoreGraphics_ を使うことになるわけですが、 API は C で書かれていて古いし、フォーマットは色々あるし、メモリマネジメントは苦痛だし、画像のピクセルにちょっとアクセスしたいだけなのにうんざりしてしまいます。

そこで、 Swift で画像を簡単にあつかえるようにするライブラリ [_EasyImagy_](https://github.com/koher/EasyImagy) を作りました。

```swift
var image = Image(named: "ImageName")!

// ピクセルのアクセス
println(image[y][x]) // `image[x, y]` も可
image[y][x] = Pixel(red: 255, green: 0, blue: 0, alpha: 255)

// 全ピクセルをイテレート
for pixel in image {
    ...
}

// 画像の変換（下記は二値化の例）
let binarized = image.map { $0.gray < 128 ? Pixel.black : Pixel.white }

// `UIImage` との相互変換
image = Image(UIImage: imageView.image!)
imageView.image = image.UIImage
```

## EasyImagy でできること

![Original.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/f7cc7075-e9d3-b63c-304a-0a64902453e6.jpeg)

以下、変換後のサンプル画像が掲載されている項がありますが、オリジナルは↑の画像です。

### ファイルからの読み込み

`UIImage` のようにファイル名を指定するだけで _main bundle_ からインスタンスを生成することができます。

```swift
let image = Image(named: "ImageName")
```

もちろん、パスを指定して読み込むこともできます。

```swift
let image = Image(contentsOfFile: "path/to/file")
```

_EasyImage_ で読み込んだ画像はすべて RGBA 形式に展開されます。各ピクセルは次のようなシンプルな構造体 `Pixel` で表されます。

```swift
struct Pixel {
    var red: UInt8
    var green: UInt8
    var blue: UInt8
    var alpha: UInt8
}
```

### UIImage との相互変換

いくら簡単に画像をあつかうことができても、 `UIImage` からインスタンスを生成したり、最終的に `UIImage` に変換できないと意味がないことも多いと思います。

次のように、 `UIImage` との相互変換も簡単に実現できます。

```swift
let image = Image(UIImage: imageView.image!)
```

```swift
imageView.image = image.UIImage
```


### ピクセルへのアクセス

_Subscript_ （ `[ ]` ）を使ってピクセルにアクセスすることができます。

指定した座標が画像からはみでてしまった場合は `nil` が返されます。クラッシュしたり、変な結果が返ってきたり（ `pixels[y * width + x]` で計算するとありがち）することはないので安全です。

```swift
if let pixel = image[y][x] { // `image[x, y]` も可
    println(pixel.red)
    println(pixel.green)
    println(pixel.blue)
    println(pixel.alpha)

    println(pixel.gray) // (red + green + blue) / 3
    println(pixel) // "#FF0000FF" のようにフォーマットされる
} else {
    // はみ出した場合
    println("Out of bounds")
}
```

上記のように、 `Pixel` には `gray` のような便利プロパティも用意されています（ _Computed Property_ なので、あくまで `Pixel` のサイズは 4 バイトの構造体です）。

`Image` は構造体なので、 `var` で宣言されていれば _Subscript_ を使ってピクセルを更新することができます。

```swift
image[y][x] = Pixel(red: 255, green: 0, blue: 0, alpha: 255)
image[y][x]?.alpha = 127
```

### イテレーション

`Image` は `Pixel` の `SequenceType` なので、次のようにして全ピクセルをいてレートして取り出すことができます。

```swift
for pixel in image {
    ...
}
```

また、次のようにして、座標と一緒に取り出すことも可能です。

```swift
for (x, y, pixel) in image.enumerate() {
	...
}
```

### 回転

![RotateClockwise.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/53792709-c293-52bf-58ab-da94531cda78.jpeg)

`rotate` メソッドを引数なしで呼ぶと、時計回りに 90 度回転した画像が得られます。

```swift
let result = image.rotate() // 時計回り
```

![RotateCounterClockwise.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/fe2be446-c444-cc8c-ae17-aadfdaebcf06.jpeg)

`rotate` メソッドに引数を渡すことで、逆回転したり 180 度、 270 度回転したりすることができます。

```swift
let result = image.rotate(-1) // 反時計回り
```

![Rotate180.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/cdcc5381-c71f-f470-31c1-cf2a080a98c7.jpeg)


```swift
let result = image.rotate(2) // 180 度回転
```

### 反転

![FlipHorizontal.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/495f9727-d4d8-0b27-40cc-80cc67fb8c37.jpeg)

```swift
let result = image.flipX() // 水平方向に反転
```

![FlipVertical.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/1f5115b2-874b-4c96-9024-e216adc400a9.jpeg)

```swift
let result = image.flipY() // 垂直方向に反転
```

### リサイズ

![Resize.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/a77dd793-04cf-966d-f1eb-01246edcf42b.jpeg)

```swift
let result = image.resize(width: 100, height: 100)
```

```swift
let result = image.resize(width: 100, height: 100,
	interpolationQuality: kCGInterpolationNone) // Nearest neighbor
```

### 切り出し

![Crop.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/7d880baf-2c09-4463-6eb8-2306e9f0828c.jpeg)

切り出しには、ピクセルのコピーコストは発生しません。

```swift
let resultOrNil = image[0..<100][0..<100] // はみ出したら `nil`
```

### 変換

`Array` と同じように、 `map` を使って画像を変換することができます。

#### グレースケール化

![Grayscale.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/9ff094d4-14f4-fd42-5b3e-ed88816e487e.jpeg)

```swift
let result = image.map { (pixel: Pixel) -> Pixel in
    Pixel(gray: pixel.gray)
}
```

```swift
// Shortened form
let result = image.map { Pixel(gray: $0.gray) }
```

#### 二値化

![Binarize.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/caf5d320-4f26-74b6-56af-67fb51ba79db.jpeg)

```swift
let result = image.map { (pixel: Pixel) -> Pixel in
    pixel.gray < 128 ? Pixel.black : Pixel.white
}
```

```swift
// Shortened form
let result = image.map { $0.gray < 128 ? Pixel.black : Pixel.white }
```

#### 二値化（自動閾値）

![BinarizeAutoThreshold.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/293ad084-279f-b724-27a0-806421cd8511.jpeg)

```swift
let threshold = UInt8(image.reduce(0) { $0 + $1.grayInt } / image.count)
let result = image.map { $0.gray < threshold ? Pixel.black : Pixel.white }
```

#### 平滑化フィルタの適用

![MeanFilter.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/14190d99-de5e-f34b-184c-9e6cae48b40f.jpeg)

`map` と切り出しを併用してより複雑なフィルタをかけることもできます。切り出しにピクセルのコピーコストが発生しないため、このような処理もシンプルに書くことができます。

```swift
let result = image.map { x, y, pixel in
	image[(y - 1)...(y + 1)][(x - 1)...(x + 1)].map {
		Pixel.mean($0)
	} ?? pixel
}
```

#### ガウシアンフィルタの適用

![GaussianFilter.jpg](https://qiita-image-store.s3.amazonaws.com/0/47085/4fe5db69-c4b7-5763-ba32-2e23c246de38.jpeg)

```swift
let weights = [
	1,  4,  6,  4, 1,
	4, 16, 24, 16, 4,
	6, 24, 36, 24, 6,
	4, 16, 24, 16, 4,
	1,  4,  6,  4, 1,
]
let result = image.map { x, y, pixel in
	image[(y - 2)...(y + 2)][(x - 2)...(x + 2)].map {
		Pixel.weightedMean(zip(weights, $0))
	} ?? pixel
}
```

## EasyImagy の Image がクラスではなく構造体な理由

_EasyImagy_ の `Image` はクラスではなく構造体です。

構造体（値型）ということは、代入の度に画像がまるごとコピーされるのかと思われるかもしれませんが、そんなことはありません。 `Image` は `Array` 同様に _Copy-on-write_ が効いているため、本当に必要になるまでコピーが実行されることはありません。逆に、 `Image` がクラスだと次のようなケースで無駄なコピーが必要になります。

例えば、イミュータブルなクラスがイニシャライザで `Image` を受け取る場合を考えます。もし `Image` がクラスであれば、イミュータブル性を保証するためにイニシャライザの内部で受け取った `Image` オブジェクトをコピーして保持する必要があります（その `Image` オブジェクトが外部で変更されてしまうとイミュータブルでなくなってしまうため）。しかし、実際には渡されたオブジェクトは変更されないこともあり、コピーが無駄になってしまうケースが多いはずです。構造体 & _Copy-on-write_ であればコピーは Lazy に実行されるので、本当にコピーが必要になったとき（渡された `Image` が外部でその後変更されたとき）までコピーが実行されることはありません。

画像の場合、データサイズが大きいことも多く、このコピーのコストはバカになりません。一方で、無駄な状態を避けメンテナンス性を上げるためには、イミュータブルにできるものはできる限りイミュータブルにしたい気持ちになります。そんなときに `Image` がクラスだと辛いことになります。僕が ["SwiftのArrayが実はすばらしかった"](http://qiita.com/koher/items/47f444200d4e02b6414e) を書いたときに思い浮かべていたのはそのようなケースで、 _EasyImagy_ はそのすばらしさを体現したライブラリです。

## ジェネリクスを採用しなかった理由

`Image` を `Image<T>` というジェネリックな構造体にして、 RGBA の画像は `Image<Pixel>` 、グレースケール画像は `Image<UInt8>` で表すという案もありました。

しかし、[実際に実装してみたところ](https://github.com/koher/EasyImagy/tree/experiment/generics)最適化がうまくいかないらしく著しくパフォーマンスが悪かったので、リリースバージョンとしては採用を見送りました。

あとは、今の Swift (1.2) では `extension Image<Pixel>` のようなことができないので、黒魔術を使いまくって実装したという微妙さもあります（例えば、 `UIImage` プロパティは任意の `T` に対して実装することはできないので、 `extension Image<Pixel|UInt8>` のようなものがほしくなります）。

ジェネリックな `Image` ができれば `Image<Int>` で _Integral Image_ にするとか、 `flatMap` でリサイズとか夢が広がるのですが・・・。

## インストール

### Carthage

[_Carthage_](https://github.com/Carthage/Carthage) に対応しています。 _Carthage_ をご利用の場合は _Cartfile_ に次の 1 行を足して下さい。

```
github "koher/EasyImagy" >= 0.1.0
```

### 手作業

`EasyImagy.xcodeproj` をプロジェクトまたはワークスペースに追加し、 `EasyImagy.framework` を _Embedded Binaries_ に追加して下さい。


## まとめ

[_EasyImagy_](https://github.com/koher/EasyImagy) を使えば Swift から簡単に画像のピクセルを操作することができるようになります。画像をあつかうのが面倒だと思ったときには是非試してみて下さい。