# Graustufen aus Bild ermitteln



## Milo (24. Okt 2006)

Hallo,

ich möchte ein TIFF-Bild einlesen und die vorhandenen Garuwerte bestimmen und letztlich manipulieren.

Das Einlesen des Bildes war nach einigen rumprobieren mit Jimi möglich:


```
Image img = Jimi.getImage("s_w_bilddatei.tiff" );
```

Wie komme ich nun am besten an die Graustufen?

Meine leider gescheiterte Idee(n) war es, ein BufferedImage zu erzeugen und dann entweder getRGB(x,y) oder mit getSample() aus dem daraus erzeugten Raster was zu erreichen. Beides liefiert mir aber keinen (richtigen) Farbwert. 

Bei getSample() kam immer 0 (Null) raus und bei getRGB() -16777216. 

Ich habe gelesen, dass das Bild nicht sofort bei BufferedImage zur Verfügung steht, deshalb vermute ich, das meine Abfragen "zu früh" starten und somit ins leere laufen.

Kann mir einer von Euch einen Tipp geben, wie ich mittels BufferedImage an meine Farbwerte komme oder, sofern ich auf dem Holzweg bin, Alterativen nennen. 


Vielen Danke!

Milo


----------



## dieta (24. Okt 2006)

Bei getRGB(x, y) wird der Farbwert in einen int "verpackt". Um direkt an die RGB-Werte zu kommen, kannst du dir z.B. ein Color-Objekt mit dem int von getRGB(x, y) konstruieren und dann dessen Farbwerte einzeln abfragen:

```
Color c = new Color(dasBufferedImage.getRGB(x, y));
int rot = c.getRed();
int gruen = c.getGreen();
//...
```
Bei Google findest du sicher auch haufenweise Hilfe zu diesem Thema.


----------



## Ark (24. Okt 2006)

```
int rgb=img.getRGB(x,y);
int gray=rgb&0xFF;//Blau, ist aber bei Graustufen eh alles gleich:
if(gray==(rgb>>8&0xFF)&&gray==(rgb>>16&0xFF)){//Graustufe gray entdeckt}
```


----------



## Milo (25. Okt 2006)

Hallo Ihr beiden,

wenn ich erst einmal so weit wäre, dann würde ich es so machen . Mein Prpblem ist, das ich den Farbwert nicht abfragen kann, da ich das BufferedImage scheinbar nicht richtig erstellen kann. 

Frag ich mal direkt, wie erzeuge ich aus meinem eingelesenen Image img ein BufferedImage?

Von diesem könnte ich dann so vorgehen, wie ihr beide beschrieben habt.

Schöne Grüße Milo


----------



## The_S (25. Okt 2006)

Zeichne das Image doch einfach auf ein BufferedImage, welchem du als ImageTyp Graustufe übergibst.


----------



## The_S (25. Okt 2006)

Zu deiner Frage von gerade eben


```
Graphics2D g2d = buffimage.createGraphics();
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
```


----------



## Milo (25. Okt 2006)

Hi Hobbit,

nachdem ich den ganzen Vormittag wild probiert habe, kam ich nun zu folgendem:


```
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.awt.image.*;
import com.sun.jimi.core.*;

public class test extends JFrame {
  // Anfang Variablen
  BufferedImage bImg;
  Image Img;
  JLabel JL = new JLabel();
// Ende Variablen

  public test(final String title) {
    // Frame-Initialisierung
    super(title);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(final WindowEvent evt) { System.exit(0); } });
    final int frameWidth = 300;
    final int frameHeight = 300;
    setSize(frameWidth, frameHeight);
    Container cp = getContentPane();
    cp.setLayout(new BorderLayout());

    Img = Jimi.getImage("test.tiff" );
    //bImg = new BufferedImage(Img.getWidth(this), Img.getHeight(this), BufferedImage.TYPE_INT_RGB);
    bImg = new BufferedImage(Img.getWidth(this), Img.getHeight(this), BufferedImage.TYPE_BYTE_GRAY);
    Graphics2D g2d = bImg.createGraphics();
    paint(g2d);
    int rgb=bImg.getRGB(150,10);
    Color c = new Color( rgb );
    System.out.println(c+"  "+rgb);
    setVisible(true);
  }

  // Anfang Ereignisprozeduren
  public void paint(Graphics graphics) {
    Graphics2D g = (Graphics2D) graphics;
    g.drawImage(Img, 0, 0, this);
    g.dispose();
  }

  // Ende Ereignisprozeduren

  public static void main(final String[] args) {
    new test("test");
  }
}
```

Das liefert mir die entsprechenden Gruwerte - Danke. 

