2D-Grafik g2.drawImage() langsam

Planich

Aktives Mitglied
Hallo,
ich zeichne sehr große Images (bis zu 30MB) und irgendwie ist das ganze viel zu langsam, wenn ich das mal so vergleiche mit ImageJ
Hier mal ein Stück Code

Java:
protected void paintComponent(Graphics g) {
        long t1 =System.currentTimeMillis();
        super.paintComponent(g);
        
        if (this.image != null) {
            Graphics2D g2 = (Graphics2D) g;
            int newW = (int) (image.getWidth() * scaleFactor);
            int newH = (int) (image.getHeight() * scaleFactor);            
            size=new Dimension(newW,newH);
            this.setPreferredSize(size);
            
            this.revalidate();//0-15 ms
            
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    //RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
                    //RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            
            g2.drawImage(image, 0, 0, newW, newH, null);  //KRITISCHE STELLE
            
            
            System.out.println(System.currentTimeMillis()-t1);
            g2.dispose();
            System.out.println("T2 ");
            
        }
    }

Die SystemTime hab ich geholt um mal zu schauen wo es denn eigentlich langsam ist - es ist die g2.drawImage Stelle - da wo auch kritische Stelle kommentiert wurde

Ich versteh einfach nicht warum das an dieser Stelle mit einem 28MB Bild satte 1600ms dauert. Wenn ich es per Zoom noch skalieren lasse komme ich auf 2000ms

Gibt es bessere Methoden oder mache ich was falsch? Ich bin über jede Hilfe dankbar
 

Planich

Aktives Mitglied
Was für ein Typ ist das Image? Schau ggf. mal in http://www.java-forum.org/spiele-mu...18-performance-bufferedimages.html#post803128 , vielleicht hilft das schon ein bißchen. Ansonsten muss man genauer überlegen. 120000000 bytes auf den Bildschirm zu bringen ist aber auch nicht von Pappe.

Es ist ein BufferedImage was ich verwende.
Es ist sogar vorher von mir von 16bit Grau auf 8 bit Grau runtergerechnet worden und trotzdem so langsam - soll heißen, das Bild ist bei der Darstellung auch nicht ansatzweise die 30MB groß - diese sollten nur im Ram liegen um darauf zu warten das ich andere Grenzen für die 8 bit festlege

Jedenfalls ist die ganze Umrechnerei von 16 auf 8 bit noch relativ flott. Was so lange dauert ist echt nur das drawImage() und das versteh ich nicht
 

Marco13

Top Contributor
Ich meinte: Welcher Typ im Sinne vom BufferedImage.TYPE_INT_ARGB (oder irgendwas mit "Grey"?). Mal' das Bild ggf. mal so wie im verlinkten Snippet in ein neu angelegtes Bild vom Typ TYPE_INT_ARGB.
 

Planich

Aktives Mitglied
ehrlich gesagt habe ich keine Ahnung welcher Typ das ist, weil ich das BufferedImage über einen solchen Konstruktor erstelle

Über einen Decoder kriege ich ein RenderedImage
damit erstelle ich mir dann ein BufferedImage mit dieser Methode:

Java:
public BufferedImage convertRenderedImage(RenderedImage img) {
		if (img instanceof BufferedImage) {
			return (BufferedImage)img;	
		}	
		ColorModel cm = img.getColorModel();                
		width = img.getWidth();
		height = img.getHeight();
		WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
		boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
		Hashtable properties = new Hashtable();
		String[] keys = img.getPropertyNames();
		if (keys!=null) {
			for (int i = 0; i < keys.length; i++) {
				properties.put(keys[i], img.getProperty(keys[i]));
			}
		}
		BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
		img.copyData(raster);
		return result;
	}

ich weiß also gar nicht um welchen Typ es sich handelt - mit welcher Methode kann ich das denn nachschauen?

Ich werde jetzt mal in der Paint versuchen eine andere Graphics zu erstellen und darauf mein eigentliches BufferedImage zu zeichnen. Das ist doch das was du meinst oder?

Danke bis hierhin

Edit: So ich hab die PaintMethode jetzt mal umgeschrieben, jedoch sehe ich jetzt nicht das Bild das gezeichnet wird. Hier mal ein Ausschnitt, falls mehr benötigt, bitte bescheid geben

Java:
@Override
    protected void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        BufferedImage imageForGraphics = new BufferedImage(
        image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D gNew = imageForGraphics.createGraphics();
        
        if (this.image != null) {
            
            int newW = (int) (image.getWidth() * scaleFactor);
            int newH = (int) (image.getHeight() * scaleFactor);            
            size=new Dimension(newW,newH);
            this.setPreferredSize(size);
            
            this.revalidate();//0-15 ms
            
            
            gNew.drawImage(image, 0, 0, null);  //KRITISCHE STELLE
            
            
            System.out.println(System.currentTimeMillis()-t1);
            gNew.dispose();            
        }
    }

Ich habe das Gefühl als wäre das gezeichnete Bild irgendwie nicht der oberste Layer, sodass es verdeckt wird.Vielleicht täusche ich mich auch. Aber mit Sicherheit habe ich dein Snippet nicht kapiert. Mir fehlen da auch Basics :(

Edit2: Wie ich auch gerade bemerkt habe, ist es jetzt auch nicht schneller als vorher

Edit3: Ich bin gerade per Suchmaschine über einen Thread gestolpert, in dem gesagt wird, dass wenn man getSubimage oder getDataBuffer benutzt, die Hardwarebeschleunigung deaktiviert wird. Dann kann es zu starken Performanceeinbrüchen kommen. Ich benutze getDataBuffer, jedoch bei der Erstellung meines BufferedImages ein gutes Stück bevor es zum Zeichnen kommt.

z.B. in der Form
Java:
byteArr = ((DataBufferByte) originalImage.getData().getDataBuffer()).getData();

Sollen diese Stellen in meinem Code dafür sorgen, dass es auch später beim Zeichnen Performanceprobleme gibt und zwar, weil die Hardwarebeschleunigung nicht mehr funktioniert? Und wenn ja, wie soll ich an meine Rohdaten kommen, wenn nicht über getDataBuffer?
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Den Typ kann man sich mit bufferedImage.getType() abholen - erstmal nur als int-Wert, den kann man aber mit den Konstanten BufferedImage.TYPE_INT_ARGB usw. vergleichen. Der int-Wert sollte am besten 2 sein (eben TYPE_INT_ARGB).

Aber da man das nicht beeinflussen kann, kann man das Bild konvertieren. Ich bin nicht sicher, welchen Zweck die Methode "convertRendereredImage" hat, bzw. wo die wann mit welchem Bild als Parameter aufgerufen wird, aber du könntest mal schauen, ob es nicht reicht, die durch
Java:
    public static BufferedImage convertToARGB(BufferedImage image)
    {
        BufferedImage newImage = new BufferedImage(
            image.getWidth(), image.getHeight(), 
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose(); 
        return newImage;
    }
zu ersetzen...
 

Planich

Aktives Mitglied
Also vorweg: mein Typ ist 0, ich habs nachgeschaut.

Die Methode convertRenderedImage brauche ich um eben aus dem RenderedImage ein BufferedImage zu konvertieren. Nur damit kann ich arbeiten.
Aber ich werde nicht schlau aus deinen Code Snippets.

Die verändern ja nicht den Typ meines darzustellenden Bildes? Ich sehe das du ein Image erstellst mit der Höhe und Breite meines Anfangsbildes und ja du übergibst noch den Typ von dem du die ganze Zeit sprichst, aber gezeichnet wird doch dann wieder das Bild von dem du sagst das es evtl. den falschen Typ besitzt.

Und in meiner paintComponent kann ich mir doch nicht einfach von einem solchen leeren BufferedImage eine Graphics holen und darauf zeichnen? Für die paintComponent wird doch eine Graphics übergeben. Die sollte ich doch dann auch benutzen oder nicht?

Hm ich bin verwirrt.

Ich habe mal beim Erstellen des Panels folgendes gemacht:

Ich habe zusätzlich zum eigentlichen Bild ein Bild erstellt, dass deinen Vorgaben entspricht und habe mit diesem dann ein Panel erstellt um nicht extra noch eine graphics erstellen zu müssen, so wie bei dir beschrieben. Und in der Paint Methode habe ich dann mein Bild vom Typ 0 auf die Graphics gezeichnet, bei der ich davon ausgehe, dass sie so ist wie von dir gewollt.

Aber die Zeit ist die gleiche, keine Veränderung.
 

Marco13

Top Contributor
Hmja, also nein ;) Das Bild, das gezeichnet wird, sollte eins vom TYPE_INT_ARGB sein. Der Typ 0, den du im Moment hast, ist "TYPE_CUSTOM", und das kann u.U. SEHR deutlich langsamer sein beim Zeichnen.

Und natürlich sollte man NICHT in der paintComponent ein neues Bild erstellen.

Wo bekommst du denn das RenderedImage her, das du da vorher mit deiner geposteten Methode konvertiert hast?

Der Grundgedanke ist, dass man das Bild direkt nach dem laden in ein passendes BufferedImage konvertiert.

Grob: Wenn du vorher sowas hattest:
Java:
RenderedImage renderedImage = woherAuchImmerDuDasHast();
BufferedImage bufferedImage = convertRenderedImage(renderedImage);
vervendeZumZeichnen(bufferedImage);
dann solltest du stattdessen sowas machen können wie
Java:
RenderedImage renderedImage = woherAuchImmerDuDasHast();
BufferedImage bufferedImage = convertToARGB(renderedImage); // Erstelle neues Bild mit TYPE_INT_ARGB
vervendeZumZeichnen(bufferedImage);
 

Planich

Aktives Mitglied
Hm ich verstehe dich, aber deine beschriebene Methode konvertiert kein BufferedImage beliebigen Typs in ein BufferedImage des Typs ARGB. Oder ich bin blind. Was ich da sehe ist nur, dass du ein BufferedImage des Typs ARGB erstellst und daraus eine Graphics holst.

Hier mal die Methode wie ich an mein RenderedImage komme

Java:
TIFFDecodeParam param =null;    //TIFF
        //Tiff Decoder erstellen
        ImageDecoder dectiff=ImageCodec.createImageDecoder("TIFF", file, param);
        //Rendered Image erstellen
        RenderedImage ritiff=dectiff.decodeAsRenderedImage();
        //BI erstellen
        originalImage=convertRenderedImage(ritiff);

Hatte ich ja am Anfang erwähnt, dass ich meine Bilder dekodiere und da nen RenderedImage erhalte. Dieses wandle ich dann in meiner convertRenderedImage Methode um, damit ich weiterarbeiten kann.

Dein Gedanke mit dem Typ leuchtet mir ein, jedoch nicht wie ich aus meinem Typ 0 ein Typ 2 BI erstelle. Irgendwie übersehe ich etwas offensichtliches oder ich bin grad einfach zu dumm ;(

Edit: Ich glaube langsam versteh ich deine Methode zur Konvertierung - ich dachte bis eben das die Graphics immer als Zeichenfläche im Zusammenhang mit der PaintComponent steht. Scheinbar ist das was du da machst so on the fly? Ich probier das gleich mal aus

Edit2: Bingo - jetzt sinds beim ersten mal zeichnen 15ms statt 1500ms - nur scheint er an irgend einer Stelle in meinem Code das ganze wieder mit dem anderen Typ zu überschreiben, denn schon beim nächsten Zeichnen ist der Typ wieder 0 und die Dauer dementsprechend

Edit3: Echt genial, es sind teilweise sogar nur 2 ms fürs Zeichnen, echt der Wahnsinn. Ich muss nur noch die Stellen finden, an denen es wieder überschrieben wird. Vorhin hab ich einfach mal an allen möglichen Stellen ein ConvertToArgb gemacht und hatte wohl ein Heap Problem :shock:
 
Zuletzt bearbeitet:

Planich

Aktives Mitglied
Ich glaube ich weiß warum das ganze ohne Konvertierung in ARGB so langsam zeichnet. Ganz einfach weil es bei jedem paint Aufruf implizit in ARGB konvertiert. Diese Konvertierung hält jedoch nur für diesen einen Paint Aufruf und muss dementsprechend jedes mal wiederholt werden.

Das Problem an einer Stelle bei mir ist nur, dass ich jedes mal ein neues BufferedImage erzeugen und somit auch konvertieren muss, was wieder lange dauert

Hier mal der Code

Java:
private ColorModel ccm = new ComponentColorModel(cs,false,true,Transparency.OPAQUE,DataBuffer.TYPE_BYTE);


public BufferedImage create8bitBufferedImage(DataBufferByte buffer) {   
        ComponentSampleModel csm = new ComponentSampleModel(DataBuffer.TYPE_BYTE,width,height,1,width,new int[] {0});
        WritableRaster wr = WritableRaster.createWritableRaster(csm, buffer, new Point(0,0));
        BufferedImage img = new BufferedImage(ccm,wr,false,new Properties());
        return img;
    }

Kann mir jemand sagen ob ich in dieser Methode etwas so ändern kann, dass ich am Ende ein BufferedImage des Typs ARGB habe und es nicht erst noch konvertieren muss?
 
Zuletzt bearbeitet:

Marco13

Top Contributor
EDIT: Das bezog sich NICHT auf den Letzten Beitrag, sondern auf den davor:

Achso wenn's darum geht:
Java:
// Hier holt man sich das Graphics-Objekt DES BILDES! 
// Alles, was man in dieses Graphics-Objekt reinzeichnet,
// landet IM BILD (und nicht etwa auf dem Bildschirm oder so)
Graphics2D g = newImage.createGraphics();

// Hier wird das alte Bild in das neue reingemalt (quasi "reinkopiert")
g.drawImage(image, 0, 0, null);

Und natürlich sollte man diese Konvertierung so selten wie möglich machen - wie gesagt, am besten direkt nach dem Laden einmal, und dann aufpassen, das Bild nicht mehr durch getSubImage, getDataBuffer etc. "kaputt" zu machen...
 

Planich

Aktives Mitglied
ich verwende diese Methode, wenn ich für mein aktuelles 16bit Bild neue Grenzen für das lineare Runterrechnen auf 8bit festlege.

Hier mal die Erklärung an Hand meines Codes
Java:
public void setUgOgDraw(int ug, int og)
        {            
            //bei 16bit
            if(type==1)
            {            
            this.ug=ug;
            this.og=og;
            
            shortToByte(data,ug,og ); 

            buffer = new DataBufferByte(byteArr, byteArr.length);
            changedImage=create8bitBufferedImage(buffer);        
            
            }            
        }

Hier setze ich meine Grenzen und wandle das short[] in ein byte[] (byteArr) womit ich meinen buffer erstelle.Mit diesem wird dann das 8bit BufferedImage erstellt, was dann dummerweise Typ 0 ist, was ich ja vermeiden will. Den Code dafür findest du in meinem vorigen Post

data ist das short[] und wird immer nur am Anfang einmal geschrieben und dann nur gelesen um immer wieder ein byte[] mit den neuen Grenzen zu erstellen
 

Marco13

Top Contributor
Hm OK... an irgendeinem Punkt muss natürlich gerechnet werden: Wenn man nur einen short-Array hat, und daraus ein Bild machen muss, bei dem die Shorts auf einen bestimmten Bereich "normalisiert" als Graustrufen dargstellt werden müssen, dann... ja, kann das einen Moment dauern. Was heißt "lange"? Hast du irgendwo zwei Slider (für ug und og), und es gibt eine Verzögerung, wenn man die bewegt, und on the fly ein 30MB-Bild aktualisiert werden muss? ;)

Eine Möglichkeit könnte sein, die 16bit-Werte in passende ints umzurechnen, (also einfach das einzelne byte, in das sie eigentlich umgerechnet werden, als R,G und B eines RGB-Wertes verwenden), und den daraus resultierenden int-Array dann mit BufferedImage (Java Platform SE 7 )[],%20int,%20int%29 (oder setPixels aus WritableRaster) in ein Bild bringen kann, aber ... wie gut das klappt und wie viel es bringt müßte ich auch erst Benchmarken...
 
S

Spacerat

Gast
Ich habe inzwischen herausgefunden, das es egal ist, welchen ImageTyp man hat, ein BI ist "langsam", sobald sein Datenpuffer in der JVM durch [c]getRaster().getDataBuffer()[/c] referenziert wird oder ein Raster mit bereits referenziertem Datenpuffer erstellt wird. Das heisst, wenn du ein Raster mit deinem Bytearray im Konstruktor eines BIs verwendest, bleibt's halt langsam. Mit ARGB oder so hat das nichts zu tun, eher damit, dass ein Puffer nur solange mit der Hardware synchron gehalten werden kann, wie auf keine Referenz davon in Java zugegriffen werden kann.
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
Ich bin mir ziemlich sicher, dass ein BufferedImage mit TYPE_3BYTE_RGB langsamer gezeichnet wird, als eins mit TYPE_INT_RGB, aber vielleicht hat sich da in den letzten VMs was getan. Der Beschreibung nach klingt es, als wolltest du sagen, dass die "managed-heit" tatsächlich mit den Referenz zu tun hat - und NICHT damit, dass eine der "bösen" Methoden aufgerufen wird. Oder meintest du das anders?
 
S

Spacerat

Gast
Nee, daran hat sich nichts geändert. Das HW-Format bleibt RGB. Von allen Bildern dürften jene mit Alpha-Kanal die langsamsten sein. Aber die jeweils erforderlichen Umrechnungsmethoden in das HW-Format muss bei unmanaged Images halt frameweise Pixel für Pixel für Pixel der gesamte Puffer in die HW geschrieben werden. bei managed Images verbleibt immer ein aktueller in der HW und es werden pro Frame nur einzelne Pixel aktualisiert. In meiner DirectBufferedImage-Version wird das in etwa "emuliert", ändert aber leider nichts daran, dass ich den gesammten Puffer immer noch frameweise kopieren darf.
[EDIT][OT]Da fällt mir noch ein... Auf aktueller Hardware gibt es zweierlei Arten von Grafik-Puffern. 1. Den FrameBuffer (32-Bit-RGB) und 2. mehrere TexturBuffer (32-Bit-ARGB). Ich nehme mal an, das die JVM den gleich den FrameBuffer (über DirectDraw) verwendet.[/OT][/EDIT]
 
Zuletzt bearbeitet von einem Moderator:

Planich

Aktives Mitglied
Hm OK... an irgendeinem Punkt muss natürlich gerechnet werden: Wenn man nur einen short-Array hat, und daraus ein Bild machen muss, bei dem die Shorts auf einen bestimmten Bereich "normalisiert" als Graustrufen dargstellt werden müssen, dann... ja, kann das einen Moment dauern. Was heißt "lange"? Hast du irgendwo zwei Slider (für ug und og), und es gibt eine Verzögerung, wenn man die bewegt, und on the fly ein 30MB-Bild aktualisiert werden muss? ;)

