2

I have some legacy code that was writing to a NITF file to display some images. In the legacy code, it appears as if there was a LUT being used, and there was a section of code that wrote out a row at a time to the NITF file , and the values of that row were calculated like so:

// convert RGB to LUT values
unsigned char *lutData = new unsigned char[numBytes/3];
for (unsigned j = 0 ; j < numBytes/3 ; j++)
    lutData[j] = (unsigned char) stuff;

Where data was my original array of unsigned chars.

So now I am trying to take that data array and output it into a QImage in my GUI.

It would seem to me in the NITF, there was a block of LUT data that was "rows x cols" in size, right? So I created an array of that lut data:

unsigned char *lutData = new unsigned char[imwidth * imheight];
QImage *qi = new QImage(imwidth,imheight, QImage::Format_Indexed8);
for (int i = 0 ; i < imheight ; i++)
{
             #pragma omp parallel for
              for (int j = 0 ; j < imwidth ; j++)
              {
                     lutData[i*imwidth + j] = stuff;
              }
}

and then I tried to populate the qimage like this:

   for (int i = 0 ; i < imheight ; i++)
   {
                #pragma omp parallel for
                 for (int j = 0 ; j < imwidth ; j++)
                 {
                     qi->setPixel(j,i,qRgb(lutData[i*imwidth + j],lutData[i*imwidth + j],lutData[i*imwidth + j]));
                }
   }

However, this seems to more or less just give me a grayscale image, instead of my actual data.

What am I doing wrong?

Thanks!

Derek
  • 11,715
  • 32
  • 127
  • 228

2 Answers2

3

The qRgb constructor looks like this:

qRgb(int r, int g, int b)

You're passing in the same value (lutData[i*imwidth + j]) for all three colors, so you'll end up with a greyscale image.

Now, qRgb is just a typedefed unsigned int, so if you store your colors in that format (RGB32 / ARGB32), you can just call:

qi->setPixel(j, i, lutData[i*imwidth + j])

But you might want to look into using QImage's built-in lookup table (aka color table) support - it might end up being as simple as:

QImage image(data, imwidth, imheight, QImage::Format_Indexed8);
QVector<QRgb> colorTable;
// Translate each color in lutData to a QRgb and push it onto colorTable;
image.setColorTable(colorTable);

Hope this helps!

Update: For reference purposes, here's the test code I used to try out QImage in indexed color mode (compiles without warnings with g++ - just remember to link to -lQtCore and -lQtGui):

