2D-Grafik Speicherprobleme große Tiff-Dateien

El_Loko

Mitglied
Hallo zusammen,

ich bin gerade dabei ein kleines Programm zu schreiben, welches mir bei 1bit Tiff Daten lediglich die Kanten der schwarz eingefärbten Elemente in eine neue Datei schreibt.
Grundsätzlich funktioniert das Programm und macht genau das was es soll.
Mein Problem ist, das sobald die Bilder größer werden läuft mir der Speicher voll und das Programm bricht mit einer Exeption ab.

Die Zuweisung von mehr RAM zur VM bringt nur bedingt Abhilfe.

Zur Zeit kann ich auf meinem System mit 2GB RAM Dateien verarbeiten die ca. 18 MB haben bei einer Pixelzahl von 147.603.456 (1Bit Farbtiefe). Leider ist das deutlich zu wenig. Die Daten die ich verarbeiten muss weisen eine Größe von mehren GB auf und haben eine Pixelzahl von ca. 103.225.600.000 (1Bit Farbtiefe).

Gibt es seitens Java (Jai) die Möglichkeit nur einen kleinen Bereich des Bildes zu öffnen, zu verarbeiten und anschließend in eine neue Datei zu schreiben, so das der RAM nur zu einen kleinen Teil genutzt wird. Ich dachte an die Möglichkeit nur die ersten 10 Reihen der Datei für die Bearbeitung in den RAM zu laden, was dann eine Pixelzahl von „nur“ noch 4.064.000 zur folge hätte und auch „schwächere“ Systeme die Berechnungen durchführen können.

Ich stehe was die Lösung angeht zur Zeit voll auf dem Schlauch, aber vielleicht kann mich hier jemand in die richtige Richtung schubsen.

Vielen Dank schon mal.

El_Loko
 
Zuletzt bearbeitet von einem Moderator:

Bernd Hohmann

Top Contributor
Das Kernproblem ist, dass ganzen Loader auf 32bit Farbtiefe pro Kanal ausgelegt sind und entsprechend großzügige Arrays nutzen.

Vermutlich wird es bei Dir auf einen eigenen Image-Loader hinauslaufen. Sourcen dafür gibt es genug (ob die aber alle 1bit können - da muss man bisserl aufpassen).

Bernd
 

El_Loko

Mitglied
Hallo Bernd,

danke für deine Info. Ich dachte mir das bereits schon. Wird also in erster Linie heißen, TiFF - Spezi studieren und verstehen.

Gruß El_Loko
 

Bernd Hohmann

Top Contributor
Was Du versuchen kannst: das Tiff mit sowas wie ImageMagick von TIF nach BMP oder PCX umzuwandeln - beide Formate sind recht Schnörkellos zu verarbeiten (da kann ich zur Not in meinen BASIC-Sourcen nachschauen).

Bernd
 

El_Loko

