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

# Thread

# 2017.04.03 17:05 "[Tiff] 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