2

This question is partially answered at how to convert an opencv cv::Mat to qimage

I use Qt 5.5, and opencv 2.4, mac yosemite 10.10.5.

I'm looking for an efficient way to display a color image with a QImage, whose color information come from 3 distinct cv::Mat objects which contain the 3 RGB channels of a color image. For my own "education" I need to understand how to originally work with the channels separated. It is unclear to me what will be the most efficient way to end up with the QImage from these 3 separated channels.

The cv::Mat objects are declared and assigned as follows:

imRed = new Mat(Size(naxis1, naxis2), CV_64F);
imGreen = new Mat(Size(naxis1, naxis2), CV_64F);
imBlue = new Mat(Size(naxis1, naxis2), CV_64F);


double* redP = imRed->ptr<double>(0);
double* greenP = imGreen->ptr<double>(0);
double* blueP = imBlue->ptr<double>(0);

for (long i = 0; i < nPixels; i++)
{

    redP[i] = (double) rawProcess.imgdata.image[i][0];;
    greenP[i] = (double) rawProcess.imgdata.image[i][1];
    blueP[i] = (double) rawProcess.imgdata.image[i][2];

}

where rawProcess is an object from the Libraw library which contains the image data. redP, greenP, blueP are, as their name indicate, the pointer to the red, green and blue channel, and this code assumes (correct me if i'm wrong) that the cv::Mat addresses of the pixel values are continuous in memory.

From here, how shall I efficiently pass the buffer of the color image to QImage, i.e, minimizing intermediate copy, loops etc... (if necessary, my system can use openMP, I have 4 threads available)?

Thanks

Community
  • 1
  • 1
Wall-E
  • 623
  • 5
  • 17
  • Although I need to do some business on the three channels separately, in a next step I may work with a single mat objects and do things maybe in an even more efficient way (with a CV_64FC3 for example). But for now, for an "academic" understanding of how images channels can work in the opencv <-> qt interaction, it is important, if possible, to know the solution for getting things from those three separated channels. – Wall-E Nov 05 '15 at 08:34
  • are there two questions: 1. how to merge separate channels to a single cv::Mat object (e.g. use cv::merge) 2. how to convert a cv::Mat object efficiently to QImage? If your image channels have RGB ordering (standard openCV ordering is BGR) then converting cv::Mat to QImage is very efficient since you just pass the data arrays. If you have BGR ordering there is a conversion to RGB afaik which might be a full image copy. If it is just about rendering, QImage might not be the best choice but OpenGL might be much faster. But: I'm not sure whether Qt can handle 64 bit channel data at all. – Micka Nov 05 '15 at 08:45
  • @Micka: my question implies both of your questions. I don't need to display 64 bits in fact. The idea is to work with 64 bits in the backend, doing some image processing on the three separated channels at double precision, and then, do whatever is the quickest to endup with some data buffer with 8 bits per channel to display, with QImage. I wasn't sure if the merging of the channel should happen with Qt functions or with opencv functions. At the moment I don't want to use openGL. – Wall-E Nov 05 '15 at 10:13
  • Note that once I have my QImage, I know how to display things. (i do my own paintevents, with QPainters, etc...). – Wall-E Nov 05 '15 at 10:14
  • I would give cv::merge a try int RGB ordering) followed by a .convertTo 24 bit if necessary. After that just use the QImage constructor which uses external data arrays to prevent another data copy. But I can't tell whether this is more or less efficient than merging and transforming in Qt. – Micka Nov 05 '15 at 10:18
  • @Micka Ok, now on the QImage side, it demands a uchar* pointer to the data. Is that why you suggest to do .convertTo 24bit? The .convertTo would then be from an intermediate cv::Mat object that results from cv::merge? I never used the latter. What would that look like in my case? – Wall-E Nov 05 '15 at 11:37
  • 24/32 bit is typical pixel format for rendering. If some library supports other types they will probably convert to 8 bit per channel before rendering. convertTo either from intermediate merged image or from each single channel before merging. Maybe one of them is more efficient than the other. convert and merge will introduce two "copies" of the image data. – Micka Nov 05 '15 at 12:20
  • Ok, I will try. After conversion with convertTo/merge, I assume it should be enough to pass the Mat.data pointer to QImage then? [There](http://stackoverflow.com/questions/5026965/how-to-convert-an-opencv-cvmat-to-qimage) they are using scanline to get the QImage data pointer line by line, but would Mat.data pointer be enoough and faster in my case? – Wall-E Nov 05 '15 at 13:40
  • is use: `QImage( mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888 );` which doesn't copy any data. If wrong data order (BGR) you can use `.rgbSwapped()` in addition, but this will include another data copy afaik. – Micka Nov 05 '15 at 13:46

1 Answers1

0

This code below is working.

Starting from the above code (with naxis1 and naxis2 the width and height of the image):

imRed = new Mat(Size(naxis1, naxis2), CV_64F);
imGreen = new Mat(Size(naxis1, naxis2), CV_64F);
imBlue = new Mat(Size(naxis1, naxis2), CV_64F);


double* redP = imRed->ptr<double>(0);
double* greenP = imGreen->ptr<double>(0);
double* blueP = imBlue->ptr<double>(0);

for (long i = 0; i < nPixels; i++)
{

    redP[i] = (double) rawProcess.imgdata.image[i][0];;
    greenP[i] = (double) rawProcess.imgdata.image[i][1];
    blueP[i] = (double) rawProcess.imgdata.image[i][2];

}

I add:

std::vector<cv::Mat> matVector;
matVector.push_back(imRed);
matVector.push_back(imGreen);
matVector.push_back(imBlue);

cv::Mat colorMat32;
cv::merge(matVector, colorMat32);

cv::Mat colorMat888

double alpha = (double) (255 / 65535);
double beta = 0.0;

colorMat32.convertTo(colorMat888, CV_8UC3, alpha, beta);

newPaintImage = new QImage(colorMat888.data, naxis1, naxis2, QImage::Format_RGB888);

Thanks @Micka.

Wall-E
  • 623
  • 5
  • 17