Diese Zeit, die die Slider etc. brauchen habe ich schon rausgerechnet. Meine Zeitmessung habe ich rein auf die Konvertierung nach ARGB bezogen. Bei einem 16bit Bild (was an der Stelle der Konvertierung nur noch 8 bit sind) Größe 6MB dauert die Konvertierung satte 500ms.Genau soviel wie in etwa auch das zeichnen, wenn ich vorher nicht konvertiere - das bestätigt nur meine Vermutung, dass das Zeichnen eines NICHT-Typ2 Images immer implizit erstmal temporär für den einen Zeichenvorgang nach Typ2 konvertiert. Und das umrechnen von 16bit auf 8 bit dauert nicht mal 300ms bei einem 30MB 16bit Bild

Eine Möglichkeit könnte sein, die 16bit-Werte in passende ints umzurechnen, (also einfach das einzelne byte, in das sie eigentlich umgerechnet werden, als R,G und B eines RGB-Wertes verwenden), und den daraus resultierenden int-Array dann mit BufferedImage (Java Platform SE 7 )[],%20int,%20int%29 (oder setPixels aus WritableRaster) in ein Bild bringen kann, aber ... wie gut das klappt und wie viel es bringt müßte ich auch erst Benchmarken...

Du meinst wenn ich das so umwandle kann ich mir direkt ein 8bit ARGB BI erstellen? Das wäre dann schön, wenn es denn nicht genau so lange dauert wie die Konvertierung nach ARGB

Spacerat hat gesagt.:
Ich habe inzwischen herausgefunden, das es egal ist, welchen ImageTyp man hat, ein BI ist "langsam", sobald sein Datenpuffer in der JVM durch getRaster().getDataBuffer() referenziert wird oder ein Raster mit bereits referenziertem Datenpuffer erstellt wird. Das heisst, wenn du ein Raster mit deinem Bytearray im Konstruktor eines BIs verwendest, bleibt's halt langsam.

Das deckt sich in etwa mit dem was ich ein paar Posts vorher gesagt habe. Dort hatte ich geschrieben, dass ich im Netz etwas davon gelesen habe, dass die Hardwarebeschleunigung deaktiviert wird, wenn man getSubimage oder getDataBuffer benutzt. Jedoch muss ich hier anmerken, dass wenn ich den Typ nach ARGB konvertiere, ich super Performance beim Zeichnen habe. Konvertiere ich das ganze nicht bricht die Performance ein. Erstellt wurden ist das BI aber mit dem gleichen Konstruktor. Soll heißen, entweder ich versteh dich falsch, oder du hast nur halb Recht. Denn die miese Performance bleibt bei der Erstellung übers Raster und den Buffer nicht, wenn man den Typ umwandelt.
 
Zuletzt bearbeitet:

Planich

Aktives Mitglied
Eine Möglichkeit könnte sein, die 16bit-Werte in passende ints umzurechnen, (also einfach das einzelne byte, in das sie eigentlich umgerechnet werden, als R,G und B eines RGB-Wertes verwenden), und den daraus resultierenden int-Array dann mit BufferedImage (Java Platform SE 7 )[],%20int,%20int%29 (oder setPixels aus WritableRaster) in ein Bild bringen kann, aber ... wie gut das klappt und wie viel es bringt müßte ich auch erst Benchmarken...

Das versteh ich nicht ganz. Wie kann ich das denn nach R,G und B umrechnen und dann damit ein BI erstellen?

Wie rechnen ich mein Byte[] in R,G, und B um? (int[])
 

Planich

Aktives Mitglied

Damit kann ich leider nicht viel anfangen.

Ich möchte meine Frage etwas konkreter formulieren.

Was ich habe ist ein normales byte[] - darin sind keine r,g,b,a Werte enthalten. Und ich muss dieses in eine int[] Form kriegen mit den passenden r,g,b,a Werten zum Erstellen eines BI Typ 2.

