CV_8UC3的CGBitmapContextCreate(在OpenCV中使用)

时间:2012-03-30 08:56:08

标签: objective-c ios opencv

我正在尝试在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。

4 个答案:

答案 0 :(得分:6)

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

所以我有一种非常简单的转换方式,而且它很成功

此代码位于XCode中:

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

它会将lastImage转换为RGB颜色,使用CV_8UC3样式。

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

答案 1 :(得分:3)

另一种方法可能是创建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;
}

答案 2 :(得分:0)

您正在使用CV_8UC3正确创建3通道图像。由于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;

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

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

答案 3 :(得分:0)

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