Mitglied
Mit Bitmaps habe ich es schon versucht. Die erste Version funktioniert sogar sehr gut damit und macht kein Probleme. Nur bekomme ich recht schnell eine Fehlermeldung, dass die Bitmap zu groß ist :( Selbst Photoshop meckert schon bei rel. kleinen Bildformaten (im Vergleich zu meiner Zielgröße). Ich weiß gerade leider nicht ob es 300.000 oder 3.000.000 px sind aber ex sind rel. wenige.
 

El_Loko

Mitglied
Hallo Bernd,

bei Generator kann ich dir leider nicht ganz folgen. :(

Das mit den byte[][] - Array hat mich auf eine gut Idee gebracht.
Ich habe meine Methoden so umgeschrieben, das das TiffBild in ein BufferedImage geschrieben wird, welches dann eine Text Datei mit 0 als weiß und 1 als schwarz füllt. (Angelehnt an das PBM-Format).

Damit konnte ich erstmals meine 50MB Datei verarbeiten.
Leider ist dann auch schon wieder Schluss. Meine 150Mb Datei wird schon nicht mehr verarbeitet. Anbei mal der Bericht aus der Konsole. Ggf. kann mir jemand sagen was das heißt. Ich versteh die Fehlermeldung leider nicht.

Kleine Datei

Zeit und Datum: 2012.11.09 at 19:51:52
FileName Flint-SP-Target.K_smm.K.tif
Image File Size: 48.078453 Mbytes
Dimensions: 14784 x 27264
Bits per Pixel: 1
This File is ready to Rip
Writing Map File - Done!
Expose Surface Screen - Zeilen: 27264 - Done!
Writing Tiff - Done!
Ripping done in: 317.0 sec

Große Datei

Zeit und Datum: 2012.11.09 at 20:00:27
FileName Testform Jumper_K.tif
Image File Size: 151.72731 Mbytes
Dimensions: 86929 x 90709
Bits per Pixel: 1
This File is ready to Rip
Writing Map File - Exception in thread "main" java.lang.IllegalArgumentException: Dimensions (width=86929 height=90709) are too large
at java.awt.image.SampleModel.<init>(Unknown Source)
at java.awt.image.MultiPixelPackedSampleModel.<init>(Unknown Source)
at java.awt.image.MultiPixelPackedSampleModel.<init>(Unknown Source)
at java.awt.image.MultiPixelPackedSampleModel.createCompatibleSampleModel(Unknown Source)
at javax.media.jai.PlanarImage.getAsBufferedImage(PlanarImage.java:2517)
at javax.media.jai.PlanarImage.getAsBufferedImage(PlanarImage.java:2546)
at timo.tutorial.CreateGrayImage.main(CreateGrayImage.java:77)

Wie bereits gesagt, ich arbeite mit riesigen Tiff Daten und diese beiden sind dabei noch rel. klein.

Vielen Dank schonmal für eure Hilfe,

Loko
 

Bernd Hohmann

Top Contributor
Hallo Bernd,

bei Generator kann ich dir leider nicht ganz folgen. :(

Naja - irgendein Programm wird doch diese TIFF-Datei erstellen. Und das Programm könnte man so umstricken, dass es die Daten zb. als plattes Byte-Array auf die Platte schmeisst ohne da mit TIFF zu hantieren.

Leider ist dann auch schon wieder Schluss. Meine 150Mb Datei wird schon nicht mehr verarbeitet. Anbei mal der Bericht aus der Konsole. Ggf. kann mir jemand sagen was das heißt. Ich versteh die Fehlermeldung leider nicht.

Ich schon: Dem java.awt.image.SampleModel ist das Bild zu breit und zu hoch: "Dimensions (width=86929 height=90709) are too large"

Was sind das für Daten? Hubble-Teleskop?

Vergiss es einfach solche grossen Bilder mit den eingebauten Java-Routinen für Bildbearbeitung verarbeiten zu wollen.

Bernd
 

El_Loko

Mitglied
Naja - irgendein Programm wird doch diese TIFF-Datei erstellen. Und das Programm könnte man so umstricken, dass es die Daten zb. als plattes Byte-Array auf die Platte schmeisst ohne da mit TIFF zu hantieren.

Ja, lol, da kann ich nur TIFF ausgeben lassen. Ist vom System her so vorgegeben.
 

El_Loko

Mitglied
Ein RIP (Raster Image Processor) aus der Druckvorstufe.
Wir verarbeiten damit Bildformate von 2,032 m x 1,27 m bei einer Dateiauflösung von bis zu 5080 dpi
 

Marco13

Top Contributor
Auch bei TIFF gibt es verschiedene Unter-Formate. Unkomprimiertes TIFF ist AFAIR recht einfach aufgebaut, nur ein roher Header mit ein paar Infos, und dann ganz plain die Daten hinten dran. Ein bißchen hängt das auch davon ab, wie genau die Daten verarbeitet werden sollen: Reicht die Möglichkeit, sequentiell durch alle Pixel zu laufen, überhaupt um die vorgesehene Aufgabe zu lösen?
 

El_Loko

Mitglied
Ich will versuchen einmal genauer zu erklären was ich eigentlich vorhaben.

So sieht die orginal Datei aus:
eingabe.png


Das möchte ich haben:
ausgabe.png


hoffe die Bilder sind groß genug um was zu erkennen.

Wie gesagt ich kann "kleine" Bilder verarbeiten. Bei großen gibt es ne Exception :(

Deshalb auch die Frage am Anfang: Kann ich nur einen Teil, z.B. die ersten 10 oder n Zeilen in für die Bearbeitung laden, verarbeiten, in die Ausgabedatei schreiben, den verarbeiteten Teil entfernen und anschließend den nächsten Block laden.

Wie in den Bildern, wenn zu erkennen, entsteht ein Frame aus 3 Pixeln. In der Bitmap habe ich es so gelöst das ich wenn ich bei Pixel n bin die Pixel n-1, n+1 ...... frage welchen Farbwert sie haben. Sind diese Schwarz dann darf das Pixel Weis werden, in allen anderen Fällen bleibt es wie es ist.

Gruß
Loko
 

Marco13

Top Contributor
Wenn ich das richtig sehe müssen dann immer nur 3 Zeilen im Speicher gehalten werden (d.h. strenggenommen immer nur ein 3x3-Block!). FALLS das TIFF nicht irgendwie komprimiert ist, sollte das machbar sein....
 

El_Loko

Mitglied
Die Frage die sich dann stellt ist nur wie. Ich habe bei der Ausgabe einige Möglichkeiten der Einstellung unter anderem auch das die TIFF nicht komprimiert wird. Da soll es also nicht dran scheitern. :)
 

Noctarius

Top Contributor
Wenn du das TIFF selber laden möchtest, würde ich dir MemoryMapped Files empfehlen. Dabei wird der Inhalt der Datei virtuell in den Adressraum des Programms abgebildet und du kannst direkt auf den Inhalt (einzelne Bytepositionen, Byte-Blöcke, ...) zugreifen, ohne die Datei wirklich komplett in den Speicher zu laden.
 

Marco13

Top Contributor
Boah, wer hat sich denn diesen Krampf ausgedacht, der sich da 'TIFF-Format' nennt? :autsch: Ein 8x8-Bild wird u.U. anders gespeichert als ein "großes" Bild, und die Sache mit den Tags erinnert irgendwie an die Lochkartenzeit... Naja, mal schauen, was man da machen kann...
 

Bernd Hohmann

Top Contributor
Boah, wer hat sich denn diesen Krampf ausgedacht, der sich da 'TIFF-Format' nennt?

Microsoft und Aldus (letztere wurden von Adobe aufgekaut und waren für weitere Unglücke im Hause Adbobe verantwortlich - wie zb. die fehlende Linearisierung des TIF-Formates).

Entweder das File komplett ins RAM laden oder mit einem Random-Access File arbeiten - ist alles 32bit (aber entweder little oder big Endian).

Bernd
 

Marco13

Top Contributor
Naja, nachdem ich gestern oder wann mit dem Header rumgekrampft habe, sollten eigentlich alle Infos zusammen sein, damit man da mit einem FileChannel und einem MappedByteBuffer drüberlaufen kann, aber das Format mit diesen Tags und dem "Wenn type und count klein genug sind, ist der offset kein offset sondern ein Wert" stammt offenbar schon noch aus einer Zeit, wo man auch versucht hat, eine Jahreszahl in einem byte abzuspeichern :autsch:
 

Noctarius

Top Contributor
... dem "Wenn type und count klein genug sind, ist der offset kein offset sondern ein Wert" stammt offenbar schon noch aus einer Zeit, wo man auch versucht hat, eine Jahreszahl in einem byte abzuspeichern :autsch:

Ungefähr aus der Zeit dürfte das TIFF Format tatsächlich stammen und damals war es halt schwierig Daten für Druckvorstufen zu bearbeiten :)
 

El_Loko

Mitglied
Ja die Druckvorstufe ist ein Spaß für sich. Und wenn man sich anschaut was dann noch kommt, das Drucken wirds noch lustiger :)


Aber zurück zum Thema.

Ich komm mit der Tiff überhaupt nicht klar.


Ich weis das der Header 8 byte hat.
in unserer 8x8.tif

Code:
49 49 2A 00 08 00 00 00

Die ersten zwei geben an welche Codierung vorliegt II / MM
die nächsten zwei ob es eine Tiff ist also == 42
danach folgt wenn ich es richtig verstanden habe die Info wo der IDF startet. Wenn ich es richtig versteh, dann hier bei Offset 08 , oder?

Das würde dann heißen, dass die Länge des IDF by 08 und 09 liegt hier also
Code:
13 00
Ich denk das müsste 19 sein.
Also folgen ab Offset 0A 19 x 12 Bytes die die Tags beinhalten.
Wobei die ersten 2 die Info was im Offset steht, Auflösung etc.
die nächsten 2 dann den Type (short, long)

So bei "in unserer 8x8.tif" ist mein Latein ehrlich gesagt schon am Ende. Die Infos im Netz helfen mir nicht wirklich weiter. Und ein Beispiel an einer Datei habe ich bisher auch nicht gefunden. :(

Tiff = mein Inferno ^^

Gruß Loko
 

Marco13

Top Contributor
Ja, das ist echt ein Krampf. Hier mal meine bisherigen Experimente

ACHTUNG, extrem planlos-trashig-hackig und natürlich weit weg von "fertig"...

vielleicht komme ich am WE dazu, da weiterzumachen, oder nächste Woche...

Java:
package _test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TiffTest
{
    public static void main(String[] args) throws IOException
    {
        FileInputStream is = new FileInputStream(new File("tiffTest01.tif"));
        FileChannel fc = is.getChannel();
        
        TiffTest tt = new TiffTest();
        tt.read(fc);
        
        is.close();
    }

    private static final int TAG_WIDTH = 256;
    private static final int TAG_HEIGHT = 257;
    private static final int TAG_PHOTOMETRIC_INTERPRETATION = 262;
    private static final int TAG_ROWS_PER_STRIP = 278;
    private static final int TAG_STRIP_OFFSETS = 273;
    private static final int TAG_STRIP_BYTE_COUNTS = 279;
    
    private static IFDEntry getEntry(IFD ifd, int tag)
    {
        for (IFDEntry entry : ifd.entries)
        {
            if (entry.tag == tag)
            {
                return entry;
            }
        }
        return null;
    }

    private static int getValueOffset(IFD ifd, int tag)
    {
        for (IFDEntry entry : ifd.entries)
        {
            if (entry.tag == tag)
            {
                return entry.valueOffset;
            }
        }
        return -1;
    }
    
    private static int getCount(IFD ifd, int tag)
    {
        for (IFDEntry entry : ifd.entries)
        {
            if (entry.tag == tag)
            {
                return entry.count;
            }
        }
        return -1;
    }

    private static int getWidth(IFD ifd)
    {
        return getValueOffset(ifd, TAG_WIDTH);
    }
    private static int getHeight(IFD ifd)
    {
        return getValueOffset(ifd, TAG_HEIGHT);
    }
    private static int getPhotometricInterpretation(IFD ifd)
    {
        return getValueOffset(ifd, TAG_PHOTOMETRIC_INTERPRETATION);
    }
    private static int getRowsPerStrip(IFD ifd)
    {
        return getValueOffset(ifd, TAG_ROWS_PER_STRIP);
    }
//    private static int getStripOffsets(IFD ifd)
//    {
//        return getValueOffset(ifd, TAG_STRIP_OFFSETS);
//    }
//    private static int getStripByteCounts(IFD ifd)
//    {
//        return getValueOffset(ifd, TAG_STRIP_BYTE_COUNTS);
//    }
    
    static class IFD
    {
        short numEntries;
        IFDEntry entries[];
        int nextIfdOffset;
        
        @Override
        public String toString()
        {
            return "IFD[" +
                "numEntries=" + numEntries + "," +
                "entries=" + Arrays.toString(entries) + "," +
                "nextIfdOffset=" + nextIfdOffset + "]";
        }
    }
    
    static class IFDEntry
    {
        short tag;
        short type;
        int count;
        int valueOffset;

        @Override
        public String toString()
        {
            return "IFDEntry[" +
                "tag=" + tag + "," +
                "type=" + type + "," +
                "count=" + count + "," +
                "valueOffset=" + valueOffset + "]";
            
            //Integer.toHexString(tag)+" "+Integer.toHexString(type)+" "+Integer.toHexString(count)+" "+Integer.toHexString(i);
        }
        
    }

    
    private ByteOrder order;
    private int firstIfdOffset;
    private IFD firstIfd;
    
    private void read(FileChannel fc) throws IOException
    {
        readHeader(fc);
        firstIfd = readIFD(fc, firstIfdOffset);
        
        System.out.println("Width                     "+getWidth(firstIfd));
        System.out.println("Height                    "+getHeight(firstIfd));
        System.out.println("PhotometricInterpretation "+getPhotometricInterpretation(firstIfd));
        System.out.println("RowsPerStrip              "+getRowsPerStrip(firstIfd));
//        System.out.println("StripOffsets              "+getStripOffsets(firstIfd));
//        System.out.println("StripByteCounts           "+getStripByteCounts(firstIfd));
        
        int stripOffsets[] = getStripOffsets(fc, firstIfd);
        int stripByteCounts[] = getStripByteCounts(fc, firstIfd);
        System.out.println("Strip offsets "+Arrays.toString(stripOffsets));
        System.out.println("Strip byte counts "+Arrays.toString(stripByteCounts));
        
        String full = "";
        for (int i=0; i<stripOffsets.length; i++)
        {
            MappedByteBuffer strip = fc.map(MapMode.READ_ONLY, stripOffsets[i], stripByteCounts[i]);
            byte stripData[] = new byte[stripByteCounts[i]];
            strip.get(stripData);
            
            for (int j=0; j<stripData.length; j++)
            {
                String s = Integer.toBinaryString(stripData[j]);
                s = pad(s);
                s = s.substring(24, 32);
                full += s;
            }
        }
        
        int x = 0;
        for (int i=0; i<full.length(); i++)
        {
            System.out.print((char)full.charAt(i));
            x++;
            if (x == getWidth(firstIfd))
            {
                x = 0;
                System.out.println();
            }
        }
        System.out.println();
    }
    
    private static String pad(String s)
    {
        while (s.length() < 32)
        {
            s = "0"+s;
        }
        return s;
    }
    
    private int[] getStripOffsets(FileChannel fc, IFD ifd) throws IOException
    {
        //int numberOfStrips = (int)Math.ceil((double)getHeight(ifd)/getRowsPerStrip(ifd));
        int valueOffset = getValueOffset(ifd, TAG_STRIP_OFFSETS);
        int numberOfStrips = getCount(ifd, TAG_STRIP_OFFSETS);
        
        //System.out.println("Number of strips "+numberOfStrips);
        //System.out.println("valueOffset "+valueOffset);
        int result[] = new int[numberOfStrips];
        
        IFDEntry entry = getEntry(ifd, TAG_STRIP_OFFSETS);
        if ((entry.type == 0x03 || entry.type == 0x04) && entry.count == 1)
        {
            result[0] = valueOffset;
        }
        else
        {
            MappedByteBuffer stripOffsets = fc.map(MapMode.READ_ONLY, valueOffset, numberOfStrips * 4);
            IntBuffer stripOffsetsInt = stripOffsets.order(order).asIntBuffer();
            stripOffsetsInt.get(result);
        }
        return result;
    }
    
    private int[] getStripByteCounts(FileChannel fc, IFD ifd) throws IOException
    {
        //int numberOfStrips = (int)Math.ceil((double)getHeight(ifd)/getRowsPerStrip(ifd));
        int valueOffset = getValueOffset(ifd, TAG_STRIP_BYTE_COUNTS);
        int numberOfStrips = getCount(ifd, TAG_STRIP_BYTE_COUNTS);
        
        //System.out.println("Number of strips "+numberOfStrips);
        //System.out.println("valueOffset "+valueOffset);
        int result[] = new int[numberOfStrips];
        
        IFDEntry entry = getEntry(ifd, TAG_STRIP_OFFSETS);
        if ((entry.type == 0x03 || entry.type == 0x04) && entry.count == 1)
        {
            result[0] = valueOffset;
        }
        else
        {
            MappedByteBuffer stripByteCounts = fc.map(MapMode.READ_ONLY, valueOffset, numberOfStrips * 4);
            IntBuffer stripByteCountsInt = stripByteCounts.order(order).asIntBuffer();
            stripByteCountsInt.get(result);
        }
        return result;
    }
    
    private void readHeader(FileChannel fc) throws IOException
    {
        MappedByteBuffer header = fc.map(MapMode.READ_ONLY, 0, 8);
        if (header.get(0) == 'I' && header.get(1) == 'I')
        {
            order = ByteOrder.LITTLE_ENDIAN;
        }
        else if (header.get(0) == 'M' && header.get(1) == 'M')
        {
            order = ByteOrder.BIG_ENDIAN;
        }
        else
        {
            throw new IOException("Expected II or MM, but found "+
                (char)header.get(0)+""+(char)header.get(1));
        }
        System.out.println("Order "+order);
        header.order(order);
        
        short magicNumber = header.getShort(2);
        if (magicNumber != 42)
        {
            throw new IOException("Expected 42 but found "+magicNumber);
        }
        
        firstIfdOffset = header.getInt(4);
        System.out.println("First IFD offset "+firstIfdOffset);
    }
    
    private IFD readIFD(FileChannel fc, int offset) throws IOException
    {
        MappedByteBuffer numEntriesBuffer = fc.map(MapMode.READ_ONLY, offset, 2);
        numEntriesBuffer.order(order);
        short numEntries = numEntriesBuffer.getShort(0);
        
        System.out.println("Num. entries: "+numEntries);

        IFD ifd = new IFD();
        ifd.numEntries = numEntries;
        ifd.entries = new IFDEntry[numEntries];
        
        int currentOffset = offset+2;
        for (int i=0; i<numEntries; i++)
        {
            ifd.entries[i] = readIFDEntry(fc, currentOffset);
            currentOffset += 12;
        }

        System.out.println("Read ifd "+ifd);
        
        MappedByteBuffer nextIfdOffsetBuffer = fc.map(MapMode.READ_ONLY, currentOffset, 4);
        nextIfdOffsetBuffer.order(order);
        int nextIfdOffset = nextIfdOffsetBuffer.getInt(0);
        ifd.nextIfdOffset = nextIfdOffset;

        System.out.println("Next ifd offset "+nextIfdOffset);
        
        return ifd;
    }
    
    private IFDEntry readIFDEntry(FileChannel fc, int offset) throws IOException
    {
        MappedByteBuffer entryBuffer = fc.map(MapMode.READ_ONLY, offset, 12);
        entryBuffer.order(order);
        
        IFDEntry entry = new IFDEntry();
        entry.tag = entryBuffer.getShort(0);
        entry.type = entryBuffer.getShort(2);
        entry.count = entryBuffer.getInt(4);
        entry.valueOffset = entryBuffer.getInt(8);

        System.out.println("Read entry "+entry);
        
        return entry;
    }
    
}
 

Bernd Hohmann

Top Contributor
Ich bremse Dich nur ungern, aber die Wahrscheinlichkeit dass beim TO irgendeine andere beschissene TIFF-Variante ausgespruckt wird die Dein Core nicht verarbeiten kann ist recht gross...

Bernd
 

Marco13

Top Contributor
Damit rechne ich auch. Ich gehe nicht davon aus, aber rechne damit ;) Das TIFF-Format ist zwar ziemlich krampfig, aber bei einem unkomprimierten, reinen Schwarzweißbild sollte es nicht sooo viele Freiheitsgrade geben. (Spätestens, wenn es komprimiert wäre, wäre klar, dass es ihn raushaut - ein TIFF kann anscheinend auch ein JPG sein :autsch: ). Aber ich hatte das unfertige Gefrickel auch nicht zuletzt gepostet, um die Möglichkeit zu geben, das ganze mal (ohne das neckische String-Bitmap :D ) mal auf eins der echten Bilder loszulassen...
 

Marco13

Top Contributor
Wenn man mit C arbeitet nicht

Ich würde eher sagen, dass da die Krampfigkeit der Sprache die des Dateiformates übertüncht :D Nee, stimmt schon: Bestimmte Elemente würden da eher passen. Tatsächlich waren das gepostete auch nur erste Experimente, um zu sehen, ob ich die Spec richtig interpretiere. Um das dann "schöner" oder auch "einfacher" zu machen hatte ich schon ein paar Gedanken (z.B. könnte man das mit der Struct, was du angedeutet hast, recht leicht emulieren). Aber irgendwelche abstrusen Fallunterscheidungen wie
Java:
if ((entry.type == 0x03 || entry.type == 0x04) && entry.count == 1)
{
    result[0] = valueOffset;
}
else
{  
    // read data from somewhere ...
}
bräuchte man auch in C. DAS ist das, was ich eigentlich schräg fand. Jaaa, ich weiß, es hat seine Rechtfertigung: Ist eben schneller, wenn man die Daten ('valueOffset') direkt verwenden kann, als wenn man das Bandlaufwerk da hin vorspulen muss, wo 'valueOffset' hin zeigt, aber ... solche Anachronsimen sind eben etwas ungewohnt für junge Java-Springinsfelde... ;)
 
S

Spacerat

Gast
Höhö... ich les' das gerade...
Wie wollt ihr in Java denn bitteschön echte Bitmaps (1 Bit pro Pixel) darstellen? Das kleinste Pixelformat, das Java darstellen kann hat 8 Bit (DataBuffer.BYTE). Daraus folgt, dass bei Pixelformaten < 8 Bit maximal 7 zuviel pro Pixel angelegt werden. Monochrome Bitmaps würden in Java also grundsätzlich 8 Mal soviel Speicher belegen. Man kann nun 1000 Algos entwickeln um für einzelne Bits Farben zu bestimmen, an dem Umstand, dass man grössere Monochrom-Bilder in Java nur auschnittweise zu Gesicht bekommt, ändert das, unabhängig vom Dateiformat, gar nichts.
Das TIFF-Format ist deswegen so schwerfällig, weil es mehr oder weniger ein Containerformat für andere Bildformate ist (wenn man so will). Es kennt diverse Kompressionsalgos, die auch in anderen (z.B. DCT-JPEG oder LZW-GIF) Bildformaten verwendet werden.
 

Bernd Hohmann

Top Contributor
Jaaa, ich weiß, es hat seine Rechtfertigung: Ist eben schneller, wenn man die Daten ('valueOffset') direkt verwenden kann, als wenn man das Bandlaufwerk da hin vorspulen muss, wo 'valueOffset' hin zeigt, aber ... solche Anachronsimen sind eben etwas ungewohnt für junge Java-Springinsfelde... ;)