Ich habe mal etwas probiert, aber das gibt mir nur ein schwarzes Bild mit ein paar blauen Punkten. Außerdem muss ich eine ArrayOutOfIndex Exception abfangen sonst fliegt das ganze in die Luft :(

Hier mal mein Code

Java:
public BufferedImage create8bitBufferedImage(DataBufferByte buffer) { 
        int[] intMyRGB = new int[width*height*4];
        int[] intArr = new int[width*height];
        
        int i =0;
        
        
        ComponentSampleModel csm = new ComponentSampleModel(DataBuffer.TYPE_BYTE,width,height,1,width,new int[] {0});        
        WritableRaster wr = WritableRaster.createWritableRaster(csm, buffer, new Point(0,0));        
        BufferedImage img = new BufferedImage(ccm,wr,false,new Properties());        
        
        intArr = img.getRGB(0, 0, width, height, intMyRGB, 0, width);
        
        try
        {
            for(int t=0;t<intArr.length;t++)
                {
                int alpha = ((intArr[t] & 0xff000000) >> 24); // alpha channel!
                int red = ((intArr[t] & 0xff0000) >> 16); // red channel!
                int green = ((intArr[t] & 0x0000ff00) >> 8); // green channel!
                int blue = (intArr[t] & 0x000000ff); // blue channel !

                intMyRGB[i] = blue;
                intMyRGB[i+1] = green;
                intMyRGB[i+2] = red;
                intMyRGB[i+3] = alpha;

                intArr[t] = (0xff000000|alpha<<24|red<<16|green<<8|blue);

                i += 4;
                }            
        }
        catch(Exception ex)
        {
            System.out.println(i);
            System.out.println(intMyRGB.length);
        }
        
        
        Image im = createImage(new MemoryImageSource(width, height, intMyRGB, 0, width));
        
        BufferedImage b=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
            b.getGraphics().drawImage(im, 0,0,width,height,null);
            setPreferredSize(new Dimension(b.getHeight(),b.getHeight()));
        
        return b;
    }

Weiß jemand was ich meine und kann mir vielleicht helfen?
 

Marco13

Top Contributor
Grob
Java:
byte input = byte[i];
int a = 0xFF000000;
int r = (input << 16);
int g = (input << 8);
int b = (input << 0);
int argb =  a | r | g | b;

EDIT: Zweite Seite übersehen, aber sollte trotzdem passen...
 

Planich

Aktives Mitglied
Sorry ich versteh das Prinzip nicht. Was macht denn die Zeile "int argb = a | r | g | b;" ich kenne das | nur als oder. und ich dachte ich müsste mir ein int [] aus argb Werten zusammenfrickeln. Dort sehe ich kein Array sondern einen int.

ich bin jetzt zwischenzeitlich bei

Java:
int[] rgbArr = new int[width*height*4];
        
        for(int i=0;i<buffer.getData().length;i++)
        {
            int a = 0xFF000000;
            int r = ( buffer.getData()[i] << 16);
            int g = ( buffer.getData()[i] << 8);
            int b = ( buffer.getData()[i] << 0);
            rgbArr =  a | r | g | b;            
        }

was natürlich nicht funktioniert. Irgendwie bin ich zu blöd glaub ich. Kann mir das bitte jemand mal so erklären das ich es kapiere?

edit: ich versteh es zwar nicht, aber jetzt funktioniert das wenigstens
hier mal der Code der ganzen Methode für Leute die es vielleicht interessiert
Java:
public BufferedImage create8bitBufferedImage(DataBufferByte buffer) { 
        
        
        ComponentSampleModel csm = new ComponentSampleModel(DataBuffer.TYPE_BYTE,width,height,1,width,new int[] {0});        
        WritableRaster wr = WritableRaster.createWritableRaster(csm, buffer, new Point(0,0));        
        BufferedImage img = new BufferedImage(ccm,wr,false,new Properties());        
        
        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); //30
        
        /*Setting RGB values to BufferedImage*/
        
            int[] raw = new int[buffer.getData().length * 4];
            for (int i = 0; i < buffer.getData().length / 3; i++) {
                raw[i] = 0xFF000000
                        | ((buffer.getData()[i + 0] & 0xFF) << 16)
                        | ((buffer.getData()[i + 1] & 0xFF) << 8)
                        | ((buffer.getData()[i + 2] & 0xFF));
            }//50
            buffImg.setRGB(0, 0, width, height, raw, 0, width);//140              
            buffImg.getGraphics().drawImage(buffImg, 0,0,width,height,null);//15
            
                
        return buffImg;
    }

Edit2: mit der neuen Methode habe ich ein Problem bei meinen großen Bildern
"Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space"

Die normalen Bilder so um die 6MB klappen ohne Probleme

weiß da jemand Rat?
 
Zuletzt bearbeitet:

Planich

Aktives Mitglied
Ich hab die Methode jetzt nochmal umgeschrieben und sie ist nochmal ein gutes Stück performanter

Java:
public BufferedImage create8bitBufferedImage(DataBufferByte buffer) {         
                
        /*Setting RGB values to BufferedImage*/
        
            int[] raw = new int[buffer.getData().length * 4];
            for (int i = 0; i < buffer.getData().length / 3; i++) {
                raw[i] = 0xFF000000
                        | ((buffer.getData()[i + 0] & 0xFF) << 16)
                        | ((buffer.getData()[i + 1] & 0xFF) << 8)
                        | ((buffer.getData()[i + 2] & 0xFF));
            }
            long t1=System.currentTimeMillis();
            Image im= createImage(new MemoryImageSource(width, height,raw , 0, width));   //0                        
            
            changedImage.getGraphics().drawImage(im, 0,0,width,height,null);         
            System.out.print("create8bitBI   "); System.out.println(System.currentTimeMillis()-t1);               
        return changedImage;
    }

Das einzige was jetzt noch nervt ist, das wohl viel zu große raw Array bei meinen großen 30MB Pics, das wohl den Heap sprengt. Wenn da jetzt jemand noch Rat hat bitte her damit
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Ja, wie du den DataBufferByte erstellst, hatte man AFAIR noch nicht gesehen. Ich bin davon ausgegangen, dass jedes einzelne byte einen Grauwert (0-255) enthält.

30 MB sind doch nichts. Notfalls die Anwendung mit
java -Xmx1000m DieAnwendung
starten, um 1000MB Speicher bereitzustellen.

Aber... vielleicht nochmal ganz der Reihe nach: Du hast einen short-Array, der magisch,magisch irgendwo her kommt. Und jetzt geht es darum, diesen short-Array
- In einen byte-Array umzuwandeln, wobei aus einen short 's' ein byte 'b' gemacht werden soll mit
b = (s-ug) / (og-ug)
(ggf. auf 0-255 geclampt)
- Diesen byte-Array dann in ein BufferedImage zu verwandeln, das die Werte als Graustufen anzeigt
Stimmt das so?
 

Planich

Aktives Mitglied
Ja, wie du den DataBufferByte erstellst, hatte man AFAIR noch nicht gesehen. Ich bin davon ausgegangen, dass jedes einzelne byte einen Grauwert (0-255) enthält.
ja jedes einzelne byte hat den Grauwert 0-255 da bist du völlig korrekt

30 MB sind doch nichts. Notfalls die Anwendung mit
java -Xmx1000m DieAnwendung
starten, um 1000MB Speicher bereitzustellen.
Die Anwendung damit starten? Wie kann ich in Netbeans denn einen solchen Startparameter verwenden? das sieht nach Console aus :D


Aber... vielleicht nochmal ganz der Reihe nach: Du hast einen short-Array, der magisch,magisch irgendwo her kommt. Und jetzt geht es darum, diesen short-Array
- In einen byte-Array umzuwandeln, wobei aus einen short 's' ein byte 'b' gemacht werden soll mit
b = (s-ug) / (og-ug)
(ggf. auf 0-255 geclampt)
- Diesen byte-Array dann in ein BufferedImage zu verwandeln, das die Werte als Graustufen anzeigt
Stimmt das so?
Der short array ist nicht magisch :D - diesen erhalte ich wenn ich mein 16Bit Bild reinlade
short[] data = ((DataBufferUShort) originalImage.getData().getDataBuffer()).getData();
Und es geht ja nicht darum das ganze in ein Byte Array umzuwandeln - denn dafür habe ich schon seit ein paar Wochen meine Methode - es geht darum dieses Byte Array in ein nutzbares int Array bestehend aus ARGB Werten umzuwandeln, damit ich ein BufferedImage des Typs 2 erstellen kann um ein schnelles Paint zu ermöglichen.

Wie oben beschrieben, habe ich nach sehr viel Probieren nun eine einigermaßen performante und nicht ganz unelegante Lösung für mein Problem gefunden.

Wobei jetzt die for Schleife die Schwachstelle ist, wo ich das byte Arr in das int Array aus ARGB Werten konvertiere. Wenn es keine andere Möglichkeit gibt das BufferedImage Typ 2 zu erstellen, komme ich wohl an der Konvertierung des byte Array nach int Arr ARGB nicht vorbei
 
Zuletzt bearbeitet:

bERt0r

Top Contributor
Also, du hast einen grauwert, z.B 50. Die RGB Kanäle sind für Grau immer gleich, das heist R=50, G=50, B=50. Wenn du noch einen Alpha Kanal hast, sollte das ganze wohl deckend sein, also A=255. Das heisst du errechnest dir für einen int ARGB Pixel eine Integerzahl, bei der das erste byte 255 ist, das 2. 50 und das 3. und 4. auch 50. Weil ein integer 4 byte groß ist geht sich das schön aus.
Mathematisch würdest du 50 + 50*256 + 50 *256² + 255*256³ rechnen. Mit java geht das auch über die bitverschiebe Operatoren.
 
S

Spacerat

Gast
short[] data = ((DataBufferUShort) originalImage.getData().getDataBuffer()).getData();
Bravo... das ist genau die oben angesprochene Zeile, die ein BufferedImage - in diesem Fall dein originalImage - "unmanaged" macht. Warum lässt du das Image nicht einfach von Java wandeln? so bleibt es wenigstens beschleunigt ("managed").
Java:
BufferedImage grayed = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
grayed.getGraphics().drawImage(originalImage, 0, 0, null);
 

Marco13

Top Contributor
Pffft... :pueh: Warum sollte man das machen? Das wär' doch voll... einfach! :noe:


Im Ernst: Es geht wohl darum
- Das BufferedImage mit ImageIO zu laden (man bekommt eins mit einen ShortBuffer)
- Diesen ShortBuffer auf die angesprochene Weise in einen Byte-Array/Buffer zu verwandeln
- DAraus dann wieder ein Bild zu machen

Der zweite Schritt ist wohl das entscheidende: Das ist - soweit ich das bisher verstanden habe - der, wo (zitat von oben: ) aus einen short 's' ein byte 'b' gemacht werden soll mit
b = (s-ug) / (og-ug)
(ggf. auf 0-255 geclampt)

Stimmt das so weit? (Und läuft das am Ende auf einen Histogrammabgleich hinaus?)
 
S

Spacerat

Gast
Pffft... :pueh: Warum sollte man das machen? Das wär' doch voll... einfach! :noe:
"Wer viel ironiert, bekommt einen Sarkasmus." :lol:

Selbst wenn es auf diesen Abgleich hinauslaufen sollte, wäre man mit den "setElements()"-Methoden des Rasters besser bedient.
BufferedImages kann man in unendlich vielen Variationen erstellen, man kann sogar ein eigenes ColorModel mit entsprechendem Histogramm-Konverter implementieren und mit einem SunWritableRaster, dessen Buffer immer noch "managed" ist, an den entsprechenden Konstruktor des BIs übergeben.
BTW1.: Nur SunWritableRasters können "managed" sein und das auch nur von vorneherein, solange der Buffer nur dieser einen Instanz bekannt ist bzw. bekannt sein kann, indem man es per Breite und Höhe instanziert und den Buffer intern anlegen lässt. Instanziert man eines mit Buffer Array ist es bereits "unmanaged" und bleibt dann auch so.
BTW2: Dieses [c]int[] raw = new int[...][/c] könnte unter Umständen den GC "überfordern", nämlich wenn es häufiger aufgerufen wird, als nur einmal. Höhere Frameraten bedeuten so mehr Datenmüll. Das kann immens viel werden und GC bzw. die JVM dankt es dir dann mit 'nem OutOfMemoryError. Wenn man davon ausgehen kann, dass das Bild immer die selbe Grösse hat, kann man den Buffer auch einmalig als Instanzvariable erstellen und in jedem Frame mit neuen Daten füllen. Such' mal nach Object Reuse.
BTW3.: Was bitte hat's denn mit diesem MemoryImage auf sich (hab's noch nicht bemerkt :oops:)? Kann es sein, dass ein DirectBufferedImage dazu besser geeignet wäre? Vllt. lässt sich davon ja auch etwas in meiner Kreation eines solchen verwenden.
 
Zuletzt bearbeitet von einem Moderator:

Planich

Aktives Mitglied
Also falls ihr es nicht bemerkt habt, ich bin hier, weil ich weiß das mein Code nicht der beste ist ;)
Und deshalb frag ich ja euch Profis. Ich bin ja mittlerweile froh das es einigermaßen rollt. Und ich hatte zwischendurch auch erwähnt, dass ich weiß, dass wenn ich den Buffer hole, das es dann nicht mehr performant - Spacerat sagt unmanaged - ist. Ich weiß nur in meiner Situation keine andere Lösung für mein Problem.

Ich schildere nochmal meine Situation.

Ich habe z.B. ein 16bit TIFF oder PNG oder was weiß ich - also verschiedene Formate und dekodiere das ganze, erhalte dabei ein RenderedImage, dass ich mir in ein BufferedImage umwandle. Dieses 16bit BufferedImage dient mir sozusagen als Ausgangssituation für alles weitere und wird von mir auch nicht mehr verändert, sondern nur noch ausgelesen. Ich hole mir also den Buffer und speichere das ganze in einer Variable die ich dann dazu nutzen kann das ganze über von mir gesetzte Grenzen (ug/og) auf 8 bit runterzubrechen (byte[]). Und nun wandle ich das ganze um zu einem int[] argb und erstelle damit ein Image das ich in mein BufferedImage Typ 2 hineinzeichne.

Warum ich mir nun mein short[] per Buffer aus dem 16bit BufferedImage hole? Nunja, wahrscheinlich weil ich keine andere Möglichkeit weiß. Und mit der Performance, die Spacerat unmanaged nennt, bin ich bis dato zufrieden. Das einzige was jetzt gerade noch schneller laufen könnte, wäre die Wandlung des byte[] nach int[] ARGB

