VBitmapFunctions library

VBitmapFunctions was never really a planned project.

When I started writing image processing filters I needed a way to load and save images, so I threw together a very simple few functions to read and write bitmap files. I then needed a few commmon image functions, so those too were written. Before I realised it, I had a library! A lot of my programs come with a 'bitmapfunctions.c' and 'bitmapfunctions.h' - various versions of the functions from before I decided to give them a consistant naming scheme and make a proper library from them.

VBitmapFunctions is not a complicated framework. It has no custom data types. An image in memory is nothing but an unsigned char*, pointing to a series of RGB color triplets each three bytes long. Thus any pixel starts at (x+(y*width))*3. Most functions take a pointer to one or more of these, and the appropriate image dimensions to go with it.

It's a useful library just the same. The ultra-simple structure is handy for getting right to the algorithm without getting bogged down in learning a whole API, and the library includes a few common image processing functions. You can load an image, apply a gaussian blur and save it again in five functions calls - one of them just the malloc. It isn't really made for production code - there's no error checking. It's more for refining algorithms. Though less powerful than something like ImageMagick, I find the extreme simplicity means more effort can be spent focusing on the mathematics of image processing without getting caught up handling images of differing bit depths, color channels, palettes and color spaces. Everything in VBitmapFunctions is a 24-bit bitmap image.

This not a 'proper' library. It's an algorithmic proof of concept. It's buggy, it's unstable, it's probably riddled with security holes. It's more of a guideline, a demonstration of some algorithms you'll want to incorporate into your own code. With this warning in mind, take the source and see what you can do.

int VBM_bitmapgetdimensions(char *filename, int *x, int *y)
Gets the dimensions of a bitmap on disk. You need these to allocate memory before you can load the image.

int VBM_bitmapload(char *filename, unsigned char *buffer, int x, int y)
void bitmapsave(char *filename, unsigned char *buffer, int x, int y)

Load and save bitmaps.

unsigned long long VBM_getframedelta(unsigned char *A, unsigned char *B, unsigned long long numpixels)
Gets a 'difference value' between frames, by simple sum-of-absolute-difference method.

unsigned long long VBM_getframesigma(unsigned char *buffer, unsigned long long numpixels)
Total value of all subpixels in an image. Handy when working with video, as it can be used to compensate for flicker.

void VBM_dilate(unsigned char *mask, unsigned int width, unsigned int height)
void VBM_erode(unsigned char *mask, unsigned int width, unsigned int height)
The basic morphological operators. These work only on 1BPP two-color masks.

void VBM_1BPP_boxblur(unsigned char *image, unsigned int width, unsigned int height, unsigned int iterations)
Box blur. Iterate and it's a decent approximation to the gaussian, too. It uses the seperable nature of the blur, so peformance is quite good. Multiple iterations are a good approximation to the gaussian, too.

float VBM_RGB_get_color_angle(unsigned char *p1, unsigned char *p2, unsigned char fromwhite)
Uses the Power of Math to compare two colors while ignoring brightness.

void VBM_extract_color_channel(unsigned char *src, unsigned char *dst, unsigned char channel, unsigned int numpixels)
void VBM_replace_color_channel(unsigned char *src, unsigned char *dst, unsigned char channel, unsigned int numpixels)

Separates a color channel into a 1BPP 256-greys image, and puts it back again.

void VBM_converging_cellular_filter(unsigned char *mask, unsigned int width, unsigned int height)
There are three different ways to view this, depending on your mathematical inclinations:
- As a cellular automaton, iterating until reaching a stable state.
- As a repeated mode filter.
- As alternating box-blur and threshhold operations (almost).
Its main application is in tidying up masks derived from noisy images - a task usually performed by morphological operators, in some combination of erode and dilate. These filters work, but tend to also distort shapes and struggle to pick out an accurate edge line in the presence of the type of salt-and-pepper noise often encountered in such masks. The 'converging cellular filter' performs better than simple morphological operators in this regard, though at the expense of taking several times longer to calculate.

A near-identical filter is described in Adriana Popovici and Dan Popovici, 'Cellular Automata in Image Processing,' though from a more mathematical perspective.

void VBM_1BPP_threshhold(unsigned char *image, unsigned char threshhold, unsigned long long numpixels)
Your basic threshhold filter.

unsigned char* VBM_pick_closest_color(unsigned char *colors, unsigned int num)
Given an array of color values, this calculates the mean color and returns a pointer to the element of the array closest to the mean. It may sound specialised, but there is a very good application for this calculation. It's a lot like a median filter, but faster to calculate and mathematically tidier. Medians are defined only for orderable one-dimensional data, which color images are certainly not - thus a conventional 'median filter' on color images actually has to process each color channel separately, and thus can return colors that don't exist in the original image. This isn't a problem on photographic images, but it can be for illustrations. The main role of this function is to be called by VBM_closest_mean_filter().

void VBM_closest_mean_filter(unsigned char *image, unsigned int width, unsigned int height, unsigned int itterations, unsigned char threshhold)

VBM_closest_mean_filter on itterations=1,threshold=5. A little softening of edges can be observed, but jpeg artifacts are removed, and softening is less severe than a simple blur would cause. It is much less effective on photos.

Utilising VBM_pick_closest_color(), filters an entire image. It's a powerful denoiser for illustrations, especially capable for removing artifacts from JPEG compression. I suggest starting around itterations=1,threshhold=10 and adjusting by trial and error until it looks good. Doesn't work very well on photos. If you're trying to run an edge-detect on illustrations and finding false edges from JPEG DCT artifacts, this may be just what you're looking for.

An interesting property of this filter - or rather, the VBM_pick_closest_color() filter which it utilises - is that it will never generate any new colors. That is, the palette of any image after processing is a subset (though not nessicarily a strict subset) of the palette of the image before processing. That is the property that makes it so ideally suited to illustrations. It also means the output images tend to compress better in lossless formats such as PNG.