Ich kann dich als ehemaliger Bandlaufwerksnutzer insofern beruhigen dass TIFF für Bandlaufwerke extrem ineffizient ist da es dort Strukturen gibt die "hinten" auf Daten von "vorne" verweisen.

TIFF ist im Wesentlichen die konzentrierte Sammlung der wichtigsten in den letzten 40 Jahren einschlägig bekanntgewordenen Anfängerfehler beim Design eines Dateiformates und wird in dieser Hinsicht nur noch von MPEG übertroffen.

Bernd
 
S

Spacerat

Gast
Wie in anderen Programmiersprachen auch.

Bernd
Heisst das, das man wo anders auch keine Bilder mit Pixeltiefe 1 darstellen kann? Ich nahm an, dass man zumindest in C wennüberhaupt die Pixel erst bei der Übergabe in den Framebuffer auf die Bittiefe des Anzeigemediums erweitern muss. In Java geschieht dies zumindest schon im MultiPixelPackedSampleModel. Mit anderen Worten, man hat in Java (besser gesagt in Java2D) mindestens einen Framebuffer der 8 Mal grösser als nötig ist.
 
Zuletzt bearbeitet von einem Moderator:

Bernd Hohmann

Top Contributor
Heisst das, das man wo anders auch keine Bilder mit Pixeltiefe 1 darstellen kann? Ich nahm an, dass man zumindest in C wennüberhaupt die Pixel erst bei der Übergabe in den Framebuffer auf die Bittiefe des Anzeigemediums erweitern muss.

