Avoid ringing effects introduced by the default Lanczos algorithm when scaling an image by using a custom resampling filter.
- iOS 13.0+
- Xcode 10.2+
In this sample code project, you’ll write a custom resampling filter to scale an image using linear interpolation.
Most of the vImage geometry operations, such as scale and rotate, use a process known as resampling to avoid image artifacts. vImage resamples with kernels that combine data from a target pixel and other nearby pixels to calculate a value for the destination pixel.
The following illustrates how resampling at fractional pixel locations scales a horizontal strip of pixels to three times its original width. For example, the scale operation sets the value of the leftmost pixel in the destination from position 0.333 in the leftmost source pixel.
Because the resampling process has to evaluate the kernel at fractional pixel locations, the process relies on a family of kernel matrices for use at different fractional distances through a given pixel. You provide a function that generates this family of kernels. This is in contrast to operations such as convolution and morphology, for which you supply a single kernel matrix that’s applied at the center of each pixel.
Review Default Lanczos Resampling
For most vImage geometric operations, vImage supplies a default resampling filter that is an implementation of the Lanczos resampling method. However, the Lanczos method can produce ringing effects near regions of high frequency signals (that is, regions that contain a lot of pixel variation, such as the hard edges typical of line art). To correct this, you can implement linear interpolation as a custom resampling filter.
This sample app allows the user to toggle between the default resampling filter (Lanczos) and a custom resampling filter. Declare the filter independently of initialization to support that functionality:
The following code initializes a default Lanczos resampling filter:
resampling is an initialized Lanczos resampling filter with the specified scale factor.
Use Shear Operations to Scale an Image
The vImage shear functions accept the resampling filter and perform the scaling. The shear functions operate in one dimension at a time, so to scale an image in both dimensions, call
v followed by
v. Because these functions require separate input and output buffers, use an intermediate buffer to pass data from the vertical shear to the horizontal shear.
On return of the horizontal shear,
destination contains the source image, scaled about its center. The following shows an original image, filled with small dots, magnified 30 times using the Lanczos resampling filter:
The ringing artifacts appear as faint lines between the magnified dots.
Write a Linear Resampling Filter Function
The shear functions used for scaling are both 1D and, therefore, the resampling filter function you create is also 1D. You can apply the same filter function for both the vertical and horizontal passes.
The function generates a set of kernel values based on a set of supplied distances from the pixel being transformed—read from
in. The generated kernel values are assigned to
In the following example, the kernel values are inversely proportional to the distance; the further a pixel is from the transformed pixel, the smaller the corresponding kernel value. After calculating the kernel values, the values scale (normalize) so that so that their sum is 1. This normalization step ensures the final image is the same brightness as the original.
user parameter allows you to optionally pass custom data to your resampling kernel function. It’s not used in this sample app.
For example, if the pixel positions passed to
The values in the
kernel array are:
Dividing each of the values in
kernel by its sum returns the normalized kernel values that you assign to the resampling function’s
The values generated by the resampling function form a 1D convolution kernel that the shear functions use in a similar way to the 1D convolution described in the Blur an Image with a Separable Kernel section of Blurring an Image. However, unlike the kernels used for convolution, the resampling kernel is suitable for use with fractional pixel positions.
Allocate Resampling Filter Function Memory
The resampling function, the scale factor, and the kernel width combine to determine the memory required by the resampling function. Use the
v function to calculate the size in bytes, and the
allocate(byte function to allocate the neccessary memory.
resampling is a
Resampling structure, allocated with the correct amount of uninitialized memory.
Create a Linear Resampling Filter
v to create the resampling filter and populate
Scaling using a custom resampling filter is the same process as using the default Lanczos resampling:
The following shows the same image as used in the Lanczos example, also maginifed 30 times.
Using linear resampling avoids the ringing artifacts.
Free the Resampling Filter Memory
After you’re finished working with the resampling filter, it’s important that you free its allocated memory. Freeing the resampling filter memory is slightly different, depending on whether you’ve used the default or a custom filter. Use the following for the default:
For custom resampling filters, use the following: