LoginSignup
0
0

More than 1 year has passed since last update.

インプットが TensorType のCoreMLモデルを実行したいときに

ImageTypeインプットのCoreMLモデルであればCIImageやCGImage、PixelBufferを入力として使えるが、TensorTypeインプットのCoreMLモデルはMLMultiArrayを入力として与える必要がある。

そのようなモデルに画像入力を使いたい時に、UIImageからMLMultiArrayに変換する方法。

方法

以下のUIImageExtensionで変換できる。

// Usage:
//     let mlMultiArray:MLMultiArray = uiImage.mlMultiArray()
//
// or if you need preprocess ...
//     let preProcessedMlMultiArray:MLMultiArray = uiImage.mlMultiArray(scale: 1/127.5, rBias: -1, gBias: -1, bBias: -1)
//
// or if you have gray scale image ...
//     let grayScaleMlMultiArray:MLMultiArray = uiImage.mlMultiArrayGrayScale()
extension UIImage {

    func mlMultiArray(scale preprocessScale:Double=1/255, rBias preprocessRBias:Double=0, gBias preprocessGBias:Double=0, bBias preprocessBBias:Double=0) -> MLMultiArray {
        let imagePixel = self.getPixelRgb(scale: preprocessScale, rBias: preprocessRBias, gBias: preprocessGBias, bBias: preprocessBBias)
        let size = self.size
        let imagePointer : UnsafePointer<Double> = UnsafePointer(imagePixel)
        let mlArray = try! MLMultiArray(shape: [3,  NSNumber(value: Float(size.width)), NSNumber(value: Float(size.height))], dataType: MLMultiArrayDataType.double)
        mlArray.dataPointer.initializeMemory(as: Double.self, from: imagePointer, count: imagePixel.count)
        return mlArray
    }
    
    func mlMultiArrayGrayScale(scale preprocessScale:Double=1/255,bias preprocessBias:Double=0) -> MLMultiArray {
        let imagePixel = self.getPixelGrayScale(scale: preprocessScale, bias: preprocessBias)
        let size = self.size
        let imagePointer : UnsafePointer<Double> = UnsafePointer(imagePixel)
        let mlArray = try! MLMultiArray(shape: [1,  NSNumber(value: Float(size.width)), NSNumber(value: Float(size.height))], dataType: MLMultiArrayDataType.double)
        mlArray.dataPointer.initializeMemory(as: Double.self, from: imagePointer, count: imagePixel.count)
        return mlArray
    }

    func getPixelRgb(scale preprocessScale:Double=1/255, rBias preprocessRBias:Double=0, gBias preprocessGBias:Double=0, bBias preprocessBBias:Double=0) -> [Double]
    {
        guard let cgImage = self.cgImage else {
            return []
        }
        let bytesPerRow = cgImage.bytesPerRow
        let width = cgImage.width
        let height = cgImage.height
        let bytesPerPixel = 4
        let pixelData = cgImage.dataProvider!.data! as Data
        
        var r_buf : [Double] = []
        var g_buf : [Double] = []
        var b_buf : [Double] = []
        
        for j in 0..<height {
            for i in 0..<width {
                let pixelInfo = bytesPerRow * j + i * bytesPerPixel
                let r = Double(pixelData[pixelInfo])
                let g = Double(pixelData[pixelInfo+1])
                let b = Double(pixelData[pixelInfo+2])
                r_buf.append(Double(r*preprocessScale)+preprocessRBias)
                g_buf.append(Double(g*preprocessScale)+preprocessGBias)
                b_buf.append(Double(b*preprocessScale)+preprocessBBias)
            }
        }
        return ((b_buf + g_buf) + r_buf)
    }
    
    func getPixelGrayScale(scale preprocessScale:Double=1/255, bias preprocessBias:Double=0) -> [Double]
    {
        guard let cgImage = self.cgImage else {
            return []
        }
        let bytesPerRow = cgImage.bytesPerRow
        let width = cgImage.width
        let height = cgImage.height
        let bytesPerPixel = 2
        let pixelData = cgImage.dataProvider!.data! as Data
        
        var buf : [Double] = []
        
        for j in 0..<height {
            for i in 0..<width {
                let pixelInfo = bytesPerRow * j + i * bytesPerPixel
                let v = Double(pixelData[pixelInfo])
                buf.append(Double(v*preprocessScale)+preprocessBias)
            }
        }
        return buf
    }
    
}

使い方
let mlMultiArray:MLMultiArray = uiImage.mlMultiArray()
// print(mlMultiArray.shape)
// [3,512,512]

// CoreMLの入力用に前処理をする場合 ...
let preProcessedMlMultiArray:MLMultiArray = uiImage.mlMultiArray(scale: 1/127.5, rBias: -1, gBias: -1, bBias: -1)

// GrayScale(1チャンネル)の場合 ...
let grayScaleMlMultiArray:MLMultiArray = uiImage.mlMultiArrayGrayScale()
// print(grayScaleMlMultiArray.shape)
// [1,512,512]
きちんと変換できているかの確認

CoreMLHelpersを使って変換したMLMultiArrayをUIImageに逆変換することでちゃんと変換できているか確認できる。

// scale = 255の場合
let resultUIImage = mlMultiArray.cgImage(min: 0, max: 1, channel: nil)
// scale = 255 各bias = -1の場合
let resultUIImage = preProcessedMlMultiArray.cgImage(min: -1, max: 1, channel: nil)
// min, maxは前処理結果のレンジ

以下の記事を参考に前処理などを加えました。

🐣


フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

機械学習、ARアプリ(Web/iOS)を作っています。
機械学習/AR関連の情報を発信しています。

Twitter
Medium
GitHub

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0