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