1

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: enter image description here

and the output is: enter image description here

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 ?

enter image description here

Michał Ziobro
  • 9,353
  • 9
  • 67
  • 122

2 Answers2

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.

saurabheights
  • 3,520
  • 1
  • 28
  • 47
  • any code how I can avoid this problem in generic way? Grayscale bitmaps can be of any size W x H and if each pixel has 1 byte, there is no guarantee that each rows will be multiple of 4 bytes – Michał Ziobro Oct 07 '16 at 20:21
  • try changing `cvSetData(inImage, inData, inImage->widthStep)` to `cvSetData(inImage, inData, 1101);` – saurabheights Oct 07 '16 at 20:46
  • I changed it but it doesn't help I use computed value: (inWidth*noOfChannels*bitDepth)/CHAR_BIT -> should evaluate to 1101 – Michał Ziobro Oct 07 '16 at 20:53
1

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

Michał Ziobro
  • 9,353
  • 9
  • 67
  • 122