пятница, 3 июня 2011 г.

Преобразование cv::Mat в QImage

Очень удобно при разработке приложений по техническому зрению с графическем интерфейсом использовать OpenCV и Qt.

Но у некоторых людей возникают проблемы конвертировать изображения cv::Mat в QImage.

Итак, это очень просто вот функция:

/* Параметры: image - изображение которое хотим превратить в QImage
qim - уже загруженный в память буфер под данные. Может быть равен нулю.
Возвращает новое преобразованное в QImage с копированием данных
*/
QImage* createQImage(cv::Mat image,QImage* qim)
{
    if(!image.data)
     return 0;
    cv::Mat color;
    //Если один канал у изображения
    if(1==image.channels())
    {
        //Делаем трехканальное изображение. К сожалению разработчики Qt не учли возможность
        //рисования серых изображений
        color.create(cv::Size(image.cols,image.rows),CV_8UC3);
        std::vector ls;
        ls.push_back(image);
        ls.push_back(image);
        ls.push_back(image);
        cv::merge(ls,color);
    }else{
        color = image;
    }
    QImage*  pImage=0;
    if(0==qim)
    {
        pImage = new QImage(color.cols,color.rows,QImage::Format_RGB888);
    }else{
        if((qim->width()==color.cols)&&(qim->height()==color.rows))
        {
            pImage = qim;
        }else{
            delete qim;
            pImage = new QImage(color.cols,color.rows,QImage::Format_RGB888);
        }
    }

    //
    IplImage im = (IplImage)color;
    //По неизвестным причинам pImage->bytesPerLine() и im.widthStep могут не совпадать
    if(pImage->bytesPerLine()!=im.widthStep)
    {
        //Копирование по строкам
        for(int i=0;iheight();i++)
        {
            memcpy(pImage->bits()+i*pImage->bytesPerLine(),color.data+im.widthStep*i,im.widthStep);
        }
    }else{
        //Копирование всего изображения
        memcpy(pImage->bits(),color.data,im.imageSize);
    }
    return pImage;
}

Вот и все. Надею кому-то будет полезно!

3 комментария:

  1. А вот как-то так (в контексте чтения Depth-изображения с Kinect'а) не проще?

    Mat matRGB(Size(640, 480), CV_8UC3);
    Mat matDepth(Size(640, 480), CV_8UC3);
    Mat matDepth_raw(Size(640, 480), CV_16UC1);
    Mat matDepth_tmp(Size(640, 480), CV_8UC1);
    freenect_sync_get_video((void **)&dataRGB, &timeStampRGB, 0, FREENECT_VIDEO_RGB);
    freenect_sync_get_depth((void **)&dataDepth, &timeStampDepth, 0, FREENECT_DEPTH_10BIT);
    matRGB.data = (uchar *)dataRGB;
    matDepth_raw.data = (uchar *)dataDepth;
    matDepth_raw.convertTo(matDepth_tmp, CV_8UC1, 255.0 / 2048.0);
    cvtColor(matDepth_tmp, matDepth, CV_GRAY2RGB);
    QImage imageRGB = QImage((uchar *)matRGB.data, matRGB.cols /*640*/, matRGB.rows /*480*/, matRGB.step /**/, QImage::Format_RGB888);
    QImage imageDepth = QImage((uchar *)matDepth.data, matDepth.cols /*640*/, matDepth.rows /*480*/, matDepth.step /**/, QImage::Format_RGB888);

    ОтветитьУдалить
  2. При автоматическом удалении cv::Mat будет освобождена память. Соответственно QImage будут хранить в себе указатель на очищенную память, что в итоге приведет к ошибке доступа к памяти (Memory Access Violation)

    ОтветитьУдалить
  3. При автоматическом удалении cv::Mat будет освобождена память. Соответственно QImage будут хранить в себе указатель на очищенную память, что в итоге приведет к ошибке доступа к памяти (Memory Access Violation)

    ОтветитьУдалить