assembling: Use fixed point for image assembly

Using floating point causes architecture dependent results due to
accuracy/rounding differences. It is not hard to switch to fixed point,
and while this does cause quite different rounding errors, the
difference is small.

Fixes: #200
This commit is contained in:
Benjamin Berg 2019-11-28 15:50:20 +01:00 committed by Marco Trevisan
parent a7541b1f76
commit 8cc0fd321f
2 changed files with 21 additions and 11 deletions

View file

@ -385,8 +385,10 @@ median_filter (int *data, int size, int filtersize)
static void static void
interpolate_lines (struct fpi_line_asmbl_ctx *ctx, interpolate_lines (struct fpi_line_asmbl_ctx *ctx,
GSList *line1, float y1, GSList *line2, GSList *line1, gint32 y1_f,
float y2, unsigned char *output, float yi, int size) GSList *line2, gint32 y2_f,
unsigned char *output, gint32 yi_f,
int size)
{ {
int i; int i;
unsigned char p1, p2; unsigned char p1, p2;
@ -396,10 +398,12 @@ interpolate_lines (struct fpi_line_asmbl_ctx *ctx,
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
{ {
gint unscaled;
p1 = ctx->get_pixel (ctx, line1, i); p1 = ctx->get_pixel (ctx, line1, i);
p2 = ctx->get_pixel (ctx, line2, i); p2 = ctx->get_pixel (ctx, line2, i);
output[i] = (float) p1
+ (yi - y1) / (y2 - y1) * (p2 - p1); unscaled = (yi_f - y1_f) * p2 + (y2_f - yi_f) * p1;
output[i] = (unscaled) / (y2_f - y1_f);
} }
} }
@ -424,7 +428,13 @@ fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
/* Number of output lines per distance between two scanners */ /* Number of output lines per distance between two scanners */
int i; int i;
GSList *row1, *row2; GSList *row1, *row2;
float y = 0.0; /* The y coordinate is tracked as a 16.16 fixed point number. All
* variables postfixed with _f follow this format here and in
* interpolate_lines.
* We could also use floating point here, but using fixed point means
* we get consistent results across architectures.
*/
gint32 y_f = 0;
int line_ind = 0; int line_ind = 0;
int *offsets = g_new0 (int, num_lines / 2); int *offsets = g_new0 (int, num_lines / 2);
unsigned char *output = g_malloc0 (ctx->line_width * ctx->max_height); unsigned char *output = g_malloc0 (ctx->line_width * ctx->max_height);
@ -476,21 +486,21 @@ fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
int offset = offsets[i / 2]; int offset = offsets[i / 2];
if (offset > 0) if (offset > 0)
{ {
float ynext = y + (float) ctx->resolution / offset; gint32 ynext_f = y_f + (ctx->resolution << 16) / offset;
while (line_ind < ynext) while ((line_ind << 16) < ynext_f)
{ {
if (line_ind > ctx->max_height - 1) if (line_ind > ctx->max_height - 1)
goto out; goto out;
interpolate_lines (ctx, interpolate_lines (ctx,
row1, y, row1, y_f,
g_slist_next (row1), g_slist_next (row1),
ynext, ynext_f,
output + line_ind * ctx->line_width, output + line_ind * ctx->line_width,
line_ind, line_ind << 16,
ctx->line_width); ctx->line_width);
line_ind++; line_ind++;
} }
y = ynext; y_f = ynext_f;
} }
} }
out: out:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB