Discussion:
TiffReadEncodedStrip
Richard Nolde
2006-08-29 20:44:11 UTC
Permalink
I am writing a tiffcrop utility (borrowing heavily from tiffcp) that
will extract segments of a tiff file into a new file for use in OCR and
barcode recognition programs.
Currently, I am reading the entire image into memory with
TIFFReadEncodedStrip/Tile and then calculating the offsets into the
buffer to pull the desired sections out of the image. IF I am reading a
scanline I can assume that each call to TIFFReadScanline returns
IMAGEWIDTH bytes of data with possibly padded bits, but when I read a
series of encoded strips, I don't know how the ends of the scanline are
handled in the buffer that comes back. A cropped section of a scanline
from a bilevel image may not be a multiple of 8 pixels wide so it is
important to know whether a strip as returned by TIFFReadEncodedStrip
does the same thing or whether the last byte of a line in the image
could also contain the first bits of the next line. In other words is a
strip divided into scanlines on byte boundaries or is it a continuous
bit stream from the beginning of the image to the end?

From what I have read in the current man pages and programs in the
tools directory, I assume that I can simple compute the start of each
row as an offset from the beginning of the buffer. For 1 bit per sample
data with one sample per pixel, something like ImageWidth / 8 bytes
would be returned into the read buffer for each row in the image and the
offset would be (row * IMAGEWIDTH) / (8 /bps) * samples per pixel for
contiguous planes. This seems to imply that the last byte of a scanline
is padded out to fill that byte. Is this correct?

Richard Nolde

_______________________________________________
Tiff mailing list: ***@lists.maptools.org
http://lists.maptools.org/mailman/listinfo/tiff
http://www.remotesensing.org/libtiff/
Joris
2006-08-29 23:56:52 UTC
Permalink
Richard,

Richard Nolde wrote:
> From what I have read in the current man pages and programs in the
> tools directory, I assume that I can simple compute the start of each
> row as an offset from the beginning of the buffer. For 1 bit per
> sample data with one sample per pixel, something like ImageWidth / 8
> bytes would be returned into the read buffer for each row in the
> image and the offset would be (row * IMAGEWIDTH) / (8 /bps) *
> samples per pixel for contiguous planes. This seems to imply that
> the last byte of a scanline is padded out to fill that byte. Is this
> correct?

The last statement is correct, but the formulae before that is not.
Scanlines are padded out with fillbits up to a byte boundary.

The correct expression to use goes something like this. A scanline
length is (imagewidth*bitsperpixel+7)/8. Think about it, if imagewidth
is 1 and bitsperpixel is 1, that'll round things up to 1 byte per row.
If imagewidth is 8 and bitsperpixel is 1... still 1 byte per row.

So if you need an offset for (column,row), the byteoffset would be

row*((imagewidth*bitsperpixel+7)/8)+(column*bitsperpixel/8)

and if bitsperpixel is not a multiple of 8, you'll need a bit offset
too...

((column*bitsperpixel)%8)

In the above, I assume / to have its C meaning, i.e. rounding down
integer divide when operands are integers. Also, a 'bit offset' as per
above is quite the inverse of what we are used to regard as bit number,
i.e. bit offset 0 is the start of the byte, i.e. the get a single bit
value at bit offset 1, you'd need to shift 6 bits to the right.

There's a good reason for the above, it is not just arbitrary. The good
reason is that this exact padding is also in effect in uncompressed data
in TIFF. So the uncompressed decoder, really is a null operation.


Best regards,

Joris Van Damme
***@awaresystems.be
http://www.awaresystems.be/
Download your free TIFF tag viewer for windows here:
http://www.awaresystems.be/imaging/tiff/astifftagviewer.html

_______________________________________________
Tiff mailing list: ***@lists.maptools.org
http://lists.maptools.org/mailman/listinfo/tiff
http://www.remotesensing.org/libtiff/
Richard Nolde
2006-08-30 22:01:39 UTC
Permalink
_______________________________________________
Tiff mailing list: ***@lists.maptools.org
http://lists.maptools.org/mailman/listinfo/tiff
http://www.remotesensing.org/libtiff/
Joris
2006-08-31 00:36:02 UTC
Permalink
Richard,

Richard Nolde wrote:
> I would deduce from this that your row and column numbers are
> zero referenced as in C arrays.

That is correct. One could be tempted to say that very often, such
indexing leads to least complication in calculation, so it seems almost
'natural'.

> My usage, for reasons related to the code that defines the zones to
> be extracted, runs row from 1 to IMAGE_LENGTH and column from 1 to
> IMAGE_WIDTH so I have to subtract one in each case.