Was mir jedoch schleierhaft ist, ist 
	
	
	
	





```
paint(g2d);
```
 hiermit übergebe ich der Methode Paint() eine Objekt vom Type Graphics2D. Diese Methode erwartet aber Graphics. Wenn ich es "korrigiere" wird mir das Bild nicht angezeigt, warum funktionierts trotzdem?

Ansonsten schon einmal vielen Dank!
Milo


----------



## The_S (25. Okt 2006)

du weißt schon, dass du die paint-Methode nicht unbedingt ohne Grund überschreiben solltest!? Benenn die mal um.


----------



## Leroy42 (25. Okt 2006)

Milo hat gesagt.:
			
		

> Was mir jedoch schleierhaft ist, ist
> 
> 
> 
> ...



Einfach weil ein Graphics2d auch gleichzeitig ein Graphics *ist*.

Graphics2D erweitert eben Graphics und kann überall dort benutzt
werden wo ein Graphics erwartet wird.


----------



## Milo (25. Okt 2006)

Hi,



			
				Hobbit_Im_Blutrausch hat gesagt.:
			
		

> du weißt schon, dass du die paint-Methode nicht unbedingt ohne Grund überschreiben solltest!?



Nein.



			
				Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Benenn die mal um.



Dann geht es nicht mehr.  

Milo


----------



## The_S (25. Okt 2006)

Du musst den Methodenaufruf natürlich auch umbennen ... Oder willste das Bild gleich anzeigen (sollteste aber auch anders lösen)?


----------



## Leroy42 (25. Okt 2006)

Normalerweise überschreibt man die paintComponent-Methode.

Allerdings weiß ich nicht ob dies direkt in einem JFrame gemacht
werden kann/darf. Habe ich bis jetzt noch nie gemacht (immer mindestens
ein JPanel auf meinem JFrame) und jetzt auch keine Lust zu eruieren
ob das möglich ist.

Allerdings:

```
public void paint(Graphics graphics) { 
    Graphics2D g = (Graphics2D) graphics; 
    g.drawImage(Img, 0, 0, this); 
    g.dispose(); 
  }
```

Die Umwandlung in ein Graphics2D-Objekt ist unnötig da drawImage bereits
in Graphics definiert ist und automatisch das drawImage von Graphics2D
aufgerufen wird falls es eines bereit stellt.

Daß _disposen_ halte ich dagegen sogar für gefährlich,
da dies, falls nötig, bereits von den, paint() aufrufenden,
Methoden gemacht wird.


----------



## Milo (25. Okt 2006)

Hallo,

ich kenne mich nicht so aus, als das ich alles beurteilen könnte. Ich habe es nun in eine Klasse gepakt:


```
class myImage extends JPanel {
  private BufferedImage bImg;
  private Image Img;
  public myImage() { }

  public myImage(Image Img) {
    this.Img = Img;
    this.bImg = new BufferedImage(this.Img.getWidth(this), this.Img.getHeight(this), BufferedImage.TYPE_BYTE_GRAY);
    Graphics2D g2d = bImg.createGraphics();
    paint(g2d);
  }

  public void paint(Graphics graphics) {
    Graphics g = graphics;
    g.drawImage(this.Img, 0, 0, this);
    //g.dispose();
```

Dabei das Graphics geändert und das dispose, das von Hobbit kam, auskommentiert. 

LG Milo


----------



## Leroy42 (25. Okt 2006)

```
class myImage extends JPanel { 
  // Variablennamen klein schreiben.
  private Image img; 

  // Leerer Konstruktor wird automatisch generiert und brauch nicht gecodet zu werden
  public myImage() { } 

  public myImage(Image img) { 
    this.bImg = new BufferedImage(img.getWidth(this), this.Img.getHeight(this), BufferedImage.TYPE_BYTE_GRAY); 
    // paint niemals direkt aufrufen
    repaint();
  } 

  public void paintComponent(Graphics graphics) { 
    // überschrieben Methode aufrufen
    super.paintComponent(g);
    g.drawImage(this.Img, 0, 0, this); 
  }
}
```


----------



## The_S (25. Okt 2006)

Milo hat gesagt.:
			
		

> und das dispose, das von Hobbit kam, auskommentiert.



Hey, nicht anfangen hier rumzustänkern :lol: . Das dispose hatte schon seine Berechtigung. Nur da wo du es eingesetzt hast ist es falsch. Kommt daher, weil du nicht erwähnt hast, dass du das Bild auch gleich anzeigen möchtest.


----------



## Leroy42 (25. Okt 2006)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Hey, nicht anfangen hier rumzustänkern :lol: .


