AWARE [SYSTEMS] Imaging expertise for the Delphi developer
AWare Systems, Imaging expertise for the Delphi developer, Home TIFF and LibTiff Mailing List Archive

LibTiff Mailing List

TIFF and LibTiff Mailing List Archive
April 2017

Previous Thread
Next Thread

Previous by Thread
Next by Thread

Previous by Date
Next by Date

Contact

The TIFF Mailing List Homepage
This list is run by Frank Warmerdam
Archive maintained by AWare Systems



Valid HTML 4.01!



Thread

2017.04.03 17:05 "Reading images with bits per pixel not a multiple of 8", by Dinesh Iyer
2017.04.03 17:50 "Re: Reading images with bits per pixel not a multiple of 8", by Bob Friesenhahn
2017.04.03 18:42 "Re: Reading images with bits per pixel not a multiple of 8", by Roger Leigh
2017.04.03 18:46 "Re: Reading images with bits per pixel not a multiple of 8", by Bob Friesenhahn
2017.04.04 14:00 "Re: Reading images with bits per pixel not a multiple of 8", by Dinesh Iyer
2017.04.04 15:14 "Re: Reading images with bits per pixel not a multiple of 8", by Bob Friesenhahn
2017.04.05 15:50 "Re: Reading images with bits per pixel not a multiple of 8", by Charles Auer

2017.04.03 17:05 "Reading images with bits per pixel not a multiple of 8", by Dinesh Iyer

Hi everyone,
I am trying to use libtiff to read TIFF files and I am looking for
clarification when it comes to the layout of images which have bits per
pixel that are non-standard such as 2, 4, 11 etc.

My understanding is that after calling TIFFReadEncodedStrip/Tile the data I
get back is packed i.e. one sample might have all its bits stored across a
byte-boundary. Is this true for all cases or does this apply only for
PackBits compression?

I was able to read 4-bit files using my code but when I tried to extend
this for a 11-bit file (yes, we have one in our test-suite), I got a
garbled image.

The image dimensions are:
height = 154, width = 287, RowsPerStrip = 20, bitsPerSample = 11,
SamplesPerPixel = 1.

So, the number of bytes in 1 row of a strip will be ceil(11*287/8) = 395.
Hence the number of bytes per strip will be 395*20 = 7900. This value,
7900, matches what TIFFStripSize returned.

My understanding of the layout is as below:

Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, ...

1st sample = Entire Byte1, Bit7,Bit6, Bit5 (of Byte2)
2nd sample: Bits4-0 (of Byte2),  Bits7-2 (of Byte3)
...

Is my understanding of the layout correct?

The code I am using is below:

// I am using the term slice in my code to mean either strip/tile as
appropriate.
uint32_T destSliceWidth = 287;
uint32_T destSliceHeight = 20;
uint16_T samplesPerPixel = 1;
uint16_T bitsPerSample = 11;

// Create a buffer to hold the appropriate number of bytes.
auto dataBuffer =
createBuffer<uint16_T>(destSliceWidth*destSliceLength*samplesPerPixel);

// readRawSlice calls TIFFReadEncodedStrip().
std::unique_ptr<uint8_T[]> dataSlice = readRawSlice();

// Read 2-bytes at a time. The readUnitSize logic below should handle this
correctly.
uint16_T* srcPtr = reinterpret_cast<uint16_T*>( dataSlice.get() );

uint16_T* destPtr = allocatedOutput<uint16_T>(destSliceWidth,
destSliceHeight, samplesPerPixel);

// This will evaluate to 395
size_t srcNumBytesPerRow = static_cast<size_t>(
                                std::ceil(
_imageInfo.sliceWidth*samplesPerPixel*bitsPerSample / 8.0 ) );

// Number of samples (not bits or bytes) in this strip
size_t destTotalNumberOfSamples =
destSliceLength*destSliceWidth*samplesPerPixel;

// Each read from the dataSlice will read 16-bits
uint16_T readUnitSize = sizeof(uint16_T)*8;    // In terms of bits

// Offset into the read-unit
uint16_T readUnitOffset = readUnitSize; // In terms of bits

// The actual computed 11-bit value of the sample
uint16_T srcSampleVal = 0;

for( size_t destCnt = 0; destCnt < destTotalNumberOfSamples; destCnt++ )
{
    // If end-of-row reached, then move 395*rowNum byes from the start of
dataSlice to get to the next row
    if( destCnt % _imageInfo.sliceWidth == 0)
    {
        srcPtr = reinterpret_cast<uint16_T*>( dataSlice.get()  + ( destCnt
/ _imageInfo.sliceWidth )*srcNumBytesPerRow );
        readUnitOffset = readUnitSize;
    }
    uint16_T destSampleVal = 0;

    // Indicates that 11-bits need to be grabbed to compute the value of
this sample completely.
    uint16_T reqNumBitsForSample = bitsPerSample;

    // If 16-bits have been completely processed, then read 16-bits more
from the source.
    if( readUnitOffset == readUnitSize )
    {
        srcSampleVal = *srcPtr++;
        readUnitOffset = 0;
    }

    // Loop until 11-bits have been obtained.
    while( reqNumBitsForSample != 0 )
    {
        // Determine how many bits are available to read in the current
16-bit value read in
        uint16_T numReqBitsAvailInReadUnit = std::min<uint16_T>(
reqNumBitsForSample, readUnitSize - readUnitOffset );

        // Create a bit-mask
        uint16_T bitMask = static_cast<uint16_T>( std::pow( 2.0,
                                                static_cast<double>(
readUnitSize - readUnitOffset ) ) - 1 );

        // Mask and shift the values suitably.
        uint16_T value = srcSampleVal & bitMask;
        value >>= (readUnitSize - (readUnitOffset +
numReqBitsAvailInReadUnit));

        // Add to already extracted bits
        destSampleVal |= (value << (reqNumBitsForSample -
numReqBitsAvailInReadUnit));

        // Updated the ofset and the number of bits required
        readUnitOffset += numReqBitsAvailInReadUnit;
        reqNumBitsForSample -= numReqBitsAvailInReadUnit;

        if( readUnitOffset == readUnitSize )
        {
            srcSampleVal = *srcPtr++;
            readUnitOffset = 0;
        }
    }

    *destPtr++ = destSampleVal;
}

Appreciate any help.

Dinesh