PM!C:\Palm\mikal\memopad\memopad.dat 0 1 0 0 1 0XBusinessBusinessPersonalPersonalTutorialTutorial@File: concept-antialias.sgml [000] Anti-aliasing tododecribe 7}File: concept-ctm.sgml [000] CTM tododecribe !yFile: concept-grayscaleconvert.sgml [000] Grayscale conversion How do you convert color images to grayscale? Well, my instant answer when I first had to do this was to just average the red, green and blue values. That answer is wrong. The reality is that the human eye is much better at seeing some colors than others. To get an accurate grayscale representation, you need to apply different coefficients to the color samples. Appropraite coefficients are 0.299 for red, 0.587 for green and 0.114 for blue. Below I have included several pictures that illustrate this concept. The first figure is a color image Which may or may not give you joy, depending on if you are viewing this document in color or not , the second is the color image converted to grayscale without the coefficients applied, and the third image is a correct grayscale rendition.
The original image
An average of the color values for each pixel
A correct conversion to grayscale
You can see that the sensible algorithm is better, have a look at the shadows on the hill and the trees in the background. The contrast and darkness of the second picture is might better than the averaging algorithm... The source code to generate these example images may be found in the TIFF chapter.
|}]File: concept-pixel.sgml [000] Pixel Pixel A pixel is the lowest unit of raster image description -- in other words, a raster image is defined in terms of pixels. Example pixels can be seen in the discussion of rasters, elsewhere in this chapter. 1wFile: concept-raster.sgml [000] Rasters Raster Image formatraster The most common image format (and the focus of the majority of this tutorial) is raster image formats. Raster image formats are those which store the picture as a bitmap You can think of a bitmap as being simply an array of pixels. We'll talk more about this later describing the state of pixels, as opposed to recording the length and locations of primatives such as lines and curves. Common examples include the output of photoshop, the gimp, scanners and digital cameras. Effectively, your monitor and printer are raster output devices.
A sample raster image
Raster image formats include TIFF, GIF, PNG, and most of the other formats in this tutorial. If in doubt, assume that it's a raster format unless I tell you otherwise. If we zoom in enough on a portion of this image, we can see that it is made up of descrete elements -- the pixels. todofalls off page
Zooming in on a portion of the raster image
todofix_the_falling_off_the_page_problem The code to produce this zoomed image is presented in the TIFF chapter of this tutorial.
|File: concepts.sgml [000] Imaging concepts There are some core terms which are used throughout this tutorial. This section defines these terms -- it's probably therefore worth bookmarking this section for reference throughout the rest of the day. This section presents these things in alphabetical order, for ease of reference. Terminology We need to get some terminology out of the way before we can discuss much. Images are made up of pixels. In black and white imaging, the pixel has one of two values -- 0 or 1. This can be represented in a single bit. For grayscale and color images however, the pixel needs to be able to store a much greater range of values -- if a pixel was to have 255 levels of gray, then we would need 8 bits to store that pixel. Each of these values is called a sample. TIFF expresses the size of the value in a tag called TIFFTAG_BITSPERSAMPLE. This will be 1 for black and white, and some larger number for grayscale. For color images, we need to store even more information. For each pixel we will need to store a red, green, and blue value. Each of these values are stored in a separate 'sample'. Therefore, we will need to define TIFFTAG_SAMPLESPERPIXEL -- this will be 1 for black and white or grayscale, but will normally be 3 for color images. We also need to define the size of each sample, so you'll still need to set a value for TIFFTAG_BITSPERSAMPLE. insertall.shconcept-*.sgml Further reading ... g4File: concept-vector.sgml [000] Vector Vector Image formatvector Some images aren't raster images. These images are normally composed of primative drawing commands, such as draw a line, draw a circle, et cetera. The advantage of vector formats is that it is an exact representation of the image, they also scale much better. Examples of vector formats include Scalar Vector Graphics (SVG), Adobe Illustrator files, Windows Meta Files, and to a certain extent PDF documents. A plotter is a vector output device. í File: introduction.sgml [000] Introduction This document is the manual associated with my tutorial on imaging programming presented at the Australian Unix User's Group 2002 Winter Conference in Melbourne Australia. It is intended to serve as the basis for discussions during this day long tutorial, as well as being a reference for the attendees once they return to their every day lives. Please note that all the information in this tutorial is copyright, as described elsewhere in this document. About the author Michael has been working in the image processing field for several years, including a couple of years managing and developing large image databases for an Australian government department. He currently works for TOWER Software, who manufacture a world leading EDMS and Records Management package named TRIM. Michael is also the developer of Panda, an open source PDF generation API, as well as being the maintainer of the comp.text.pdf USENET frequently asked questions document. You can contact Michael at mikal@stillhq.com. Michael also has a whole bunch of code (most of which relates to imaging) at his website: http://www.stillhq.com Motivation for this tutorial This tutorial started life as a series of articles about the TIFF image format, which were published by IBM DeveloperWorks (http://www.ibm.com/developerworks) in April and June 2002. This logically grew into the tutorial you see before you today. This tutorial is based on my several years experience as an imaging developer, and the common mistakes that people seem to make over and over. Perhaps this tutorial will go some way to correcting some common misconceptions. Assumed knowledge There are some things which I assume you know, and which are outside the scope of this tutorial. C This tutorial discusses code. Almost all of the code discussed is written in C. It is therefore safe to assume that if you don't have a good working knowledge of C, then you're likely to get a lot less out of this tutorial as those who do know some C. On the other hand, don't worry about the more esoteric syntax, I'll explain this as we need it. It should also be noted that the code samples in this tutorial are not optimal. They have been written to be as readable as possible, and not nessesarily the most efficient possible. Please bear this in mind before blindly copying them. How to compile and link on your chosen operating system It is outside the scope of this document to teach you how to compile and link source code into an executable form on your chosen architecture and operating system. You will need to understand this before you will be able to use any of File: introduction.sgml [001] the code in this document. For those of you using gcc on a unix (or unix-like) operating system, then the following points might be all you need to know. If you need more information, then a http://www.google.com search will serve you well. Libraries are added to the link command using the -l command line option. For instance, to compile and link the source file foo, with the tiff library, you would use a command line along the lines of gcc foo.c -o foo -ltiff -lm. You need to include -lm almost always. When you compile a c program using gcc without specifying any libraries, you get a -lm for free. As soon as you start specifying any libraries (for instance in this case -ltiff), then you also need to explicitly specify the math library as well. You will almost certainly also need to add the library and include paths for the installed version of the relevant library to the compile command line as well. Directories are added to the library search path using the -L command line option. Include directories are added with the -I option. The makefiles included with the samples in this tutorial a probably a bad place to look for introductory compile examples, as they use automake and autoconf to try to detect where the various required libraries are installed... Credits There are many people who need to be thanked when one attempts a project of this size. I would specifically like to thank my wife and son, who put up with me being geeky so very often. todopicture of Andrew and Catherine I should also thank the following people: Doug Jackson (doug_jackson@citadel.com.au), for proof reading and getting me interested in this whole topic to start with Tony Green (greeno@bandcamp.tv), for occassional docbook wrangling Michael Smith (smith@xml-doc.org), for docbook hints How this document was produced This tutorial was written in Docbook SGML using xemacs. This was then converted into PDF using the jade SGML tools. Diagrams were developed in a combination of the gimp, killustrator, and custom developed code. License This document is covered by two licenses -- the license for the text of this document, and the license for the included source code. The license terms are set out below. License for text (OPL) text2db.pl.noneOPL License for source code (GPL)</�����������������wc���������������������File: introduction.sgml [002] title> <para> <execute><cmd>text2db.pl.none</cmd><input>GPL</input></execute> </para> </sect2> <sect2><title>License for the libtiff man pages Copyright (c) 1988-1997 Sam Leffler Copyright (c) 1991-1997 Silicon Graphics, Inc. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that (i) the above copyright notices and this permission notice appear in all copies of the software and related documentation, and (ii) the names of Sam Leffler and Silicon Graphics may not be used in any advertising or publicity relating to the software without the specific, prior written permission of Sam Leffler and Silicon Graphics. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ZFile: jpeg.sgml [000] JPEG ... s File: output.sgml [000] AUUG 2002: Imaging Tutorial MichaelStill Introduction This document is the manual associated with my tutorial on imaging programming presented at the Australian Unix User's Group 2002 Winter Conference in Melbourne Australia. It is intended to serve as the basis for discussions during this day long tutorial, as well as being a reference for the attendees once they return to their every day lives. Please note that all the information in this tutorial is copyright, as described elsewhere in this document. About the author Michael has been working in the image processing field for several years, including a couple of years managing and developing large image databases for an Australian government department. He currently works for TOWER Software, who manufacture a world leading EDMS and Records Management package named TRIM. Michael is also the developer of Panda, an open source PDF generation API, as well as being the maintainer of the comp.text.pdf USENET frequently asked questions document. You can contact Michael at mikal@stillhq.com. Michael also has a whole bunch of code (most of which relates to imaging) at his website: http://www.stillhq.com Motivation for this tutorial This tutorial started life as a series of articles about the TIFF image format, which were published by IBM DeveloperWorks (http://www.ibm.com/developerworks) in April and June 2002. This logically grew into the tutorial you see before you today. This tutorial is based on my several years experience as an imaging developer, and the common mistakes that people seem to make over and over. Perhaps this tutorial will go some way to correcting some common misconceptions. Assumed knowledge There are some things which I assume you know, and which are outside the scope of this tutorial. C This tutorial discusses code. Almost all of the code discussed is written in C. It is therefore safe to assume that if you don't have a good working knowledge of C, then you're likely to get a lot less out of this tutorial as those who do know some C. On the other hand, don't worry about the more esoteric syntax, I'll explain this as we need it. It should also be noted that the code samples in this tutorial are not optimal. They have been written to be as readable as possible, and not nessesarily the most efficient possible. Please bear this in mind before blindly copying them. How to compile and link on your chosen operating system It is outside the scope of this document to teach you how to compile and link source code into an executable form on your chosen architecture and operating system. You will need to understand this before you will be able to use any of the code in this document. For those of you using gcc on a unix (or unix-like) operating system, then the following points might be all you need to know. If you need more information, then a http://www.google.com search will serve you well. Libraries are added to the link command using the -l command line option. For instance, to compile and link the source file foo, with the tiff library, you would use a command line along the lines of gcc foo.c -o foo -ltiff -lm. You need to include -lm almost always. When you compile a c program using gcc without specifying any libraries, you get a -lm for free. As soon as you start specifying any libraries (for instance in this case -ltiff), then you also need to explicitly specify the math library as well. You will almost certainly also need to add the library and include paths for the installed version of the relevant library to the compile command line as well. Directories are added to the library search path using the -L command line option. Include directories are added with the -I option. The makefiles included with the samples in this tutorial a probably a bad place to look for introductory compile examples, as they use automake and autoconf to try to detect where the various required libraries are installed... Credits There are many people who need to be thanked when one attempts a project of this size. I would specifically like to thank my wife and son, who put up with me being geeky so very often.
picture of Andrew and Catherine
I should also thank the following people: Doug Jackson (doug_jackson@citadel.com.au), for proof reading and getting me interested in this whole topic to start with Tony Green (greeno@bandcamp.tv), for occassional docbook wrangling Michael Smith (smith@xml-doc.org), for docbook hints
How this document was produced This tutorial was written in Docbook SGML using xemacs. This was then converted into PDF using the jade SGML tools. Diagrams were developed in a combination of the gimp, killustrator, and custom developed code. License This document is covered by two licenses -- the license for the text of this document, and the license i> File: output.sgml [002] for the included source code. The license terms are set out below. License for text (OPL) License for source code (GPL) License for the libtiff man pages Copyright (c) 1988-1997 Sam Leffler Copyright (c) 1991-1997 Silicon Graphics, Inc. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that (i) the above copyright notices and this permission notice appear in all copies of the software and related documentation, and (ii) the names of Sam Leffler and Silicon Graphics may not be used in any advertising or publicity relating to the software without the specific, prior written permission of Sam Leffler and Silicon Graphics. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Imaging concepts There are some core terms which are used throughout this tutorial. This section defines these terms -- it's probably therefore worth bookmarking this section for reference throughout the rest of the day. This section presents these things in alphabetical order, for ease of reference. Terminology We need to get some terminology out of the way before we can discuss much. Images are made up of pixels. In black and white imaging, the pixel has one of two values -- 0 or 1. This can be represented in a single bit. For grayscale and color images however, the pixel needs to be able to store a much greater range of values -- if a pixel was to have 255 levels of gray, then we would need 8 bits to store that pixel. Each of these values is called a sample. TIFF expresses the size of the value in a tag called TIFFTAG_BITSPERSAMPLE. This will be 1 for black and white, and some larger number for grayscale. For color images, we need to store even more information. For each pixel we will need to store a red, green, and blue value. Each of these values are stored in a separate 'sample'. Therefore, we will need to define TIFFTAG_SAMPLESPERPIXEL -- this will be 1 for black and white or grayscale, but will normally be 3 for color images. We also need to define the size of each sample, so you'll still needbV File: output.sgml [003] to set a value for TIFFTAG_BITSPERSAMPLE. Anti-aliasing
decribe
CTM
decribe
Grayscale conversion How do you convert color images to grayscale? Well, my instant answer when I first had to do this was to just average the red, green and blue values. That answer is wrong. The reality is that the human eye is much better at seeing some colors than others. To get an accurate grayscale representation, you need to apply different coefficients to the color samples. Appropraite coefficients are 0.299 for red, 0.587 for green and 0.114 for blue. Below I have included several pictures that illustrate this concept. The first figure is a color image Which may or may not give you joy, depending on if you are viewing this document in color or not , the second is the color image converted to grayscale without the coefficients applied, and the third image is a correct grayscale rendition.
The original image
An average of the color values for each pixel
A correct conversion to grayscale
You can see that the sensible algorithm is better, have a look at the shadows on the hill and the trees in the background. The contrast and darkness of the second picture is might better than the averaging algorithm... The source code to generate these example images may be found in the TIFF chapter.
Pixel Pixel A pixel is the lowest unit of raster image description -- in other words, a raster image is defined in terms of pixels. Example pixels can be seen in the discussion of rasters, elsewhere in this chapter. Rasters Raster Image formatraster The most common image format (and the focus of the majority of this tutorial) is raster image formats. Raster image formats are those which store the picture as a bitmap You can think of a bitmap as being simply an array of pixels. We'll talk more about this later describing the state of pixels, as opposed to recording the length and locations of primatives such as lines and curves. Common examples include the output of photoshop, the gimp, scanners and digital cameras. Effectively, your monitori File: output.sgml [004] and printer are raster output devices.
A sample raster image
Raster image formats include TIFF, GIF, PNG, and most of the other formats in this tutorial. If in doubt, assume that it's a raster format unless I tell you otherwise. If we zoom in enough on a portion of this image, we can see that it is made up of descrete elements -- the pixels.
falls off page
Zooming in on a portion of the raster image
fix_the_falling_off_the_page_problem
The code to produce this zoomed image is presented in the TIFF chapter of this tutorial.
Vector Vector Image formatvector Some images aren't raster images. These images are normally composed of primative drawing commands, such as draw a line, draw a circle, et cetera. The advantage of vector formats is that it is an exact representation of the image, they also scale much better. Examples of vector formats include Scalar Vector Graphics (SVG), Adobe Illustrator files, Windows Meta Files, and to a certain extent PDF documents. A plotter is a vector output device. Further reading ...
Imaging theory Theory of color and grayscale storage The first thing that we need to understand to be able to support color and grayscale images is the format of the image data within memory. There are two main representations for color and grayscale images. I'll explain these by describing grayscale, and then extend it to color. Direct storage of pixel data If you think back to the way pixel information was stored in the black and white images from the previous article, the information was just in the strips. You can also do this with grayscale and color images. This representation of image data is quite inefficient, because for the scenario when the image has a solid background (for example), there are many pixels with the same value. If the pixel data is stored in the strips, then this value will waste a large amount of space. Thankfully, there is a more efficient way to store image data. Imagine a simple four color 24 bit per pixel image. If we build a lookup table of the four color values (the 24 bit values which represent those colors), then we just need to store the relevant entry number of the color in the image strip itself. This can be done in only File: output.sgml [005] two bits, instead of the full 24. The maths looks something like this: A 24 bit color image which is 1,000 by 1,000 pixels will take 24 million bits to store. The same image, if it was a four color image, would take 4,000,000 bits for the strip data, and 98 bits for the color table. Neither of these numbers includes header and footer information for the file format, and the numbers are for uncompressed bitmaps. The advantages of the lookup table should be obvious. There is a name for this style of lookup table, it is called a palette -- probably because of those things painters carry around. This concept works for grayscale images as well. The only difference is that the colors in the palette are not just shades of gray. TIFF With great power comes great responsibility -- Spiderman In this chapter we will discuss the Tagged Image File Format, which is one of the most common raster image formats for professional imaging -- especially scanned images and faxes. Unfortunately, it is not supported by web browsers, so isn't used as much as it might otherwise be. Introduction TIFF (Tagged Image File Format) is a raster image format which was originally produced by Aldus and Microsoft. Aldus was later acquired by Adobe, who manage the TIFF specification to this day. At the time of writing of this chapter, the current version of the TIFF specification is TIFF version 6.0. The TIFF specification can be downloaded from http://partners.adobe.com/asn/developer/pdfs/tn/TIFF6.pdf. The TIFF technical notes at http://partners.adobe.com/asn/developer/technotes/main.html Libtiff is a standard implementation of the TIFF specification, which is free and works on many operating systems. All of the examples in this chapter refer to libtiff. Installation The source for libtiff can be downloaded from http://www.libtiff.org/ -- the current version of the library being version 3.5.7 at the time of writing. This version can be downloaded from ftp://ftp.remotesensing.org/pub/libtiff, look for the files named either tiff-v3.5.7.tar.gz or tiff-v3.5.7.zip. Unix Once you have downloaded the tarball for the library as described above, then you need to follow the steps below to install libtiff Please note that there are binary packages for libtiff for most operating systems, so you might be best off installing one of those.. tar xvzf tiff-v3.5.7.tar.gz cd tiff-v3.5.7 ./configure make make install Not too hard at all... win32
windows_install_details
For the Visual Studio challenged, there is a precompiled version of libtiff for win32 available from http://www.stillhq.com.
make it so
The TIFF on disc format It is useful for use to know something about how TIFF images are laid out on disc. For a fuller discussion of this, have a look at the TIFF specification version 6, which is referenced in the further reading section of this chapter From page 13 onwards... . File header TIFF on disc: the file header Bytes Description 0 - 1 Byte order for this file: ll (ASCII) for little endian, or MM (ASCII) for big endian 2 - 3 Magic number = 42, in the byte order specified above 4 - 7 Offset in bytes to the first IFD IFD: Image File Directory
Image File Directory TIFF on disc: the image file directory Bytes Description 0 - 1 Number of entries in the IFD ??? The entries in the IFD (all are 12 bytes long) 4 bytes at end Offset in bytes to the next IFD (four zero bytes if this is the last IFD)
Image File Directory Entries TIFF on disc: an image file directory Bytes Description 0 - 1 Tag that identifies the entry. Have a look at tiff.h 2 - 3 Field type 4 - 7 Count of the number of type size fields used. For instance, if the type is an ASCII string, then this field will store the length of the string, including the NULL terminating byte that C strings have. The count does not include padding (if any). 8 - 11 The number of bytes inside the file thapW File: output.sgml [007] t the value is stored at. Because the value must be stored on a word boundary, it will always be an even number. This file offset may point anywhere in the file, even after the image data.
These entries have a type associated with them, possible types are: 1: BYTE 8 bit unsigned integer 2: ASCII 8 bit byte that contains a 7 bit ASCII code 3: SHORT 16 bit (2 byte) unsigned integer 4: LONG 32 bit (4 byte) unsigned integer 5: RATIONAL Two LONGs: the first represents the numerator of a fraction; the second, the denominator TIFF version 6 added the following fields: 6: SBYTE An 8 bit signed integer 7: UNDEFINED An 8 bit byte that may contain anything 8: SSHORT A 16 bit (2 byte) signed integer 9: SLONG A 32 bit (4 byte) signed integer 10: SRATIONAL Two SLONGs: the first represents the numerator of a fraction; the second, the denominator 11: FLOAT Singled precision (4 byte) IEEE format 12: DOUBLE Double precision (8 byte) IEEE format Sign is implemented using two's complement notation. New field types may be added later, although it seems unlikely at this stage, given the TIFF specification hasn't changed in quite a long time. Image readers should ignore types they don't understand You need not worry about this, because libtiff worries about all of this for you. .
Possible field entries Discussing the many different possible field entries is out of the scope of this document. Refer to tiff.h and the TIFF specification for more information. So where's the image data? Interestingly, the image data itself is just stored as another tag value... The tag value StripOffsets stores where in the file the image data strips start. The tag value StripByteCounts stores the size of each strip.
Coding for TIFF can be hard Most file format specifications define some basic rules for the representation of the file. For instance, PNG (a competitor to TIFF) documents are always big endian. TIFF doesn't mandate things like this though, here is a list of some of the seemingly basic things that it doesn't define: The byte order -- big endian, or little endian The fill order of the bit within the image bytes -- most sign& File: output.sgml [008] ificant bit first, or least significant The meaning of a given pixel value for black and white -- is 0 black, or white? ...and so on This means that creating a TIFF can be very easy, because it is rare to have to do any conversion of the data that you already have. It does mean, on the other hand, that being able to read in random TIFFs created by other applications can be very hard -- you have to code for all these possible combinations in order to be reasonably certain of having a reliable product. So how do you write an application which can read in all these different possible permutations of the TIFF format? The most important thing to remember is to never make assumptions about the format of the image data you are reading in. Writing Black and White TIFF files The first thing I want to do is show you how to write a TIFF file out. We'll then get onto how to read a TIFF file back into your program. Infrastructure for writing It is traditional for bitmaps to be represented inside your code with an array of chars. This is because on most operating systems, a char maps well to one byte. In the block of code below, we will setup libtiff, and create a simple buffer which contains an image which we can then write out to disc. #include <stdio.h> #include <tiffio.h> int main (int argc, char *argv[]) { char buffer[32 * 9]; } Code: /home/mikal/opensource/tiff-bw-write/write-infrastructure.c The code above is pretty simple. All you need to use libtiff is to include the tiffio.h header file. The char buffer that we have defined here is going to be our black and white image, so we should define one of those next... Writing the image To make up for how boring that example was, I am now pleased to present you with possibly the worst picture of the Sydney Harbor Bridge ever drawn. In the example below, the image is already in the image buffer, and all we have to do is save it to the file on disc. The example first opens a TIFF image in write mode, and then places the image into that file. Please note, that for clarity I have omitted the actual hex for the image, this is available in the download version of this code for those who are interested. #include <stdio.h> #include <tiffio.h> int main(int argc, char *argv[]){ // Define an image -- this is 32 pixels by 9 pixels char buffer[25 * 144] = { ...boring hex omitted... }; TIFF *image; // Open the TIFF file if((image = TIFFOpen("output.tif", "w")) == NULL){ printf("Could not open output.tif for writing\n"); exit  File: output.sgml [009] (42); } // We need to set some values for basic tags before we can add any data TIFFSetField(image, TIFFTAG_IMAGEWIDTH, 25 * 8); TIFFSetField(image, TIFFTAG_IMAGELENGTH, 144); TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1); TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 144); TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4); TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE); TIFFSetField(image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(image, TIFFTAG_XRESOLUTION, 150.0); TIFFSetField(image, TIFFTAG_YRESOLUTION, 150.0); TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); // Write the information to the file TIFFWriteEncodedStrip(image, 0, buffer, 25 * 144); // Close the file TIFFClose(image); } Code: /home/mikal/opensource/tiff-bw-write/write-nohex.c There are some interesting things to note in this example. The most interesting of these is that the output image will not display using the xview command on my Linux machine. In fact, I couldn't find an example of a group 4 fax compressed black and white image which would display using that program. See the sidebar for more detail. Problems with xview Xview is part of the xloadimage package written by Jim Frost, which comes with X windows. It's a good example of how hard it can be to handle TIFF images well. If you have trouble viewing the output of the sample code, then try using some other program, like the GIMP. The sample code shows the basics of using the libtiff API. The following interesting points should be noted... The buffers presented to and returned from libtiff each contain 8 pixels in a single byte. This means that you have to be able to extract the pixels you are interested in. The use of masks, and the right and left shift operators come in handy here. The TIFFOpen function is very similar to the fopen function we are all familiar with. We need to set the value for quite a few fields before we can start writing the image out. These fields give libtiff information about the size and shape of the image, as well as the way that data will be compressed within the image. These fields need to be set before you can start handing image data to libtiff. There are many more fields for which a value could be set, I have used close to the bar minimum in this example. TIFFWriteEncodedStrip is the function call which actually inserts the image into the file. This call inserts uncompressed image data into the file. This means that libtiff will compress the image data for you before writing it to tV File: output.sgml [010] he file. If you have already compressed data, then have a look at the TIFFWriteRawStrip instead. Finally, we close the file with TIFFClose. More information about the libtiff function calls If you need more information about any of the libtiff function calls mentioned in this chapter, then checkout the extensive man pages which come with the library. Remember that case is important with man pages, so you need to get the case in the function names right -- it's TIFFOpen, not tiffopen.
The Sydney Harbor Bridge, by Michael Still
Reading Black and White TIFF files Reading TIFF files reliably is much harder than writing them. The issue that complicates reading black and white TIFF images the most is the several different storage schemes which are possible within the TIFF file itself. libtiff doesn't hold your hand much with these schemes, so you have to be able to handle them yourself. The three schemes TIFF supports are single stripped images, stripped images, and tiled images. A single strip image is as the name suggests -- a special case of a stripped image. In this case, all of the bitmap is stored in one large block. I have experienced reliability issues with images which are single strip on Windows machines. The general recommendation is that no one strip should take more than 8 kilobytes uncompressed which with black and white images limits us to 65,536 pixels in a single strip. A multiple strip image is where horizontal blocks of the image are stored together. More than one strip is joined vertically to make the entire bitmap. Figure 2 shows this concept. A tiled image is like your bathroom wall, it is composed of tiles. This representation is show in Figure 3, and is useful for extremely large images -- this is especially true when you might only want to manipulate a small portion of the image at any one time.
The Sydney Harbor Bridge, in strips
The Sydney Harbor Bridge, in tiles
Tiled images are comparatively uncommon, so I will focus on stripped images in this chapter. Remember as we go along, that the single stripped case is merely a subset of a multiple strip images. Infrastructure for reading The most important thing to remember when reading in TIFF images is to be flexible. The example below has the same basic concepts as the writing example above, with the major difference being that it ne File: output.sgml [011] eds to deal with many possible input images. Apart from stripping and tiling, the most important thing to remember to be flexible about is photo metric interpretation. Luckily, with black and white images there are only two photo metric interpretations to worry about (with color and to a certain extent gray scale images there are many more). What is photo metric interpretation? Well, the representation of the image in the buffer is really a very arbitrary thing. I might code my bitmaps so that 0 means black (TIFFTAG_MINISBLACK), whilst you might find black being 1 (TIFFTAG_MINISWHITE) more convenient. TIFF allows both, so our code has to be able to handle both cases. In the example below, I have assumed that the internal buffers need to be in MINISWHITE, so we will convert images which are in MINISBLACK. The other big thing to bear in mind is fill order (whether the first bit in the byte is the highest value, or the lowest). The example below also handles both of these correctly. I have assumed that we want the buffer to have the most significant bit first. TIFF images can be either big endian or little endian, but libtiff handles this for us. Thankfully, libtiff also supports the various compression algorithms without you having to worry about those. These are by far the scariest area of TIFF, so it is still worth your time to use libtiff. #include <stdio.h> #include <tiffio.h> int main(int argc, char *argv[]){ TIFF *image; uint16 photo, bps, spp, fillorder; uint32 width; tsize_t stripSize; unsigned long imageOffset, result; int stripMax, stripCount; char *buffer, tempbyte; unsigned long bufferSize, count; // Open the TIFF image if((image = TIFFOpen(argv[1], "r")) == NULL){ fprintf(stderr, "Could not open incoming image\n"); exit(42); } // Check that it is of a type that we support if((TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bps) == 0) || (bps != 1)){ fprintf(stderr, "Either undefined or unsupported number of bits per sample\n"); exit(42); } if((TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp) == 0) || (spp != 1)){ fprintf(stderr, "Either undefined or unsupported number of samples per pixel\n"); exit(42); } // Read in the possibly multile strips stripSize = TIFFStripSize (image); stripMax = TIFFNumberOfStrips (image); imageOffset = 0; bufferSize = TIFFNumberOfStrips (image) * stripSize; if((buffer = (char *) malloc(bufferSize)) == NULL){ fprintf(stderr, "Could not allocate enough memory for the uncompressed image\n"); exit(42); } for (stripCount = 0; stripCount < stripMax; stripCount++){ if((result = TIFFReadEncodedStrip (image, stripCount, buffer + imageOffset, stripSize)) == -1){ fprintf(stderr, "Read error on input strip number %d\n", stripCount); exit(42); } imageOffset += result; } // Deal with photometric File: output.sgml [012] interpretations if(TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &photo) == 0){ fprintf(stderr, "Image has an undefined photometric interpretation\n"); exit(42); } if(photo != PHOTOMETRIC_MINISWHITE){ // Flip bits printf("Fixing the photometric interpretation\n"); for(count = 0; count < bufferSize; count++) buffer[count] = ~buffer[count]; } // Deal with fillorder if(TIFFGetField(image, TIFFTAG_FILLORDER, &fillorder) == 0){ fprintf(stderr, "Image has an undefined fillorder\n"); exit(42); } if(fillorder != FILLORDER_MSB2LSB){ // We need to swap bits -- ABCDEFGH becomes HGFEDCBA printf("Fixing the fillorder\n"); for(count = 0; count < bufferSize; count++){ tempbyte = 0; if(buffer[count] & 128) tempbyte += 1; if(buffer[count] & 64) tempbyte += 2; if(buffer[count] & 32) tempbyte += 4; if(buffer[count] & 16) tempbyte += 8; if(buffer[count] & 8) tempbyte += 16; if(buffer[count] & 4) tempbyte += 32; if(buffer[count] & 2) tempbyte += 64; if(buffer[count] & 1) tempbyte += 128; buffer[count] = tempbyte; } } // Do whatever it is we do with the buffer -- we dump it in hex if(TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width) == 0){ fprintf(stderr, "Image does not define its width\n"); exit(42); } for(count = 0; count < bufferSize; count++){ printf("%02x", (unsigned char) buffer[count]); if((count + 1) % (width / 8) == 0) printf("\n"); else printf(" "); } TIFFClose(image); } Code: /home/mikal/opensource/tiff-bw-read/read.c This code works by first opening the image and checking that it is one that we can handle. It then reads in all the strip for the image, and appends them together in one large memory block. If required, it also flips bits until the photo metric interpretation the one we handle, and deals with having to swap bits if the fill order is wrong. Finally, our sample outputs the image as a series of lines composed of hex values. Remember that each of the values represents 8 pixels in the actual image. Compression algorithms in libtiff There are several compression algorithms available within libtiff. How do you select which one is right for your imaging needs? Please note that all the tags in this table should be preceded by COMPRESSION, for example COMPRESSION_CCITTFAXG4... Libtiff compression algorithms Compression algorithm Well suited for TIFFTAG CCITT Group 4 Fax and Group 3 Fax If your coding for black and white images, then you're probably usin s File: output.sgml [013] g the CCITT fax compression methods. These compression algorithms don't support color. _CCITTFAX3, _CCITTFAX4 JPEG JPEG compression is great for large images such as photos. However, the compression is normally lossy (in that image data is thrown away as part of the compression process). This makes JPEG very poor for compressing text which needs to remain readable. The other thing to bear in mind is that the loss is accumulative -- see the loss section below for more information about this. _JPEG LZW This is the compression algorithm used in GIF images. Because of the licensing requirements from Unisys, support for this compression codec has been removed from libtiff. There are patches available if you would like to add it back, but the majority of programs your code will integrate with no linger support LZW. _LZW Deflate This is the gzip compression algorithm, which is also used for PNG. It is the compression algorithm I would recommend for color images. _DEFLATE
Accumulating loss? Why does the loss in lossy compression algorithms such as JPEG accumulate? Imagine that you compress an image using JPEG. You then need to add say a bar code to the image, so you uncompress the image, add the bar code, and recompress it. When the recompression occurs, then a new set of loss is introduced. You can imagine that if you do this enough, then you'll end up with an image which is a big blob. Whether this is a problem depends on the type of your data. To test how much of a problem this is, I wrote a simple libtiff program which repeatedly uncompresses and recompresses an image. What I found was that with pictures, the data is much more resilient to repeated compression.
The picture before we compressed it
The sample text before we compressed it
The code I used had a 'quality' rating of 25% on the JPEG compression, which is a way of tweaking the loss of the compression algorithm. The lower the quality, the higher the compression ratio. The default is 75%.
The picture after it has been recompressed 200 times
The text after it has been recompressed 200 times
The code for the repeated compression example is at the end of this chapter.
Writing a color image It time to show you  File: output.sgml [014] how to write a color image to disc. Remember that this is a simple example, which can be elaborated on greatly. #include <tiffio.h> #include <stdio.h> int main(int argc, char *argv[]){ TIFF *output; uint32 width, height; char *raster; // Open the output image if((output = TIFFOpen("output.tif", "w")) == NULL){ fprintf(stderr, "Could not open outgoing image\n"); exit(42); } // We need to know the width and the height before we can malloc width = 42; height = 42; if((raster = (char *) malloc(sizeof(char) * width * height * 3)) == NULL){ fprintf(stderr, "Could not allocate enough memory\n"); exit(42); } // Magical stuff for creating the image // ... // Write the tiff tags to the file TIFFSetField(output, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(output, TIFFTAG_IMAGELENGTH, height); TIFFSetField(output, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); TIFFSetField(output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(output, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(output, TIFFTAG_SAMPLESPERPIXEL, 3); // Actually write the image if(TIFFWriteEncodedStrip(output, 0, raster, width * height * 3) == 0){ fprintf(stderr, "Could not write image\n"); exit(42); } TIFFClose(output); } Code: /home/mikal/opensource/tiff-color-write/write.c You can see from this code some of the things that we have discussed in theory. The image has three samples per pixel, each of eight bits. This means that the image is a 24 bit RGB image. If this was a black and white or gray scale image, then this value would be one. The tag PHOTOMETRIC_RGB says that the image data is stored within the strips themselves (as opposed to being paletted) -- more about this in a second. Other values for samples per pixel? In this example, we have three samples per pixel. If this was a black and white image, or a gray scale image, then we would have one sample per pixel. There are other valid values for here as well -- for instance, sometimes people will store a transparency value for a given pixel, which is called an alpha channel. This would result in having four samples per pixel. It is possible to have an arbitrary number of samples per pixel, which is good if you need to pack in extra information about a pixel. Note that doing this can break image viewers which make silly assumptions -- I once had to write code for a former employer to strip out alpha channels and the like so that their PDF generator wouldn't crash. The other interesting thing to discuss here is the planar configuration of the image. Here we have specified PLANARCONFIG_CONTIG, which means that the red green and blue information for a given pixel is grouped together in the strips of image data.- File: output.sgml [015] The other option is PLANARCONFIG_SEPARATE, where the red samples for the image are stored together, then the blue samples, and finally the green samples. Writing a paletted color image So how do we write a paletted version of this image? Well, libtiff makes this really easy -- all we need to do is change the value of TIFFTAG_PHOTOMETRIC to PHOTOMETRIC_PALETTE. It's not really worth including an example in this chapter, given it's a one word change. Reading a color image Now all we have to do is work out how to read other people's color and gray scale images reliably, and we're home free. Initially I was very tempted to not tell you about the TIFFReadRGBAStrip() and TIFFReadRGBBSTile() calls, which hide some of the potential ugliness from the caller. These functions have some limitations I'm not astoundingly happy with. To quote the TIFFReadRGBAStrip() man page: TIFFReadRGBAStrip reads a single strip of a strip-based image into memory, storing the result in the user supplied RGBA raster. The raster is assumed to be an array of width times rowsperstrip 32-bit entries, where width is the width of the image (TIFFTAG_IMAGEWIDTH) and rowsperstrip is the maximum lines in a strip (TIFFTAG_ROWSPERSTRIP). The strip value should be the strip number (strip zero is the first) as returned by the TIFFComputeStrip function, but always for sample 0. Note that the raster is assume to be organized such that the pixel at location (x,y) is raster[y*width+x]; with the raster origin in the lower-left hand corner of the strip. That is bottom to top organization. When reading a partial last strip in the file the last line of the image will begin at the beginning of the buffer. Raster pixels are 8-bit packed red, green, blue, alpha samples. The macros TIFFGetR, TIFFGetG, TIFFGetB, and TIFFGetA should be used to access individual samples. Images without Associated Alpha matting information have a constant Alpha of 1.0 (255). See the TIFFRGBAImage(3T) page for more details on how various image types are converted to RGBA values. NOTES Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). Palette image colormaps that appear to be incorrectly written as 8-bit values are automatically scaled to 16-bits. TIFFReadRGBAStrip is just a wrapper around the more general TIFFRGBAImage(3T) File: output.sgml [016] facilities. It's main advantage over the similar TIFFReadRGBAImage() function is that for large images a single buffer capable of holding the whole image doesn't need to be allocated, only enough for one strip. The TIFFReadRGBATile() function does a similar operation for tiled images. There are a couple of odd things about this function -- it defines (0, 0) to be in a different location from all the other code that we have been writing. In all the previous code we have written, the (0, 0) point has been in the top left of the image. This call defines (0, 0) to be in the bottom left. The other limitation to be aware of is that not all valid values for bits per sample are supported. If you find these quirks unacceptable, then remember that you can still use TIFFReadEncodedStrip() in the same manner that we did for the black and white images in the previous chapter... #include <stdio.h> #include <tiffio.h> int main(int argc, char *argv[]){ TIFF *image; uint32 width, height, *raster; tsize_t stripSize; unsigned long imagesize, c, d, e; // Open the TIFF image if((image = TIFFOpen(argv[1], "r")) == NULL){ fprintf(stderr, "Could not open incoming image\n"); exit(42); } // Find the width and height of the image TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height); imagesize = height * width + 1; if((raster = (uint32 *) malloc(sizeof(uint32) * imagesize)) == NULL){ fprintf(stderr, "Could not allocate enough memory\n"); exit(42); } // Read the image into the memory buffer if(TIFFReadRGBAStrip(image, 0, raster) == 0){ fprintf(stderr, "Could not read image\n"); exit(42); } // Here I fix the reversal of the image (vertically) and show you how to get the color values from each pixel d = 0; for(e = height - 1; e != -1; e--){ for(c = 0; c < width; c++){ // Red = TIFFGetR(raster[e * width + c]); // Green = TIFFGetG(raster[e * width + c]); // Blue = TIFFGetB(raster[e * width + c]); } } TIFFClose(image); } Code: /home/mikal/opensource/tiff-color-read/read.c Storing TIFF data in places other than files All of these examples that I have included to this point have read and written with files. There are many scenarios when you wouldn't want to store your image data in a file, but would still want to use libtiff and TIFF. For example, you might have customer pictures for id cards, and these would be stored in a database. The example which I am most familiar with is PDF documents. In PDF files, you can embed images into the document. These images can be in a subset of TIFF if desired, and TIFF is quite clearly the choice for black and white images. An expanded example If you need more information about hooking the file input and output functions within libtiff than this chapter allows, then have a look at the images.c file in Panda, my PDF library. The web pages for Panda can be found at http://www.stillhq.com http://www.stillhq.com/cgi-bin/getpage?area=panda<page=index.htm to be exact. . libtiff allows you to replace the file input and output functions in the library with your own. This is done with the TIFFClientOpen() method. Here's an example (please note this code wont compile, and is just to describe the main concepts:
make_this_example_better
// Please note that this code wont compile, and is intended to only show you // the sturcture of TIFFClient* calls #include <tiffio.h> #include <pthread.h> // Function prototypes static tsize_t libtiffDummyReadProc (thandle_t fd, tdata_t buf, tsize_t size); static tsize_t libtiffDummyWriteProc (thandle_t fd, tdata_t buf, tsize_t size); static toff_t libtiffDummySeekProc (thandle_t fd, toff_t off, int i); static int libtiffDummyCloseProc (thandle_t fd); // We need globals because of the callbacks (they don't allow us to pass state) char *globalImageBuffer; unsigned long globalImageBufferOffset; // This mutex keeps the globals safe by ensuring only one user at a time pthread_mutex_t convMutex = PTHREAD_MUTEX_INITIALIZER; TIFF *conv; // Lock the mutex pthread_mutex_lock (&convMutex); globalImageBuffer = NULL; globalImageBufferOffset = 0; // Open the dummy document (which actually only exists in memory) conv = TIFFClientOpen ("dummy", "w", (thandle_t) - 1, libtiffDummyReadProc, libtiffDummyWriteProc, libtiffDummySeekProc, libtiffDummyCloseProc, NULL, NULL, NULL); // Setup the image as if it was any other tiff image here, including setting tags // Actually do the client open TIFFWriteEncodedStrip (conv, 0, stripBuffer, imageOffset); // Unlock the mutex pthread_mutex_unlock (&convMutex); //... /////////////////// Callbacks to libtiff static tsize_t libtiffDummyReadProc (thandle_t fd, tdata_t buf, tsize_t size) { // Return the amount of data read, which we will always set as 0 because // we only need to be able to write to these in-memory tiffs return 0; } static tsize_t libtiffDummyWriteProc (thandle_t fd, tdata_t buf, tsize_t size) { // libtiff will try to write an 8 byte header into the tiff file. We need // to ignore this because PDF does not use it... if ((size == 8) && (((char *) buf)[0] == 'I') && (((char *) buf)[1] == 'I') && (((char *) buf)[2] == 42)) { // Skip the header -- little endian } else if ((size == 8) && (((char *) buf)[0] == 'M') && (((char *) buf)[1] == 'M') &&y (((char *) buf)[2] == 42)) { // Skip the header -- big endian Bo File: output.sgml [018] } else { // Have we done anything yet? if (globalImageBuffer == NULL) if((globalImageBuffer = (char *) malloc (size * sizeof (char))) == NULL) { fprintf(stderr, "Memory allocation error\n"); exit(42); } // Otherwise, we need to grow the memory buffer else { if ((globalImageBuffer = (char *) realloc (globalImageBuffer, (size * sizeof (char)) + globalImageBufferOffset)) == NULL) fprintf(stderr, "Could not grow the tiff conversion memory buffer\n"); exit(42); } // Now move the image data into the buffer memcpy (globalImageBuffer + globalImageBufferOffset, buf, size); globalImageBufferOffset += size; } return (size); } static toff_t libtiffDummySeekProc (thandle_t fd, toff_t off, int i) { // This appears to return the location that it went to return off; } static int libtiffDummyCloseProc (thandle_t fd) { // Return a zero meaning all is well return 0; } Code: /home/mikal/opensource/tiff-client/client.c
Storing more than one image inside a TIFF A common request is to be able to store more than one image inside a single TIFF file. This is done with multiple directories, which is one of the reasons they're described at the start of this chapter. Before embarking on a multiple image per TIFF solution, it is important to remember that the TIFF specification doesn't require TIFF image viewers to implement support for multiple images per TIFF, and many viewers in fact do not Refer to page 16 of the TIFF specification, version 6. . Anyway, now that I've nagged you, putting multiple images into one TIFF file is really easy, just call the TIFFWriteDirectory(), which writes out the current image so you can move onto the next one. The following sample program is an example of this... #include <stdio.h> #include <tiffio.h> #include <unistd.h> #include <string.h> void usage (char *, int); int main (int argc, char *argv[]) { TIFF *input, *output; uint32 width, height; tsize_t stripSize, stripNumber; unsigned long x, y; char *inputFilename = NULL, *outputFilename = NULL, *raster, *roff, optchar; int count = 4, i; ///////////////////////////////////////////////////////////////////////////// // Parse the command line options while ((optchar = getopt (argc, argv, "i:o:c:")) != -1) { switch (optchar) { case 'i': inputFilename = (char *) strdup (optarg); break; case 'o': outputFilename = (char *) strdup (optarg); break; case 'c': count = atoi(optarg); break; default: usage(argv[0], 0); break; } } // Open the input TIFF image if ((inputFilename == NUh File: output.sgml [019] LL) || (input = TIFFOpen (inputFilename, "r")) == NULL) { fprintf (stderr, "Could not open incoming input %s\n", inputFilename); usage (argv[0], 42); } // Open the output TIFF if ((outputFilename == NULL) || (output = TIFFOpen (outputFilename, "w")) == NULL) { fprintf (stderr, "Could not open outgoing input %s\n", outputFilename); usage (argv[0], 42); } // Find the width and height of the input TIFFGetField (input, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField (input, TIFFTAG_IMAGELENGTH, &height); ///////////////////////////////////////////////////////////////////////////// // Grab some memory if ((raster = (char *) malloc (sizeof (char) * width * height * 3)) == NULL) { fprintf (stderr, "Could not allocate enough memory for input raster\n"); exit (42); } ///////////////////////////////////////////////////////////////////////////// // Read the input into the memory buffer // todo: I couldn't use TIFFReadRGBAStrip here, because it gets confused stripSize = TIFFStripSize (input); roff = raster; for (stripNumber = 0; stripNumber < TIFFNumberOfStrips (input); stripNumber++) { roff += TIFFReadEncodedStrip (input, stripNumber, roff, stripSize); } ///////////////////////////////////////////////////////////////////////////// // Write the image buffer to the file, we do this c times for(i = 0; i < count; i++){ printf("."); fflush(stdout); // todo: We need to copy tags from the input image to the output image TIFFSetField (output, TIFFTAG_IMAGEWIDTH, width); TIFFSetField (output, TIFFTAG_IMAGELENGTH, height); TIFFSetField (output, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); TIFFSetField (output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField (output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField (output, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField (output, TIFFTAG_SAMPLESPERPIXEL, 3); // todo: balance this off with having 8 kb per strip... TIFFSetField (output, TIFFTAG_ROWSPERSTRIP, 100000); if (TIFFWriteEncodedStrip (output, 0, raster, width * height * 3 * sizeof (char)) == 0) { fprintf (stderr, "Could not write the output image\n"); exit (42); } // Flush this subfile and move onto the next one if(TIFFWriteDirectory(output) == 0){ fprintf(stderr, "Error writing subfile %d\n", i); exit(44); } } printf("\n"); // Cleanup TIFFClose (input); TIFFClose (output); free (raster); } void usage (char *cmd, int exitamt) { fprintf (stderr, "Bad command line arguements...\n\n"); fprintf (stderr, "Usage: %s -i <inputfile> -o <outputfile> -c <count>\n", cmd); exit (exitamt); } Code: /home/mikal/opensource/tiff-directories/create.c
Four pictures of my son Andrew
quality_of_picture
Reading more than one image inside a TIFF We should also probably know how to get to these images once we have more than one image inside a single TIFF file...
convert_will_create_multiple_files
more!
#include <stdio.h> #include <tiffio.h> #include <unistd.h> #include <string.h> void usage (char *, int); int main (int argc, char *argv[]) { TIFF *input, *output; uint32 width, height; tsize_t stripSize, stripNumber; unsigned long x, y; char *inputFilename = NULL, *outputFilename = NULL, outputFilenameActual[200], *raster, *roff, optchar; int count; ///////////////////////////////////////////////////////////////////////////// // Parse the command line options while ((optchar = getopt (argc, argv, "i:o:")) != -1) { switch (optchar) { case 'i': inputFilename = (char *) strdup (optarg); break; case 'o': outputFilename = (char *) strdup (optarg); break; default: usage(argv[0], 0); break; } } // Check the output parent name if(outputFilename == NULL){ fprintf(stderr, \ "You need to specify a name for the series of output files\n"); usage(argv[0], 42); } // Open the input TIFF image if ((inputFilename == NULL) || (input = TIFFOpen (inputFilename, "r")) == NULL) { fprintf (stderr, "Could not open incoming input %s\n", inputFilename); usage (argv[0], 42); } ///////////////////////////////////////////////////////////////////////////// // Grab a sub file from the input image and move it to a separate file. We do // this forever (until we break down below)... for(count = 0;; count++){ // Find the width and height of the input TIFFGetField (input, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField (input, TIFFTAG_IMAGELENGTH, &height); /////////////////////////////////////////////////////////////////////////// // Grab some memory if ((raster = (char *) malloc (sizeof (char) * width * height * 3)) == NULL) { fprintf (stderr, "Could not allocate enough memory for input raster\n"); exit (42); } /////////////////////////////////////////////////////////////////////////// // Read the input into the memory buffer // todo: I couldn't use TIFFReadRGBAStrip here, because it gets confused stripSize = TIFFStripSize (input); roff = raster; for (stripNumber = 0; stripNumber < TIFFNumberOfStrips (input); stripNumber++) { roff += TIFFReadEncodedStrip (input, stripNumber, roff, stripSize); } /////////////////////////////////////////////////////////////////////////// B File: output.sgml [021] // Open the output TIFF snprintf(outputFilenameActual, 200, "%s-%d.tif", outputFilename, count); if ((output = TIFFOpen (outputFilenameActual, "w")) == NULL) { fprintf (stderr, "Could not open outgoing input %s\n", outputFilename); usage (argv[0], 42); } printf("."); fflush(stdout); // todo: We need to copy tags from the input image to the output image TIFFSetField (output, TIFFTAG_IMAGEWIDTH, width); TIFFSetField (output, TIFFTAG_IMAGELENGTH, height); TIFFSetField (output, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); TIFFSetField (output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField (output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField (output, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField (output, TIFFTAG_SAMPLESPERPIXEL, 3); // todo: balance this off with having 8 kb per strip... TIFFSetField (output, TIFFTAG_ROWSPERSTRIP, 100000); // Copy the subfile to a output location if (TIFFWriteEncodedStrip (output, 0, raster, width * height * 3 * sizeof (char)) == 0) { fprintf (stderr, "Could not write the output image\n"); exit (42); } /////////////////////////////////////////////////////////////////////////// // Flush this subfile and move onto the next one if(TIFFReadDirectory(input) == 0){ printf(" No more subfiles"); break; } /////////////////////////////////////////////////////////////////////////// // Doing correct cleanup with a loop like this is important... free(raster); } printf("\n"); // Cleanup TIFFClose (input); TIFFClose (output); free (raster); } void usage (char *cmd, int exitamt) { fprintf (stderr, "Bad command line arguements...\n\n"); fprintf (stderr, "Usage: %s -i <inputfile> -o <outputfile> -c <count>\n", cmd); exit (exitamt); } Code: /home/mikal/opensource/tiff-directories/read.c
This doesn't work at the moment
Man pages ... tiff2bw ... NAME tiff2bw - convert a color TIFF image to greyscale SYNOPSIS tiff2bw [ options ] input.tif output.tif DESCRIPTION Tiff2bw converts an RGB or Palette color TIFF image to a greyscale image by combining percentages of the red, green, and blue channels. By default, output samples are created by taking 28% of the red channel, 59% of the green channel, and 11% of the blue channel. To alter these percentages, the and options may be used. OPTIONS -c Specify a compression scheme to use when writing image data: "-c none" for no compression, "-c packbits" for the PackBits compression algorithm, "-c zip for the Deflate compression algorithm, "-c g3 for the CCITT Group 3 compression algorithm, "-c g4 for the CCITT Group 4 compression algorithm, and "-c lzw" for Lempel-Ziv & Welch (the default). -r Write data with a specified number of rows per strip; by default the number of rows/strip is selected so that each strip is approximately 8 kilobytes. -R Specify the percentage of the red channel to use (default 28). -G Specify the percentage of the green channel to use (default 59). -B Specify the percentage of the blue channel to use (default 11). SEE ALSO pal2rgb (1), tiffinfo (1), tiffcp (1), tiffmedian (1), libtiff (3) tiff2ps ... NAME tiff2ps - convert a TIFF image to \*(Ps\(tm SYNOPSIS tiff2ps [ options ] "input.tif ..." DESCRIPTION tiff2ps reads TIFF images and writes \*(Ps or Encapsulated \*(Ps (EPS) on the standard output. By default, tiff2ps writes Encapsulated \*(Ps for the first image in the specified TIFF image file. By default, tiff2ps will generate \*(Ps that fills a printed area specified by the TIFF tags in the input file. If the file does not contain XResolution or YResolution tags, then the printed area is set according to the image dimensions. The -w and -h options (see below) can be used to set the dimensions of the printed area in inches; overriding any relevant TIFF tags. The \*(Ps generated for RGB, palette, and CMYK images uses the colorimage operator. The \*(Ps generated for greyscale and bilevel images uses the image operator. When the colorimage operator is used, \*(Ps code to emulate this operator on older \*(Ps printers is also generated. Note that this emulation code can be very slow.s File: output.sgml [024] Color images with associated alpha data are composited over a white background. OPTIONS -1 Generate \*(Ps Level I (the default). -2 Generate \*(Ps Level II. -a Generate output for all IFDs (pages) in the input file. -d Set the initial TIFF directory to the specified directory number. (NB: directories are numbered starting at zero.) This option is useful for selecting individual pages in a multi-page (e.g. facsimile) file. -e Force the generation of Encapsulated \*(Ps. -h Specify the vertical size of the printed area (in inches). -o Set the initial TIFF directory to the IFD at the specified file offset. This option is useful for selecting thumbnail images and the like which are hidden using the SubIFD tag. -p Force the generation of (non-Encapsulated) \*(Ps. -s Generate output for a single IFD (page) in the input file. -w Specify the horizontal size of the printed area (in inches). -z When generating \*(Ps Level II, data is scaled so that it does not image into the deadzone on a page (the outer margin that the printing device is unable to mark). This option suppresses this behaviour. When \*(Ps Level I is generated, data is imaged to the entire printed page and this option has no affect. EXAMPLES The following generates \*(Ps Level II for all pages of a facsimile: DEFAULT COMMAND FOUND: RS tiff2ps -a2 fax.tif | lpr DEFAULT COMMAND FOUND: RE Note also that if you have version 2.6.1 or newer of Ghostscript then you can efficiently preview facsimile generated with the above command. To generate Encapsulated \*(Ps for a the image at directory 2 of an image use: DEFAULT COMMAND FOUND: RS tiff2ps -d 1 foo.tif DEFAULT COMMAND FOUND: RE (notice that directories are numbered starting at zero.) BUGS Because \*(Ps does not support the notion of a colormap, 8-bit palette images produce 24-bit \*(Ps images. This conversion results in output that is six times bigger than the original image and which takes a long time to send to a printer over a serial line. Matters are even worse for 4-, 2-, and 1-bit palette images. BUGS<�����������������.k��������������������� File: output.sgml [025] /title> <para>Does not handle tiled images when generating PS Level I output. </para> </sect0> <sect0><title>SEE ALSO pal2rgb (1), tiffinfo (1), tiffcp (1), tiffgt (1), tiffmedian (1), tiff2bw (1), tiffsv (1), libtiff (3) tiffcmp ... NAME tiffcmp - compare two TIFF files SYNOPSIS tiffcmp [ options ] "file1.tif file2.tif" DESCRIPTION Tiffcmp compares the tags and data in two files created according to the Tagged Image File Format, Revision 6.0. The schemes used for compressing data in each file are immaterial when data are compared-data are compared on a scanline-by-scanline basis after decompression. Most directory tags are checked; notable exceptions are: GrayResponseCurve , ColorResponseCurve , and ColorMap tags. Data will not be compared if any of the BitsPerSample , SamplesPerPixel , or ImageWidth values are not equal. By default, tiffcmp will terminate if it< File: output.sgml [026] encounters any difference. OPTIONS -l List each byte of image data that differs between the files. -t Ignore any differences in directory tags. BUGS Tags that are not recognized by the library are not compared; they may also generate spurious diagnostics. SEE ALSO pal2rgb (1), tiffinfo (1), tiffcp (1), tiffmedian (1), libtiff (3) Sample output tiffcmp shows the differences in tags between images, for example, when I compare the input and output images from the pixel example in this chapter: [mikal@localhost tiff-pixels]$ tiffcmp input.tif output.tif ImageWidth: 256 520 [mikal@localhost tiff-pixels]$ tiffcp ... NAME tiffcp - copy (and possibly convert) a TIFF file SYNOPSIS tiffcp [ options ] "src1.tif ... srcN.tif dst.tif" DESCRIPTION tiffcp combines one or more files created according to the Tag Image File Format, Revision 6.0 into a single TIFF file. Because the output file may be compressed using a different algorithm than the input files, tiffcp is most often used to convert between different compression schemes. By default, tiffcp will copy all the understood tags in a TIFF directory of an input file to the associated directory in the output file. tiffcp can be used to reorganize the storage characteristics of data in a file, but it is explicitly intended to not alter or convert the image data content in any way. OPTIONS -B Force output to be written with Big-Endian byte order. This option only has an effect when the output file is created or overwritten and not when it is appended to. -C Suppress the use of ``strip chopping'' when reading images that have a single strip/tile of uncompressed data. -c Specify the compression to use for data written to the output file: none for no compression, packbits for PackBits compression, lzw for Lempel-Ziv & Welch compression, jpeg for baseline JPEG compression, zip for Deflate compression, g3 for CCITT Group 3 (T.4) compression, and g4 for CCITT Group 4 (T.6) compression. By default tiffcp will compress data according to the value of the Compression tag found in the source file. DEFAULT COMMAND FOUND: IP The CCITT Group 3 and Group 4 compression algorithms can only be used with bilevel data. DEFAULT COMMAND FOUND: IP Group 3 compression can be specified together with several T.4-specific options: 1d for 1-dimensional encoding, 2d for 2-dimensional encoding, and fill to force each encoded scanline to be zero-filled so that the terminating EOL code lies on a byte boundary. Group 3-specific options are specified by appending a ``:''-separated list to the ``g3'' option; e.g. "-c g3:2d:fill" to get 2D-encoded data with byte-aligned EOL codes. DEFAULT COMMAND FOUND: IP LZW compression can be specified together with a predictor value. A predictor value of 2 causes each scanline of the output image to undergo horizontal differencing before it is encoded; a value of 1 forces each scanline to be encoded without differencing. LZW-specific options are specified by appending a ``:''-separated list to the ``lzw'' option; e.g. "-c lzw:2" for LZW compression with horizontal differencing. -f Specify the bit fill order to use in writing output data. By default, tiffcp will create a new file with the same fill order as the original. Specifying "-f lsb2msb" will force data to be written with the FillOrder tag set to LSB2MSB, while "-f msb2lsb" will force data to be written with the FillOrder tag set to MSB2LSB. -l Specify the length of a tile (in pixels). tiffcp attempts to set the tile dimensions so that no more than 8 kilobytes of data appear in a tile. -L Force output to be written with Little-Endian byte order. This option only has an effect when the output file is created or overwritten and not when it is appended to. -M Suppress the use of memory-mapped files when reading images. -p Specify the planar configuration to use in writing image data that has one 8-bit sample per pixel. By default, tiffcp will create a new file with the same planar configuration as the original. Specifying "-p contig" will force data to be written with multi-sample data packed together, while "-p separate" will force samples to be written in separate planes. -r Specify the number of rows (scanlines) in each strip of data written to the output file. By default, tiffcp attempts to set the rows/strip that no more than 8 kilobytes of data appear in a strip. -s Force the output file to be written with data organized in strips (rather than tiles). -t Force the output file to be written wtih data organized in tiles (rather than strips). options can be used to force the resultant image to be written as strips or tiles of data, respectively. -w Specify the width of a tile (in pixels). tiffcp attempts to set the tile dimensions so that no more than 8 kilobytes of data appear in a tile. EXAMPLES The following concatenates two files and writes the result using LZW encoding: DEFAULT COMMAND FOUND: RS tiffcp -c lzw a.tif b.tif result.tif DEFAULT COMMAND FOUND: RE To convert a G3 1d-encoded TIFF to a single strip of G4-encoded data the following might be used: DEFAULT COMMAND FOUND: RS tiffcp -c g4 -r 1001 File: output.sgml [029] 00 g3.tif g4.tif DEFAULT COMMAND FOUND: RE (1000 is just a number that is larger than the number of rows in the source file.) SEE ALSO pal2rgb (1), tiffinfo (1), tiffcmp (1), tiffmedian (1), tiffsplit (1), libtiff (3) tiffdither ... NAME tiffdither - convert a greyscale image to bilevel using dithering SYNOPSIS tiffdither [ options ] input.tif output.tif DESCRIPTION tiffdither converts a single channel 8-bit greyscale image to a bilevel image using Floyd-Steinberg error propagation with threholding. OPTIONS -c Specify the compression to use for data written to the output file: none for no compression, packbits for PackBits compression, lzw for Lempel-Ziv & Welch compression, zip for Deflate compression, g3 for CCITT Group 3 (T.4) compression, and g4 for CCITT Group 4 (T.6) compression. By defaul/<~ File: output.sgml [030] t tiffdither will compress data according to the value of the Compression tag found in the source file. DEFAULT COMMAND FOUND: IP The CCITT Group 3 and Group 4 compression algorithms can only be used with bilevel data. DEFAULT COMMAND FOUND: IP Group 3 compression can be specified together with several T.4-specific options: 1d for 1-dimensional encoding, 2d for 2-dimensional encoding, and fill to force each encoded scanline to be zero-filled so that the terminating EOL code lies on a byte boundary. Group 3-specific options are specified by appending a ``:''-separated list to the ``g3'' option; e.g. "-c g3:2d:fill" to get 2D-encoded data with byte-aligned EOL codes. DEFAULT COMMAND FOUND: IP LZW compression can be specified together with a predictor value. A predictor value of 2 causes each scanline of the output image to undergo horizontal differencing before it is encoded; a value of 1 forces each scanline to be encoded without differencing. LZW-specific options are specified by appending a ``:''-separated list to the ``lzw'' option; e.g. "-c lzw:2" for LZW compression with horizontal differencing. -f Specify the bit fill order to use in writing output data. By default, tiffdither will create a new file with the same fill order as the original. Specifying "-f lsb2msb" will force data to be written with the FillOrder tag set to LSB2MSB , while "-f msb2lsb" will force data to be written with the FillOrder tag set to MSB2LSB . -t Set the threshold value for dithering. By default the threshold value is 128. NOTES The dither algorithm is taken from the tiffmedian (1) program (written by Paul Heckbert). SEE ALSO pal2rgb (1), fax2tiff (1), tiffinfo (1), tiffcp (1), tiff2bw (1), libtiff (3) Sample output tiffdither takes gray scale images and dithers them into black and white images. For instance, the gray scale example from earlier in this tutorial produced the following image:
The gray scale input image
This image, after being pushed through tiffdither looks something like:
The dithered output image
tiffdump ... NAME tiffdump - print verbatim information about TIFF files SYNOPSIS tiffdump [ options ] "name ..." DESCRIPTION tiffdump displays directory information from files created according to the Tag Image File Format, Revision 6.0. The header of each TIFF file (magic number, version, and first directory offset) is displayed, followed by the tag contents of each directory in the file. For each tag, the name, datatype, count, and value(s) is displayed. When the symbolic name for a tag or datatype is known, the symbolic name is displayed followed by it's numeric (decimal) value. Tag values are displayed enclosed in ``<>'' characters immediately preceded by the value of the count field. For example, an ImageWidth tag might be displayed as ``ImageWidth (256) SHORT (3) 1<800>''. tiffdump is particularly useful for investigating the contents of TIFF files that libtiff does not understand. OPTIONS -h Force numeric Y޿ File: output.sgml [032] data to be printed in hexadecimal rather than the default decimal. -o Dump the contents of the IFD at the a particular file offset. The file offset may be specified using the usual C-style syntax; i.e. a leading ``0x'' for hexadecimal and a leading ``0'' for octal. SEE ALSO tiffinfo (1), libtiff (3) Sample output tiffdump shows you heaps of useful things about a TIFF image, basically anything stored in a tag. Some sample output is: [mikal@localhost tutorial-imaging]$ tiffdump tiff-figure10.tif tiff-figure10.tif: Magic: 0x4949 <little-endian> Version: 0x2a Directory 0: offset 38228 (0x9554) next 0 (0) ImageWidth (256) SHORT (3) 1<640> ImageLength (257) SHORT (3) 1<479> BitsPerSample (258) SHORT (3) 1<1> Compression (259) SHORT (3) 1<32773> Photometric (262) SHORT (3) 1<1> ImageDescription (270) ASCII (2) 41<Dithered B&W version of ...> StripOffsets (273) LONG (4) 5<8 8141 16315 24528 32662> SamplesPerPixel (277) SHORT (3) 1<1> RowsPerStrip (278) SHORT (3) 1<102> StripByteCounts (279) LONG (4) 5<8133 8174 8213 8134 5566> PlanarConfig (284) SHORT (3) 1<1> [mikal@localhost tutorial-imaging]$ tiffgt ... NAME tiffgt - display an image stored in a TIFF file (Silicon Graphics version) SYNOPSIS tiffgt [ options ] "input.tif ..." DESCRIPTION tiffgt displays one or more images stored using the Tag Image File Format, Revision 6.0. Each image is placed in a fixed size window that the user must position on the display (unless configured otherwise through X defaults). If the display has fewer than 24 bitplanes, or if the image does not warrant full color, then RGB color values are mapped to the closest values that exist in the colormap (this is done using the rgbi routine found in the graphics utility library tiffgt correctly handles files with any of the following characteristics: DEFAULT COMMAND FOUND: ta \w'PhotometricInterpretation 'u BitsPerSample 1, 2, 4, 8, 16 SamplesPerPixel 1, 3, 4 (the 4th sample is ignored) PhotometricInterpretation 0 (min-is-white), 1 (min-is-black), 2 (RGB), 3 (palette), 6 (YCbCr) PlanarConfiguration 1 (contiguous), 2 (separate) Orientation 1 (top-left), 4 (bottom-left) Data may be organized as strips or tiles and may be compressed with any of the compression algorithms supported by the libtiff (3) library. For palette images (\c PhotomatricInterpretation =3), tiffgt inspects the colormap values and assumes either 16-bit or 8-bit values according to the maximum value. That is, if no colormap entry greater than 255 is found, tiffgt assumes the colormap has only 8-bit values; otherwise it assumes 16-bit values. This inspection is done to handle old images written by previous (incorrect) versions of libtiff . tiffgt can be used to display multiple images one-at-a-time. The left mouse button switches the display to the first image in the next file in the list of files specified on the command line. The right mouse button switches to the first image in the previous file in the list. The middle mouse button causes the first image in the first file specified on the command line to be displayed. In addition the following keyboard commands are recognized: b Use a PhotometricIntepretation of MinIsBlack in displaying the current image. l Use a FillOrder of lsb-to-msb in decoding the current image. m Use a FillOrder of msb-tolmsb in decoding the current image. c Use a colormap visual to display the current image. r Use a true color (24-bit RGB) visual to display the current image. w Use a PhotometricIntepretation of MinIsWhite in displaying the current image. W Toggle (enable/disable) display of warning messages from the TIFF library when decoding images. E Toggle (enable/disable) display of error messages from the TIFF library when decoding images. z Reset all parameters to their default settings (\c FillOrder , PhotometricInterpretation , handling of warnings and errors). PageUp Display the previous image in the current file or the last image in the previous file. PageDown Display the next image in the current file or the first image in the next file. Home Display the first image in the current file. End Display the last image in the current file (unimplemented). OPTIONS -c Force image display in a colormap window. -d Specify an image to display by directory number. By default the first image in the file is displayed. Directories are numbered starting at zero. -e Enable reporting of error messages from the TIFF library. By default tiffgt silently ignores images that cannot be read. -f Force tiffgt to run as a foreground process. By default tiffgt will place itself in the background once it has opened the requested image file. -l Force the presumed bit ordering to be LSB to MSB. -m Force the presumed bit ordering to be MSB to LSB. -o Specify an image to display by directory offset. By default the first image in the file is displayed. Directories offsets may be specified using C-style syntax; i.e. a leading ``0x'' for hexadecima9= File: output.sgml [035] l and a leading ``0'' for octal. -p Override the value of the PhotometricInterpretation tag; the parameter may be one of: miniswhite , minisblack , rgb , palette , mask , separated , ycbcr , and cielab . -r Force image display in a full color window. -s Stop on the first read error. By default all errors in the input data are ignored and tiffgt does it's best to display as much of an image as possible. -w Enable reporting of warning messages from the TIFF library. By default tiffgt ignores warning messages generated when reading an image. -v Place information in the title bar describing what type of window (full color or colormap) is being used, the name of the input file, and the directory index of the image (if non-zero). By default, the window type is not shown in the title bar. BUGS Images wider and taller than the display are silently truncated to avoid crashing old versions of the window manager. SEE ALSO tiffdump (1), tiffinfo (1), tiffcp (1), libtiff (3) tiffinfo ... NAME tiffinfo - print information about TIFF files SYNOPSIS tiffinfo [ options ] "input.tif ..." DESCRIPTION Tiffinfo displays information about files created according to the Tag Image File Format, Revision 6.0. By default, the contents of each TIFF directory in each file is displayed, with the value of each tag shown symbolically (where sensible). OPTIONS -c Display the colormap and color/gray response curves, if present. -D In addition to displaying the directory tags, read and decompress all the data in each image (but not display it). -d In addition to displaying the directory tags, print each byte of decompressed data in hexadecimal. -j Display any \s-2JPEG\s0-related tags that are present. -o Set the initial TIFF directory according to the specified file offset. The file offset may be specified using the usual C-style syntax; i.e. a leading ``0x'' for hexadecimal and a leading ``0'' for octal. -s Display the offsets and byte counts for each data strip in a directory. -z Enable strip chopping when reading image data. -# Set the initial TIFF directory to # . SEE ALSO pal2rgb (1), tiffcp (1), tiffcmp (1), tiffmedian (1), libtiff (3) Sample output tiffinfo is probably the most useful command which comes with libtiff... In normal operation, it shows you nicely formatted information about the TIFF image, like so: [mikal@localhost tutorial-imaging]$ tiffinfo tiff-figure9.tif TIFF Directory at offset 0x40f7c Image Width: 640 Image Length: 480 Bits/Sample: 8 Compression Scheme: Deflate Photometric Interpretation: min-is-black Samples/Pi4 File: output.sgml [037] xel: 1 Rows/Strip: 100000 Planar Configuration: single image plane [mikal@localhost tutorial-imaging]$ When you ask nicely (with a -d), then you're shown random tiff data, which can be very handy: [mikal@localhost tutorial-imaging]$ tiffinfo -d tiff-figure9.tif TIFF Directory at offset 0x40f7c Image Width: 640 Image Length: 480 Bits/Sample: 8 Compression Scheme: Deflate Photometric Interpretation: min-is-black Samples/Pixel: 1 Rows/Strip: 100000 Planar Configuration: single image plane Strip 0: 59 5d 5c 55 4f 51 58 5d 5a 5a 57 51 4d 4d 4e 4d 58 54 55 5f 6c 76 7a 7c 86 83 89 89 7f 7c 79 6e 61 57 70 7a 64 6f 7d 62 60 61 68 75 82 85 7e 76 78 79 80 7a 66 5f 62 61 53 5b 62 6d 77 75 7a 8e a0 91 7a 66 5d 5d 61 62 6f 69 65 64 63 61 63 68 76 79 74 76 86 8b 86 85 83 83 66 6a a0 b8 a7 a1 94 a7 9d 72 5b 69 7b 7c 88 84 7f 7a 73 72 7e 8c 94 96 8a 75 6f 77 75 6a 73 7d 85 85 84 89 90 94 8b 7d 74 7a 84 8a 8e 91 9c 99 87 7e 8d 93 85 79 73 7e 88 87 80 7e 88 92 96 93 95 9d a1 9c 94 91 9c 8f 90 9e 9e 8a 7b 7b 89 7a 68 5d 58 5b 66 71 81 82 7c 71 6c 6f 70 6e 5f 62 60 60 65 66 6f 84 85 93 a4 a8 98 86 81 86 88 7e 83 7a 73 74 6f 7e 78 72 73 74 71 75 73 67 72 73 77 85 90 8a 8f a7 ba cc cc c0 c1 c8 cf d7 dd e6 ec e5 e2 ef ed d4 e4 e6 ea ee f1 f2 f1 ef f3 f3 f3 f2 f2 f2 f1 f1 f2 f1 f0 ef ee ed ec eb ... and so on tiffmedian ...