将UIImage从BGR转换为RGB [英] Convert UIImage from BGR to RGB

查看:94
本文介绍了将UIImage从BGR转换为RGB的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正如标题所示,我在进行某些UIImage颜色空间转换时遇到了一些麻烦. TL; DR版本是我需要一种将BGR格式的UIIMage转换为RGB的方法.

As the title suggests, I'm having some trouble with some UIImage color space conversions. The TL;DR version is that I need a way to convert a UIIMage in BGR format to RGB.

这是我的应用程序中的事件流:

Here's the flow of events in my app:

  1. 应用:获取图片
  2. 应用程序:转换为base64并发送到服务器
  3. 服务器:将base64转换为要使用的图像
  4. 服务器:将图像转换回base64字符串
  5. 服务器:将base64字符串发送到应用
  6. 应用程序:将base64字符串转换为UIImage

服务器上测试图像的RGB版本

测试图像客户端的BGR版本

此时,显示的UIImage是BGR格式.我最好的猜测是第4步出了问题,因为直到那时图像都是RGB格式的(我已经将其写入文件并检查了).我已将代码添加到下面的第4步中,仅供参考.我正在积极寻求更改UIImage客户端的颜色空间,但我不反对解决服务器端的问题.两种解决方案都可以.

It's at this point that the UIImage displayed is in BGR format. My best guess is that something goes wrong at step 4, because up until then the image is in RGB format (I've written it to a file and checked). I've added the code to step 4 below just for reference. I'm actively looking to change the color space of the UIImage client-side, but I'm not opposed to fixing the issue server-side. Either solution would work.

第2步:将UIIMage转换为base64字符串

Step 2: Convert UIIMage to base64 string

  let imageData: Data = UIImageJPEGRepresentation(map.image,0.95)!
  let base64EnCodedStr: String = imageData.base64EncodedString()

第3步:将base64字符串转换为PIL映像

Step 3: Convert base64 String to a PIL Image

import io
import cv2
import base64 
import numpy as np
from PIL import Image

# Take in base64 string and return PIL image
def stringToImage(base64_string):
    imgdata = base64.b64decode(base64_string)
    return Image.open(io.BytesIO(imgdata))

第4步:将图像(numpy数组)转换回base64字符串

Step 4: Convert Image (numpy array) back to a base64 string

# Convert a numpyArray into a base64 string in JPEG format
def imageToString(npArray):

    # convert array to PIL Image
    newImage = Image.fromarray(npArray.astype('uint8'), 'RGB')

    # convert to JPEG format
    file = io.BytesIO()
    newImage.save(file, format="JPEG")

    # reset file pointer to start
    file.seek(0)
    img_bytes = file.read()

    # encode data
    encodedData = base64.b64encode(img_bytes)

    return encodedData.decode('ascii')

如前所述,我可以在两个位置进行转换:服务器端或客户端.多亏了对这个问题的回答,我才能够找到这两种情况的解决方案.

As was mentioned earlier, there were two locations where I could do the conversions: Sever-side or client-side. Thanks to the responses to this question I was able to find solutions for both scenarios.

参考步骤4中的代码,将该函数的第一行更改为以下内容:

referring to the code in step 4, change the first line in that function to the following:

 # convert array to PIL Image
 newImage = Image.fromarray( npArray[...,[2,1,0]] ) # swap color channels which converts BGR -> RGB

解决方案2:客户端

请参阅@dfd的解决方案.它写的很好,效果很好.这是我在应用程序中测试过的略有改动的版本(使用swift 4).

Solution 2: Client-side

Refer to @dfd 's solution. It's well written and works wonderfully. Here's the slightly adapted version I've tested in my application (which uses swift 4).

let data =  NSData(base64Encoded: base64String, options: .ignoreUnknownCharacters)

