Listing 15.2 gives an outline of the fileimage-analysis.c. I have left out the headers to let you figure out what to include. I will describe the details of the rest of the outline in the following subsections.
140 Chapter 15. Image analysis
Listing 15.2: An outline of the fileimage-analysis.c.
1 #include ...
2 static int prune_matrix(double **a, int m, int n, double rel_err) ...
3 static void clip_matrix(double **a, int m, int n, int M) ...
4 static void reduce_pgm_image(struct image *img, double rel_err) ...
5 static void reduce_ppm_image(struct image *img, double rel_err) ...
6 void show_usage(char *progname) ...
7 int main(int argc, char **argv) ...
15.6.1 The functionprune_matrix()
The functionprune_matrix()that appears on line 2 of Listing 15.2 receives a pointer ato an m×nmatrix and a relative error,0.0 ≤rel_err≤1.0, which is what was called ˆerel in Section 15.2. It implements that section’s bisection algorithm to find the cut-off threshold corresponding to ˆerel. It then zeroes all of the matrix’s entries that fall below the threshold and returns the count of the zeroed entries to the caller.
I will describe the workings of the functionprune_matrix()in words and leave it to you to convert it to C code. You should read and understand Section 15.2 in order to be able to follow these instructions.
1. Find the values maximum, max, and minimum, min, of the absolute values of a[i][j]. Also computenorm2, which is square of the norm, that is,a2. Hint: Let min = max = fabs(a[0][0]);and norm2 = 0.0;. Scan the matrix’s entries in a doubly nestedfor-loop, and adjustmax, min, andnorm2 as you go along.
Important: C has at least two functions for computing absolute values. The func- tionabs()computes the absolute value of a number ofinteger type. The function fabs()computes the absolute value of a number offloating point type. Carelessly confusing the two can lead to nasty and hard-to-spot bugs in your code. Be on guard when usingabs()andfabs()!
Note: The function abs() is declared instdlib.h, while fabs() is declared in math.h. Do#include<stdlib.h>to useabs()and#include<math.h>
to usefabs(). In particular, in the case offabs()you may have to link explic- itly with the standard mathematics library through the “-lm” flag, as is the case with gccon Linux. If you are working on a Unix or Unix-like system, information such as this is at your fingertips in your system’sman pages. Type, for instance
$ man 3 fabs
to read all aboutfabs(). The “3” specifies Section 3 of the man pages. Section 3 is the repository of the system’s documents on programming libraries. Type
$ man man
to learn about the organization of the man pages.
2. We wish to findtso thateabs(t) =aˆerel, that is,eabs(t)2=a2ˆerel2 . We have com- puteda2and are given ˆerel; therefore we may calculatea2ˆerel2 . Call itabs_err2.
You will need it in the next step.
3. Everything is all set to implement Section 15.2’s bisection algorithm for the cut-off thresholdt. Do it.
15.6. The implementation ofimage-analysis.c 141 4. Now that we havet, scan the matrix and, wherever|ai j|<t, change it to zero. Keep
a count of the number of zeroed entries.
5. Return the number of zeroed entries to the caller.
15.6.2 The functionclip_matrix()
The functionclip_matrix()that appears on line 3 of Listing 15.2 receives a pointera to anm×nmatrix which represents (approximately) the pixel values of a grayscale image or one of the red, green, and blue pixel values of a color image. The matrix that arrives here has been constructed through applying the inverse wavelet transform and may not exactly conform to the requirements of a PGM or PPM image. Specifically, its entries are not necessarily integers, and their values are not necessarily in the 0 toM range as they are supposed to be if they were properly formed. Theclip_matrix()function imple- ments Section 15.4’s recommendations for bringing the matrixainto conformance with the requirements ofNetpbm. I will describe what it does in words and leave it to you to convert it to C code. You should read and understand Section 15.4 in order to be able to follow these instructions.
The functionclip_matrix()scans the matrixa:
1. ifai j<0, it setsai j to zero;
2. else ifai j>M, it setsai j toM;
3. else, it roundsai j to the nearest integer.
This should be straightforward except possibly for the “round to the nearest integer” part.
Since this issue comes up frequently in programming, I will explain the solution as a stand- alone idea so that other chapters may refer to it.
Rounding a floating point number to an integer. Casting a floating point number to an integer type in C discards the number’s fractional part. For instance,(int)3.1 yields3and(int)(-3.1)yields-3. We see that the casting mechanism rounds floating point numbers “toward zero”.
This property may be adapted to achieve rounding “toward the nearest integer” through the very common “0.5 trick” as follows. Supposex = 3.1andy = 3.9. We see that
(int)(x + 0.5) = (int)3.6 = 3 //3.1is rounded to3 (int)(y + 0.5) = (int)4.4 = 4 //3.9is rounded to4
To round negative numbers “toward the nearest integer”, we subtract0.5. Thus, if x = -3.1andy = -3.9, we see that
(int)(x - 0.5) = (int)(-3.6) = -3 //−3.1is rounded to−3 (int)(y - 0.5) = (int)(-4.4) = -4 //−3.9is rounded to−4
Remark 15.4. As an alternative to this “homemade” rounding trick, you may consider the C standard library’slrint()function, introduced in C99, which works equally well with negative and positive numbers. Thus,lrint(3.1)yields3andlrint(-3.9) yields-4. I continue using the “0.5 trick” out of a force of habit.
142 Chapter 15. Image analysis
Listing 15.3: The functionreduce_pgm_image().
1 static void reduce_pgm_image(struct image *img, double rel_err)
2 {
3 int m = img→pam.height;
4 int n = img→pam.width;
5 int M = img→pam.maxval;
6 int zero_count;
7 haar_transform_matrix(img→g, m, n, WT_FWD);
8 zero_count = prune_matrix(img→g, m, n, rel_err);
9 haar_transform_matrix(img→g, m, n, WT_REV);
10 clip_matrix(img→g, m, n, M);
11 fprintf(stderr, "zeroed %d of %d wavelet coefficients, %d remaining\n",
12 zero_count, m*n, m*n - zero_count);
13 }
15.6.3 The functionreduce_pgm_image()
The function reduce_pgm_image()that appears on line 4 of Listing 15.2, receives a pointerimg to a “struct image” and arel_errnumber in the range[0, 1]. It expects thatimgpoints to a PGM image. (It doesn’t check for this!) Here is what it does:
1. applieshaar_transform_matrix()(with theWT_FWDflag) to the image’s ma- trix;
2. appliesprune_matrix()to zero those matrix entries that fall below the cut-off threshold corresponding to the prescribed relative errorrel_err;
3. applieshaar_transform_matrix()(with theWT_REVflag) to reconstruct the image from the truncated coefficients;
4. appliesclip_matrix()to bring the matrix’s entries into conformance with the PGM’s requirements; and
5. prints a brief report on the number of coefficients that were zeroed.
Listing 15.3 shows my implementation of the function.
15.6.4 The functionreduce_ppm_image()
The functionreduce_ppm_image() that appears on line 5 of Listing 15.2 receives a pointerimg to a “struct image” and arel_errnumber in the range[0, 1]. It expects thatimgpoints to a PPM image. (It doesn’t check for this!) It performs exactly the same tasks as thereduce_pgm_image()function described above, so I won’t repeat them. However, you will need to read Section 15.3 carefully regarding the construction of theT matrix because that’s what will be sent toprune_matrix()for truncation.
15.6.5 The functionshow_usage()
The functionshow_usage()that appears on line 6 of Listing 15.2 prints a brief help about how to invoke the program. You can see the output of my implementation’s show_usage()in Listing 15.1. It is more detailed than is customary in such things, but I was driven to it because despite having worked with this program for many years, I tend to forget the meanings of its command-line arguments and what the program is meant to do. Theshow_usage()prints a little handy reminder.
15.6. The implementation ofimage-analysis.c 143
Listing 15.4:The functionmain()in its entirety. The midblock declarations on lines 20 and 21 are allowed in C99 but not in C89. If you wish to reduce the code to C89, then follow the instructions regardingLine 12on page 130.
1 int main(int argc, char **argv)
2 {
3 char *infile, *outfile, *endptr;
4 double rel_err;
5
6 if (argc = 4) {
7 show_usage(argv[0]);
8 return EXIT_FAILURE;
9 }
10 rel_err = strtod(argv[1], &endptr);
11 if (*endptr = ’\0’ || rel_err < 0.0 || rel_err > 1.0) {
12 fprintf(stderr, "*** the rel_err argument should be "
13 "between 0.0 and 1.0\n");
14 return EXIT_FAILURE;
15 }
16 infile = argv[2];
17 outfile = argv[3];
18
19 pm_init(argv[0], 0);
20 struct image *img = read_image(infile);
21 struct pam *pam = &img→pam;
22 if (pam→format == PGM_FORMAT || pam→format == RPGM_FORMAT)
23 reduce_pgm_image(img, rel_err);
24 else if (pam→format == PPM_FORMAT || pam→format == RPPM_FORMAT)
25 reduce_ppm_image(img, rel_err);
26 else {
27 fprintf(stderr, "*** file %s, line %d: shouldn’t be here\n",
28 __FILE__, __LINE__);
29 return EXIT_FAILURE;
30 }
31
32 write_image(outfile, img);
33 free_image(img);
34 return EXIT_SUCCESS;
35 }
15.6.6 The functionmain()
The functionmain()that appears on line 7 of Listing 15.2 is the driver of the other functions that have been discussed up to this point. It parses the command-line for the user-specified relative error and the input and output file names. It reads the image from the input file into a “struct img”, detects the type of the image (PGM or PPM), and then dispatches the images toreduce_pgm_image()orreduce_ppm_image()for processing. Subsequently, it writes the modified image into the output file and exits. List- ing 15.4 shows my implementation ofmain(). I will proceed to describe some of its more subtle points.
Line 6. We expect the program to be invoked with three arguments (see Section 15.5);
thereforeargcshould be 4.
144 Chapter 15. Image analysis Lines 10–17. The user-specified relative error is inargv[1]as a string. Here we apply the standard library’sstrtod()function (see Chapter 5) to extract its numerical value.
Line 19. This initializes thelibnetpbmlibrary. See Section 14.5 for explanation.
Line 20. We call read_image()(see subsection 14.8.1) to read the image file that’s given on the command-line.
Line 21. There are frequent references toimg→pamin the rest of the code. We define a shorthand here to reduce clutter. (We did the same in many place in Chapter 14.) Lines 22–30. The meanings of preprocessor symbolsPGM_FORMAT, etc., are described
in Section 14.5. Here we test for the image type. If it is a plain PGM or raw PGM, we invokereduce_pgm_image(). If it is a plain PPM or raw PPM, we invoke reduce_ppm_image(). Otherwise we print an error message and exit because we are not equipped to handle other types of images.
On line 27 I say “shouldn’t be here” and I mean it. The functionread_image() (see subsection 14.8.1) checks for the image type. If anything other than a PGM or PPM, it prints an error message and exits the program. Therefore, if the program is functioning correctly, line 27 cannot be reached. If it is reached, then there is a bug.
Lines 32–34. We are done with image processing. We callwrite_image()(see sub- section 14.8.3) to write the reconstructed image to the specified output file, free the allocated memory, and exit.