# Fehlersuche beim Weichzeichner-Algorithmus



## LMnd (23. Jun 2009)

Hallo zusammen!

EDIT: ACHTUNG: mittlerweile hab ich herausgefunden, dass es nicht an der Größe liegt, sondern am Verhältnis Höhe / Breite. Ist es nicht gleich, dann treten die Verschiebungen auf. Leider weiss ich nicht warum. So, weiter im Originaltext:

Ich habe einen Weichzeichner geschrieben, und ihn auf kleinen Bildern (512*512) getestet. Schien zu funktionieren:






auf größeren Bildern hab ich aber das Problem, dass er bestimmte Bereiche mit falschen Werten überlagert (man beachte die Desktopsymbole am folgendem Bild):

große Bilder, desshalb als Link:
http://img.photobucket.com/albums/v484/Thomas87/out_breit_3.jpg
http://img.photobucket.com/albums/v484/Thomas87/out_breit_5.jpg

Die beiden großen Bilder sind entsprechend ihrem Namen mit strenght = 3 bzw 5 gemacht worden.

Ich bin nicht Programmiersprachen - bewandert genug um rauszufinden, woran es liegt, sitze jetzt schon seit mehreren Stunden dran, hab diverse Sachen probiert, aber nichts will gelingen.

Hier der Code:


```
/*
 *  momentan wird noch auf dem Originalbild gearbeitet,
 *  dh der Filter beeinflusst sich selber im Bilddurchlauf.
 *  sinnvolle strenght Werte zum testen: ungerade Zahlen zwischen 3 und 11
 *  
 */


public class Weichzeichner extends Filter {
	int hmax = BildLae; //Bildhöhe
	int wmax = BildBre; //BildBreite
	
	public Weichzeichner(Image im2){
		super(im2);
	}
	
	//prüft, ob der Punkt mit den Koordinaten h,w im Bild ist
	public boolean imBild (int h, int w){
		boolean bool=false;
		if (h>=0 && h<hmax && w>=0 && w<wmax) bool = true;
		return bool;
	}
	
	public void weichzeichnen(int strenght){

		//berechnet für die ungerade strenght Zahl die Mitte also zb 4 für 7
		int mitte = (strenght - 1)/2; 
		//sum0 = Summe rot, sum1 = Summe Grün, sum2 = Summe Blau
		int sum0, sum1, sum2;
		//abstand 1: Abstand in Spalten vom Mittelpunkt mitte,mitte
		//abstand 2: Abstand in Zeilen
		int abstand1=0, abstand2=0;
		//abstand = 1 + Summe der Abstände ... also gesammt-abstand
		double abstand;
		double z; //zähler
		
		//zeilen, spalten durchlaufen
		for (int h=0; h<hmax; h++){
			for (int w=0; w<wmax; w++){
				
				//z und sum0,sum1,sum2 bei jedem Durchlauf zurücksetzen
				z = 0;
				sum0 = sum1 = sum2 = 0;
				
				//die strenght * strenght "Matrix" durchlaufen
				for (int s1=1; s1<=strenght; s1++){
					for (int s2=1; s2<=strenght; s2++){
						
						//wenn Punkt im Bild, int "abstand" berechnen
						if (imBild(h+mitte-s1,w+mitte-s2) == true){
							if (s1<mitte) abstand1 = mitte-s1;
							if (s1>mitte) abstand1 = s1-mitte;
							if (s1==mitte) abstand1 = 0;
							if (s2<mitte) abstand2 = mitte-s2;
							if (s2>mitte) abstand2 = s2-mitte;
							if (s2==mitte) abstand2 = 0;
							abstand = abstand1 + abstand2 +1;
							
							//zu z addieren, damit wir später wieder teilen können
							//z ist nach Schleifenbeendigung die Summe der 1/Abstände
							z=z + (1 / abstand);
							
							//die RGB Werte entsprechend der Gewichtung 1/abstand addieren
							sum0 = (int)(sum0 + im.getPixel(h+mitte-s1,w+mitte-s2,0)/abstand);
							sum1 = (int)(sum1 + im.getPixel(h+mitte-s1,w+mitte-s2,1)/abstand);
							sum2 = (int)(sum2 + im.getPixel(h+mitte-s1,w+mitte-s2,2)/abstand);
						}
					}
				}
				//den Pixel auf die oben berechnete, und entsprechend der
				//Abstände korrigierte, Summe setzen
				im.setPixel(h, w, 0, (short)(sum0/z));
				im.setPixel(h, w, 1, (short)(sum1/z));
				im.setPixel(h, w, 2, (short)(sum2/z));
			}
		}
	}
	
}
```