Yes, the correct way to deal with such coordinates, would be to replace
'row' with 'myrow-1' and 'column' with 'mycolumn-1' in the offset
calculations I mentioned earlier.

> The snippet
> below is the code to extract the data bytes from the input image
> and write them to an output image. My code now gets the right
> bytes, but I am confused by your comment on the bit offset.
> Assuming an image of 1658 pixels wide with 1 bit per sample, 1
> sample per pixel, there will be 207 "full bytes" and two extra
> bits. Will these last bits be in the highorder bits or loworder
> bits, ie 128 and 64 or 2 and 1

I myself am always consistently confused by words such as 'highorder'
and 'bigendian'. Reminds me of 'left' and 'right', couldn't keep those
apart either until I was a teenager and found a little trick. 'right'
and 'writing' both share the letter 'r'. Yes, that trick works in dutch
just like it does in english. ;-)

Now I'm 36, but I still haven't thought of a way to get 'high order'
and 'big endian' in my head... I always refer to Intel and Motorola byte
order. People think that's because I'm a TIFF freak. Well, I'm not about
to tell them the real reason is that I'm simply stupid. Whenever they
drag their big and little endians and low and high orders onto the
table, I just pull an emperor's clothes, and say 'wow, that's neat'. ;-)

Let's do the maths, hey, that'll make things clear. In your example,
1658 pixels with 1 bits per pixel, the row byte count is
(1658*1+7)/8=208.

Let's look at bit offset for pixel 0. It's (0*1)%8=0. With 'bit offset'
I mean indexing from left to right, just like we index bytes. This is
inverse to how we usually index them, the bit offset 0 equals the bit
usually named 7, the bit that presents the value 128.

If you want to go from bit offset as I defined it here, to actual pixel
value, you'll have to shift and mask. Assuming bitsperpixel is smaller
then 8, and 8 is a multiple of bitsperpixel, the shift count to the
right is 8-bitoffset-bitsperpixel, and the mask is
((1<<bitsperpixel)-1). So for pixel 0 in a row in your example,
byteoffset equals 0 (in that row), bitoffset equals 0 (in that byte),
you'll thus have to shift the value at that byte offset right 7 bits,
and bitwise and the result with 1.

Now let's do the calculation for your the two bits that confuse you.

pixel 1656 in a row
byteoffset = (1656*1)/8 = 207
bitoffset = (1656*1)%8 = 0
shiftcount = 8-0-1 = 7
mask = (1<<1)-1 = 1

pixel 1657 in a row
byteoffset = (1657*1)/8 = 207
bitoffset = (1657*1)%8 = 1
shiftcount = 8-1-1 = 6
mask = (1<<1)-1 = 1

Yes, pixel 0 will be in the 128 bit, ans so will pixel 8, and 16, and
24, and... and 1656.

> does the fill order matter when
> the image has been extracted by ReadEncodedStrip?

No, it does not. Fillordering happens after compression, before writing,
and defillordering happens after reading, before decompression. In other
words, fillorder applies to the compressed data, and is resolved already
before decompression, so long before the data is the way it is returned
by ReadEncodedStrip.

> I had assumed a
> continuous bit stream with the padding in the low order bits and
> planned on reading the trailing bits followed by zeros if it is the
> end of the line.

If your use of the word 'low order' is consistent with what you wrote
before, meaning you expect padding to go in the bits with 'binary
weights' 1, 2, 4, 8...x, and the last couple of pixel values in the bits
with 'binary weights' 128,64,32,16...x*2, that is correct.

> Of course, when cropping from within the line,
> the remaining bits need to be masked off and converted to fill bits
> too

Asuming you mean you may have to mask out some of the last bits on each
row to turn 'm into padding bits, yes, that is correct. Plus, you might
need to shift the complete row in order to crop, for example if you cut
away one column on the left side, and bitsperpixel is 1, you'll have to
shift the complete data line 1 bit to the 'left' (that's without 'r'
:-)).

> for (i = 0; i < crop->zones; i++)

Too tired right now, I'll have a look tomorrow if someone else hasn't by
then.


Best regards,

Joris Van Damme
***@awaresystems.be
http://www.awaresystems.be/
Download your free TIFF tag viewer for windows here:
http://www.awaresystems.be/imaging/tiff/astifftagviewer.html

_______________________________________________
Tiff mailing list: ***@lists.maptools.org
http://lists.maptools.org/mailman/listinfo/tiff
http://www.remotesensing.org/libtiff/
Loading...