Das würde ich mir gegenüber meinem zukünftigen Schwager doch nie erlauben.  :wink:


----------



## Milo (25. Okt 2006)

Hi,

@Hobbit: Ich stänker doch nicht 
Ich wollte es auch nicht anzeigen aber ohne das Anzeigen kam leider nix. Deshalb wollte ich dann zumindest sehen, ob es "da" ist. Als es dann vorhanden war, konnte ich auch die Stufen bestimmen. Kann sein, das es auch irgendwie beim rumprobieren vorher bereits ging.

@Leroy
Welcher Vorteil bei Deiner Methode:

```
public void paintComponent(Graphics graphics) { 
    // überschrieben Methode aufrufen 
    super.paintComponent(g); 
    g.drawImage(this.Img, 0, 0, this); 
  }
```

sollte mit gegenüber "meiner" paint-Methode sofort ins Auge fallen? wenn Du mir nur den Code zeigst, ohne etwas dazu zu schreiben, habe ich wenig davon. Nach der Regel: "Richtig ist, was funktioniert" hätte ich bereits 100 Punkte aber trotzdem scheint Dich ja etwas gestört zu haben - als, raus mit der Sprache 

Vielen Dank Euch beiden

Milo


----------



## Leroy42 (25. Okt 2006)

Das "Stänkern" bezog sich auf meine Äußerung!


```
public myImage(Image Img) { 
    this.Img = Img; 
    this.bImg = new BufferedImage(this.Img.getWidth(this), this.Img.getHeight(this), BufferedImage.TYPE_BYTE_GRAY); 
    Graphics2D g2d = bImg.createGraphics(); 
    paint(g2d); 
  } 

  public void paint(Graphics graphics) { 
    Graphics g = graphics; 
    g.drawImage(this.Img, 0, 0, this); 
    //g.dispose();
  }
```

In deiner Methode wird das Bild nur einmal bei Abarbeitung des Konstruktors
aufgerufen. Solltest du dein Frame zwischenzeitlich überdecken (z.B. mit dem Browserfenster)
wird, bei Wiedersichtbarkeit, automatisch vom System die paint-Methode aufgerufen.

Nun ist es allerdings so, daß die paint-Methode von JPanel noch einiges andere
tut, nämlich den Hintergrund zu löschen, ein eventuelles Border zu zeichnen, ...

Aus diesem Grund sollte man (in Swing) niemals die paint-Methode direkt überschreiben
sondern immer nur die paintComponent-Methode, die dann von der nicht-überschriebenen
paint-Methode in JPanel zur richtigen Zeit aufgerufen wird.

Ich hoffe, mich verständlich ausgedrückt zu haben.  ???:L


----------



## The_S (25. Okt 2006)

Leroy42 hat gesagt.:
			
		

> Hobbit_Im_Blutrausch hat gesagt.:
> 
> 
> 
> ...



Das war auf Milo bezogen. Du bist nicht immer gemeint sobald jemand das Schimpfen anfängt  

@Milo

ohne Anzeigen wäre das dann sowas:


```
private BufferedImage getGrayScale(Image img) {
    BufferedImage buffimage = new BufferedImage(img.getWidth(this), img.getHeight(this), BufferedImage.TYPE_BYTE_GRAY);
    Graphics2D g2d = buffimage.createGraphics();
    g2d.drawImage(img, 0, 0, null);
    g2d.dispose();
    return buffimage;
}
```


----------



## Milo (25. Okt 2006)

Hi,

ahja, auf eine einfache Methode bin ich gar nicht gekommen...

Ich hatte schon wieder Probleme, aus meiner neuen Klasse die entsprechenden Grauwerte auszulesen aber das hatte sich ja nun erledigt. So, nun muss ich aber mal als reduzieren, das ist ja der Kern der Aufgabe.

Milo


----------



## Milo (26. Okt 2006)

Guten morgen,

nun habe ich ein neues Problem.

Ich habe die Grauwerte (8Bit) in ein Integer-Array eingelesen. 


```
private int[][] convertImage2Array(BufferedImage bImage){
    int width = bImage.getWidth();
    int height = bImage.getHeight();
    int PixelsAsInt[][] = new int[height][width];

    for (int i=0; i<height; i++)
      for (int j=0; j<width; j++)                  //x, y
        PixelsAsInt[i][j] = new Color( bImage.getRGB(i, j) ).getBlue();
    return PixelsAsInt;
  }
```

Diese Werte will ich reduzieren (7,...,1 Bit).

Dazu habe ich mir folgende Methode überlegt:


