6

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.

Pochi
  • 13,391
  • 3
  • 64
  • 104

4 Answers4

8

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.

3

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;
}
magma
  • 8,432
  • 1
  • 35
  • 33
0

You can not create context with 24-bytes (8 bits * 3 components) per pixel. See cgbitmapcontextcreate-with-kcgimagealphanone. One option is to create CV_8UC4 matrix and then convert it to CV_8UC3 one with cvtColor. If you need code sample, look into cant-make-opencv-detect-people-on-ios.

Community
  • 1
  • 1
fspirit
  • 2,537
  • 2
  • 19
  • 27
0

You are correctly creating a 3-channel image by using CV_8UC3. Since CGBitmapContextCreate expects 9792 bytes/row, that means there should be 3264 columns (of 3-channel pixels). If I run the following code,

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

it outputs 9792 as is required by CGBitmapContextCreate. Can you tell what cvMat.step[0] is returning in your code? It looks correct, but maybe something else is going on.

Also, note OpenCV natively stores data in BGR format if you're getting the image data from OpenCV functions like imread etc. So, if the color looks weird, pay attention to that.

mevatron
  • 13,911
  • 4
  • 55
  • 72
  • cols: 2448.000000, rows: 3264.000000, 7344, But i dont understand, this function works for my other formats. This image is from the iphone. – Pochi Apr 01 '12 at 18:19
  • Something fishy is going on here... The step of 7344 is exactly 2448.0 * 3. So, it looks like your columns and rows are getting swapped somewhere. Although, it doesn't look like it from your code. Is something like screen tilt occurring causing the dimensions to transpose? – mevatron Apr 02 '12 at 13:21
  • Hello, I am applying a resize to reduce computation time (since the original picture has a really big resolution), could this be the problem? cv::resize(noMask, noMask, cv::Size(320,240)); – Pochi Apr 02 '12 at 14:01
  • no nevermind, that is not the problem, i tried commenting the resize and there was no change. – Pochi Apr 02 '12 at 14:04