インプットが 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関連の情報を発信しています。