なすびのブログ

iOS・swiftの情報を気ままに書いていきます。

【swift4.2】UIImageから二値化画像を作成する

はじめに

こんにちは、なすびです。
この前カメラロールの画像を二値化するアプリをストアに公開しました。

その時にswiftでの二値化処理の情報があまり見つからなかったのでメモ書き程度に二値化処理を共有しようかと思います。
今回の例は単純二値化の処理になります。

ちなみにこんなアプリです。よかったらインストールお願いします!

ニチカメラ

ニチカメラ

  • Naoya Ishida
  • 写真/ビデオ
  • 無料

UIImageからピクセルデータを取得

・UIImage → CGImage → ピクセルデータとどんどん下位のデータにアクセスしバッファに詰め込む。

// 画像データを取得
let image = UIImage(named: "SampleImage")!
let imageRef = image.cgImage!
let data = imageRef.dataProvider!.data
let buffer = CFDataGetBytePtr(data)!


二値化処理

・今回のデータは8bitのRGBA画像を想定。
そのため二値化後のデータを格納するimageBytesをUInt8で高さ×幅×4(RGBA)で準備する。
・計算するピクセルの先頭インデックスを計算する。
・先頭インデックスから8bitずつずらして各色情報にアクセスする。
・RGBの平均値が閾値を超えるかを判定し、0と255を割り当てる。

let threshold = 128
let imageBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: imageRef.height * imageRef.width * 4)
// 各ピクセルに対し色を割り当てる
for y in 0..<imageRef.height {
    for x in 0..<imageRef.width {
        // ピクセルの先頭の配列
        let frontPixelIndex = (y * imageRef.width + x) * 4
        let red     = Float(buffer[frontPixelIndex + 0]) / 255.0
        let green   = Float(buffer[frontPixelIndex + 1]) / 255.0
        let blue    = Float(buffer[frontPixelIndex + 2]) / 255.0
        let alpha   = buffer[frontPixelIndex + 3]
        let average = UInt8((red + green + blue) * 255 / 3)
        let binaryValue = average < threshold * 255 ? 0 : 255
        imageBytes[frontPixelIndex + 0] = binaryValue
        imageBytes[frontPixelIndex + 1] = binaryValue
        imageBytes[frontPixelIndex + 2] = binaryValue
        imageBytes[frontPixelIndex + 3] = alpha
    }
}


二値化後のピクセルデータからUIImageを作成

ピクセルデータからCFDataRefを作成
・忘れずにimageBytesを解放する
・元画像の情報を元にCGImageを作成
・UIImageを作成

// 画像処理後のデータから画像を作成
let resultData = CFDataCreate(nil, imageBytes, imageRef.height * imageRef.width * 4)!
imageBytes.deallocate()
let  resultDataProvider = CGDataProvider(data: resultData)!
let resultImageRef = CGImage(width: imageRef.width,
                             height: imageRef.height,
                             bitsPerComponent: imageRef.bitsPerComponent,
                             bitsPerPixel: imageRef.bitsPerPixel,
                             bytesPerRow: imageRef.bytesPerRow,
                             space: imageRef.colorSpace!,
                             bitmapInfo: imageRef.bitmapInfo,
                             provider: resultDataProvider,
                             decode: nil,
                             shouldInterpolate: imageRef.shouldInterpolate,
                             intent: imageRef.renderingIntent)!
let resultImage = UIImage(cgImage: resultImageRef, scale: 1.0, orientation:image.imageOrientation)


最後に

今回はswiftで単純二値化処理を行うサンプルを説明しました。
カラースペースは8bitのRGBA固定でやっているのでそれ以外の場合にも対応できるように修正する必要はあるかと思います。

やっぱり画像の扱いはどんどん型を変換していく必要があるのでわかりにくい印象がありますね。
精進せねば。。。

今後も画像以外にもswiftに関するブログを書くのでよかったら読者登録お願いします!