Navigation C API Pages Python bindings Applications

Context filters

Pixel filters for GP_Context.

The context filter is basically a function that operates on context pixels. The result may be stored into a new bitmap or placed to bitmap passed as argument or, in some cases, the filter could be used in place so the result is stored into the same context as the one passed as filter source.

Common filter API

For convenience, the filters API is unified:

  • There are two functions for each filter

    • first one takes destination as an argument

    • second one allocates the destination and returns pointer to it

  • First argument(s) are always source(s)

  • Then, in case of second variant, destination

  • Other parameters follow

  • And the last argument is progress callback

When using allocating version of the filter, pointer to the newly allocated context is returned, or in case of failure NULL is returned.

If malloc() has failed NULL is returned.

If filter has been interrupted by a callback, all allocated memory is freed, and NULL is returned.

When using non-allocating variant of the filter, the destination context must have correct pixel type and the size must be big enough to store the result. The return value from such filter is either zero, in case of success, or non-zero when filter was interrupted by a callback.

For filters that work in-place (which is explicitly said for each filter) the source and the destination could be the same context. Note that this is not expected to work if you do several overlapping sub-contexts and pass these as arguments.

/*
 * Filter common API.
 */
int GP_FilterFoo(const GP_Context *src, GP_Context *dst,
                 foo params ...,
                 GP_ProgressCallback *callback);

GP_Context *GP_FilterFooAlloc(const GP_Context *src,
                              foo params ...,
                              GP_ProgressCallback *callback);

Point Filters

Point operations are filters that works with pixels as with independent values (the value of destination pixel depends only on the pixel on the same coordinates in source image). All of these filters works in-place and the result has always the same size as the source.

Invert

#include <GP.h>
/* or */
#include <filters/GP_Point.h>

int GP_FilterInvert(const GP_Context *src, GP_Context *dst,
                    GP_ProgressCallback *callback);

GP_Context *GP_FilterInvertAlloc(const GP_Context *src,
                                 GP_ProgressCallback *callback);

The pixel channel values are counted as chann_max - val.

Original Image; Inverted

Original Image Inverted

Brightness

#include <GP.h>
/* or */
#include <filters/GP_Point.h>

int GP_FilterBrightness(const GP_Context *src, GP_Context *dst,
                        float p, GP_ProgressCallback *callback);

GP_Context *GP_FilterBrightnessAlloc(const GP_Context *src, float p,
                                     GP_ProgressCallback *callback);

The pixel channel values are counted as val + chann_max * p.

Original Image; Brightness p=-0.5, p=-0.2, p=0.2, p=0.5

Original Image Brightness -0.5 Brightness -0.2 Brightness 0.2 Brightness 0.5

Contrast

#include <GP.h>
/* or */
#include <filters/GP_Point.h>

int GP_FilterContrast(const GP_Context *src, GP_Context *dst,
                      float p, GP_ProgressCallback *callback);

GP_Context *GP_FilterContrastAlloc(const GP_Context *src, float p,
                                   GP_ProgressCallback *callback);

The pixel channel values are counted as val * p.

Original Image; Contrast p=0.2, p=0.5, p=1.5, p=2, p=3

Original Image Contrast 0.2 Contrast 0.5 Contrast 1.5 Contrast 2 Contrast 3

BrightnessContrast

#include <GP.h>
/* or */
#include <filters/GP_Point.h>

int GP_FilterBrightnessContrast(const GP_Context *src, GP_Context *dst,
                                float b, float c,
                                GP_ProgressCallback *callback);

GP_Context *GP_FilterBrightnessContrastAlloc(const GP_Context *src,
                                             float b, float c,
                                             GP_ProgressCallback *callback);

The pixel channel values are counted as val * c + chann_max * b.

Original Image; BrightnessContrast b=-0.2 c=0.8, b=-0.5 c=2, b=0.2 c=0.8, b=0.2 c=1.5

Original Image BrightnessContrast -0.2 0.8 BrightnessContrast -0.5 2 BrightnessContrast 0.2 0.8 BrightnessContrast 0.2 1.5

