Leptonica 1.82.0
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 */
78static SELA *makeCheckerboardCornerSela(l_int32 size, l_int32 dilation,
79 l_int32 nsels, PIXA *pixadb);
80static PIXA *makeCheckerboardCornerPixa(l_int32 size, l_int32 dilation,
81 l_int32 nsels);
82
83static const char selnames[64] = "s_diag1 s_diag2 s_cross1 s_cross2";
84
109l_ok
110pixFindCheckerboardCorners(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{
118BOXA *boxa1;
119PIX *pix1, *pix2, *pix3;
120PTA *pta1;
121SEL *sel;
122SELA *sela;
123
124 PROCNAME("pixFindCheckerboardCorners");
125
126 if (ppix_corners) *ppix_corners = NULL;
127 if (ppta_corners) *ppta_corners = NULL;
128 if (!pixs)
129 return ERROR_INT("pixs not defined", procName, 1);
130 if (size <= 0) size = 7;
131 if (size < 7)
132 return ERROR_INT("size too small", procName, 1);
133 if (dilation < 1 || dilation > 5)
134 return ERROR_INT("dilation not in [1 ...5]", procName, 1);
135 if (nsels != 2 && nsels != 4)
136 return ERROR_INT("nsels not 2 or 4", procName, 1);
137
138 /* Generate the hit-miss sels for finding corners */
139 sela = makeCheckerboardCornerSela(size, dilation, nsels, pixadb);
140 if (!sela)
141 return ERROR_INT("sela not made", procName, 1);
142 if (pixadb) {
143 pix1 = selaDisplayInPix(sela, 15, 3, 15, 2);
144 pixaAddPix(pixadb, pix1, L_INSERT);
145 }
146
147 /* Do the hit-miss transform to find corner locations */
148 pix1 = pixUnionOfMorphOps(pixs, sela, L_MORPH_HMT);
149 if (pixadb) pixaAddPix(pixadb, pix1, L_CLONE);
150 selaDestroy(&sela);
151
152 /* Remove large noise c.c. */
153 pix2 = pixSelectBySize(pix1, size, size, 8, L_SELECT_IF_BOTH,
154 L_SELECT_IF_LTE, NULL);
155 if (pixadb) pixaAddPix(pixadb, pix2, L_CLONE);
156
157 /* Thin remaining c.c. */
158 pix3 = pixThinConnected(pix2, L_THIN_FG, 8, 0);
159 if (pixadb) pixaAddPix(pixadb, pix3, L_CLONE);
160
161 /* Extract the location of the center of each component */
162 boxa1 = pixConnCompBB(pix3, 8);
163 pta1 = boxaExtractCorners(boxa1, L_BOX_CENTER);
164 boxaDestroy(&boxa1);
165 pixDestroy(&pix1);
166 pixDestroy(&pix2);
167 if (pixadb) { /* show the result as colored plus signs on the input */
168 sel = selMakePlusSign(15, 2);
169 pix1 = pixDisplaySelectedPixels(pixs, pix3, sel, 0xff000000);
170 pixaAddPix(pixadb, pix1, L_INSERT);
171 selDestroy(&sel);
172 }
173
174 if (ppix_corners)
175 *ppix_corners = pix3;
176 else
177 pixDestroy(&pix3);
178 if (ppta_corners)
179 *ppta_corners = pta1;
180 else
181 ptaDestroy(&pta1);
182 return 0;
183}
184
185
200static SELA *
201makeCheckerboardCornerSela(l_int32 size,
202 l_int32 dilation,
203 l_int32 nsels,
204 PIXA *pixadb)
205{
206PIX *pix1;
207PIXA *pixa1;
208SARRAY *sa;
209SELA *sela;
210
211 PROCNAME("makeCheckerboardCornerSela");
212
213 if (size <= 0) size = 7;
214 if (size < 7)
215 return (SELA *)ERROR_PTR("size too small", procName, NULL);
216 if (dilation < 1 || dilation > 5)
217 return (SELA *)ERROR_PTR("dilation not in [1 ...5]", procName, NULL);
218 if (nsels != 2 && nsels != 4)
219 return (SELA *)ERROR_PTR("nsels not 2 or 4", procName, NULL);
220
221 if ((pixa1 = makeCheckerboardCornerPixa(size, dilation, nsels)) == NULL)
222 return (SELA *)ERROR_PTR("pixa for sels not made", procName, NULL);
223 if (pixadb) {
224 pix1 = pixaDisplayTiledInColumns(pixa1, 4, 8.0, 15, 2);
225 pixaAddPix(pixadb, pix1, L_INSERT);
226 }
227 sa = sarrayCreateWordsFromString(selnames);
228 sela = selaCreateFromColorPixa(pixa1, sa);
229 pixaDestroy(&pixa1);
230 sarrayDestroy(&sa);
231 if (!sela)
232 return (SELA *)ERROR_PTR("sela not made", procName, NULL);
233 return sela;
234}
235
236
253static PIXA *
254makeCheckerboardCornerPixa(l_int32 size,
255 l_int32 dilation,
256 l_int32 nsels)
257{
258PIX *pix1, *pix2, *pix3;
259PIXA *pixa1;
260
261 pixa1 = pixaCreate(4);
262
263 /* Represent diagonal neg slope hits and pos slope misses */
264 pix1 = pixCreate(size, size, 32);
265 pixSetAll(pix1);
266 pix2 = pixCreate(size, size, 1); /* slope -1 line (2 pixel) mask */
267 pixSetPixel(pix2, 1, 1, 1); /* UL corner */
268 pixSetPixel(pix2, size - 2, size - 2, 1); /* LR corner */
269 if (dilation > 1)
270 pixDilateBrick(pix2, pix2, dilation, dilation); /* dilate each pixel */
271 pixSetMasked(pix1, pix2, 0x00ff0000); /* green hit */
272 pix3 = pixRotate90(pix2, 1); /* slope +1 line (2 pixel) mask */
273 pixSetMasked(pix1, pix3, 0xff000000); /* red miss */
274 pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
275 pixaAddPix(pixa1, pix1, L_INSERT);
276
277 /* Represent diagonal pos slope hits and neg slope misses */
278 pix1 = pixCreate(size, size, 32);
279 pixSetAll(pix1);
280 pixSetMasked(pix1, pix2, 0xff000000); /* red hit */
281 pixSetMasked(pix1, pix3, 0x00ff0000); /* green miss */
282 pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
283 pixaAddPix(pixa1, pix1, L_INSERT);
284 pixDestroy(&pix2);
285 pixDestroy(&pix3);
286
287 if (nsels == 2)
288 return pixa1;
289
290 /* Represent cross: vertical hits and horizontal misses */
291 pix1 = pixCreate(size, size, 32);
292 pixSetAll(pix1);
293 pix2 = pixCreate(size, size, 1); /* vertical line (2 pixel) mask */
294 pixSetPixel(pix2, size / 2, 1, 1);
295 pixSetPixel(pix2, size / 2, size - 2, 1);
296 if (dilation > 1)
297 pixDilateBrick(pix2, pix2, dilation, dilation); /* dilate each pixel */
298 pixSetMasked(pix1, pix2, 0x00ff0000); /* green hit */
299 pix3 = pixRotate90(pix2, 1); /* horizontal line (2 pixel) mask */
300 pixSetMasked(pix1, pix3, 0xff000000); /* red miss */
301 pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
302 pixaAddPix(pixa1, pix1, L_INSERT);
303
304 /* Represent cross: horizontal hits and vertical misses */
305 pix1 = pixCreate(size, size, 32);
306 pixSetAll(pix1);
307 pixSetMasked(pix1, pix3, 0x00ff0000); /* green hit */
308 pixSetMasked(pix1, pix2, 0xff000000); /* red miss */
309 pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */
310 pixaAddPix(pixa1, pix1, L_INSERT);
311 pixDestroy(&pix2);
312 pixDestroy(&pix3);
313
314 return pixa1;
315}
316
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
PTA * boxaExtractCorners(BOXA *boxa, l_int32 loc)
boxaExtractCorners()
Definition: boxfunc2.c:1322
PIX * pixThinConnected(PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters)
pixThinConnected()
Definition: ccthin.c:162
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:310
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:688
PIX * pixUnionOfMorphOps(PIX *pixs, SELA *sela, l_int32 type)
pixUnionOfMorphOps()
Definition: morphapp.c:505
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
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:817
l_ok pixSetRGBPixel(PIX *pix, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval)
pixSetRGBPixel()
Definition: pix2.c:382
PIX * pixDisplaySelectedPixels(PIX *pixs, PIX *pixm, SEL *sel, l_uint32 val)
pixDisplaySelectedPixels()
Definition: pix3.c:1453
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
@ L_SELECT_IF_LTE
Definition: pix.h:784
@ L_SELECT_IF_BOTH
Definition: pix.h:806
@ L_CLONE
Definition: pix.h:713
@ L_INSERT
Definition: pix.h:711
@ L_BOX_CENTER
Definition: pix.h:1120
@ L_THIN_FG
Definition: pix.h:1152
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
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:219
PIX * pixaDisplayTiledInColumns(PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border)
pixaDisplayTiledInColumns()
Definition: pixafunc2.c:930
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:195
PIX * pixRotate90(PIX *pixs, l_int32 direction)
pixRotate90()
Definition: rotateorth.c:166
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:362
SARRAY * sarrayCreateWordsFromString(const char *string)
sarrayCreateWordsFromString()
Definition: sarray1.c:233
void selaDestroy(SELA **psela)
selaDestroy()
Definition: sel1.c:276
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:340
SELA * selaCreateFromColorPixa(PIXA *pixa, SARRAY *sa)
Definition: sel1.c:2203
PIX * selaDisplayInPix(SELA *sela, l_int32 size, l_int32 gthick, l_int32 spacing, l_int32 ncols)
selaDisplayInPix()
Definition: sel1.c:2373
SEL * selMakePlusSign(l_int32 size, l_int32 linewidth)
selMakePlusSign()
Definition: sel2.c:878
Definition: pix.h:492
Definition: pix.h:139
Definition: pix.h:456
Definition: pix.h:517
Definition: array.h:127
Definition: morph.h:74