Leptonica  1.83.1
Image processing and image analysis suite
checkerboard.c
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 /*
28  * \file checkerboard.c
29  * <pre>
30  *
31  * Find the checker corners where 4 squares come together
32  * PIX *pixFindCheckerboardCorners()
33  *
34  * Generate the hit-miss sels
35  * static SELA *makeCheckerboardCornerSela()
36  * static PIXA *makeCheckerboardCornerPixa()
37  *
38  * The functions in this file locate the corners where four squares
39  * in a checkerboard come together. With a perfectly aligned checkerboard,
40  * the solution is trivial: take the union of two hit-miss transforms (HMTs),
41  * each having a simple diagonal structuring element (sel). The two
42  * sels can be generated from strings such as these, using
43  * selCreateFromString():
44  *
45  * static const char *str1 = "o x"
46  * " "
47  * " "
48  * " C "
49  * " "
50  * " "
51  * "x o";
52  * static const char *str2 = "x o"
53  * " "
54  * " "
55  * " C "
56  * " "
57  * " "
58  * "o x";
59  *
60  * A more interesting problem is to consider the checkerboard viewed from
61  * some arbitrary angle and orientation from the normal. The method
62  * developed here works for a camera located within a cone with an opening
63  * half-angle of about 45 degrees, and with its axis along the normal
64  * to the checkerboard.
65  *
66  * See prog/checkerboard_reg.c for usage.
67  *
68  * </pre>
69  */
70 
71 #ifdef HAVE_CONFIG_H
72 #include <config_auto.h>
73 #endif /* HAVE_CONFIG_H */
74 
75 #include "allheaders.h"
76 
77  /* Static helpers */
78 static SELA *makeCheckerboardCornerSela(l_int32 size, l_int32 dilation,
79  l_int32 nsels, PIXA *pixadb);
80 static PIXA *makeCheckerboardCornerPixa(l_int32 size, l_int32 dilation,
81  l_int32 nsels);
82 
83 static const char selnames[64] = "s_diag1 s_diag2 s_cross1 s_cross2";
84 
109 l_ok
110 pixFindCheckerboardCorners(PIX *pixs,
111  l_int32 size,
112  l_int32 dilation,
113  l_int32 nsels,
114  PIX **ppix_corners,
115  PTA **ppta_corners,
116  PIXA *pixadb)
117 {
118 BOXA *boxa1;
119 PIX *pix1, *pix2, *pix3;
120 PTA *pta1;
121 SEL *sel;
122 SELA *sela;
123 
124  if (ppix_corners) *ppix_corners = NULL;
125  if (ppta_corners) *ppta_corners = NULL;
126  if (!pixs)
127  return ERROR_INT("pixs not defined", __func__, 1);
128  if (size <= 0) size = 7;
129  if (size < 7)
130  return ERROR_INT("size too small", __func__, 1);
131  if (dilation < 1 || dilation > 5)
132  return ERROR_INT("dilation not in [1 ...5]", __func__, 1);
133  if (nsels != 2 && nsels != 4)
134  return ERROR_INT("nsels not 2 or 4", __func__, 1);
135 
136  /* Generate the hit-miss sels for finding corners */
137  sela = makeCheckerboardCornerSela(size, dilation, nsels, pixadb);
138  if (!sela)
139  return ERROR_INT("sela not made", __func__, 1);
140  if (pixadb) {
141  pix1 = selaDisplayInPix(sela, 15, 3, 15, 2);
142  pixaAddPix(pixadb, pix1, L_INSERT);
143  }
144 
145  /* Do the hit-miss transform to find corner locations */
146  pix1 = pixUnionOfMorphOps(pixs, sela, L_MORPH_HMT);
147  if (pixadb) pixaAddPix(pixadb, pix1, L_CLONE);
148  selaDestroy(&sela);
149 
150  /* Remove large noise c.c. */
151  pix2 = pixSelectBySize(pix1, size, size, 8, L_SELECT_IF_BOTH,
152  L_SELECT_IF_LTE, NULL);
153  if (pixadb) pixaAddPix(pixadb, pix2, L_CLONE);
154 
155  /* Thin remaining c.c. */
156  pix3 = pixThinConnected(pix2, L_THIN_FG, 8, 0);
157  if (pixadb) pixaAddPix(pixadb, pix3, L_CLONE);
158 
159  /* Extract the location of the center of each component */
160  boxa1 = pixConnCompBB(pix3, 8);
161  pta1 = boxaExtractCorners(boxa1, L_BOX_CENTER);
162  boxaDestroy(&boxa1);
163  pixDestroy(&pix1);
164  pixDestroy(&pix2);
165  if (pixadb) { /* show the result as colored plus signs on the input */
166  sel = selMakePlusSign(15, 2);
167  pix1 = pixDisplaySelectedPixels(pixs, pix3, sel, 0xff000000);
168  pixaAddPix(pixadb, pix1, L_INSERT);
169  selDestroy(&sel);
170  }
171 
172  if (ppix_corners)
173  *ppix_corners = pix3;
174  else
175  pixDestroy(&pix3);
176  if (ppta_corners)
177  *ppta_corners = pta1;
178  else
179  ptaDestroy(&pta1);
180  return 0;
181 }
182 
183 
198 static SELA *
199 makeCheckerboardCornerSela(l_int32 size,
200  l_int32 dilation,
201  l_int32 nsels,
202  PIXA *pixadb)
203 {
204 PIX *pix1;
205 PIXA *pixa1;
206 SARRAY *sa;
207 SELA *sela;
208 
209  if (size <= 0) size = 7;
210  if (size < 7)
211  return (SELA *)ERROR_PTR("size too small", __func__, NULL);
212  if (dilation < 1 || dilation > 5)
213  return (SELA *)ERROR_PTR("dilation not in [1 ...5]", __func__, NULL);
214  if (nsels != 2 && nsels != 4)
215  return (SELA *)ERROR_PTR("nsels not 2 or 4", __func__, NULL);
216 
217  if ((pixa1 = makeCheckerboardCornerPixa(size, dilation, nsels)) == NULL)
218  return (SELA *)ERROR_PTR("pixa for sels not made", __func__, NULL);
219  if (pixadb) {
220  pix1 = pixaDisplayTiledInColumns(pixa1, 4, 8.0, 15, 2);
221  pixaAddPix(pixadb, pix1, L_INSERT);
222  }
223  sa = sarrayCreateWordsFromString(selnames);
224  sela = selaCreateFromColorPixa(pixa1, sa);
225  pixaDestroy(&pixa1);
226  sarrayDestroy(&sa);
227  if (!sela)
228  return (SELA *)ERROR_PTR("sela not made", __func__, NULL);
229  return sela;
230 }
231 
232 
249 static PIXA *
250 makeCheckerboardCornerPixa(l_int32 size,
251  l_int32 dilation,
252  l_int32 nsels)
253 {
254 PIX *pix1, *pix2, *pix3;
255 PIXA *pixa1;
256 
257  pixa1 = pixaCreate(4);
258 
259  /* Represent diagonal neg slope hits and pos slope misses */
260  pix1 = pixCreate(size, size, 32);
261  pixSetAll(pix1);
262  pix2 = pixCreate(size, size, 1); /* slope -1 line (2 pixel) mask */
263  pixSetPixel(pix2, 1, 1, 1); /* UL corner */
264  pixSetPixel(pix2, size - 2, size - 2, 1); /* LR corner */
265  if (dilation > 1)
266  pixDilateBrick(pix2, pix2, dilation, dilation); /* dilate each pixel */
267  pixSetMasked(pix1, pix2, 0x00ff0000); /* green hit */
268  pix3 = pixRotate90(pix2, 1); /* slope +1 line (2 pixel) mask */
269  pixSetMasked(pix1, pix3, 0xff000000); /* red miss */
270  pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
271  pixaAddPix(pixa1, pix1, L_INSERT);
272 
273  /* Represent diagonal pos slope hits and neg slope misses */
274  pix1 = pixCreate(size, size, 32);
275  pixSetAll(pix1);
276  pixSetMasked(pix1, pix2, 0xff000000); /* red hit */
277  pixSetMasked(pix1, pix3, 0x00ff0000); /* green miss */
278  pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
279  pixaAddPix(pixa1, pix1, L_INSERT);
280  pixDestroy(&pix2);
281  pixDestroy(&pix3);
282 
283  if (nsels == 2)
284  return pixa1;
285 
286  /* Represent cross: vertical hits and horizontal misses */
287  pix1 = pixCreate(size, size, 32);
288  pixSetAll(pix1);
289  pix2 = pixCreate(size, size, 1); /* vertical line (2 pixel) mask */
290  pixSetPixel(pix2, size / 2, 1, 1);
291  pixSetPixel(pix2, size / 2, size - 2, 1);
292  if (dilation > 1)
293  pixDilateBrick(pix2, pix2, dilation, dilation); /* dilate each pixel */
294  pixSetMasked(pix1, pix2, 0x00ff0000); /* green hit */
295  pix3 = pixRotate90(pix2, 1); /* horizontal line (2 pixel) mask */
296  pixSetMasked(pix1, pix3, 0xff000000); /* red miss */
297  pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
298  pixaAddPix(pixa1, pix1, L_INSERT);
299 
300  /* Represent cross: horizontal hits and vertical misses */
301  pix1 = pixCreate(size, size, 32);
302  pixSetAll(pix1);
303  pixSetMasked(pix1, pix3, 0x00ff0000); /* green hit */
304  pixSetMasked(pix1, pix2, 0xff000000); /* red miss */
305  pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
306  pixaAddPix(pixa1, pix1, L_INSERT);
307  pixDestroy(&pix2);
308  pixDestroy(&pix3);
309 
310  return pixa1;
311 }
312 
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:519
PTA * boxaExtractCorners(BOXA *boxa, l_int32 loc)
boxaExtractCorners()
Definition: boxfunc2.c:1295
PIX * pixThinConnected(PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixThinConnected()
Definition: ccthin.c:160
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:307
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:672
PIX * pixUnionOfMorphOps(PIX *pixs, SELA *sela, l_int32 type)
pixUnionOfMorphOps()
Definition: morphapp.c:494
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:799
l_ok pixSetRGBPixel(PIX *pix, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval)
pixSetRGBPixel()
Definition: pix2.c:378
PIX * pixDisplaySelectedPixels(PIX *pixs, PIX *pixm, SEL *sel, l_uint32 val)
pixDisplaySelectedPixels()
Definition: pix3.c:1427
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
@ L_SELECT_IF_LTE
Definition: pix.h:577
@ L_SELECT_IF_BOTH
Definition: pix.h:599
@ L_CLONE
Definition: pix.h:506
@ L_INSERT
Definition: pix.h:504
@ L_BOX_CENTER
Definition: pix.h:913
@ L_THIN_FG
Definition: pix.h:945
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:404
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
PIX * pixSelectBySize(PIX *pixs, l_int32 width, l_int32 height, l_int32 connectivity, l_int32 type, l_int32 relation, l_int32 *pchanged)
pixSelectBySize()
Definition: pixafunc1.c:220
PIX * pixaDisplayTiledInColumns(PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border)
pixaDisplayTiledInColumns()
Definition: pixafunc2.c:912
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:191
PIX * pixRotate90(PIX *pixs, l_int32 direction)
pixRotate90()
Definition: rotateorth.c:162
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:353
SARRAY * sarrayCreateWordsFromString(const char *string)
sarrayCreateWordsFromString()
Definition: sarray1.c:228
void selaDestroy(SELA **psela)
selaDestroy()
Definition: sel1.c:274
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:336
SELA * selaCreateFromColorPixa(PIXA *pixa, SARRAY *sa)
Definition: sel1.c:2121
PIX * selaDisplayInPix(SELA *sela, l_int32 size, l_int32 gthick, l_int32 spacing, l_int32 ncols)
selaDisplayInPix()
Definition: sel1.c:2287
SEL * selMakePlusSign(l_int32 size, l_int32 linewidth)
selMakePlusSign()
Definition: sel2.c:866
Definition: morph.h:74