CGBitmapContextCreate for CV_8UC3 (for use in OpenCV)

I am trying to use the people detection feature 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 statement:

OpenCV error: approval 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 that makes sense because I am transmitting the CV_8UC4 image.

So, I figured that I should create cvmat with such characteristics. Now I have these 2 methods. which allow me to get gray or color cvmats (CV_8UC1 / CV_8UC4)

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 shades of gray:

 -(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 do this 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 to create an 8GB compatible CGBitmapContext? (I assume 8UC3 means 8 bits per pixel with RGB channels)

Thanks.

PD: Image conversion code belongs to Robin Summerhill.

+6
source share
4 answers

You correctly create a three-channel image using CV_8UC3 . Since CGBitmapContextCreate expects 9792 bytes / row, this means there must be 3264 columns (three-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; 

outputs 9792 as required by CGBitmapContextCreate . Can you say that cvMat.step[0] returning to your code? It looks right, but maybe something else is happening.

Also note that OpenCV initially saves data in BGR format if you get image data from OpenCV functions such as imread , etc. Therefore, if the color looks strange, pay attention to this.

0
source

I used your mix, but it doesn’t work: the result is an RGB color image, but many colors are lost.

So, I have a very easy way to convert, and it successfully

This code is in Xcode:

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

It converts lastImage to RGB color using CV_8UC3 style.

Sorry because this is the first time I am commenting and I do not know how to format this.

+6
source

Another approach could be to create the CV_8UC4 matrix, and then split the channels, get the bgr matrix and alpha matrix (in this case, discarded):

 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
source

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

0
source

Source: https://habr.com/ru/post/912032/


All Articles