How to Make a GIFKR Filter

One new thing in GIFKR 2 is the ability to create and import your own image filters. The cool thing about GIFKR is that it does the hard work for you. UI controls are created using reflection, so all you have to do is declare a variable and use it. For example the tiny amount of code in the multiply filter gives the user a de-camel-cased keyframe (or simply a number spinner when not in animation mode) controller that automatically re-renders the preview whenever the value is changed.

 

This post will show you how to write your own using the API available here. In this tutorial I will be using the Eclipse IDE, but you can use whatever you like. I will also assume you have a basic understanding of Java programming. Before we get to actually creating an image filter, we first need to look at how this whole process works on the inside.

All image filters extend from the class ImageFilter (or a class that extends ImageFilter). Since the real ImageFilter class is quite messy and confusing, I have provided in the API a stripped down version that only includes the method signatures and instance variables that you need to worry about. So let’s get started:

The Apply() Function

    protected abstract BufferedImage apply(BufferedImage img);

This is the most important function in the ImageFilter class. This is where you define what your filter does. It takes an image and returns one. You are free to modify and/or return the original image, as the program copies every image before filtering anyways.You also are welcome to create a new image and return that. The only requirement is that the new image should have the same dimensions as the original.

I have also provided several subtypes of ImageFilter that you can extend from that already implement this method, but require you to define your filter in terms of other simpler functions. I will go through these individually later in this post.

Defining Variables in Filters

If you want to make your own image filter, it’s quite likely that you’d like to allow the user to control variables that might influence how the filter behaves. This is where GIFKR shines. If you’d like an interface control for a Number, Boolean, Color, String, Convolution Matrix, or any Enum type, all you have to do is declare it as public. For example if you want an integer variable named “variable name” with a default value of 7, simply write

    public int variableName = 7;

and GIFKR will take care of the rest:

Screen Shot 2017-03-06 at 12.35.35 PM

Then you can use variableName just like you would any other instance variable in your apply() method, or anywhere else. Some guidelines:

  • Float/float variables ALWAYS are between 0 and 1. Always. If you want a decimal variable that can take a larger range, use a double. Since GIFKR is only working with 8 bits per color channel, the difference between precision is effectively meaningless.
  • If you change the value of a variable, the UI controls will not (at this time) reflect the change. For that reason it is recommended that you do not modify public instance variables in code.
  • If you’d like to create a drop-down with many options (see pixel sorting filters) simply define your enum type within the filter class.
  • You can also make a custom UI controller for whatever type you like by making a public JComponent. Unfortunately however these cannot be keyframed.
  • doubles and ints by default are within the range [0, 2,147,483,647]. You can override this using the following format:
    @ControlOverride(min = "-255", max = "255")
    public int constant;

Random Numbers in Image Filters

The following line is included in the ImageFilter class:

    protected final Random rand;

Do not create your own Random or use Math.rand() to use random numbers in any ImageFilter. Simply call rand.nextInt(), rand.nextDouble() etc.. Using the included rand instance variable allows the user to control the seed of the RNG. Here is the javadoc for Random. If you do not use any random numbers in your ImageFilter, include the following function to hide the controls for random numbers:

    @Override
    protected boolean randomControls() {
        return false;
    }

Similarly, there exist controls for rotating, filtering, and then un-rotating the image. This allows for things like pixel sorting to be applied at an angle (example) but does pretty much nothing for many filters. If you’d like to hide the built in angle controls (and they are not already hidden by the subclass of ImageFilter that you are extending from), include the following function in your filter:

    @Override
    protected boolean angleControls() {
        return false;
    }

Types of Image Filters

GIFKR includes several subclasses of ImageFilter for you to extend to make creating your own filters even simpler.

Algebraic Image Filter

AddFilter, ColorLimitFilter, DivideFilter, MultiplyFilter, NoiseFilter, ReplacementFlilter, SubtractFilter

An “algebraic image filter” is the simplest type of ImageFilter. These are defined by the function:

    public abstract int apply(int channel);

effectively, it takes values from 0-255 representing the red, green, or blue channel of each pixel and returns the new value. Values greater than 255 will behave as that number mod 255. Despite its simplicity, this class can define very interesting effects. For example the MultiplyFilter looks like:

