Traverse-Research / ispc-downsampler

Image downsampler using a Lanczos filter implemented in ISPC
Other
11 stars 1 forks source link

lanczos3: Const-fold `offset` calculation to hardcoded `0.5` #28

Closed MarijnS95 closed 1 year ago

MarijnS95 commented 1 year ago

Depends on #26

The usefulness of this magic took a fair bit of time to understand, while we can trivially remove it after deducing that it always computes to the constant 0.5, and gets rid of some strange bright spots in the center of our image compared to #26.

Before:

square_test_result

After:

square_test_result

First, we start by knowing that uv is divided by target_size before it is passed to resample_internal(). Hence, if we multiply it by target_size again, there should be no fractional part and center_pixel always becomes 0. Floating point rounding errors being gone now, this is what solves the bright spots in the center of the image mentioned above.

Then we are left with:

center = uv - (0.0 - 0.5) * inv_target_size

Which becomes:

center = uv + 0.5 * inv_target_size

As a drive-by cleanup we can now see that (inv_)target_size is only used to offset uv by another half target pixel to point to the center instead of the top-left. These values were already involved in converting the uv coordinate from target pixels to normalized coordinates, so it reads more logical (involving less math) to factor this calculation into the call site and remove two extraneous function parameters from resample_internal() as a result.

Now, continuing our journey, plug this into offset and simplify:

offset = (uv - center) * target_size
offset = (uv - (uv + 0.5 * inv_target_size)) * target_size
offset = (-0.5 * inv_target_size) * target_size
offset = -0.5

And we have our target value. Then, because they are subtracted when calling lanczos3_filter(), we turn this into positive 0.5.

Note that I have zero clue whether this is the right value, but when sampling a 6x6 grid (not 7x7 as thought in #27) we only visit pixel positions [-3, ..., 2], thus neatly retrieving weights at [-2.5, ..., 2.5] and never hitting the 3.5 value which is above 3 where lanczos3_filter(3.5) returns 0..

MarijnS95 commented 1 year ago

Note that this PR basically shows that the filter kernel is constant for the given dimensions. This idea was also implemented in (the massively conflated, IMO) PR #15. Apparently all this broken code that I'm fixing with all these PRs has been completely removed there too; we should've put more effort into cleaning it up and landing it.

But it's also useful to have a fixed target implementation to compare to.