【问题标题】:Swift : Convert byte array into CIImageSwift:将字节数组转换为 CIImage
【发布时间】:2018-12-24 14:47:37
【问题描述】:

我正在寻找一种将字节数组转换为 CIIimage 的方法,以便将其输入 ML 模型进行分类。我正在使用 REST HTTP 服务器,我在其中向服务器发送一个 POST 请求,并将有效负载作为图像。服务器接收到的图像字节需要处理并转换为 MAC OS 的 CIImage 格式,以便可以将其输入到接受 VNImageRequestHandler(ciImage: <ciimage>) 类型请求的 ML 模型中。

有人可以举一个例子来快速做到这一点吗?

VNImageRequestHandler : NSObject

let data = Data(bytes) let imgHandler = VNImageRequestHandler(ciImage: data) 上述数据变量需要类型化为CIImage 类型。

在 HTTP 服务器端,我正在接收图像的字节,如下所示: imageData = request.body.bytes

【问题讨论】:

  • 你是否在使用 MNIST 数据库?
  • 不。 MNIST 有什么有趣的事情吗?
  • 从 REST 返回的字节类型是什么。它们是原始字节、基于 64 的编码还是什么?你能说得更详细些吗?
  • 如果是原始字节,则转换为 CGImage->CIImage。如果它们是 64base 编码的,则转换为 Data->NSImage->CIImage。
  • @CosmosMan,我只是使用 curl 将图像的字节发送到 REST 服务器,然后我试图将它们转换为 CIImage。因为我没有传递任何标题,所以返回的默认内容类型是application/x-www-form-urlencoded。即使我在发出 curl 请求时将标头设置为“Content-type: image/jpeg”,服务器接收到的字节也是相同的,但将其转换为 NSImage 会给我一个 0*0 的图像。

标签: swift macos rest http ciimage


【解决方案1】:

使用此方法将字节数组转换为 CGImage。您必须确保您的字节是 rgba 32 位像素原始字节。

func byteArrayToCGImage(raw: UnsafeMutablePointer<UInt8>, // Your byte array
                        w: Int, // your image's width
                        h: Int // your image's height
    ) -> CGImage! {

    // 4 bytes(rgba channels) for each pixel
    let bytesPerPixel: Int = 4
    // (8 bits per each channel)
    let bitsPerComponent: Int = 8

    let bitsPerPixel = bytesPerPixel * bitsPerComponent;
    // channels in each row (width)
    let bytesPerRow: Int = w * bytesPerPixel;

    let cfData = CFDataCreate(nil, raw, w * h * bytesPerPixel)
    let cgDataProvider = CGDataProvider.init(data: cfData!)!

    let deviceColorSpace = CGColorSpaceCreateDeviceRGB()

    let image: CGImage! = CGImage.init(width: w,
                                       height: h,
                                       bitsPerComponent: bitsPerComponent,
                                       bitsPerPixel: bitsPerPixel,
                                       bytesPerRow: bytesPerRow,
                                       space: deviceColorSpace,
                                       bitmapInfo: [],
                                       provider: cgDataProvider,
                                       decode: nil,
                                       shouldInterpolate: true,
                                       intent: CGColorRenderingIntent.defaultIntent)



    return image;
}

使用这个方法,你可以像这样转换成CIImage。

let cgimage = byteArrayToCGImage(raw: <#Pointer to Your byte array#> ,
                                 w: <#your image's width#>,
                                 h: <#your image's height#>)
if cgimage != nil {
    let ciImage = CIImage.init(cgImage: cgimage)
}

根据评论,您的数据可能是 RGB 原始字节而不是 RGBA。在这种情况下,您必须分配新的缓冲区,手动为每个 Alpha 通道放置 255 并将该缓冲区传递给方法。

更新了 32 位 RGB 到 32 位 RGBA 的转换

func convertTo32bitsRGBA(from32bitsRGB pointer: UnsafeMutablePointer<UInt8>!,
                         width: Int,
                         height: Int) -> UnsafeMutablePointer<UInt8> {

    let pixelCount = width * height
    let memorySize = pixelCount * 4
    let newBuffer = malloc(memorySize).bindMemory(to: UInt8.self, capacity: width * height)

    var i = 0;
    while(i < pixelCount) {
        let oldBufferIndex = i * 3;
        let newBufferIndex = i * 4;

        // red channel
        newBuffer.advanced(by: newBufferIndex).pointee = pointer.advanced(by: oldBufferIndex).pointee
        // green channel
        newBuffer.advanced(by: newBufferIndex + 1).pointee = pointer.advanced(by: oldBufferIndex + 1).pointee
        // blue channel
        newBuffer.advanced(by: newBufferIndex + 2).pointee = pointer.advanced(by: oldBufferIndex + 2).pointee
        // alpha channel
        newBuffer.advanced(by: newBufferIndex + 3).pointee = 0xff;

        // &+ is used for little performance gain
        i = i &+ 1;
    }


    return newBuffer;
}

您可以使用您的 rgb 图像缓冲区调用转换器方法,如下所示

let newImageBuffer = convertTo32bitsRGBA(from32bitsRGB: <#Your RGB image buffer#>,
                    width: <#Your image pixel row count or width#>,
                    height: <#Your image pixel column count or height#>)

但请记住,就像在 C、C++ 或 Objective-C 中一样,您有责任释放此方法返回的内存分配。这些是编译器不管理内存的指针。

你可以用简单的函数释放。

newImageBuffer.deallocate();

释放后,您不能访问释放的内存。如果你这样做,你会得到BAD_ACCESS_EXC(操作系统为访问你不拥有的内存而抛出的错误访问异常)。

【讨论】:

  • 非常感谢@CosmosMan。这或多或少我也是这样做的。您能否分享一个代码 sn-p 以将 RBB 字节数组快速转换为 RGBA?
  • 另外,如果我将 bytesPerPixel 设置为 3 并改用 RGB,是否需要将其转换为 RGBA ?
  • @psbits 我已经根据您的要求更新了答案。如果您处理大图像,我在此处给出的代码将受到性能下降的影响。如果您愿意,我可以为您提供使用 GCD(Grand Central Dispatch)并发编程增强的代码。
  • 谢谢@CosmosMan。当然。那会很好,但在此之前我的问题是,如果我使用 RGB(每像素 3 个字节)而不是 RGBA(每像素 4 个字节)有什么缺点?
  • @psbits 这只是一种表示。由于 JPG 没有 alpha 通道,它们可以用来表示相机输出(不需要 alpha 通道)。 PNG 确实有 alpha 通道,因此它可以用作应用程序中的图标或图像,可以与其他 UI 元素混合。但是 JPG 或 PNG 可能不会将这些信息表示为原始像素,而是压缩格式(我不确切知道它们是如何格式化的)。另一方面,位图使用原始字节以及其他一些属性(宽度、高度、每行字节数等),但与 JPG 和 PNG 相比,它们的大小很大,但处理速度更快
猜你喜欢
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
  • 2019-10-19
  • 1970-01-01
  • 2014-08-08
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
相关资源
最近更新 更多