如何逐像素创建图像

Posted

技术标签:

【中文标题】如何逐像素创建图像【英文标题】:How to create an image pixel by pixel 【发布时间】:2016-10-23 17:33:11 【问题描述】:

我想在 swift 3 中逐个像素地创建一个 UIImage

我已搜索但找不到实际有效的代码

所以让我解释一下,我有一个包含字符的数组

var array = ["w", "x", "y", "x", "y", "y", "y", "x", "x", "x", "w", "x", "y", "w", "y"] //there will be like 26 millions of those

如果是w,像素的颜色为蓝色

如果是x,像素的颜色为红色

如果是y,则像素的颜色为绿色

如果是v,则像素颜色为黑色

我想从这些角色中创建一个图像并将其存储在照片中

有什么想法吗??

感谢您的回答

【问题讨论】:

【参考方案1】:

您可以创建一个CGContext,然后检索该图像的data 缓冲区,然后用与您的字符串值对应的值填充该缓冲区:

func createImage(width: Int, height: Int, from array: [String], completionHandler: @escaping (UIImage?, String?) -> Void) 
    DispatchQueue.global(qos: .utility).async 
        let colorSpace       = CGColorSpaceCreateDeviceRGB()
        let bytesPerPixel    = 4
        let bitsPerComponent = 8
        let bytesPerRow      = bytesPerPixel * width
        let bitmapInfo       = RGBA32.bitmapInfo

        guard array.count == width * height else 
            completionHandler(nil, "Array size \(array.count) is incorrect given dimensions \(width) x \(height)")
            return
        

        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else 
            completionHandler(nil, "unable to create context")
            return
        

        guard let buffer = context.data else 
            completionHandler(nil, "unable to get context data")
            return
        

        let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)

        for (index, string) in array.enumerated() 
            switch string 
            case "w": pixelBuffer[index] = .blue
            case "x": pixelBuffer[index] = .red
            case "y": pixelBuffer[index] = .green
            case "v": pixelBuffer[index] = .black
            default: completionHandler(nil, "Unexpected value: \(string)"); return
            
        

        let cgImage = context.makeImage()!

        let image = UIImage(cgImage: cgImage)

        // or
        //
        // let image = UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: .up)

        completionHandler(image, nil)
    


如果有 2600 万像素,您可能希望将其设为异步以避免阻塞主队列。

顺便说一下,上面用的是这个struct

struct RGBA32: Equatable 
    private var color: UInt32

    var redComponent: UInt8 
        return UInt8((color >> 24) & 255)
    

    var greenComponent: UInt8 
        return UInt8((color >> 16) & 255)
    

    var blueComponent: UInt8 
        return UInt8((color >> 8) & 255)
    

    var alphaComponent: UInt8 
        return UInt8((color >> 0) & 255)
    

    init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) 
        color = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
    

    static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue

    static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool 
        return lhs.color == rhs.color
    

    static let black = RGBA32(red: 0, green: 0, blue: 0, alpha: 255)
    static let red   = RGBA32(red: 255, green: 0, blue: 0, alpha: 255)
    static let green = RGBA32(red: 0, green: 255, blue: 0, alpha: 255)
    static let blue  = RGBA32(red: 0, green: 0, blue: 255, alpha: 255)

要保存图像,您可以:

createImage(width: width, height: height, from: array)  image, errorMessage in
    guard let image = image, errorMessage == nil else 
        print(errorMessage!)
        return
    

    DispatchQueue.main.async 
        self.imageView.image = image
        UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
    

在哪里

func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: Any?) 
    guard error == nil else 
        print(error!.localizedDescription)
        return
    

    print("image saved")

【讨论】:

感谢 Rob,但我有最后一个问题,我创建了图像,但我无法存储它,因为:“此应用程序已崩溃,因为它试图在没有使用说明的情况下访问隐私敏感数据。应用程序的Info.plist 必须包含一个 NSPhotoLibraryUsageDescription 键和一个字符串值,向用户解释应用程序如何使用这些数据。" @l.b.dev 是的,这个错误说明了一切:您必须将 NSPhotoLibraryUsageDescription 键添加到您的 info.plist 并提供一条消息,该消息将显示在对话框中,询问是否允许照片库。 只是为了确定你知道如果我从照片库中取回图片,并尝试找出数组,我会得到相同的数组还是会不同(bc图像是也许压缩)? 我不认为你有任何这样的保证。我不会怀疑它会,但是UIImageWriteToSavedPhotosAlbum 的文档不能保证它不会使用 JPG 压缩(PNG 压缩本来可以的)。至少,当您以编程方式检索图像时,您会想要检索原始资产(请参阅***.com/a/32938728/1271826)。就个人而言,我会确保将图像保存为 PNG,然后使用 Photos 框架保存它,或者自己将其保存在持久存储中。 @l.b.dev - 坦率地说,与图像相关的变量太多(例如颜色空间、压缩等),我会犹豫是否要依靠它来检索底层 w/x /y/v 数据。所以,我只是将我的模型保存为 plist 或 keyed archiver,并避免尝试使用图像来捕获底层数据。

以上是关于如何逐像素创建图像的主要内容,如果未能解决你的问题,请参考以下文章

渐进式图像渲染,逐像素加载图像并在 imageLoader 中更新图像视图

逐像素复制ipl图像

SLAM建图

opencv C++ 三重for循环遍历RGB图像像素(逐像素操作操作像素值遍历像素遍历)

Python和OpenCV创建超快的“for”像素循环

iOS:从相机获取逐像素数据