Makefile: reload udev rules as soon as we install them
[experiments/cyclabile.git] / projective_split.c
1 /*
2  * Separate calculating projected coordinates and transforming the pixels.
3  *
4  * Copyright (C) 2018  Antonio Ospite <ao2@ao2.it>
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 /*
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.
24  *
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.
28  */
29
30 #include "projective_split.h"
31
32 /*
33  * Pre-calculate the transformation map.
34  *
35  * This can be useful if the transform mapping does not change but we want to
36  * apply the same transform to different data.
37  */
38 struct p *
39 _pixProjectiveSampled_precalc_map(BOX        *viewport,
40                                         l_float32  *vc)
41 {
42         l_int32    i, j, x, y;
43         struct p   *map;
44
45         if (!vc)
46                 return (struct p *)ERROR_PTR("vc not defined", __func__, NULL);
47
48         map = malloc(viewport->w * viewport->h * sizeof(*map));
49         if (map == NULL)
50                 return (struct p *)ERROR_PTR("cannot allocate map", __func__, NULL);
51
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);
56
57                         /* XXX: This is a hack to avoid conditionals when
58                          * applying the map.
59                          * Basically we set all out of bound pixels to be the same
60                          * as the pixel at 0,0.
61                          *
62                          * When applying the map make sure that the pixel at
63                          * 0,0 is set to the desired "background" color.
64                          */
65                         if (x < 0 || y < 0 || x >= viewport->w || y >= viewport->h) {
66                                 x = 0;
67                                 y = 0;
68                         }
69
70                         map[i * viewport->w + j].x = x;
71                         map[i * viewport->w + j].y = y;
72                 }
73         }
74
75         return map;
76 }
77
78 #ifdef USE_NEON
79
80 #include <arm_neon.h>
81
82 /*
83  * Apply the map pre-calculated transformation map to an image, but only to
84  * a region of interest.
85  *
86  * This is a NEON optimized version, however the speedup is not that
87  * impressive because moving pixels around always means accessing uncontiguous
88  * memory.
89  */
90 PIX *
91 _pixProjectiveSampled_apply_map_dest_roi(PIX        *pixs,
92                                struct p   *map,
93                                l_int32     incolor,
94                                PIX        *pixd,
95                                BOX        *roi)
96 {
97         int32_t     i, j, w, h, d, wpls, wpld;
98         uint32_t   *datas, *datad, *lined;
99         l_uint32 pixel0;
100
101         uint32_t stride;
102         uint32x4x2_t point;
103         uint32x4_t lines;
104         uint32x4_t roi_x;
105         uint32x4_t roi_y;
106         uint32x4_t x_offset;
107         uint32x4_t y_offset;
108         uint32x4_t wpls_v;
109
110         PROCNAME("pixProjectiveSampled_neon");
111
112         if (!pixs)
113                 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
114         if (!map)
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);
119         if (d != 32)
120                 return (PIX *)ERROR_PTR("depth not 32", procName, NULL);
121         if (!pixd)
122                 return (PIX *)ERROR_PTR("pixd not defined", procName, NULL);
123
124         /* Scan over the dest pixels */
125         datas = pixGetData(pixs);
126         wpls = pixGetWpl(pixs);
127         datad = pixGetData(pixd);
128         wpld = pixGetWpl(pixd);
129
130         roi_x = vmovq_n_u32(roi->x);
131         roi_y = vmovq_n_u32(roi->y);
132         wpls_v = vmovq_n_u32(wpls);
133
134         /*
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
137          * of bounds pixels.
138          */
139         pixGetPixel(pixs, roi->x, roi->y, &pixel0);
140         pixSetPixel(pixs, roi->x, roi->y, 0);
141
142         for (i = 0; i < roi->h; i++) {
143                 lined = datad + i * wpld;
144                 stride = i * roi->w;
145                 for (j = 0; j < roi->w; j += 4) {
146                         unsigned int map_index = stride + j;
147
148                         point = vld2q_u32((uint32_t *)(map + map_index));
149
150                         x_offset = vaddq_u32(point.val[0], roi_x);
151
152                         y_offset = vaddq_u32(point.val[1], roi_y);
153
154                         lines = vmlaq_u32(x_offset, y_offset, wpls_v);
155
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]);
160                 }
161         }
162
163         /* restore the previous value */
164         pixSetPixel(pixs, roi->x, roi->y, pixel0);
165
166         return pixd;
167 }
168
169 #else
170
171 /*
172  * Apply the map pre-calculated transformation map to an image, but only to
173  * a region of interest.
174  */
175 PIX *
176 _pixProjectiveSampled_apply_map_dest_roi(PIX        *pixs,
177                                struct p   *map,
178                                l_int32     incolor,
179                                PIX        *pixd,
180                                BOX        *roi)
181 {
182         l_int32     i, j, w, h, d, wpls, wpld;
183         l_uint32   *datas, *datad, *lined;
184         l_uint32 datas_offset;
185         l_uint32 stride;
186         l_uint32 pixel0;
187         struct p *point;
188
189         PROCNAME("pixProjectiveSampled");
190
191         if (!pixs)
192                 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
193         if (!map)
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);
197
198         pixGetDimensions(pixs, &w, &h, &d);
199         if (d != 32)
200                 return (PIX *)ERROR_PTR("depth not 32", procName, NULL);
201         if (!pixd)
202                 return (PIX *)ERROR_PTR("pixd not defined", procName, NULL);
203
204         /* Scan over the dest pixels */
205         datas = pixGetData(pixs);
206         wpls = pixGetWpl(pixs);
207         datad = pixGetData(pixd);
208         wpld = pixGetWpl(pixd);
209
210         /*
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
213          * of bounds pixels.
214          *
215          * This is just a dirty trick to avoid a conditional deep in the loop
216          * below.
217          *
218          * A more general algorithm would check for out of bounds explicitly,
219          * with something like:
220          *
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);
224          *      }
225          */
226         pixGetPixel(pixs, roi->x, roi->y, &pixel0);
227         pixSetPixel(pixs, roi->x, roi->y, 0);
228
229         for (i = 0; i < roi->h; i++) {
230                 lined = datad + i * wpld;
231                 stride = i * roi->w;
232                 for (j = 0; j < roi->w; j++) {
233                         point = map + stride + j;
234
235                         datas_offset = (point->x + roi->x) + (point->y + roi->y) * wpls;
236                         *(lined + j) = *(datas + datas_offset);
237                 }
238         }
239
240         /* restore the previous value */
241         pixSetPixel(pixs, roi->x, roi->y, pixel0);
242
243         return pixd;
244 }
245
246
247 #endif