Posterize

#include <GP.h>
/* or */
#include <filters/GP_Point.h>

int GP_FilterPosterize(const GP_Context *src, GP_Context *dst,
                       unsigned int levels, GP_ProgressCallback *callback);

GP_Context *GP_FilterPosterizeAlloc(const GP_Context *src, unsigned int levels,
                                    GP_ProgressCallback *callback);

The pixel channel values are quantized into number of levels.

Original Image; Posterize s=2, s=3, s=4, s=5, s=6

Original Image Posterize 2 Posterize 3 Posterize 4 Posterize 5 Posterize 6

Gaussian additive noise filter

#include <GP_Filters.h>
/* or */
#include <filters/GP_GaussianNoise.h>

int GP_FilterGaussianNoiseAddEx(const GP_Context *src,
                                GP_Coord x_src, GP_Coord y_src,
                                GP_Size w_src, GP_Size h_src,
                                GP_Context *dst,
                                GP_Coord x_dst, GP_Coord y_dst,
                                float sigma, float mu,
                                GP_ProgressCallback *callback);

GP_Context *GP_FilterGaussianNoiseAddExAlloc(const GP_Context *src,
                                             GP_Coord x_src, GP_Coord y_src,
                                             GP_Size w_src, GP_Size h_src,
                                             float sigma, float mu,
                                             GP_ProgressCallback *callback);

static inline int GP_FilterGaussianNoiseAdd(const GP_Context *src,
                                            GP_Context *dst,
                                            float sigma, float mu,
                                            GP_ProgressCallback *callback);

static inline GP_Context *
GP_FilterGaussianNoiseAddAlloc(const GP_Context *src,
                               float sigma, float mu,
                               GP_ProgressCallback *callback);

Gaussian additive noise filter adds gaussian distributed noise to an image with a defined sigma and mu. Both sigma and mu weights mapped to [0,1] interval.

Original Image; Gaussian Additive Noise s=0.03 m=0, s=0.05 m=0, s=0.05 m=-0.1, s=0.05 m=0.1, s=0.07 m=0.0

Original Image Gaussian Additive Noise 0.03 0 Gaussian Additive Noise 0.05 0 Gaussian Additive Noise 0.05 -0.1 Gaussian Additive Noise 0.05 0.1 Gaussian Additive Noise 0.07 0.0

Arithmetic filters

Arithmetic filters do take two contexts as an input and combines them into one output context.

The pixel type of both input contexts must match.

If size of the input contexts differs, minimum is used.

#include <filters/GP_Arithmetic.h>
/* or */
#include <GP.h>

int GP_FilterAddition(const GP_Context *src_a,
                      const GP_Context *src_b,
                      GP_Context *dst,
                      GP_ProgressCallback *callback);

GP_Context *GP_FilterAdditionAlloc(const GP_Context *src_a,
                                   const GP_Context *src_b,
                                   GP_ProgressCallback *callback);

Produces saturated (clamped) addition of two contexts.

#include <filters/GP_Arithmetic.h>
/* or */
#include <GP.h>

int GP_FilterMultiply(const GP_Context *src_a,
                      const GP_Context *src_b,
                      GP_Context *dst,
                      GP_ProgressCallback *callback);

GP_Context *GP_FilterMultiplyAlloc(const GP_Context *src_a,
                                   const GP_Context *src_b,
                                   GP_ProgressCallback *callback);

Produces saturated (clamped) multiplication of two contexts.

#include <filters/GP_Arigthmetic.h>
/* or */
#include <GP.h>

int GP_FilterDifference(const GP_Context *src_a,
                        const GP_Context *src_b,
                        GP_Context *dst,
                        GP_ProgressCallback *callback);

GP_Context *GP_FilterDifferenceAlloc(const GP_Context *src_a,
                                     const GP_Context *src_b,
                                     GP_ProgressCallback *callback);

Produces symmetric difference (i.e. abs(a - b)).

#include <filters/GP_Arigthmetic.h>
/* or */
#include <GP.h>

