【问题标题】:How to reconstruct grayscale image from intensity values?如何从强度值重建灰度图像?
【发布时间】:2016-01-08 12:26:26
【问题描述】:

通常需要从图像中获取像素数据或从像素数据中重建该图像。如何拍摄图像,将其转换为像素值数组,然后使用Swift 中的像素数组使用CoreGraphics 重建它?

这个问题的答案质量到处都是,所以我想要一个规范的答案。

【问题讨论】:

    标签: swift uikit core-graphics


    【解决方案1】:

    以数组形式获取像素值

    这个功能可以很容易地扩展到彩色图像。为简单起见,我使用的是灰度,但我已经评论了获取 RGB 的更改。

    func pixelValuesFromImage(imageRef: CGImage?) -> (pixelValues: [UInt8]?, width: Int, height: Int)
    {
        var width = 0
        var height = 0
        var pixelValues: [UInt8]?
        if let imageRef = imageRef {
            let totalBytes = imageRef.width * imageRef.height
            let colorSpace = CGColorSpaceCreateDeviceGray()
            
            pixelValues = [UInt8](repeating: 0, count: totalBytes)
            pixelValues?.withUnsafeMutableBytes({
                width = imageRef.width
                height = imageRef.height
                let contextRef = CGContext(data: $0.baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width, space: colorSpace, bitmapInfo: 0)
                let drawRect = CGRect(x: 0.0, y:0.0, width: CGFloat(width), height: CGFloat(height))
                contextRef?.draw(imageRef, in: drawRect)
            })
        }
    
        return (pixelValues, width, height)
    }
    

    从像素值获取图像

    我将图像(在本例中为每像素 8 位灰度)重建为 CGImage

    func imageFromPixelValues(pixelValues: [UInt8]?, width: Int, height: Int) ->  CGImage?
    {
        var imageRef: CGImage?
        if let pixelValues = pixelValues {
            let bitsPerComponent = 8
            let bytesPerPixel = 1
            let bitsPerPixel = bytesPerPixel * bitsPerComponent
            let bytesPerRow = bytesPerPixel * width
            let totalBytes = width * height
            let unusedCallback: CGDataProviderReleaseDataCallback = { optionalPointer, pointer, valueInt in }
            let providerRef = CGDataProvider(dataInfo: nil, data: pixelValues, size: totalBytes, releaseData: unusedCallback)
    
            let bitmapInfo: CGBitmapInfo = [CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), CGBitmapInfo(rawValue: CGImageByteOrderInfo.orderDefault.rawValue)]
            imageRef = CGImage(width: width,
                               height: height,
                               bitsPerComponent: bitsPerComponent,
                               bitsPerPixel: bitsPerPixel,
                               bytesPerRow: bytesPerRow,
                               space: CGColorSpaceCreateDeviceGray(),
                               bitmapInfo: bitmapInfo,
                               provider: providerRef!,
                               decode: nil,
                               shouldInterpolate: false,
                               intent: .defaultIntent)
        }
    
        return imageRef
    }
        
    

    在 Playground 中演示代码

    您需要将图像复制到 Playground 的 Resources 文件夹中,然后更改下面的文件名和扩展名以匹配。最后一行的结果是从 CGImage 构造的 UIImage。

    import Foundation
    import CoreGraphics
    import UIKit
    import PlaygroundSupport
    
    let URL = playgroundSharedDataDirectory.appendingPathComponent("zebra.jpg")
    print("URL \(URL)")
    
    var image: UIImage? = nil
    if FileManager().fileExists(atPath: URL.path) {
        do {
            try NSData(contentsOf: URL, options: .mappedIfSafe)
        } catch let error as NSError {
            print ("Error: \(error.localizedDescription)")
        }
        image = UIImage(contentsOfFile: URL.path)
    } else {
        print("File not found")
    }
    
    let (intensityValues, width, height) = pixelValuesFromImage(imageRef: image?.cgImage)
    let roundTrippedImage = imageFromPixelValues(pixelValues: intensityValues, width: width, height: height)
    let zebra = UIImage(cgImage: roundTrippedImage!)
    

    【讨论】:

    • image 在第一个函数中未定义,无法编译。为什么不使用可选绑定而不是强制展开(比较stackoverflow.com/questions/29717210/…)? alloc() 也可能失败。
    • @MartinR 我做了一些清理以避免显式的 nil 检查
    【解决方案2】:

    我无法让上面的 Cameron 代码正常工作,所以我想测试另一种方法。我找到了Vacawama's code,它依赖于 ARGB 像素。您可以使用该解决方案并通过简单地映射每个值将每个灰度值转换为 ARGB 值:

    /// Assuming grayscale pixels contains floats in the range 0...1
    let grayscalePixels: [Float] = ...
    let pixels = grayscalePixels.map { 
      let intensity = UInt8(round($0 / Float(UInt8.max)))
      return PixelData(a: UInt8.max, r: intensity, g: intensity, b: intensity)
    }
    let image = UIImage(pixels: pixels, width: width, height: height)
    

    【讨论】:

    • 为当前的 Swift 更新 - 原始代码的目的是处理可能在 Matlab 数据中找到的黑白图像的原始强度值,并且绝对不打算使用 ARGB 像素数据。此外,像素数据表示很复杂,处理严格的强度值并允许 CoreGraphics 管理复杂性绝对是一个明智的选择。
    猜你喜欢
    • 2014-05-30
    • 1970-01-01
    • 1970-01-01
    • 2015-05-30
    • 2015-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多