Skip to content

Instantly share code, notes, and snippets.

@kiliankoe
Created April 28, 2014 12:00
Show Gist options
  • Save kiliankoe/11369756 to your computer and use it in GitHub Desktop.
Save kiliankoe/11369756 to your computer and use it in GitHub Desktop.
Aufgabe 1: Bildverarbeitung Medien und Medienströme @ TU Dresden SS14
package de.tudresden.inf.mms;
import java.awt.Image;
import java.awt.image.BufferedImage;
/**
* @author Kilian Koeltzsch, 3848487
*
*/
@SuppressWarnings("JavadocReference")
public class ImageEditor {
/**
* Originalbild (Ausgangspunkt fuer Berechnungen)
*/
private BufferedImage image;
/**
* Temporaere Kopie bzw. Ergebnisbild
*/
private BufferedImage tmpImg;
public ImageEditor(BufferedImage image) {
super();
this.image = image;
tmpImg = new BufferedImage(image.getWidth(), image.getHeight(),
image.getType());
}
/**
* Konvertiert das vorgegebene RGB-Bild ({@link ImageEditor#image}) in ein
* Graustufenbild.
*
* @return {@link java.awt.image.BufferedImage}
*/
public Image grayscale() {
int gray;
int[] rgb;
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
gray = (int) Math.floor((rgb[0] + rgb[1] + rgb[2]) / 3);
rgb[0] = gray;
rgb[1] = gray;
rgb[2] = gray;
tmpImg.setRGB(x, y, ImageHelper.toIntRGB(rgb));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.1.1
* @return {@link java.awt.image.BufferedImage}
*/
public Image invert() {
// Jeden Farbwert durch Subtraktion von 255 invertieren.
int[] rgb;
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
rgb[0] = 255 - rgb[0];
rgb[1] = 255 - rgb[1];
rgb[2] = 255 - rgb[2];
tmpImg.setRGB(x, y, ImageHelper.toIntRGB(rgb));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.1.2
* @return {@link java.awt.image.BufferedImage}
*/
public Image rotate() {
// Pixel von oben links lesen und unten links schreiben.
// Bild wird so verkehrt herum wieder aufgebaut.
int[] pixel;
int new_x;
int new_y;
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
pixel = ImageHelper.toRGBArray(image.getRGB(x, y));
new_x = image.getWidth() - (x + 1);
new_y = image.getHeight() - (y + 1);
tmpImg.setRGB(new_x, new_y, ImageHelper.toIntRGB(pixel));
}
}
return tmpImg;
}
// konvertiere rgb matrix nach ycbcr
// relevant fuer Aufgabe 1.1.3 und 1.1.2
private int[] convertToYCBCR(int[] rgb) {
int[] ycbcr = new int[3];
ycbcr[0] = (int) Math.floor((0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]));
ycbcr[1] = (int) Math.floor(128 - (0.168736 * rgb[0]) - (0.331264 * rgb[1]) + (0.5 * rgb[2]));
ycbcr[2] = (int) Math.floor(128 + (0.5 * rgb[0]) - (0.418688 * rgb[1]) - (0.081312 * rgb[2]));
return ycbcr;
}
// konvertiere ycbcr matrix nach rgb
// relevant fuer Aufgabe 1.1.3 und 1.1.2
private int[] convertToRGB(int[] ycbcr) {
int[] rgb = new int[3];
rgb[0] = (int) Math.floor(ycbcr[0] + 1.402 * (ycbcr[2] - 128));
rgb[1] = (int) Math.floor(ycbcr[0] - 0.34414 * (ycbcr[1] - 128) - 0.71414 * (ycbcr[2] - 128));
rgb[2] = (int) Math.floor(ycbcr[0] + 1.772 * (ycbcr[1] - 128));
return rgb;
}
/**
* @see Aufgabe 1.1.3
* @return {@link java.awt.image.BufferedImage}
*/
public Image linHist() {
int rgb[];
int ycbcr[] = new int[3];
double ywerte[] = new double[256];
double sumpercent;
int amountpix = image.getHeight() * image.getWidth();
// Berechne fuer jeden Pixel den Y (Helligkeit) Wert und summiere
// das Auftreten dieser Helligkeitswerte in einem Array (ywerte)
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
// aus Performancegruenden hier nur der Y Wert und nicht mit convertToYCBCR()
ycbcr[0] = (int) Math.floor((0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]));
ywerte[ycbcr[0]]++;
}
}
// Wandle Werte in ywerte in prozentuale Anteile um
for (int i = 0; i < ywerte.length; i++) {
ywerte[i] = ywerte[i] / amountpix;
}
// Durchlaufe erneut alle Pixel
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
// Berechne Y, Cb und Cr Werte
ycbcr = convertToYCBCR(rgb);
// Summiere die Auftrittswahrscheinlichkeiten aller Pixel mit einem
// niedrigerem Y Wert
sumpercent = 0;
for (int i = 0; i < ycbcr[0]; i++) {
sumpercent += ywerte[i];
}
// Berechne den neuen Y Wert fuer diesen Pixel
ycbcr[0] = (int) Math.floor(255 * sumpercent);
// Wandle Y, Cb und Cr wieder in RGB Werte um
rgb = convertToRGB(ycbcr);
tmpImg.setRGB(x, y, ImageHelper.toIntRGB(rgb));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.2.1
* @return {@link java.awt.image.BufferedImage}
*/
public Image subsampling() {
// Schreibe die rgb Daten eines jeden Pixels in
// sich selbst und die relevanten Nachbarn.
// Das Ergebnis koennte man als eine Version des Bildes
// mit Pixeln der doppelten Groesse in beide Richtungen
// beschreiben.
int[] rgb;
for (int x = 0; x < image.getWidth(); x += 2) {
for (int y = 0; y < image.getHeight(); y += 2) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
tmpImg.setRGB(x ,y ,ImageHelper.toIntRGB(rgb));
tmpImg.setRGB(x+1,y ,ImageHelper.toIntRGB(rgb));
tmpImg.setRGB(x ,y+1,ImageHelper.toIntRGB(rgb));
tmpImg.setRGB(x+1,y+1,ImageHelper.toIntRGB(rgb));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.2.2
* @return {@link java.awt.image.BufferedImage}
*/
public Image colorSubsampling() {
// Dieser Algorithmus scheint sein Werk ziemlich gut zu erledigen...
// Unterschiede zwischen Original und Output sind kaum zu erkennen,
// bei genauerer Betrachtung (mit ImageMagick's compare -> http://i.imgur.com/UDXp6gO.png)
// jedoch ersichtlich.
// pixel reihenfolge im array
// self, ost, suedost, sued
int[][] rgb = new int[4][3];
int[][] ycbcr = new int[4][3];
int avcb;
int avcr;
for (int x = 0; x < image.getWidth() - 1; x++) {
for (int y = 0; y < image.getHeight() - 1; y++) {
// hole relevante pixel
rgb[0] = ImageHelper.toRGBArray(image.getRGB(x ,y ));
rgb[1] = ImageHelper.toRGBArray(image.getRGB(x+1,y ));
rgb[2] = ImageHelper.toRGBArray(image.getRGB(x+1,y+1));
rgb[3] = ImageHelper.toRGBArray(image.getRGB(x ,y+1));
// konvertiere alle ins ycbcr modell
for (int i = 0; i < 4; i++) {
ycbcr[i] = convertToYCBCR(rgb[i]);
}
// berechne mittelwerte fuer cb und cr
avcb = 0;
avcr = 0;
for (int i = 0; i < 4; i++) {
avcb += ycbcr[i][1];
avcr += ycbcr[i][2];
}
avcb /= 4;
avcr /= 4;
// schreibe mittelwerte zurueck nach ycbcr
for (int i = 0; i < 4; i++) {
ycbcr[i][1] = avcb;
ycbcr[i][2] = avcr;
}
// konvertiere alle zurueck zu rgb
for (int i = 0; i < 4; i++) {
rgb[i] = convertToRGB(ycbcr[i]);
}
// schreibe ins tmpImg
tmpImg.setRGB(x ,y ,ImageHelper.toIntRGB(rgb[0]));
tmpImg.setRGB(x+1,y ,ImageHelper.toIntRGB(rgb[1]));
tmpImg.setRGB(x+1,y+1,ImageHelper.toIntRGB(rgb[2]));
tmpImg.setRGB(x ,y+1,ImageHelper.toIntRGB(rgb[3]));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.3.1
* @return {@link java.awt.image.BufferedImage}
*/
public Image colorQuant() {
// Dank der gegebenen Methode getColorFromPalette kann die naechste
// Palettfarbe einfach geholt und in das tmpImage geschrieben werden.
int[] rgb;
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
rgb = ImageHelper.getColorFromPalette(rgb);
tmpImg.setRGB(x, y, ImageHelper.toIntRGB(rgb));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.3.2
* @return {@link java.awt.image.BufferedImage}
*/
public Image orderedDithering() {
// Bevor die Farbe aus der Palette geholt wird, wird erst noch der gegebene
// Fehlerwert aus der Bayer Matrix auf die rgb Werte des aktuellen Pixels addiert.
int[] rgb;
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
rgb = ImageHelper.toRGBArray(image.getRGB(x, y));
rgb[0] += ImageHelper.BAYER8x8[x % 8][y % 8];
rgb[1] += ImageHelper.BAYER8x8[x % 8][y % 8];
rgb[2] += ImageHelper.BAYER8x8[x % 8][y % 8];
rgb = ImageHelper.getColorFromPalette(rgb);
tmpImg.setRGB(x, y, ImageHelper.toIntRGB(rgb));
}
}
return tmpImg;
}
/**
* @see Aufgabe 1.3.3
* @return {@link java.awt.image.BufferedImage}
*/
public Image floydSteinbergDithering() {
// Berechne fuer jeden Pixel (solang er in den passenden Boundaries liegt)
// einen Fehlerwert zwischen Original- und passender Farbe aus der Palette.
// Dieser Fehler wird dann je nach Position mit dem passendem Wert aus
// der Floyd-Steinberg-Matrix zusammenaddiert.
// da in diesem Algorithmus mit nur einem Bild gearbeitet wird, ist
// dies notwendig, damit nach dem Ausfuehren von Floyd-Steinberg
// auch die anderen Algorithmen noch funktionieren.
tmpImg.setData(image.getData());
int[] oldpixel;
int[] newpixel;
int[] error = new int[3];
int[] rgb;
for (int x = 0; x < tmpImg.getWidth(); x++) {
for (int y = 0; y < tmpImg.getHeight(); y++) {
oldpixel = ImageHelper.toRGBArray(tmpImg.getRGB(x, y));
newpixel = ImageHelper.getColorFromPalette(oldpixel);
tmpImg.setRGB(x, y, ImageHelper.toIntRGB(newpixel));
// Berechne fuer jeden Pixel den Fehler aus dem Unterschied
// zwischen dem Pixel selbst und seiner "Palette-Version"
// Dieser Fehler wird dann auf die entsprechenden Pixel
// in der Umgebung multipliziert, bevor diese einzeln
// betrachtet werden.
// Die einzelnen if Bedingungen existieren um die Randfaelle
// abzudecken, die Logik fuer die einzelnen Bloecke bleibt
// identisch.
if (x < tmpImg.getWidth() - 1) {
for (int i = 0; i < 3; i++) {
error[i] = oldpixel[i] - newpixel[i];
rgb = ImageHelper.toRGBArray(tmpImg.getRGB(x + 1, y));
rgb[i] += error[i] * 7 / 16;
tmpImg.setRGB(x + 1, y, ImageHelper.toIntRGB(rgb));
}
}
if (x > 0 && y < tmpImg.getHeight() - 1) {
for (int i = 0; i < 3; i++) {
error[i] = oldpixel[i] - newpixel[i];
rgb = ImageHelper.toRGBArray(tmpImg.getRGB(x - 1, y + 1));
rgb[i] += error[i] * 3 / 16;
tmpImg.setRGB(x - 1, y + 1, ImageHelper.toIntRGB(rgb));
}
}
if (y < tmpImg.getHeight() - 1) {
for (int i = 0; i < 3; i++) {
error[i] = oldpixel[i] - newpixel[i];
rgb = ImageHelper.toRGBArray(tmpImg.getRGB(x, y + 1));
rgb[i] += error[i] * 5 / 16;
tmpImg.setRGB(x, y + 1, ImageHelper.toIntRGB(rgb));
}
}
if (x < tmpImg.getWidth() - 1 && y < tmpImg.getHeight() - 1) {
for (int i = 0; i < 3; i++) {
error[i] = oldpixel[i] - newpixel[i];
rgb = ImageHelper.toRGBArray(tmpImg.getRGB(x + 1, y + 1));
rgb[i] += error[i] / 16;
tmpImg.setRGB(x + 1, y + 1, ImageHelper.toIntRGB(rgb));
}
}
}
}
return tmpImg;
}
// Vergleich der drei Farbreduktionsverfahren
//
// Waehrend colorQuant das Bild wirklich nur in eine Palette aus 16 Farben
// zwaengt und so einheitliche Farbflaechen erzeugt in denen jedoch Details
// des Bildes verloren gehen, verteilen die anderen beiden Algorithmen die
// Farben so, dass gewisse Details erhalten bleiben.
// Im Falle des Ordered Ditherings werden die Farbwerte eines Pixels mit
// einer festen Fehlermatrix multipliziert bevor man sie mit der Palette
// abgleicht. Durch diese Werte ergibt sich eine bessere Verteilung.
// Fuer Floyd-Steinberg wird dieser Fehler dynamisch berechnet um eine noch
// bessere Verteilung zu generieren.
//
// Fuer eine Palette von nur 16 Farben erzeugt Floyd-Steinberg ein
// beeindruckendes Ergebnis. Details bleiben trotz geringer Palette erhalten.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment