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