let uiInput = UIImage(data: data! as Data)
let ciInput = CIImage(image: uiInput!)
let ctx = CIContext(options: nil)
let swapKernel = CIColorKernel( string:
    "kernel vec4 swapRedAndGreenAmount(__sample s) {" +
                 "return s.bgra;" +
     "}"
)
let ciOutput = swapKernel?.apply(withExtent: (ciInput?.extent)!, arguments: [ciInput as Any])
let cgImage = ctx.createCGImage(ciOutput!, from: (ciInput?.extent)!)
let rgbOutput = UIImage(cgImage: cgImage!)

推荐答案

这是一个非常简单的CIKernel,用于交换内容:

Here's a very simple CIKernel to swap things:

kernel vec4 swapRedAndGreenAmount(__sample s) {
    return s.bgra;
}

以下是使用它的Swift代码:

Here's the Swift code to use it:

let uiInput = UIImage(named: "myImage")
let ciInput = CIImage(image: uiInput!)
let ctx = CIContext(options: nil)
let swapKernel = CIColorKernel( source:
    "kernel vec4 swapRedAndGreenAmount(__sample s) {" +
        "return s.bgra;" +
    "}"
)
let ciOutput = swapKernel?.apply(extent: (ciInput?.extent)!, arguments: [ciInput as Any])
let cgImage = ctx.createCGImage(ciOutput!, from: (ciInput?.extent)!)
let uiOutput = UIImage(cgImage: cgImage!)

请注意以下几点:

  • 这将在运行 iOS 9或更高版本的设备上运行.
  • 第二,并且几乎同样重要,它使用CoreImage和GPU.因此,在模拟器上进行测试可能需要几秒钟的时间来渲染.但是在设备上将需要微秒.
  • 在以UIImage结尾之前,我倾向于使用CIContext创建CGImage.您 也许可以删除此步骤,然后直接从CIImage转到UIImage.
  • 请原先的换行/解包是从旧代码转换而来的.您可能可以做得更好.
  • This will work on devices running iOS 9 or later.
  • Second and almost as important, this uses CoreImage and the GPU. Thus, testing this on a simulator may take seconds to render. But on a device it will take microseconds.
  • I tend to use a CIContext to create a CGImage before ending up with a UIImage. You may be able to remove this step and go straight from a CIImage to a UIImage.
  • Excuse the wrapping/unwrapping, it's converted from old code. You can probably do a better job.

说明:

使用CoreImage内核"代码(直到iOS 11只能是GLSL代码的子集),我编写了一个简单的CIColorKernel,它使用像素的RGB值并将像素颜色返回为GRB.

Using CoreImage "Kernel" code, which until iOS 11 could only be a subset of GLSL code, I wrote a simple CIColorKernel that takes a pixel's RGB value and returns the pixel color as GRB.

A CIColorKernel已优化为一次可处理单个像素,而 no 不能访问其周围的像素.与此不同的是,CIWarpKernel已优化为根据其周围的像素扭曲"像素.这两个都是(或多或少)CIKernel的优化子类,在iOS 11和Metal Performance Shaders之前,这是您在CoreImage内部使用openGL最接近的子类.

A CIColorKernel is optimized to work on a single pixel at a time with no access to the pixels surrounding it. Unlike that, a CIWarpKernel is optimized to "warp" a pixel based on the pixels around it. Both of these are (more or less) optimized subclasses of a CIKernel, which - until iOS 11 and Metal Performance Shaders - is about the closest you get to using openGL inside of CoreImage.

最终

此解决方案的作用是使用CoreImage一次交换一个像素的RGB.它之所以快是因为它使用GPU的速度快得惊人(因为模拟器无法为您提供接近设备实时性能的任何东西),并且操作简单(因为它可以将RGB转换为BGR).

What this solution does is swap a pixel's RGB one-by-one using CoreImage. It's fast because it uses the GPU, deceptively fast (because the simulator does not give you anything close to real-time performance on a device), and simple (because it swaps things from RGB to BGR).

执行此操作的实际代码很简单.希望对于那些想使用CoreImage进行更大的幕后"工作的人来说,它是一个开始.

The actual code to do this is straightforward. Hopefully it works as a start for those who want to do much larger "under the hood" things using CoreImage.

这篇关于将UIImage从BGR转换为RGB的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