Es läuft also irgendwas mit den Variablen schief, allerdings weiss ich nicht, an welchen Variablen es scheitert, und weiss auch nicht, wie ich das rausfinden soll.
Wäre über jede Hilfe dankbar 

MfG,
LMnd


----------



## SlaterB (23. Jun 2009)

klasse wäre noch ne main-Methode mit dem Einlesen und Abspeichern des Bildes, sonst muss man sich das immer selber zusammensammeln


----------



## LMnd (23. Jun 2009)

SlaterB hat gesagt.:


> klasse wäre noch ne main-Methode mit dem Einlesen und Abspeichern des Bildes, sonst muss man sich das immer selber zusammensammeln




die hab ich nicht geschrieben, ich frage mal grade um erlaubnis die zu veröffentlichen 

edit: du hast ne pm bekommen

wer helfen möchte und das pw will: pm an mich
RapidShare: Easy Filehosting

oder

Forum.zip ... at uploaded.to


----------



## LMnd (23. Jun 2009)

neuer Post, weil wichtig:

Es hängt anscheinend vom Verhältnis Höhe/Breite ab.

Das kleine Bild oben (Lenna) ist nämlich 512 x 512

Der Desktop 1920 x 1200.


Hab beide Bilder mal gecroppt und es klappt auf 1200 x 1200, aber zB nicht auf 512x360.

Nur wieso? Seh da grade garkeinen Zusammenhang im Code.


----------



## Soulfly (23. Jun 2009)

Das ist kein typischer Weichzeichner, den du da implementiert hast. Ich mach mir jetzt keine Gedanke um dein Problem, da es nicht dem Standard entspricht. 

Investiere lieber mal in die Suche nach: Filterkernel, lineare Filter, Bildmanipulation etc.


----------



## SlaterB (23. Jun 2009)

deine Image-Klasse hat mich einen Haufen Nerven gekostet,

int index = 54 + clrTableSize * 4 + ((width - 1) - setWidth) * height* 3 + 
                    3 * setHeight + setRgb;

bzw. kannst dein alignment wieder einbauen,
aber width und heigth waren bei getPixel und setPixel bei der Haupt-Index-Berechung für das Array vertauscht,
bei quadratischen Bildern fiel es verständlicherweise nicht auf

edit:
> und weiss auch nicht, wie ich das rausfinden soll.

der Grundgedanke hätte sein müssen:
es werden nicht direkt benachbarte Punkte verschmolzen, sondern versetzte Punkte aus einer Zeile


----------



## LMnd (23. Jun 2009)

edit:
Es werden sehr wohl die benachbarten Pixel überprüft, nämlich die, die innerhalb einer "strength x strength Matrix" liegen, welche mit dem Mittelpunkt auf dem gerade weichzuzeichnendem Pixel liegt (darum auch strenght ungerade!).

