Kernel/convolution matrix processing

A project log for Supercon2017 Enhanced imagefx

I added a few fx, enhanced the UI (including a HW hack), and allowed the fx'd images to be saved.

ToddTodd 11/30/2017 at 04:050 Comments

Wikipedia can explain Kenel's for image processing better than I can:

Convolution is the process of adding each element of the image to its local neighbors, weighted by the kernel. This is related to a form of mathematical convolution.

Image Kernels: Explained Visually, has an interactive demo

The existing code seems to do this in the dither function but it's not really reusable:

for(y=0 ; y != ypixels; y++) {
  for (x = 0; x != xpixels; x++) {
    inptr = cambuffer + bufstart + x + y * xpixels;
    op = *inptr;                   
    if (op > 0x80) np = 0xff; else np = 0;
    er = op - np;
    *inptr = np;
    inptr++; //right
    z = (int) *inptr + er * 7/16;
    if (z<0) z = 0; else if (z > 255) z = 255;
    *inptr = z;
    /* snip */
  } // x
} // y

Here's an example of the Boxcar (smoothing) Kernel. Unlike the example in Wikipedia, these are not 1's - but I was trying to get some variation, as we'll see later.

const Kernel_t BoxcarKernel = {
  {7, 7, 7},
  {7, 7, 7},
  {7, 7, 7},

One thing I learned the hard way was you can't modify the matrix in-place; in other words the result of the calculation has to go into a new array. But where to put it? The answer was in the dither function but I failed to realize that at the time. I tied allocating the entire frame buffer again, but there's not sufficient RAM for that. The trick is that the frame buffer is allocated for RGB with 1 byte per color per pixel, while the effects only work on Greyscale - 1 byte total per pixel. 

I don't know about you but since I'm only an occasional C coder, so things like pointers, addresses and the like really hurt my brain.

So I planned to pass a matrix (only a 3x3 array - in the interest of performance), along with the from and to buffers, and a some other values to help in the calculations.  Here's the function signature:

void convolution(mono_buffer_t sourceBuffer, mono_buffer_t targetBuffer, Kernel_t Kernel, signed int scaler, signed int offset)

Then it's just a matter of doing some prep, and calling the convolution code:

convolution(cambuffer + bufstart, cambuffer + bufstart + cambuffmono_offset, Kernel, 5, 0);

And then displaying it - just be tweaking the existing display code to work with my "new" buffer definitions:

monopalette (0,255);
dispimage(0, 12, xpixels, ypixels, (img_mono | img_revscan), cambuffer + bufstart + cambuffmono_offset);