32

我有一个 UIImage,它是从 iPhone 相机捕获的图片,现在我希望将 UIImage 转换为 cv::Mat (OpenCV)。我正在使用以下代码行来完成此操作:

-(cv::Mat)CVMat
{

CGColorSpaceRef colorSpace = CGImageGetColorSpace(self.CGImage);
CGFloat cols = self.size.width;
CGFloat rows = self.size.height;

cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data
                                                cols,                      // Width of bitmap
                                                rows,                     // Height of bitmap
                                                8,                          // Bits per component
                                                cvMat.step[0],              // Bytes per row
                                                colorSpace,                 // Colorspace
                                                kCGImageAlphaNoneSkipLast |
                                                kCGBitmapByteOrderDefault); // Bitmap info flags

CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), self.CGImage);
CGContextRelease(contextRef);

return cvMat;
}

此代码适用于横向模式下的 UIImage,但是当我对从纵向模式拍摄的图像使用相同的代码时,图像会向右旋转 90 度。

我是 iOS 和 Objective C 的新手,因此我无法找出问题所在。

有人可以告诉我代码有什么问题还是我错过了什么。

4

3 回答 3

45

这将是因为UIImage它实际上不是肖像。使用 iPhone 相机拍摄的所有照片在原始位图状态下都是横向的,例如 3264 宽 x 2488 高。通过图像中设置的方向 EXIF 标志显示“人像”照片,例如,照片库应用程序根据该标志和相机的查看方向旋转图像,这很荣幸。

该标志还影响UIImage报告其宽度和高度属性的方式,将它们从标记为肖像的图像的位图值中转置。

cv::Mat不理会这些。这意味着(i)在转换为cv::Mat纵向图像时,其size.widthsize.height值将被转置,并且(ii)在从您那里转换回来时,cv::Mat将丢失方向标志。

UIImage从到处理此问题的最简单方法cv::Mat是在图像被标记为纵向时交换宽度和高度值:

if  (self.imageOrientation == UIImageOrientationLeft
      || self.imageOrientation == UIImageOrientationRight) {
         cols = self.size.height;
         rows = self.size.width;
    }

从 转换回 时cv::MatUIImage您将需要恢复方向标志。假设您的cv::Mat -> UIImage代码包含以下内容:

self = [self initWithCGImage:imageRef];

您可以改用此方法,并根据原件重置方向。

self = [self initWithCGImage:imageRef scale:1 orientation:orientation];
于 2013-01-15T10:52:55.050 回答
15

您应该考虑使用本机 OpenCV 函数来回转换:

#import <opencv2/imgcodecs/ios.h>
...
UIImage* MatToUIImage(const cv::Mat& image);
void UIImageToMat(const UIImage* image,
                         cv::Mat& m, bool alphaExist = false);

注意:如果您的 UIImage 来自相机,则应在转换为之前对其进行“标准化”(上传后的 iOS UIImagePickerController 结果图像方向),cv::Mat因为 OpenCV 不考虑 Exif 数据。如果你不这样做,结果应该是错误的。

于 2017-03-08T10:05:25.347 回答
1

我试图将肖像转换UIImage回来Mat,但我对这篇文章的两种方法都有问题。

  • MatToUIImage带有并UIImageToMat导致旋转图像的本机方法。
  • 自定义方法CGContext最终导致了一个奇怪的颜色错误(不仅是 RGB / BGR 交换)。红色是红色,蓝色是粉红色,绿色是黄色。

我最终使用了OpenCV带有旋转的原生:

#import <opencv2/imgcodecs/ios.h>

- (cv::Mat)convertUIImageToCVMat:(UIImage *)image
{
    cv::Mat imageMat;
    UIImageToMat(image, imageMat);
    cv::rotate(imageMat, imageMat, cv::ROTATE_90_CLOCKWISE);
    return imageMat;
}

- (UIImage *)convertCVMatToUIImage:(cv::Mat)cvMat
{
    UIImage *image = MatToUIImage(cvMat);
    return image;
}
于 2021-11-08T14:06:35.283 回答