CGBitmapContextCreate for CV_8UC3(在 OpenCV 中使用)

Posted

技术标签:

【中文标题】CGBitmapContextCreate for CV_8UC3(在 OpenCV 中使用)【英文标题】:CGBitmapContextCreate for CV_8UC3 (to use in OpenCV) 【发布时间】:2012-03-30 08:56:07 【问题描述】:

我正在尝试使用 OpenCV 中的人员检测功能:

cv::HOGDescriptor hog;
hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());
std::vector<cv::Rect> found;
hog.detectMultiScale(noMask, found, 0.2, cv::Size(8,8), cv::Size(16,16), 1.05, 2);

但我得到以下断言:

OpenCV 错误:断言失败 (img.type() == CV_8U || img.type() == CV_8UC3) 在computeGradient,文件 /Users/robin/Projects/OpenCVForiPhone/opencv/opencv/modules/objdetect/src/hog.cpp, 第 174 行

这很有意义,因为我传递的是 CV_8UC4 图像。

所以我想我应该创建一个具有此特征的 cvmat。现在我有这两种方法。这让我可以得到灰色或彩色 cvmats (CV_8UC1/CV_8UC4)

颜色:

-(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;

对于灰度:

-(cv::Mat)CVGrayscaleMat

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

    cv::Mat cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channel

    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
                                                    kCGImageAlphaNone |
                                                    kCGBitmapByteOrderDefault); // Bitmap info flags

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

    return cvMat;

这是我的猜测,将其设为 3 个频道:

-(cv::Mat)CVMat3Channels


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

    cv::Mat cvMat(rows, cols, CV_8UC3); // 8 bits per component, 3 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);
    CGColorSpaceRelease(colorSpace);

    return cvMat;

但我收到以下错误:

<Error>: CGBitmapContextCreate: invalid data bytes/row: should be 
         at least 9792 for 8 integer bits/component, 3 components, 
         kCGImageAlphaNoneSkipLast. 
<Error>: CGContextDrawImage: invalid context 0x0

所以我的问题是,创建兼容 8UC3 的 CGBitmapContext 的正确方法是什么? (我假设 8UC3 表示每个像素 8 位,RGB 通道)

谢谢。

PD:图像转换代码来自 Robin Summerhill。

【问题讨论】:

【参考方案1】:

我使用了你的混合方式,但它不起作用:结果是RGB彩色图像,但失去了很多颜色。

所以我有一个很简单的方法可以转换,而且成功了

此代码在 XCode 中:

lastImage = [firstImage CVMat];
cv::cvtColor(lastImage , lastImage , CV_RGBA2RGB);

它将lastImage转换为RGB颜色,CV_8UC3风格。

抱歉,这是我第一次发表评论,我不知道如何格式化。

【讨论】:

这是一条救命命令,谢谢你,如果这是我的问题,我会选择这个作为正确答案【参考方案2】:

另一种方法可能是创建一个 CV_8UC4 矩阵,然后拆分通道,获得一个 bgr 矩阵和一个 alpha 矩阵(在这种情况下被丢弃):

cv::Mat CVMat(CGImageRef cgimage)


    CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgimage);
    CGFloat cols = CGImageGetWidth(cgimage);
    CGFloat rows = CGImageGetHeight(cgimage);

    cv::Mat rgba(rows, cols, CV_8UC4, Scalar(1,2,3,4)); // 8 bits per component, 4 channels

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

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), cgimage);
    CGContextRelease(contextRef);

    Mat bgr( rgba.rows, rgba.cols, CV_8UC3 );
    Mat alpha( rgba.rows, rgba.cols, CV_8UC1 );

    Mat out[] =  bgr, alpha ;
    // rgba[0] -> bgr[2], rgba[1] -> bgr[1],
    // rgba[2] -> bgr[0], rgba[3] -> alpha[0]
    int from_to[] =  0,2, 1,1, 2,0, 3,3 ;
    mixChannels( &rgba, 1, out, 2, from_to, 4 );

    return bgr;

【讨论】:

【参考方案3】:

您使用CV_8UC3 正确地创建了一个三通道图像。由于CGBitmapContextCreate 需要9792 字节/行,这意味着应该有3264 列(3 通道像素)。如果我运行以下代码,

int cols = 3264;
int rows = 1960; // assuming a ~1.66 aspect ratio here...
Mat temp(rows, cols,  CV_8UC3);
cout << temp.step[0] << endl;

它按照CGBitmapContextCreate 的要求输出9792。您能说出cvMat.step[0] 在您的代码中返回的内容吗?它看起来是正确的,但可能发生了其他事情。

另外,请注意,如果您从诸如 imread 等 OpenCV 函数获取图像数据,OpenCV 会以 BGR 格式存储数据。因此,如果颜色看起来很奇怪,请注意这一点。

【讨论】:

cols: 2448.000000, rows: 3264.000000, 7344, 但我不明白,这个函数适用于我的其他格式。此图片来自 iphone。 这里发生了一些可疑的事情...... 7344 的步长正好是 2448.0 * 3。所以,看起来你的列和行在某个地方被交换了。虽然,它看起来不像你的代码。是否会发生诸如屏幕倾斜之类的事情导致尺寸转置? 您好,我正在应用调整大小以减少计算时间(因为原始图片的分辨率非常高),这可能是问题吗? cv::resize(noMask, noMask, cv::Size(320,240)); 没关系,这不是问题,我尝试评论调整大小并没有变化。【参考方案4】:

您不能创建每像素 24 字节(8 位 * 3 个分量)的上下文。见cgbitmapcontextcreate-with-kcgimagealphanone。一种选择是创建 CV_8UC4 矩阵,然后使用 cvtColor 将其转换为 CV_8UC3 矩阵。如果您需要代码示例,请查看cant-make-opencv-detect-people-on-ios

【讨论】:

以上是关于CGBitmapContextCreate for CV_8UC3(在 OpenCV 中使用)的主要内容,如果未能解决你的问题,请参考以下文章

CGBitmapContextCreate 在 ios7下变化

CGBitmapContext为 RBG565 创建 bitsPerComponent

CGBitmapContextCreateImage 错误

iOS - 快速创建无符号字符

GBitmapContextCreate:不支持的参数组合

iPhone - UIImage imageWithData 返回 nil