Solche Kleinigkeiten wie int[] raw = new int[...] interessieren mich, wenn das ganze so rollt wie ich es will - denn dann merze ich genau solche Fehler aus. Das hat mit dem Kernproblem gerade überhaupt nichts zu tun

SPACERAT hat gesagt.:
Bravo... das ist genau die oben angesprochene Zeile, die ein BufferedImage - in diesem Fall dein originalImage - "unmanaged" macht. Warum lässt du das Image nicht einfach von Java wandeln? so bleibt es wenigstens beschleunigt ("managed").
In was soll ich denn mein 16bit BufferedImage bitte von Java wandeln lassen? Ich versteh dich nicht ganz? Ich brauch den Buffer/die Daten. Und das Typ 0 wie ich ihn original erhalte beim Zeichnen extrem langsam ist, wahrscheinlich weil er implizit temporär nach Typ 2 konvertiert, hatte ich bereits erwähnt

Wenn ihr den Code wollt sagt bescheid, aber das wird dann sicherlich etwas mehr. Doch ich denke es ist nun einigermaßen nachvollziehbar
 
Zuletzt bearbeitet:

Planich

Aktives Mitglied
Java:
BufferedImage grayed = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
grayed.getGraphics().drawImage(originalImage, 0, 0, null);

Was soll mir das bringen? Dann habe ich was gekonnt? Ich weiß jetzt nicht was Java da macht.
1) es zeichnet mein 16bit Bild linear auf 8bit ab
2) das BufferedImage "grayed" ist auch wieder 16bit

beides ist doch für mich völlig ungeeignet, weil ich die 16bit Daten brauche
 

Marco13

Top Contributor
Da, von dem Image mit dem Short-Buffer den Buffer zu holen ist nicht schlimm (auch wenn es dadurch unmanaged wird) FALLS das Bild dann nicht mehr gezeichnet werden muss (und das muss es ja wohl nicht?!)

Poste ggf. mal die Umwandlung von byte[] nach int[], und wie du diesen int[] dann ins eigentlich zu zeichnende Bild reinbringst. (Möglichst klar abgegrenzt nur diesen Teil, aber am besten testbar als KSKB)
 

Planich

Aktives Mitglied
Stimmt das so weit? (Und läuft das am Ende auf einen Histogrammabgleich hinaus?)

Das stimmt vollends. Wenn ich jetzt noch wüsste was ein Histogrammabgleich ist, könnt ich dir das vielleicht beantworten. Ich hab leider kein Informatik studiert und auch sonst kein Profi was programmieren angeht.

Insgesamt nochmal: das erste BufferedImage - 16bit - das ich mir von einem File reinlade, brauche ich im Prinzip nicht als BufferedImage. Das einzige das ich brauche sind die Farbwerte für jeden Pixel.
So wie ich es verstanden habe, wird nicht nur das BufferedImage unmanaged, sondern auch der Buffer, den ich davon kriege.

Und mit diesem Buffer, ob nun in der Variable oder per Methode vom BufferedImage, kann ich dann nur noch langsam arbeiten? Habe ich das richtig verstanden? Dann wäre ja interessant, wie ich ein Bild lade (16bit, beliebiges Format) ohne es unmanaged zu machen und auf die Farbwerte aller Pixel zugreifen kann.
 

Planich

Aktives Mitglied
Da, von dem Image mit dem Short-Buffer den Buffer zu holen ist nicht schlimm (auch wenn es dadurch unmanaged wird) FALLS das Bild dann nicht mehr gezeichnet werden muss (und das muss es ja wohl nicht?!)
Ja genau muss es nicht! Lediglich der Buffer dieses Bildes wird regelmäßig genutzt

Poste ggf. mal die Umwandlung von byte[] nach int[], und wie du diesen int[] dann ins eigentlich zu zeichnende Bild reinbringst. (Möglichst klar abgegrenzt nur diesen Teil, aber am besten testbar als KSKB)

Das was du da möchtest ist im Prinzip meine oben genannte Methode. Hier nochmal

Java:
public BufferedImage create8bitBufferedImage(DataBufferByte buffer) {         
      
            for (int i = 0; i < buffer.getData().length / 3; i++) {
                raw[i] = 0xFF000000
                        | ((buffer.getData()[i + 0] & 0xFF) << 16)
                        | ((buffer.getData()[i + 1] & 0xFF) << 8)
                        | ((buffer.getData()[i + 2] & 0xFF));
            }
            
            Image im= createImage(new MemoryImageSource(width, height,raw , 0, width));                         
            
            changedImage.getGraphics().drawImage(im, 0,0,width,height,null);         
                      
        return changedImage;
    }

Was heißt KSKB?
Die letzte Zeile Code, da wo ich auf die Graphics meines changedImages zeichne, ist auch nen Teil den ich gern etwas flotter hätte. Aber vielleicht liegts auch an meiner lahmen Schnecke von PC

Vom Release am Slider bis zum Zeichnen vergehen jetzt nur noch 250 ms circa. Davon gehen etwa 50ms beim Zeichnen auf die Graphics drauf. Das Erstellen des byte[] aus den 16bit dauert etwa 80 ms
Alles bezogen auf mein 6MB 16bit Referenzbild
 
Zuletzt bearbeitet:
S

Spacerat

Gast
Was soll mir das bringen? Dann habe ich was gekonnt? Ich weiß jetzt nicht was Java da macht.
1) es zeichnet mein 16bit Bild linear auf 8bit ab
2) das BufferedImage "grayed" ist auch wieder 16bit

beides ist doch für mich völlig ungeeignet, weil ich die 16bit Daten brauche
1. Ja, 2. NEIN! es ist definitiv 8 Bit (DataBuffer.TYPE_BYTE). Welche Daten benötigst du denn nun? Dein 2. Punkt und deine Begründung dass es ungeeignet wäre (16 Bit obwohl du 16 Bit benötigst), sind mir nicht ganz klar, ich schliesse mal auf einen Flüchtigkeitsfehler.
Du hättest damit, wie gewünscht, das Zeichnen in dein changedImage flotter bekommen, anders geht's nicht, weil "unmanaged". Ok, da dir linear nicht reicht, was ja schon abzusehen war, bleibt immer noch ein BufferedImage per selbst implementiertem ColorModel und einem WritableRaster per [c]Raster.createInterleavedRaster(width, height, width, 1, new int[] {1}, DataBuffer.TYPE_BYTE)[/c] instanziert, zu erstellen. Die Puffer-Arrays sind für dich fortan bedeutungslos. Die Farbwerte der Originalbildes bekommst du von dort per [c]getData().getElements()[/c]. Obwohl, wenn's nicht mehr gezeichnet werden soll, kannst dir auch den Puffer wie gewohnt beschaffen, das hat aber den Nachteil, dass du bei Farb-Indizierten Bildern (z.B. GIF) Indices statt Farbwerte bekommst. Mit diesen Elementen berechnest du deine Grenzen und übergibst diese per Konstruktor an dein selbst gebautes ColorModel in welchem dann sämtliche Farbberechnungen stattfinden. Mit diesem ColorModel dann schlicht ein neues BI erstellen und das Originalbild wie oben reinzeichnen. Wenn du das nicht hinbekommst, zeig mir mal die Methode, wie du an deine Ober- bzw. Untergrenzen kommst.
 

Marco13

Top Contributor
Ein KSKB ist das, was erscheint, wenn man mit der Maus über "KSKB" fährt. Sowas zum Beispiel:
Java:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferUShort;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


class ImagePanel extends JPanel
{
    private BufferedImage image;
    
    public ImagePanel()
    {
    }
    
    public void setImage(BufferedImage image)
    {
        this.image = image;
        repaint();
    }
    
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (image != null)
        {
            long before = System.nanoTime();
            g.drawImage(image, 0, 0, null);
            long after = System.nanoTime();
            System.out.println("Painting "+(after-before)/1e6+" ms");
        }
    }
    
}

public class ShortToByteImageTest extends JPanel
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    
    private ImagePanel imagePanel;
    private BufferedImage shortImage;
    private short shortImageData[];
    private int w = 1700;
    private int h = 1700;
    
    public ShortToByteImageTest()
    {
        super(new BorderLayout());
        
        shortImage = createImageWithShortBuffer(w, h);
        shortImageData = getShortArray(shortImage);
        
        imagePanel = new ImagePanel();
        add(imagePanel, BorderLayout.CENTER);
        
        JPanel controlPanel = new JPanel(new GridLayout(2,0));

        JPanel pMin = new JPanel(new BorderLayout());
        final JSlider minSlider = new JSlider(
            -Short.MAX_VALUE, Short.MAX_VALUE, -Short.MAX_VALUE);
        pMin.add(new JLabel("min"), BorderLayout.WEST);
        pMin.add(minSlider, BorderLayout.CENTER);
        controlPanel.add(pMin);
        
        JPanel pMax = new JPanel(new BorderLayout());
        final JSlider maxSlider = new JSlider(
            -Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE);
        pMax.add(new JLabel("max"), BorderLayout.WEST);
        pMax.add(maxSlider, BorderLayout.CENTER);
        controlPanel.add(pMax);
        
        add(controlPanel, BorderLayout.SOUTH);
        
        ChangeListener changeListener = new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                int min = minSlider.getValue();
                int max = maxSlider.getValue();
                updateImage(min, max);
            }
        };
        minSlider.addChangeListener(changeListener);
        maxSlider.addChangeListener(changeListener);
    }
    
    
    private void updateImage(int min, int max)
    {
        long before = System.nanoTime();
        BufferedImage iImage = null;
        boolean combined = false;
        combined = true;
        if (combined)
        {
            iImage = createImageFromShortArrayNormalized(w, h, shortImageData, min, max);
        }
        else
        {
            byte bData[] = convertShortToByteArray(shortImageData, min, max);
            iImage = createImageFromByteArray(w, h, bData);
        }
        long after = System.nanoTime();
        System.out.println("Conversion "+(after-before)/1e6+" ms");
        
        imagePanel.setImage(iImage);
    }
    
    
    private static BufferedImage createImageWithShortBuffer(int w, int h)
    {
        BufferedImage image = new BufferedImage(w,h,BufferedImage.TYPE_USHORT_GRAY);
        short data[] = getShortArray(image);
        for (int i=0; i<data.length; i++)
        {
            data[i] = (short)i;
        }
        return image;
    }
    
    private static short[] getShortArray(BufferedImage image)
    {
        DataBuffer dataBuffer = image.getRaster().getDataBuffer();
        DataBufferUShort databufferUShort = (DataBufferUShort)dataBuffer;
        return databufferUShort.getData();
    }
    
    private static byte[] convertShortToByteArray(short input[], int min, int max)
    {
        byte output[] = new byte[input.length];
        float normalization = 1.0f / (max - min);
        for (int i=0; i<input.length; i++)
        {
            float f = (input[i]-min)*normalization;
            output[i] = (byte)(f * 256);
        }
        return output;
    }
    
    private static BufferedImage createImageFromByteArray(int w, int h, byte input[]) 
    {   
        int output[] = new int[input.length];
        for (int i = 0; i < input.length; i++) 
        {
            byte v = input[i];
            int a = 0xFF000000;
            int r = (v & 0xFF) << 16; 
            int g = (v & 0xFF) <<  8; 
            int b = (v & 0xFF) <<  0; 
            output[i] = a | r | g | b;
        }
        BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        result.setRGB(0,0, w, h, output, 0, w);
        return result;
    }
    
    private static BufferedImage createImageFromShortArrayNormalized(int w, int h, short input[], int min, int max)
    {
        int output[] = new int[input.length];
        float normalization = 1.0f / (max - min);
        for (int i = 0; i < input.length; i++) 
        {
            float f = (input[i]-min)*normalization;
            int v = (int)(f * 256);
            int a = 0xFF000000;
            int r = (v & 0xFF) << 16; 
            int g = (v & 0xFF) <<  8; 
            int b = (v & 0xFF) <<  0; 
            output[i] = a | r | g | b;
        }
        BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        result.setRGB(0,0, w, h, output, 0, w);
        return result;
    }
    
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new ShortToByteImageTest());
        f.setSize(1000,1000);
        f.setVisible(true);
    }
    
}

Brauchst du diesen zwischen-Byte-Array irgendwo, oder kann man direkt den short-Array in ein Bild verwandeln? Warum packst du den Byte-Array in einen DataBuffer?
 

Planich

Aktives Mitglied
1. Ja, 2. NEIN! es ist definitiv 8 Bit (DataBuffer.TYPE_BYTE). Welche Daten benötigst du denn nun?

