lol
This commit is contained in:
commit
613bef2496
108 changed files with 8397 additions and 0 deletions
210
src/zutil/image/ImageFilterProcessor.java
Normal file
210
src/zutil/image/ImageFilterProcessor.java
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
package zutil.image;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
|
||||
/**
|
||||
* This is a abstract class for all the effects
|
||||
*
|
||||
* Inspiration:
|
||||
* http://www.dickbaldwin.com/tocadv.htm
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class ImageFilterProcessor {
|
||||
private BufferedImage img;
|
||||
private ProgressListener progress;
|
||||
|
||||
public ImageFilterProcessor(BufferedImage img){
|
||||
this.img = img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener
|
||||
* @param listener The listener, null to disable the progress
|
||||
*/
|
||||
public void setProgressListener(ProgressListener listener){
|
||||
this.progress = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress in percent
|
||||
*/
|
||||
protected void setProgress(double percent){
|
||||
if(progress != null) progress.progressUpdate(this, null, percent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a effect to a given image
|
||||
*
|
||||
* @param effect The effect to use
|
||||
* @param img The image to process
|
||||
* @return The processed image
|
||||
*/
|
||||
public static ImageFilterProcessor getProcessor(String effect, BufferedImage img) throws InstantiationException, IllegalAccessException, ClassNotFoundException, InterruptedException{
|
||||
ImageFilterProcessor processor = (ImageFilterProcessor)Class.forName(effect).newInstance();
|
||||
processor.img = img;
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the chosen effect to the image
|
||||
*
|
||||
* @return The Image with the effect
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public BufferedImage process() throws InterruptedException{
|
||||
int cols = img.getWidth();
|
||||
int rows = img.getHeight();
|
||||
|
||||
if(cols < 0 || rows < 0){
|
||||
throw new InterruptedException("Image not Loaded!!!");
|
||||
}
|
||||
|
||||
// converts the img to raw data
|
||||
int[][][] data = convertToArray(img, cols, rows);
|
||||
//processes the image
|
||||
data = process(data, cols, rows);
|
||||
//converts back the image
|
||||
return convertToImage(data, data[0].length, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Integer array whit the pixel data of the image
|
||||
* int[row][col][4]
|
||||
* 0 -> Alpha data
|
||||
* Red data
|
||||
* Green data
|
||||
* 4 -> Blue data
|
||||
*
|
||||
* @param img The image to convert
|
||||
* @param cols Columns of the image
|
||||
* @param rows Rows of the image
|
||||
* @return A Integer array
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public static int[][][] convertToArray(BufferedImage img, int cols, int rows) throws InterruptedException{
|
||||
int[][][] data = new int[rows][cols][4];
|
||||
// Reads in the image to a one dim array
|
||||
int[] pixels = img.getRGB(0, 0, cols, rows, null, 0, cols);
|
||||
|
||||
// Read the pixel data and put it in the data array
|
||||
for(int y=0; y<rows ;y++){
|
||||
// reading a row
|
||||
int[] aRow = new int[cols];
|
||||
for(int x=0; x<cols ;x++){
|
||||
int element = y * cols + x;
|
||||
aRow[x] = pixels[element];
|
||||
}
|
||||
|
||||
// Reading in the color data
|
||||
for(int x=0; x<cols ;x++){
|
||||
//Alpha data
|
||||
data[y][x][0] = (aRow[x] >> 24) & 0xFF;
|
||||
//Red data
|
||||
data[y][x][1] = (aRow[x] >> 16) & 0xFF;
|
||||
//Green data
|
||||
data[y][x][2] = (aRow[x] >> 8) & 0xFF;
|
||||
//Blue data
|
||||
data[y][x][3] = (aRow[x])& 0xFF;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a pixel data array to a java Image object
|
||||
*
|
||||
* @param pixels The pixel data array
|
||||
* @param cols Columns of the image
|
||||
* @param rows Rows of the image
|
||||
* @return A Image
|
||||
*/
|
||||
public static BufferedImage convertToImage(int[][][] pixels, int cols, int rows){
|
||||
int[] data = new int[cols * rows * 4];
|
||||
|
||||
//Move the data into the 1D array. Note the
|
||||
// use of the bitwise OR operator and the
|
||||
// bitwise left-shift operators to put the
|
||||
// four 8-bit bytes into each int.
|
||||
int index = 0;
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x< cols ;x++){
|
||||
data[index] = ((pixels[y][x][0] << 24) & 0xFF000000)
|
||||
| ((pixels[y][x][1] << 16) & 0x00FF0000)
|
||||
| ((pixels[y][x][2] << 8) & 0x0000FF00)
|
||||
| ((pixels[y][x][3]) & 0x000000FF);
|
||||
index++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BufferedImage img = new BufferedImage(cols, rows, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
img.setRGB(0, 0, cols, rows, data, 0, cols);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given array to a new one that it returns
|
||||
* @param data The data to duplicate
|
||||
* @param cols The amount of columns
|
||||
* @param rows The amount of rows
|
||||
* @return The array copy
|
||||
*/
|
||||
public static int[][][] copyArray(int[][][] data,int cols,int rows){
|
||||
int[][][] copy = new int[rows][cols][4];
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
copy[y][x][0] = data[y][x][0];
|
||||
copy[y][x][1] = data[y][x][1];
|
||||
copy[y][x][2] = data[y][x][2];
|
||||
copy[y][x][3] = data[y][x][3];
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method clips the values of the pixel so that they
|
||||
* are in the range 0-255
|
||||
* @param data The image data
|
||||
* @param cols The amount of columns
|
||||
* @param rows The amount of rows
|
||||
*/
|
||||
public static void clip(int[][][] data, int cols, int rows){
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
data[y][x][1] = clip(data[y][x][1]);
|
||||
data[y][x][1] = clip(data[y][x][2]);
|
||||
data[y][x][1] = clip(data[y][x][3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method clips the values of a color so that it
|
||||
* is in the range 0-255
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
public static int clip(int color){
|
||||
if(color < 0)
|
||||
return 0;
|
||||
else if(color > 255)
|
||||
return 255;
|
||||
else
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The underlying effect is run here
|
||||
* @param data The raw image to apply the effect to
|
||||
* @param cols Columns of the image
|
||||
* @param rows Rows of the image
|
||||
*/
|
||||
public abstract int[][][] process(final int[][][] data, int cols, int rows);
|
||||
}
|
||||
170
src/zutil/image/ImageUtil.java
Normal file
170
src/zutil/image/ImageUtil.java
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
package zutil.image;
|
||||
|
||||
/**
|
||||
* Some util methods for image processing
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class ImageUtil {
|
||||
/**
|
||||
* Returns the peek value in the image
|
||||
*
|
||||
* @param data The image data
|
||||
* @param cols The number of columns
|
||||
* @param rows The number of rows
|
||||
* @return The peak value of the image
|
||||
*/
|
||||
public static int peakValue(int[][][] data, int cols, int rows) {
|
||||
int peak = 0;
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
if(data[y][x][1] > peak) peak = data[y][x][1];
|
||||
if(data[y][x][2] > peak) peak = data[y][x][2];
|
||||
if(data[y][x][3] > peak) peak = data[y][x][3];
|
||||
}
|
||||
}
|
||||
return peak;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the image data by the given scale
|
||||
*
|
||||
* @param data The image data
|
||||
* @param cols The number of columns
|
||||
* @param rows The number of rows
|
||||
* @param scale The scale to normalize the image by
|
||||
*/
|
||||
public static void normalize(int[][][] data, int cols, int rows, double scale) {
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
data[y][x][1] = (int)(data[y][x][1] * scale);
|
||||
data[y][x][2] = (int)(data[y][x][2] * scale);
|
||||
data[y][x][3] = (int)(data[y][x][3] * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rms value of the image
|
||||
* (The RMS value is a measure of the width of the color distribution.)
|
||||
*
|
||||
* @param data The image data
|
||||
* @param cols The number of columns
|
||||
* @param rows The number of rows
|
||||
* @return The rms value for the image
|
||||
*/
|
||||
public static int rms(int[][][] data, int cols, int rows){
|
||||
int pixelCount = 0;
|
||||
long accum = 0;
|
||||
for(int y=0; y <rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
accum += data[y][x][1] * data[y][x][1];
|
||||
accum += data[y][x][2] * data[y][x][2];
|
||||
accum += data[y][x][3] * data[y][x][3];
|
||||
pixelCount += 3;
|
||||
}
|
||||
}
|
||||
int meanSquare = (int)(accum/pixelCount);
|
||||
int rms = (int)(Math.sqrt(meanSquare));
|
||||
return rms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the given image data by the given value
|
||||
*
|
||||
* @param data The image data
|
||||
* @param cols The number of columns
|
||||
* @param rows The number of rows
|
||||
* @param scale The number to scale the image by
|
||||
*/
|
||||
public static void scale(int[][][] data, int cols, int rows, double scale){
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
data[y][x][1] *= scale;
|
||||
data[y][x][2] *= scale;
|
||||
data[y][x][3] *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mean value of the given image data
|
||||
*
|
||||
* @param data The image data
|
||||
* @param cols The column count
|
||||
* @param rows The row count
|
||||
* @return The mean value of the image
|
||||
*/
|
||||
public static int meanValue(int[][][] data,int cols, int rows){
|
||||
int pixelCount = 0;
|
||||
long accum = 0;
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
accum += data[y][x][1];
|
||||
accum += data[y][x][2];
|
||||
accum += data[y][x][3];
|
||||
pixelCount += 3;
|
||||
}
|
||||
}
|
||||
// calculate the mean value
|
||||
return (int)(accum/pixelCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the mean value to the image data
|
||||
*
|
||||
* @param data The image data
|
||||
* @param cols The number of columns
|
||||
* @param rows The number of rows
|
||||
* @param mean The mean value
|
||||
*/
|
||||
public static void addMeanValue(int[][][] data,int cols, int rows, int mean){
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
data[y][x][1] += mean;
|
||||
data[y][x][2] += mean;
|
||||
data[y][x][3] += mean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all the pixel data from the image to the new array
|
||||
*
|
||||
* @param data The data to copy
|
||||
* @param xStart X start position on the source
|
||||
* @param yStart Y start position on the source
|
||||
* @param width The amount of pixels to copy
|
||||
* @param hight The amount of pixels to copy
|
||||
* @return A copy of the data array
|
||||
*/
|
||||
public static int[][][] crop(int[][][] data, int xStart, int yStart, int width, int hight){
|
||||
return crop(data, xStart, yStart, null, 0, 0, width, hight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all the pixel data from the image to the new array
|
||||
*
|
||||
* @param data The data to copy
|
||||
* @param xData X start position in the source
|
||||
* @param yData Y start position in the source
|
||||
* @param crop The destination
|
||||
* @param xCrop X start position in the destination
|
||||
* @param yCrop Y start position in the destination
|
||||
* @param width The amount of pixels to copy
|
||||
* @param hight The amount of pixels to copy
|
||||
* @return A copy of the data array
|
||||
*/
|
||||
public static int[][][] crop(int[][][] data, int xData, int yData, int[][][] crop, int xCrop, int yCrop, int width, int hight){
|
||||
if(crop==null) crop = new int[width][hight][4];
|
||||
for(int y=0; y<width ;y++){
|
||||
for(int x=0; x<hight ;x++){
|
||||
crop[y+yData][x+xData][0] = data[y+yCrop][x+xCrop][0];
|
||||
crop[y+yData][x+xData][1] = data[y+yCrop][x+xCrop][1];
|
||||
crop[y+yData][x+xData][2] = data[y+yCrop][x+xCrop][2];
|
||||
crop[y+yData][x+xData][3] = data[y+yCrop][x+xCrop][3];
|
||||
}
|
||||
}
|
||||
return crop;
|
||||
}
|
||||
}
|
||||
88
src/zutil/image/filters/BlurFilter.java
Normal file
88
src/zutil/image/filters/BlurFilter.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.image.ImageUtil;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
public class BlurFilter extends ImageFilterProcessor{
|
||||
private int blurValue;
|
||||
|
||||
/**
|
||||
* Creates a blur effect on the image
|
||||
* @param img The image to blur
|
||||
*/
|
||||
public BlurFilter(BufferedImage img){
|
||||
this(img, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blur effect on the image
|
||||
* @param img The image to blur
|
||||
* @param blur The amount to blur
|
||||
*/
|
||||
public BlurFilter(BufferedImage img, int blur){
|
||||
super(img);
|
||||
blurValue = blur;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(final int[][][] data, int cols, int rows) {
|
||||
int inputPeak = ImageUtil.peakValue(data, cols, rows);
|
||||
|
||||
int[][][] output = new int[rows][cols][4];
|
||||
//Perform the convolution one or more times
|
||||
// in succession
|
||||
for(int i=0; i<blurValue ;i++){
|
||||
//Iterate on each pixel as a registration
|
||||
// point.
|
||||
for(int y=1; y<rows-2 ;y++){
|
||||
setProgress(ZMath.percent(0, (blurValue-1)*(rows-3), i*(rows-3)+y));
|
||||
for(int x=0+1; x<cols-2 ;x++){
|
||||
int redSum =
|
||||
data[y - 1][x - 1][1] +
|
||||
data[y - 1][x - 0][1] +
|
||||
data[y - 1][x + 1][1] +
|
||||
data[y - 0][x - 1][1] +
|
||||
data[y - 0][x - 0][1] +
|
||||
data[y - 0][x + 1][1] +
|
||||
data[y + 1][x - 1][1] +
|
||||
data[y + 1][x - 0][1] +
|
||||
data[y + 1][x + 1][1];
|
||||
int greenSum =
|
||||
data[y - 1][x - 1][2] +
|
||||
data[y - 1][x - 0][2] +
|
||||
data[y - 1][x + 1][2] +
|
||||
data[y - 0][x - 1][2] +
|
||||
data[y - 0][x - 0][2] +
|
||||
data[y - 0][x + 1][2] +
|
||||
data[y + 1][x - 1][2] +
|
||||
data[y + 1][x - 0][2] +
|
||||
data[y + 1][x + 1][2];
|
||||
int blueSum =
|
||||
data[y - 1][x - 1][3] +
|
||||
data[y - 1][x - 0][3] +
|
||||
data[y - 1][x + 1][3] +
|
||||
data[y - 0][x - 1][3] +
|
||||
data[y - 0][x - 0][3] +
|
||||
data[y - 0][x + 1][3] +
|
||||
data[y + 1][x - 1][3] +
|
||||
data[y + 1][x - 0][3] +
|
||||
data[y + 1][x + 1][3];
|
||||
|
||||
output[y][x][0] = data[y][x][0];
|
||||
output[y][x][1] = redSum;
|
||||
output[y][x][2] = greenSum;
|
||||
output[y][x][3] = blueSum;
|
||||
}
|
||||
}
|
||||
|
||||
// getting the new peak value and normalizing the image
|
||||
int outputPeak = ImageUtil.peakValue(output, cols, rows);
|
||||
ImageUtil.normalize(output, cols, rows, ((double)inputPeak)/outputPeak );
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
90
src/zutil/image/filters/ColorIntensityFilter.java
Normal file
90
src/zutil/image/filters/ColorIntensityFilter.java
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
public class ColorIntensityFilter extends ImageFilterProcessor{
|
||||
private boolean invert;
|
||||
private int redScale;
|
||||
private int greenScale;
|
||||
private int blueScale;
|
||||
|
||||
public ColorIntensityFilter(BufferedImage img){
|
||||
this(img, 50, 50, 50, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ColorIntensityEffect object with the given values
|
||||
* @param img The image data
|
||||
* @param inv If the image color should be inverted
|
||||
*/
|
||||
public ColorIntensityFilter(BufferedImage img, boolean inv){
|
||||
this(img, 100, 100, 100, inv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ColorIntensityEffect object with the given values
|
||||
* @param img The image data
|
||||
* @param red The scale of red (0-100)
|
||||
* @param green The scale of green (0-100)
|
||||
* @param blue The scale of blue (0-100)
|
||||
*/
|
||||
public ColorIntensityFilter(BufferedImage img, int red, int green, int blue){
|
||||
this(img, red, green, blue, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ColorIntensityEffect object with the given values
|
||||
* @param img The image data
|
||||
* @param red The scale of red (0-100)
|
||||
* @param green The scale of green (0-100)
|
||||
* @param blue The scale of blue (0-100)
|
||||
* @param inv If the image color should be inverted
|
||||
*/
|
||||
public ColorIntensityFilter(BufferedImage img, int red, int green, int blue, boolean inv){
|
||||
super(img);
|
||||
invert = false;
|
||||
redScale = red;
|
||||
greenScale = green;
|
||||
blueScale = blue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(final int[][][] data, int cols, int rows) {
|
||||
// making sure the scales are right
|
||||
if(redScale > 100) redScale = 100;
|
||||
else if(redScale < 0) redScale = 0;
|
||||
|
||||
if(greenScale > 100) greenScale = 100;
|
||||
else if(greenScale < 0) greenScale = 0;
|
||||
|
||||
if(blueScale > 100) blueScale = 100;
|
||||
else if(blueScale < 0) blueScale = 0;
|
||||
|
||||
int[][][] output = new int[rows][cols][4];
|
||||
|
||||
// Applying the color intensity to the image
|
||||
for(int y=0; y<rows ;y++){
|
||||
setProgress(ZMath.percent(0, rows-1, y));
|
||||
for(int x=0; x<cols ;x++){
|
||||
if(!invert){
|
||||
// inversion
|
||||
output[y][x][0] = data[y][x][0];
|
||||
output[y][x][1] = 255 - data[y][x][1] * redScale/100;
|
||||
output[y][x][2] = 255 - data[y][x][2] * greenScale/100;
|
||||
output[y][x][3] = 255 - data[y][x][3] * blueScale/100;
|
||||
}
|
||||
else{
|
||||
output[y][x][0] = data[y][x][0];
|
||||
output[y][x][1] = data[y][x][1] * redScale/100;
|
||||
output[y][x][2] = data[y][x][2] * greenScale/100;
|
||||
output[y][x][3] = data[y][x][3] * blueScale/100;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
47
src/zutil/image/filters/ContrastBrightnessFilter.java
Normal file
47
src/zutil/image/filters/ContrastBrightnessFilter.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.image.ImageUtil;
|
||||
|
||||
public class ContrastBrightnessFilter extends ImageFilterProcessor{
|
||||
private double contrast;
|
||||
private double brightness;
|
||||
|
||||
/**
|
||||
* Creates a ContrastBrightnessEffect object with the given values
|
||||
* @param img The image to apply the effect to
|
||||
*/
|
||||
public ContrastBrightnessFilter(BufferedImage img){
|
||||
this(img, 3, 1.2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ContrastBrightnessEffect object with the given values
|
||||
* @param img The image to apply the effect to
|
||||
* @param con The contrast to apply
|
||||
* @param brig The brightness to apply
|
||||
*/
|
||||
public ContrastBrightnessFilter(BufferedImage img, double con, double brig){
|
||||
super(img);
|
||||
contrast = con;
|
||||
brightness = brig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(final int[][][] data, int cols, int rows) {
|
||||
int mean = ImageUtil.meanValue(data, cols, rows);
|
||||
|
||||
int[][][] output = copyArray(data, cols, rows);
|
||||
|
||||
ImageUtil.addMeanValue(output, cols, rows, mean*(-1));
|
||||
ImageUtil.scale(output, cols, rows, contrast);
|
||||
ImageUtil.addMeanValue(output, cols, rows, (int)(brightness*mean));
|
||||
|
||||
clip(output , cols, rows);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
87
src/zutil/image/filters/DitheringFilter.java
Normal file
87
src/zutil/image/filters/DitheringFilter.java
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
|
||||
public class DitheringFilter extends ImageFilterProcessor{
|
||||
// default palette is black and white
|
||||
private int[][] palette = {
|
||||
{255,0,0,0},
|
||||
{255,255,255,255}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets up a default DitheringEffect
|
||||
*/
|
||||
public DitheringFilter(BufferedImage img){
|
||||
super(img);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Dithering Effect object
|
||||
* @param img The image to apply the effect on
|
||||
* @param palette The palette to use on the image
|
||||
* int[colorCount][4]
|
||||
* 0 -> Alpha data
|
||||
* Red data
|
||||
* Green data
|
||||
* 4 -> Blue data
|
||||
*/
|
||||
public DitheringFilter(BufferedImage img, int[][] palette){
|
||||
super(img);
|
||||
this.palette = palette;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(final int[][][] data, int cols, int rows) {
|
||||
int error, index;
|
||||
int[] currentPixel;
|
||||
|
||||
int[][][] output = copyArray(data, cols, rows);
|
||||
|
||||
for(int y=0; y<rows ;y++){
|
||||
setProgress(ZMath.percent(0, rows-1, y));
|
||||
for(int x=0; x<cols ;x++){
|
||||
currentPixel = output[y][x];
|
||||
index = findNearestColor(currentPixel, palette);
|
||||
output[y][x] = palette[index];
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
error = currentPixel[i] - palette[index][i];
|
||||
if (x + 1 < cols) {
|
||||
output[y+0][x+1][i] = clip( output[y+0][x+1][i] + (error*7)/16 );
|
||||
}
|
||||
if (y + 1 < rows) {
|
||||
if (x - 1 > 0)
|
||||
output[y+1][x-1][i] = clip( output[y+1][x-1][i] + (error*3)/16 );
|
||||
output[y+1][x+0][i] = clip( output[y+1][x+0][i] + (error*5)/16 );
|
||||
if (x + 1 < cols)
|
||||
output[y+1][x+1][i] = clip( output[y+1][x+1][i] + (error*1)/16 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static int findNearestColor(int[] color, int[][] palette) {
|
||||
int minDistanceSquared = 255*255 + 255*255 + 255*255 + 1;
|
||||
int bestIndex = 0;
|
||||
for (byte i = 0; i < palette.length; i++) {
|
||||
int Rdiff = color[1] - palette[i][0];
|
||||
int Gdiff = color[2] - palette[i][1];
|
||||
int Bdiff = color[3] - palette[i][2];
|
||||
int distanceSquared = Rdiff*Rdiff + Gdiff*Gdiff + Bdiff*Bdiff;
|
||||
if (distanceSquared < minDistanceSquared) {
|
||||
minDistanceSquared = distanceSquared;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
return bestIndex;
|
||||
}
|
||||
}
|
||||
171
src/zutil/image/filters/FaceDetectionFilter.java
Normal file
171
src/zutil/image/filters/FaceDetectionFilter.java
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
public class FaceDetectionFilter extends ImageFilterProcessor{
|
||||
|
||||
public FaceDetectionFilter(BufferedImage img) {
|
||||
super(img);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(int[][][] data, int cols, int rows) {
|
||||
int[][][] IRgBy = convertARGBToIRgBy(data, cols, rows);
|
||||
|
||||
MedianFilter median = new MedianFilter(null, 4*getSCALE(cols,rows), new boolean[]{false,false,true,true});
|
||||
IRgBy = median.process(IRgBy, cols, rows);
|
||||
setProgress(ZMath.percent(0, 4, 1));
|
||||
|
||||
//********* Texture Map ********
|
||||
median = new MedianFilter(null, 8*getSCALE(cols,rows), new boolean[]{false,true,false,false});
|
||||
int[][][] textureMap = median.process(IRgBy, cols, rows);
|
||||
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
textureMap[y][x][1] = Math.abs(IRgBy[y][x][1]-textureMap[y][x][1]);
|
||||
}
|
||||
}
|
||||
|
||||
median = new MedianFilter(null, 12*getSCALE(cols,rows), new boolean[]{false,true,false,false});
|
||||
textureMap = median.process(textureMap, cols, rows);
|
||||
setProgress(ZMath.percent(0, 4, 2));
|
||||
|
||||
//*********** Hue & Saturation *********
|
||||
int[][] skinMap = new int[rows][cols];
|
||||
int[][] hueMap = new int[rows][cols];
|
||||
int[][] saturationMap = new int[rows][cols];
|
||||
|
||||
int hue, saturation;
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
// hue = (atan^2(Rg,By))
|
||||
hue = (int)( Math.atan2(IRgBy[y][x][2], IRgBy[y][x][3]) * 360/2*Math.PI);
|
||||
// saturation = sqrt(Rg^2+By^2)
|
||||
saturation = (int) Math.sqrt(IRgBy[y][x][2]*IRgBy[y][x][2] + IRgBy[y][x][3]*IRgBy[y][x][3]);
|
||||
|
||||
hueMap[y][x] = hue;
|
||||
saturationMap[y][x] = saturation;
|
||||
|
||||
// (1) texture<4.5, 120<160, 10<60
|
||||
// (2) texture<4.5, 150<180, 20<80
|
||||
if((textureMap[y][x][1] < 4.5 && (hue >= 120 && hue <= 160) && (saturation >= 10 && saturation <= 60)) ||
|
||||
(textureMap[y][x][1] < 4.5 && (hue >= 150 && hue <= 180) && (saturation >= 20 && saturation <= 80)) ){
|
||||
skinMap[y][x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
setProgress(ZMath.percent(0, 4, 3));
|
||||
|
||||
//************** SkinMap dilation ********************
|
||||
skinMap = dilation(skinMap , cols, rows);
|
||||
|
||||
|
||||
//*****************************************************
|
||||
setProgress(100);
|
||||
//return convertArrayToARGBchannel(hueMap, cols, rows, -150, 150, 1);
|
||||
//return convertArrayToARGBchannel(saturationMap, cols, rows, 0, 70, 1);
|
||||
return convertArrayToARGBchannel(skinMap, cols, rows, 0, 1, 2);
|
||||
}
|
||||
|
||||
private int[][] dilation(int[][] data, int cols, int rows){
|
||||
int[][] output = new int[rows][cols];
|
||||
int radX = 8;
|
||||
int radY = 8;
|
||||
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
if(data[y][x] == 1){
|
||||
|
||||
for(int dy=y-radY; dy<y+radY ;dy++){
|
||||
for(int dx=x-radX; dx<x+radX ;dx++){
|
||||
if(dy >= 0 && dy < rows && dx >= 0 && dx < cols)
|
||||
output[dy][dx] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts the given data array to a color image
|
||||
*
|
||||
* @param data The 2d data
|
||||
* @param cols The size of the image data
|
||||
* @param rows The size of the image data
|
||||
* @param min The minimum value in the data
|
||||
* @param max The maximum value in the data
|
||||
* @param channel The color channel to apply the data to
|
||||
* @return A ARGB array
|
||||
*/
|
||||
public int[][][] convertArrayToARGBchannel(int[][] data, int cols, int rows,int min, int max, int channel){
|
||||
int[][][] output = new int[rows][cols][4];
|
||||
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
output[y][x][0] = 255;
|
||||
output[y][x][channel] = (int) ZMath.percent(min, max, data[y][x]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts RGB color to log-opponent (IRgBy) with the formula:
|
||||
* I= [L(R)+L(B)+L(G)]/3
|
||||
* Rg = L(R)-L(G)
|
||||
* By = L(B)-[L(G)+L(R)]/2
|
||||
*
|
||||
* @param data The RGB data
|
||||
* @param cols The number of columns
|
||||
* @param rows The number of rows
|
||||
* @return IRgBy data
|
||||
*/
|
||||
public int[][][] convertARGBToIRgBy(int[][][] data, int cols, int rows){
|
||||
int[][][] output = new int[rows][cols][4];
|
||||
|
||||
for(int y=0; y<rows ;y++){
|
||||
for(int x=0; x<cols ;x++){
|
||||
output[y][x][0] = data[y][x][0];
|
||||
// I= [L(R)+L(B)+L(G)]/3
|
||||
output[y][x][1] = (
|
||||
IRgByFunction(data[y][x][1]) +
|
||||
IRgByFunction(data[y][x][2]) +
|
||||
IRgByFunction(data[y][x][3])
|
||||
) / 3;
|
||||
// Rg = L(R)-L(G)
|
||||
output[y][x][2] = IRgByFunction(output[y][x][1]) - IRgByFunction(data[y][x][2]);
|
||||
// By = L(B)-[L(G)+L(R)]/2
|
||||
output[y][x][3] = IRgByFunction(output[y][x][3]) -
|
||||
(IRgByFunction(output[y][x][2]) - IRgByFunction(output[y][x][1])) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
// Helper function to convertToIRgBy()
|
||||
private int IRgByFunction(int value){
|
||||
return (int)(105*Math.log10(value+1));
|
||||
}
|
||||
|
||||
|
||||
private int getSCALE(int cols, int rows){
|
||||
return (cols+rows)/320;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Rectangle getFaceRectangle(){
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
169
src/zutil/image/filters/MedianFilter.java
Normal file
169
src/zutil/image/filters/MedianFilter.java
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.algo.sort.sortable.SortableDataList;
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
/**
|
||||
* The MedianFilter is used for noise reduction and things
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class MedianFilter extends ImageFilterProcessor{
|
||||
private int windowSize;
|
||||
private boolean[] channels;
|
||||
|
||||
/**
|
||||
* Setup a default MedianFilter
|
||||
*
|
||||
* @param img The image to process
|
||||
*/
|
||||
public MedianFilter(BufferedImage img) {
|
||||
this(img, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a default MedianFilter
|
||||
*
|
||||
* @param img The image to process
|
||||
* @param pixels The size of the window
|
||||
*/
|
||||
public MedianFilter(BufferedImage img, int pixels) {
|
||||
this(img, pixels, new boolean[]{true,true,true,true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a default MedianFilter
|
||||
*
|
||||
* @param img The image to process
|
||||
* @param pixels The size of the window
|
||||
* @param channels Is a 4 element array for witch channels to use the filter on
|
||||
*/
|
||||
public MedianFilter(BufferedImage img, int pixels, boolean[] channels) {
|
||||
super(img);
|
||||
this.windowSize = pixels;
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
/*
|
||||
edgex := (window width / 2) rounded down
|
||||
edgey := (window height / 2) rounded down
|
||||
for x from edgex to image width - edgex:
|
||||
for y from edgey to image height - edgey:
|
||||
colorArray[window width][window height];
|
||||
for fx from 0 to window width:
|
||||
for fy from 0 to window height:
|
||||
colorArray[fx][fy] := pixelvalue[x + fx - edgex][y + fy - edgey]
|
||||
Sort colorArray[][];
|
||||
pixelValue[x][y] := colorArray[window width / 2][window height / 2];
|
||||
*/
|
||||
@Override
|
||||
public int[][][] process(int[][][] data, int cols, int rows) {
|
||||
int[][][] output = new int[rows][cols][4];
|
||||
|
||||
int edgeX = windowSize / 2;
|
||||
int edgeY = windowSize / 2;
|
||||
|
||||
int[][] tmpArray = new int[4][256*2];
|
||||
int pixelCount = 0;
|
||||
for(int y=0; y<rows ;y++){
|
||||
setProgress(ZMath.percent(0, rows-1, y));
|
||||
for(int x=0; x<cols ;x++){
|
||||
|
||||
pixelCount = 0;
|
||||
for(int fy=0; fy<windowSize ;fy++){
|
||||
for(int fx=0; fx<windowSize ;fx++){
|
||||
if(y+fy-edgeY >= 0 && y+fy-edgeY < rows && x+fx-edgeX >= 0 && x+fx-edgeX < cols){
|
||||
//colorArray[fx][fy] := pixelvalue[x + fx - edgex][y + fy - edgey]
|
||||
if(channels[0]) tmpArray[0][ getMedianIndex( data[y + fy - edgeY][x + fx - edgeX][0] ) ]++;
|
||||
if(channels[1]) tmpArray[1][ getMedianIndex( data[y + fy - edgeY][x + fx - edgeX][1] ) ]++;
|
||||
if(channels[2]) tmpArray[2][ getMedianIndex( data[y + fy - edgeY][x + fx - edgeX][2] ) ]++;
|
||||
if(channels[3]) tmpArray[3][ getMedianIndex( data[y + fy - edgeY][x + fx - edgeX][3] ) ]++;
|
||||
pixelCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(channels[0])output[y][x][0] = findMedian(tmpArray[0], pixelCount/2);
|
||||
else output[y][x][0] = data[y][x][0];
|
||||
if(channels[1])output[y][x][1] = findMedian(tmpArray[1], pixelCount/2);
|
||||
else output[y][x][1] = data[y][x][1];
|
||||
if(channels[2])output[y][x][2] = findMedian(tmpArray[2], pixelCount/2);
|
||||
else output[y][x][2] = data[y][x][2];
|
||||
if(channels[3])output[y][x][3] = findMedian(tmpArray[3], pixelCount/2);
|
||||
else output[y][x][3] = data[y][x][3];
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private int getMedianIndex(int i){
|
||||
if(i < 0) return Math.abs(i);
|
||||
else return i+256;
|
||||
}
|
||||
|
||||
private int findMedian(int[] median, int medianCount){
|
||||
int sum = 0;
|
||||
int ret = 0;
|
||||
for(int i=0; i<median.length ;i++){
|
||||
sum += median[i];
|
||||
median[i] = 0;
|
||||
if(sum >= medianCount && ret == 0){
|
||||
ret = i-256;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
class SortableARGB implements SortableDataList<Integer>{
|
||||
private int[][][] data;
|
||||
private int cols;
|
||||
private int rows;
|
||||
private int channel;
|
||||
|
||||
public SortableARGB(int[][][] data, int cols, int rows, int channel){
|
||||
this.data = data;
|
||||
this.cols = cols;
|
||||
this.rows = rows;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public int compare(int a, int b) {
|
||||
return compare(a, data[ getY(b) ][ getX(b) ][ channel ]);
|
||||
}
|
||||
|
||||
public int compare(int a, Integer b) {
|
||||
return ((Integer)data[ getY(a) ][ getX(a) ][ channel ]).compareTo(b);
|
||||
}
|
||||
|
||||
public Integer getIndex(int i) {
|
||||
return data[ getY(i) ][ getX(i) ][ channel ];
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return cols * rows;
|
||||
}
|
||||
|
||||
public void swap(int a, int b) {
|
||||
int tmp = data[ getY(a) ][ getX(a) ][ channel ];
|
||||
data[ getY(a) ][ getX(a) ][ channel ] = data[ getY(b) ][ getX(b) ][ channel ];
|
||||
data[ getY(b) ][ getX(b) ][ channel ] = tmp;
|
||||
}
|
||||
|
||||
|
||||
private int getX(int a){
|
||||
return a % cols;
|
||||
}
|
||||
|
||||
private int getY(int a){
|
||||
return a / cols;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
63
src/zutil/image/filters/ResizeImage.java
Normal file
63
src/zutil/image/filters/ResizeImage.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
public class ResizeImage extends ImageFilterProcessor{
|
||||
private int width;
|
||||
private int hight;
|
||||
|
||||
|
||||
/**
|
||||
* Will create a ResizeImage object and fix the hight with the aspect
|
||||
* of the width
|
||||
*
|
||||
* @param img The image to resize
|
||||
* @param w The new width
|
||||
*/
|
||||
public ResizeImage(BufferedImage img, int w){
|
||||
this(img, w, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a ResizeImage object
|
||||
*
|
||||
* @param img The image to resize
|
||||
* @param w The new width if -1 then it will be scaled whit aspect of the hight
|
||||
* @param h The new hight if -1 then it will be scaled whit aspect of the width
|
||||
*/
|
||||
public ResizeImage(BufferedImage img, int w, int h){
|
||||
super(img);
|
||||
width = w;
|
||||
hight = h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(final int[][][] data, int cols, int rows) {
|
||||
if(width < 1){
|
||||
hight = (int)(((double)width/cols)*rows);
|
||||
}
|
||||
else if(hight < 1){
|
||||
width = (int)(((double)hight/rows)*cols);
|
||||
}
|
||||
|
||||
int[][][] tmp = new int[hight][width][4];
|
||||
double xScale = ((double)cols/width);
|
||||
double yScale = ((double)rows/hight);
|
||||
|
||||
for(int y=0; y<width ;y++){
|
||||
setProgress(ZMath.percent(0, width-1, y));
|
||||
for(int x=0; x<hight ;x++){
|
||||
tmp[y][x][0] = data[(int)(y*yScale)][(int)(x*xScale)][0];
|
||||
tmp[y][x][1] = data[(int)(y*yScale)][(int)(x*xScale)][1];
|
||||
tmp[y][x][2] = data[(int)(y*yScale)][(int)(x*xScale)][2];
|
||||
tmp[y][x][3] = data[(int)(y*yScale)][(int)(x*xScale)][3];
|
||||
}
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
}
|
||||
73
src/zutil/image/filters/SpotLightFilter.java
Normal file
73
src/zutil/image/filters/SpotLightFilter.java
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package zutil.image.filters;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import zutil.image.ImageFilterProcessor;
|
||||
import zutil.math.ZMath;
|
||||
|
||||
public class SpotLightFilter extends ImageFilterProcessor{
|
||||
private int radius;
|
||||
private int xPos;
|
||||
private int yPos;
|
||||
|
||||
/**
|
||||
* Sets up a default spotlight effect in
|
||||
* the middle of the image
|
||||
*/
|
||||
public SpotLightFilter(BufferedImage img){
|
||||
this(img, 100, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a custom spotlight
|
||||
* @param r The radius of the spotlight in pixels
|
||||
*/
|
||||
public SpotLightFilter(BufferedImage img, int r){
|
||||
this(img, r, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a custom spotlight
|
||||
* @param r The radius of the spotlight in pixels
|
||||
* @param x The x position of the spotlight, if -1 then it will be centered
|
||||
* @param y The y position of the spotlight, if -1 then it will be centered
|
||||
*/
|
||||
public SpotLightFilter(BufferedImage img, int r, int x, int y){
|
||||
super(img);
|
||||
radius = r;
|
||||
xPos = x;
|
||||
yPos = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[][][] process(final int[][][] data, int cols, int rows) {
|
||||
if(xPos < 0) xPos = cols/2;
|
||||
if(yPos < 0) yPos = rows/2;
|
||||
|
||||
int[][][] output = new int[rows][cols][4];
|
||||
double scale, dx, dy, distance;
|
||||
for(int y=0; y<rows ;y++){
|
||||
setProgress(ZMath.percent(0, rows-1, y));
|
||||
for(int x=0; x<cols ;x++){
|
||||
dx = x-xPos;
|
||||
dy = y-yPos;
|
||||
|
||||
distance = Math.sqrt(dx*dx+dy*dy);
|
||||
|
||||
if(distance > radius){
|
||||
scale = 0;
|
||||
}
|
||||
else{
|
||||
scale = 1-(distance/radius);
|
||||
}
|
||||
|
||||
output[y][x][0] = data[y][x][0];
|
||||
output[y][x][1] = clip((int)(scale * data[y][x][1]));
|
||||
output[y][x][2] = clip((int)(scale * data[y][x][2]));
|
||||
output[y][x][3] = clip((int)(scale * data[y][x][3]));
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue