Categories
Programming Visualization

Folders of Images, Compare and Contrast

Now I’ve experimented with things, I wanted to make something that would allow me to compare different effects on the same photo, and loop through a folder of pictures looking for nice effects.

I overrode mousePressed() to change the image on click.

The big challenge here was running out of Java Heap space once I had more than one image being processed – most of the images are around 2MB each. This meant I couldn’t just read in the image 3 times and manipulate it (or not manipulate it), and even drawing the original first then manipulating it meant I had two manipulated images. I tried a few things here, starting with just increasing the heap size, to no avail. Eventually what I did was resize the image down to the size I was going to display, and creating new images of that size, and then copying the pixels over.

That resolved the issue, except when looping through the images and taking screenshots – I have no idea why this would affect things. For the most part, it worked fine though.

It’s nice to compare the effects side-by-side, see the different bits that are highlighted, depending on the effect. Sometimes it just looks like the weather is completely different. I really love the night-time photos, and the effect that makes them black and white… apart from the lights. Including in this post my favourites and those I found most interesting.

One suggestion I’ve had is to ignore pixels that are at extremes in saturation/brightness (i.e. close to black or white), but having looked at the effects on more images, I’m not convinced that is going to make things better.

 img 1
img 2

img 3
img 4
img 5
img 6
img 7
img 8
img 9
img 10
img 11
img 12
img 13
img 14
img 15
img 16
img 17
img 18
img 19
img 20
img 21
img 22
img 23
img 24
img 25
img 26
img 27
img 28
img 29
img 30
img 31
img 32
img 33
img 34
img 35
img 36
img 37
img 38
img 39
img 40

Source Code

ColorHelper.java

package color;

import processing.core.PApplet;
import processing.core.PImage;
import model.HSBColor;

public class ColorHelper {

	public static HSBColor hsbColorFromImage(PImage img, PApplet applet, int hueRange) {
		img.loadPixels();
		int numberOfPixels = img.pixels.length;
		int[] hues = new int[hueRange];
		float[] saturations = new float[hueRange];
		float[] brightnesses = new float[hueRange];

		for (int i = 0; i < numberOfPixels; i++) {
			int pixel = img.pixels[i];
			int hue = Math.round(applet.hue(pixel));
			float saturation = applet.saturation(pixel);
			float brightness = applet.brightness(pixel);
			hues[hue]++;
			saturations[hue] += saturation;
			brightnesses[hue] += brightness;
		}

		// Find the most common hue.
		int hueCount = hues[0];
		int hue = 0;
		for (int i = 1; i < hues.length; i++) {
 			if (hues[i] > hueCount) {
				hueCount = hues[i];
				hue = i;
			}
		}

		// Return the color to display.
		float s = saturations[hue] / hueCount;
		float b = brightnesses[hue] / hueCount;
		return new HSBColor(hue, s, b);
	}
}

CompareAndContrastHue.java

package ui;

import java.io.File;

import color.ColorHelper;
import model.HSBColor;
import processing.core.PApplet;
import processing.core.PImage;

@SuppressWarnings("serial")
public class CompareAndContrastHue extends PApplet {

	int fileIndex = 0;
	private String[] fileNames;
	private static String filePath = "../data/images/";

	static final int hueRange = 320;
	static final int hueTolerance = 40;
	private static final int imageWidth = 384;
	private static final int imageHeight = 268;

	public void setup() {
		size(3*imageWidth, imageHeight);
		background(0);
		noLoop();
		colorMode(HSB, hueRange - 1);
		// Read in images.
		File dir = new File(filePath);
		fileNames = dir.list();
		nextFileIndex();
	}

	public void draw() {
		PImage img = loadImage(filePath + fileNames[fileIndex]);
		img.loadPixels();
		img.resize(imageWidth, imageHeight);
		HSBColor color = ColorHelper.hsbColorFromImage(img, this, hueRange);
		image(img, imageWidth, 0, imageWidth, imageHeight);

		// Display image only showing dominant hue.
		drawImage(createImage(imageWidth, imageHeight, HSB), img.pixels, color,
				0, 0, imageWidth, imageHeight, false);

		// Display image excluding dominant hue.
		drawImage(createImage(imageWidth, imageHeight, HSB), img.pixels, color,
				2 * imageWidth, 0, imageWidth, imageHeight, true);
	}

	public void mousePressed() {
		nextFileIndex();
		redraw();
	}

	private void drawImage(PImage img, int[] pixels, HSBColor color,
			int x, int y, int width, int height, boolean showDominantHue) {
		img.loadPixels();
		// Manipulate photo, grayscale any pixel that isn't close to that hue.
		for (int i = 0; i < img.pixels.length; i++) {
			int pixel = pixels[i];
			float hue = hue(pixel);
			if (hueInRange(hue, color.h, hueTolerance) == showDominantHue) {
				float brightness = brightness(pixel);
				img.pixels[i] = color(brightness);
			} else {
				img.pixels[i] = pixel;
			}
		}
		image(img, x, y, width, height);
	}

	private void nextFileIndex() {
		while (true) {
			fileIndex++;
			if (fileIndex < fileNames.length) {
				if (fileNames[fileIndex].toLowerCase().contains(".jpg")) {
					break;
				}
			} else {
				fileIndex = 0;
			}
		}
	}

	private static boolean hueInRange(float hueA, float hueB, int tolerance) {
		return Math.abs(hueA - hueB) < tolerance;
	}
}