int GP_FilterMax(const GP_Context *src_a,
                 const GP_Context *src_b,
                 GP_Context *dst,
                 GP_ProgressCallback *callback);

GP_Context *GP_FilterMaxAlloc(const GP_Context *src_a,
                              const GP_Context *src_b,
                              GP_ProgressCallback *callback);

int GP_FilterMin(const GP_Context *src_a,
                 const GP_Context *src_b,
                 GP_Context *dst,
                 GP_ProgressCallback *callback);

GP_Context *GP_FilterMinAlloc(const GP_Context *src_a,
                              const GP_Context *src_b,
                              GP_ProgressCallback *callback);

Maximum and minimum filter.

Rotation and Symmetry filters

#include <filters/GP_Rotate.h>
/* or */
#include <GP.h>

int GP_FilterMirrorH(const GP_Context *src, GP_Context *dst,
                     GP_ProgressCallback *callback);

GP_Context *GP_FilterMirrorHAlloc(const GP_Context *src,
                                   GP_ProgressCallback *callback);

Mirrors context horizontally.

Works in-place.

The destination has to have the same pixel type and the size must be at least as large as source.

#include <filters/GP_Rotate.h>
/* or */
#include <GP.h>

int GP_FilterMirrorV(const GP_Context *src, GP_Context *dst,
                     GP_ProgressCallback *callback);

GP_Context *GP_FilterMirrorVAlloc(const GP_Context *src,
                                   GP_ProgressCallback *callback);

Mirrors context vertically.

Works in-place.

The destination has to have the same pixel type and the size must be at least as large as source.

#include <filters/GP_Rotate.h>
/* or */
#include <GP.h>

int GP_FilterRotate90(const GP_Context *src, GP_Context *dst,
                      GP_ProgressCallback *callback);

GP_Context *GP_FilterRotate90Alloc(const GP_Context *src,
                                    GP_ProgressCallback *callback);

Rotate context by 90 degrees.

Doesn’t work in-place (yet).

The destination has to have the same pixel type and size must be large enough to fit rotated context (i.e. W and H are swapped).

#include <filters/GP_Rotate.h>
/* or */
#include <GP.h>

int GP_FilterRotate180(const GP_Context *src, GP_Context *dst,
                       GP_ProgressCallback *callback);

GP_Context *GP_FilterRotate180Alloc(const GP_Context *src,
                                     GP_ProgressCallback *callback);

Rotate context by 180 degrees.

Doesn’t work in-place (yet).

The destination has to have the same pixel type and the size must be at least as large as source.

#include <filters/GP_Rotate.h>
/* or */
#include <GP.h>

int GP_FilterRotate270(const GP_Context *src, GP_Context *dst,
                       GP_ProgressCallback *callback);

GP_Context *GP_FilterRotate270Alloc(const GP_Context *src,
                                     GP_ProgressCallback *callback);

Rotate context by 270 degrees.

Doesn’t work in-place (yet).

The destination has to have the same pixel type and destination size must be large enough to fit rotated context (i.e. W and H are swapped).

#include <filters/GP_Rotate.h>
/* or */
#include <GP.h>

typedef enum GP_FilterSymmetries {
        GP_ROTATE_90,
        GP_ROTATE_CW = GP_ROTATE_90,
        GP_ROTATE_180,
        GP_ROTATE_270,
        GP_ROTATE_CCW = GP_ROTATE_270,
        GP_MIRROR_H,
        GP_MIRROR_V,
} GP_FilterSymmetries;

GP_Context *GP_FilterSymmetry(const GP_Context *src,
                              GP_FilterSymmetries symmetry,
                              GP_ProgressCallback *callback);

int GP_FilterSymmetry(const GP_Context *src, GP_Context *dst,
                      GP_FilterSymmetries symmetry,
                      GP_ProgressCallback *callback);

Catch all function for symmetry filters.

Linear filters

Linear filters family consists of filters based on discrete linear convolution, that means that computed pixel value depends on linear combination of the image pixels.

It’s defined as:

discrete_linear_convolution.png

The K denotes convolution kernel and in practice, due to computational complexity i and j are bounded in relatively small intervals. For example i and j are in (-1,1) and the kernel size is 3x3.

