14.6 A no-frills demo of libnetpbm
The filenetpbm-minimal.cshown in Listing 14.2 provides a no-frills demo oflibnetpbm.
It reads a PGM image into a matrixa, draws a black line over it by changing the pixels ai i,i =0, 1, 2, . . ., to zero, and then writes the result into another PGM file. If you have read the descriptions of thelibnetpbmfunctions in the previous section, you should have no difficulty in following the program’s logic and understanding what it does; therefore I will not elaborate on it any further other than pointing out that on line 42 we set thepam structure’splainformatmember totrueorfalse, according to the input image’s format, so that a plain format input results in a plain format output and a raw format input results in a raw format output. In the absence of that line, the output image will be in the raw format regardless of the input image’s format because that islibnetpbm’s default behavior.
The program depends on Chapter 8’sarray.h, which in turn depends on Chapter 7’s xmallocmodule. Therefore I suggest that you construct aMakefileaccording to the in- structions of Chapter 6 to buildnetpbm-minimal as well as the several other programs which you will develop in this chapter. Here is the beginning part of theMakefile:
CFLAGS = -Wall -pedantic -std=c99 -O2 LIBS = -lnetpbm
netpbm-minimal: xmalloc.o netpbm-minimal.o
$(CC) $^ $(LIBS) -o $@
14.7 The interface of the image-io module
We are going to develop animage-iomodule which will provide functions for reading and writing PGM and PPM images. Listing 14.3 shows the file image-io.h that pro- vides the module’s interface. It declares a “struct image”, which extendslibnetpbm’s
“struct pam” by adding new membersr,g,bthat point toh×wmatrices that hold an image’s red, green, and blue pixel values. (Here h andware the image’s height and width in pixels.) Additionally, it declares the prototypes of functions that read and write images and free the allocated memory.
We use “struct image” with both PGM and PPM image types. In the case of a PGM image only one of the three matrix pointersr,g,bis needed. I will use thegpointer consistently for that purpose since “g” may be thought of as standing both for “green” and
“gray”. Therandbpointers are not used in that case and may be left unassigned.
Remark 14.4. Why are ther,g,bmatrices in “struct image” of typedouble? The pixel values of an image are integers in the range 0 toM, where typicallyMis 28−1.
Isn’t it more natural to store the numbers in a matrix of typeunsigned char? Even in the worst case, whenMis 216−1=65535), a matrix of typeunsigned short int ought to suffice.
Those observations are valid. Nevertheless, the type of storage for an image is dictated by the application. I am using the typedoublefor storage in anticipation of applications of thewavelet transformto image analysis, which is the subject of the next chapter. There we overwrite an image matrix with the corresponding wavelet transform, which is no longer a matrix of integers. If your applications don’t do such drastic things to your im- ages, then feel free to changedoubletounsigned charorunsigned short int inimage-io.h.
Theimage-iomodule relies on thexmallocmodule (Chapter 7) for memory allocation and thearray.hheader file (Chapter 8) for building matrices. Therefore, following the suggestions in Chapter 2, the project’s directory will contain
122 Chapter 14. Image I/O
Listing 14.2: The file netpbm-minimal.c provides a no-frills demo of the libnetpbm functions.
It reads a PGM image into a matrix, draws a black line over it, and then writes the result into another PGM file. Calls to the libnetpbmfunctions are set on a
shaded background .
1 #include <stdio.h>
2 #include <pam.h>
3 #include "array.h"
4 int main(int argc, char **argv)
5 {
6 char *infile = "aya_matsuura.pgm";
7 char *outfile = "zz.pgm";
8 struct pam pam;
9 FILE *fp;
10 double **a;
11 tuple *row;
12 int i, j;
13
14 pm_init (argv[0], 0); //initialize the library
15
16 //read image header
17 fp = pm_openr (infile);
18 pnm_readpaminit (fp, &pam, PAM_STRUCT_SIZE(tuple_type));
19 printf("image: %dx%d, depth: %d\n", 20 pam.width, pam.height, pam.depth);
21 if (!(pam.format == PGM_FORMAT || pam.format == RPGM_FORMAT)) { 22 fprintf(stderr, "Sorry, this demo handles PGM images only.\n");
23 return EXIT_FAILURE;
24 }
25
26 make_matrix(a, pam.height, pam.width); //matrix to store image data
27 row = pnm_allocpamrow (&pam); //tuplerow vector
28 for(i = 0; i < pam.height; i++) { //read pixel data
29 pnm_readpamrow (&pam, row);
30 for (j = 0; j < pam.width; j++) { //copy tuplerow into matrix
31 a[i][j] = row [j][0];
32 }
33 }
34 pm_close (fp);
35
36 for (i = 0; i < pam.width && i < pam.height; i++)
37 a[i][i] = 0.0; //change the ai ipixels to black
38
39 //write image header
40 fp = pm_openw (outfile);
41 pam.file = fp;
42 pam.plainformat = pam.format == PGM_FORMAT ? 1 : 0;
43 pnm_writepaminit (&pam);
44
45 for (i = 0; i < pam.height; i++) { //write pixel data
46 for(j = 0; j < pam.width; j++)
47 row[j][0] = a[i][j];
48 pnm_writepamrow (&pam, row);
49 }
50 pm_close (fp);
51 free_matrix(a);
52 pnm_freepamrow (row);
53 return EXIT_SUCCESS;
54 }
14.7. The interface of theimage-io module 123
Listing 14.3:The fileimage-io.h.
1 #ifndef H_IMAGE_IO_H
2 #define H_IMAGE_IO_H
3 #include <pam.h>
4 struct image {
5 struct pam pam;
6 double **r;
7 double **g;
8 double **b;
9 };
10 struct image *read_image(char *filename);
11 void write_image(char *filename, struct image *img);
12 void free_image(struct image *img);
13 #endif /∗H_IMAGE_IO_H*/
$ cd image-io
$ ls -F
Makefile image-io-test-3.c netpbm-minimal.c array.h@ image-io-test-4.c xmalloc.c@
image-io-test-0.c image-io-test-5.c xmalloc.h@
image-io-test-1.c image-io.c image-io-test-2.c image-io.h
The stand-alone file netpbm-minimal.c was presented in Section 14.6. The image-io module itself is contained in the filesimage-io.[ch]. The filesimage-io-test-[0–5].care inde- pendent drivers for demonstrating the module’s functionality in various ways, as will be described later. They compile to executables namedimage-io-test-[0–5]. Each executable is invoked with two arguments, as in
$ image-io-test-0 infile outfile
The program reads an image frominfile, modifies the images in certain ways, and writes the modified image tooutfile. You may name the input and output files as you wish, but it is customary to name images in PGM and PPM formats with the.pgmand.ppm extensions, respectively. The program does not rely on a file name to determine the im- age format. Rather, it detects the image format through the magic number embedded in the file.
Remark 14.5. In the descriptions oflibnetpbm’spm_openr()andpm_openw()func- tions in Section 14.5, we noted that these functions treat the special file name"-"as a reference to thestdinorstdout. Therefore, the program may also be invoked as
$ image-io-test - outfile.pgm <infile.pgm or even as
$ image-io-test - - <infile.pgm >outfile.pgm
These will work in Unix or in Unix-like systems where a file is treated a stream of bytes.
On an operating system such as Windows that distinguishes between text and binary files, the redirection may corrupt a raw image.
124 Chapter 14. Image I/O
Listing 14.4: An outline of the fileimage-io.c.
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "array.h"
4 #include "image-io.h"
5 static void read_pgm_pixel_data(struct image *img) ...
6 static void read_ppm_pixel_data(struct image *img) ...
7 struct image *read_image(char *filename) ...
8 static void write_pgm_pixel_data(struct image *img) ...
9 static void write_ppm_pixel_data(struct image *img) ...
10 void write_image(char *filename, struct image *img) ...
11 void free_image(struct image *img) ...
Listing 14.5: An implementation of the functionread_image().
1 struct image *read_image(char *filename)
2 {
3 struct image *img = xmalloc(sizeof *img);
4 struct pam *pam = &img→pam;
5 FILE *fp = pm_openr(filename);
6 pnm_readpaminit(fp, pam, PAM_STRUCT_SIZE(tuple_type));
7 if (pam→format == PGM_FORMAT || pam→format == RPGM_FORMAT)
8 read_pgm_pixel_data(img);
9 else if (pam→format == PPM_FORMAT || pam→format == RPPM_FORMAT)
10 read_ppm_pixel_data(img);
11 else {
12 fprintf(stderr, "error: file ‘%s’ contains neither "
13 "a PGM nor a PPM image\n", filename);
14 exit(EXIT_FAILURE);
15 }
16 pm_close(fp);
17 return img;
18 }