![](/img/trans.png)
[英]Python OpenCV - imshow doesn't need convert from BGR to RGB
[英]Convert UIImage from BGR to RGB
正如标题所暗示的,我在一些 UIImage 颜色空间转换方面遇到了一些麻烦。 TL;DR 版本是我需要一种将 BGR 格式的 UIIMage 转换为 RGB 的方法。
这是我的应用程序中的事件流:
此时显示的 UIImage 为 BGR 格式。 我最好的猜测是第 4 步出了问题,因为在那之前图像是 RGB 格式的(我已经将它写入文件并进行了检查)。 我已将代码添加到下面的第 4 步,仅供参考。 我正在积极寻求更改 UIImage 客户端的颜色空间,但我不反对解决服务器端的问题。 任何一种解决方案都可以。
第 2 步:将 UIIMage 转换为 base64 字符串
let imageData: Data = UIImageJPEGRepresentation(map.image,0.95)!
let base64EnCodedStr: String = imageData.base64EncodedString()
第 3 步:将 base64 字符串转换为 PIL 图像
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 字符串
# 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')
如前所述,我可以在两个位置进行转换:服务器端或客户端。 感谢对这个问题的回答,我能够为这两种情况找到解决方案。
参考步骤 4 中的代码,将该函数中的第一行更改为以下内容:
# convert array to PIL Image
newImage = Image.fromarray( npArray[...,[2,1,0]] ) # swap color channels which converts BGR -> RGB
请参阅@dfd 的解决方案。 它写得很好,效果很好。 这是我在我的应用程序中测试过的稍微调整的版本(使用 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 来交换东西:
kernel vec4 swapRedAndGreenAmount(__sample s) {
return s.bgra;
}
这是使用它的 Swift 代码:
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!)
请注意以下几点:
UIImage
之前,我倾向于使用CIContext
创建一个CGImage
。 您可以删除此步骤并直接从CIImage
转到UIImage
。说明:
使用 CoreImage“Kernel”代码,在 iOS 11 之前,它只能是 GLSL 代码的一个子集,我编写了一个简单的CIColorKernel
,它采用像素的 RGB 值并将像素颜色作为 GRB 返回。
CIColorKernel
被优化为一次处理单个像素,而无法访问它周围的像素。 与此不同的是, CIWarpKernel
被优化为根据周围的像素“扭曲”一个像素。 这两个(或多或少)都是CIKernel
优化子类,在 iOS 11 和 Metal Performance Shaders 之前,它是您最接近在CoreImage
使用openGL
的CoreImage
。
最终编辑:
此解决方案的作用是使用 CoreImage 逐个交换像素的 RGB。 它很快,因为它使用 GPU,看似很快(因为模拟器不会在设备上为您提供接近实时性能的任何东西),而且很简单(因为它可以将事物从 RGB 转换为 BGR)。
执行此操作的实际代码很简单。 希望它可以作为那些想要使用 CoreImage 做更大的“幕后”事情的人的开始。
编辑(2021 年 2 月 25 日):
从 WWDC 2019 开始,Apple 弃用了GLKit
特别是GLKit
转而使用MetalKit
。 对于像这样的颜色内核,转换此代码相当简单。 不过,Warp 内核稍微复杂一些。
至于 Apple什么时候会“杀死”OpenGL 很难说。 我们都知道有一天UIKit
也会被弃用,但是(现在显示我的年龄)它可能不会在我有生之年。 天啊。
我认为没有办法使用CoreImage
或CoreGraphics
来做到这CoreImage
,因为 iOS 在创建自定义色彩空间方面没有给你太多余地。 但是,我从这篇文章中发现了一些可能有助于使用 OpenCV 的内容: https : //sriraghu.com/2017/06/04/computer-vision-in-ios-swiftopencv/ 。 它需要一点Objective-C,但有一个桥接头,一旦编写代码就会隐藏起来。
- 添加一个新文件 -> 'Cocoa Touch Class',将其命名为 'OpenCVWrapper' 并将语言设置为 Objective-C。 单击下一步并选择创建。 当提示创建桥接头时,单击“创建桥接头”按钮。 现在您可以观察到创建了 3 个文件名:OpenCVWrapper.h、OpenCVWrapper.m 和 -Bridging-Header.h。 打开“-Bridging-Header.h”并添加以下行:#import “OpenCVWrapper.h”
- 转到“OpenCVWrapper.h”文件并添加以下代码行:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface OpenCVWrapper: NSObject
+ (UIImage *) rgbImageFromBGRImage: (UIImage *) image;
@end
- 将 OpenCVWrapper.m 重命名为“OpenCVWrapper.mm”以支持 C++ 并添加以下代码:
#import "OpenCVWrapper.h"
// import necessary headers
#import <opencv2/core.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <opencv2/imgproc/imgproc.hpp>
using namespace cv;
@implementation OpenCVWrapper
+ (UIImage *) rgbImageFromBGRImage: (UIImage *) image {
// Convert UIImage to cv::Mat
Mat inputImage; UIImageToMat(image, inputImage);
// If input image has only one channel, then return image.
if (inputImage.channels() == 1) return image;
// Convert the default OpenCV's BGR format to RGB.
Mat outputImage; cvtColor(inputImage, outputImage, CV_BGR2RGB);
// Convert the BGR OpenCV Mat to UIImage and return it.
return MatToUIImage(outputImage);
}
@end
与链接文章的细微差别是他们将 BGR 转换为灰度,但我们将 BGR 转换为 RGB(好东西 OpenCV 有 很多转换! )。
现在有了这个 Objective-C 类的桥接头,您可以在 Swift 中使用OpenCVWrapper
:
// assume bgrImage is your image from the server
let rgbImage = OpenCVWrapper.rgbImage(fromBGR: bgrImage)
// double check the syntax on this ^ I'm not 100% sure how the bridging header will convert it
您可以使用底层 CGImage 以您想要的格式创建 CIImage。
func changeToRGBA8(image: UIImage) -> UIImage? {
guard let cgImage = image.cgImage,
let data = cgImage.dataProvider?.data else { return nil }
let flipped = CIImage(bitmapData: data as Data,
bytesPerRow: cgImage.bytesPerRow,
size: CGSize(width: cgImage.width, height: cgImage.height),
format: kCIFormatRGBA8,
colorSpace: cgImage.colorSpace)
return UIImage(ciImage: flipped)
}
唯一的问题是这仅在UIImage
是首先使用CGImage
创建的情况下才有效! 您也可以将其转换为CIImage
然后转换为CGImage
但同样适用,它仅在UIImage
是从CIImage
创建的情况下才有效。
如果我有更好的答案,我会探索并在此处发布解决此限制的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.