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 * smallpix_reg.c
29 *
30 * This is a regression test for scaling and rotation.
31 *
32 * The question to be answered is: in the quantization, where, if
33 * anywhere, do we add 0.5?
34 *
35 * The answer is that it should usually, but not always, be omitted.
36 * To see this, we operate on a very small pix and for visualization,
37 * scale up with replication to avoid aliasing and shifting.
38 *
39 * To determine that the current implementations in scalelow.c,
40 * rotate.c and rotateamlow.c are better, change the specific
41 * implementations and re-run.
42 *
43 * In all cases here, the pix to be operated on is of odd size
44 * so that the center pixel is symmetrically located, and there
45 * are a couple of black pixels outside the pattern so that edge
46 * effects (e.g., in pixScaleSmooth()) do not affect the results.
47 */
48
49 #include "allheaders.h"
50
DisplayPix(PIXA ** ppixa,l_int32 x,l_int32 y,char * fname)51 void DisplayPix(PIXA **ppixa, l_int32 x, l_int32 y, char *fname)
52 {
53 PIX *pixt;
54
55 pixt = pixaDisplay(*ppixa, 0, 0);
56 if (fname)
57 pixWrite(fname, pixt, IFF_PNG);
58 pixDisplay(pixt, x, y);
59 pixDestroy(&pixt);
60 pixaDestroy(ppixa);
61 }
62
63
main(int argc,char ** argv)64 int main(int argc,
65 char **argv)
66 {
67 l_int32 i;
68 l_float32 pi, scale, angle;
69 PIX *pixc, *pixm, *pix1, *pix2, *pix3;
70 PIXA *pixa;
71 PTA *pta1, *pta2, *pta3, *pta4;
72
73 /* Make a small test image, the hard way! */
74 pi = 3.1415926535;
75 pixc = pixCreate(9, 9, 32);
76 pixm = pixCreate(9, 9, 1);
77 pta1 = generatePtaLineFromPt(4, 4, 3.1, 0.0);
78 pta2 = generatePtaLineFromPt(4, 4, 3.1, 0.5 * pi);
79 pta3 = generatePtaLineFromPt(4, 4, 3.1, pi);
80 pta4 = generatePtaLineFromPt(4, 4, 3.1, 1.5 * pi);
81 ptaJoin(pta1, pta2, 0, -1);
82 ptaJoin(pta1, pta3, 0, -1);
83 ptaJoin(pta1, pta4, 0, -1);
84 pixRenderPta(pixm, pta1, L_SET_PIXELS);
85 pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000);
86 ptaDestroy(&pta1);
87 ptaDestroy(&pta2);
88 ptaDestroy(&pta3);
89 ptaDestroy(&pta4);
90 pixDestroy(&pixm);
91
92 /* Results differ for scaleSmoothLow() w/ and w/out + 0.5.
93 * Neither is properly symmetric (with symm pattern on odd-sized
94 * pix, because the smoothing is destroying the symmetry. */
95 pixa = pixaCreate(11);
96 pix1 = pixExpandReplicate(pixc, 2);
97 for (i = 0; i < 11; i++) {
98 scale = 0.30 + 0.035 * (l_float32)i;
99 pix2 = pixScaleSmooth(pix1, scale, scale);
100 pix3 = pixExpandReplicate(pix2, 6);
101 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
102 pixDestroy(&pix2);
103 pixDestroy(&pix3);
104 }
105 pixDestroy(&pix1);
106 DisplayPix(&pixa, 100, 100, NULL);
107
108 /* Results same for pixScaleAreaMap w/ and w/out + 0.5 */
109 pixa = pixaCreate(11);
110 pix1 = pixExpandReplicate(pixc, 2);
111 for (i = 0; i < 11; i++) {
112 scale = 0.30 + 0.035 * (l_float32)i;
113 pix2 = pixScaleAreaMap(pix1, scale, scale);
114 pix3 = pixExpandReplicate(pix2, 6);
115 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
116 pixDestroy(&pix2);
117 pixDestroy(&pix3);
118 }
119 pixDestroy(&pix1);
120 DisplayPix(&pixa, 100, 200, NULL);
121
122 /* Results better for pixScaleBySampling with + 0.5, for small,
123 * odd-dimension pix. */
124 pixa = pixaCreate(11);
125 pix1 = pixExpandReplicate(pixc, 2);
126 for (i = 0; i < 11; i++) {
127 scale = 0.30 + 0.035 * (l_float32)i;
128 pix2 = pixScaleBySampling(pix1, scale, scale);
129 pix3 = pixExpandReplicate(pix2, 6);
130 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
131 pixDestroy(&pix2);
132 pixDestroy(&pix3);
133 }
134 pixDestroy(&pix1);
135 DisplayPix(&pixa, 100, 300, NULL);
136
137 /* Results same for pixRotateAM w/ and w/out + 0.5 */
138 pixa = pixaCreate(11);
139 pix1 = pixExpandReplicate(pixc, 1);
140 for (i = 0; i < 11; i++) {
141 angle = 0.10 + 0.05 * (l_float32)i;
142 pix2 = pixRotateAM(pix1, angle, L_BRING_IN_BLACK);
143 pix3 = pixExpandReplicate(pix2, 8);
144 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
145 pixDestroy(&pix2);
146 pixDestroy(&pix3);
147 }
148 pixDestroy(&pix1);
149 DisplayPix(&pixa, 100, 400, NULL);
150
151 /* If the size is odd, we express the center exactly, and the
152 * results are better for pixRotateBySampling() w/out 0.5
153 * However, if the size is even, the center value is not
154 * exact, and if we choose it 0.5 smaller than the actual
155 * center, we get symmetrical results with +0.5.
156 * So we choose not to include + 0.5. */
157 pixa = pixaCreate(11);
158 pix1 = pixExpandReplicate(pixc, 1);
159 for (i = 0; i < 11; i++) {
160 angle = 0.10 + 0.05 * (l_float32)i;
161 pix2 = pixRotateBySampling(pix1, 4, 4, angle, L_BRING_IN_BLACK);
162 pix3 = pixExpandReplicate(pix2, 8);
163 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
164 pixDestroy(&pix2);
165 pixDestroy(&pix3);
166 }
167 pixDestroy(&pix1);
168 DisplayPix(&pixa, 100, 500, NULL);
169
170 /* Results same for pixRotateAMCorner w/ and w/out + 0.5 */
171 pixa = pixaCreate(11);
172 pix1 = pixExpandReplicate(pixc, 1);
173 for (i = 0; i < 11; i++) {
174 angle = 0.10 + 0.05 * (l_float32)i;
175 pix2 = pixRotateAMCorner(pix1, angle, L_BRING_IN_BLACK);
176 pix3 = pixExpandReplicate(pix2, 8);
177 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
178 pixDestroy(&pix2);
179 pixDestroy(&pix3);
180 }
181 pixDestroy(&pix1);
182 DisplayPix(&pixa, 100, 600, NULL);
183
184 /* Results better for pixRotateAMColorFast without + 0.5 */
185 pixa = pixaCreate(11);
186 pix1 = pixExpandReplicate(pixc, 1);
187 for (i = 0; i < 11; i++) {
188 angle = 0.10 + 0.05 * (l_float32)i;
189 pix2 = pixRotateAMColorFast(pix1, angle, 0);
190 pix3 = pixExpandReplicate(pix2, 8);
191 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
192 pixDestroy(&pix2);
193 pixDestroy(&pix3);
194 }
195 pixDestroy(&pix1);
196 DisplayPix(&pixa, 100, 700, NULL);
197
198 /* Results slightly better for pixScaleColorLI() w/out + 0.5 */
199 pixa = pixaCreate(11);
200 pix1 = pixExpandReplicate(pixc, 1);
201 for (i = 0; i < 11; i++) {
202 scale = 1.0 + 0.2 * (l_float32)i;
203 pix2 = pixScaleColorLI(pix1, scale, scale);
204 pix3 = pixExpandReplicate(pix2, 4);
205 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
206 pixDestroy(&pix2);
207 pixDestroy(&pix3);
208 }
209 pixDestroy(&pix1);
210 DisplayPix(&pixa, 100, 800, NULL);
211
212 /* Results slightly better for pixScaleColorLI() w/out + 0.5 */
213 pixa = pixaCreate(11);
214 pix1 = pixExpandReplicate(pixc, 1);
215 for (i = 0; i < 11; i++) {
216 scale = 1.0 + 0.2 * (l_float32)i;
217 pix2 = pixScaleLI(pix1, scale, scale);
218 pix3 = pixExpandReplicate(pix2, 4);
219 pixSaveTiled(pix3, pixa, 1.0, (i == 0), 20, 32);
220 pixDestroy(&pix2);
221 pixDestroy(&pix3);
222 }
223 pixDestroy(&pix1);
224 DisplayPix(&pixa, 100, 940, NULL);
225
226 pixDestroy(&pixc);
227 return 0;
228 }
229