Kann sein dass ich Dich missverstanden habe, aber auch in C ist die kleinste Einheit 1 Byte (normalerweise also 8 Bit). Kompression über Shift-Operationen (also ein indexed-byte) ist in C natürlich genauso drin wie in Java.

Bernd
 
S

Spacerat

Gast
[OT]Ich mach das mal OT...
Aber in C kann man die nötigen Bitshifts afaik direkt am Framebuffer der Karte vornehmen. In Java hängt da immer noch ein virtueller FB dazwischen, der mit dem der Karte synchronisiert ist. Zumindest war es noch bis Java6 so. In Java7 hat man aber einiges am Java2D-Core geändert und ich hab' längst nicht ergründet, was alles genau.[/OT]
 

Bernd Hohmann

Top Contributor
[OT]Ich mach das mal OT...
Aber in C kann man die nötigen Bitshifts afaik direkt am Framebuffer der Karte vornehmen. In Java hängt da immer noch ein virtueller FB dazwischen, der mit dem der Karte synchronisiert ist. Zumindest war es noch bis Java6 so. In Java7 hat man aber einiges am Java2D-Core geändert und ich hab' längst nicht ergründet, was alles genau.[/OT]

Da muss ich etwas passen weil meine letzte Hardwareprogrammierung einer Grafikkarte gut 20 jahre her ist.

