GCC Code Coverage Report


Directory: ./
File: libfprint/fpi-assembling.c
Date: 2024-05-04 14:54:39
Exec Total Coverage
Lines: 200 203 98.5%
Functions: 10 10 100.0%
Branches: 75 92 81.5%

Line Branch Exec Source
1 /*
2 * Image assembling routines
3 * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
4 * Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
5 * Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #define FP_COMPONENT "assembling"
23
24 #include "fpi-log.h"
25 #include "fpi-image.h"
26
27 #include <string.h>
28
29 #include "fpi-assembling.h"
30
31 /**
32 * SECTION:fpi-assembling
33 * @title: Image frame assembly
34 * @short_description: Image frame assembly helpers
35 *
36 * Those are the helpers to manipulate capture data from fingerprint readers
37 * into a uniform image that can be further processed. This is usually used
38 * by drivers for devices which have a small sensor and thus need to capture
39 * data in small stripes.
40 */
41
42 static unsigned int
43 181968 calc_error (struct fpi_frame_asmbl_ctx *ctx,
44 struct fpi_frame *first_frame,
45 struct fpi_frame *second_frame,
46 int dx,
47 int dy)
48 {
49 181968 unsigned int width, height;
50 181968 unsigned int x1, y1, x2, y2, err, i, j;
51
52 181968 width = ctx->frame_width - (dx > 0 ? dx : -dx);
53 181968 height = ctx->frame_height - dy;
54
55
1/2
✓ Branch 0 taken 181968 times.
✗ Branch 1 not taken.
181968 if (height == 0 || width == 0)
56 return INT_MAX;
57
58 y1 = 0;
59 y2 = dy;
60 i = 0;
61 err = 0;
62 2715120 do
63 {
64 2715120 x1 = dx < 0 ? 0 : dx;
65
2/2
✓ Branch 0 taken 1357560 times.
✓ Branch 1 taken 1357560 times.
2715120 x2 = dx < 0 ? -dx : 0;
66 j = 0;
67
68 344962368 do
69 {
70 344962368 unsigned char v1, v2;
71
72
73 344962368 v1 = ctx->get_pixel (ctx, first_frame, x1, y1);
74 344962368 v2 = ctx->get_pixel (ctx, second_frame, x2, y2);
75
2/2
✓ Branch 0 taken 136630760 times.
✓ Branch 1 taken 208331608 times.
344962368 err += v1 > v2 ? v1 - v2 : v2 - v1;
76 344962368 j++;
77 344962368 x1++;
78 344962368 x2++;
79
80 }
81
2/2
✓ Branch 0 taken 342247248 times.
✓ Branch 1 taken 2715120 times.
344962368 while (j < width);
82 2715120 i++;
83 2715120 y1++;
84 2715120 y2++;
85 }
86
2/2
✓ Branch 0 taken 2533152 times.
✓ Branch 1 taken 181968 times.
2715120 while (i < height);
87
88 /* Normalize error */
89 181968 err *= (ctx->frame_height * ctx->frame_width);
90 181968 err /= (height * width);
91
92 181968 return err;
93 }
94
95 /* This function is rather CPU-intensive. It's better to use hardware
96 * to detect movement direction when possible.
97 */
98 static void
99 519 find_overlap (struct fpi_frame_asmbl_ctx *ctx,
100 struct fpi_frame *first_frame,
101 struct fpi_frame *second_frame,
102 int *dx_out,
103 int *dy_out,
104 unsigned int *min_error)
105 {
106 519 int dx, dy;
107 519 unsigned int err;
108
109 519 *min_error = 255 * ctx->frame_height * ctx->frame_width;
110
111 /* Seeking in horizontal and vertical dimensions,
112 * for horizontal dimension we'll check only 8 pixels
113 * in both directions. For vertical direction diff is
114 * rarely less than 2, so start with it.
115 */
116
2/2
✓ Branch 0 taken 11373 times.
✓ Branch 1 taken 519 times.
11892 for (dy = 2; dy < ctx->frame_height; dy++)
117 {
118
2/2
✓ Branch 0 taken 181968 times.
✓ Branch 1 taken 11373 times.
193341 for (dx = -8; dx < 8; dx++)
119 {
120 181968 err = calc_error (ctx, first_frame, second_frame,
121 dx, dy);
122
2/2
✓ Branch 0 taken 6016 times.
✓ Branch 1 taken 175952 times.
181968 if (err < *min_error)
123 {
124 6016 *min_error = err;
125 6016 *dx_out = -dx;
126 6016 *dy_out = dy;
127 }
128 }
129 }
130 519 }
131
132 static unsigned int
133 17 do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
134 GSList *stripes, gboolean reverse)
135 {
136 17 GSList *l;
137 17 GTimer *timer;
138 17 guint num_frames = 1;
139 17 struct fpi_frame *prev_stripe;
140 17 unsigned int min_error;
141 /* Max error is width * height * 255, for AES2501 which has the largest
142 * sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
143 * we might get int overflow. Use 64bit value here to prevent integer overflow
144 */
145 17 unsigned long long total_error = 0;
146
147 17 timer = g_timer_new ();
148
149 /* Skip the first frame */
150 17 prev_stripe = stripes->data;
151
152
2/2
✓ Branch 0 taken 519 times.
✓ Branch 1 taken 17 times.
536 for (l = stripes->next; l != NULL; l = l->next, num_frames++)
153 {
154 519 struct fpi_frame *cur_stripe = l->data;
155
156
2/2
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 343 times.
519 if (reverse)
157 {
158 176 find_overlap (ctx, prev_stripe, cur_stripe,
159 &cur_stripe->delta_x, &cur_stripe->delta_y,
160 &min_error);
161 176 cur_stripe->delta_y = -cur_stripe->delta_y;
162 176 cur_stripe->delta_x = -cur_stripe->delta_x;
163 }
164 else
165 {
166 343 find_overlap (ctx, cur_stripe, prev_stripe,
167 &cur_stripe->delta_x, &cur_stripe->delta_y,
168 &min_error);
169 }
170 519 total_error += min_error;
171
172 519 prev_stripe = cur_stripe;
173 }
174
175 17 g_timer_stop (timer);
176 17 fp_dbg ("calc delta completed in %f secs", g_timer_elapsed (timer, NULL));
177 17 g_timer_destroy (timer);
178
179 17 return total_error / num_frames;
180 }
181
182 /**
183 * fpi_do_movement_estimation:
184 * @ctx: #fpi_frame_asmbl_ctx - frame assembling context
185 * @stripes: a singly-linked list of #fpi_frame
186 *
187 * fpi_do_movement_estimation() estimates the movement between adjacent
188 * frames, populating @delta_x and @delta_y values for each #fpi_frame.
189 *
190 * This function is used for devices that don't do movement estimation
191 * in hardware. If hardware movement estimation is supported, the driver
192 * should populate @delta_x and @delta_y instead.
193 */
194 void
195 6 fpi_do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
196 GSList *stripes)
197 {
198 6 int err, rev_err;
199
200 6 err = do_movement_estimation (ctx, stripes, FALSE);
201 6 rev_err = do_movement_estimation (ctx, stripes, TRUE);
202 6 fp_dbg ("errors: %d rev: %d", err, rev_err);
203
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (err < rev_err)
204 5 do_movement_estimation (ctx, stripes, FALSE);
205 6 }
206
207 static inline void
208 182 aes_blit_stripe (struct fpi_frame_asmbl_ctx *ctx,
209 FpImage *img,
210 struct fpi_frame *stripe,
211 int x, int y)
212 {
213 182 unsigned int ix1, iy1;
214 182 unsigned int fx1, fy1;
215 182 unsigned int fx, fy, ix, iy;
216
217 /* Select starting point inside image and frame */
218
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 150 times.
182 if (x < 0)
219 {
220 32 ix1 = 0;
221 32 fx1 = -x;
222 }
223 else
224 {
225 150 ix1 = x;
226 150 fx1 = 0;
227 }
228
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 182 times.
182 if (y < 0)
230 {
231 iy1 = 0;
232 fy1 = -y;
233 }
234 else
235 {
236 182 iy1 = y;
237 182 fy1 = 0;
238 }
239
240
3/4
✓ Branch 0 taken 4483 times.
✓ Branch 1 taken 182 times.
✓ Branch 2 taken 4483 times.
✗ Branch 3 not taken.
4665 for (fy = fy1, iy = iy1; fy < ctx->frame_height && iy < img->height; fy++, iy++)
241
4/4
✓ Branch 0 taken 609788 times.
✓ Branch 1 taken 3628 times.
✓ Branch 2 taken 608933 times.
✓ Branch 3 taken 855 times.
613416 for (fx = fx1, ix = ix1; fx < ctx->frame_width && ix < img->width; fx++, ix++)
242 608933 img->data[ix + (iy * img->width)] = ctx->get_pixel (ctx, stripe, fx, fy);
243 182 }
244
245 /**
246 * fpi_assemble_frames:
247 * @ctx: #fpi_frame_asmbl_ctx - frame assembling context
248 * @stripes: linked list of #fpi_frame
249 *
250 * fpi_assemble_frames() assembles individual frames into a single image.
251 * It expects @delta_x and @delta_y of #fpi_frame to be populated.
252 *
253 * Returns: a newly allocated #fp_img.
254 */
255 FpImage *
256 6 fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx,
257 GSList *stripes)
258 {
259 6 GSList *l;
260 6 FpImage *img;
261 6 int height = 0;
262 6 int y, x;
263 6 gboolean reverse = FALSE;
264 6 struct fpi_frame *fpi_frame;
265
266 //FIXME g_return_if_fail
267
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 g_return_val_if_fail (stripes != NULL, NULL);
268
269 /* No offset for 1st image */
270 6 fpi_frame = stripes->data;
271 6 fpi_frame->delta_x = 0;
272 6 fpi_frame->delta_y = 0;
273
2/2
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 6 times.
188 for (l = stripes; l != NULL; l = l->next)
274 {
275 182 fpi_frame = l->data;
276
277 182 height += fpi_frame->delta_y;
278 }
279
280 6 fp_dbg ("height is %d", height);
281
282
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (height < 0)
283 {
284 1 reverse = TRUE;
285 1 height = -height;
286 }
287
288 /* For last frame */
289 6 height += ctx->frame_height;
290
291 /* Create buffer big enough for max image */
292 6 img = fp_image_new (ctx->image_width, height);
293 6 img->flags = FPI_IMAGE_COLORS_INVERTED;
294
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 img->flags |= reverse ? 0 : FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED;
295 6 img->width = ctx->image_width;
296 6 img->height = height;
297
298 /* Assemble stripes */
299
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 y = reverse ? (height - ctx->frame_height) : 0;
300 6 x = ((int) ctx->image_width - (int) ctx->frame_width) / 2;
301
302
2/2
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 6 times.
188 for (l = stripes; l != NULL; l = l->next)
303 {
304 182 fpi_frame = l->data;
305
306 182 y += fpi_frame->delta_y;
307 182 x += fpi_frame->delta_x;
308
309 182 aes_blit_stripe (ctx, img, fpi_frame, x, y);
310 }
311
312 return img;
313 }
314
315 static int
316 130402 cmpint (const void *p1, const void *p2, gpointer data)
317 {
318 130402 int a = *((int *) p1);
319 130402 int b = *((int *) p2);
320
321
2/2
✓ Branch 0 taken 102943 times.
✓ Branch 1 taken 27459 times.
130402 if (a < b)
322 return -1;
323
2/2
✓ Branch 0 taken 67574 times.
✓ Branch 1 taken 35369 times.
102943 else if (a == b)
324 return 0;
325 else
326 67574 return 1;
327 }
328
329 static void
330 2 median_filter (int *data, int size, int filtersize)
331 {
332 2 int i;
333 2 int *result = (int *) g_malloc0 (size * sizeof (int));
334 2 int *sortbuf = (int *) g_malloc0 (filtersize * sizeof (int));
335
336
2/2
✓ Branch 1 taken 1634 times.
✓ Branch 2 taken 2 times.
1638 for (i = 0; i < size; i++)
337 {
338 1634 int i1 = i - (filtersize - 1) / 2;
339 1634 int i2 = i + (filtersize - 1) / 2;
340 1634 if (i1 < 0)
341 i1 = 0;
342
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1610 times.
1634 if (i2 >= size)
343 24 i2 = size - 1;
344 1634 memmove (sortbuf, data + i1, (i2 - i1 + 1) * sizeof (int));
345 1634 g_qsort_with_data (sortbuf, i2 - i1 + 1, sizeof (int), cmpint, NULL);
346 1634 result[i] = sortbuf[(i2 - i1 + 1) / 2];
347 }
348 2 memmove (data, result, size * sizeof (int));
349 2 g_free (result);
350 2 g_free (sortbuf);
351 2 }
352
353 static void
354 1485 interpolate_lines (struct fpi_line_asmbl_ctx *ctx,
355 GSList *line1, gint32 y1_f,
356 GSList *line2, gint32 y2_f,
357 unsigned char *output, gint32 yi_f,
358 int size)
359 {
360 1485 int i;
361 1485 unsigned char p1, p2;
362
363
1/2
✓ Branch 0 taken 1485 times.
✗ Branch 1 not taken.
1485 if (!line1 || !line2)
364 return;
365
366
2/2
✓ Branch 0 taken 168540 times.
✓ Branch 1 taken 1485 times.
170025 for (i = 0; i < size; i++)
367 {
368 168540 gint unscaled;
369 168540 p1 = ctx->get_pixel (ctx, line1, i);
370 168540 p2 = ctx->get_pixel (ctx, line2, i);
371
372 168540 unscaled = (yi_f - y1_f) * p2 + (y2_f - yi_f) * p1;
373 168540 output[i] = (unscaled) / (y2_f - y1_f);
374 }
375 }
376
377 /**
378 * fpi_assemble_lines:
379 * @ctx: #fpi_frame_asmbl_ctx - frame assembling context
380 * @lines: linked list of lines
381 * @num_lines: number of items in @lines to process
382 *
383 * #fpi_assemble_lines assembles individual lines into a single image.
384 * It also rescales image to account variable swiping speed.
385 *
386 * Note that @num_lines might be shorter than the length of the list,
387 * if some lines should be skipped.
388 *
389 * Returns: a newly allocated #fp_img.
390 */
391 FpImage *
392 2 fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
393 GSList *lines, size_t num_lines)
394 {
395 /* Number of output lines per distance between two scanners */
396 2 int i;
397 2 GSList *row1, *row2;
398 /* The y coordinate is tracked as a 16.16 fixed point number. All
399 * variables postfixed with _f follow this format here and in
400 * interpolate_lines.
401 * We could also use floating point here, but using fixed point means
402 * we get consistent results across architectures.
403 */
404 2 gint32 y_f = 0;
405 2 int line_ind = 0;
406
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 int *offsets = g_new0 (int, num_lines / 2);
407 2 unsigned char *output = g_malloc0 (ctx->line_width * ctx->max_height);
408 2 FpImage *img;
409
410
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 g_return_val_if_fail (lines != NULL, NULL);
411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 g_return_val_if_fail (num_lines >= 2, NULL);
412
413 2 fp_dbg ("%"G_GINT64_FORMAT, g_get_real_time ());
414
415 2 row1 = lines;
416
3/4
✓ Branch 1 taken 1636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1636 times.
✗ Branch 4 not taken.
1640 for (i = 0; (i < num_lines - 1) && row1; i += 2)
417 {
418 1636 int bestmatch = i;
419 1636 int bestdiff = 0;
420 1636 int j, firstrow, lastrow;
421
422 1636 firstrow = i + 1;
423 1636 lastrow = MIN (i + ctx->max_search_offset, num_lines - 1);
424
425 1636 row2 = g_slist_next (row1);
426
2/2
✓ Branch 0 taken 151370 times.
✓ Branch 1 taken 1636 times.
153006 for (j = firstrow; j <= lastrow; j++)
427 {
428 151370 int diff = ctx->get_deviation (ctx,
429 row1,
430 row2);
431
2/2
✓ Branch 0 taken 15182 times.
✓ Branch 1 taken 136188 times.
151370 if ((j == firstrow) || (diff < bestdiff))
432 {
433 15182 bestdiff = diff;
434 15182 bestmatch = j;
435 }
436
1/2
✓ Branch 0 taken 151370 times.
✗ Branch 1 not taken.
151370 row2 = g_slist_next (row2);
437 }
438 1636 offsets[i / 2] = bestmatch - i;
439 1636 fp_dbg ("%d", offsets[i / 2]);
440 1636 row1 = g_slist_next (row1);
441
1/2
✓ Branch 0 taken 1636 times.
✗ Branch 1 not taken.
1636 if (row1)
442 1636 row1 = g_slist_next (row1);
443 }
444
445 2 median_filter (offsets, (num_lines / 2) - 1, ctx->median_filter_size);
446
447 2 fp_dbg ("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time ());
448
2/2
✓ Branch 1 taken 1636 times.
✓ Branch 2 taken 2 times.
1640 for (i = 0; i <= (num_lines / 2) - 1; i++)
449 1636 fp_dbg ("%d", offsets[i]);
450 row1 = lines;
451
3/4
✓ Branch 0 taken 3271 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3271 times.
✓ Branch 3 taken 2 times.
6544 for (i = 0; i < num_lines - 1; i++, row1 = g_slist_next (row1))
452 {
453 3271 int offset = offsets[i / 2];
454
1/2
✓ Branch 0 taken 3271 times.
✗ Branch 1 not taken.
3271 if (offset > 0)
455 {
456 3271 gint32 ynext_f = y_f + (ctx->resolution << 16) / offset;
457
2/2
✓ Branch 0 taken 1485 times.
✓ Branch 1 taken 3271 times.
4756 while ((line_ind << 16) < ynext_f)
458 {
459
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1485 times.
1485 if (line_ind > ctx->max_height - 1)
460 goto out;
461 1485 interpolate_lines (ctx,
462 row1, y_f,
463 g_slist_next (row1),
464 ynext_f,
465 1485 output + line_ind * ctx->line_width,
466 line_ind << 16,
467
1/2
✓ Branch 0 taken 1485 times.
✗ Branch 1 not taken.
1485 ctx->line_width);
468 1485 line_ind++;
469 }
470 y_f = ynext_f;
471 }
472 }
473 2 out:
474 2 img = fp_image_new (ctx->line_width, line_ind);
475 2 img->height = line_ind;
476 2 img->width = ctx->line_width;
477 2 img->flags = FPI_IMAGE_V_FLIPPED;
478 2 memmove (img->data, output, ctx->line_width * line_ind);
479 2 g_free (offsets);
480 2 g_free (output);
481 2 return img;
482 }
483