Чтение OpenCV Mat 16bit в QImage 8bit Greyscale

Что я хочу сделать

  1. Файл: TIFF, 2 страницы, оттенки серого, 16 бит.
  2. OpenCV: 2x Mat, оттенки серого, 16 бит (CV_16UC1)
  3. Qt: 2x QImage, оттенки серого, 8 бит (Format_Greyscale8)

Я хочу прочитать 16-битный Tiff, а затем преобразовать его, чтобы показать его как 8-битный.

Чтение

void ImgProc::Load_Image_2xTiff_16bit_G(Mat *Mat_A_in, Mat *Mat_B_in, string file_name)
{  
    vector<Mat> vec_Mat;
    vec_Mat.reserve(2);

    imreadmulti(file_name, vec_Mat);

    *Mat_A_in = vec_Mat[0];
    *Mat_B_in = vec_Mat[1];
}

Преобразование

void ImgProc::Convert_Mat16_2_QIm8(QImage *QIm_Out, Mat *Mat_In, double scale_factor)
{
    unsigned int rows = Mat_In->rows;
    unsigned int cols = Mat_In->cols;
    *QIm_Out = QImage(cols, rows, QImage::Format_Grayscale8);
    unsigned char* line_QIm;

    if (!Mat_In->data)
        return;

    for(unsigned int y = 0; y < rows; y++)
    {
        line_QIm = QIm_Out->scanLine(y);
        for(unsigned int x = 0; x < cols; x++)
        {
            line_QIm[x] = (unsigned char)(Mat_In->at<ushort>(y, x) * scale_factor);
        }
    }
}

Проблема

Когда я использую Mat_In->at<ushort>(y, x) (читай 16 бит), он вылетает с abort() has been called. То же самое произойдет, если я вместо этого использую <short>.

Когда я использую Mat_In->at<uchar>(y, x) (читай 8 бит), это работает, но обрезает информацию без масштабирования. Это проявляется как «черные дыры» в более ярких областях изображения, вероятно, эффект переполнения.

Я думаю, я должен упомянуть, что камера, которая делала изображения, использует только 14-битную глубину.


person Kemendil    schedule 05.09.2018    source источник
comment
Каков фактический тип элементов Mat_In ? | Почему бы не использовать cv::Mat::convertTo (а затем просто работать с весь необработанный буфер, а не перебирать его пиксель за пикселем).   -  person Dan Mašek    schedule 05.09.2018
comment
Mat_In.type(); возвращает 0. Я пытался использовать cv::Mat::convertTo, но потерпел неудачу, поэтому я попробовал этот способ. Мне также нравится, что я точно знаю, что там происходит, и могу манипулировать в любое время, когда захочу.   -  person Kemendil    schedule 05.09.2018
comment
Тип 0 соответствует CV_8U, то есть изображение уже 8-битное. Это объяснило бы сбой (я ожидаю, что он пройдёт половину строк). Можете ли вы предоставить образец изображения, которое вызывает эту проблему?   -  person Dan Mašek    schedule 05.09.2018
comment
показать, как вы читаете файл изображения. OpenCV по умолчанию читается как тип 8UC3   -  person Micka    schedule 05.09.2018
comment
@Micka Мой метод чтения показан выше. Хороший намек, спасибо. Я попытаюсь выяснить, как читать его как 16-битный.   -  person Kemendil    schedule 06.09.2018
comment
Jhon_Sharp1318s Ответ сработал, когда я добавил cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR в качестве флагов к imreadmulti, так что, Дэн Масек, ваша мысль была правильной, спасибо.   -  person Kemendil    schedule 06.09.2018
comment
@kemedil ах, хорошо, я еще не знал функцию imreadmulti и думал, что это какая-то пользовательская функция.   -  person Micka    schedule 06.09.2018


Ответы (1)


Я столкнулся с такой же проблемой. Следующий код — это решение, которое я использовал для исправления этого случая.

   #include <opencv2/core.hpp>
   #include <opencv2/imgproc.hpp>
   #include <opencv2/highgui.hpp>

   #include <QImage>

    int main(void)
    {

      //Read the 16 bit per pixel image.
      cv::Mat I = cv::imread('16BitsPerPixelImage.tiff',cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR);

     //Convert from 16 bit per pixel to 8 bit per pixel using a min max normalization and store it a 8 bit per pixel.
     cv::normalize(I,I,0.,255.,cv::NORM_MINMAX,CV_8U);

    // Then actualy the easiest way to convert it to a QImage is to save a temporary file and open it using QT functions.
    // PNG use a compression without loss algorithm.
      cv::imwrite("/tmp/convert16to8.png",I);

      QImage QI;

      QI.load("/tmp/convert16to8.png");

      return EXIT_SUCCESS;
    }
person John_Sharp1318    schedule 05.09.2018
comment
Добавление cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR в качестве флагов для imreadmulti решило проблему, остальная часть моего кода работала как есть. Большое спасибо! Теперь я могу начать делать классные вещи. - person Kemendil; 06.09.2018