Created
July 19, 2013 13:53
-
-
Save tahb/6039254 to your computer and use it in GitHub Desktop.
Colour Dither
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public ColorImage apply(ColorImage image) | |
{ | |
// Store the image objects width and height in local variables. | |
int image_width = image.getWidth(); | |
int image_height = image.getHeight(); | |
// Store all given fixed colours into array. | |
Color[] palette = new Color[8]; | |
palette[0] = new Color (0,0,0); | |
palette[1] = new Color (255,255,255); | |
palette[2] = new Color (255,0,0); | |
palette[3] = new Color (0,255,0); | |
palette[4] = new Color (0,0,255); | |
palette[5] = new Color (0,255,255); | |
palette[6] = new Color (255,0,255); | |
palette[7] = new Color (255,255,0); | |
// Declare and create a 2D array for all pixels (x,y) in image to provide quicker lookups. | |
int[][] pixels = new int[image_width][image_height]; | |
// Iterate over each pixel and get the RGB values from the original image object and store them in this array. | |
for ( int y = 0; y < image_height; y++ ) { | |
for ( int x = 0; x < image_width; x++ ) { | |
pixels[x][y] = image.getRGB( x, y ); | |
} | |
} | |
// Iterate over each pixel starting on the Y axis and then the X axis for full coverage. | |
for ( int y = 0; y < image_height; y++ ) { | |
for ( int x = 0; x < image_width; x++ ) { | |
// Determine if there are pixels to the bottom, left and right of this pixel for later dithering. | |
boolean left, right, bottom; | |
bottom = ( y + 1 ) < ( image_height - 1 ); | |
right = ( x + 1 ) < ( image_width - 1 ); | |
left = ( x - 1 ) >= 1; | |
// Create new temp array to store the RGB values for *this* pixel from the pre-defined pixels array | |
int[] old_rgb = new int[3]; | |
// Create counter used later in Euclid's theorem to detect the nearest colour accurately. | |
double counter = 99999; | |
// Create variable that will hold the best colour we can find from the palette array | |
Color best_color = null; | |
// Create error array that will hold the error values, each index will correspond to RGB values | |
int[] error = new int[3]; | |
// Find the RGB (0-255) values from *this* pixel in the array by bit shifting (no extra method calls to getRed()) | |
old_rgb[0] = ( pixels[x][y] >> 16 ) &255; | |
old_rgb[1] = ( pixels[x][y] >> 8 ) &255; | |
old_rgb[2] = ( pixels[x][y] ) &255; | |
// For each Palette colour defined, loop over so we cover each one | |
for ( int i = 0; i < palette.length; i++ ) { | |
// Using Euclid's theorem, use the RGB values for the old pixel with the corresponding RGB values of | |
// each colour in our palette. In order to establish the most likely colour | |
double color_vector = Math.sqrt( | |
Math.pow((old_rgb[0] - palette[i].getRed()), 2) + | |
Math.pow((old_rgb[1] - palette[i].getGreen()), 2) + | |
Math.pow((old_rgb[2] - palette[i].getBlue()), 2) | |
); | |
// If the resulting color_vector is lower than any previous tested palette colours, set it as the new best match | |
if ( color_vector < counter ) { | |
counter = color_vector; | |
best_color = palette[i]; | |
} | |
} | |
// From the best matched colour found, calculate the difference for each RGB value by taking it away from the original pixel | |
error[0] = old_rgb[0] - best_color.getRed(); | |
error[1] = old_rgb[1] - best_color.getGreen(); | |
error[2] = old_rgb[2] - best_color.getBlue(); | |
// Update this pixel on the image object with its new colour. | |
image.setPixel( x, y, best_color ); | |
// Depending on neighbouring pixels, call the diffuse method on each position in the pixels array and set the new value back | |
if ( right ) { | |
// Call the dither method giving it the current position 7, the RGB values and the error array | |
// Assign this new RGB integer that dithering returns to the correct position in the pixels array so it accumalates | |
pixels[x+1][y] = dither (7, ( pixels[x+1][y] >> 16 ) &255, ( pixels[x+1][y] >> 8 ) &255, pixels[x+1][y] &255, error); | |
} | |
if ( bottom ) { | |
pixels[x][y+1] = dither (5, ( pixels[x][y+1] >> 16 ) &255, ( pixels[x][y+1] >> 8 ) &255, pixels[x][y+1] &255, error); | |
} | |
if ( bottom && right ) { | |
pixels[x+1][y+1] = dither (1, ( pixels[x+1][y+1] >> 16 ) &255, ( pixels[x+1][y+1] >> 8 ) &255, pixels[x+1][y+1] &255, error); | |
} | |
if ( bottom && left ) { | |
pixels[x-1][y+1] = dither (3, ( pixels[x-1][y+1] >> 16 ) &255, ( pixels[x-1][y+1] >> 8 ) &255, pixels[x-1][y+1] &255, error); | |
} | |
} | |
} | |
// Return the image after applying Ffloyd Steinberg algorithm using Euclid's theorem for improved quality | |
return image; | |
} | |
/** | |
* Apply dithering to a particular pixel | |
* @param multiply The value to multiply by when creating the quant_error | |
* @param red The RGB value for Red | |
* @param green The RGB value for Green | |
* @param blue The RGB value for Blue | |
* @param error[] Is an array of three error values pre determined from the difference of the old and new pixel | |
* @return The new RGB value after dithering in integer form | |
*/ | |
private int dither (int multiply, int red, int green, int blue, int[] error) { | |
// Store RGB values into an array to iterate over and compare against it's error | |
int[] rgb_values = new int [3]; | |
rgb_values[0] = red; | |
rgb_values[1] = green; | |
rgb_values[2] = blue; | |
// For each colour (will always be three) | |
for ( int i = 0; i < rgb_values.length; i++ ) { | |
// Calculate the associative error to add onto the pixel for each RGB value | |
int quant_error = ( ( multiply * error[i] ) / 16 ); | |
// If combining the current RGB and the error results in going over 255, thus an invalid value, cap it at max (255) | |
if ( rgb_values[i] + quant_error > 255 ) { | |
rgb_values[i] = 255; | |
// If the same addition results in a negative number and therefore will cause a broken value after bit shifting cap at 0 | |
} else if ( rgb_values[i] + quant_error < 0 ) { | |
rgb_values[i] = 0; | |
} else { | |
// Else adding the error on does not put the value out of bounds | |
rgb_values[i] += quant_error; | |
} | |
} | |
// Create a new RGB integer value via bit shifting and return it | |
int new_pixel = ( ( rgb_values[0] &255 ) << 16 ) | ( ( rgb_values[1] &255 ) << 8 ) | rgb_values[2] &255; | |
return new_pixel; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment