gpt4 book ai didi

swift - 从灰度矩阵创建 CGImage/UIImage

转载 作者:行者123 更新时间:2023-11-28 05:41:49 25 4
gpt4 key购买 nike

我有一个灰度图像像素矩阵,例如:

[ [0, 0, 125],
[10, 50, 255],
[90, 0, 255] ]

我的目标是对其应用色调 (UIColor) 并从包含它的结构中导出 CGImage/UIImage

public typealias Pixel = UInt8

extension UIColor {
var red: Float { return Float(CIColor(color: self).red * 255) }
var green: Float { return Float(CIColor(color: self).green * 255) }
var blue: Float { return Float(CIColor(color: self).blue * 255) }
var alpha: Float { return Float(CIColor(color: self).alpha * 255) }
}

public struct PixelData {
let r: UInt8
let g: UInt8
let b: UInt8
let a: UInt8
}

public struct Map {
let pixelCount: UInt
let pixels: [Pixel] //all pixels of an image, linear
let dimension: UInt //square root of pixel count
let tintColor: UIColor = UIColor(red: 9/255, green: 133/255, blue: 61/255, alpha: 1)

public var image: UIImage? {
var pixelsData = [PixelData]()
pixelsData.reserveCapacity(Int(pixelCount) * 3)
let alpha = UInt8(tintColor.alpha)
let redValue = tintColor.red
let greenValue = tintColor.green
let blueValue = tintColor.blue
let red: [PixelData] = pixels.map {
let redInt: UInt8 = UInt8((Float($0) / 255.0) * redValue)
return PixelData(r: redInt, g: 0, b: 0, a: alpha)
}
let green: [PixelData] = pixels.map {
let greenInt: UInt8 = UInt8((Float($0) / 255.0) * greenValue)
return PixelData(r: 0, g: greenInt, b: 0, a: alpha) }
let blue: [PixelData] = pixels.map {
let blueInt: UInt8 = UInt8((Float($0) / 255.0) * blueValue)
return PixelData(r: 0, g: 0, b: blueInt, a: alpha) }
pixelsData.append(contentsOf: red)
pixelsData.append(contentsOf: green)
pixelsData.append(contentsOf: blue)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
let bitsPerComponent = 8
let bitsPerPixel = 32
let dimension: Int = Int(self.dimension)
var data = pixelsData
guard let providerRef = CGDataProvider(
data: NSData(bytes: &data, length: data.count * MemoryLayout<PixelData>.size)
) else { return nil }
if let cgim = CGImage(
width: dimension,
height: dimension,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: dimension * MemoryLayout<PixelData>.size,
space: rgbColorSpace,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent
) {
return UIImage(cgImage: cgim)
}
return nil
}
}

问题是输出看起来乱码。我用过this tutorialthis SO thread但没有成功。 Playground 上的结果是:

enter image description here

(输出在那里,只是勉强可见)

感谢任何帮助!

最佳答案

有两个关键问题。

  1. 代码计算每个灰度像素的所有红色值并为每个创建四字节 PixelData(即使只填充红色 channel )并将其添加到 pixelsData 数组。然后它对绿色值重复该操作,然后对蓝色值重复该操作。结果是图像所需数据的三倍,而且只使用了红色 channel 数据。

    相反,我们应该计算一次 RGBA 值,为每个值创建一个 PixelData,然后逐个像素地重复这个过程。

  2. premultipliedFirst 表示 ARGB。但是您的结构使用的是 RGBA,因此您需要 premultipliedLast

因此:

func generateTintedImage(completion: @escaping (UIImage?) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
let image = self.tintedImage()
DispatchQueue.main.async {
completion(image)
}
}
}

private func tintedImage() -> UIImage? {
let tintRed = tintColor.red
let tintGreen = tintColor.green
let tintBlue = tintColor.blue
let tintAlpha = tintColor.alpha

let data = pixels.map { pixel -> PixelData in
let red = UInt8((Float(pixel) / 255) * tintRed)
let green = UInt8((Float(pixel) / 255) * tintGreen)
let blue = UInt8((Float(pixel) / 255) * tintBlue)
let alpha = UInt8(tintAlpha)
return PixelData(r: red, g: green, b: blue, a: alpha)
}.withUnsafeBytes { Data($0) }

let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let bitsPerComponent = 8
let bitsPerPixel = 32

guard
let providerRef = CGDataProvider(data: data as CFData),
let cgImage = CGImage(width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: width * MemoryLayout<PixelData>.stride,
space: rgbColorSpace,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent)
else {
return nil
}

return UIImage(cgImage: cgImage)
}

我还重命名了一些变量,使用 stride 而不是 size,将 dimension 替换为 widthheight 这样我就可以处理非方形图像等。

我还建议不要对这种计算密集型的任何东西使用计算属性,所以我给了它一个异步方法,您可以按如下方式使用它:

let map = Map(with: image)
map.generateTintedImage { image in
self.tintedImageView.image = image
}

无论如何,上面的结果如下,其中最右边的图像是您的着色图像:

enter image description here


不用说,要将您的矩阵转换为您的像素数组,您只需展平数组的数组即可:

let matrix: [[Pixel]] = [
[0, 0, 125],
[10, 50, 255],
[90, 0, 255]
]
pixels = matrix.flatMap { $0 }

这是一个并行化的再现,它在内存缓冲区方面也稍微更有效:

private func tintedImage() -> UIImage? {
let tintAlpha = tintColor.alpha
let tintRed = tintColor.red / 255
let tintGreen = tintColor.green / 255
let tintBlue = tintColor.blue / 255

let alpha = UInt8(tintAlpha)

let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
let bitsPerComponent = 8
let bytesPerRow = width * MemoryLayout<PixelData>.stride

guard
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo),
let data = context.data
else {
return nil
}

let buffer = data.bindMemory(to: PixelData.self, capacity: width * height)

DispatchQueue.concurrentPerform(iterations: height) { row in
let start = width * row
let end = start + width
for i in start ..< end {
let pixel = pixels[i]
let red = UInt8(Float(pixel) * tintRed)
let green = UInt8(Float(pixel) * tintGreen)
let blue = UInt8(Float(pixel) * tintBlue)
buffer[i] = PixelData(r: red, g: green, b: blue, a: alpha)
}
}

return context.makeImage()
.flatMap { UIImage(cgImage: $0) }
}

关于swift - 从灰度矩阵创建 CGImage/UIImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56427766/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com