Ich benötige im Prinzip die 16bit Daten des Bildes. Das Bild ansich ist mir egal. Bisher hole ich mir die Daten eben durch erstellen eines BI und dann über den Buffer, weil ich es nicht anders weiß

Dein 2. Punkt und deine Begründung dass es ungeeignet wäre (16 Bit obwohl du 16 Bit benötigst), sind mir nicht ganz klar, ich schliesse mal auf einen Flüchtigkeitsfehler.

Wieso unbegründet? Ich glaube wir reden ein wenig aneinander vorbei. Bei deiner Methode sieht es so aus als hätte ich dann eine Kopie meines 16 bit BufferedImages. Was nützt mir eine Kopie, wenn ich die 16bit Daten/Farbwerte brauche?

Du hättest damit, wie gewünscht, das Zeichnen in dein changedImage flotter bekommen, anders geht's nicht, weil "unmanaged".

Das Zeichnen des Images mit den int[] ARGB Werten in mein 8bit BufferedImage (changedImage) wäre flotter, wenn ich mir die Daten aus dem 16bit BufferedImage nicht über den Buffer hole? Dann wäre es interessant wie ich das verhindere bzw. hinbekomme.

Ok, da dir linear nicht reicht, was ja schon abzusehen war, bleibt immer noch ein BufferedImage per selbst implementiertem ColorModel und einem WritableRaster per [c]Raster.createInterleavedRaster(width, height, width, 1, new int[] {1}, DataBuffer.TYPE_BYTE)[/c] instanziert, zu erstellen. Die Puffer-Arrays sind für dich fortan bedeutungslos. Die Farbwerte der Originalbildes bekommst du von dort per [c]getData().getElements()[/c].
Ja linear reicht nicht. Und die Methoden die du da nennst, sind wieder komplettes Neuland für mich

Obwohl, wenn's nicht mehr gezeichnet werden soll, kannst dir auch den Puffer wie gewohnt beschaffen, das hat aber den Nachteil, dass du bei Farb-Indizierten Bildern (z.B. GIF) Indices statt Farbwerte bekommst.
Was denn jetzt nun? Du verwirrst mich? Das 16bit Bild zeichne ich nicht. Ich nutze es nur einmal am Anfang in dem ich es reinlade und mir den Buffer in einer Variable speichere. Ist denn nun das Zeichnen in mein 8bit Bild auch langsamer durch das unmanaged 16bit Bild oder nicht?

Mit diesen Elementen berechnest du deine Grenzen und übergibst diese per Konstruktor an dein selbst gebautes ColorModel in welchem dann sämtliche Farbberechnungen stattfinden. Mit diesem ColorModel dann schlicht ein neues BI erstellen und das Originalbild wie oben reinzeichnen. Wenn du das nicht hinbekommst, zeig mir mal die Methode, wie du an deine Ober- bzw. Untergrenzen kommst.
Soll das heißen das läuft dann alles über so ein RGB oder ARGB int []? Dann wird das alles ja noch langsamer weil die Arrays die ich durchlaufe 3 oder 4 mal so groß sind.

Marco13 hat gesagt.:
Ein KSKB ist das, was erscheint, wenn man mit der Maus über "KSKB" fährt.

Ich hab hier nirgends KSKB stehen wo ich mit der Maus drüber fahren kann? Brauch ich dazu ne spezielle IDE?

Marco13 hat gesagt.:
Brauchst du diesen zwischen-Byte-Array irgendwo, oder kann man direkt den short-Array in ein Bild verwandeln? Warum packst du den Byte-Array in einen DataBuffer?

Welchen zwischen-Byte Array?

Hier mal für euch meine Methode um das short-Array auf ein byte-Array mit meinen Grenzen runterzubrechen

Java:
public void shortToByte(short[] data, int ug, int og)
{           //Berechnen der Hälfte - Schnittpunkt Hälfte ist weiß und schwarz
            short half=(short)(((og-ug)>>1));
                       
            //float ratio = (og-ug)/256;
            short ratio =(short)( (og-ug));
            
            
            //-32768 ist grau|| -1 weiß|| 0 schwarz || 32767 grau 
            for (int i=0;i<data.length;i++)
            {
                if(data[i]>=ug&&data[i]<=og)
                {
                    //Schwarzstufen
                    if(data[i]<og-(half+1))                    
                    {                            
                        byteArr[i]=(byte)(((data[i]-ug)<<8)/ratio);                     
                    }
                    //Weißstufen     
                    else
                    {                    
                        byteArr[i]=(byte)(((data[i]-ug)<<8)/ratio-256);
                        //Falls Rundungsfehler (-0.3 wäre 0 und damit schwarz), manuelles setzen auf -1 ->entspricht weiß
                        if(byteArr[i]==0)
                        {
                            byteArr[i]=-1;
                        }
                    }
                }
                if(data[i]<ug)
                    byteArr[i]=0;//schwarz
                if(data[i]>og)
                    byteArr[i]=-1;//weiß  
            }       
}

Direkt das short-Array in ein Bild verwandeln geht nicht. Es läuft immer über diese eben genannte Methode. Das mit dem Databuffer ist gerade historisch so entstanden. Vor 2 Tagen war ja meine Methode das 8bitBufferedImage zu erstellen auch noch anders. Dort habe ich ja ein Raster mit ColorModel und Buffer gebastelt um dann das BI zu erstellen. Das könnte ich jetzt auch wieder anders machen. Ist mir gestern auch so eingefallen

Edit: das mit KSKB hat sich erledigt. Ich dachte das wäre irgend eine Funktion. Ich habs geschnallt
 
Zuletzt bearbeitet:

Planich

Aktives Mitglied
Wir haben ja viel geschrieben. Ich poste hier mal einen "KSKB" ich hoffe zumindest das ihr ihn verwenden könnt. Er ist jedoch nur mit 16bit Images nutzbar. Und nur für den Typ tiff bzw tif.

Java:
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecodeParam;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.PNGDecodeParam;
import com.sun.media.jai.codec.TIFFDecodeParam;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import java.awt.image.MemoryImageSource;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.swing.JComponent;

class OwnImage extends JComponent{

    private int ug, og;
    private int width,height;
    private BufferedImage originalImage=null;
    private BufferedImage changedImage=null;
    private int[] raw;
    private short[] data;    
    private byte[] byteArr;        
    private byte type;
    private int[] chartValue = new int[100];
    private boolean isPNG=false;
    private boolean isJPG=false;
    private short[] UgOgArr= new short[2];
	
public OwnImage(File file) throws IOException{
	
	//wandelt den Typ File in einen String um und macht alle Buchstaben klein
        String tempstring=file.toString().toLowerCase();
		
	if (tempstring.endsWith("tiff")||tempstring.endsWith("tif"))
        {
        TIFFDecodeParam param =null;    //TIFF
        //Tiff Decoder erstellen
        ImageDecoder dectiff=ImageCodec.createImageDecoder("TIFF", file, param);
        //Rendered Image erstellen
        RenderedImage ritiff=dectiff.decodeAsRenderedImage();
        //BI erstellen
        originalImage=convertRenderedImage(ritiff);        
        }
		//Das BufferedImage wird überprüft ob 8bit oder 16bit
        type=(byte)(originalImage.getData().getDataBuffer().getDataType());

		//**************************************************************************
        //bei 16bit
        //Anlegen eines neuen BufferedImages mit den selbst festgelegten Grenzen ug und og
        if(type==1&&!isJPG)
        {           
        //ein short[] aus dem BufferedImage erstellen, dass die Farbwerte beinhaltet
        data = ((DataBufferUShort) originalImage.getData().getDataBuffer()).getData();	
		
		//ein byte[] erstellen, dass so groß ist wie das short[]
        byteArr = new byte[data.length];
		
		//Herausfinden der Grenzen
        UgOgArr=minmax(data);
        
        ug=UgOgArr[0];
        og=UgOgArr[1];
		
		//Basierend auf ug und og werden die Farbwerte linear auf ein byte[] verteilt
        shortToByte(data,ug ,og );
		
		raw = new int[byteArr.length * 4];
         
        create8bitBufferedImage();
        }
		
		//den 8bit Teil des Codes lass ich mal weg, damit es nicht zu viel wird
		changedImage=convertToARGB(changedImage);
        originalImage=convertToARGB(originalImage);        
}

public BufferedImage convertRenderedImage(RenderedImage img) {
		if (img instanceof BufferedImage) {
			return (BufferedImage)img;	
		}	
		ColorModel cm = img.getColorModel();                
		width = img.getWidth();
		height = img.getHeight();
		WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
		boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
		Hashtable properties = new Hashtable();
		String[] keys = img.getPropertyNames();
		if (keys!=null) {
			for (int i = 0; i < keys.length; i++) {
				properties.put(keys[i], img.getProperty(keys[i]));
			}
		}
                changedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
		img.copyData(raster);
		return result;
	}

public void create8bitBufferedImage() {           
            for (int i = 0; i < byteArr.length / 3; i++) {
                raw[i] = 0xFF000000
                        | ((byteArr[i + 0] & 0xFF) << 16)
                        | ((byteArr[i + 1] & 0xFF) << 8)
                        | ((byteArr[i + 2] & 0xFF));
            } //31-46            
            Image im= createImage(new MemoryImageSource(width, height,raw , 0, width));  //0                   
            
            changedImage.getGraphics().drawImage(im, 0,0,width,height,null);     //47-63           
    }

public void shortToByte(short[] data, int ug, int og)
{           //Berechnen der Hälfte - Schnittpunkt Hälfte ist weiß und schwarz
            short half=(short)(((og-ug)>>1));
                       
            //float ratio = (og-ug)/256;
            short ratio =(short)( (og-ug));
            
            
            //-32768 ist grau|| -1 weiß|| 0 schwarz || 32767 grau 
            for (int i=0;i<data.length;i++)
            {
                if(data[i]>=ug&&data[i]<=og)
                {
                    //Schwarzstufen
                    if(data[i]<og-(half+1))                    
                    {                            
                        byteArr[i]=(byte)(((data[i]-ug)<<8)/ratio);                     
                    }
                    //Weißstufen     
                    else
                    {                    
                        byteArr[i]=(byte)(((data[i]-ug)<<8)/ratio-256);
                        //Falls Rundungsfehler (-0.3 wäre 0 und damit schwarz), manuelles setzen auf -1 ->entspricht weiß
                        if(byteArr[i]==0)
                        {
                            byteArr[i]=-1;
                        }
                    }
                }
                if(data[i]<ug)
                    byteArr[i]=0;//schwarz
                if(data[i]>og)
                    byteArr[i]=-1;//weiß  
            }       
}

public short[] minmax(short[] shortarr)
        {            
            short min=shortarr[0]; short max=shortarr[0];
            float ratio=(float) 655.36;
            
            for (int i=0;i<shortarr.length;i++)
            {
                //Für den Chart die Daten sammeln
                try{
                    
                chartValue[(int)(((shortarr[i]+32768)/ratio))]++;
                }
                catch(Exception ex)
                        {
                            
                        }
                if (shortarr[i]<min)
                    min=shortarr[i];
                if (shortarr[i]>max)
                    max=shortarr[i];
            }
            
            short [] shortArr = new short[]{min,max};              
            return shortArr;
        }

		//Grenzen setzen, shortToByte aufrufen und das aktuelle changedImage aktualisieren
        public void setUgOgDraw(int ug, int og)
        {            
            
            //bei 16bit
            if(type==1)
            {            
            this.ug=ug;
            this.og=og;            
            
            shortToByte(data,ug,og );            
            
            create8bitBufferedImage();               
            }            
        }		
		
		public BufferedImage getBI()
        {
            if(changedImage.getType()!=2)
            {                
                changedImage=convertToARGB(changedImage);                               
            }            
            return changedImage;
        }
		
