Swift中的像素数组到UIImage

Posted

技术标签:

【中文标题】Swift中的像素数组到UIImage【英文标题】:Pixel Array to UIImage in Swift 【发布时间】:2015-06-20 20:13:29 【问题描述】:

我一直在试图弄清楚如何在 Swift 中将 rgb 像素数据数组转换为 UIImage。

我将每个像素的 rgb 数据保存在一个简单的结构中:

public struct PixelData 
   var a: Int
   var r: Int
   var g: Int
   var b: Int

我已使用以下功能,但生成的图像不正确:

func imageFromARGB32Bitmap(pixels:[PixelData], width: Int, height: Int)-> UIImage 
    let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo:CGBitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedFirst.rawValue)
    let bitsPerComponent:Int = 8
    let bitsPerPixel:Int = 32

    assert(pixels.count == Int(width * height))

    var data = pixels // Copy to mutable []
    let providerRef = CGDataProviderCreateWithCFData(
        NSData(bytes: &data, length: data.count * sizeof(PixelData))
    )

    let cgim = CGImageCreate(
        width,
        height,
        bitsPerComponent,
        bitsPerPixel,
        width * Int(sizeof(PixelData)),
        rgbColorSpace,
        bitmapInfo,
        providerRef,
        nil,
        true,
        kCGRenderingIntentDefault
    )
    return UIImage(CGImage: cgim)!

关于如何将 rgb 数组正确转换为 UIImage 的任何提示或指针?

【问题讨论】:

【参考方案1】:

注意:这是 ios 创建UIImage 的解决方案。有关 macOS 和 NSImage 的解决方案,请参阅 this answer。

您唯一的问题是PixelData 结构中的数据类型必须是UInt8。我在 Playground 中使用以下内容创建了一个测试图像:

public struct PixelData 
    var a: UInt8
    var r: UInt8
    var g: UInt8
    var b: UInt8


var pixels = [PixelData]()

let red = PixelData(a: 255, r: 255, g: 0, b: 0)
let green = PixelData(a: 255, r: 0, g: 255, b: 0)
let blue = PixelData(a: 255, r: 0, g: 0, b: 255)

for _ in 1...300 
    pixels.append(red)

for _ in 1...300 
    pixels.append(green)

for _ in 1...300 
    pixels.append(blue)


let image = imageFromARGB32Bitmap(pixels: pixels, width: 30, height: 30)

Swift 4 更新:

我更新了 imageFromARGB32Bitmap 以使用 Swift 4。该函数现在返回一个 UIImage?,如果出现任何问题,guard 用于返回 nil

func imageFromARGB32Bitmap(pixels: [PixelData], width: Int, height: Int) -> UIImage? 
    guard width > 0 && height > 0 else  return nil 
    guard pixels.count == width * height else  return nil 

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

    var data = pixels // Copy to mutable []
    guard let providerRef = CGDataProvider(data: NSData(bytes: &data,
                            length: data.count * MemoryLayout<PixelData>.size)
        )
        else  return nil 

    guard let cgim = CGImage(
        width: width,
        height: height,
        bitsPerComponent: bitsPerComponent,
        bitsPerPixel: bitsPerPixel,
        bytesPerRow: width * MemoryLayout<PixelData>.size,
        space: rgbColorSpace,
        bitmapInfo: bitmapInfo,
        provider: providerRef,
        decode: nil,
        shouldInterpolate: true,
        intent: .defaultIntent
        )
        else  return nil 

    return UIImage(cgImage: cgim)


使其成为 UIImage 的便捷初始化器:

这个函数可以很好地作为UIImageconvenience 初始化器。这是实现:

extension UIImage 
    convenience init?(pixels: [PixelData], width: Int, height: Int) 
        guard width > 0 && height > 0, pixels.count == width * height else  return nil 
        var data = pixels
        guard let providerRef = CGDataProvider(data: Data(bytes: &data, count: data.count * MemoryLayout<PixelData>.size) as CFData)
            else  return nil 
        guard let cgim = CGImage(
            width: width,
            height: height,
            bitsPerComponent: 8,
            bitsPerPixel: 32,
            bytesPerRow: width * MemoryLayout<PixelData>.size,
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue),
            provider: providerRef,
            decode: nil,
            shouldInterpolate: true,
            intent: .defaultIntent)
        else  return nil 
        self.init(cgImage: cgim)
    

下面是它的用法示例:

// Generate a 500x500 image of randomly colored pixels

let height = 500
let width = 500

var pixels: [PixelData] = .init(repeating: .init(a: 0, r: 0, g: 0, b: 0), count: width * height)
for index in pixels.indices 
    pixels[index].a = 255
    pixels[index].r = .random(in: 0...255)
    pixels[index].g = .random(in: 0...255)
    pixels[index].b = .random(in: 0...255)

let image = UIImage(pixels: pixels, width: width, height: height)

【讨论】:

【参考方案2】:

Swift 3 更新

struct PixelData 
    var a: UInt8 = 0
    var r: UInt8 = 0
    var g: UInt8 = 0
    var b: UInt8 = 0


func imageFromBitmap(pixels: [PixelData], width: Int, height: Int) -> UIImage? 
    assert(width > 0)

    assert(height > 0)

    let pixelDataSize = MemoryLayout<PixelData>.size
    assert(pixelDataSize == 4)

    assert(pixels.count == Int(width * height))

    let data: Data = pixels.withUnsafeBufferPointer 
        return Data(buffer: $0)
    

    let cfdata = NSData(data: data) as CFData
    let provider: CGDataProvider! = CGDataProvider(data: cfdata)
    if provider == nil 
        print("CGDataProvider is not supposed to be nil")
        return nil
    
    let cgimage: CGImage! = CGImage(
        width: width,
        height: height,
        bitsPerComponent: 8,
        bitsPerPixel: 32,
        bytesPerRow: width * pixelDataSize,
        space: CGColorSpaceCreateDeviceRGB(),
        bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue),
        provider: provider,
        decode: nil,
        shouldInterpolate: true,
        intent: .defaultIntent
    )
    if cgimage == nil 
        print("CGImage is not supposed to be nil")
        return nil
    
    return UIImage(cgImage: cgimage)

【讨论】:

这确实很好,但有点啰嗦。我建议将断言更改为简单的 guard 语句,并使用 guard let 而不是 ifing 来表示 nils。在我的实现中,我还使用了UInt 来防止负宽度和高度。

以上是关于Swift中的像素数组到UIImage的主要内容,如果未能解决你的问题,请参考以下文章

从像素数组问题中获取 NSImage (Swift)

int像素数组到java中的bmp

如何在 Swift 中将数值数据数组转换为 RAW 图像数据?

swift获取图片像素颜色值

如何在 Swift iOS 中读取和记录图像的原始像素

swift 将闭包映射到swift中的数组