Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CGBitmapContextCreate for CV_8UC3 (to use in OpenCV)

I am trying to use the people detection function in 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);

But I get the following assertion:

OpenCV Error: Assertion failed (img.type() == CV_8U || img.type() == CV_8UC3) in computeGradient, file /Users/robin/Projects/OpenCVForiPhone/opencv/opencv/modules/objdetect/src/hog.cpp, line 174

And it makes sense because I am passing a CV_8UC4 image.

So I guessed I should create a cvmat with this characteristics. Right now I have these 2 methods. which allow me to get gray or color cvmats (CV_8UC1/CV_8UC4)

For Color:

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

For Grayscale:

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

And this is my guess to make it 3 channels:

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

But I get the following error:

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

So my question is, What is the correct way of creating a 8UC3 compatible CGBitmapContext? (I assume the 8UC3 means 8 bits per pixel with RGB channels)

Thank you.

PD: The image transformation code is from Robin Summerhill.

like image 487
Pochi Avatar asked Mar 30 '12 08:03

Pochi


2 Answers

I used your mix way, but it's not work: the result is RGB color image, but losing many colors.

So I have a very simple way to convert, and it's successful

This code is in XCode:

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

It will convert lastImage to RGB color, with CV_8UC3 style.

Sorry because it's the first time I comment and I don't know how to format this.

like image 163
Bui Quang Tien Avatar answered Sep 21 '22 11:09

Bui Quang Tien


A different approach might be creating a CV_8UC4 matrix and then splitting the channels, obtaining a bgr matrix, and an alpha matrix (discarded in this case):

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;
}
like image 40
magma Avatar answered Sep 19 '22 11:09

magma