		public BufferedImage convertToARGB(BufferedImage image)
		{
        if(image.getType()!=2)
        {
            BufferedImage newImage = new BufferedImage(
            image.getWidth(), image.getHeight(), 
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose(); 
        return newImage;
        }
        return image;
		}

Die Methode setUgOgDraw ist dazu da um z.B. von außen die Grenzen wieder zu aktualisieren und das ganze anzutriggern das Bild dementsprechend anzupassen.
 

Marco13

Top Contributor
Hmja... hat das, was ich gepostet hatte, geholfen? (Und sei es nur um Dinge wie
Java:
                catch(Exception ex)
                        {
                            
                        }
zu vermeiden :autsch: ) ?
 

bERt0r

Top Contributor
Also ich finde dein ColorModel etwas seltsam: 0=Schwarz, -1= Weiß und 32000=grau? Liegt wohl daran, dass du durch dein seltsames Einlesen des Images ein Custom Type BufferedImage bekommst.
Wie dir bereits vorgeschlagen wurde, es wäre sehr viel einfacher wenn du vor der ganzen Herunterrechnerei, die dir meiner Meinung nach eh nix bringt, das image in ein BufferedImage mit passendem Type umwandelst.
Wenns ein graues bild sein soll, na prima: BufferedImage.TYPE_BYTE_GRAY.
Schau mal, das ist ein KSKB. Selbstständig Kompilierbar heisst, dass ich es in ein Java-File copy/pasten, kompilieren und dann ausführen kann:
Java:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.Field;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;

public class ImageBsp extends JFrame
{
	
	private JPanel contentPane;
	private JFileChooser fileChooser;
	private JLabel lblOriginal;
	private JPanel panel;
	private JLabel lblShort;
	private JLabel lblGrey;
	private JScrollPane panel_1;
	private JScrollPane panel_2;
	private JScrollPane panel_3;
	private BufferedImage imgOriginal,imgShort,imgByteGrey;
	private JLabel lblType;
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable()
			{
				public void run()
				{
					try
					{
						ImageBsp frame = new ImageBsp();
						frame.setVisible(true);
					} catch (Exception e)
					{
						e.printStackTrace();
					}
				}
			});
	}
	
	/**
	 * Create the frame.
	 */
	public ImageBsp()
	{
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 600, 400);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		panel = new JPanel();
		contentPane.add(panel, BorderLayout.CENTER);
		panel.setLayout(new GridLayout(0, 3, 0, 0));
		
		panel_1 = new JScrollPane();
		panel.add(panel_1);
		
		lblOriginal = new JLabel();
		lblOriginal.setHorizontalAlignment(SwingConstants.CENTER);
		lblType=new JLabel("Original");
		lblType.setHorizontalAlignment(SwingConstants.CENTER);
		panel_1.setColumnHeaderView(lblType);
		panel_1.setViewportView(lblOriginal);
		
		panel_2 = new JScrollPane();
		panel.add(panel_2);
		
		lblShort = new JLabel();
		lblShort.setHorizontalAlignment(SwingConstants.CENTER);
		JLabel lblType2 = new JLabel("Short (TYPE_USHORT_555_RGB)");
		lblType2.setHorizontalAlignment(SwingConstants.CENTER);
		panel_2.setColumnHeaderView(lblType2);
		panel_2.setViewportView(lblShort);
		
		panel_3 = new JScrollPane();
		panel.add(panel_3);
		
		lblGrey = new JLabel()
		{
			@Override
			public void paintComponent(Graphics g)
			{
				long start=System.nanoTime();
				super.paintComponent(g);
				long end=System.nanoTime();
				System.out.println("Time to paint: "+(end-start)+" ns");
			}
		};
		lblGrey.setHorizontalAlignment(SwingConstants.CENTER);
		JLabel lblType3 = new JLabel("byte (TYPE_BYTE_GRAY)");
		lblType3.setHorizontalAlignment(SwingConstants.CENTER);
		panel_3.setColumnHeaderView(lblType3);
		panel_3.setViewportView(lblGrey);
		
		JButton btnLoadImage = new JButton("Load Image");
		contentPane.add(btnLoadImage, BorderLayout.SOUTH);
		
		btnLoadImage.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent arg0)
				{
					int ret = fileChooser.showOpenDialog(ImageBsp.this);
					if (ret == JFileChooser.APPROVE_OPTION)
					{
						try
						{
							imgOriginal = ImageIO.read(fileChooser.getSelectedFile());
							imgShort=toShortImg(imgOriginal);
							imgByteGrey=toByteGreyImg(imgShort);
							
							int type=imgOriginal.getType();
							String name="";
							for (Field f:BufferedImage.class.getDeclaredFields())
							{
								if(f.getName().startsWith("TYPE"))
								{
									try
									{
										if(f.getInt(imgOriginal)==type)
										{
											name=f.getName();
										}
									} catch (IllegalArgumentException e)
									{
										e.printStackTrace();
									} catch (IllegalAccessException e)
									{
										e.printStackTrace();
									}
								}
							}
							
							lblType.setText("Original ("+name+")");
							lblOriginal.setIcon(new ImageIcon(imgOriginal));
							lblShort.setIcon(new ImageIcon(imgShort));
							lblGrey.setIcon(new ImageIcon(imgByteGrey));
							
						} catch (IOException e)
						{
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			});
		
		fileChooser = new JFileChooser();
	}
	
	public BufferedImage toShortImg(Image img)
	{
		BufferedImage shortImg = new BufferedImage(img.getWidth(null), img.getHeight(null),
				BufferedImage.TYPE_USHORT_555_RGB);
		Graphics g = shortImg.getGraphics();
		g.drawImage(img, 0, 0, null);
		g.dispose();
		return shortImg;
	}
	
	public BufferedImage toByteGreyImg(Image img)
	{
		BufferedImage greyImg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_BYTE_GRAY);
		Graphics g = greyImg.getGraphics();
		g.drawImage(img, 0, 0, null);
		g.dispose();
		return greyImg;
	}
}
 
S

Spacerat

Gast
So, ich hab' jetzt mal ein KSKB gebaut, in welchem ich mal meinen Weg aufzeigen will. Leider habe ich deine Farbumrechnungen nicht ganz nachvollziehen können und ausserdem wird intern ohnehin stets mit 32-Bit ARGB gerechnet, von daher ist das Bildeingangsformat nunmehr egal. Kannst es dir ja mal ansehen...
Java:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.ImageObserver;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileNotFoundException;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import datatypes.DataType;
import datatypes.imaging.Image;


public class OwnPanel
extends JComponent
{
	private static final long serialVersionUID = 1L;

	private final OwnImage img;
	private final ImageObserver obs = new ImageObserver()
	{
		@Override
		public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height)
		{
			repaint();
			return false;
		}
	};

	private OwnPanel(OwnImage img)
	{
		super();
		if(img == null) {
			throw new NullPointerException("img may not be null");
		}
		this.img = img;
		Dimension size = new Dimension(img.getWidth(), img.getHeight());
		setMinimumSize(new Dimension(1, 1));
		setPreferredSize(size);
		setSize(size);
	}

	@Override
	protected void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		g.drawImage(img, 0, 0, getWidth(), getHeight(), obs);
	}

	public static void main(String[] args)
	{
		if(args == null || args.length == 0) {
			args = new String[] {"GW236H236.png"};
		}
		JFrame f = new JFrame("OwnImageTest");
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Container cp = f.getContentPane();
		final JSlider upper = new JSlider(JSlider.VERTICAL);
		final JSlider lower = new JSlider(JSlider.VERTICAL);
		final OwnPanel op = new OwnPanel(new OwnImage(new File(args[0]).getAbsoluteFile()));
		cp.setLayout(new BorderLayout());
		cp.add(op, BorderLayout.CENTER);
		cp.add(upper, BorderLayout.WEST);
		cp.add(lower, BorderLayout.EAST);
		upper.setMaximum(0xFFFFFF);
		upper.setMinimum(0);
		upper.addChangeListener(new ChangeListener()
		{
			@Override
			public void stateChanged(ChangeEvent e)
			{
				op.img.setOgDraw(upper.getValue());
				op.repaint();
			}
		});
		lower.setMaximum(0xFFFFFF);
		lower.setMinimum(0);
		lower.addChangeListener(new ChangeListener()
		{
			@Override
			public void stateChanged(ChangeEvent e)
			{
				op.img.setUgDraw(lower.getValue());
				op.repaint();
			}
		});
		f.pack();
		f.setVisible(true);
	}
}

class OwnImage
extends BufferedImage
{
	private final BufferedImage original;

	public OwnImage(File f)
	{
		this(new Loader(f));
	}

	private OwnImage(Loader l)
	{
		super(l.cm, l.data, false, null);
		original = l.originalImage;
		getGraphics().drawImage(l.originalImage, 0, 0, null);
	}

	public synchronized void setUgDraw(int ug)
	{
		((OwnColorModel) getColorModel()).setUg(ug);
		getGraphics().drawImage(original, 0, 0, null);
	}

	public synchronized void setOgDraw(int og)
	{
		((OwnColorModel) getColorModel()).setOg(og);
		getGraphics().drawImage(original, 0, 0, null);
	}

	public synchronized void setUgOgDraw(int ug, int og)
	{
		((OwnColorModel) getColorModel()).setBorders(ug, og);
		getGraphics().drawImage(original, 0, 0, null);
	}

	private static class Loader
	{
		private ColorModel cm;
		private WritableRaster data;
		private BufferedImage originalImage;

		private Loader(File f)
		{
			try {
				originalImage = (Image) DataType.getFile(f);
			} catch(FileNotFoundException e) {
				e.printStackTrace();
				return;
			}
			int min = Integer.MAX_VALUE;
			int max = Integer.MIN_VALUE;
			Raster r = originalImage.getData();
			ColorModel scm = originalImage.getColorModel();
			Object buffer = null;
			switch(r.getTransferType()) {
			case DataBuffer.TYPE_BYTE:
				buffer = new byte[r.getNumDataElements()];
				break;
			case DataBuffer.TYPE_SHORT:
			case DataBuffer.TYPE_USHORT:
				buffer = new short[r.getNumDataElements()];
				break;
			case DataBuffer.TYPE_INT:
				buffer = new int[r.getNumDataElements()];
				break;
			default:
				throw new IllegalArgumentException("invalid type");
			}
			int w = r.getWidth();
			int h = r.getHeight();
			int c = max;
			for(int x = 0; x < w; x++) {
				for(int y = 0; y < h; y++) {
					c = scm.getRGB(r.getDataElements(x, y, buffer)) & 0xFFFFFF;
					if(min > c) {
						min = c;
					}
					if(max < c) {
						max = c;
					}
				}
			}
			data = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, w, 1, new int[] {1}, new Point());
			cm = new OwnColorModel(min, max);
		}
	}

	private static class OwnColorModel
	extends ColorModel
	{
		private int og, ug;
		private double ratio;

		public OwnColorModel(int ug, int og)
		{
			super(8);
			setBorders(ug, og);
		}

		@Override
		public boolean isCompatibleRaster(Raster raster)
		{
			return true;
		}

		@Override
		public int getRed(int pixel)
		{
			return pixel & 0xFF;
		}

		@Override
		public int getGreen(int pixel)
		{
			return pixel & 0xFF;
		}

		@Override
		public int getBlue(int pixel)
		{
			return pixel & 0xFF;
		}

		@Override
		public int getAlpha(int pixel)
		{
			return 0xFF;
		}

		@Override
		public Object getDataElements(int rgb, Object pixel)
		{
			rgb = calcColor(rgb);
			if(pixel == null) {
				pixel = new byte[1];
			}
			if(pixel instanceof byte[]) {
				((byte[]) pixel)[0] = (byte) (rgb & 0xFF);
			} else if(pixel instanceof short[]) {
				((short[]) pixel)[0] = (short) (rgb & 0xFFFF);
			} else if(pixel instanceof int[]) {
				((int[]) pixel)[0] = rgb;
			}
			return pixel;
		}

		private void setUg(int ug)
		{
			setBorders(ug, og);
		}

		private void setOg(int og)
		{
			setBorders(ug, og);
		}

		private void setBorders(int ug, int og)
		{
			this.ug = Math.min(ug, og);
			this.og = Math.max(ug, og);
			ratio = this.og - this.ug;
		}

		/**
		 * Im Parameter "pixel" wird bei Bildern die nicht farbindiziert sind, wie in
		 * diesem Fall, direkt der Farbwert übergeben.
		 * Abhängig vom Typ des Quellbildes kann daraus der Bytewert wie im Beispiel
		 * errechnet werden.
		 * Das Beispiel ist äquivalent zu deinem geposteten Code.
		 * In dieser Version wird "calcColor()" noch 4 mal pro Pixel aufgerufen. Ob sich
		 * das ändern lässt, weis ich bisher noch nicht (liegt am Graphics-Object).
		 * @param pixel
		 * @return
		 */
		private final int calcColor(int pixel)
		{
			int rc;
			int alpha = pixel & 0xFF000000;
			pixel &= 0xFFFFFF;
			if(pixel >= ug && pixel <= og) {
				rc = (int) (((pixel - ug) / ratio) * 255.0);
			}
			else if(pixel < ug) {
				rc = 0;//schwarz
			}
			else {
				rc = 0xFF;//weiß  
			}
			return alpha | rc << 16 | rc << 8 | rc;
		}
	}
}
[EDIT]Muss noch anmerken, dass ich zum Laden des Bildes meine DatatypesLibrary verwendet habe. Die Zeile mit [c]DataTypes.getFile()[/c] (147) müsstest du also in eine JAI passende Laderoutine umschreiben.[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:

Planich

Aktives Mitglied
Also ich finde dein ColorModel etwas seltsam: 0=Schwarz, -1= Weiß und 32000=grau? Liegt wohl daran, dass du durch dein seltsames Einlesen des Images ein Custom Type BufferedImage bekommst.
Wie dir bereits vorgeschlagen wurde, es wäre sehr viel einfacher wenn du vor der ganzen Herunterrechnerei, die dir meiner Meinung nach eh nix bringt, das image in ein BufferedImage mit passendem Type umwandelst.
Wenns ein graues bild sein soll, na prima: BufferedImage.TYPE_BYTE_GRAY.
Schau mal, das ist ein KSKB. Selbstständig Kompilierbar heisst, dass ich es in ein Java-File copy/pasten, kompilieren und dann ausführen kann:

Hi,
erstmal danke für deine Mühe
Zwei Dinge die mir sofort auffallen
1) so wie du das Image einliest, hat man Probleme bei manchen Formaten. Z.B. bei Tiffs
Das ist einer der Gründe warum ich meine Images so "komisch" einlese. Die Standard Methoden sind da nicht ganz geeignet.
2) Bei deinen Umwandlungen fällt mir auf, dass wohl sehr viele Farbinformationen fehlen. Da ist ein Farbübergang als Treppe erkennbar.