#include <QtCore/QVector>
#include <QtGui/QApplication>
#include <QtGui/QImage>
#include <QtGui/QLabel>
#include <QtGui/QPixmap>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    unsigned char indices[1024];
    for(int i = 0; i < 1024; ++i)
    {
        indices[i] = qrand() & 0x0f;
    }

    QVector<QRgb> ctable;
    for(int i = 0; i < 16; ++i)
    {
        ctable.append(qRgb(qrand() & 0xff, qrand() & 0xff, qrand() & 0xff));
    }

    QImage image(indices, 32, 32, QImage::Format_Indexed8);
    image.setColorTable(ctable);

    QLabel label;
    label.setPixmap(QPixmap::fromImage(image));
    label.show();

    return app.exec();
} 
Xavier Holt
  • 14,471
  • 4
  • 43
  • 56
  • trying it your first way, I got an image that was really blue looking. Not really sure why - I guess because it has the smallest denominator in the LUT calculation perhaps? But it is what they have in the original code - and I can view the output data fine in ENVI and other programs – Derek Mar 31 '11 at 19:58
  • @Derek - You know, looking at the original code again, I don't think it's a lookup table at all. I think it's a compression routine - it takes three bytes from `data` and shrinks them down to a single byte in `lutData`. And the size of `lutData` is directly related (x 1/3) to the size of `data`; if it was a color table, it would be independent of the number of pixels, and dependent on the number of colors instead... What format is `data` in originally? Can you just use the original `data` array with `QImage::Format_RGB888`? – Xavier Holt Mar 31 '11 at 20:24
  • data is originally an unsigned char *. It is imwidth*3*imheight in size, based on the code showing in my snippet – Derek Mar 31 '11 at 20:43
  • @Derek - Sounds like a good candidate for RGB888 data to me. What happens if you dump it directly to your QImage, bypassing `lutData` altogether (there's a convenient [QImage constructor](http://doc.trolltech.com/main-snapshot/qimage.html#QImage-6) in case width != pitch)? – Xavier Holt Mar 31 '11 at 20:54
  • when I use the original data array, and that RGB888 format, I get something that looks like a severely blurred version of the output. The way it is in my original post at least looks like the right thing, and is clear, just doenst have the right colors – Derek Mar 31 '11 at 20:57
  • same goes for using that convenient constructor that I didnt know about..looks clear, just still seems like the colors are grayscale. Am I correct in assuming if I am using a LUT, there has to be one entry in the LUT for every pixel in the image? – Derek Mar 31 '11 at 21:00
  • @Derek - Blurred how? If it just looks "smoother", I'm willing to bet it's just a more accurate representation of the info in `data` - dividing a value in the range [0, 255] gives you a value in the range [0, 5], clamping each color component to one of six "levels" (this also explains the multiplication by 6^X when building `lutData` - it's a left shift, but in a different base). So if you're seeing a "blurred" image, you might just be seeing a more accurate one (unless, of course, you _want_ that clamping...)! – Xavier Holt Mar 31 '11 at 21:33
  • @Derek - Aha! I seem to have misunderstood `lutData`, too. It's not a color table - it's an array of indices _into_ a color table - one for each pixel. Which means the actual color table is still floating around somewhere; it's not `data` (the full colors of each pixel), and it's not `lutData` (indexes _into_ said color table for each pixel), so it must be some other variable... If you can find it, you can make your Indexed8 QImage out of `lutData`, build your QVector out of your color-table variable, and then setColorTable()... You get your old image, complete with clamping! – Xavier Holt Mar 31 '11 at 21:40
  • Its hard to say it is just "smoother" - the features in the image are almost unrecognizable. If I was going to use the setColorTable method, and this image is Format_Indexed8 - I need a loop over 256 values Maximum for going in that Colortable, right? How do I then decide what value from the colortable goes in for a particular pixel? is that what my lutData is calculating? If so, maybe I need to look in the original code to see how "colorTable" was calculated there? – Derek Mar 31 '11 at 21:44
  • ah - we seem to have had our ah-ha moment at the same time..I will post back results – Derek Mar 31 '11 at 21:44
  • Hello - I generated a 256 element vector of QRgb, and used qi.setColorTable(myVector) to set it in the image. However, I need to put this into a QGraphicsPixmapItem to display, but when I run the method: QPixmap::fromImage(*qi,Qt::AutoColor) I get a segmentation fault - any ideas? – Derek Apr 01 '11 at 16:55
  • @Derek - Really hard to say without seeing your code. I've added the code I used to test things to my answer - you can compare, and see if there are any obvious differences... (I've never used QGraphicsPixmapItem - if you're just displaying the image, it's easier to just paint it to a QLabel. Still need it in QPixmap format, though.). And no, it doesn't show anything - just a 32x32 bunch of randomness. – Xavier Holt Apr 01 '11 at 20:17
0

Very interesting image compression. Here's my try to convert a RGB888 image into a Index8 with your function:

    QImage image8(image.size(), QImage::Format_Indexed8);
    QVector<QRgb> lut(256);
    for(int i=0;i<image888.height();++i) {
        const uchar * p = image888.bits() + image888.bytesPerLine()*i;
        uchar * q = image8.bits() + image8.bytesPerLine()*i;
        for(int j=0;j<image.width();++j, p+=3) {
            QRgb c = qRgb(p[0], p[1], p[2]);
            int n = qRed(c)/51*36 + qGreen(c)/51*6+qBlue(c)/51;
            lut[n] = c;
            *q++ = n;
        }
    }
    image8.setColorTable(lut);

It basically fills up the 8-bit color table as it converts from 888 to 8-bit data. For better result, you can accumulate the RGB values at an index, then average the values before putting them into the color table. It can also use some optimization on walking the image buffer.

Stephen Chu
  • 12,724
  • 1
  • 35
  • 46