5

I've found very similiar topic: how to convert an opencv cv::Mat to qimage , but it does not solve my problem.

I have function converting cv::Mat to QImage

QImage cvMatToQImg(cv::Mat& mat)
{
    cv::Mat rgb;
    if(mat.channels()==1)
    {
        cv::cvtColor(mat,rgb,CV_GRAY2BGR);
        cv::cvtColor(rgb,rgb,CV_BGR2BGRA);
        QImage temp = QImage((unsigned char*)(rgb.data), rgb.cols, 
                              rgb.rows,QImage::Format_ARGB32 );

        QImage returnImage = temp.copy();
        return returnImage;
}

And it's works for my but I want to make it more efficient. First: why changing 2 cvtColor functions with:

cv::cvtColor(mat,rgb,CV_GRAY2BGRA)

fails on

QImage returnImage = temp.copy() 

with segfault.

Then how to eliminate copying of QImage. When I simply return temp image, I'm getting segfault.

Any other optimalizations can be done there? It's very often used function so I want to make it as fast as possible.

Community
  • 1
  • 1
krzych
  • 2,126
  • 7
  • 31
  • 50
  • You haven't specified the image format of `mat`, is it even a 24bit grayscale image? – cmannett85 Jun 13 '12 at 13:49
  • Mostly I'm working with 8bit grayscale. But the more cases function cope the better. – krzych Jun 13 '12 at 14:10
  • 1
    There is no need to copy. QImage is shared pointer. Segfauld is not due to not-copying, but seems to be due to incorrect alignment. There is similar constructor of QImage specifying additionally bytes per line - use this. P.S: found by googling: http://code.google.com/p/qt-opencv-multithreaded/source/browse/trunk/src/MatToQImage.cpp?r=75 – Valentin H Dec 22 '12 at 19:58

3 Answers3

4

It may be easiest to roll your own solution. Below is the current OpenCV implementation for going from gray to RGBA format:

template<typename _Tp>
struct Gray2RGB
{
    typedef _Tp channel_type;

    Gray2RGB(int _dstcn) : dstcn(_dstcn) {}
    void operator()(const _Tp* src, _Tp* dst, int n) const
    {
        if( dstcn == 3 )
            for( int i = 0; i < n; i++, dst += 3 )
            {
                dst[0] = dst[1] = dst[2] = src[i];
            }
        else
        {
            _Tp alpha = ColorChannel<_Tp>::max();
            for( int i = 0; i < n; i++, dst += 4 )
            {
                dst[0] = dst[1] = dst[2] = src[i];
                dst[3] = alpha;
            }
        }
    }

    int dstcn;
};

Here is where the actual cvtColor call occurs:

    case CV_GRAY2BGR: case CV_GRAY2BGRA:
        if( dcn <= 0 ) dcn = 3;
        CV_Assert( scn == 1 && (dcn == 3 || dcn == 4));
        _dst.create(sz, CV_MAKETYPE(depth, dcn));
        dst = _dst.getMat();

        if( depth == CV_8U )
            CvtColorLoop(src, dst, Gray2RGB<uchar>(dcn));

This code is contained in the color.cpp file in the imgproc library.

As you can see, since you are not setting the dstCn parameter in your cvtColor calls, it defaults to dcn = 3. To go straight from gray to BGRA, set dstCn to 4. Since OpenCV's default color order is BGR, you'll still need to swap the color channels for it to look right (assuming you get your image data from an OpenCV function). So, it may be worth it to implement your own converter possibly following the above example, or using ypnos answer here.

Also, have a look at my other answer involving how to integrate OpenCV with Qt.

Community
  • 1
  • 1
mevatron
  • 13,911
  • 4
  • 55
  • 72
4

Your solution to the problem is not efficient, in particular it is less efficient then the code I posted on the other question you link to.

Your problem is that you have to convert from grayscale to color, or RGBA. As soon as you need this conversation, naturally a copy of the data is needed.

My solution does the conversion between grayscale and color, as well as between cv::Mat and QImage at the same time. That's why it is the most efficient you can get.

In your solution, you first try to convert and then want to build QImage around OpenCV data directly to spare a second copy. But, the data you point to is temporary. As soon as you leave the function, the cv::Mat free's its associated memory and that's why it is not valid anymore also within the QImage. You could manually increase the reference counter of the cv::Mat beforehand, but that opens the door for a memory leak afterwards.

In the end, you attempt a dirty solution to a problem better solved in a clean fashion.

ypnos
  • 50,202
  • 14
  • 95
  • 141
1

The problem is that both the cv::Mat and QImage data isn't necessarily contiguous. New data rows in opencv start on a 32bit boundary (not sure about QImage - I think it's system dependant) so you can't copy a memeory block unless your rows happen to be exact multiples of 4bytes

See How to output this 24 bit image in Qt

Community
  • 1
  • 1
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263