# getRaster().getData() vs PixelGrabber



## Degush (18. Dez 2012)

Hallo liebe Community,

um die Bilder in einer Engine schnell darzustellen, nutze ich BufferedImages. Nun möchte ich die Bilder aber ( Geschwindigkeit spielt eine untergeordnete Rolle ) mit bestimmten Operatoren filtern - Movarec Operator, Sobel, Median, usw. um sie zu glätten, schärfen, Kanten zu detektieren, Rauschen zu verkleinern usw.
Dafür brauche ich die konkreten Pixeldaten des Bildes.

An die kann ich ja kommen über:

```
(DataBufferInt)getRaster().getDataBuffer()).getData();
```
in einer Klasse, die BufferedImage erweitert.
Ich habe allerdings bereits jetzt einen sehr hohen Arbeitsspeicher- und CPUverbrauch und habe Angst, dass die Methode da zu langsam ist, um sie bei jeder Nachfrage nach einem Pixel auf  Position XY neu zu verwenden und dass ich zu viel Arbeitsspeicher aufbrauche, wenn ich jetzt anfange, für jedes BufferedImage zusätzlich noch seine Rohinformationen anzuzeigen.

Ich weiß, dass die PixelInformationen von BufferedImages, wenn man es richitg macht, im Grafikkartenspeicher gespeichert sind, um das Rendern zu beschleunigen.
Und ich habe noch irgendwelche Informationen mit PixelGrabbern gesehen, die auch die Bildpixel auslesen.

Frage: Wie kriege ich möglichst direkten Zugriff auf die Bildpixel?


----------



## Marco13 (18. Dez 2012)

Wenn die Geschwindigkeit eine Untergeordnete Rolle spielt, könnte man ggf. auch BufferedImageOp (Java Platform SE 6) (ConvoleOp für Sobel etc) verwenden - damit ist das ganze etwas abstrakter und allgemeingültiger. 

Wenn du auf die Bilddaten direkt zugreifen musst/willst, kannst du dir den DataBufferInt holen. Das erfordert nicht mehr Speicher, weil die Daten sowieso einmal im normalen RAM liegen, und man sich mit der angedeuteten Methode _direkt_ DEN (echten) Array aus dem BufferedImage holt (und nicht etwa eine Kopie).

Beachte aber, dass wenn man sich einmal diesen Array geholt hat, das Bild nicht mehr "managed" sein kann - GROB gesagt muss danach sichergestellt werden, dass die Daten im Array (im RAM) und auf der Grafikkarte (im VRAM) konsistent sind - und im speziellen danach das _Zeichnen_ des Bildes langsamer gehen kann (siehe auch http://www.java-forum.org/spiele-multimedia-programmierung/120318-performance-bufferedimages.html )

Auf Java Image Processing - Managed Images and Performance gibt's noch ein paar Infos dazu, und allgemein könnte die Seite für dich auch interessant sein.


----------



## Degush (18. Dez 2012)

Hey, danke Marco!

Eine Frage habe ich noch;


```
Wenn du auf die Bilddaten direkt zugreifen musst/willst, kannst du dir den DataBufferInt holen
```

Heißt dass, dass diese Funktion:
	
	
	
	





```
(DataBufferInt)getRaster().getDataBuffer()).getData();
```
 einfach nur den Zugriff auf ein DataBufferInt kapselt?
Und dass ich die Daten nicht zwischenspeichern sollte?


```
Beachte aber, dass wenn man sich einmal diesen Array geholt hat, das Bild nicht mehr "managed" sein kann
```

Nach der Bearbeitung kann ich das Bild ja neu auf sich selbst zeichnen lassen.


----------



## Marco13 (18. Dez 2012)

In dem DataBufferInt steckt ja der int[]-Array mit den Pixeln, den man sich mit "getData" abholen kann. Den kann man speichern, es ist ja nur eine Referenz auf die Daten im Image (und wenn man den Arrayinhalt ändert, ändert man das Bild). 
Aber wenn ein Bild erstmal "unmanaged" ist, kann man es nicht wieder "managed" machen. Ggf. könnte (!) es dann sogar sinnvoll sein, das "unmanaged" Bild (bei dem man den int[] verändert hat) in ein _neues_ Bild reinzumalen, und dann das neue (managed) Bild zu zeichnen.


----------



## Degush (23. Dez 2012)

Habe es jetzt so gemacht, dass ich einfach den Buffer speichere. Der DataIntBuffer ist ja nur eine Referenz. Wenn ich den Array auslese und verändere, passiert nichts.
Allerdubgs habe ich irgendwie unerwartete Resultate beim Filtern.

Bei einem Glättefilter mit der Maske
1 1 1
1 1 1
1 1 1

mal 1/9 kriege ich beim Filtern ein Bild mit folgendem Ergebnis:

Directupload.net - dl9wepzk.png

Als Pixelwerte kriege ich auch negative Pixel. Ich vermute, dass das am DataBufferInt liegt. Dass er signed Integers ausspuckt oder so.


----------



## Marco13 (23. Dez 2012)

Mehr als "da ist wohl ein Fehler im Code" kann man da ja jetzt kaum sagen... :bahnhof:


----------



## Degush (30. Dez 2012)

Da ist mein Code:


```
/**
 *
 * @author Malte
 */
public class LinearImageFilter implements ImageFilter
{
    public static final int SMOOTHING_FILTER = 0;
    public static final int SHARPENING_FILTER = 1;
    public static final int SOBEL_FILTER = 2;
    
    public static Logger log = new Logger("LinearImageFilter");
    
    private int[][] filterMask;
    private double weighfactor = 1;
    private int maxIndex;
    
    public static LinearImageFilter create(int type, int size)
    {
        if ( type == SMOOTHING_FILTER )
        {
            int[][] matrix = new int[size][size];
            for(int x = 0; x < size; x++)
            {
                for(int y = 0; y < size; y++)
                {
                    matrix[x][y] = 1;
                    log.log("Matrix "+x+"/"+y+": "+matrix[x][y]);
                }
            }
            return new LinearImageFilter(matrix, 1./(size*size) );
        } else if ( type == SHARPENING_FILTER )
        {
            
        } else if ( type == SOBEL_FILTER )
        {
            int[][] matrix = {
            {1,2,1},
            {0,0,0},
            {-1,-2,-1}
            };
            return new LinearImageFilter(matrix, 1);
        }
        return null;
    }
    
    public LinearImageFilter(int[][] multi, double weight)
    {
        this.filterMask = multi;
        log.log("Weightfactor: "+weight);
        this.weighfactor = weight;
        this.maxIndex = (multi.length-1)/2;
        log.log("MaxIndex: "+maxIndex);
    }
    
    @Override
    public int getSize()
    {
        return filterMask.length;
    }
    
    @Override
    public int operate(int[][] values)
    {
        int value = 0;
        for(int i = 0; i < values.length; i++)
        {
            for(int y = 0; y < values[0].length; y++)
            {
                value += values[i][y]*filterMask[i][y];
            }
        }
        return (int)(value*weighfactor);
    }
    
    private double getPositive(double d)
    {
        if ( d < 0 )
            return -d;
        return d;
    }
    
    @Override
    public int[] filter(int[] pixels, int imageWidth)
    {
        int[] filteredData = new int[pixels.length];
        int imageHeight = pixels.length/imageWidth;
        
        if ( log.isActivated() )
            log.log("Filtering Image width Width: "+imageWidth+" and Height: "+imageHeight);
        
        double value;
        int i;
        
        for(int y = 0; y < imageHeight-maxIndex; y++)
        {
            for(int x = 0; x < imageWidth-maxIndex; x++)
            {
                value = 0;
                //log.log("===================================");
                for(int u = 0; u < filterMask.length; u++)
                {
                    for(int v = 0; v < filterMask.length; v++)
                    {
                        i = ((y+v-maxIndex)*imageWidth)+(x+u-maxIndex);
                        if ( i >= 0 && i < pixels.length )
                        {
                            value += (((double)pixels[i])*weighfactor*filterMask[u][v]);
                            //log.log("Value: "+value);
                        }
                    }
                }
                i = ( (y + maxIndex) * imageWidth + x + maxIndex);
                if ( i >= 0 && i < filteredData.length )
                {
                    if ( !(value > -4000000 && value < 250000) )
                    {
                        filteredData[i] = (int)(value);
                    } else
                    {
                        filteredData[i] = 0;
                    }
                }
                //log.log("Pixel: "+filteredData[i]);
            }
        }
        
        return filteredData;
    }
}
```


----------



## Marco13 (30. Dez 2012)

Ein KSKB wäre nicht schlecht. Aber wenn du das mit einem programmatisch erstellten BufferedImage füttern würdest, bei dem die Pixel-Werte alle 1 sind, würdest du den Fehler vermutlich schon selbst finden.


----------



## Degush (31. Dez 2012)

Hmm, ich finde den Fehler nicht. In einem schwarzen Quadrat auf weißem Untergrund mit der Filtergröße 25 und einem Glättefilter, ändern sich die Farben Ränder des schwarzen Kastens von dunkelblau (innen) zu hellorange (außen). Nicht von dunkel zu hell oder von hell zu dunkel.


----------



## Marco13 (31. Dez 2012)

Jetzt wo das bei mir im Großhirn angekommen ist: Das ist ja auch kompletter Unfug... Du rechnest ja mit den int-Werten, die den RGB-Farbcode beschreiben. Rechnungen (Addition, Division...) darauf und nebenbei solche Abfragen wie
 if ( !(value > -4000000 && value < 250000) )
ergeben dort gar keinen Sinn. Berechne die RGB-Komponenen einzeln

```
int rSum = 0;
int gSum = 0;
int bSum = 0;
for (... all pixels...)
{
    for (... kernel ... )
    {
        int rgb = pixel[index]; 
        byte r = (byte)((rgb >> 16) & 0xFF);
        byte g = (byte)((rgb >>  8) & 0xFF);
        byte b = (byte)((rgb >>  0) & 0xFF);
        rSum += r * ...
        gSum += g * ...
        bSum += b* ...
   }
    rSum /= sum;
    gSum /= sum;
    bSum /= sum;
    int newRgb = (rSum << 16) | (gSum << 8) | bSum;
    ...

}
```


----------



## Degush (3. Jan 2013)

Danke für die Hilfe, Marco, jetzt klappt's!

Habe in anderen Quellen aber eigenartigerweise andere Implementationen gefunden, die wohl auch funktionieren, ohne die ARGB-Werte aus dem Pixel zu extrahieren:



```
Bildbearbeitung - Matritzen [Archiv] - 3DCenter Forum[/url]


Zitat:

[code=Java]
public int[][] wendeMaskeAn (int[][] grau, int[][] maske, int hoehe, int breite) {

int [][] pixelKante = new int[hoehe][breite];

int farbsumme =0;
int farbe=0;

for(int i=1;i<hoehe-1;i++) {
for(int j=1;j<breite-1;j++) {

for(int iM=0;iM<3;iM++) {
for (int jM=0;jM<3;jM++) {
farbsumme = farbsumme+((grau[i-1+iM][j-1+jM])*maske[iM][jM]);

}
}
farbe = farbsumme/16;
pixelKante[i][j] = farbe;
}
}
return pixelKante;
}
```

Wie mache ich das denn, wenn ich z.B. den Pixelwert mit dem Int aus der Maske multipliziere? Kann man den Pixel dann einfach mit z.B. zwei multiplizieren, oder muss man wieder erst die Farben extrahieren?

Gibt es irgendeine Möglichkeit, so etwas eleganter mithilfe von Bitoperationen durchzuführen?

Malte

*Edit:* _Liegt wohl daran, dass er mit einem Graustufenbild arbeitet_


----------



## Namphiz (7. Feb 2013)

Marco13 hat gesagt.:


> Aber wenn ein Bild erstmal "unmanaged" ist, kann man es nicht wieder "managed" machen.



Ist das auch der Grund, weshalb man keine Graphics-Methoden (brauchen die "managed" Bilder?) mehr auf dem Bild ausführen kann?

Ich benutze ein Pixel Array und eine BufferStrategy... von der BufferStrategy bekomme ich das Graphics-Objekt, welches ich an eine Methode weitergebe und später das Bild anzeigen lasse.

In dieser Methode möchte ich zum Beispiel einen String auf das Bild "malen". Ein Aufruf der Form g.drawString(...) führt jedoch zu keiner Änderung.

Folgendes funktioniert allerdings:

```
pixels[5433] = 0xFF0000;
```
In diesem Fall würde Pixel Nr. 5433 rot sein.


----------



## Marco13 (7. Feb 2013)

BufferStrategy habe ich nocht nicht so intensiv verwendet, und kann das gerade nicht einordnen, kann man da ein KSKB erstellen?


----------



## Namphiz (7. Feb 2013)

Hier habe ich dir ein kleines Beispiel geschrieben, dass die Benutzung demonstriert:


```
package graphics;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

import javax.swing.JFrame;

public class PerPixelRendering extends Canvas implements Runnable {
	
	public static final int WIDTH = 640;
	public static final int HEIGHT = 480;
	
	Thread renderThread;
	BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
	int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
	
	@Override
	public void run() {
		while (true) {
			render();
		}
	}
	
	public void render() {
		BufferStrategy bs = getBufferStrategy();
		if (bs == null) {
			createBufferStrategy(3);
			return;
		}
		
		Graphics g = bs.getDrawGraphics();
		
		renderBackground();	// FUNKTIONIERT
		drawString(g);		// FUNKTIONIERT NICHT
		
		g.drawImage(image, 0, 0, null);
		g.dispose();
		bs.show();
	}
	
	private void renderBackground() {
		for (int y = 0; y < HEIGHT; y++) {
			for (int x = 0; x < WIDTH; x++) {
				pixels[x + y * WIDTH] = 0xFF0000;
			}
		}
	}
	
	private void drawString(Graphics g) {
		g.setColor(new Color(0xFFFFFF));
		g.drawString("TEST", 100, 100);
	}
	
	public void start() {
		renderThread = new Thread(this, "Render Thread");
		renderThread.start();
	}

	public static void main(String[] args) {
		PerPixelRendering ppr = new PerPixelRendering();
		ppr.setPreferredSize(new Dimension(PerPixelRendering.WIDTH, PerPixelRendering.HEIGHT));
		JFrame frame = new JFrame("Per Pixel Rendering");
		frame.add(ppr);
		frame.setResizable(false);
		frame.pack();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		ppr.start();
	}
}
```

Ach.. und ich habe noch eine Verständnisfrage:

Das Pixel-Array ist ja eine Referenz (ich wusste gar nicht genau, dass es sowas in Java gibt...kenne das nur von C/C++) auf den Speicherbereich, der die Integer für die Pixelfarben darstellt oder? 
Weil ich mich gewundert habe, wieso das Darstellen funktioniert, wenn das Pixel-Array dem Image doch nirgendwo mehr zugewiesen wird.


----------



## Marco13 (8. Feb 2013)

Ja, der int[] array ist eine Referenz (einschließlich der Auswirkungen auf das managed-sein).

Bzgl des Codes: Das Bild wird einfach über den Text drübergepinselt.... Mit sowas wie

        Graphics gg = image.createGraphics();
        drawString(gg);      // FUNKTIONIERT DOCH
        gg.dispose();

landet der Text IM Bild (d.h. im int-array), und das sieht man dann auch... Ansonsten müßte man den Text NACH dem Bild malen...


----------



## Namphiz (8. Feb 2013)

Vielen Dank!

Dann ist mein Problem ja geklärt.


----------