Note that pixel values outside the image are undefined. The linear convolution in GFXprim simply uses the closest border pixel values for all pixels outside the image.

Particular convolution kernel is called separable if it could be decomposed into two one dimensional kernels (these when combined yields back the original kernel). Such convolution then could be applied as two one dimensional convolutions which is faster operation (especially for big kernels).

Bilinear Convolution

Following paragraph describes linear convolution implementation as well as a little of the math background skip it if you just need to use one of the ready-to-use filters.

#include <filters/GP_Linear.h>
/* or */
#include <GP.h>

int GP_FilterLinearConvolution_Raw(const GP_Context *src,
                                   GP_Coord x_src, GP_Coord y_src,
                                   GP_Size w_src, GP_Size h_src,
                                   GP_Context *dst,
                                   GP_Coord x_dst, GP_Coord y_dst,
                                   float kernel[], uint32_t kw, uint32_t kh,
                                   float kern_div, GP_ProgressCallback *callback);

Internal generic convolution filter, this is a base for all linear convolution filters with non-separable kernel.

The src coordinate and sizes denotes rectangle in the source context that the filter operates on.

The dst coordinates defines offset into the dst context.

The kernel is two-dimensional array of a size kw * kh indexed as kernel[x + y*kw].

The kern_div is a coefficient that is used to divide the resulting values often used to normalize the result.

This filter works in-place.

The pixel value is computed as:

discrete_linear_convolution_alg1.png

Which is the same as:

discrete_linear_convolution_alg2.png
Note The number of kernel rows and columns is expected to be odd number.
Original Image; Convolution: 3x3 Box Blur, 5x5 Box Blur, 3x3 Laplacian, 3x3 Sobel, 3x3 Roberts

Original Image 3x3 Box Blur 5x5 Box Blur 3x3 Laplacian 3x3 Sobel 3x3 Roberts

#include <filters/GP_Linear.h>
/* or */
#include <GP.h>

int GP_FilterHLinearConvolution_Raw(const GP_Context *src,
                                    GP_Coord x_src, GP_Coord y_src,
                                    GP_Size w_src, GP_Size h_src,
                                    GP_Context *dst,
                                    GP_Coord x_dst, GP_Coord y_dst,
                                    float kernel[], uint32_t kw, float kern_div,
                                    GP_ProgressCallback *callback);

int GP_FilterVLinearConvolution_Raw(const GP_Context *src,
                                    GP_Coord x_src, GP_Coord y_src,
                                    GP_Size w_src, GP_Size h_src,
                                    GP_Context *dst,
                                    GP_Coord x_dst, GP_Coord y_dst,
                                    float kernel[], uint32_t kh, float kern_div,
                                    GP_ProgressCallback *callback);

int GP_FilterVHLinearConvolution_Raw(const GP_Context *src,
                                     GP_Coord x_src, GP_Coord y_src,
                                     GP_Size w_src, GP_Size h_src,
                                     GP_Context *dst,
                                     GP_Coord x_dst, GP_Coord y_dst,
                                     float hkernel[], uint32_t kw, float hkern_div,
                                     float vkernel[], uint32_t kh, float vkern_div,
                                     GP_ProgressCallback *callback);

void GP_FilterKernelPrint_Raw(float kernel[], int kw, int kh, float kern_div);

Internal special functions for one dimensional vertical and horizontal convolution these two functions are base for all separable convolution filters.

The src coordinate and sizes denotes rectangle in the source context that the filter operates on.

The dst coordinates are offset into the dst.

The kernel is one-dimensional array of floats of size kw or kh.

The kern_div is a coefficient that is used to divide the resulting values.

The last function does both vertical and horizontal convolution and takes care of correct progress callback.

These filters work in-place.

The pixel value is computed as:

discrete_linear_1D_convolution_alg1.png

Which is the same as:

