Richard: Sobel-Operator

Hallo,

ich implementiere gerade ein paar Bildbearbeitungsalgorithmen in Java (wobei die Sprache für das Problem irrelevant ist) und hänge gerade etwas beim Sobel-Operator. Ich hatte den vor einiger Zeit schonmal erfolgreich in C# umgesetzt, doch leider habe ich den Code nicht aufgehoben.

Die Ergebnisbilder sehen im Prinzip richtig aus, aber bei genauerem Hinsehen merkt man (je nach Bild), das irgendetwas nicht so ganz stimmt. Einige Bilder zeigen starkes Rauschen, das ich mir nicht erklären kann, bei anderen wiederum ist das Ergebnis eigentlich perfekt.

Zur Verdeutlichung mal ein besonders schlimmes Beispiel:

Original und Ergebnis der Faltung

Hier allerdings ein Bild, das mit exakt demselben Code bearbeitet wurde, aber im Ergebnis wesentlich besser ist:

Original und Ergebnis der Faltung

Und hier der relevante Code:

  
public int[][] getYKernel() {  
    return new int[][]  
        {  
	    {1, 0, -1},  
	    {2, 0, -2},  
            {1, 0, -1}  
        };  
}  
  
public int[][] getXKernel() {  
    return new int[][]  
        {  
            {1, 2, 1},  
	    {0, 0, 0},  
	    {-1, -2, -1}  
	};  
}  
  
public final void applyOperation() {  
	final int[][] Sx = getXKernel();  
	final int[][] Sy = getYKernel();  
  
	int gx = 0, gy = 0, g = 0;  
  
	for(int y = 0; y < source.getHeight(); y++) {  
	    for(int x = 0; x < source.getWidth(); x++) {  
  
		// Farbwerte des letzten Pixels zurück auf 0 setzen  
		gx = 0; gy = 0; g = 0;  
  
		// Sx traversieren  
		for(int ySx = -1; ySx <= 1; ySx++) {  
		    for(int xSx = -1; xSx <= 1; xSx++) {  
  
			// Grenzen des Bildes prüfen  
			if(y + ySx >= 0 && y + ySx < source.getHeight() &&  
				x + xSx >= 0 && x + xSx < source.getWidth()) {  
			    /* Pixel existiert  
			     * Neuer Pixelwert ergibt sich aus der Summe der Produkte  
			     * der jeweils korrespondierenden Pixel aus Bild und Matrix  
			     */  
			    gx += source.getSample(0, x + xSx, y + ySx) *  
			    	Sx[xSx + 1][ySx + 1];  
			}  
  
		    }  
		}  
  
		// Sy traversieren  
		for(int ySy = -1; ySy <= 1; ySy++) {  
		    for(int xSy = -1; xSy <= 1; xSy++) {  
  
			// Grenzen des Bildes prüfen  
			if(y + ySy >= 0 && y + ySy < source.getHeight() &&  
				x + xSy >= 0 && x + xSy < source.getWidth()) {  
			    /* Pixel existiert  
			     * Neuer Pixelwert ergibt sich aus der Summe der Produkte  
			     * der jeweils korrespondierenden Pixel aus Bild und Matrix  
			     */  
			    gy += source.getSample(0, x + xSy, y + ySy) *  
			    	Sy[xSy + 1][ySy + 1];  
			}  
  
		    }  
		}  
  
		/* neuen Farbwert aus beiden Filtern berechnen  
		 * und auf 255 skalieren!  
		 */  
		g = (int) Math.round(Math.sqrt(gx * gx + gy * gy));  
		// neuen Pixel setzen  
		if(applyThreshold) {  
		    for(int channel = 0; channel < 3; channel++) {  
    		        destination.putSample(channel, x, y, g >= threshold ? 255 : 0);  
    		    }  
		} else {  
    		    for(int channel = 0; channel < 3; channel++) {  
    		        destination.putSample(channel, x, y, g);  
    		    }  
		}  
  
	    }  
	}  
    }

Was mache ich falsch?

Grüße
Richard

  1. Hallo,

    noch ein paar Anmerkungen:

    die Methoden getSample() und putSample() nehmen folgende Parameter:

    getSample(int channel, int x, int y) und putSample(int channel, int x, int y, int value)

    Die Variable source verweist auf das Quellbild, destination auf das Zielbild.

    Grüße
    Richard

  2. Hallo,

    ich habe das Problem gelöst (zumindest glaube ich das fest). Bei Testausgaben stellte sich heraus, dass Farbwerte > 255 in den Farbwert 0 umgewandelt werden. Ein Ändern der Zeile

    destination.putSample(channel, x, y, g);

    in

    destination.putSample(channel, x, y, g > 255 ? 255 : 0);

    verringert das Rauschen.

    Noch ein Hinweis, falls es in grauer Zukunft noch einmal jemanden interessieren sollte: Die gepostete Lösung ist noch nicht vollständig, da es die Randpixel ignoriert.

    Grüße
    Richard