2 * Separate calculating projected coordinates and transforming the pixels.
4 * Copyright (C) 2018 Antonio Ospite <ao2@ao2.it>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * The idea behind these functions is that in some use cases the perspective
22 * deformation does not change, it's only the projected data which changes, so
23 * there's no need to recalculate the projection parameters every time.
25 * It is enough to apply the pre-calculated projection to the new data,
26 * possibly using a region-of-interest, to avoid clipping the image in case
27 * it's larger than the viewport.
30 #include "projective_split.h"
33 * Pre-calculate the transformation map.
35 * This can be useful if the transform mapping does not change but we want to
36 * apply the same transform to different data.
39 _pixProjectiveSampled_precalc_map(BOX *viewport,
46 return (struct p *)ERROR_PTR("vc not defined", __func__, NULL);
48 map = malloc(viewport->w * viewport->h * sizeof(*map));
50 return (struct p *)ERROR_PTR("cannot allocate map", __func__, NULL);
52 for (i = 0; i < viewport->h; i++) {
53 for (j = 0; j < viewport->w; j++) {
54 /* XXX: transform i and j by considering the viewport origin */
55 projectiveXformSampledPt(vc, j, i, &x, &y);
57 /* XXX: This is a hack to avoid conditionals when
59 * Basically we set all out of bound pixels to be the same
60 * as the pixel at 0,0.
62 * When applying the map make sure that the pixel at
63 * 0,0 is set to the desired "background" color.
65 if (x < 0 || y < 0 || x >= viewport->w || y >= viewport->h) {
70 map[i * viewport->w + j].x = x;
71 map[i * viewport->w + j].y = y;
83 * Apply the map pre-calculated transformation map to an image, but only to
84 * a region of interest.
86 * This is a NEON optimized version, however the speedup is not that
87 * impressive because moving pixels around always means accessing uncontiguous
91 _pixProjectiveSampled_apply_map_dest_roi(PIX *pixs,
97 int32_t i, j, w, h, d, wpls, wpld;
98 uint32_t *datas, *datad, *lined;
110 PROCNAME("pixProjectiveSampled_neon");
113 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
115 return (PIX *)ERROR_PTR("map not defined", procName, NULL);
116 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
117 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
118 pixGetDimensions(pixs, &w, &h, &d);
120 return (PIX *)ERROR_PTR("depth not 32", procName, NULL);
122 return (PIX *)ERROR_PTR("pixd not defined", procName, NULL);
124 /* Scan over the dest pixels */
125 datas = pixGetData(pixs);
126 wpls = pixGetWpl(pixs);
127 datad = pixGetData(pixd);
128 wpld = pixGetWpl(pixd);
130 roi_x = vmovq_n_u32(roi->x);
131 roi_y = vmovq_n_u32(roi->y);
132 wpls_v = vmovq_n_u32(wpls);
135 * Save the value of the pixel at 0,0 in the destination image,
136 * and set it to black, because the map uses the pixel at 0,0 for out
139 pixGetPixel(pixs, roi->x, roi->y, &pixel0);
140 pixSetPixel(pixs, roi->x, roi->y, 0);
142 for (i = 0; i < roi->h; i++) {
143 lined = datad + i * wpld;
145 for (j = 0; j < roi->w; j += 4) {
146 unsigned int map_index = stride + j;
148 point = vld2q_u32((uint32_t *)(map + map_index));
150 x_offset = vaddq_u32(point.val[0], roi_x);
152 y_offset = vaddq_u32(point.val[1], roi_y);
154 lines = vmlaq_u32(x_offset, y_offset, wpls_v);
156 lined[j + 0] = *(datas + lines[0]);
157 lined[j + 1] = *(datas + lines[1]);
158 lined[j + 2] = *(datas + lines[2]);
159 lined[j + 3] = *(datas + lines[3]);
163 /* restore the previous value */
164 pixSetPixel(pixs, roi->x, roi->y, pixel0);
172 * Apply the map pre-calculated transformation map to an image, but only to
173 * a region of interest.
176 _pixProjectiveSampled_apply_map_dest_roi(PIX *pixs,
182 l_int32 i, j, w, h, d, wpls, wpld;
183 l_uint32 *datas, *datad, *lined;
184 l_uint32 datas_offset;
189 PROCNAME("pixProjectiveSampled");
192 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
194 return (PIX *)ERROR_PTR("map not defined", procName, NULL);
195 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
196 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
198 pixGetDimensions(pixs, &w, &h, &d);
200 return (PIX *)ERROR_PTR("depth not 32", procName, NULL);
202 return (PIX *)ERROR_PTR("pixd not defined", procName, NULL);
204 /* Scan over the dest pixels */
205 datas = pixGetData(pixs);
206 wpls = pixGetWpl(pixs);
207 datad = pixGetData(pixd);
208 wpld = pixGetWpl(pixd);
211 * Save the value of the pixel at 0,0 in the destination image,
212 * and set it to black, because the map uses the pixel at 0,0 for out
215 * This is just a dirty trick to avoid a conditional deep in the loop
218 * A more general algorithm would check for out of bounds explicitly,
219 * with something like:
221 * if (point->x >= 0 && point->y >= 0 && point->x < roi->w && point->y < roi->h) {
222 * datas_offset = (point->x + roi->x) + (point->y + roi->y) * wpls;
223 * *(lined + j) = *(datas + datas_offset);
226 pixGetPixel(pixs, roi->x, roi->y, &pixel0);
227 pixSetPixel(pixs, roi->x, roi->y, 0);
229 for (i = 0; i < roi->h; i++) {
230 lined = datad + i * wpld;
232 for (j = 0; j < roi->w; j++) {
233 point = map + stride + j;
235 datas_offset = (point->x + roi->x) + (point->y + roi->y) * wpls;
236 *(lined + j) = *(datas + datas_offset);
240 /* restore the previous value */
241 pixSetPixel(pixs, roi->x, roi->y, pixel0);