Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blur an image using java.util.concurrent, however, the resulting image is entirely black

I'm new to Java and is trying to learn the concept of high level concurrency. I saw this code at Java Tutorial Oracle. However, when I run the code, the IDE output an image that is entire black. Why is this happening? And also, how is the compute() method called?

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import javax.imageio.ImageIO;

/**
 * ForkBlur implements a simple horizontal image blur. It averages pixels in the
 * source array and writes them to a destination array. The sThreshold value
 * determines whether the blurring will be performed directly or split into two
 * tasks.
 *
 * This is not the recommended way to blur images; it is only intended to
 * illustrate the use of the Fork/Join framework.
 */
public class ForkBlur extends RecursiveAction {

    private int[] mSource;
    private int mStart;
    private int mLength;
    private int[] mDestination;
    private int mBlurWidth = 15; // Processing window size, should be odd.

    public ForkBlur(int[] src, int start, int length, int[] dst) {
        mSource = src;
        mStart = start;
        mLength = length;
        mDestination = dst;
    }

    // Average pixels from source, write results into destination.
    protected void computeDirectly() {
        int sidePixels = (mBlurWidth - 1) / 2;
        for (int index = mStart; index < mStart + mLength; index++) {
            // Calculate average.
            float rt = 0, gt = 0, bt = 0;
            for (int mi = -sidePixels; mi <= sidePixels; mi++) {
                int mindex = Math.min(Math.max(mi + index, 0), mSource.length - 1);
                int pixel = mSource[mindex];
                rt += (float) ((pixel & 0x00ff0000) >> 16) / mBlurWidth;
                gt += (float) ((pixel & 0x0000ff00) >> 8) / mBlurWidth;
                bt += (float) ((pixel & 0x000000ff) >> 0) / mBlurWidth;
            }

            // Re-assemble destination pixel.
            int dpixel = (0xff000000)
                    | (((int) rt) << 16)
                    | (((int) gt) << 8)
                    | (((int) bt) << 0);
            mDestination[index] = dpixel;
        }
    }
    protected static int sThreshold = 10000;

    @Override
    protected void compute() {
        if (mLength < sThreshold) {
            computeDirectly();
            return;
        }

        int split = mLength / 2;

        invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
                new ForkBlur(mSource, mStart + split, mLength - split, 
                mDestination));
    }

    // Plumbing follows.
    public static void main(String[] args) throws Exception {
        String srcName = "/Users/justin/NetBeansProjects/JavaTutorialOracle/src/JTOConcurrency/Screen Shot 2016-02-19 at 10.30.51 AM.jpg";
        File srcFile = new File(srcName);
        BufferedImage image = ImageIO.read(srcFile);

        System.out.println("Source image: " + srcName);

        BufferedImage blurredImage = blur(image);

        String dstName = "blurred-tulips.jpg";
        File dstFile = new File(dstName);
        ImageIO.write(blurredImage, "jpg", dstFile);

        System.out.println("Output image: " + dstName);

    }

    public static BufferedImage blur(BufferedImage srcImage) {
        int w = srcImage.getWidth();
        System.out.println("w: " + w);
        int h = srcImage.getHeight();
        System.out.println("h: " + h);

        int[] src = srcImage.getRGB(0, 0, w, h, null, 0, w);
        System.out.println("src[0]" + src[0]);
        System.out.println("src[src.length - 1]: " + src[src.length - 1]);
        int[] dst = new int[src.length];
        System.out.println("src.length: " + src.length);

        System.out.println("Array size is " + src.length);
        System.out.println("Threshold is " + sThreshold);

        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println(Integer.toString(processors) + " processor"
                + (processors != 1 ? "s are " : " is ")
                + "available");

        ForkBlur fb = new ForkBlur(src, 0, src.length, dst);

        ForkJoinPool pool = new ForkJoinPool();

        long startTime = System.currentTimeMillis();
        pool.invoke(fb);

        long endTime = System.currentTimeMillis();

        System.out.println("Image blur took " + (endTime - startTime) + 
                " milliseconds.");

        BufferedImage dstImage =
                new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        dstImage.setRGB(0, 0, w, h, dst, 0, w);

        return dstImage;
    }
}

Output:

Source image: /Users/justin/NetBeansProjects/JavaTutorialOracle/src/JTOConcurrency/Screen Shot 2016-02-19 at 10.30.51 AM.jpg
w: 454
h: 679
src[0]-5945082
src[src.length - 1]: -9673172
src.length: 308266
Array size is 308266
Threshold is 10000
4 processors are available
Image blur took 53 milliseconds.
Output image: blurred-tulips.jpg

I have attached some information about the photo I am using.

enter image description here

like image 355
Thor Avatar asked Feb 22 '16 00:02

Thor


1 Answers

Using Oracle JRE 1.7.0_71 on OS X 10.11, I can reproduce the issue with a completely black output image.

However, there's no bug in your code, as I can see that the dst array contains the expected values after the fork/join blur operation.

Instead, the problem is our old friend, the ImageIO JPEGImageWriter. It does not write ARGB data as per the JPEG conventions, thus other software will misinterpret the colors*.

Simply change the line:

BufferedImage dstImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

to:

BufferedImage dstImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); // No alpha here

This will create an image without alpha. The JPEGImageWriter will just do the right thing, and the final output image looks as expected.

Another option, using a different file format, like PNG, also fixes the issue. I.e.:

ImageIO.write(blurredImage, "PNG", dstFile);

*) You can see this better if you use 0x00 instead of 0xff for alpha in the computeDirectly(...) method. You'll see an image with weird, probably pink/blueish colors.

like image 187
Harald K Avatar answered Nov 18 '22 20:11

Harald K