(also gibt es (strength^2)-1 Nachbarpixel, welche gewertet an ihrem Abstand in die Berechnung einfließen.

BSP für strength = 3:
1/3,1/2,1/3
1/2,1/1,1/2
1/3,1/2,1/3

(Wobei der mittlere Pixel mein weichzuzeichnender Pixel ist. Das Ergebnis wird dann natürlich noch im Fall strength = 3 durch 13/3 geteilt.)

Trotzdem nochmal vielen Dank, es klappt jetzt.
Zwischenzeitlich wurden auch einige Änderungen an der ImageIO vorgenommen, allerdings nicht in Bezug auf das angebliche Width / Height Problem. Jaja, böses SVN... 










Das in der ImageIO ist auch nicht falschrum (was unsere Methoden und Klassen Elliptisch ausblenden, Grauwert- / Sepiafilter, Zufallsrauschen, Pixelrauschen, Drehen, Spiegeln, Morphen, Helligkeit und Kontrast, die wie vorgesehen  funktionieren, bestätigen).



Vielen Dank trotzdem, so klappt es jetzt :




```
public class Weichzeichner extends Filter {
	int hmax;
	int wmax;
	short[][][] neu;
	
	public Weichzeichner(Image im2){
		super(im2);
		hmax = BildLae;
		wmax = BildBre;
		neu = new short[hmax][wmax][3];
	}
	
	//prüft, ob der Punkt mit den Koordinaten h,w im Bild ist
	public boolean imBild (int h, int w){
		return (h>=0 && h<hmax && w>=0 && w<wmax);
	}
	
	public void weichzeichnen(int strenght){

		//berechnet für die ungerade strenght Zahl die Mitte also zb 4 für 7
		int mitte = (strenght - 1)/2;
		//sum0 = Summe rot, sum1 = Summe Grün, sum2 = Summe Blau
		int sum0, sum1, sum2;
		//abstand 1: Abstand in Spalten vom Mittelpunkt mitte,mitte
		//abstand 2: Abstand in Zeilen
		int abstand1=0, abstand2=0;
		//abstand = 1 + Summe der Abstände / gesammt-abstand
		double abstand;
		double z; //zähler
		
		//zeilen, spalten durchlaufen
		for (int h=0; h<hmax; h++){
			for (int w=0; w<wmax; w++){
				
				//z und sum0,sum1,sum2 bei jedem Durchlauf zurücksetzen
				z = 0;
				sum0 = sum1 = sum2 = 0;
				
				//die strenght * strenght "Matrix" durchlaufen
				for (int s1=1; s1<=strenght; s1++){
					for (int s2=1; s2<=strenght; s2++){
						
						//wenn Punkt im Bild, int "abstand" berechnen
						if (imBild(h+mitte-s1,w+mitte-s2)){
							abstand1=Math.abs(mitte-s1);
							abstand2=Math.abs(mitte-s2);
							abstand = abstand1 + abstand2 +1;
							
							//zu z addieren, damit wir später wieder teilen können
							//z ist nach Schleifenbeendigung die Summe der 1/Abstände
							z=z + (1 / abstand);
							
							//die RGB Werte entsprechend der Gewichtung 1/abstand addieren
							sum0 = (int)(sum0 + im.getPixel(h+mitte-s1,w+mitte-s2,0)/abstand);
							sum1 = (int)(sum1 + im.getPixel(h+mitte-s1,w+mitte-s2,1)/abstand);
							sum2 = (int)(sum2 + im.getPixel(h+mitte-s1,w+mitte-s2,2)/abstand);
						}
					}
				}

				//das Array auf die oben berechneten, und entsprechend der
				//Abstände korrigierten, Summen setzen
				
				neu[h][w][0] = (short)(sum0/z);
				neu[h][w][1] = (short)(sum1/z);
				neu[h][w][2] = (short)(sum2/z);
			}
		}
		
		//array ins Bild schreiben
		for (int h=0; h<hmax; h++){
			for (int w=0; w<wmax; w++){
				im.setPixel(h, w, 0, (short)(neu[h][w][0]));
				im.setPixel(h, w, 1, (short)(neu[h][w][1]));
				im.setPixel(h, w, 2, (short)(neu[h][w][2]));
			}
		}
	}
}
```


----------



## LMnd (25. Jun 2009)

es verbarg sich noch ein kleiner fehler in der Zeile:


```
int mitte = (strenght - 1)/2;
```

Es muss natürlich


```
int mitte = (strenght + 1)/2;
```

sein.

Ausserdem gab es noch weiter kleine Verbesserungen (beeinflusst sich jetzt zB beim Durchlaufen nicht mehr selber), hier der aktuelle Code:


```
public class Weichzeichner extends Filter {
	int hmax;
	int wmax;
	short[][][] neu;

	public Weichzeichner(Image im2) {
		super(im2);
		hmax = BildLae;
		wmax = BildBre;
		neu = new short[hmax][wmax][3];
	}

	// prüft, ob der Punkt mit den Koordinaten h,w im Bild ist
	public boolean imBild(int h, int w) {
		return (h >= 0 && h < hmax && w >= 0 && w < wmax);
	}

	public void weichzeichnen(int strenght) {

		// berechnet für die ungerade strenght Zahl die Mitte also zb 4 für 7
		int mitte = (strenght + 1) / 2;
		// sum0 = Summe rot, sum1 = Summe Grün, sum2 = Summe Blau
		double sum0, sum1, sum2;

		// abstand = 1 + Summe der Abstände / gesammt-abstand
		double abstand;
		double z; // zähler
		int temp1, temp2;

		// zeilen, spalten durchlaufen
		for (int h = 0; h < hmax; h++) {
			for (int w = 0; w < wmax; w++) {

				// z und sum0,sum1,sum2 bei jedem Durchlauf zurücksetzen
				z = sum0 = sum1 = sum2 = 0;

				// die strenght * strenght "Matrix" durchlaufen
				for (int s1 = 0; s1 < strenght; s1++)
					for (int s2 = 0; s2 < strenght; s2++) {
						temp1 = mitte - s1;
						temp2 = mitte - s2;

						// wenn Punkt im Bild, int "abstand" berechnen
						if (imBild(h + temp1, w + temp2)) {
							abstand = Math.abs(temp1) + Math.abs(temp2) + 1;

							// zu z addieren, damit wir später wieder teilen
							// können
							// z ist nach Schleifenbeendigung die Summe der
							// 1/Abstände
							z = z + (1 / abstand);

							// die RGB Werte entsprechend der Gewichtung
							// 1/abstand addieren
							sum0 += im.getPixel(h + temp1, w + temp2, 0)
									/ abstand;
							sum1 += im.getPixel(h + temp1, w + temp2, 1)
									/ abstand;
							sum2 += im.getPixel(h + temp1, w + temp2, 2)
									/ abstand;
						}

						// das Array auf die oben berechneten, und entsprechend
						// der
						// Abstände korrigierten, Summen setzen

						neu[h][w][0] = (short) (sum0 / z);
						neu[h][w][1] = (short) (sum1 / z);
						neu[h][w][2] = (short) (sum2 / z);
					}
			}
		}

		// array ins Bild schreiben
		for (int h = 0; h < hmax; h++)
			for (int w = 0; w < wmax; w++) {
				im.setPixel(h, w, 0, (short) (neu[h][w][0]));
				im.setPixel(h, w, 1, (short) (neu[h][w][1]));
				im.setPixel(h, w, 2, (short) (neu[h][w][2]));
			}
	}
}
```

Das einzige Problem: Wen ich als Filtergröße eine 51 x 51 Matrix möchte, dann rechnet er doch schon recht lange (aufm Notebook bei 512x512 Originalbild dauert es eine knappe Minute).
Werde ihn also noch weiter optimieren.

Oder ist das normal? Ich meine er führt ja ca 9 * (51^2) * (512^2) Berechnungen aus ( = 6,136,528,896).


----------



## EgonOlsen (25. Jun 2009)

LMnd hat gesagt.:


> Das einzige Problem: Wen ich als Filtergröße eine 51 x 51 Matrix möchte, dann rechnet er doch schon recht lange (aufm Notebook bei 512x512 Originalbild dauert es eine knappe Minute).
> Werde ihn also noch weiter optimieren.
> 
> Oder ist das normal? Ich meine er führt ja ca 9 * (51^2) * (512^2) Berechnungen aus ( = 6,136,528,896).


Sieh zu, dass du die getPixel()-Aufrufe los wirst und ersetze das durch direkte Zugriffe auf das Pixelarray des Bildes.


----------



## SlaterB (25. Jun 2009)

sind es quasi schon, Image ist ne eigene Klasse,

der Index für das Array wird etwas aufwendig berechnet, a la
> int index = 54 + clrTableSize * 4 + ((width - 1) - setWidth) * height* 3 + 3 * setHeight + setRgb;
aber das dürfte keine Minute ausmachen


----------