Mir ist jetzt auch noch nicht wirklich ein Vorteil ersichtlich muss ich ehrlich sagen
 

Planich

Aktives Mitglied
Hmja... hat das, was ich gepostet hatte, geholfen? ?

Also ich habe irgendwie nicht merklich Unterschiede festgestellt. Außer das dein Code sehr viel besser geschrieben ist.


Kann gut sein, dass ich irgendwas übersehe, aber im Prinzip ist es doch komplett das gleiche oder nicht?

Das was du nicht drin hast, ist das Gewurschtel von mir, dass dadurch entsteht, jedes x beliebige Format überhaupt erstmal einlesen zu können. Das ist auch bei bertor der Fall.

Ein Image File überhaupt erstmal reinzukriegen ist schon ne Menge Frickelkram, je nach Library. Ich werd mal schauen ob ich die Files anders reinkriege. Sodass sie dann vielleicht vorliegen als könne man sie gleich benutzen :lach:
 

Planich

Aktives Mitglied
So, ich hab' jetzt mal ein KSKB gebaut, in welchem ich mal meinen Weg aufzeigen will. Leider habe ich deine Farbumrechnungen nicht ganz nachvollziehen können und ausserdem wird intern ohnehin stets mit 32-Bit ARGB gerechnet, von daher ist das Bildeingangsformat nunmehr egal. Kannst es dir ja mal ansehen...
Also ich habe eben mitbekommen, dass das RenderedImage, dass ich bei meinen Laderoutinen bekomme, ein raster und ColorModel liefern kann.

Allerdings erhalte ich beim Laden des Bildes ein komplett schwarzes Bild
Hier mal der Code falls es hilft

Java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package spacerat;

import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.TIFFDecodeParam;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.ImageObserver;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileNotFoundException;
 
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
 

import java.awt.image.*;
import java.io.IOException;
 
 
public class OwnPanel
extends JComponent
{
    private static final long serialVersionUID = 1L;
 
    private final OwnImage img;
    private final ImageObserver obs = new ImageObserver()
    {
        @Override
        public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height)
        {
            repaint();
            return false;
        }
    };
 
    private OwnPanel(OwnImage img)
    {
        super();
        if(img == null) {
            throw new NullPointerException("img may not be null");
        }
        this.img = img;
        Dimension size = new Dimension(img.getWidth(), img.getHeight());
        setMinimumSize(new Dimension(1, 1));
        setPreferredSize(size);
        setSize(size);
    }
 
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, getWidth(), getHeight(), obs);
    }
 
    public static void main(String[] args) throws IOException
    {
        if(args == null || args.length == 0) {
            args = new String[] {"tiff16bit.tiff"};
        }
        JFrame f = new JFrame("OwnImageTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container cp = f.getContentPane();
        final JSlider upper = new JSlider(JSlider.VERTICAL);
        final JSlider lower = new JSlider(JSlider.VERTICAL);
        final OwnPanel op = new OwnPanel(new OwnImage(new File(args[0]).getAbsoluteFile()));
        cp.setLayout(new BorderLayout());
        cp.add(op, BorderLayout.CENTER);
        cp.add(upper, BorderLayout.WEST);
        cp.add(lower, BorderLayout.EAST);
        upper.setMaximum(0xFFFFFF);
        upper.setMinimum(0);
        upper.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                op.img.setOgDraw(upper.getValue());
                op.repaint();
            }
        });
        lower.setMaximum(0xFFFFFF);
        lower.setMinimum(0);
        lower.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                op.img.setUgDraw(lower.getValue());
                op.repaint();
            }
        });
        f.pack();
        f.setVisible(true);
    }
}
 
class OwnImage
extends BufferedImage
{
    private final BufferedImage original;
 
    public OwnImage(File f) throws IOException
    {
        this(new Loader(f));
    }
 
    private OwnImage(Loader l)
    {
        super(l.cm, l.data, false, null);
        original = l.originalImage;
        getGraphics().drawImage(l.originalImage, 0, 0, null);
    }
 
    public synchronized void setUgDraw(int ug)
    {
        ((OwnColorModel) getColorModel()).setUg(ug);
        getGraphics().drawImage(original, 0, 0, null);
    }
 
    public synchronized void setOgDraw(int og)
    {
        ((OwnColorModel) getColorModel()).setOg(og);
        getGraphics().drawImage(original, 0, 0, null);
    }
 
    public synchronized void setUgOgDraw(int ug, int og)
    {
        ((OwnColorModel) getColorModel()).setBorders(ug, og);
        getGraphics().drawImage(original, 0, 0, null);
    }
 
    private static class Loader
    {
        private ColorModel cm;
        private WritableRaster data;
        private BufferedImage originalImage;
        private RenderedImage ritiff;
        private Loader(File f) throws IOException
        {
            try {
            TIFFDecodeParam param =null;   
            //Tiff Decoder erstellen
            ImageDecoder dectiff=ImageCodec.createImageDecoder("TIFF", f, param);
            //Rendered Image erstellen
            ritiff=dectiff.decodeAsRenderedImage();
            } catch(FileNotFoundException e) {
                e.printStackTrace();
                return;
            }
            
            
            
            int min = Integer.MAX_VALUE;
            int max = Integer.MIN_VALUE;
            
            Raster r = ritiff.getData();
            ColorModel scm = ritiff.getColorModel();
            Object buffer = null;
            
            
            switch(r.getTransferType()) {
            case DataBuffer.TYPE_BYTE:
                buffer = new byte[r.getNumDataElements()];
                break;
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_USHORT:
                buffer = new short[r.getNumDataElements()];
                break;
            case DataBuffer.TYPE_INT:
                buffer = new int[r.getNumDataElements()];
                break;
            default:
                throw new IllegalArgumentException("invalid type");
            }
            
            
            
            
            
            int w = r.getWidth();
            int h = r.getHeight();
            
            
            int c = max;
            for(int x = 0; x < w; x++) {
                for(int y = 0; y < h; y++) {
                    c = scm.getRGB(r.getDataElements(x, y, buffer)) & 0xFFFFFF;
                    if(min > c) {
                        min = c;
                    }
                    if(max < c) {
                        max = c;
                    }
                }
            }
            data = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, w, 1, new int[] {1}, new Point());
            cm = new OwnColorModel(min, max);
        }
    }
 
    private static class OwnColorModel
    extends ColorModel
    {
        private int og, ug;
        private double ratio;
 
        public OwnColorModel(int ug, int og)
        {
            super(8);
            setBorders(ug, og);
        }
 
        @Override
        public boolean isCompatibleRaster(Raster raster)
        {
            return true;
        }
 
        @Override
        public int getRed(int pixel)
        {
            return pixel & 0xFF;
        }
 
        @Override
        public int getGreen(int pixel)
        {
            return pixel & 0xFF;
        }
 
        @Override
        public int getBlue(int pixel)
        {
            return pixel & 0xFF;
        }
 
        @Override
        public int getAlpha(int pixel)
        {
            return 0xFF;
        }
 
        @Override
        public Object getDataElements(int rgb, Object pixel)
        {
            rgb = calcColor(rgb);
            if(pixel == null) {
                pixel = new byte[1];
            }
            if(pixel instanceof byte[]) {
                ((byte[]) pixel)[0] = (byte) (rgb & 0xFF);
            } else if(pixel instanceof short[]) {
                ((short[]) pixel)[0] = (short) (rgb & 0xFFFF);
            } else if(pixel instanceof int[]) {
                ((int[]) pixel)[0] = rgb;
            }
            return pixel;
        }
 
        private void setUg(int ug)
        {
            setBorders(ug, og);
        }
 
        private void setOg(int og)
        {
            setBorders(ug, og);
        }
 
        private void setBorders(int ug, int og)
        {
            this.ug = Math.min(ug, og);
            this.og = Math.max(ug, og);
            ratio = this.og - this.ug;
        }
 
        /**
         * Im Parameter "pixel" wird bei Bildern die nicht farbindiziert sind, wie in
         * diesem Fall, direkt der Farbwert übergeben.
         * Abhängig vom Typ des Quellbildes kann daraus der Bytewert wie im Beispiel
         * errechnet werden.
         * Das Beispiel ist äquivalent zu deinem geposteten Code.
         * In dieser Version wird "calcColor()" noch 4 mal pro Pixel aufgerufen. Ob sich
         * das ändern lässt, weis ich bisher noch nicht (liegt am Graphics-Object).
         * @param pixel
         * @return
         */
        private final int calcColor(int pixel)
        {
            int rc;
            int alpha = pixel & 0xFF000000;
            pixel &= 0xFFFFFF;
            if(pixel >= ug && pixel <= og) {
                rc = (int) (((pixel - ug) / ratio) * 255.0);
            }
            else if(pixel < ug) {
                rc = 0;//schwarz
            }
            else {
                rc = 0xFF;//weiß  
            }
            return alpha | rc << 16 | rc << 8 | rc;
        }
    }
}
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Ich kapier's noch nicht so ganz. Das, was ich gepostet hatte, waren ein paar statische standalone-Utility-Methoden, die man eigentlich einfach so verwenden kann - warum du jetzt mit ColorModel rummachst, erschließt sich mir nicht ganz. Von der JAI lib bekommst du ein RenderedImage. Davon kannst du dir das Raster und die Daten abholen, als short[] Array. Und von da ab sollten es die geposteten Methoden doch tun...?!
 

Planich

Aktives Mitglied
Ja genau das habe ich jetzt mitbekommen. Nur wie ich das Raster und das ColorModel so verwende, dass ich am Ende ein für mich geeignetes BufferedImage habe, erschließt sich für mich nicht

Edit: ich glaube ich habe einen Ansatz, ich meld mich dann nochmal
 
Zuletzt bearbeitet:

Planich

Aktives Mitglied
Ne ich glaub echt ich raffs nicht.
Kann mir jemand das Raster und das Color Model erklären?

Wo stehen denn jetzt meine Farbinformationen?

Das beste wäre doch wenn ich das jetzt richtig verstanden habe, mittels JAI zu dekodieren, dann habe ich mein RenderedImage und somit ein Raster und ColorModel.

Wie komme ich dann an die Daten, sodass ich mittels diesem Code mein BufferedImage erstellen kann:

Java:
for (int i = 0; i < byteArr.length / 3; i++) {
                raw[i] = 0xFF000000
                        | ((byteArr[i + 0] & 0xFF) << 16)
                        | ((byteArr[i + 1] & 0xFF) << 8)
                        | ((byteArr[i + 2] & 0xFF));
            } //31-46            
            Image im= createImage(new MemoryImageSource(width, height,raw , 0, width));  //0
            changedImage.getGraphics().drawImage(im, 0,0,width,height,null);     //47-63

dann müsste ich nur wissen wie ich an raw komme. Oder ist das schlecht?
changedImage würde ich dann einfach vorher als Type ARGB erstellen mit der passenden Größe. Sonst brauch ich ja eigentlich nichts?
 