discrete_linear_1D_convolution_alg2.png
Note The number of kernel rows and columns is expected to be odd number.
Note The linear convolutions are internally implemented using integer arithmetics, which works fine, but you need to take a care not to overflow 32bit signed integer. If the pixel channel size is 8bit long and 10bits are used for the fixed point part of the number the rest must fit into about 10 bits to be safe.
#include <filters/GP_Convolution.h>
/* or */
#include <GP.h>

typedef struct GP_FilterKernel2D {
        unsigned int w;
        unsigned int h;
        float div;
        float *kernel;
} GP_FilterKernel2D;

int GP_FilterConvolutionEx(const GP_Context *src,
                           GP_Coord x_src, GP_Coord y_src,
                           GP_Size w_src, GP_Coord h_src,
                           GP_Context *dst,
                           GP_Coord x_dst, GP_Coord y_dst,
                           const GP_FilterKernel2D *kernel,
                           GP_ProgressCallback *callback);

GP_Context *GP_FilterConvolutionExAlloc(const GP_Context *src,
                                        GP_Coord x_src, GP_Coord y_src,
                                        GP_Size w_src, GP_Size h_src,
                                        const GP_FilterKernel2D *kernel,
                                        GP_ProgressCallback *callback);

int GP_FilterConvolution(const GP_Context *src, GP_Context *dst,
                         const GP_FilterKernel2D *kernel,
                         GP_ProgressCallback *callback);

GP_Context *GP_FilterConvolutionAlloc(const GP_Context *src,
                                      const GP_FilterKernel2D *kernel,
                                      GP_ProgressCallback *callback);

void GP_FilterKernel2DPrint(const GP_FilterKernel2D *kernel);

Linear convolution filters, you should preferably use this API over the _Raw variants.

The Ex variants takes a rectangle on which the filter should operate as well as offset into the destination. The destination must be large enough so that starting with offset there is at least w_dst and h_dst pixels.

The kernel is a pointer to a structure initialized with the kernel size, divider and array of kernel values.

The last function prints convolution kernel in human-readable format into the stdout.

Warning If filter is executed in-place the work cannot be distributed between threads (as some of the threads will overwrite values read by other threads). In this case convolution filters runs in one thread regardless of if threads are eanbled or not.
#include <GP.h>

/*
 * Example box smoothing filter.
 */
static void box_smoothing(GP_Context *img)
{
        float box_filter[] = {
                1, 1, 1,
                1, 1, 1,
                1, 1, 1,
        };

        GP_FilterKernel2D box_kernel = {
                .w = 3,
                .h = 3,
                .div = 9,
                .kernel = box_filter,
        };

        GP_FilterConvolution(img, img, &box_kernel, NULL);
}

Example function that implements simple in-place smoothing filter.

Laplace Filter

#include <GP_Filters.h>
/* or */
#include <GP.h>

int GP_FilterLaplace(const GP_Context *src, GP_Context *dst,
                     GP_ProgressCallback *callback);

GP_Context *GP_FilterLaplaceAlloc(const GP_Context *src,
                                  GP_ProgressCallback *callback);

Discrete Laplace filter that produces a second derivative of the original image.

The convolution kernel is defined as:

laplacian_kernel.png
Note This filter is not separable but could be written as a sum of two one dimensional filters as the kernel definition suggests.

Laplacian Edge Sharpening

#include <GP_Filters.h>
/* or */
#include <GP.h>

int GP_FilterEdgeSharpening(const GP_Context *src, GP_Context *dst,
                            float w, GP_ProgressCallback *callback);

GP_Context *GP_FilterEdgeSharpeningAlloc(const GP_Context *src, float w,
                                         GP_ProgressCallback *callback);

Laplace based edge sharpening filter, subtracts weighted second derivative from the original image.

The w paramerter is multiplicative weight applied on the second derivative. Reasonable results are when the parameter is between 0.1 and 1.

laplacian_edge_sharpening.png
Original Image; Edge Sharpening w=0.1, w=0.3, w=0.5, w=0.8, w=1.0

Original Image Edge Sharpening 0.1 Edge Sharpening 0.3 Edge Sharpening 0.5 Edge Sharpening 0.8 Edge Sharpening 1.0

Gaussian Blur

