# ImageJ Kantenerkennung mit interaktivem ImageProcessor



## Slaid (11. Jan 2015)

Hey Leute,

ich habe ein kleines Problem mit meinem PlugIn für ImageJ. 
Ich habe die Aufgabe ein PlugIn mit Java zu programmieren, dass Kanten erkennt. 
Das funktioniert auch soweit. Jedoch habe ich mir die Mühe gemacht ein interaktives Fenster anzulegen, welches bei Veränderungen der Schwellwerte die Kanten anzeigt. 

Nun zu meinem Problem:
Die interaktive Fläche soll die Kanten auf einem Graustufenbild anzeigen. Jedoch verschwindet das Graustufenbild immer und es bleibt ein schwarzer Hintergrund mit Kanten. oder aber, die Kanten werden auf dem Graustufenbild angezeigt, lassen sich aber nicht zurücksetzen. Also wenn jemand eine Idee hat, die interaktive Ansicht zu zu programmieren, dass sich die Kanten auf dem Graustufenbild anzeigen lassen. 

Die Methode für die Berechnung der Kanten heißt berechneBild in Zeile 120 bis 282.
Die ImageProcessoren Zeile 366 bis 380.
Und der ChangeListener für die interaktive Vorschau Zeile 420 bis 446.

Danke euch 

Slaid