Vermutlich wird es bei C davon abhängen, welche Grafiklibrary genutzt wird und was der Grafiktreiber so anbietet damit irgendwelche Tricksereien für 1bit Farbtiefe genutzt werden können.

Wirtschaftlich wäre das heute nicht mehr wirklich (vielleicht noch bei Nischen die ich nicht kenne).

Bernd
 

Marco13

Top Contributor
Mal ganz nebenbei: Es ging (wenn ich den inzwischen etwas weit entfernten Anfang des Threads noch richtig in Erinnerung habe :D ) nur um das Lesen und Verarbeiten, und nicht um das Anzeigen solcher Dateien.
 

El_Loko

Mitglied
Leute ihr seit der Hammer :)

Anzeigen möchte ich die Daten nicht, das ist richtig, dass kann Photoshop oder sonstige Viewer.

Ich möchte das Ganze aber auch nicht auf die Spitze treiben, da wir jetzt schon anfangen über C zu reden (da hab ich gar keine Ahnung von).

Was mir nun aufgefalle ist, ist das darüber debatiert wird, warum man mit dem jetzigen Format und Java eigentlich nur bedingt 1bit Daten verarbeite kann. Die Frage die ich mir nun stelle ist warum sollte ich für ein Bit einen ganzen Bte belegen?

Also für mich ist wenn ich eine Bildfolge von 00 11 00 11 habe wäre dann der Byte dafür nicht 33.

Wenn ich nun also nun von Position n den Pixel n-1 und n+1 prüfen möchte bräuchte ich lediglich dafür lediglich max 2byte zu decodieren, die BitWerte in ein Array zu schreiben die änderungen zu machen, wieder

Also wenn meine Feld 11 11 11 11 ist als Hex dann FF und nur der erste und letzte Pixel 1 sein sollen, würde der neue Byte dann den HEx Wert 81 haben. Somit wäre das Speicherproblem nur zweitrangig, oder?

Gruß Loko
 

El_Loko

Mitglied
Mit ImageJ habe ich mich schon beschäftigt und auch ein Plugin zusammengebastelt um die Kanten zu erkennen. Mein Problem jedoch war die mangelnde Rechenleistung. Hab mir aber heute einen neuen Rechner gekauft. Werde nun nochmal versuchen was ich in ImageJ laden kann.
 

Marco13

Top Contributor
Was mir nun aufgefalle ist, ist das darüber debatiert wird, warum man mit dem jetzigen Format und Java eigentlich nur bedingt 1bit Daten verarbeite kann. Die Frage die ich mir nun stelle ist warum sollte ich für ein Bit einen ganzen Bte belegen?

Bin nicht sicher, ob ich das richtig verstanden habe, aber: Der Hinweis von Spacerat bezog sich darauf, dass wenn man das Bild anzeigen will, man für jedes Bit (aus der Datei) ein Byte (für den Pixel auf dem Bildschirm) verwenden muss. Ich denke zwar, dass man auch DAS irgendwie vermeiden könnte, aber wohl nur mit viel Aufwand. Aber die Frage stellt sich ja gar nicht.
 
S

Spacerat

Gast
Ich meinte im übrigen auch mindestens ein Byte. Ich hab' es zwar selber noch nicht ausprobiert, aber ich denke mal, dass man in C diese Zwischenpufferung umgehen kann, weil man dort die Farben, welche sich aus den Bits des Rasterbuffers ergeben, direkt in den Framebuffer der Karte schreiben kann, wo sie dann je nach Anzeigemodus entsprechend abgelegt werden. In Java wird jedem einzelnen Bit eine Farbe aus einem IndexColorModel mit 2 Farben (im Zweifelsfalle also 32 Bit) zugewiesen, diese in einen Puffer geschrieben und dieser dann wiederum mit dem FB der Karte synchronisiert. Das heisst, eine 8*8 1-Bit-Pixelmatrix wird im Zweifelsfalle 8*8*32-Bit oder 8*8*4 also 256 Byte groß. Das wären nach meiner Rechnung glatt 248 Bytes zu viel, aber Hauptsache die JVM hat noch 'ne Kopie davon.
Aber wenn man die Bilder ohnehin nicht anzeigen will, kann man sich auch den Puffer des BufferedImages holen, welcher dann im oben erwähntem MultiPixelPackedSampleModel vorliegen sollte. Eine Bearbeitung dieser Puffer muss dann aber händisch ohne "Graphics" stattfinden, es sei denn, man betreibt den Aufwand und implementiert sich selber einen entsprechenden Kontext ("Graphics" erweitern).
 

Marco13

Top Contributor
Hast du mal probiert, ob das bisher gepostete (OHNE die Ausgabe als String !!!) zumindest ohne Fehler ansetzt, eine der "echten" Dateien zu lesen?
 

El_Loko

Mitglied
Hast du mal probiert, ob das bisher gepostete (OHNE die Ausgabe als String !!!) zumindest ohne Fehler ansetzt, eine der "echten" Dateien zu lesen?