Marco13

Top Contributor
Vielleicht kann Spacerat etwas dazu sagen, was entweder hinausgeht oder verdaulicher oder fokussierter ist, als das, was z.B. unter Learning Java 2D, Part 2 steht (der Link ist im Moment anscheinend tot, notfalls mit dem Google Übersetzer Cache lesen)

Wie auch immer, hier nochmal das KSKB, wo man grundsätzlich an der mit "TODO" markierten Stelle einfach das RenderedImage zurückgeben können sollte, das mit JAI eingelesen wurde....

Java:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferUShort;
import java.awt.image.RenderedImage;
 
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
 
 
class ImagePanel extends JPanel
{
    private BufferedImage image;
    
    public ImagePanel()
    {
    }
    
    public void setImage(BufferedImage image)
    {
        this.image = image;
        repaint();
    }
    
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (image != null)
        {
            long before = System.nanoTime();
            g.drawImage(image, 0, 0, null);
            long after = System.nanoTime();
            System.out.println("Painting "+(after-before)/1e6+" ms");
        }
    }
    
}
 
public class ShortToByteImageTest extends JPanel
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    
    private ImagePanel imagePanel;
    private RenderedImage shortImage;
    private short shortImageData[];
    
    public ShortToByteImageTest()
    {
        super(new BorderLayout());
        
        shortImage = obtainImageWithShortBufferFromSomewhere();
        shortImageData = getShortArray(shortImage);
        
        imagePanel = new ImagePanel();
        add(imagePanel, BorderLayout.CENTER);
        
        JPanel controlPanel = new JPanel(new GridLayout(2,0));
 
        JPanel pMin = new JPanel(new BorderLayout());
        final JSlider minSlider = new JSlider(
            -Short.MAX_VALUE, Short.MAX_VALUE, -Short.MAX_VALUE);
        pMin.add(new JLabel("min"), BorderLayout.WEST);
        pMin.add(minSlider, BorderLayout.CENTER);
        controlPanel.add(pMin);
        
        JPanel pMax = new JPanel(new BorderLayout());
        final JSlider maxSlider = new JSlider(
            -Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE);
        pMax.add(new JLabel("max"), BorderLayout.WEST);
        pMax.add(maxSlider, BorderLayout.CENTER);
        controlPanel.add(pMax);
        
        add(controlPanel, BorderLayout.SOUTH);
        
        ChangeListener changeListener = new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                int min = minSlider.getValue();
                int max = maxSlider.getValue();
                updateImage(shortImage.getWidth(), shortImage.getHeight(), min, max);
            }
        };
        minSlider.addChangeListener(changeListener);
        maxSlider.addChangeListener(changeListener);
    }
    
    
    private void updateImage(int w, int h, int min, int max)
    {
        long before = System.nanoTime();
        BufferedImage iImage = null;
        boolean combined = false;
        combined = true;
        if (combined)
        {
            iImage = createImageFromShortArrayNormalized(w, h, shortImageData, min, max);
        }
        else
        {
            byte bData[] = convertShortToByteArray(shortImageData, min, max);
            iImage = createImageFromByteArray(w, h, bData);
        }
        long after = System.nanoTime();
        System.out.println("Conversion "+(after-before)/1e6+" ms");
        
        imagePanel.setImage(iImage);
    }

    
    private static RenderedImage obtainImageWithShortBufferFromSomewhere()
    {
    	// TODO: Replace this with the code for loading a TIFF from JAI
    	return createImageWithShortBuffer(1000, 1000);
    }
    
    
    private static RenderedImage createImageWithShortBuffer(int w, int h)
    {
        BufferedImage image = new BufferedImage(w,h,BufferedImage.TYPE_USHORT_GRAY);
        DataBuffer dataBuffer = image.getRaster().getDataBuffer();
        DataBufferUShort databufferUShort = (DataBufferUShort)dataBuffer;
        short data[] = databufferUShort.getData();
        for (int i=0; i<data.length; i++)
        {
            data[i] = (short)i;
        }
        return image;
    }
    
    private static short[] getShortArray(RenderedImage image)
    {
        DataBuffer dataBuffer = image.getData().getDataBuffer();
        DataBufferUShort databufferUShort = (DataBufferUShort)dataBuffer;
        return databufferUShort.getData();
    }
    
    private static byte[] convertShortToByteArray(short input[], int min, int max)
    {
        byte output[] = new byte[input.length];
        float normalization = 1.0f / (max - min);
        for (int i=0; i<input.length; i++)
        {
            float f = (input[i]-min)*normalization;
            output[i] = (byte)(f * 256);
        }
        return output;
    }
    
    private static BufferedImage createImageFromByteArray(int w, int h, byte input[]) 
    {   
        int output[] = new int[input.length];
        for (int i = 0; i < input.length; i++) 
        {
            byte v = input[i];
            int a = 0xFF000000;
            int r = (v & 0xFF) << 16; 
            int g = (v & 0xFF) <<  8; 
            int b = (v & 0xFF) <<  0; 
            output[i] = a | r | g | b;
        }
        BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        result.setRGB(0,0, w, h, output, 0, w);
        return result;
    }
    
    private static BufferedImage createImageFromShortArrayNormalized(int w, int h, short input[], int min, int max)
    {
        int output[] = new int[input.length];
        float normalization = 1.0f / (max - min);
        for (int i = 0; i < input.length; i++) 
        {
            float f = (input[i]-min)*normalization;
            int v = (int)(f * 256);
            int a = 0xFF000000;
            int r = (v & 0xFF) << 16; 
            int g = (v & 0xFF) <<  8; 
            int b = (v & 0xFF) <<  0; 
            output[i] = a | r | g | b;
        }
        BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        result.setRGB(0,0, w, h, output, 0, w);
        return result;
    }
    
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new ShortToByteImageTest());
        f.setSize(1000,1000);
        f.setVisible(true);
    }
    
}
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
H Transparent zeichnen mit drawImage in paintComponent Methode AWT, Swing, JavaFX & SWT 3
J drawImage Fehlersuche AWT, Swing, JavaFX & SWT 5
U drawImage mit EPS AWT, Swing, JavaFX & SWT 0
A Problem mit drawImage AWT, Swing, JavaFX & SWT 1
M Graphics.drawImage von unten nach oben abbilden lassen AWT, Swing, JavaFX & SWT 6
L Graphics.drawImage() - Output-Größe entspricht nicht Parametern AWT, Swing, JavaFX & SWT 10
L Border verschwindet durch Graphics.drawImage() AWT, Swing, JavaFX & SWT 4
P Swing Skalieren mit DrawImage macht Linien kaputt AWT, Swing, JavaFX & SWT 6
G .ico drawImage AWT, Swing, JavaFX & SWT 5
B drawImage funktioniert nicht AWT, Swing, JavaFX & SWT 4
B drawImage auf JPanel bleibt ohne Auswirkungen AWT, Swing, JavaFX & SWT 9
K Graphics.drawImage() sehr schnell AWT, Swing, JavaFX & SWT 5
M Graphics.drawImage verlangsamt sich plötzlich AWT, Swing, JavaFX & SWT 15
0 AWT Graphics2D.drawImage() funktioniert nicht mehr korrekt mit Core i7 AWT, Swing, JavaFX & SWT 4
G Graphics.drawImage() AWT, Swing, JavaFX & SWT 6
? Problem mit drawImage: bei Frame ok, bei JPanel nicht AWT, Swing, JavaFX & SWT 4
F Problem mit drawImage() AWT, Swing, JavaFX & SWT 6
M drawImage bremst GUI AWT, Swing, JavaFX & SWT 2
I drawImage AWT, Swing, JavaFX & SWT 4
B drawImage() hängt! AWT, Swing, JavaFX & SWT 18
O performance g2d.drawImage() AWT, Swing, JavaFX & SWT 17
L Bildbewegung mit g.drawImage AWT, Swing, JavaFX & SWT 3
K g.DrawImage unter paintComponent klappt nur beim 1. Aufruf AWT, Swing, JavaFX & SWT 3
S kurze Frage zu drawImage AWT, Swing, JavaFX & SWT 12
F Endlosschleife bei drawImage() AWT, Swing, JavaFX & SWT 4
L Gezeichnetes Image mit DrawImage überzeichnen AWT, Swing, JavaFX & SWT 3
M drawImage mit seltsamen verhalten AWT, Swing, JavaFX & SWT 2
D graphische Ausgabe zu langsam (vsync gzielt abschaltbar?)... AWT, Swing, JavaFX & SWT 13
E Java-TexturePaint sehr langsam AWT, Swing, JavaFX & SWT 9
Tommy135 JFileChooser ist sehr langsam AWT, Swing, JavaFX & SWT 13
M Swing GUI wird nach invokeLater() langsam AWT, Swing, JavaFX & SWT 19
D JavaFX GUI Komponenten werden langsam bei größerer Datenmenge AWT, Swing, JavaFX & SWT 6
J JavaFX Rendering von Canvas sehr langsam AWT, Swing, JavaFX & SWT 2
C Swing GUI extrem langsam - GUI-Code richtig ausführen AWT, Swing, JavaFX & SWT 1
L [Slick2d] Sidescroller/Hintergrundbild sehr langsam AWT, Swing, JavaFX & SWT 3
S Swing JtextPane sau langsam AWT, Swing, JavaFX & SWT 15
P JFrame langsam / seltsames Verhalten AWT, Swing, JavaFX & SWT 6
X JInternalFrame vor Java2D-Zeichnung langsam bzw. Gui friert ein AWT, Swing, JavaFX & SWT 1
H Swing JScrollPane mit "viel Inhalt" scrollt zu langsam (inkl. See-For-Yourself.jar :D) AWT, Swing, JavaFX & SWT 2
D Image soll langsam sichtbar werden AWT, Swing, JavaFX & SWT 4
M JTable mit wechselnden Spalten - sehr Langsam AWT, Swing, JavaFX & SWT 5
A HELP: JFieldText dynamisch setzen -> langsam AWT, Swing, JavaFX & SWT 19
O RandomAccesFile langsam AWT, Swing, JavaFX & SWT 6
lumo AWT Screenshots machen ist langsam? AWT, Swing, JavaFX & SWT 6
J JApplet langsam wegen vielen Tooltips? AWT, Swing, JavaFX & SWT 36
R Image laden sehr langsam AWT, Swing, JavaFX & SWT 7
F Swing JTable langsam AWT, Swing, JavaFX & SWT 13
Kr0e VolatileImage langsam AWT, Swing, JavaFX & SWT 10
A repaint() zu langsam, bitte um alternativen AWT, Swing, JavaFX & SWT 5
A Swing JTextPane sehr langsam AWT, Swing, JavaFX & SWT 6
R TableRowSorter... zu langsam AWT, Swing, JavaFX & SWT 9
Stillmatic JTextPane langsam? AWT, Swing, JavaFX & SWT 5
R JTable für sehr viele Daten sehr langsam AWT, Swing, JavaFX & SWT 20
PAX JList aktualisiert zu langsam beim Hinzufügen von Einträgen AWT, Swing, JavaFX & SWT 6
G JScrollPane scrollt zu langsam AWT, Swing, JavaFX & SWT 6
S Bilder werden sehr langsam geladen AWT, Swing, JavaFX & SWT 4
M jFileChooser extrem langsam AWT, Swing, JavaFX & SWT 15
G Swing Programmstart zu langsam AWT, Swing, JavaFX & SWT 3
J JFileChooser öffnet sich in manchen Fällen extrem langsam! AWT, Swing, JavaFX & SWT 12
D Scrollbalken zu langsam AWT, Swing, JavaFX & SWT 10
S Programm aufgrund von paint() zu langsam AWT, Swing, JavaFX & SWT 18
doctus img.getScaledInstance() sehr rechenintensiv und langsam? AWT, Swing, JavaFX & SWT 3
T Linie langsam zeichnen AWT, Swing, JavaFX & SWT 3
C JButton + JFrame Reaktion SEHR langsam. AWT, Swing, JavaFX & SWT 2
J Double-Buffering zu langsam AWT, Swing, JavaFX & SWT 4
A Warum ist jtable.addRowSelectionIntervall so langsam? AWT, Swing, JavaFX & SWT 10
T Swing bei Realtime-Aktualisierung zu langsam? AWT, Swing, JavaFX & SWT 10
C TreeModel zu langsam für EventDispatchThread AWT, Swing, JavaFX & SWT 5

Ähnliche Java Themen

Neue Themen


Oben