LUT clarification for RGB32 Color images.

Help
Chuck Shaw
2013-07-09
2013-07-11
  • Chuck Shaw
    Chuck Shaw
    2013-07-09

    Hey Guys,

    I just started using nitro a few days ago to add the ability to output exploited imagery (EO/IR Snapshots from EOP Streams) as a NITF image. So far so good, I have it outputting valid NITF files. The only issue i'm having is the loss of color information. Following the the examples, i'm creating 3 bands, but i'm not exactly sure how to use the LUT's. Would someone explain to me how I should setup things to guarantee i'm not going to loose color in my NITF images? My source imagery is RGB32.

    Thanks,
    Chuck

     
  • Chuck Shaw
    Chuck Shaw
    2013-07-09

    As i'm reading more about the NITF standard, I'm realizing that I shouldn't need to use the LUT to produce a NITF with a color image, since my image source is RGB already. I'm not really sure at this point where i'm going wrong with setting up my ImageData.

     
  • Chuck Shaw
    Chuck Shaw
    2013-07-09

    I've made some more progress, mainly with understanding how to setup multiple bands. At this point I can write out color images, however the colors are just a little off. Here is my writeImage function, I'm hoping someone may see where i have gone wrong. The source image data is RGB 32.

    nitf::Record record(NITF_VER_21);
        nitf::Writer writer;
        nitf::IOHandle output(details.filename, NITF_ACCESS_WRITEONLY, NITF_CREATE);
    
        //
        //Build the file header.
        //
        nitf::FileHeader header;// = setupHeader();
        header.getFileHeader().set("NITF");
        header.getFileVersion().set("02.10");
        header.getComplianceLevel().set("03");
        header.getSystemType().set("BF01");
        header.getOriginStationID().set("EVE");
        header.getFileTitle().set("EVE Image");
        header.getClassification().set("U");
        header.getEncrypted().set("0");
    
        //
        // Add the Images
        //
        nitf::ImageSegment segment = record.newImageSegment();
        nitf::ImageSubheader subHeader = segment.getSubheader();
        subHeader.createBands(3);
    
        nitf::BandInfo redBand = subHeader.getBandInfo(0);
        redBand.init("R",     //The band representation
                     " ",     //The band subcategory
                     "N",     //The band filter condition
                     "   ");  //The band standard image filter code
    
        nitf::BandInfo greenBand = subHeader.getBandInfo(1);
        greenBand.init("G",    //The band representation
                       " ",    //The band subcategory
                       "N",    //The band filter condition
                       "   "); //The band standard image filter code
    
        nitf::BandInfo blueBand = subHeader.getBandInfo(2);
        blueBand.init("B",     //The band representation
                      " ",     //The band subcategory
                      "N",     //The band filter condition
                      "   ");  //The band standard image filter code
    
        std::vector<nitf::BandInfo> bands;
        bands.push_back(redBand);
        bands.push_back(greenBand);
        bands.push_back(blueBand);
    
        subHeader.setPixelInformation("INT",          //Pixel value type
                                      32,             //Number of bits/pixel
                                      32,             //Actual number of bits/pixel
                                      "R",            //Pixel justification
                                      "RGB",          //Image representation
                                      details.sensor, //Image category
                                      bands);         //Band information object list
    
        subHeader.getImageCompression().set("NC");
        subHeader.getImageId().set("EVE_IMAGE");
    
        /* SetBlocking info */
        subHeader.setBlocking(details.height, // The number of rows
                              details.width,  // The number of columns
                              details.height, // The number of rows/block
                              details.width,  // The number of columns/block
                              "P");           // Image mode
    
        writer.prepare(output, record);
        nitf::ImageWriter imageWriter = writer.newImageWriter(0);
        nitf::ImageSource imageSource;
    
        char* imgData = reinterpret_cast<char *>(imageData);
        size_t bufSize = details.width * details.height;
    
        for(int i=0; i < 3; i++)
        {
            nitf::MemorySource source(imgData,  // The memory buffer
                                      bufSize,  // The size of the buffer
                                      i,        // The number of bytes per pixel
                                      1,        // The start offset
                                      0);       // The amount of pixels to skip
    
            imageSource.addBand(source);
        }
    
        imageWriter.setWriteCaching(1);
        imageWriter.attachSource(imageSource);
    
        //
        //Write out the file.
        //
        writer.write();
    
     
  • Adam Sylvester
    Adam Sylvester
    2013-07-10

    Chuck,

    So when you say your input imagery is 32-bit RGB, is it four 8-bit bands with an alpha channel (RGBA)? Or what is the format? Take a look at Table A-2 in the NITF 2500C spec for allowable values. If it's RGBA, I would think IREP would be set to "MULTI" (which supports 2-9 bands), and then you'd just create a fourth band and push it on the same way you're doing the three currently. Also take a look at how your MemorySource is set up - if your input data is band interleaved by pixel (i.e. RGBARGBARGBA...), you'll want start = 0 and skip = 3 (you want to skip 3 extra pixels so you'll increment the pointer by a total of four). If your input data is band sequential (i.e. RRR...GGG...BBB...AAA), you'll want start = numPixels*i and skip = 0.

    -Adam

     
  • Chuck Shaw
    Chuck Shaw
    2013-07-10

    Ah ok, thats good information.. as image exploitation and the NITF format are complete new areas of development for me.

    To answer your question..I'm going to data dump here to see if something jumps out to me just as much as you. I have a larger EOP Frontend for an airborne platform developed in C++ and Qt. I currently produce snapshots of live video streams and open them in a image editor for operators to exploit. I'm (obviously) trying to output the exploited images as NITF.

    Now internally i'm using a QImage to hold the image data as its being manipulated. QImage is a hardware-independent image representation that allows direct access to the pixel data.

    When I load an image of any format (bmp, gif, jpg, jpeg, png, pbm, pgm, ppm, xbm, xpm), I automatically convert it into any format i want. I have the option of many but the following are the ones i have been trying to use:

    QImage::Format_RGB888 -- The image is stored using a 24-bit RGB format (8-8-8)
    QImage::Format_RGB32 -- The image is stored using a 32-bit RGB format (0xffRRGGBB)
    QImage::Format_ARGB32 -- The image is stored using a 32-bit ARGB format (0xAARRGGBB)
    QImage::Format_ARGB32_Premultiplied -- The image is stored using a premultiplied 32-bit ARGB format (0xAARRGGBB), i.e. the red, green, and blue channels are multiplied by the alpha component divided by 255. (If RR, GG, or BB has a higher value than the alpha channel, the results are undefined.) Certain operations (such as image composition using alpha blending) are faster using premultiplied ARGB32 than with plain ARGB32.

    It appears that for the RGB32 that I was using, it was actually three 8 bit bands for RGB, with an 8 bit padding.

    Each pixel stored in a QImage is represented by an integer. The size of the integer varies depending on the format. 32-bit images have no color table; instead, each pixel contains an QRgb value. There are three different types of 32-bit images storing RGB (i.e. 0xffRRGGBB), ARGB and premultiplied ARGB values respectively. In the premultiplied format the red, green, and blue channels are multiplied by the alpha component divided by 255.

    I'm currently calling a QImage::bits() function to retrieve the raw data, for which i'm passing into my NITRO-NITF based writeImage() function.

    I have not been able to find any documentation or discussions online that state whether the data representation is BIP, BSQ, or BLP at this point. So i'm just doing a bit of trial and error at the moment. (though it just dawned on me i can just go look at the QImage source myself and fine out. ;)

    Regardless:

    for RGB888 i've tried the following:

    for(int i=0; i < 3; i++)
    {
            nitf::MemorySource source(imgData,  // The memory buffer
                                      bufSize,  // The size of the buffer
                                      3,        // The number of bytes per pixel
                                      0,        // The start offset
                                      2);       // The amount of pixels to skip
    
            imageSource.addBand(source);
    }
    

    for ARGB32 i've tried the following:

    for(int i=0; i < 4; i++)
    {
            nitf::MemorySource source(imgData,  // The memory buffer
                                      bufSize,  // The size of the buffer
                                      4,        // The number of bytes per pixel
                                      0,        // The start offset
                                      3);       // The amount of pixels to skip
    
            imageSource.addBand(source);
    }
    

    Now following your suggestions (as i posted above), anytime I put anything other than 0 in the skip pixel function I get a crash/hang upon write. I have not hooked up the debugger to see exactly where it is having an issue yet. I'm assuming at this point that it may be buffer length/padding issue. I don't know of any other reason it would crash for any pixel skip value greater than zero.

    --chuck

     
  • Adam Sylvester
    Adam Sylvester
    2013-07-11

    Chuck,

    Don't have too much time to look at this currently, but in short, the numBytesPerPixel value should be 1 in your case (it's the # of bytes per pixel per band). For RGBRGBRGB... you'd want start=i and skip=2. For ARGBARGB... you'd want start=i+1 (to skip over A) and skip=3.

    The reason it was crashing with larger skip values was because it was multiplying by numBytesPerPixel when offsetting the pointer so it was going out of bounds.

    -Adam