OpenCV中Mat与UIImage互转后图像出现黑色背景如何修复
黑背景异常原因
UIImage与cv::Mat双向转换后出现黑背景,核心是三个配置错误:
- 通道配置不匹配:UIImage转Mat时创建的是4通道
CV_8UC4格式Mat,上下文用了kCGImageAlphaNoneSkipLast标志跳过alpha通道,但反向转换时用了kCGImageAlphaNone标志,和4通道像素布局不匹配,透明区域、未初始化通道值会被解析为纯黑。 - 尺寸计算错误:直接取
image.size的宽高计算Mat尺寸,没有乘以UIImage的scale缩放因子,Retina屏幕下逻辑尺寸和实际像素尺寸不一致,创建的Mat尺寸小于实际图像,未填充的区域默认值为0,显示为黑色。 - 坐标系不匹配:CoreGraphics坐标系原点在左下角,UIKit坐标系原点在左上角,直接调用
CGContextDrawImage会导致图像翻转,部分像素读取错位也会引发黑块。
修复后的代码
1. UIImage 转 cv::Mat 修正实现
- (cv::Mat)cvMatFromUIImage:(UIImage *)image { // 适配Retina屏缩放因子,计算真实像素尺寸 CGFloat scale = image.scale; CGFloat cols = image.size.width * scale; CGFloat rows = image.size.height * scale; CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage); cv::Mat cvMat(rows, cols, CV_8UC4); CGContextRef contextRef = CGBitmapContextCreate( cvMat.data, cols, rows, 8, cvMat.step[0], colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault ); // 翻转坐标系适配CoreGraphics原点规则 CGContextTranslateCTM(contextRef, 0, rows); CGContextScaleCTM(contextRef, 1.0, -1.0); CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage); CGContextRelease(contextRef); CGColorSpaceRelease(colorSpace); // OpenCV默认使用BGR通道顺序,转换为适配OpenCV处理的通道排列 cv::cvtColor(cvMat, cvMat, cv::COLOR_RGBA2BGRA); return cvMat; }
2. cv::Mat 转 UIImage 修正实现
- (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat { cv::Mat mat; if (cvMat.elemSize() == 1) { mat = cvMat.clone(); } else { // 转回iOS系统使用的RGBA通道顺序 cv::cvtColor(cvMat, mat, cv::COLOR_BGRA2RGBA); } NSData *data = [NSData dataWithBytes:mat.data length:mat.elemSize()*mat.total()]; CGColorSpaceRef colorSpace; if (mat.elemSize() == 1) { colorSpace = CGColorSpaceCreateDeviceGray(); } else { colorSpace = CGColorSpaceCreateDeviceRGB(); } CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); // 对齐转Mat时的alpha通道配置 CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; if (mat.elemSize() == 4) { bitmapInfo |= kCGImageAlphaPremultipliedLast; } else { bitmapInfo |= kCGImageAlphaNone; } CGImageRef imageRef = CGImageCreate( mat.cols, mat.rows, 8, 8 * mat.elemSize(), mat.step[0], colorSpace, bitmapInfo, provider, NULL, false, kCGRenderingIntentDefault ); // 传入屏幕缩放因子,保证Retina屏显示尺寸正常 UIImage *finalImage = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]; CGImageRelease(imageRef); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpace); return finalImage; }
3. 调用示例
原有业务逻辑无需修改,直接调用修正后的转换方法即可:
- (UIImage *)stroked:(UIImage *)image color:(int)color strock:(float)strock { cv::Mat img = [self cvMatFromUIImage:image]; // 此处插入OpenCV图像处理逻辑 return [self UIImageFromCVMat:img]; }
额外注意点
- 如果不需要处理透明通道,可以把Mat格式改为
CV_8UC3,对应调整bitmap标志为kCGImageAlphaNone,通道转换改为COLOR_RGB2BGR/COLOR_BGR2RGB,可以减少内存占用提升处理性能。 - OpenCV默认通道顺序为BGR,和iOS系统常用的RGB顺序不同,做颜色阈值、通道拆分、色彩空间转换操作时注意顺序匹配,避免颜色显示异常。
内容的提问来源于stack exchange,提问作者Евгений Чепелев
相关产品推荐
相关产品推荐