Ja es funktioniert. Die Daten werden mit den Daten die ich hier zu hause habe korrekt ausgelesen. Ich werden morgen mal ein paar richtig große versuchen und dann noch mal ein Feedback geben,


Hab im Moment nur eine "kleinere" und die ist LZW komprimiert. Aber auch hier bekomme ich einen Output

Code:
Order LITTLE_ENDIAN
First IFD offset 8
Num. entries: 20
Read entry IFDEntry[tag=254,type=4,count=1,valueOffset=0]
Read entry IFDEntry[tag=255,type=3,count=1,valueOffset=1]
Read entry IFDEntry[tag=256,type=4,count=1,valueOffset=86929]
Read entry IFDEntry[tag=257,type=4,count=1,valueOffset=90709]
Read entry IFDEntry[tag=258,type=3,count=1,valueOffset=1]
Read entry IFDEntry[tag=259,type=3,count=1,valueOffset=5]
Read entry IFDEntry[tag=262,type=3,count=1,valueOffset=0]
Read entry IFDEntry[tag=269,type=2,count=22,valueOffset=828]
Read entry IFDEntry[tag=270,type=2,count=22,valueOffset=804]
Read entry IFDEntry[tag=271,type=2,count=14,valueOffset=852]
Read entry IFDEntry[tag=273,type=4,count=235,valueOffset=868]
Read entry IFDEntry[tag=274,type=3,count=1,valueOffset=1]
Read entry IFDEntry[tag=277,type=3,count=1,valueOffset=1]
Read entry IFDEntry[tag=278,type=4,count=1,valueOffset=386]
Read entry IFDEntry[tag=279,type=4,count=235,valueOffset=1808]
Read entry IFDEntry[tag=282,type=5,count=1,valueOffset=2748]
Read entry IFDEntry[tag=283,type=5,count=1,valueOffset=2756]
Read entry IFDEntry[tag=296,type=3,count=1,valueOffset=2]
Read entry IFDEntry[tag=305,type=2,count=18,valueOffset=784]
Read entry IFDEntry[tag=700,type=1,count=10923,valueOffset=2764]
Read ifd IFD[numEntries=20,entries=[IFDEntry[tag=254,type=4,count=1,valueOffset=0], IFDEntry[tag=255,type=3,count=1,valueOffset=1], IFDEntry[tag=256,type=4,count=1,valueOffset=86929], IFDEntry[tag=257,type=4,count=1,valueOffset=90709], IFDEntry[tag=258,type=3,count=1,valueOffset=1], IFDEntry[tag=259,type=3,count=1,valueOffset=5], IFDEntry[tag=262,type=3,count=1,valueOffset=0], IFDEntry[tag=269,type=2,count=22,valueOffset=828], IFDEntry[tag=270,type=2,count=22,valueOffset=804], IFDEntry[tag=271,type=2,count=14,valueOffset=852], IFDEntry[tag=273,type=4,count=235,valueOffset=868], IFDEntry[tag=274,type=3,count=1,valueOffset=1], IFDEntry[tag=277,type=3,count=1,valueOffset=1], IFDEntry[tag=278,type=4,count=1,valueOffset=386], IFDEntry[tag=279,type=4,count=235,valueOffset=1808], IFDEntry[tag=282,type=5,count=1,valueOffset=2748], IFDEntry[tag=283,type=5,count=1,valueOffset=2756], IFDEntry[tag=296,type=3,count=1,valueOffset=2], IFDEntry[tag=305,type=2,count=18,valueOffset=784], IFDEntry[tag=700,type=1,count=10923,valueOffset=2764]],nextIfdOffset=0]
Next ifd offset 0
Width                     86929
Height                    90709
PhotometricInterpretation 0
RowsPerStrip              386
Strip offsets [17684, 22538, 27392, 34606, 38602, 42598, 63902, 114144, 180358, 336874, 1160346, 1984250, 2809536, 3636016, 4461732, 5288358, 6115588, 6941064, 7764588, 8588442, 8849168, 8853164, 8857160, 10097044, 11375978, 12655516, 13934832, 15213890, 16493040, 17772152, 19051350, 20330192, 21609020, 22634862, 22638858, 22642854, 23081902, 23678910, 24285676, 24898696, 25516112, 26137426, 26763764, 27395682, 28031560, 28670358, 29314794, 29961654, 30609750, 31262956, 31919888, 32580330, 33244370, 33909942, 34577312, 35248312, 35924272, 36601242, 37282798, 37969664, 38659208, 39353722, 40048450, 40745144, 41444958, 42158578, 42871718, 43586316, 44299862, 45011602, 45717934, 46416148, 47106574, 47791596, 48471074, 49142384, 49812064, 50479008, 51152306, 51833742, 52520048, 53206158, 53884808, 54564218, 55227420, 55884104, 56538300, 57190050, 57836176, 58484924, 59127182, 59767820, 60405370, 61044466, 61679880, 62310760, 62950136, 63587210, 64224268, 64863314, 65500328, 66140138, 66777800, 67423986, 68069316, 68716714, 69359064, 69978562, 70585234, 71191812, 71816206, 72430498, 73049192, 73677050, 74296820, 74923688, 75559206, 76195802, 76830912, 77492270, 78177644, 78853846, 79495018, 80157560, 80871972, 81601968, 82328836, 83077300, 83825364, 84570812, 85323996, 86082968, 86857802, 87654552, 88465868, 89295596, 90148476, 91039622, 91982192, 92948624, 93922246, 94902060, 95879498, 96830934, 97755894, 98686706, 99648778, 100610116, 101570742, 102527688, 103474936, 104411952, 105339936, 106261094, 107165616, 108072154, 108996788, 109919970, 110820998, 111706014, 112545728, 113378384, 114205850, 115021112, 115821446, 116597160, 117352790, 118095814, 118805190, 119462574, 120061358, 120608754, 121114396, 121620426, 122131636, 122652112, 123175946, 123703338, 124234462, 124769708, 125307884, 125847874, 126393192, 126941762, 127492314, 128044642, 128600402, 129159104, 129719682, 130289362, 130858024, 131428610, 132000806, 132570142, 133140070, 133711104, 134286486, 134859976, 135431412, 136003998, 136425126, 136429122, 136433118, 137468916, 138751750, 140034628, 141318160, 142601232, 143883804, 145166546, 146449948, 147733136, 149016436, 150257672, 150261668, 150265664, 150578314, 151402406, 152226082, 153051150, 153877356, 154703600, 155530372, 156357202, 157182198, 158006262, 158829738, 158929476, 158937182, 158991062, 159050982, 159076824, 159080820, 159087918, 159092772]
Strip byte counts [4854, 4854, 7214, 3996, 3996, 21304, 50242, 66214, 156516, 823472, 823904, 825286, 826480, 825716, 826626, 827230, 825476, 823524, 823854, 260726, 3996, 3996, 1239884, 1278934, 1279538, 1279316, 1279058, 1279150, 1279112, 1279198, 1278842, 1278828, 1025842, 3996, 3996, 439048, 597008, 606766, 613020, 617416, 621314, 626338, 631918, 635878, 638798, 644436, 646860, 648096, 653206, 656932, 660442, 664040, 665572, 667370, 671000, 675960, 676970, 681556, 686866, 689544, 694514, 694728, 696694, 699814, 713620, 713140, 714598, 713546, 711740, 706332, 698214, 690426, 685022, 679478, 671310, 669680, 666944, 673298, 681436, 686306, 686110, 678650, 679410, 663202, 656684, 654196, 651750, 646126, 648748, 642258, 640638, 637550, 639096, 635414, 630880, 639376, 637074, 637058, 639046, 637014, 639810, 637662, 646186, 645330, 647398, 642350, 619498, 606672, 606578, 624394, 614292, 618694, 627858, 619770, 626868, 635518, 636596, 635110, 661358, 685374, 676202, 641172, 662542, 714412, 729996, 726868, 748464, 748064, 745448, 753184, 758972, 774834, 796750, 811316, 829728, 852880, 891146, 942570, 966432, 973622, 979814, 977438, 951436, 924960, 930812, 962072, 961338, 960626, 956946, 947248, 937016, 927984, 921158, 904522, 906538, 924634, 923182, 901028, 885016, 839714, 832656, 827466, 815262, 800334, 775714, 755630, 743024, 709376, 657384, 598784, 547396, 505642, 506030, 511210, 520476, 523834, 527392, 531124, 535246, 538176, 539990, 545318, 548570, 550552, 552328, 555760, 558702, 560578, 569680, 568662, 570586, 572196, 569336, 569928, 571034, 575382, 573490, 571436, 572586, 421128, 3996, 3996, 1035798, 1282834, 1282878, 1283532, 1283072, 1282572, 1282742, 1283402, 1283188, 1283300, 1241236, 3996, 3996, 312650, 824092, 823676, 825068, 826206, 826244, 826772, 826830, 824996, 824064, 823476, 99738, 7706, 53880, 59920, 25842, 3996, 7098, 4854, 4846]