#include <filters/GP_Blur.h>
/* or */
#include <GP.h>

int GP_FilterGaussianBlurEx(const GP_Context *src,
                            GP_Coord x_src, GP_Coord y_src,
                            GP_Size w_src, GP_Size h_src,
                            GP_Context *dst,
                            GP_Coord x_dst, GP_Coord y_dst,
                            float x_sigma, float y_sigma,
                            GP_ProgressCallback *callback);

GP_Context *GP_FilterGaussianBlurExAlloc(const GP_Context *src,
                                         GP_Coord x_src, GP_Coord y_src,
                                         GP_Size w_src, GP_Size h_src,
                                         float x_sigma, float y_sigma,
                                         GP_ProgressCallback *callback);

int GP_FilterGaussianBlur(const GP_Context *src, GP_Context *dst,
                          float x_sigma, float y_sigma,
                          GP_ProgressCallback *callback)

GP_Context *GP_FilterGaussianBlurAlloc(const GP_Context *src,
                                       float x_sigma, float y_sigma,
                                       GP_ProgressCallback *callback)

Gaussian blur (low pass) filters implemented as bilinear separable convolution.

The sigma denotes amount of the blur (the radius is computed accordingly automatically).

The sigma values can be set for vertical and horizontal direction independently which may be useful when Gaussian blur is used as a low pass filter before image is resampled non proportionally.

Original Image; Gaussian Blur xsig=2 ysig=2, xsig=0 ysig=4, xsig=4 ysig=0, xsig=4 ysig=4, xsig=10 ysig=10

Original Image Gaussian Blur 2 2 Gaussian Blur 0 4 Gaussian Blur 4 0 Gaussian Blur 4 4 Gaussian Blur 10 10

Interpolation filters

Filters to resize image.

Nearest Neighbour Interpolation

Fast, but produces "pixelated" images. May however work better for images with sharp edges mostly consisting of big one color regions (it doesn’t blur the result on upscaling).

Also is commonly used to show preview before you resample the image correctly.

Bilinear Interpolation

Bilinear is faster than bicubic interpolation and produces quite good results especially the low pass variant doesn’t need additional filter on down-sampling.

Bicubic Interpolation

Works well as is on image upscaling. To get decent result on downscaling low-pass filter (Gaussian blur) must be used on original image before actual downscaling. To do this reasonably fast we could cheat a little: first resize big images a little without the low-pass filter, then apply low-pass filter and finally downscale it to desired size.

Dithering

Dithering filters are filters for better loosy (source pixel type has more bits to represent color or grayscale value) pixel type conversions.

Median

#include <filters/GP_Median.h>
/* or */
#include <GP.h>

int GP_FilterMedianEx(const GP_Context *src,
                      GP_Coord x_src, GP_Coord y_src,
                      GP_Size w_src, GP_Size h_src,
                      GP_Context *dst,
                      GP_Coord x_dst, GP_Coord y_dst,
                      int xmed, int ymed,
                      GP_ProgressCallback *callback);

GP_Context *GP_FilterMedianExAlloc(const GP_Context *src,
                                   GP_Coord x_src, GP_Coord y_src,
                                   GP_Size w_src, GP_Size h_src,
                                   int xmed, int ymed,
                                   GP_ProgressCallback *callback);

int GP_FilterMedian(const GP_Context *src,
                    GP_Context *dst,
                    int xmed, int ymed,
                    GP_ProgressCallback *callback);

GP_Context *GP_FilterMedianAlloc(const GP_Context *src,
                                 int xmed, int ymed,
                                 GP_ProgressCallback *callback);

Constant time median filter (the computational complexity is independent of radius size).

The xmed and ymed are radius values for x and y. The algorithm uses xmed respectively ymed pixel neighbors from each side so the result is median of rectangle of 2 * xmed + 1 x 2 * ymed + 1 pixels.

Original Image; Median xr=3 yr=3, xr=5 yr=5, xr=7 yr=7, xr=9 yr=9, xr=12 yr=12

Original Image Median 3 3 Median 5 5 Median 7 7 Median 9 9 Median 12 12