34533


<https://stackoverflow.com/questions/448125/how-to-get-pixel-data-from-a-
uiimage-cocoa-touch-or-cgimage-core-graphics/1262893#1262893>

上記タイトルの通りです。本家のstackoverflowの記事について興味がありこのような方法で  
色情報を抜き出すにはどのようにコードを書くのが良いでしょうか。  
宜しくお願い致します。


CC BY-SA 3.0
2017-05-08T00:06:21.533
34529
2017-05-08T01:35:11.993
2017-05-23T12:38:56.083
-1
14780
question
swift3
CGContext で bitmap を作成し、 UIImage を描画してbitmap のビットの値を出力したい


ほとんどが掲載していただいたリンク先のコードをSwift
3に置き換えただけなのですが、以下のような感じになります。[関連質問](https://ja.stackoverflow.com/a/34524/13972)のコードとも見比べながらご覧いただければと思います。

[code]

    class BitmapBuffer {
        private var pixelData: Data
        let width: Int
        let height: Int
        private let bytesPerRow: Int
        private let bytesPerPixel = 4
    
        init?(uiImage: UIImage) {
            guard
                //CGImageが取得できること
                let cgImage = uiImage.cgImage
                else {return nil}
            //自分が望む形式でCGContextを作成する
            width = cgImage.width
            height = cgImage.height
            let colorSpace = cgImage.colorSpace!
            bytesPerRow = bytesPerPixel * width
            pixelData = Data(count: height * bytesPerRow)
            let bitsPerComponent = 8
            pixelData.withUnsafeMutableBytes {(rawData: UnsafeMutablePointer<UInt8>)->Void in
                let context = CGContext(data: rawData,
                                        width: width,
                                        height: height,
                                        bitsPerComponent: bitsPerComponent,
                                        bytesPerRow: bytesPerRow,
                                        space: colorSpace,
                                        bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
                context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
            }
        }
    
        func getColor(x: Int, y: Int) -> UIColor {
            let pixelInfo = bytesPerRow * y + x * bytesPerPixel
            let r = CGFloat(pixelData[pixelInfo]) / CGFloat(255.0)
            let g = CGFloat(pixelData[pixelInfo+1]) / CGFloat(255.0)
            let b = CGFloat(pixelData[pixelInfo+2]) / CGFloat(255.0)
            let a = CGFloat(pixelData[pixelInfo+3]) / CGFloat(255.0)
    
            return UIColor(red: r, green: g, blue: b, alpha: a)
        }
    
        subscript (x: Int, y: Int) -> UIColor {
            return getColor(x: x, y: y)
        }
    }
    
[/code]

コードとしては非常に似ているのでわかりにくい点もあるかもしれませんが、`CGContext`を作成して、そこに`draw(_:in:)`で元の`CGImage`の中身を描画しています。

以上のクラスを使って全画素をUIColorとしてコンソールに出力するコードはこんな感じになります。

[code]

        //リソースから画像読み込みUIImage作成
        let url = Bundle.main.url(forResource: "sample", withExtension: "png")
        do {
            let imageData = try Data(contentsOf: url!,options: NSData.ReadingOptions.mappedIfSafe)
            let img = UIImage(data: imageData)!
    
            if let pixelBuffer = BitmapBuffer(uiImage: img) {
                for y in 0..<pixelBuffer.height {
                    for x in 0..<pixelBuffer.width {
                        print(pixelBuffer[x, y].debugDescription)
                    }
                }
            } else {
                print("Bad image format")
            }
        } catch {
            print(error)
        }
    
[/code]

こちらは関連質問のコードと使用するクラス名が変わっているだけで、全く同じですが、`BitmapBuffer`のコンストラクタが`nil`を返すのは`UIImage`から`CGImage`を取得できない場合だけなので、より適用できる範囲が広がっていると言うことになります。

* * *

`CGContext`に描画することで自分の所望のビットマップ形式に変換できると言うことで、RGBA各8ビットではなく、RGBA各32ビット(`Float`)で各画素のデータを取得する例も挙げておきます。

[code]

    class BitmapBufferF {
        private var pixelData: Data
        let width: Int
        let height: Int
        private let bytesPerRow: Int
        private let bytesPerPixel = 16 //RGBA各32ビット(4バイト)なので1画素が16バイト
    
        init?(uiImage: UIImage) {
            guard
                //CGImageが取得できること
                let cgImage = uiImage.cgImage
                else {return nil}
            //自分が望む形式でCGContextを作成する
            width = cgImage.width
            height = cgImage.height
            let colorSpace = cgImage.colorSpace!
            bytesPerRow = bytesPerPixel * width
            pixelData = Data(count: height * bytesPerRow)
            let bitsPerComponent = 32 //R,G,B,Aの各要素は32ビット
            pixelData.withUnsafeMutableBytes {(rawData: UnsafeMutablePointer<UInt8>)->Void in
                let context = CGContext(data: rawData,
                                        width: width,
                                        height: height,
                                        bitsPerComponent: bitsPerComponent,
                                        bytesPerRow: bytesPerRow,
                                        space: colorSpace,
                                        bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
                                            //Floatを使用することをCGBitmapInfoで明示
                                            | CGBitmapInfo.floatComponents.rawValue
                                            | CGBitmapInfo.byteOrder32Little.rawValue)!
                context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
            }
        }
    
        func getColor(x: Int, y: Int) -> UIColor {
            let pixelInfo = (width * y + x) * 4
            return pixelData.withUnsafeMutableBytes {(pixelPointer: UnsafeMutablePointer<Float>) in
                let r = CGFloat(pixelPointer[pixelInfo])
                let g = CGFloat(pixelPointer[pixelInfo+1])
                let b = CGFloat(pixelPointer[pixelInfo+2])
                let a = CGFloat(pixelPointer[pixelInfo+3])
    
                return UIColor(red: r, green: g, blue: b, alpha: a)
            }
        }
    
        subscript (x: Int, y: Int) -> UIColor {
            return getColor(x: x, y: y)
        }
    }
    
[/code]

あまりたくさんの例で試してはいないので、細かい誤り等あるかもしれませんが、動作確認はできるはずです。誤りを見つけられたり、この回答そのものに対するご質問等があれば、コメント等でお知らせください。


CC BY-SA 3.0
2017-05-08T01:35:11.993
34533
2017-05-08T01:35:11.993
13972
34529
answer