OpenCV using cvImageCreate() with grayscale image fails, and resizing usually fails

匿名 (未验证) 提交于 2019-12-03 09:52:54

问题:

I have such code that is loading grayscale image from buffer 1byte, 8bits bitmap. Then it resizes this image.

int resizeBitmap(const unsigned char *inData, const size_t inDataLength, const size_t inWidth, const size_t inHeight,                  const int bitDepth, const int noOfChannels, unsigned char **outData, size_t *outDataLength, const size_t outWidth, const size_t outHeight) {      // create input image     IplImage *inImage = cvCreateImage(cvSize(inWidth, inHeight), bitDepth, noOfChannels);     cvSetData(inImage, inData, inImage->widthStep);        // show input image         cvNamedWindow("OpenCV Input Image", CV_WINDOW_FREERATIO);         cvShowImage("OpenCV Input Image", inImage);         cvWaitKey(0);         cvDestroyWindow("OpenCV Input Image");     /* */      // create output image     IplImage *outImage = cvCreateImage(cvSize(outWidth, outHeight), inImage->depth, inImage->nChannels);      // select interpolation type     double scaleFactor = (((double) outWidth)/inWidth + ((double) outHeight)/inHeight)/2;     int interpolation = (scaleFactor > 1.0) ? CV_INTER_LINEAR : CV_INTER_AREA;      // resize from input image to output image     cvResize(inImage, outImage, interpolation);      /*  // show output image         cvNamedWindow("OpenCV Output Image", CV_WINDOW_FREERATIO);         cvShowImage("OpenCV Output Image", outImage);         cvWaitKey(0);         cvDestroyWindow("OpenCV Output Image");     */      // get raw data from output image     int step = 0;     CvSize size;     cvGetRawData(outImage, outData, &step, &size);     *outDataLength = step*size.height;      cvReleaseImage(&inImage);     cvReleaseImage(&outImage);      return 0; } 

I am using here bitDepth = 8 and noOfChannels = 1. Loaded image is:

and the output is:

this output is not always written as program usually fails with error:

OpenCV Error: Bad number of channels (Source image must have 1, 3 or 4 channels) in cvConvertImage, file /tmp/opencv-20160915-26910-go28a5/opencv-2.4.13/modules/highgui/src/utils.cpp, line 611 libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /tmp/opencv-20160915-26910-go28a5/opencv-2.4.13/modules/highgui/src/utils.cpp:611: error: (-15) Source image must have 1, 3 or 4 channels in function cvConvertImage 

I am attaching debugger output as there is interesting situation as I am passing grayscale buffer of size 528480 which equals 1 byte * 1101 *480, but after cvCreateImage there is inside imageSize 529920 and widthStep is 1104! Maybe here is the problem with this image, but why it is ?

回答1:

This issue is related to widthstep and width of IplImage. Opencv pads the image to have a widthstep of multiple of 4 bytes. Here opencv is using width of 1101 and widthstep of 1104. But data when written from bitmap to IplImage, few extra pixels get written per row(note the diagonal line from top-left to bottom-right).

Note, that the image is not tilted. It's just that every next row is shifted a little to left(by 3 pixels), thus giving the idea of shearing transformation.

It could also be possible that you are giving a smaller width than what Bitmap holds.

See docs here and search for padding. You can try copying all column data row-wise.

Why crash: Sometimes opencv will end up reading beyond Bitmap buffer and may hit untouchable memory addresses, causing exception.

Note: Bitmap probably also has padding from which you received the black diagonal line.



回答2:

Based on answer saurabheights I have wrote procedure to make padding of each bitmap row to any given multiplicity of bytes in the row.

int padBitmap(const unsigned char *data, const size_t dataLength, const size_t width, const size_t height,               const int bitDepth, const int noOfChannels, unsigned char **paddedData, size_t *paddedDataLength, const size_t row_multiple) {       size_t row_length = (width*noOfChannels*bitDepth)/CHAR_BIT;     size_t row_padding_size = row_multiple - row_length % row_multiple;      if(row_padding_size == 0) return 0;      size_t new_row_length = row_length + row_padding_size;     size_t newDataLength = height * new_row_length;     unsigned char *newData = malloc(sizeof(unsigned char) *newDataLength);      unsigned char padding[3] = {0, 0, 0};     for(int i=0; i<height; i++) {         memcpy(newData + i*new_row_length, data + i*row_length, row_length);         memcpy(newData + i*new_row_length + row_length, padding, row_padding_size);     }      *paddedData = newData;     *paddedDataLength = newDataLength;      return row_padding_size; } 

Now before passing bitmap to resizeBitmap(), I am doing this padding:

unsigned char *paddedData = 0;     size_t paddedDataLength = 0;     int padding = padBitmap(gData, gDataLength, width, height, PNG_BIT_DEPTH_8, GRAYSCALE_COMPONENTS_PER_PIXEL, &paddedData, &paddedDataLength, 4);     width += padding; 

And I am using as bitmap paddedData. It seems to work correctly



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!