1 /* pgmmorphconv.c - morphological convolutions on a graymap: dilation and
2 ** erosion
3 **
4 ** Copyright (C) 2000 by Luuk van Dijk/Mind over Matter
5 **
6 ** Based on
7 ** pnmconvol.c - general MxN convolution on a portable anymap
8 **
9 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
10 **
11 ** Permission to use, copy, modify, and distribute this software and its
12 ** documentation for any purpose and without fee is hereby granted, provided
13 ** that the above copyright notice appear in all copies and that both that
14 ** copyright notice and this permission notice appear in supporting
15 ** documentation. This software is provided "as is" without express or
16 ** implied warranty.
17 */
18
19 #include "pm_c_util.h"
20 #include "shhopt.h"
21 #include "mallocvar.h"
22 #include "pgm.h"
23
24
25
26 enum Operation { ERODE, DILATE, OPEN, CLOSE, GRADIENT };
27
28
29
30 struct CmdlineInfo {
31 /* All the information the user supplied in the command line,
32 in a form easy for the program to use.
33 */
34 const char * inputFileName; /* File name of input file */
35 const char * templateFileName; /* File name of template file */
36
37 enum Operation operation;
38 };
39
40
41
42 static void
parseCommandLine(int argc,const char ** const argv,struct CmdlineInfo * const cmdlineP)43 parseCommandLine(int argc, const char ** const argv,
44 struct CmdlineInfo * const cmdlineP) {
45 /*----------------------------------------------------------------------------
46 Note that the file spec array we return is stored in the storage that
47 was passed to us as the argv array.
48 -----------------------------------------------------------------------------*/
49 optEntry * option_def;
50 /* Instructions to OptParseOptions3 on how to parse our options.
51 */
52 optStruct3 opt;
53 unsigned int option_def_index;
54 unsigned int erode, dilate, open, close, gradient;
55
56 MALLOCARRAY_NOFAIL(option_def, 100);
57
58 option_def_index = 0; /* incremented by OPTENT3 */
59 OPTENT3(0, "erode", OPT_FLAG, NULL, &erode, 0);
60 OPTENT3(0, "dilate", OPT_FLAG, NULL, &dilate, 0);
61 OPTENT3(0, "open", OPT_FLAG, NULL, &open, 0);
62 OPTENT3(0, "close", OPT_FLAG, NULL, &close, 0);
63 OPTENT3(0, "gradient", OPT_FLAG, NULL, &gradient, 0);
64
65 opt.opt_table = option_def;
66 opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
67 opt.allowNegNum = TRUE; /* We may have parms that are negative numbers */
68
69 pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
70 /* Uses and sets argc, argv, and some of *cmdlineP and others. */
71
72 if (erode + dilate + open + close + gradient > 1)
73 pm_error("You may specify at most one of -erode, -dilate, "
74 "-open, -close, or -gradient");
75
76 if (erode)
77 cmdlineP->operation = ERODE;
78 else if (dilate)
79 cmdlineP->operation = DILATE;
80 else if (open)
81 cmdlineP->operation = OPEN;
82 else if (close)
83 cmdlineP->operation = CLOSE;
84 else if (gradient)
85 cmdlineP->operation = GRADIENT;
86 else
87 cmdlineP->operation = DILATE;
88
89 if (argc-1 < 1)
90 pm_error("You must specify the template file name as an argument");
91 else {
92 cmdlineP->templateFileName = argv[1];
93
94 if (argc-1 < 2)
95 cmdlineP->inputFileName = "-";
96 else {
97 cmdlineP->inputFileName = argv[2];
98
99 if (argc-1 > 2)
100 pm_error("Too many arguments: %u. "
101 "The only possible arguments "
102 "are the template file name and the input file name",
103 argc-1);
104 }
105 }
106 }
107
108
109
110 static void
readTemplateMatrix(const char * const fileName,bit *** const templateP,unsigned int * const rowsP,unsigned int * const colsP)111 readTemplateMatrix(const char * const fileName,
112 bit *** const templateP,
113 unsigned int * const rowsP,
114 unsigned int * const colsP) {
115 /*----------------------------------------------------------------------------
116 Read in the template matrix.
117 -----------------------------------------------------------------------------*/
118 FILE * templateFileP;
119 int cols, rows;
120
121 templateFileP = pm_openr(fileName);
122
123 *templateP = pbm_readpbm(templateFileP, &cols, &rows);
124
125 pm_close(templateFileP);
126
127 if (cols % 2 != 1 || rows % 2 != 1)
128 pm_error("the template matrix must have an odd number of "
129 "rows and columns" );
130
131 /* the reason is that we want the middle pixel to be the origin */
132 *rowsP = rows;
133 *colsP = cols;
134 }
135
136
137
138 static void
setAllPixel(gray ** const image,unsigned int const rows,unsigned int const cols,gray const value)139 setAllPixel(gray ** const image,
140 unsigned int const rows,
141 unsigned int const cols,
142 gray const value) {
143
144 unsigned int col;
145
146 for (col = 0; col < cols; ++col) {
147 unsigned int row;
148 for (row = 0; row < rows; ++row)
149 image[row][col] = value;
150 }
151 }
152
153
154
155 static void
dilate(bit ** const template,int const trowso2,int const tcolso2,gray ** const inImage,gray ** const outImage,unsigned int const rows,unsigned int const cols,unsigned int * const templateCountP)156 dilate(bit ** const template,
157 int const trowso2,
158 int const tcolso2,
159 gray ** const inImage,
160 gray ** const outImage,
161 unsigned int const rows,
162 unsigned int const cols,
163 unsigned int * const templateCountP) {
164
165 unsigned int templateCount;
166 int tr;
167
168 setAllPixel(outImage, rows, cols, 0);
169
170 /* for each non-black pixel of the template add in to out */
171
172 for (tr = -trowso2, templateCount = 0; tr <= trowso2; ++tr) {
173 int tc;
174 for (tc = -tcolso2; tc <= tcolso2; ++tc) {
175 int r;
176 if (template[trowso2+tr][tcolso2+tc] != PBM_BLACK) {
177 ++templateCount;
178
179 for (r = ((tr > 0) ? 0 : -tr);
180 r < ((tr > 0) ? (rows-tr) : rows);
181 ++r) {
182 int c;
183 for (c = ((tc > 0) ? 0 : -tc);
184 c < ((tc > 0) ? (cols-tc) : cols);
185 ++c) {
186 gray const source = inImage[r+tr][c+tc];
187 outImage[r][c] = MAX(source, outImage[r][c]);
188 }
189 }
190 }
191 }
192 }
193 *templateCountP = templateCount;
194 }
195
196
197
198 static void
erode(bit ** const template,int const trowso2,int const tcolso2,gray ** const inImage,gray ** const outImage,unsigned int const rows,unsigned int const cols,unsigned int * const templateCountP)199 erode(bit ** const template,
200 int const trowso2,
201 int const tcolso2,
202 gray ** const inImage,
203 gray ** const outImage,
204 unsigned int const rows,
205 unsigned int const cols,
206 unsigned int * const templateCountP) {
207
208 unsigned int templateCount;
209 int tr;
210
211 setAllPixel(outImage, rows, cols, PGM_MAXMAXVAL);
212
213 /* For each non-black pixel of the template add in to out */
214
215 for (tr = -trowso2, templateCount = 0; tr <= trowso2; ++tr) {
216 int tc;
217 for (tc = -tcolso2; tc <= tcolso2; ++tc) {
218 if (template[trowso2+tr][tcolso2+tc] != PBM_BLACK) {
219 int r;
220 ++templateCount;
221
222 for (r = ((tr > 0) ? 0 : -tr);
223 r < ((tr > 0) ? (rows-tr) : rows);
224 ++r){
225 int c;
226
227 for (c = ((tc > 0) ? 0 : -tc);
228 c < ((tc > 0) ? (cols-tc) : cols);
229 ++c) {
230
231 gray const source = inImage[r+tr][c+tc];
232 outImage[r][c] = MIN(source, outImage[r][c]);
233
234 }
235 }
236 }
237 }
238 }
239 *templateCountP = templateCount;
240 }
241
242
243
244 static void
openMorph(bit ** const template,int const trowso2,int const tcolso2,gray ** const inputImage,gray ** const outputImage,unsigned int const rows,unsigned int const cols,unsigned int * const templateCountP)245 openMorph(bit ** const template,
246 int const trowso2,
247 int const tcolso2,
248 gray ** const inputImage,
249 gray ** const outputImage,
250 unsigned int const rows,
251 unsigned int const cols,
252 unsigned int * const templateCountP) {
253
254 gray ** erodedImage;
255 unsigned int erodedTemplateCount;
256
257 erodedImage = pgm_allocarray(cols, rows);
258
259 erode(template, trowso2, tcolso2,
260 inputImage, erodedImage, rows, cols, &erodedTemplateCount);
261
262 dilate(template, trowso2, tcolso2,
263 erodedImage, outputImage, rows, cols, templateCountP);
264
265 pgm_freearray(erodedImage, rows);
266 }
267
268
269
270 static void
closeMorph(bit ** const template,int const trowso2,int const tcolso2,gray ** const inputImage,gray ** const outputImage,unsigned int const rows,unsigned int const cols,unsigned int * const templateCountP)271 closeMorph(bit ** const template,
272 int const trowso2,
273 int const tcolso2,
274 gray ** const inputImage,
275 gray ** const outputImage,
276 unsigned int const rows,
277 unsigned int const cols,
278 unsigned int * const templateCountP) {
279
280 gray ** dilatedImage;
281 unsigned int dilatedTemplateCount;
282
283 dilatedImage = pgm_allocarray(cols, rows);
284
285 dilate(template, trowso2, tcolso2,
286 inputImage, dilatedImage, rows, cols, &dilatedTemplateCount);
287
288 erode(template, trowso2, tcolso2,
289 dilatedImage, outputImage, rows, cols, templateCountP);
290
291 pgm_freearray(dilatedImage, rows);
292 }
293
294
295
296 static void
subtract(gray ** const subtrahendImage,gray ** const subtractorImage,gray ** const outImage,unsigned int const rows,unsigned int const cols)297 subtract(gray ** const subtrahendImage,
298 gray ** const subtractorImage,
299 gray ** const outImage,
300 unsigned int const rows,
301 unsigned int const cols ) {
302
303 /* (I call the minuend the subtrahend and the subtrahend the subtractor,
304 to be consistent with other arithmetic terminology).
305 */
306
307 unsigned int c;
308
309 for (c = 0; c < cols; ++c) {
310 unsigned int r;
311 for (r = 0; r < rows; ++r)
312 outImage[r][c] = subtrahendImage[r][c] - subtractorImage[r][c];
313 }
314 }
315
316
317
318 static void
gradient(bit ** const template,int const trowso2,int const tcolso2,gray ** const inputImage,gray ** const outputImage,unsigned int const rows,unsigned int const cols,unsigned int * const templateCountP)319 gradient(bit ** const template,
320 int const trowso2,
321 int const tcolso2,
322 gray ** const inputImage,
323 gray ** const outputImage,
324 unsigned int const rows,
325 unsigned int const cols,
326 unsigned int * const templateCountP) {
327
328 gray ** dilatedImage;
329 gray ** erodedImage;
330 unsigned int dilatedTemplateCount;
331
332 dilatedImage = pgm_allocarray(cols, rows);
333 erodedImage = pgm_allocarray(cols, rows);
334
335 dilate(template, trowso2, tcolso2,
336 inputImage, dilatedImage, rows, cols, &dilatedTemplateCount);
337
338 erode(template, trowso2, tcolso2,
339 inputImage, erodedImage, rows, cols, templateCountP);
340
341 subtract(dilatedImage, erodedImage, outputImage, rows, cols);
342
343 pgm_freearray(erodedImage, rows );
344 pgm_freearray(dilatedImage, rows );
345 }
346
347
348
349 int
main(int argc,const char ** argv)350 main(int argc, const char ** argv) {
351
352 struct CmdlineInfo cmdline;
353 FILE * ifP;
354 bit ** template;
355 unsigned int templateCols, templateRows;
356 int cols, rows;
357 gray maxval;
358 gray ** inputImage;
359 gray ** outputImage;
360 unsigned int templateCount;
361
362 pm_proginit(&argc, argv);
363
364 parseCommandLine(argc, argv, &cmdline);
365
366 ifP = pm_openr(cmdline.inputFileName);
367
368 readTemplateMatrix(cmdline.templateFileName,
369 &template, &templateRows, &templateCols);
370
371 /* Template coords run from -templateCols/2 .. 0 .. + templateCols/2 */
372
373 inputImage = pgm_readpgm(ifP, &cols, &rows, &maxval);
374
375 if (cols < templateCols || rows < templateRows)
376 pm_error("the image is smaller than the convolution matrix" );
377
378 outputImage = pgm_allocarray(cols, rows);
379
380 switch (cmdline.operation) {
381 case DILATE:
382 dilate(template, templateRows/2, templateCols/2,
383 inputImage, outputImage, rows, cols,
384 &templateCount);
385 break;
386 case ERODE:
387 erode(template, templateRows/2, templateCols/2,
388 inputImage, outputImage, rows, cols,
389 &templateCount);
390 break;
391 case OPEN:
392 openMorph(template, templateRows/2, templateCols/2,
393 inputImage, outputImage, rows, cols,
394 &templateCount);
395 break;
396 case CLOSE:
397 closeMorph(template, templateRows/2, templateCols/2,
398 inputImage, outputImage, rows, cols,
399 &templateCount);
400 break;
401 case GRADIENT:
402 gradient(template, templateRows/2, templateCols/2,
403 inputImage, outputImage, rows, cols,
404 &templateCount);
405 break;
406 }
407
408 if (templateCount == 0)
409 pm_error( "The template was empty!" );
410
411 pgm_writepgm(stdout, outputImage, cols, rows, maxval, 0);
412
413 pgm_freearray(outputImage, rows);
414 pgm_freearray(inputImage, rows);
415 pm_close(ifP);
416
417 return 0;
418 }
419
420