如何在Swift中从字节数组创建位图图像

3

我有一个来自指纹传感器设备的字节数组,我想把它转换成位图。我尝试了几个例子,但是得到的都是空的UIImage。

如果有任何方法能够完成这个操作,请告诉我。

谢谢。

这是我的函数所做的:

func didFingerGrabDataReceived(data: [UInt8]) {
    if data[0] == 0 {
        let width1 = Int16(data[0]) << 8
        let finalWidth = Int(Int16(data[1]) | width1)

        let height1 = Int16(data[2]) << 8
        let finalHeight = Int(Int16(data[3]) | height1)

        var finalData:[UInt8] = [UInt8]()

// i dont want the first 8 bytes, so am removing it

        for i in 8 ..< data.count {
            finalData.append(data[i])
        }

     dispatch_async(dispatch_get_main_queue()) { () -> Void in

        let msgData = NSMutableData(bytes: finalData, length: finalData.count)

        let ptr = UnsafeMutablePointer<UInt8>(msgData.mutableBytes)


        let colorSpace = CGColorSpaceCreateDeviceGray()
        if colorSpace == nil {
            self.showToast("color space is nil")
            return
        }

        let bitmapContext = CGBitmapContextCreate(ptr, finalWidth, finalHeight, 8, 4 * finalWidth, colorSpace,  CGImageAlphaInfo.Only.rawValue);

        if bitmapContext == nil {
            self.showToast("context is nil")
            return
        }

        let cgImage=CGBitmapContextCreateImage(bitmapContext);
        if cgImage == nil {
            self.showToast("image is nil")
            return
        }

        let newimage = UIImage(CGImage: cgImage!)

            self.imageViewFinger.image = newimage
        }
}

我得到了一个扭曲的图像。有人能帮忙吗?

请重新查看我的问题并帮助我。 - Vinoth ios
顺便提一下,宽度和高度只占用4个字节,但是你跳过了前8个字节。我在下面的答案中保留了这一点,但我想确保这是你的实际意图,即在宽度和高度之后有四个字节被跳过... - Rob
1个回答

9
重要问题在于当你调用了 CGBitmapContextCreate 时,你指定了只构建一个 alpha 通道,而你的 data 缓冲区显然每个像素只使用了一个字节,但是对于“bytes per row”参数,你指定了 4 * width。它应该只是 width。通常情况下,在捕获每个像素四个字节(例如 RGBA)时才使用 4 倍,但由于你的缓冲区每个像素只使用一字节,所以应该去掉这个 4 倍因子。
就我个人而言,我还建议进行一系列其他改进,包括:
  • 唯一应该分派到主队列的事情是更新 UIKit 控件

  • 你可以废弃 finalData,因为你不需要从一个缓冲区复制到另一个缓冲区,而是可以直接构建 msgData

  • 你可能要完全绕过自己创建缓冲区这一步,并使用 nil 调用 CGBitmapContextCreate 来获取其自身的缓冲区,这样可以通过 CGBitmapContextGetData 获取它。如果你传递一个缓冲区,它会认为你将自己管理这个缓冲区,而我们在这里并没有这样做。

    如果你创建自己的缓冲区并且没有正确管理内存,你将经历难以重现的错误,使得看起来像是在相似的情况下它工作了,但突然间你会看到缓冲区因没有理由而损坏。通过让 Core Graphics 管理内存,可以避免这种问题。

  • 我可能会将将此字节缓冲区转换为 UIImage 与更新 UIImageView 分开处理。

因此大致上如下:
func mask(from data: [UInt8]) -> UIImage? {
    guard data.count >= 8 else {
        print("data too small")
        return nil
    }

    let width  = Int(data[1]) | Int(data[0]) << 8
    let height = Int(data[3]) | Int(data[2]) << 8

    let colorSpace = CGColorSpaceCreateDeviceGray()

    guard
        data.count >= width * height + 8,
        let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width, space: colorSpace, bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue),
        let buffer = context.data?.bindMemory(to: UInt8.self, capacity: width * height)
    else {
        return nil
    }

    for index in 0 ..< width * height {
        buffer[index] = data[index + 8]
    }

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

然后

if let image = mask(from: data) {
    DispatchQueue.main.async {
        self.imageViewFinger.image = image
    }
}

关于Swift 2版本,请参考此答案的先前版本


嘿嘿嘿嘿嘿嘿嘿:):):)它像魔法一样工作:)非常感谢Rob:)我不想要前8个字节,因为它包含宽度、高度和总字节数。所以我忽略了它。 - Vinoth ios

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接