public class MultiplyFilter extends AlgebraicImageFilter {

    public double multiplicationFactor = 1d;
    
    @Override
    public int apply(int channel) {
        return (int) (channel * multiplicationFactor);
    }
    
    @Override
    protected boolean randomControls() {
        return false;
    }
}

and can create things like:

psychadelic

Pixel Image Filter

HSBFilter, MonochromeNoiseFilter

This class is very similar to the Algebraic Image Filter, but allows you to work with an entire pixel at a time using the method:

    public abstract int apply(int color);

where color is an integer where the first 8 bits are for alpha, and the following for red, green, and blue respectively. If you are not comfortable with bitshifting, feel free to work with it as a color object by using new Color(color) and then returning the getRGB() method in the color class, but do be aware this results in a significant performance hit.

Convolution Filter

BoxBlurFilter, EdgeDetectFilter, MatrixFilter, SharpenFilter

Many filters from edge detection to blurs can be succinctly described using a simple matrix. To learn more about how this works, I highly recommend reading the wikipedia article on kernel convolutions. Essentially the method

    public abstract float[][] getMatrix();

is called before the image is filtered. So you can simply define a constant matrix like I did in the EdgeDetectFilter:

    @Override
    public float[][] getMatrix() {
        return new float[][] {
            { 0f,-1f, 0f},
            {-1f, 4f,-1f},
            { 0f,-1f, 0f}
        };
    }

or do something more fancy like in the GaussianBlurFilter where the matrix is dependent on other variables.

public class GaussianBlur extends ConvolutionFilter {

    public double sigma = 1;
    
    @Override
    public float[][] getMatrix() {
        
        int dim = dim();
        float[][] matrix = new float[dim][dim];
        
        for(int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = gaussianDistribution(Math.sqrt(Math.pow(dim/2 - j, 2) + Math.pow(dim/2 - j, 2)));
            }
        }
        
        normalize(matrix);
        return matrix;
    }

    private float gaussianDistribution(double x) {
        return (float) (Math.exp(-x*x/(2 * sigma * sigma)) * (1d/Math.sqrt(2 * Math.PI) * sigma));
    }
    
    private int dim() {
        return 2 * (int) sigma + 1;
    }
}

I have also created a interpolator for matrices, so simply writing “public float[][] varName = new Float[][]…” is perfectly valid, and will give the user complete control over the matrix. This is utilized in the MatrixFilter class.

Text Filter

ASCIIFilter, MessageFilter, TheMatrixFilter

The text filter takes care of font, drawing text, etc for you. All you have to define is what character and color to use given the color at the location of the character and the index of the character.

    public abstract char getChar(int color, int count);

    public abstract Color getColor(int color, int count);

Pixel Sort Filter

LinePixelSort, StandardPixelSort, RandomPixelSort

The pixel sort filter requires you to implement the Apply() method from ImageFilter. It provides you with methods and variables to control pixel sorting, but ultimately the implementation is up to you. If you would like to implement your own, I highly suggest you take a look at the source code to the built-in pixel sorting filters.

Actually Creating, Using and Sharing Your Filter

Finally, it’s time to actually create a filter. I’ll pretend you didn’t just skim past all the stuff above.

First: Download the API and open it in Eclipse. You can do this by unzipping the file and opening it via File>Open projects from filesystem.

Your project should look something like this:

Screen Shot 2017-03-06 at 2.53.59 PM

click on the “default package” and then go to File>New>Class

Enter a name for your filter, and choose a type of ImageFilter you want to extend from. Be sure to selected “Inherited abstract methods” to have stubs generated for you.

Screen Shot 2017-03-06 at 2.57.19 PM.png

Then write the code for your filter using the information above. I’m going to leave mine pretty simple by just returning the original image, but I’ll add some variables just to make sure they show up:

Screen Shot 2017-03-06 at 8.48.04 PM

Now save it. In order to test, your filter, open the project in your file viewer:

Screen Shot 2017-03-06 at 3.04.13 PM

Navigate to the “bin” folder, and drag .class onto GIFKR.

Screen Shot 2017-03-06 at 3.16.54 PM

Finally, test your filter. If you’re satisfied with it, share it with the world! The .class file is entirely your property, and is free to be distributed (sold even if you want I guess?) however you feel.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s