1 #   include	"bitmapConfig.h"
2 
3 #   include	"bmintern.h"
4 #   include	<stdlib.h>
5 
6 #   include	<appDebugon.h>
7 
8 /************************************************************************/
9 /*									*/
10 /*  Binary morphology operations.					*/
11 /*									*/
12 /************************************************************************/
13 
14 typedef struct DilateJob
15     {
16     BitmapDescription		djBdOut;
17     unsigned char *		djBufOut;
18 				    /************************************/
19 				    /*  Output destination.		*/
20 				    /************************************/
21 
22     const RasterImage *		djRiSel;
23     unsigned char		djInvertMaskSel;
24 				    /************************************/
25 				    /* Structuring element.		*/
26 				    /************************************/
27     int				djRowSelOrig;
28     int				djColSelOrig;
29 				    /************************************/
30 				    /* Origin of structuring element.	*/
31 				    /************************************/
32 
33     const RasterImage *		djRiIn;
34     unsigned char		djInvertMaskIn;
35 				    /************************************/
36 				    /* Input image.			*/
37 				    /************************************/
38     } DilateJob;
39 
40 /************************************************************************/
41 /*									*/
42 /*  Draw the input buffer for a foreground pixel in the structuring	*/
43 /*  element.								*/
44 /*									*/
45 /*  As the input image is supposed to be much bigger than the		*/
46 /*  structuring element, it is more efficient to repeatedly draw the	*/
47 /*  image than repeatedly draw the element: The tight loops that copy	*/
48 /*  the rows of the image are more efficient than the coordinating code	*/
49 /*  around them.							*/
50 /*									*/
51 /*  NOTE: The efficiency assumption could be wrong for a big black	*/
52 /*  structuring element.						*/
53 /*									*/
54 /************************************************************************/
55 
bmMorphoDilateElementPixel(void * voiddj,int rowSel,int colSel)56 static int bmMorphoDilateElementPixel(	void *			voiddj,
57 					int			rowSel,
58 					int			colSel )
59 
60     {
61     DilateJob *	dj= (DilateJob *)voiddj;
62 
63     bmDraw1BitImage( &(dj->djBdOut), dj->djBufOut,
64 					dj->djRiIn,
65 					dj->djInvertMaskIn,
66 					rowSel- dj->djRowSelOrig,
67 					colSel- dj->djColSelOrig );
68 
69     /* test code:
70     bmCopyArea( colIn- dj->djColSelOrig, rowIn- dj->djRowSelOrig,
71 		    dj->djBufOut, dj->djBufIn, &(dj->djBdOut), dj->djBdIn );
72     */
73 
74     return 0;
75     }
76 
77 /************************************************************************/
78 /*									*/
79 /*  Check the input of a morphology operation.				*/
80 /*									*/
81 /*  1)  Check the format of the input image.				*/
82 /*  2)  Check the format of the structuring element.			*/
83 /*									*/
84 /************************************************************************/
85 
bmMorphoCheckInputFormats(const BitmapDescription * bdIn,const BitmapDescription * bdSel)86 static int bmMorphoCheckInputFormats(	const BitmapDescription *	bdIn,
87 					const BitmapDescription *	bdSel )
88     {
89     /*  1  */
90     switch( bdIn->bdColorEncoding )
91 	{
92 	case BMcoBLACKWHITE:
93 	case BMcoWHITEBLACK:
94 	    if  ( bdIn->bdBitsPerPixel != 1 )
95 		{
96 		LLDEB(bdIn->bdColorEncoding,bdIn->bdBitsPerPixel);
97 		return -1;
98 		}
99 	    break;
100 
101 	default:
102 	    LLDEB(bdIn->bdColorEncoding,bdIn->bdBitsPerPixel);
103 	    return -1;
104 	}
105 
106     /*  2  */
107     if  ( bdSel->bdColorEncoding != bdIn->bdColorEncoding )
108 	{
109 	LLDEB(bdSel->bdColorEncoding,bdIn->bdColorEncoding);
110 	return -1;
111 	}
112 
113     if  ( bdSel->bdBitsPerPixel != 1 )
114 	{
115 	LLDEB(bdSel->bdColorEncoding,bdSel->bdBitsPerPixel);
116 	return -1;
117 	}
118 
119     return 0;
120     }
121 
122 /************************************************************************/
123 /*									*/
124 /*  Remember what to do.						*/
125 /*									*/
126 /************************************************************************/
127 
bmMorphoStartDilateJob(DilateJob * dj,const RasterImage * riIn,const RasterImage * riSel,int rowSelOrig,int colSelOrig)128 static void bmMorphoStartDilateJob(
129 				DilateJob *			dj,
130 				const RasterImage *		riIn,
131 				const RasterImage *		riSel,
132 				int				rowSelOrig,
133 				int				colSelOrig )
134     {
135     bmInitDescription( &(dj->djBdOut) );
136 
137     dj->djBufOut= (unsigned char *)0;
138     dj->djRiIn= riIn;
139     dj->djRiSel= riSel;
140     dj->djRowSelOrig= rowSelOrig;
141     dj->djColSelOrig= colSelOrig;
142 
143     if  ( riIn->riDescription.bdColorEncoding == BMcoWHITEBLACK )
144 	{ dj->djInvertMaskIn= 0xff;	}
145     else{ dj->djInvertMaskIn= 0x00;	}
146 
147     if  ( riSel->riDescription.bdColorEncoding == BMcoWHITEBLACK )
148 	{ dj->djInvertMaskSel= 0xff;	}
149     else{ dj->djInvertMaskSel= 0x00;	}
150 
151     return;
152     }
153 
154 /************************************************************************/
155 /*									*/
156 /*  Dilate an image.							*/
157 /*									*/
158 /*  As a completely arbitrary decision, the '1' value in both the	*/
159 /*  input and the output have been chosen as the 'black' value of the	*/
160 /*  morphology books. In the most common WHITEBLACK case of bitmaps,	*/
161 /*  this means that the colors are swapped.				*/
162 /*									*/
163 /*  1)  Allocate output resources.					*/
164 /*  2)  Draw input image for all pixels in the structuring element.	*/
165 /*  3)  Return output.							*/
166 /*  4)  Cleanup.							*/
167 /*									*/
168 /************************************************************************/
169 
bmMorphoDilateLow(RasterImage * riOut,DilateJob * dj,int reverse)170 static int bmMorphoDilateLow(	RasterImage *			riOut,
171 				DilateJob *			dj,
172 				int				reverse )
173     {
174     int			rval= 0;
175 
176     /*  1  */
177     if  ( bmCopyDescription( &(dj->djBdOut), &(dj->djRiIn->riDescription) ) )
178 	{ LDEB(1); rval= -1; goto ready;	}
179 
180     /*
181     dj->djBdOut.bdPixelsWide += dj->djBdSel->bdPixelsWide- 1;
182     dj->djBdOut.bdPixelsHigh += dj->djBdSel->bdPixelsHigh- 1;
183     */
184 
185     bmCalculateSizes( &(dj->djBdOut) );
186 
187     if  ( reverse )
188 	{
189 	dj->djBufOut= bmForegroundBuffer( dj->djBdOut.bdBufferLength,
190 						dj->djBdOut.bdColorEncoding );
191 	}
192     else{
193 	dj->djBufOut= bmBackgroundBuffer( dj->djBdOut.bdBufferLength,
194 						dj->djBdOut.bdColorEncoding );
195 	}
196     if  ( ! dj->djBufOut )
197 	{
198 	LXDEB(dj->djBdOut.bdBufferLength,dj->djBufOut);
199 	rval= -1; goto ready;
200 	}
201 
202 #   if 0
203     {
204     const char *	name= "/tmp/background.png";
205     int			format;
206 
207     format=  bmSuggestFormat( name, -1, &(dj->djBdOut) );
208     bmWrite( name, dj->djBufOut, &(dj->djBdOut), format, 1.0 );
209     }
210 #   endif
211 
212     /*  2  */
213     bmForAll1Pixels( dj->djRiSel,
214 	    dj->djInvertMaskSel, (void *)dj, bmMorphoDilateElementPixel );
215 
216 #   if 0
217     {
218     const char *	name= "/tmp/dilated.png";
219     int			format;
220 
221     format=  bmSuggestFormat( name, -1, &(dj->djBdOut) );
222     bmWrite( name, dj->djBufOut, &(dj->djBdOut), format, 1.0 );
223     }
224 #   endif
225 
226     /*  3  */
227     if  ( bmCopyDescription( &(riOut->riDescription), &(dj->djBdOut) ) )
228 	{ LDEB(1); rval= -1; goto ready;	}
229 
230     /*  steal  */
231     riOut->riBytes= dj->djBufOut;
232     dj->djBufOut= (unsigned char *)0;
233 
234   ready:
235 
236     /*  4  */
237     if  ( dj->djBufOut )
238 	{ free( dj->djBufOut );	}
239 
240     bmCleanDescription( &(dj->djBdOut) );
241 
242     return rval;
243     }
244 
245 /************************************************************************/
246 /*									*/
247 /*  Dilate an image.							*/
248 /*									*/
249 /************************************************************************/
250 
bmMorphoDilate(RasterImage * riOut,const RasterImage * riIn,const RasterImage * riSe,int rowSelOrig,int selColOrig)251 int bmMorphoDilate(	RasterImage *			riOut,
252 			const RasterImage *		riIn,
253 			const RasterImage *		riSe,
254 			int				rowSelOrig,
255 			int				selColOrig )
256     {
257     DilateJob		dj;
258     int			reverse= 0;
259 
260     if  ( bmMorphoCheckInputFormats( &(riIn->riDescription),
261 						&(riSe->riDescription) ) )
262 	{ LDEB(1); return -1;	}
263 
264     bmMorphoStartDilateJob( &dj, riIn, riSe, rowSelOrig, selColOrig );
265 
266     return bmMorphoDilateLow( riOut, &dj, reverse );
267     }
268 
269 /************************************************************************/
270 /*									*/
271 /*  Erode an image.							*/
272 /*									*/
273 /*  Use the fact that the erosion is the complement of the dilation of	*/
274 /*  the complement of the input with the mirrored structuring element.	*/
275 /*									*/
276 /*  1)  Initialization and verification of the input parameters.	*/
277 /*  2)  Mirror structuring element.					*/
278 /*  3)  Set dilate parameters. Realize that these are dilate parameters	*/
279 /*	and that we want to erode.					*/
280 /*  4)  To get the complement of the input image, swap the invert mask	*/
281 /*	for the input image.						*/
282 /*  5)  Invert the output image.					*/
283 /*									*/
284 /************************************************************************/
285 
bmMorphoErode(RasterImage * riOut,const RasterImage * riIn,const RasterImage * riSe,int rowSelOrig,int selColOrig)286 int bmMorphoErode(		RasterImage *			riOut,
287 				const RasterImage *		riIn,
288 				const RasterImage *		riSe,
289 				int				rowSelOrig,
290 				int				selColOrig )
291     {
292     int			rval= 0;
293     DilateJob		dj;
294 
295     RasterImage		riRot;
296 
297     int			reverse= 1;
298 
299     /*  1  */
300     bmInitRasterImage( &riRot );
301 
302     if  ( bmMorphoCheckInputFormats( &(riIn->riDescription),
303 						&(riSe->riDescription) ) )
304 	{ LDEB(1); return -1;	}
305 
306     /*  2  */
307     if  ( bmRotate180( &riRot, riSe, 180 ) )
308 	{ LDEB(180); rval= -1; goto ready;	}
309 
310     /*  3  */
311     bmMorphoStartDilateJob( &dj, riIn, &riRot, rowSelOrig, selColOrig );
312 
313     /*  4,5  */
314     dj.djInvertMaskIn= ~dj.djInvertMaskIn;
315     reverse= 1;
316 
317     if  ( bmMorphoDilateLow( riOut, &dj, reverse ) )
318 	{ XDEB(dj.djInvertMaskIn); rval= -1; goto ready;	}
319 
320   ready:
321 
322     bmCleanRasterImage( &riRot );
323 
324     return rval;
325     }
326 
327 /************************************************************************/
328 /*									*/
329 /*  Return the 'Simple' structuring element.				*/
330 /*									*/
331 /************************************************************************/
332 
bmMorphoSetSimpleSe(RasterImage * riOut)333 int bmMorphoSetSimpleSe(	RasterImage *		riOut )
334     {
335     const int		size= 3;
336     const int		wide= size;
337     const int		high= size;
338 
339     riOut->riDescription.bdColorEncoding= BMcoWHITEBLACK;
340     riOut->riDescription.bdPixelsWide= wide;
341     riOut->riDescription.bdPixelsHigh= high;
342     riOut->riDescription.bdBitsPerSample= 1;
343     riOut->riDescription.bdSamplesPerPixel= 1;
344     riOut->riDescription.bdBitsPerPixel= 1;
345     riOut->riDescription.bdXResolution= 1;
346     riOut->riDescription.bdYResolution= 1;
347     riOut->riDescription.bdUnit= BMunPIXEL;
348 
349     if  ( bmCalculateSizes( &(riOut->riDescription) ) )
350 	{ LDEB(1); return -1;	}
351 
352     riOut->riBytes= bmBackgroundBuffer(
353 			    riOut->riDescription.bdBufferLength,
354 			    riOut->riDescription.bdColorEncoding );
355     if  ( ! riOut->riBytes )
356 	{ LXDEB(riOut->riDescription.bdBufferLength,riOut->riBytes); return -1;	}
357 
358     return 0;
359     }
360 
361 /************************************************************************/
362 /*									*/
363 /*  Dilate/Erode an image using the 'Simple' structuring element.	*/
364 /*									*/
365 /*  For Erosion, use the approach of bmMorphoErode(), but with some	*/
366 /*  shortcuts to avoid double work.					*/
367 /*									*/
368 /************************************************************************/
369 
bmMorphoDilateSimple(RasterImage * riOut,const RasterImage * riIn,int ignoredInt)370 int bmMorphoDilateSimple(	RasterImage *			riOut,
371 				const RasterImage *		riIn,
372 				int				ignoredInt )
373     {
374     int			rval= 0;
375 
376     RasterImage		riSel;
377 
378     const int		rowSelOrig= 1;
379     const int		selColOrig= 1;
380 
381     bmInitRasterImage( &riSel );
382 
383     if  ( bmMorphoSetSimpleSe( &riSel ) )
384 	{ LDEB(1); rval= -1; goto ready;	}
385 
386     if  ( bmMorphoDilate( riOut, riIn, &riSel, rowSelOrig, selColOrig ) )
387 	{ LLDEB(rowSelOrig,selColOrig); rval= -1; goto ready;	}
388 
389   ready:
390 
391     bmCleanRasterImage( &riSel );
392 
393     return rval;
394     }
395 
bmMorphoErodeSimple(RasterImage * riOut,const RasterImage * riIn,int ignoredInt)396 int bmMorphoErodeSimple(	RasterImage *			riOut,
397 				const RasterImage *		riIn,
398 				int				ignoredInt )
399     {
400     int			rval= 0;
401     const unsigned char	invertMaskIn= 0xff;
402     DilateJob		dj;
403 
404     RasterImage		riSel;
405 
406     const int		rowSelOrig= 1;
407     const int		selColOrig= 1;
408     int			reverse= 1;
409 
410     bmInitRasterImage( &riSel );
411 
412     if  ( bmMorphoSetSimpleSe( &riSel ) )
413 	{ LDEB(1); rval= -1; goto ready;	}
414 
415     if  ( bmMorphoCheckInputFormats( &(riIn->riDescription),
416 						&(riSel.riDescription) ) )
417 	{ LDEB(1); return -1;	}
418 
419     bmMorphoStartDilateJob( &dj, riIn, &riSel, rowSelOrig, selColOrig );
420 
421     /*  4  */
422     dj.djInvertMaskIn= ~dj.djInvertMaskIn;
423     reverse= 1;
424 
425     if  ( bmMorphoDilateLow( riOut, &dj, reverse ) )
426 	{ XDEB(invertMaskIn); rval= -1; goto ready;	}
427 
428   ready:
429 
430     bmCleanRasterImage( &riSel );
431 
432     return rval;
433     }
434 
435 /************************************************************************/
436 /*									*/
437 /*  Make an element to recognize lines.					*/
438 /*									*/
439 /************************************************************************/
440 
bmMorphoLineElement(RasterImage * riOut,int wide,int high,int x0,int y0,int x1,int y1)441 int bmMorphoLineElement(	RasterImage *			riOut,
442 				int				wide,
443 				int				high,
444 				int				x0,
445 				int				y0,
446 				int				x1,
447 				int				y1 )
448     {
449     if  ( x0 < 0 || x0 >= wide )
450 	{ LLDEB(x0,wide); return -1;	}
451     if  ( x1 < 0 || x1 >= wide )
452 	{ LLDEB(x1,wide); return -1;	}
453 
454     if  ( y0 < 0 || y0 >= high )
455 	{ LLDEB(y0,high); return -1;	}
456     if  ( y1 < 0 || y1 >= high )
457 	{ LLDEB(y1,high); return -1;	}
458 
459     riOut->riDescription.bdColorEncoding= BMcoWHITEBLACK;
460     riOut->riDescription.bdPixelsWide= wide;
461     riOut->riDescription.bdPixelsHigh= high;
462     riOut->riDescription.bdBitsPerSample= 1;
463     riOut->riDescription.bdSamplesPerPixel= 1;
464     riOut->riDescription.bdBitsPerPixel= 1;
465     riOut->riDescription.bdXResolution= 1;
466     riOut->riDescription.bdYResolution= 1;
467     riOut->riDescription.bdUnit= BMunPIXEL;
468 
469     if  ( bmCalculateSizes( &(riOut->riDescription) ) )
470 	{ LDEB(1); return -1;	}
471 
472     riOut->riBytes= bmBackgroundBuffer(
473 			    riOut->riDescription.bdBufferLength,
474 			    riOut->riDescription.bdColorEncoding );
475     if  ( ! riOut->riBytes )
476 	{ LXDEB(riOut->riDescription.bdBufferLength,riOut->riBytes); return -1;	}
477 
478     bmDrawLine( riOut->riBytes, &(riOut->riDescription), x0, y0, x1, y1, 1 );
479 
480     return 0;
481     }
482 
483