```
import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.plugin.filter.PlugInFilter;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;

import java.awt.FlowLayout;
import java.awt.GridLayout;

import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DigVerProjektarbeit implements PlugInFilter {
	public int setup(String arg, ImagePlus imp) {
		if (arg.equals("about")) {
			return DONE;
		}
		return DOES_RGB;
	}

	/**
	 * Diese Methode konvertiert ein RGB in ein graustufen Bild.
	 * @param ipRGB Original RGB Bild
	 * @return in Graustufen konvertiertes Bild.
	 */
	public ImageProcessor konvertiereRGBzuGrau(ImageProcessor ipRGB) {
		int breite = ipRGB.getWidth();
		int hoehe = ipRGB.getHeight();

		ImageProcessor ipGrau = new ColorProcessor(breite, hoehe);
		int[] farbWert = { 0, 0, 0 };

		for (int y = 0; y < hoehe; y++) {
			for (int x = 0; x < breite; x++) {

// Wandelt RGB Bild in ein Graustufen bild um
				ipRGB.getPixel(x, y, farbWert);
				int rot = farbWert[0];
				int gruen = farbWert[1];
				int blau = farbWert[2];
// Wikipedia Grauwert = 0,299 × Rotanteil + 0,587 × Grünanteil + 0,114 × Blauanteil
				int grauWert = (int) (rot * 0.299f + gruen * 0.587f + blau * 0.114f);
				int grauRGB[] = { grauWert, grauWert, grauWert };
				ipGrau.putPixel(x, y, grauRGB);
			}
		}
		return ipGrau;
	}

	/**
	 * Konvertiert den RGB-Wert eines Pixels in einen HSV-Wert.
	 * @param rgb  Farbwert eines Pixels
	 * @return HSV-Wert
	 */
	public double[] konvertierungHSV(int[] rgb) {

		double rot = rgb[0] / 255.0;
		double gruen = rgb[1] / 255.0;
		double blau = rgb[2] / 255.0;
		// R,G,B Farbwerte liegen im Intervall [0,1]

		// Transformation von RGB zu HSV
		double maxRG = Math.max(rot, gruen);
		double max = Math.max(maxRG, blau);// Maximum von R,G,B

		double minRG = Math.min(rot, gruen);
		double min = Math.min(minRG, blau);// Minimum von R,G,B

		// HSV initialisieren
		double hue = 0; // Startwert
		double saturation = 0; // Startwert
		double value = max; // Value = max

		// Formel Umrechnung RGB in HSV bei Vorbedingung von Wikipedia [0,1]
		if (max == min) {
			hue = 0;
		} else if (max == rot) {
			hue = (60 * (0 + (gruen - blau) / (max - min)));
		} else if (max == gruen) {
			hue = (60 * (2 + (blau - rot) / (max - min)));
		} else if (max == blau) {
			hue = (60 * (4 + (rot - gruen) / (max - min)));
		}
		if (hue < 0) {
			hue = hue + 360;
		}
		if (max == 0) {
			saturation = 0;
		} else {
			saturation = (max - min) / (max);
		}
		double[] hsv = { hue, saturation, value }; // Array mit h,s,v füllen
		return hsv;
	}
	/**
	 * Berechnet die Kanten im Bild und gibt diese je nach Auswahl des Nutzers aus. 
	 * Informationen im Infofenster aktualisiert.
	 * 
	 * @param hsvArray
	 * @param ipGrau
	 * @param ipKanten
	 * @param schwellwertH
	 * @param schwellwertV
	 * @param vorschau
	 * @param extraEcken
	 * @param infoFenster
	 * @param farbwertAnzeige
	 * @param hellwertAnzeige
	 * @param vertikalePixelAnzeige
	 * @param horizontalePixelAnzeige
	 * @param eckpunkteAnzeige 
	 */
	public void berechneKante(double[][][] hsvArray, ImageProcessor ipGrau,
			ImageProcessor ipKanten, int schwellwertH, double schwellwertV,
			boolean vorschau, boolean extraEcken, JFrame infoFenster,
			JLabel farbwertAnzeige, JLabel hellwertAnzeige,
			JLabel vertikalePixelAnzeige, JLabel horizontalePixelAnzeige,
			JLabel eckpunkteAnzeige, ImageProcessor ipGrauOhneKanten) {
		 
//		ipGrau =  (ImageProcessor) ipGrauOhneKanten.duplicate();
		//Die Hoehe und Breite des Bildes werden angelegt
		int hoehe = hsvArray.length;
		int breite = hsvArray[0].length;
		// Anlegen der Zaehler
		int eckZaehler = 0;
		int horizontalePixel = 0;
		int vertikalePixel = 0;
		// Durchläuft alle Pixel im Bild
		for (int y = 1; y < hoehe - 3; y++) {
			for (int x = 1; x < breite - 3; x++) {
				
				
				// Pixel werden vom Mainpixel aus bestimmt und angelegt
				double[] hsvMain = hsvArray[y][x];
				double hueMain = hsvMain[0];
				double valueMain = hsvMain[2];

				double[] hsvRechts = hsvArray[y][x + 1];
				double hueRechts = hsvRechts[0];
				double valueRechts = hsvRechts[2];

				double[] hsvRechts2 = hsvArray[y][x + 2];
				double hueRechts2 = hsvRechts2[0];
				double valueRechts2 = hsvRechts2[2];

				double[] hsvUnten = hsvArray[y + 1][x];
				double hueUnten = hsvUnten[0];
				double valueUnten = hsvUnten[2];

				double[] hsvUnten2 = hsvArray[y + 2][x];
				double hueUnten2 = hsvUnten2[0];
				double valueUnten2 = hsvUnten2[2];

				double[] hsvObenRechts = hsvArray[y - 1][x + 1];
				double hueObenRechts = hsvObenRechts[0];
				double valueObenRechts = hsvObenRechts[2];

				double[] hsvUntenRechts = hsvArray[y + 1][x + 1];
				double hueUntenRechts = hsvUntenRechts[0];
				double valueUntenRechts = hsvUntenRechts[2];

				double[] hsvUnten2Rechts = hsvArray[y + 2][x + 1];
				double hueUnten2Rechts = hsvUnten2Rechts[0];
				double valueUnten2Rechts = hsvUnten2Rechts[2];

				double[] hsvUntenRechts2 = hsvArray[y + 1][x + 2];
				double hueUntenRechts2 = hsvUntenRechts2[0];
				double valueUntenRechts2 = hsvUntenRechts2[2];

				double[] hsvOben = hsvArray[y - 1][x];
				double hueOben = hsvOben[0];
				double valueOben = hsvOben[2];

				double[] hsvLinks = hsvArray[y][x - 1];
				double hueLinks = hsvLinks[0];
				double valueLinks = hsvLinks[2];

				double[] hsvUntenLinks = hsvArray[y + 1][x - 1];
				double hueUntenLinks = hsvUntenLinks[0];
				double valueUntenLinks = hsvUntenLinks[2];

				boolean eckeX = false;
				boolean eckeY = false;
//Prüft ob 3 Pixel die Bedingungen erfüllen und zeichnet diese als eine horizontale Kante ein
				if (Math.abs(hueUnten - hueMain) > schwellwertH
						|| Math.abs(valueUnten - valueMain) > schwellwertV) {
					if (Math.abs(hueUntenRechts - hueRechts) > schwellwertH
							|| Math.abs(valueUntenRechts - valueRechts) > schwellwertV) {
						if (Math.abs(hueUntenRechts2 - hueRechts2) > schwellwertH
								|| Math.abs(valueUntenRechts2 - valueRechts2) > schwellwertV) {

							ipGrau.putPixel(x, y, new int[] { 0, 255, 0 });
							ipKanten.putPixel(x, y, new int[] { 0, 255, 0 });
							ipGrau.putPixel(x + 1, y, new int[] { 0, 255, 0 });
							ipKanten.putPixel(x + 1, y, new int[] { 0, 255, 0 });
							ipGrau.putPixel(x + 2, y, new int[] { 0, 255, 0 });
							ipKanten.putPixel(x + 2, y, new int[] { 0, 255, 0 });
							eckeX = true;
							horizontalePixel += 1;
						}
					}
				}
//Prüft ob 3 Pixel die Bedingungen erfüllen und zeichnet diese als eine vertikale Kante ein
				if (Math.abs(hueRechts - hueMain) > schwellwertH
						|| Math.abs(valueRechts - valueMain) > schwellwertV) {
					if (Math.abs(hueUntenRechts - hueUnten) > schwellwertH
							|| Math.abs(valueUntenRechts - valueUnten) > schwellwertV) {
						if (Math.abs(hueUnten2Rechts - hueUnten2) > schwellwertH
								|| Math.abs(valueUnten2Rechts - valueUnten2) > schwellwertV) {

							ipGrau.putPixel(x, y, new int[] { 0, 0, 255 });
							ipKanten.putPixel(x, y, new int[] { 0, 0, 255 });
							ipGrau.putPixel(x, y + 1, new int[] { 0, 0, 255 });
							ipKanten.putPixel(x, y + 1, new int[] { 0, 0, 255 });
							ipGrau.putPixel(x, y + 2, new int[] { 0, 0, 255 });
							ipKanten.putPixel(x, y + 2, new int[] { 0, 0, 255 });
							eckeY = true;
							vertikalePixel += 1;
						}
					}
				}
				
/**
 **-----Extra Eckpunkteerkennung----**
 */
				// Wird abgefragt wenn zuvor das Feld extra Eckpunkte finden angewaehlt wurde
				if (extraEcken == true) {
					//Abfragen für Eckpunktbedingungen
					if ((Math.abs(hueObenRechts - hueOben) > schwellwertH || Math
							.abs(valueObenRechts - valueOben) > schwellwertV)
							&& (Math.abs(hueRechts - hueUntenRechts) > schwellwertH || Math
									.abs(valueRechts - valueUntenRechts) > schwellwertV)) {
						eckeX = true;
						eckeY = true;
					}
					else if ((Math.abs(hueUntenRechts - hueUnten) > schwellwertH || Math
							.abs(valueUntenRechts - valueUnten) > schwellwertV)
							&& (Math.abs(hueMain - hueUnten) > schwellwertH || Math
									.abs(valueMain - valueUnten) > schwellwertV)) {
						eckeX = true;
						eckeY = true;
					}
					else if ((Math.abs(hueRechts - hueUntenRechts) > schwellwertH || Math
							.abs(valueRechts - valueUntenRechts) > schwellwertV)
							&& (Math.abs(hueUntenRechts - hueUnten) > schwellwertH || Math
									.abs(valueUntenRechts - valueUnten) > schwellwertV)) {
						eckeX = true;
						eckeY = true;
					}
					else if ((Math.abs(hueObenRechts - hueOben) > schwellwertH || Math
							.abs(valueObenRechts - valueOben) > schwellwertV)
							&& (Math.abs(hueUntenLinks - hueLinks) > schwellwertH || Math
									.abs(valueUntenLinks - valueLinks) > schwellwertV)) {
						eckeX = true;
						eckeY = true;
					}
				}
				
				// Eckpunkte werden gezeichnet
				if (eckeX == true && eckeY == true) {
					ipGrau.putPixel(x, y, new int[] { 250, 200, 50 });
					ipKanten.putPixel(x, y, new int[] { 250, 200, 50 });
//					ipGrau.putPixel(x + 1, y, new int[] { 250, 200, 50 });
//					ipKanten.putPixel(x + 1, y, new int[] { 250, 200, 50 });
//					ipGrau.putPixel(x - 1, y, new int[] { 250, 200, 50 });
//					ipKanten.putPixel(x - 1, y, new int[] { 250, 200, 50 });
//					ipGrau.putPixel(x, y + 1, new int[] { 250, 200, 50 });
//					ipKanten.putPixel(x, y + 1, new int[] { 250, 200, 50 });
//					ipGrau.putPixel(x, y - 1, new int[] { 250, 200, 50 });
//					ipKanten.putPixel(x, y - 1, new int[] { 250, 200, 50 });
					eckZaehler += 1;
				}
			}
			
		}
		/**
		 * Wenn im Auswahl Menü "ohne Vorschau" ausgewählt wurde
		 * wird "ipGrau und ipKanten" angezeigt
		 */
		if (vorschau == false) { 
			new ImagePlus("Ausgabe: Kantenerkennung mit Graustufenbild", ipGrau).show();
			new ImagePlus("Ausgabe: Kantenerkennung ohne Bild", ipKanten).show();
		}
		//Aktualisiert die Werte des Infofensters
		farbwertAnzeige.setText("Angegebener Farbwert: " + schwellwertH);
		hellwertAnzeige.setText("Angegebener Hellwert: " + schwellwertV);
		vertikalePixelAnzeige.setText("Vertikale Kanten: " + vertikalePixel);
		horizontalePixelAnzeige.setText("Horizontale Kanten: " + horizontalePixel);
		eckpunkteAnzeige.setText("Gefundene Eckpunkte " + eckZaehler);
	}

	/**
	 * Baut das Auswahlmenue auf
	 * und zeigt je nach Auswahl des Nutzers ein weiteres Menue an
	 */
	public void run(ImageProcessor ipRGB) {
		final int breite = ipRGB.getWidth();
		final int hoehe = ipRGB.getHeight();

		// Abfrage wie das Programm gestartet werden soll.
		GenericDialog auswahl = new GenericDialog("Auswahlmenue");
		auswahl.setLayout(new GridLayout(5, 1));

		// RadioButtons mit Gruppe
		JRadioButton kantemV = new JRadioButton("Kantenerkennung mit Vorschau", true);
		kantemV.setToolTipText("Vorsicht bei Bildern ab 3000x2500Pixel");
		final JRadioButton kanteoV = new JRadioButton("Kantenerkennung ohne Vorschau", false);
		JCheckBox extraEckenCheck = new JCheckBox("Extra Eckpunkte", false);

		ButtonGroup gruppeRadio = new ButtonGroup();
		gruppeRadio.add(kantemV);
		gruppeRadio.add(kanteoV);
		auswahl.add(kantemV);
		auswahl.add(kanteoV);
		auswahl.add(extraEckenCheck);
		auswahl.showDialog();
		
		// RGB Array
		int[] rgb = { 0, 0, 0 }; 
		int schwellwertH = 100;
		double schwellwertV = 0.05;

		// Speichert die Farbinformationen im hsvArray
		final double hsvArray[][][] = new double[hoehe][breite][3];
		for (int y = 0; y < hoehe - 1; y++) {
			for (int x = 0; x < breite - 1; x++) {
				hsvArray[y][x] = konvertierungHSV(ipRGB.getPixel(x, y, rgb));
			}
		}
		
		if (auswahl.wasCanceled())// Auswahlmenue wird geschlossen
			return;

		// Extra Ecken werden bei true angezeigt
		final boolean extraEcken;
		if (extraEckenCheck.isSelected() == true) {
			extraEcken = true;
		} else {
			extraEcken = false;
		}

		// Aufbau des Infofensters
		final JFrame infoFenster = new JFrame();
		infoFenster.setLayout(new FlowLayout());
		final JLabel farbwertAnzeige = new JLabel("Angegebener Farbwert: " + schwellwertH);
		infoFenster.add(farbwertAnzeige);
		final JLabel hellwertAnzeige = new JLabel("Angegebener Hellwert: " + schwellwertV);
		infoFenster.add(hellwertAnzeige);
		final JLabel vertikalePixelAnzeige = new JLabel("Gefundene Vertikalekanten: ");
		infoFenster.add(vertikalePixelAnzeige);
		final JLabel horizontalePixelAnzeige = new JLabel("Gefundene Horizontalekanten: ");
		infoFenster.add(horizontalePixelAnzeige);
		final JLabel eckpunkteAnzeige = new JLabel("Gefundene Eckpunkte ");
		infoFenster.add(eckpunkteAnzeige);
		infoFenster.setTitle("Informationen");
		infoFenster.setSize(280, 150);
		infoFenster.setLocation(690, 180);
		
/**
 * Image Prozessoren 
 */
		// Prozessor RGB zu HSV konvertierung
		final ImageProcessor ipGrauOhneKanten = konvertiereRGBzuGrau(ipRGB);
//		final ImageProcessor ipGrau =  konvertiereRGBzuGrau(ipRGB);
		final ImageProcessor ipGrau =  (ImageProcessor) ipGrauOhneKanten.duplicate();
		
		// Prozessor zur Kantendarstellung
		final ImageProcessor ipKanten = new ColorProcessor(breite, hoehe);

		// Interaktiver Slider
		final ImagePlus kantenVorschau = new ImagePlus("Vorschau", ipGrau);
		final ImagePlus kantenVorschau2 = new ImagePlus("Vorschau2", ipGrauOhneKanten);
		
		// Ruft die Kantenerkennung mit Vorschau auf
		if (kantemV.isSelected() == true) {
			infoFenster.setVisible(true);								
			GenericDialog gd = new GenericDialog("Kantenerkennung mit Vorschau");
			gd.setLayout(new GridLayout(3, 2));
			
			/**
			 * Der boolean Parameter "vorschau" verhindert das öffnen der ImageProcessoren, 
			 * beim interagieren mit den Slidern.
			 */ 
			final boolean vorschau = true;
			
			// Aufbau des Fensters mit Slidern und interaktiven Zählern
			final JSlider slider[] = new JSlider[2];
			slider[0] = new JSlider(0, 360, 0);
			slider[1] = new JSlider(0, 100, 0);

			final JLabel anzeigeHue = new JLabel("" + schwellwertH);
			final JLabel anzeigeValue = new JLabel("" + schwellwertV);
			JLabel nameHue = new JLabel("Farbwert");
			JLabel nameValue = new JLabel("Hellwert");

			JPanel panelSliderH = new JPanel();
			panelSliderH.add(nameHue);
			panelSliderH.add(slider[0]);
			panelSliderH.add(anzeigeHue);
			gd.add(panelSliderH);

			JPanel panelSliderV = new JPanel();
			panelSliderV.add(nameValue);
			panelSliderV.add(slider[1]);
			panelSliderV.add(anzeigeValue);
			gd.add(panelSliderV);

			slider[0].setValue(100);
			slider[1].setValue(5);
			kantenVorschau.show();
			kantenVorschau2.show();
			// die Slider werden dem ChangeListener zugewiesen
			for (int i = 0; i <= 1; i++) {
				slider[i].addChangeListener(new ChangeListener() {
					public void stateChanged(ChangeEvent e) {
						
//						ipGrau.fill(); //Setzt das Bild auf schwarz zurück ?? Graustufen funktioniert nicht.						
						int schwellwertH = slider[0].getValue();
						double schwellwertV = (double) slider[1].getValue() / 100;

						// Aufruf der Methode zur Kantenerkennung
						berechneKante(hsvArray, ipGrau, ipKanten, schwellwertH,
								schwellwertV, vorschau, extraEcken,
								infoFenster, farbwertAnzeige, hellwertAnzeige,
								vertikalePixelAnzeige, horizontalePixelAnzeige,
								eckpunkteAnzeige, ipGrauOhneKanten);
						
						// Aktualisierung der Zahlen neben der Slider
						anzeigeHue.setText("" + schwellwertH);
						anzeigeHue.revalidate();
						anzeigeHue.repaint();
						anzeigeValue.setText("" + schwellwertV);
						anzeigeValue.revalidate();
						anzeigeValue.repaint();
						kantenVorschau2.draw();
						kantenVorschau.draw();
					}
				});
			}
			
			gd.showDialog();
			
			// Bei Klick auf Cancel wird die Vorschau, das Menue und Infofenster geschlossen
			if (gd.wasCanceled())
				kantenVorschau.close();
				infoFenster.dispose();
				return;

		} else if (kanteoV.isSelected() == true) {// Ruft die Kantenerkennung ohne Vorschau auf.
			GenericDialog gd = new GenericDialog("Kantenerkennung ohne Vorschau");

			/**
			 * Wenn kein Fehler bei der Eingabe gefunden wurde, dann wird
			 * "boolean weiter" auf true gesetzt und die while Schleife
			 * unterbrochen.
			 */
			boolean weiter = false;
			while (weiter == false) {
				boolean vorschau = false;

				gd.addSlider("Schwellwert des Farbtons", 0, 360, 100);
				gd.addSlider("Schwellwert des Hellwerts", 0, 1, 0.05);

				schwellwertH = (int) gd.getNextNumber();
				schwellwertV = (double) gd.getNextNumber();
				gd.showDialog();
				// Bei Fehlerhafter Eingabe wird der GenericDialog Fehler
				// aufgerufen.
				GenericDialog fehler = new GenericDialog("Fehler");
				fehler.setLayout(new GridLayout(2, 1));
				fehler.hideCancelButton();
				JLabel fehlerSchwellwert = new JLabel("");
				fehler.add(fehlerSchwellwert);
				fehler.addHelp("http://de.wikipedia.org/wiki/HSV-Farbraum");

				// Abfrage welche Art von Fehler aufgetreten ist
				if ((schwellwertH < 0 || schwellwertH > 360)
						&& (schwellwertV < 0 || schwellwertV > 1)) {
					fehlerSchwellwert.setText("Bitte auf die Grenzbereiche der Schwellwerte achten!");
					fehler.showDialog();
				} else if (schwellwertH < 0 || schwellwertH > 360) {
					fehlerSchwellwert.setText("Beim Farbton auf den Grenzbereich des Schwellwertes achten!");
					fehler.showDialog();
				} else if (schwellwertV < 0 || schwellwertV > 1) {
					fehlerSchwellwert.setText("Beim Hellwert auf den Grenzbereich des Schwellwertes achten!");
					fehler.showDialog();
				// Wenn kein Fehler vorhanden ist wird die while Schleife abgebrochen
				} else {
					weiter = true;
					berechneKante(hsvArray, ipGrau, ipKanten, schwellwertH,
							schwellwertV, vorschau, extraEcken,
							infoFenster, farbwertAnzeige, hellwertAnzeige,
							vertikalePixelAnzeige, horizontalePixelAnzeige,
							eckpunkteAnzeige, ipGrauOhneKanten);
					infoFenster.setVisible(true);
				}
				// Bei Klick auf Cancel wird das Menue und Infofenster geschlossen
				if (gd.wasCanceled())
					infoFenster.dispose();
				return;
			}
		}
	}
}
```


----------

