Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fastest Performance Filtering an Image

Tags:

java

Context: I'm trying to create an animation in java. The animation is simply take an image and make it appear from the darkest pixels to the lightest.

The Problem: The internal algorithm defining the pixels transformations is not my issue. I'm new to Java and Computing in general. I've done a bit of research, and know that there are plenty of APIs that helps with image filters/transformations. My problem is performance, understanding it.

To the implementation i've created a method that do the following:

  1. Receives an BufferedImage.
  2. Get the WritableRaster of the BufferedImage.
  3. Using setSample and getSample, process and change pixel by pixel.
  4. Return the BufferedImage.

After that, i use a Timer to call the method. The returned BufferedImage is attached to a JButton via setIcon after each call.

With a 500x500 image my machine takes around 3ms to process each call. For standard 1080p images it takes around 30ms, wich is about 33 frames per second.

My goal is to process/animate FullHD images at 30fps... And i will not be able to with the path I'm following. Not in most computers.

What i'm getting wrong? How i can make it faster? Using getDataBuffer or getPixels instead of getSample can improve it?

Thanks in advance! And sorry my english.


Partial Conclusions: Thanks to some help here. I've changed concept. Instead of using getSample and setSample I've stored the pixels ARGB informations of the BufferedImage into an array. So i process the array and copy it all at once into a Raster of another BufferedImage.

The process time reduced from 30ms ( get/set sample ) to 1ms. ( measured poorly, but in the same machine, enviroment and code ).

Below is a little class i coded to implement it. The class can filter pixels only below a Brightness level, the other pixels become transparent ( alpha = 0 ).

Hope it help's who search for the same solution in the future. Be wary that I'm below rookie level in Java, so the code might be poorly organized/optimized.

import java.awt.Graphics2D;
import java.awt.image.*;

/**
 * @author Psyny
 */
public class ImageAppearFX {
    //Essencial Data
    BufferedImage imgProcessed;
    int[] RAWoriginal;
    int[] RAWprocessed;
    WritableRaster rbgRasterProcessedW;

    //Information about the image
    int x,y;
    int[] mapBrightness;

    public ImageAppearFX(BufferedImage inputIMG) {
        //Store Dimensions
        x = inputIMG.getWidth();
        y = inputIMG.getHeight();

        //Convert the input image to INT_ARGB and store it. 
        this.imgProcessed = new BufferedImage(x, y, BufferedImage.TYPE_INT_ARGB); 
        Graphics2D canvas = this.imgProcessed.createGraphics();
        canvas.drawImage(inputIMG, 0, 0, x, y, null); 
        canvas.dispose();

        //Create an int Array of the pixels informations.
        //p.s.: Notice that the image was converted to INT_ARGB
        this.RAWoriginal = ((DataBufferInt) this.imgProcessed.getRaster().getDataBuffer()).getData();
        //Dupplication of original pixel array. So we can make changes based on original image
        this.RAWprocessed = this.RAWoriginal.clone();

        //Get Raster. We will need the raster to write pixels on
        rbgRasterProcessedW = imgProcessed.getRaster();

        //Effect Information: Store brightness information
        mapBrightness = new int[x*y];
        int r,g,b,a,greaterColor;
        // PRocess all pixels
        for(int i=0 ; i < this.RAWoriginal.length ; i++) {   
            a = (this.RAWoriginal[i] >> 24) & 0xFF;
            r = (this.RAWoriginal[i] >> 16) & 0xFF;
            g = (this.RAWoriginal[i] >>  8) & 0xFF;
            b = (this.RAWoriginal[i]      ) & 0xFF;

            //Search for Stronger Color
            greaterColor = r;
            if( b > r ) {
                    if( g > b ) greaterColor = g;
                    else greaterColor = b;        
            } else if ( g > r ) {
                greaterColor = g;
            }

            this.mapBrightness[i] = greaterColor;
        }
    }

    //Effect: Show only in a certain percent of brightness
    public BufferedImage BrightnessLimit(float percent) {
        // Adjust input values
        percent = percent / 100;

        // Pixel Variables
        int hardCap = (int)(255 * percent);
        int r,g,b,a,bright;

        // Process all pixels
        for(int i=0 ; i < this.RAWoriginal.length ; i++) {   
                //Get information of a pixel of the ORIGINAL image 
                a = (this.RAWoriginal[i] >> 24) & 0xFF;
                r = (this.RAWoriginal[i] >> 16) & 0xFF;
                g = (this.RAWoriginal[i] >>  8) & 0xFF;
                b = (this.RAWoriginal[i]      ) & 0xFF;

                //Brightness information of that same pixel
                bright = this.mapBrightness[i];

                //
                if( bright > hardCap  ) {     
                    a = 0;                 
                }
                this.RAWprocessed[i] = ((a << 24) + (r << 16) + (g << 8) + ( b )); //Write ARGB in byte format
        }

        //Copy the processed array into the raster of processed image
        rbgRasterProcessedW.setDataElements(0, 0, x, y, RAWprocessed);

        return imgProcessed;
    }

    //Return reference to the processed image
    public BufferedImage getImage() {
        return imgProcessed;
    }
}
like image 842
Psyny Avatar asked Jul 15 '15 02:07

Psyny


1 Answers

While the time difference resulting from the change doesn't prove that the repeated searching is the bottleneck, it does strongly implicate it.

If you are willing/able to trade memory for time, I would first sort a list of all the pixel locations by brightness. Next, I would use the sorted list during the animation to look up the next pixel to copy.

An extra piece of advice: use one of Java's built in sorting methods. It's educational to make your own, but learning how to sort doesn't seem to be your goal here. Also, if my guess about the bottleneck is wrong, you'll want to minimize your time pursuing this answer.

like image 152
Pikalek Avatar answered Oct 21 '22 08:10

Pikalek