Zum auslesen der 158 MB File brauch ich genau 14 ms. :)

Gruß Loko
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Äh.. ja, nee, die Endianness sollte eigentlich schon richtig berücksichtigt werden, ich meinte nur, dass das Auslesen der Daten und die Ausgabe bei diesem 'TiffTest' verwurstet waren, und ... wenn du die Ausgabe rausnimmst, liest er die eigentlichen Daten nicht - und wenn du sie NICHT rausnimmst, würde er einen String erstellen, den er bei der Größe sicher NICHT in 14ms erstellen und ausgeben könnte ;) Aber vielleicht hast du das ja schon auseinanderklamüsert... ?!
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
H new Operator in paintComponent - Speicherprobleme vorprogrammiert? AWT, Swing, JavaFX & SWT 2
P TreeView automatisch an große von Inhalt anpassen AWT, Swing, JavaFX & SWT 3
H zwei gleich große Panels getrennt durch ein weiteres Panel AWT, Swing, JavaFX & SWT 10
J Swing Große Gui designen/gestalten AWT, Swing, JavaFX & SWT 13
J Welche(n) LayoutManager für große Gui? AWT, Swing, JavaFX & SWT 6
M Swing Große GUI - Konzept, Ideen, Vorschläge AWT, Swing, JavaFX & SWT 20
G Große dynamische ContextMenus erzeugen (Eclipse RCP) AWT, Swing, JavaFX & SWT 4
R JPanel sehr große JPanel hinzufügen AWT, Swing, JavaFX & SWT 5
T JTable / RowSorter macht große Probleme AWT, Swing, JavaFX & SWT 2
P SWT Button --> große Probleme!! AWT, Swing, JavaFX & SWT 7
D verschieden große Componenten in einem JPanel? AWT, Swing, JavaFX & SWT 2
G zu viele/große BufferedImage führt zu Java heap space AWT, Swing, JavaFX & SWT 5
C JTree bereitet große Probleme, kann uns bitte jemand helfen! AWT, Swing, JavaFX & SWT 6
J Große Anzahl von Comboboxen in Tabelle darstellen AWT, Swing, JavaFX & SWT 2
H große mengen an jbuttons dynamisch erzeugen? AWT, Swing, JavaFX & SWT 15
I TIFF im CMYK-Farbmodell + mit dpi-Auflösung im Header erzeugen - wie? AWT, Swing, JavaFX & SWT 0
P Bild im TIFF oder BMP Format einlesen AWT, Swing, JavaFX & SWT 3
W Anzeige von anderen Bildformaten: BMP, TIFF, PCX AWT, Swing, JavaFX & SWT 5

Ähnliche Java Themen

Neue Themen


Oben