OpenCV с использованием cvImageCreate() с изображением в оттенках серого завершается с ошибкой, и изменение размера обычно завершается с ошибкой

#c #opencv

#c #opencv

Вопрос:

У меня есть такой код, который загружает изображение в оттенках серого из буфера размером 1 байт, растровое изображение размером 8 бит. Затем он изменяет размер этого изображения.

 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, amp;step, amp;size);
    *outDataLength = step*size.height;

    cvReleaseImage(amp;inImage);
    cvReleaseImage(amp;outImage);

    return 0;
}
  

Я использую здесь BitDepth = 8 и noOfChannels = 1.
Загруженное изображение:
введите описание изображения здесь

и вывод: введите описание изображения здесь

этот вывод не всегда записывается, поскольку программа обычно завершается с ошибкой:

 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
  

Я прикрепляю вывод отладчика, поскольку возникает интересная ситуация, когда я передаю буфер в оттенках серого размером 528480, который равен 1 байту * 1101 * 480, но после cvCreateImage внутри ImageSize 529920, а widthStep равен 1104! Может быть, здесь проблема с этим изображением, но почему это так?

введите описание изображения здесь

Комментарии:

1. Привет, я предоставил быстрый ответ, так как уже поздно. Улучшит его, как только я освобожусь.

Ответ №1:

Эта проблема связана с widthstep и width IplImage. Opencv дополняет изображение шириной, кратной 4 байтам. Здесь opencv использует ширину 1101 и widthstep 1104. Но при записи данных из bitmap в IplImage в строку записывается несколько дополнительных пикселей (обратите внимание на диагональную линию сверху слева направо).

Обратите внимание, что изображение не наклонено. Просто каждая следующая строка немного сдвигается влево (на 3 пикселя), что дает представление о сдвиговом преобразовании.

Также возможно, что вы предоставляете меньшую ширину, чем то, что содержит растровое изображение.

Смотрите Документы здесь и ищите отступы. Вы можете попробовать скопировать все данные столбца по строкам.

Почему сбой: иногда opencv завершает чтение за пределами буфера растрового изображения и может попасть в неприкасаемые адреса памяти, вызывая исключение.

Примечание: Растровое изображение, вероятно, также имеет отступ, из которого вы получили черную диагональную линию.

Комментарии:

1. любой код, как я могу избежать этой проблемы в общем виде? Растровые изображения в оттенках серого могут быть любого размера W x H, и если каждый пиксель имеет 1 байт, нет гарантии, что каждая строка будет кратна 4 байтам

2. попробуйте изменить cvSetData(inImage, inData, inImage->widthStep) на cvSetData(inImage, inData, 1101);

3. Я изменил его, но это не помогает, я использую вычисленное значение: (inWidth noOfChannels BitDepth)/CHAR_BIT -> должно быть равно 1101

Ответ №2:

На основе ответа saurabheights я написал процедуру для заполнения каждой строки растрового изображения любым заданным количеством байтов в строке.

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

Теперь, прежде чем передавать bitmap в resizeBitmap(), я делаю это дополнение:

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

И я использую как растровое paddedData изображение. Кажется, он работает правильно