Optimus 1072 Optimus 1072 - 11 days ago 5
C++ Question

How to convert binary Mat to QImage?

I have started learning Qt and is trying to make a simple video Player which will load the video and will play it. It worked perfectly fine. Now added thresholding functionality to it. The threshold value will be obtained from the spinBox.
Code is written in such a way that thresholding operation will be done with value in spinBox except at value 0 (where normal video is displayed).
So this is my function for the same:

void Player::run()
{

while(!stop )
{

if(!capture.read(frame))
stop = true;
// convert RGB to gray
if(frame.channels() == 3)
{
if(thresh == 0)
{
cvtColor(frame, RGBframe, CV_BGR2RGB);
img = QImage((const unsigned char*)(RGBframe.data),
RGBframe.cols,RGBframe.rows,QImage::Format_RGB888);

}
else
{
Mat temp;
cvtColor(frame, temp, CV_BGR2GRAY);
threshold(temp, binary, thresh, 255, 0);
img = QImage((const unsigned char*)(binary.data),
binary.cols, binary.rows, QImage::Format_Indexed8);




bool save = img.save("/home/user/binary.png");

cout<<"threshold value = "<<thresh<<endl;

//imshow("Binary", binary);
}
}
else
{
if(thresh == 0) // original Image
{
img = QImage((const unsigned char*)(frame.data),
frame.cols,frame.rows,QImage::Format_Indexed8);

}
else // convert to Binary Image
{

threshold(frame, binary, thresh, 255, 0);

img = QImage((const unsigned char*)(binary.data),
binary.cols, binary.rows, QImage::Format_Indexed8);
}




}

emit processedImage(img);
this->msleep(delay);
}
}


for spinBox value equals 0 it runs fine but when spinBox value is incremented I get only black screen. I tried
imshow(cv:: Mat binary)
and it is showing the correct binary image but when I try to save
QImage img
it is some random black and white pixels (though of same size of original frame).

Answer

It seems that you're missing the color table for your indexed image. You need to add a color table (before the while loop):

 QVector<QRgb> sColorTable(256); 
 for (int i = 0; i < 256; ++i){ sColorTable[i] = qRgb(i, i, i); }

and after you create the QImage from the binary Mat you need to add

 img.setColorTable(sColorTable);

In general, you can wrap all Mat to QImage conversion in a function. Below there is the bug corrected version of cvMatToQImage originally found here.

You can then remove all the conversion to QImage from your code and use this function instead.

QImage cvMatToQImage(const cv::Mat &inMat)
{
    switch (inMat.type())
    {
        // 8-bit, 4 channel
    case CV_8UC4:
    {
        QImage image(inMat.data,
            inMat.cols, inMat.rows,
            static_cast<int>(inMat.step),
            QImage::Format_ARGB32);

        return image;
    }

    // 8-bit, 3 channel
    case CV_8UC3:
    {
        QImage image(inMat.data,
            inMat.cols, inMat.rows,
            static_cast<int>(inMat.step),
            QImage::Format_RGB888);

        return image.rgbSwapped();
    }

    // 8-bit, 1 channel
    case CV_8UC1:
    {
        static QVector<QRgb>  sColorTable;

        // only create our color table the first time
        if (sColorTable.isEmpty())
        {
            sColorTable.resize(256);
            for (int i = 0; i < 256; ++i)
            {
                sColorTable[i] = qRgb(i, i, i);
            }
        }

        QImage image(inMat.data,
            inMat.cols, inMat.rows,
            static_cast<int>(inMat.step),
            QImage::Format_Indexed8);

        image.setColorTable(sColorTable);

        return image;
    }

    default:
        qWarning() << "cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
        break;
    }

    return QImage();
}
Comments