【发布时间】:2019-08-21 07:40:54
【问题描述】:
我们正在尝试对 UIImage 进行规范化,以便它可以正确地传递到 CoreML 模型中。
我们从每个像素中检索 RGB 值的方法是首先初始化一个名为 rawData 的 [CGFloat] 数组,该数组包含每个像素的值,这样就有了红色、绿色、蓝色和 alpha 值的位置.在bitmapInfo 中,我们从原始 UIimage 本身获取原始像素值并执行。这用于填充context 中的bitmapInfo 参数,CGContext 变量。稍后我们将使用context 变量将draw 转换为CGImage,稍后将标准化的CGImage 转换回UIImage。
使用嵌套的 for 循环遍历 x 和 y 坐标,找到所有像素中所有颜色(通过 CGFloat 的原始数据数组找到)中的最小和最大像素颜色值。
设置绑定变量以终止 for 循环,否则将出现超出范围错误。
range 表示可能的 RGB 值的范围(即最大颜色值与最小颜色值之间的差异)。
使用方程对每个像素值进行归一化:
A = Image
curPixel = current pixel (R,G, B or Alpha)
NormalizedPixel = (curPixel-minPixel(A))/range
和一个类似设计的嵌套for循环从上面解析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 个通道中的最小值吗?还是取决于渠道? -
@ielyamani - 不,他只想缩放颜色通道,而不是 alpha 值。例如,如果颜色的范围是 31-63(
UInt8),但 alpha 为 255,他仍然希望根据最大值 63 而不是 255 来缩放颜色。 -
@Rob
rawData[byteIndex+3] = ...不更新 alpha 通道吗? -
@ielyamani - 确实如此,但我确信这是一个错误。如果他真的想标准化颜色和 alpha 值,他会希望为每个值保持一个单独的最小和最大范围。但我敢打赌,他真正关心的只是颜色范围。
标签: swift image-processing uiimage normalization pixel