HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Reading and writing of pixels of BufferedImages

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
readingwritingpixelsandbufferedimages

Problem

I'm working on a project that takes in a video file and does some image processing on each individual frame. The first step in that processing is to take each source frame and generate three monochromatic images based upon the average red, green, and blue values of the image. If that doesn't make sense, I've drawn up an example image for illustration:

I achieve this step by passing in a BufferedImage (along with a frame number) to a function that loops over the individual pixels and extracts the color data (via getRGB()), and then setRGB()s it into the the three new images as is appropriate.

Unfortunately the performance is killing me, with it taking up to 4 seconds to render the three images per frame (and, by the way, this is with test footage whose resolution is only 856x480! I'll need to be able to use it on footage of 1080p!) For a 30 second clip at 30FPS, this first step takes around an hour! (~4 seconds x 30 seconds x 30 frames = 3600 seconds) I admit, my 1.4GHz dual core really isn't cut out for this kind of stuff, but if I can render 4 minutes of 1080p 60FPS footage in 10 minutes using Virtualdub, then I know I can squeeze more performance out of this thing.

The code I wrote is below. I'm using Rhino to script Java, but I think it should be clear even to those who normally don't use JavaScript.

`importPackage(java.io)
importPackage(javax.imageio)
importPackage(java.awt.image)
importPackage(java.awt)

function makeMonochromaticImages(img, n) {
var w = img.getWidth()
var h = img.getHeight()

// These are the three monochromatic images produced.
var red = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
var green = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
var blue = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

// I've cached the constructor to avoid constant overload resoloution
var _Color = Color['(int,int,int)']

for(var x = 0; x

I've already considered some other potential options such as manually c

Solution

The problem comes from iteratively accessing your image object in your current manner. Take a look below. This is substantially faster.

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;

public class FastRGB {

private final int width;
private final int height;
private final boolean hasAlphaChannel;
private int pixelLength;
private final byte[] pixels;

public FastRGB(BufferedImage image) {
    pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
    width = image.getWidth();
    height = image.getHeight();
    hasAlphaChannel = image.getAlphaRaster() != null;
    pixelLength = 3;
    if (hasAlphaChannel) {
        pixelLength = 4;
    }
}

public int getRGB(int x, int y) {
    int pos = (y * pixelLength * width) + (x * pixelLength);
    int argb = -16777216;
    if (hasAlphaChannel) {
        argb = (((int) pixels[pos++] & 0xff) << 24);
    }
    argb += ((int) pixels[pos++] & 0xff);
    argb += (((int) pixels[pos++] & 0xff) << 8);
    argb += (((int) pixels[pos++] & 0xff) << 16);
    return argb;
}
}

Code Snippets

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;


public class FastRGB {

private final int width;
private final int height;
private final boolean hasAlphaChannel;
private int pixelLength;
private final byte[] pixels;

public FastRGB(BufferedImage image) {
    pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
    width = image.getWidth();
    height = image.getHeight();
    hasAlphaChannel = image.getAlphaRaster() != null;
    pixelLength = 3;
    if (hasAlphaChannel) {
        pixelLength = 4;
    }
}

public int getRGB(int x, int y) {
    int pos = (y * pixelLength * width) + (x * pixelLength);
    int argb = -16777216;
    if (hasAlphaChannel) {
        argb = (((int) pixels[pos++] & 0xff) << 24);
    }
    argb += ((int) pixels[pos++] & 0xff);
    argb += (((int) pixels[pos++] & 0xff) << 8);
    argb += (((int) pixels[pos++] & 0xff) << 16);
    return argb;
}
}

Context

StackExchange Code Review Q#79822, answer score: 2

Revisions (0)

No revisions yet.