```
private BufferedImage scaleImage(int scale, int[][] PixelsAsInt){
    int width = PixelsAsInt[0].length;
    int height = PixelsAsInt.length;
    scale = scale > 8?8:(scale < 1?1:scale);
    scale = (int)(256/Math.pow(2,scale));
    System.out.println(scale);
    BufferedImage bImage = new BufferedImage (width, height, BufferedImage.TYPE_BYTE_GRAY);
    WritableRaster WR = bImage.getRaster();
    
    for (int i=0; i<height; i++){
      for (int j=0; j<width; j++){
        WR.setSample(i,j,0,scale*(int)(PixelsAsInt[i][j]/scale));
      }
    }
   // System.out.println("Original "+PixelsAsInt[250][0]+" Reduziert "+(scale*(int)(PixelsAsInt[250][0]/scale)));

    return bImage;
```

scale ist der übergebene Bit-Wert. Rufe ich die Methode mit scaleImage(8,PixelAsInt); auf, so bekomme ich jedoch nicht mehr mein Originalbild, obwohl ich letztlich keine Veränderung der Werte vornehme.

Zunächst bestimme ich, wieviel Pixel nun einen Wert annehmen: scale = (int)(256/Math.pow(2,scale))
Wenn scale 6 ist, dann bilden immer 4 Pixel eine Graustufe. Durch diesem Wert teile ich jeden Farbwert des 8Bit-Bildes und multipliziere anschließend mal scale.

Bsp.
scale = 6 --> 256/ (2^6) = 4 Pixel ein Grauwert.
Farbe im 256-Bild bezitzt Grauwert: 73
73/3 = 24,333 = (int)24
24 * 4 = 96 <-- neuer Grauwert in einem 6Bit Bild

Bei der Konvertierung in ein 8Bit Bild, dürfte also keine Änderung kommen:
scale = 8 --> 256/(2^8) = 1
73/1 = 73 = (int)73
73 * 1 = 73 == keine Änderung (rechnerisch  )

Sieht einer, wo ich meinen Denkfehler habe?

LG Milo


Vll sieht ja einer meinen Denkfehler


----------



## Milo (26. Okt 2006)

Hi,

habe einen Weg gefunden und zwar über ein RGB Bild:


```
BufferedImage bImg = new BufferedImage (width, height, BufferedImage.TYPE_INT_RGB);
    WritableRaster WR = bImg.getRaster();
    
    for (int i=0; i<height; i++){
      for (int j=0; j<width; j++){
        int[] tmp = { scale*(int)(PixelsAsInt[i][j]/scale),scale*(int)(PixelsAsInt[i][j]/scale),scale*(int)(PixelsAsInt[i][j]/scale) };
        WR.setPixel(j,i, tmp );
      }
    }
```

damit bekomme ich zumindest schoin mal wieder mein Originalbild heraus.

Milo


----------



## Krisch (26. Okt 2006)

ich kriegs nicht hin...

es wird einfach kein bild angezeigt...


----------



## Milo (26. Okt 2006)

Hi,



			
				Krisch hat gesagt.:
			
		

> ich kriegs nicht hin...
> es wird einfach kein bild angezeigt...



Es soll auch keiun Bild angezeigt, sondern nur umgewandelt werden. Wobei ich manchmal auch das Problem habe, dass das Bild nicht eingelesen wird bzw. er die nächste Methode aufruft noch bevor das Bild als BufferedImage vorliegt. Kann ich das irgendiew "puffern"?

LG Milo


----------



## The_S (26. Okt 2006)

Kirsch meint eigentlich nen anderen Thread 

Du kannst einen MediaTracker verwenden. Der wartet bis das Bild geladet ist. Oder einfach dein Image über ImageIO lesen.


----------



## Guest (27. Okt 2006)

Hallo,



			
				Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Du kannst einen MediaTracker verwenden. Der wartet bis das Bild geladet ist. Oder einfach dein Image über ImageIO lesen.



Ah-ja, Danke. Ich habe es nun erst einmal so gelöst:


```
private Image readImage(File f){
    MediaTracker mt = new MediaTracker(this);
    if (f.exists() && f.isFile() && f.canRead()) {
      Image img = Jimi.getImage( f.toString() );
      mt.addImage(img, 0);
      try {
        mt.waitForAll();
        return img;
      } catch (InterruptedException e) {
        return null;
      }
    }
    return null;
  }
```

Milo


----------



## Leroy42 (27. Okt 2006)

Anonymous hat gesagt.:
			
		

> Ich habe es nun erst einmal so gelöst:



Wieso _erst einmal_? Das ist doch die übliche Vorgehensweise.


----------



## Milo (27. Okt 2006)

Hi,

wenn ich das öfters machen würde, könnte ich das sicher auch behaupten 

Milo


----------

