我们尝试规范化一个UIImage
,以便它可以正确地传递到CoreML模型中。
我们从每个像素中检索RGB值的方式是首先初始化一个名为rawData
的[CGFloat]
数组,该数组包含每个像素的值,使得颜色红、绿、蓝和 alpha 值都有一个位置。在bitmapInfo
中,我们从原始UIimage本身获取原始像素值并进行处理。这用于填充context
参数,它是一个CGContext
变量。稍后,我们将使用context
变量来draw
一个CGImage
,后者将把规范化的CGImage
转换回一个UIImage
。
使用嵌套的循环迭代x
和y
坐标,找到所有像素中所有颜色的最小和最大像素颜色值(通过CGFloat
的原始数据数组找到)。设置bound变量以终止for循环,否则将出现超出范围的错误。
range
指示可能的 RGB 值范围(即最大颜色值和最小颜色值之间的差)。
使用规范化每个像素值的方程:
A = Image
curPixel = current pixel (R,G, B or Alpha)
NormalizedPixel = (curPixel-minPixel(A))/range
我们需要一个类似于上面的嵌套循环来遍历rawData
数组,并根据此规范修改每个像素的颜色。
我们的大部分代码都来自于:
- UIImage to UIColor array of pixel colors
- Change color of certain pixels in a UIImage
- https://gist.github.com/pimpapare/e8187d82a3976b851fc12fe4f8965789
我们使用CGFloat
而不是UInt8
,因为规范化的像素值应该是介于0和1之间的实数,而不是0或1。
func normalize() -> UIImage?{
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let cgImage = cgImage else {
return nil
}
let width = Int(size.width)
let height = Int(size.height)
var rawData = [CGFloat](repeating: 0, count: width * height * 4)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bytesPerComponent = 8
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
let context = CGContext(data: &rawData,
width: width,
height: height,
bitsPerComponent: bytesPerComponent,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo)
let drawingRect = CGRect(origin: .zero, size: CGSize(width: width, height: height))
context?.draw(cgImage, in: drawingRect)
let bound = rawData.count
//find minimum and maximum
var minPixel: CGFloat = 1.0
var maxPixel: CGFloat = 0.0
for x in 0..<width {
for y in 0..<height {
let byteIndex = (bytesPerRow * x) + y * bytesPerPixel
if(byteIndex > bound - 4){
break
}
minPixel = min(CGFloat(rawData[byteIndex]), minPixel)
minPixel = min(CGFloat(rawData[byteIndex + 1]), minPixel)
minPixel = min(CGFloat(rawData[byteIndex + 2]), minPixel)
minPixel = min(CGFloat(rawData[byteIndex + 3]), minPixel)
maxPixel = max(CGFloat(rawData[byteIndex]), maxPixel)
maxPixel = max(CGFloat(rawData[byteIndex + 1]), maxPixel)
maxPixel = max(CGFloat(rawData[byteIndex + 2]), maxPixel)
maxPixel = max(CGFloat(rawData[byteIndex + 3]), maxPixel)
}
}
let range = maxPixel - minPixel
print("minPixel: \(minPixel)")
print("maxPixel : \(maxPixel)")
print("range: \(range)")
for x in 0..<width {
for y in 0..<height {
let byteIndex = (bytesPerRow * x) + y * bytesPerPixel
if(byteIndex > bound - 4){
break
}
rawData[byteIndex] = (CGFloat(rawData[byteIndex]) - minPixel) / range
rawData[byteIndex+1] = (CGFloat(rawData[byteIndex+1]) - minPixel) / range
rawData[byteIndex+2] = (CGFloat(rawData[byteIndex+2]) - minPixel) / range
rawData[byteIndex+3] = (CGFloat(rawData[byteIndex+3]) - minPixel) / range
}
}
let cgImage0 = context!.makeImage()
return UIImage.init(cgImage: cgImage0!)
}
在规范化之前,我们期望像素值的范围为0-255,在规范化后,像素值的范围是0-1。
规范化公式能够将像素值规范化为0到1之间的值。但是当我们尝试打印出(仅在遍历像素值时添加打印语句)规范化之前的像素值以验证是否正确获取原始像素值时,我们发现这些值的范围不正确。例如,某个像素值的值为3.506e+305(大于255)。我们认为我们一开始就错误地获得了原始像素值。
我们不熟悉Swift中的图像处理,也不确定整个规范化过程是否正确。任何帮助都将不胜感激!
minPixel
应该是整个rawData
数组中最小的值,还是仅在当前像素之前的像素中最小的值?并且它应该是所有 4 个通道中最小的值吗?还是取决于通道? - ielyamanirawData[byteIndex+3] = ...
不会更新 alpha 通道吗? - ielyamani