AWARE [SYSTEMS] | Imaging expertise for the Delphi developer |
|||||||

TIFF and LibTiff Mailing List Archive | ||||||||

## LibTiff Mailing List
TIFF and LibTiff Mailing List Archive Contact
The TIFF Mailing List Homepage |
## Thread## 2017.04.03 17:05 "Reading images with bits per pixel not a multiple of 8", by Dinesh IyerHi 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 |