Converting 16 bit per channel images for QImage
-
Hi,
If I may, you should maybe consider using OpenCV for the image manipulation and only convert to QImage once you're done. IIRC they have a Qt backend for the UI part so you probably can take some ideas on how they do the conversion.
-
I have considered this but for now, this is quite an overkill if I just want to display images. I don't want to manipulate, and If I do, the point of my project is to understand the basics before going (maybe) with yet another framework.
The question remains. With the
red8
,green8
, andblue8
channels above, 8 bits unsigned, how do I get to build my coloured image with QImage?As I said, I have no trouble for displaying grayscale images. I simply would like to use those channels to get a coloured QImage. So far, the propositions above are giving me worse display than what I first did in my 1st post.
Thanks
-
I may have found what I was missing:
http://stackoverflow.com/questions/1982878/how-to-display-image-from-array-of-colors-data-in-qt
Will report back.
-
I looked at the stackoverflow post. I think that
new QImage((uchar*)coloredImage, width, height, QImage::Format_ARGB32);
is equivalent to and QImage image( data, width, height, 4, QImage::Format_ARGB32 );
http://doc.qt.io/qt-5/qimage.html#QImage-5
So you should have the same results as you first results with this function.The image.setPixel( i, argb ); should work since you are setting each pixel individually., So, if this method does not work, then, your source data is incorrect.
We will wait for your report once you experiment ^^. Good luck.
-
Ok. In fact, I am confused with qRgb. It accepts "int", which is 32 bit:
http://doc.qt.io/qt-5/qcolor.html#qRgb
But the documentation doesn't say what is the accepted range.When I look at this setRgb(): http://doc.qt.io/qt-5/qcolor.html#setRgb
it also accepts "int" (which is 32 bits) and yet is says the range must be [0-255]So, is that the same for qRgb? It takes argument as "int", but they need to be within [0-255]?
-
I made some progress. I'll give some up-to-date code for Qt 5.5 as the information in the link at
http://stackoverflow.com/questions/1982878/how-to-display-image-from-array-of-colors-data-in-qt
is outdated. In my case, setPixel() does not accept 1D indexing but needs a QPoint instead.So, still using
red16
,green16
,blue16
as my unsigned 16 bits values of the color channels from my original image. They have a size of nPixels, i.e, the total number of pixels in the image. So, e.g.,red16[ii]
is giving me the red value of pixel ii.int range = 65535; // below, naxis1 is the width, naxis2 is the height (in pixels) newPaintImage = new QImage(naxis1, naxis2, QImage::Format_ARGB32 ); for ( int ii = 0; ii < nPixels; ++ii ) { // Below we scale the channel value to fit within [0-255] // This assumes that qRgba() needs that range. This is undocumented. cred = (int) 255 * red16[ii] / range; cgreen = (int) 255 * green16[ii]/ range; cblue = (int) 255 * blue16[ii] / range; QRgb argb = qRgba( cred, //red cgreen, //green cblue, //blue 255); //alpha //1D index to 2D (x,y) coordinates. QPoint loc(ii%naxis1, ii/naxis1); newPaintImage->setPixel( loc, argb); }
This is finally giving the "expected" results (from comparison with a reference .tiff image), and that result is indeed different from what I had at the very beginning. So, since i'm using the same channel values as in the 1st post, my source image is correct but my original implementation was not.
I think I misunderstood how i'm supposed to populate my 32 bits buffer with the QRgb converted colors. I'm probably not looping over the right indices.So, let's start over, and i'll show you how i'm looping. What follows is more compact version of my 1st post, but I stick to a 32 bits image buffer which is casted when passing it to the QImage constructor.
// initialize an image buffer, 32 bits unsigned. nPixels is my total number of pixels. image32 = new quint32[nPixels]; for (long ii = 0; ii < nPixels; ii++) { cred = (int) 255 * red16[ii] / range; cgreen = (int) 255 * green16[ii] / range; cblue = (int) 255 * blue16[ii] / range; QRgb colorV = qRgb(cred, cgreen, cblue); // Can also be quint32, i tried both, same results. image32[ii] = colorV; // so the image32 is populated here at pixel index ii, with the quadruplet. } // Finally, send the buffer to QImage myQImage = new QImage((uchar*)image32, naxis1, naxis2, QImage::Format_ARGB32);
With this i'm assuming that image32[ii] gives me the address of the pixel ii, to which I assign a color value.
Yet when I look at the stackoverflow above, it seems I should maybe account for the "Bytes_per_line" (aka, the stride?) if I want to populate the buffer directly, and that I cannot just give the QRgb color directly to image32[ii] like above, can I? If not, then how should I do it?Thanks
-
Just a side note. I don't really need to use an image buffer at all. I want to avoid using setPixel(), which is a bit slow, that's all. I tried working with scanLine, but again, using the exact same loop, it crashed, and I believe it's caused again because I'm not accounting for the Bytes_per_line properly.
-
I've got it working with scanLine().
The only things that changed from the usage of setPixel(), in my above codes, is:
QPoint loc(ii%naxis1, ii/naxis1); newPaintImage->setPixel( loc, argb);
Simply replaced with:
QRgb* rowData = (QRgb*) newPaintImage->scanLine(ii/naxis1); rowData[ii%naxis1] = argb;
Then scanLine, given the row number (i.e, the Y coordinates), works out the stride for you and gives the proper pointer for the whole row.
-
Something interesting came up...
When I was comparing my methods, I had a pragma directives for openMP for parallelising my for loop. They were active for the methods i used with my buffer
image32
tests.
Then for the new tests with setPixels, writing another block of code, I didn't use the openMP directive. This directive was simply:#pragma omp parallel for for (long ii = 0; ii < nPixels; ii++) {
for every for loops in the method using the buffer explicitly. (image32)
But i went back comparing without openMP... I realised the latter was actually messing with the shared buffer then...
Here are some screenshots below.- image32 (openMP): https://www.dropbox.com/s/9lw8gs4oczrm2b7/image32_A.jpg?dl=0
shows the output image when using the original method, with openMP. Note the scrambled horizontal lines. This is non-deterministic. Other instances of the program gives me different results. - image32(no openMP): https://www.dropbox.com/s/w854ahn309nbo33/image32_no_OMP.jpg?dl=0
same as above, without openMP. The scrambled lines are gone, image is cleaner, and identical across different runs. - setPixel(no openMP): https://www.dropbox.com/s/h6su6tp1qjn7gqt/setPixels_no_OMP.jpg?dl=0
It is identical as above, in fact. So my original method was ok, as anticipated by @Yostane. - setPixel(with openMP again): https://www.dropbox.com/s/onmhbk8pppuuc4o/setPixels.jpg?dl=0
The scrambled lines show up again.
Here is the code with the openMP directive:
int range = 65535; newPaintImage = new QImage(naxis1, naxis2, QImage::Format_ARGB32 ); #pragma omp parallel for // THAT WAS THE CULPRIT!!! for (long ii = 0; ii < nPixels; ii++) { cred = (int) 255 * red16 / range; cgreen = (int) 255 * green16 /range; cblue = (int) 255 * blue16/ range; QRgb argb = qRgba( cred, cgreen, cblue, 255); QPoint loc(ii%naxis1, ii/naxis1); newPaintImage->setPixel( loc, argb); } }
Then the newPaintImage is sent to a widget with a paintEvent implemented.
So, I have been abusively using openMP for this, this was the source of my troubles. Is there a way to make this more thread safe? So that I can still use parallelisation while making sure that things get displayed once all the threads are done ( i have 4)?
Thanks
- image32 (openMP): https://www.dropbox.com/s/9lw8gs4oczrm2b7/image32_A.jpg?dl=0
-
Fixed!
I posted the problem there
http://stackoverflow.com/questions/33130691/qimage-and-openmp-when-updating-image-display/33133653#33133653I needed to declare variables private for the pragma directive. So, instead of using cred, cgreen, and cblue as I was, the code becomes:
int cred2, cgreen2, cblue2 #pragma omp parallel for private(cred2, cgreen2, cblue2) for ( int ii = 0; ii < nPixels; ++ii ) { cred2 = (int) 255 * red16[ii] / range; cgreen2 = (int) 255 * green16[ii] / range; cblue2 = (int) 255 * blue16[ii] / range; QRgb argb = qRgba( cred2, cgreen2, cblue2, 255); QRgb* rowData = (QRgb*) newPaintImage->scanLine(ii/naxis1); rowData[ii%naxis1] = argb; }
That's all. Then all methods become strictly equivalent regarding the end result. Haven't compared speed though, which is a concern in my context.