# ObjectOutputStream und ObjectInputStream machen Ärger



## Goldfish (15. Sep 2012)

Hi, ich wills gar nicht lang halten hier, also gleich zur Sache ^^
Ich habe eine selbstgeschriebene Klasse, vonvder ich ein Objekt mit dem ObjectOutputStream in einer Datei speichern will und dieses Objekt beim starten des Programmes wieder auslesen will.

Klasse hat folgende Struktur


```
public class Movie implements Serializable
{
    String ean;
    String title;
    String original;
    BufferedImage cover;    
    ArrayList<String> language;
    BufferedImage  movierating;
    String year;
    ArrayList<String> actors;
    ArrayList<String> genres;
    String duration;
    String description;

...
}
```

und speichern bzw. wieder einlesen tu ich das ganze so:


```
public class DataSaving {
    
     private static FileOutputStream fos;
     private static ObjectOutputStream oos;
     
     private static FileInputStream fis;
     private static ObjectInputStream ois;
    
     public DataSaving()
     {
        try {
            fos = new FileOutputStream(".\\data.ini");
            oos = new ObjectOutputStream(fos);
            
            fis = new FileInputStream(".\\data.ini");
            ois = new ObjectInputStream(fis);
        } catch (IOException ex) {
            Logger.getLogger(DataSaving.class.getName()).log(Level.SEVERE, null, ex);
        }
     }
     
    public static void saveMovie(Movie movie)
    {
        try {
            oos.writeObject(movie);
            oos.close();
        } catch (IOException ex) {
            Logger.getLogger(DataSaving.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public static Movie readMovie()
    {
        try {
            Object m;
            m = ois.readObject();
            return (Movie) m;
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger(DataSaving.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
}
```

ich erhalte folgende Fehlermeldung:

java.io.NotSerializableException: java.awt.image.BufferedImage

wie kann ich dieses Problem umgehen?

Und dazu hab ich noch ein Problem. Wenn ich das BufferedImage weglasse, klappt soweit alles gut. Nur beim einlesen der Daten, wenn ich das Programm dann wieder starte mit der Funktion readMovie()
bekomme ich auch wieder eine Fehlermeldung und weiß nicht wieso...


```
new DataSaving();
            Movie m = DataSaving.readMovie();
            m.showData();
```

Fehlermeldung sieht so aus:

Schwerwiegend: null
java.io.EOFException
	at java.ibjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2571)
	at java.ibjectInputStream.readObject0(ObjectInputStream.java:1315)
	at java.ibjectInputStream.readObject(ObjectInputStream.java:369)
	at netcontroller.DataSaving.readMovie(DataSaving.java:50)
	at netcontroller.NetController.main(NetController.java:191)


wäre super, wenn sich jemand auskennt und weiß was zu tun ist...

danke im Voraus


----------



## kaetzacoatl (15. Sep 2012)

Du erzeugst gleichzeitig einen Input und einen OutputStream:autsch:
erzeug die Streams erst beim auslesen, dann dürfte es gehn.
Und BuffferedImage musst du nicht entfernen, machs einfach transient.


----------



## Bernd Hohmann (15. Sep 2012)

Zwei Probleme:

1) Es ist keine gute Idee, eine Datei gleichzeitig zum lesen und zum schreiben aufzumachen. In Deinem Fall wird die Datei im OutputStream gelöscht und mit 0 bytes erzeugt, dein folgender InputStream liest also aus einer leeren Datei.

2) BufferedImage ist nicht serialisierbar. Was aber geht, ist das BufferedImage zb. in ein BMP umwandeln und das über ImageIO wegzuschreiben.

Ungetestet, im Netz gefunden:

```
class WrapperBufferedImage implements Serializable {
	private BufferedImage bi = null;
 
	public WrapperBufferedImage(BufferedImage b) {
		this.bi = b;
	}
 
	public BufferedImage getImage() {
		return bi;
	}
 
	private void writeObject(ObjectOutputStream os) throws IOException {
		ImageIO.write(bi, "bmp", ImageIO.createImageOutputStream(os));
	}
 
	private void readObject(ObjectInputStream is) throws IOException {
		bi = ImageIO.read(ImageIO.createImageInputStream(is));
	}
}
```

Alternativ könnte man schauen, ob man aus BufferedImage alle Informationen (zb. das interne byte-Array) für eine Rekonstruktion herauspuhlen kann - so mach ich das zb. mit swt.ImageData für die Serialisierung.

Bernd


----------



## Goldfish (16. Sep 2012)

Super, jetzt klappt alles vielen Dank.
Was das BufferedImage angeht, das hab ich einfach in ein Image und dann in ein ImageIcon konvertiert, da dies ja Serialisierbar ist. Die einfachste Variante zumindest ^^


Ich hab nur nochmal eine extra Frage. Ein Grund dafür, dass ich meine Daten auf diese Weise speichern wollte, war die Einfachheit, die dahinter steckt. Mir ist nun allerdings aufgefallen, dass das Speichern eines einzelnen Objektes bis jetzt schon 110kb einnimmt.
Kann es sein, dass man mit dem ObjectOutputStream DEUTLICH mehr Speicher in Anspruch nimmt, als wie wenn man die Daten (auch die bilder) jeweils einzeln speichert?
Da die Datenbank am Ende nicht unbedeutend groß werden können soll bspw. bis zu 30000 Einträgen, bin ich nicht sicher, ob das eine Rentable Angelegenheit ist so und ob ich meine Speichermöglichkeiten vielleicht nicht nochmal überdenken sollte.


----------



## troll (16. Sep 2012)

wir hatten hier vor kurzem mal einen thread der sich mit dem thema befasste wie viel speicher ein objekt im ram verbraucht ...
ein darin verlinkter beitrag hat folgendes deutlich gemacht :
java kennt als kleinste einheit den int : alles wird grundsätzlich mit 4byte im ram gehalten, das gilt besonders für die kleineren typen bool, byte, short und char ... jeder belegt, obwohl eigentlich kleiner, immer 4byte im ram ... wenn du also 2 bools hast verbraucht das für 2bit nutzdaten 8byte ram ... java ist nun mal selbst nicht in der lage das intiligent zu verwalten ...
daraus lässt sich schließen das alleine für dein objekt folgende ram-belegung zustande kommt :
4byte für die referenz auf das objekt selbst + 11 * 4byte für die referenzierten variablen + größe der einzelnen variablen sowie deren intern refernzierten variablen rekursiv + payload-größe der nutzdaten ... macht unter strich mal weit überschlagen ein paar 100bytes nur für die deklaration ...
alleine string hat intern ein char-array, eine längenangabe, ein offset und noch ein paar weitere variablen, gleich ob all diese serialisiert werden ...
bei den listen multipliziert sich das ganze mit dem overhead der liste selbst
und bei den images ... naja ... ich kenne mich zwar nicht so gut aus ... würde aber behaupten das java intern gerade bilder als RAW daten hält ohne kompressions-algos wie z.b. jpeg
zusätzlich kommen für jede klasse die serialisiert wird noch weitere overhead-daten hinzu ...
es wäre also günstiger (und vor allem einfacher) wenn du die bilder als "transient" kennzeichnest (das bedeutet sie werden nicht mit-serialisiert) und diese selbst mit z.b. jpeg rausschreibst ... der overhead dürfte in etwa gleich sein ... aber der payload bei jpeg-bildern deutlich kleiner ... (java kann aus 5MB bildern einer digital-kamara ohne merklichen qualitätsverlust und mit guter kompression mal eben knapp 300kB machen)

durch diesen ganzen overhead , die eigenschaft das java alles mindestens als INT sieht und den nutzlast-payload kommst du auf solche große daten-größen für eigentlich recht wenig nutzdaten ...

grundlegend ist serialisierung eine der schlechtesten mittel zur speicherung von daten ...

deutlich effektiver wäre es wenn du den INHALT jeder variable in ein einzelnes feld deiner datenbank schreiben würdest ...
der programmier-aufwand hält sich in grenzen und ist bei solch einfachen datentypen schnell gemacht ... da brauchst keine großen tricks ...
zusätzlich könntest noch mit kompression arbeiten (COMPRESS, GZIP, DEFLATE) und ich denke das die datenbank auch noch einiges zubieten haben dürfte (gerade wenn man für die bilder BLOB nimmt)


----------



## Bernd Hohmann (16. Sep 2012)

Goldfish hat gesagt.:


> Super, jetzt klappt alles vielen Dank.
> Was das BufferedImage angeht, das hab ich einfach in ein Image und dann in ein ImageIcon konvertiert, da dies ja Serialisierbar ist. Die einfachste Variante zumindest ^^



Es wäre zu überlegen ob Du das Image nicht vorher gleich auf passende Größe herunterskalierst und nur die kleine Variante abspeicherst.  Wenn Du das Orginal auch sichern willst, würde ich überlegen die Datei in ein byte-Array zu lesen und zu serialisieren - JPEG ist wesentlich kompakter als der ausgepackte Kram im BufferedImage.



Goldfish hat gesagt.:


> Kann es sein, dass man mit dem ObjectOutputStream DEUTLICH mehr Speicher in Anspruch nimmt, als wie wenn man die Daten (auch die bilder) jeweils einzeln speichert?



Siehe oben: JPEG ist in der Datei gut komprimiert, im BufferedImage aber unkomprimiert. Deshalb bläht das so auf. Der Overhead vom Object-Stream ist hingegen sosolala - viele kleine Variablen blähen natürlich auf weil jedesmal der Name der Variablen, Typ und noch andere Dinge abgelegt werden müssen.

Bernd


----------



## Bernd Hohmann (16. Sep 2012)

troll hat gesagt.:


> wir hatten hier vor kurzem mal einen thread der sich mit dem thema befasste wie viel speicher ein objekt im ram verbraucht ...
> ein darin verlinkter beitrag hat folgendes deutlich gemacht :
> java kennt als kleinste einheit den int : alles wird grundsätzlich mit 4byte im ram gehalten, das gilt besonders für die kleineren typen bool, byte, short und char ... jeder belegt, obwohl eigentlich kleiner, immer 4byte im ram ... wenn du also 2 bools hast verbraucht das für 2bit nutzdaten 8byte ram ... java ist nun mal selbst nicht in der lage das intiligent zu verwalten ...



Komisch, bei mir sieht das anders aus: Ein Array von 100 Elementen belegt im ObjectOutputStream für 

boolean: 123
byte: 123
short: 223
char: 223
int: 423
long: 823


```
import java.io.*;

public class Test {

	public static void main(String[] args) throws Throwable {

		boolean blnTest[] = new boolean[100];
		byte bytTest[] = new byte[100];
		short shtTest[] = new short[100];
		char chrTest[] = new char[100];
		int intTest[] = new int[100];
		long lngTest[] = new long[100];

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);

		oos.reset();
		baos.reset();
		oos.writeObject(blnTest);
		System.out.println("boolean: " + baos.toByteArray().length);

		oos.reset();
		baos.reset();
		oos.writeObject(bytTest);
		System.out.println("byte: " + baos.toByteArray().length);

		oos.reset();
		baos.reset();
		oos.writeObject(shtTest);
		System.out.println("short: " + baos.toByteArray().length);

		oos.reset();
		baos.reset();
		oos.writeObject(chrTest);
		System.out.println("char: " + baos.toByteArray().length);

		oos.reset();
		baos.reset();
		oos.writeObject(intTest);
		System.out.println("int: " + baos.toByteArray().length);

		oos.reset();
		baos.reset();
		oos.writeObject(lngTest);
		System.out.println("long: " + baos.toByteArray().length);

		oos.close();
		baos.close();
	}
}
```

Bernd


----------



## Goldfish (16. Sep 2012)

okay, danke für eure Antworten.
Also die Images sind bereits auf der richtigen Größe, wenn ich die runterziehe. Ich nutze ja htmlUnit und lade die Bilder aus dem Netz. Ich denke, dass ich es einfach so machen werde, dass ich die sich wiederholenden images einfach einmal fest speichere und dann ggf. wieder einlese.
Dabei wäre ich jetzt nur überfragt bei dem Gedanken, wie ich rauskriegen soll, um welches Image es sich dabei handelt... Ich kann im Voraus sagen, dass ich alle einmal gesichert habe, und dass kein anderes außer denen vorkommen kann. Aber möchte ich nun eine Referenz haben, wie gleich ich das dann ab?
Also ich meine, ich lade Daten aus dem Netz und habe ein Bild. Und um herauszukriegen welches Bild das ist, hab ich nur meine gespeicherten Versionen. Hat java da iregendwelche Möglichkeiten da einen Abgleich zu machen?

Aber ansonste, werd ich den ObjectOutputStream wohl an den Nagel hängen. Ich werd mir ne weniger komfortable Speicherstruktur überlegen, die aber großzügiger ist ^^


----------



## Spacerat (16. Sep 2012)

[OT]@Bernd: An dem was troll das sagt, ist aber was dran. Allerdings hat er vergessen zu erwähnen, dass Arrayelemente eine Ausnahme sind, dort sind Bytes die kleinste Einheit. Weitere Ausnahmen der 4-Byte-Regel, sind Long und Double-Werte mit jeweils 8 Byte und UTF-Zeichenketten mit 1 bis 4 Bytes pro Zeichen.[/OT]
BufferedImages haben jeweils ein ColorModel und ein Raster. Das Raster beinhaltet wiederum ein SampleModel und einen DataBuffer. Sample- und ColorModel müssen dabei stets zusammen passen und bestimmen, wie viele Bänder (Farbkomponenten) in wie vielen Arrays (1 bis 4) des DataBuffers abgelegt werden. So sind auch Bilder mit mehr als 32 Bit speicherbar. Eine Ausnahme bildet das IndexColorModel. Ein solches Bild hat definitiv nur ein DataBuffer-Array mit 8 oder 16 Bit und ein Paletten-Array mit 4 Bytes pro Farbe.
Für den Abgleich müsstest du nun die Raster bzw. die DataBuffer-Arrays und Farbmodelle der Bilder mit dem Speicherpfad als Key in einer Map ablegen und die Daten dann immer vergleichen.


----------



## troll (16. Sep 2012)

Bernd Hohmann hat gesagt.:


> Komisch, bei mir sieht das anders aus: Ein Array von 100 Elementen belegt im ObjectOutputStream für
> 
> boolean: 123
> byte: 123
> ...



hast du meinen post überhaupt richtig gelesen ?
*es ging um den direkten speicherverbrauch im RAM während der runtime*
ich habe NIE behauptet das dies 1-zu-1 auch wirklich so für JEDE implementierung des standard-ObjectOutputStream gilt ... es war lediglich ein versuch TO zu erklären wo der ganze overhead herkommt ...
außerdem ist dein code völlig ohne aussage da du lediglich ausgibst wie groß das byte-array ist ... jedoch nicht den durch dieses verwendeten RAM ... denn dafür müsstest du schon mindestens sowas wie Runtime.freeMemory() und Runtime.maxMemory() drin haben ... was bei dir einfach fehlt ...

@Spacerat
ist mir bekannt und ich denke du hast den thread ebenfalls gelesen ... aber ich meine mich erinnern zu können das selbst UTF-chars grundsätzlich minimum 4byte verbraucht haben ... bin mir aber nicht mehr sicher ...
und was du mit LONG und DOUBLE willst weis ich nicht ganz ... denn das diese 8byte lng sind weis ich auch ... und ich habe ja auch nie was anderes behauptet ... sondern nur das halt die kleineren datentypen in mindestens ein int gepackt werden ... das LONG/DOUBLE nicht in 2x4byte zersägt werden sondern als komplette 8byte am stück im ram landen sollte klar sein ...


----------



## Spacerat (16. Sep 2012)

@troll: Ich bin mir nicht sicher, welchen Thread du meinst, aber ich habe mindestens ein mal in einem solchen ähnliches kommentiert.
Bernds Arrays sagen durchaus etwas über den direkten Speicherverbrauch der Arrays aus, würde man in solchen die 4-Byte-Regel beibehalten, wäre das sehr fatal. Long und Double hab' ich nur der Vollständigkeit halber erwähnt.
Man kann aus Bernds Zahlen sogar direkt den Overhead eines Arrays in einem ObjectStream ablesen. Der Overhead eines normalen Arrays ist bekanntlich 12 (8 Bytes für ein normales Objekt + 4 Bytes Länge), daraus folgt ein ObjectStream Overhead 11 Bytes von denen 8 widerum die "serialVersionUID" welche zwar für jede Arrayart eine andere ist, jedoch in jeder JVM dieselbe ist (betrifft nur primitive Arrays) und 3 Bytes für den voll qualifizierten Klassennamen (sofern man bei primitiven Arrays überhaupt noch von einem Namen sprechen kann).


----------



## Bernd Hohmann (16. Sep 2012)

troll hat gesagt.:


> hast du meinen post überhaupt richtig gelesen ?
> *es ging um den direkten speicherverbrauch im RAM während der runtime*



Ich hab Deinen Post schon richtig gelesen, Deine Antwort passte lediglich nicht zur Frage des Posters.

Die Serialisierung des ImageIcons geht darüber, dass ein Pixelgrabber aus den internen Daten ein byte-Array macht welches (darum mein Testcode) über einen ObjectOutputStream weggeschrieben wird.

Die erstaunliche Grösse von 110kb per Bild kommt also nicht durch ein Aufblähen der bytes auf int sondern ist der Tatsache geschuldet, dass so ein harmloses JPEG als dekomprimiertes Bytearray unerwartete Größe zeigen kann.

War also kein Angriff auf Dich sondern nur ein Hinweis auf das eigentliche Problem.

Bernd


----------



## Bernd Hohmann (16. Sep 2012)

Goldfish hat gesagt.:


> Also ich meine, ich lade Daten aus dem Netz und habe ein Bild. Und um herauszukriegen welches Bild das ist, hab ich nur meine gespeicherten Versionen. Hat java da iregendwelche Möglichkeiten da einen Abgleich zu machen?



Ich denke ich hab zumindest den halben Bahnhof verstanden: Du hast so eine Art Videoverwaltung, die Daten (insbesondere Cover- oder Plakatfotos) dazu ziehst Du dir von irgendeiner Webseite ab.

Aber warum möchtest Du das Coverfoto (oder so) mit den lokalen Daten vergleichen?

Wenn Du auf eine Ähnlichkeitsanalyse zwischen Bildern hinauswillst, wäre das Thema einer Doktorarbeit.

Bernd


----------



## troll (16. Sep 2012)

halli hallo ???

sagt bitte mal den punkt an dem ich behauptet habe das der vm-interne overhead auch für einen ObjectOutputStream gilt ? das habe ich NIE behauptet ...

ich finde den thread jetzt leider auf anhieb auch nicht so schnell in dem das mal erklärt wurde ... fakt ist jedoch das das was ich mit den 4byte gesagt habe NUR für die VM selbst und deren INTERNE datenverwaltung gilt ...
wie genau nun dieser overhead mit in einen ObjectOutputStream geschrieben wird ... oder halt eben nicht ... hängt von der jeweiligen implementierung ab ...

wenn wir das ganze mal auf die spitze treiben könnte ich genau so gut auch sagen : ich bau mir meine eigene serialisierung und lege darin fest das JEDER primitive datentyp grundsätzlich in 16bytes dargestellt wird ... und dann wird auch ein bool in 16byte dargestellt ... in einer anderen implementierung die wirklich versucht sehr wenig speicher zu verbrauchen macht sich vielleicht noch die mühe und setzt mehrer "kleinere" datentypen sinnvoll zu einem "größeren" zusammen und schiebt diesen durch einen kompressions-algo der auf grund der sinnvollen zusammstellung optimal arbeiten und die daten auf ein bruchteil reduzieren kann ...

es gibt noch x-weitere möglichkeiten ... und wenn der standard ObjectOutputStream nun mal doch so recht "effizient" ist dann is doch gut ...
worauf ich halt nur hinaus wollte war der fakt das die VM selbst intern mit ihrem HEAP sehr verschwenderisch umgeht ... und das es sicherlich durch aus möglich ist das intern schon aus diesem grund prinzip-bedingt größere datentypen für kleinere informationen genutzt werden und damit overhead erzeugen ... die antwort auf diese frage würde allerdings nur der source der nativen writeObject-methode liefern ... und da habe ich jetzt erlich gesagt keine lust mich da durch zu wühlen nur weil ich wie so oft mal wieder missverstanden werde ...

aber 110kB für ein Objekt das gleich 3 BufferedImages referenziert finde ich noch recht human ... denn folglich können die einzelnen bilder ja nur maximal 110kB / 3 (overhead mal weggelassen) groß sein ...
macht das ganze mal mit "riesigen" bildern von ner guten spiegelreflex die selbst mit JPEG und sehr hoher kompression noch mehrere MB fressen ... ich glaub da springt eher der HEAP der VM auseinander ...


----------



## Bernd Hohmann (16. Sep 2012)

troll hat gesagt.:


> sagt bitte mal den punkt an dem ich behauptet habe das der vm-interne overhead auch für einen ObjectOutputStream gilt ? das habe ich NIE behauptet ...



Die Frage des TO war "Kann es sein, dass man mit dem ObjectOutputStream DEUTLICH mehr Speicher in Anspruch nimmt, als wie wenn man die Daten (auch die bilder) jeweils einzeln speichert?" und daher bin ich davon ausgegangen, dass Deine Antwort sich darauf bezieht.



troll hat gesagt.:


> aber 110kB für ein Objekt das gleich 3 BufferedImages referenziert finde ich noch recht human ... denn folglich können die einzelnen bilder ja nur maximal 110kB / 3 (overhead mal weggelassen) groß sein ...



Ich hab jetzt in seinem Code nur zwei BufferedImages gefunden wobei eines ("movierating") bestimmt irgendwas mit einem Winzbild von 64x12 pixel (0...6 Sterne oder so) zu tun hat. Bleibt noch "cover", overhead und dingensbumsen abgezogen könnten für die Variable 100k verbleiben, wäre also ein Bild mit Seitenlänge 320x320 (=sqrt(100x1024)  ) - da würde ich sagen "passt schon" für ein Cover für eine Webseite.



troll hat gesagt.:


> macht das ganze mal mit "riesigen" bildern von ner guten spiegelreflex die selbst mit JPEG und sehr hoher kompression noch mehrere MB fressen ... ich glaub da springt eher der HEAP der VM auseinander ...



Mach ich gerade ( Projekt: TagTagGo (nichtlineare Bildbearbeitung für Eventfotografie) - Seite 4 - DFORUM ) - da dekomprimiere ich mir erstmal die JPGs in ein ImageData, zweige mir 2 verkleinerte Bilder ab und serialisiere das wie der TO auch weg (über einen SerializedImageData-Wrapper). Das hab ich auch erst "so" auf die Platte als Cache geschmissen, aber selbst von einer SSD herunter ist das Laden eine Qual sodass ich noch GZIP in den Stream reinpacken werde.

Bernd


Bernd


----------



## Spacerat (16. Sep 2012)

troll hat gesagt.:


> halli hallo ???
> 
> sagt bitte mal den punkt an dem ich behauptet habe das der vm-interne overhead auch für einen ObjectOutputStream gilt ? das habe ich NIE behauptet ...


Hast du auch nicht... zu keiner Zeit. Allerdings hast du aber auch die Arrays nicht von der 4-Byte-Regel ausgeschlossen und behauptet, Bernds Zahlen würden über die interne Speicherbelegung nichts aussagen, jedoch tun sie genau das. Dazu muss man sich allerdings fragen, wieso dort immer ein Overhead von 23 statt von 12 auftaucht und die Erklärung dafür liefert nun mal der Overhead des ObjectStreams.
[EDIT]Mal was anderes: Was haltet ihr von einer Kompression, bei der ein Pixel nicht mehr als 8 Bit (also ein Byte benötigt, aber trotzdem ca. 16,8 Millionen Farben zulässt? Hold-And-Modify - Wikipedia, the free encyclopedia. Für derartiges Vorhaben wie das des TO, ist das gar nicht mal so obsolet.[/EDIT]


----------



## Bernd Hohmann (17. Sep 2012)

Spacerat hat gesagt.:


> [EDIT]Mal was anderes: Was haltet ihr von einer Kompression, bei der ein Pixel nicht mehr als 8 Bit (also ein Byte benötigt, aber trotzdem ca. 16,8 Millionen Farben zulässt? Hold-And-Modify - Wikipedia, the free encyclopedia. Für derartiges Vorhaben wie das des TO, ist das gar nicht mal so obsolet.[/EDIT]



Woah... da haste was ausgebuddelt :idea:

HAM8 braucht für wenige Farben auch nur wenige Bytes, jede Farbe mehr wird aber quasi hinten angehängt so dass Du bei voller Nutzung auch volle Größe hast.

Im Prinzip eine Vorstufe von JPG, siehe ....

<erinnerung>
Ich glaube, dass ich den HAM8 mal auf einem Amiga im Rahmen einer TIFF32 Toolbox programmiert habe - aber das ist bestimmt 20 Jahre her und auch mehrfaches Lesen diverser Artikel zum Thema fördert keine weitere Erinnerung bei mir hervor.

Weil ich neulich ein Buch über "CCITT Recommendation T.81", Ausgabe 1990 (oder so) weggeschmissen habe vermute ich dass meine Toolbox erst eine Farbraumumwandlung und dann einen Tiefpass durchgeführt hatte ehe dann ein HAM8 Modell errechnet wurde. Mein damaliger Kollege Deniz könnte mehr dazu sagen, aber der Kontakt ist mir irgendwann mal entglitten. Jedenfalls scheint das ähnlich wie die ersten paar Stufen bei JPEG gelaufen zu sein.
<erinnerung />

Bernd


----------



## Spacerat (17. Sep 2012)

Bernd Hohmann hat gesagt.:


> HAM8 braucht für wenige Farben auch nur wenige Bytes, jede Farbe mehr wird aber quasi hinten angehängt so dass Du bei voller Nutzung auch volle Größe hast.


Naja, ich weiss nicht, wie JPEG genau funktioniert, aber mit HAM ist das wohl nicht vergleichbar. HAM ist um einiges simpler. Da gibt es keine Pässe oder ähnliches.
Die Einschränkung bei HAM ist es nämlich, dass sog. HAM-Farben (also Farben, die nicht zur 16/64 breiten Pallette gehören) über 3 Pixel hinweg modifiziert werden. Die beiden höchsten Bits eines Pixels bestimmen dabei woher die Farbinformation des aktuellen Pixels kommt.
00 - Farbinformation aus der Palette mit Index der übrigen 4 bzw. 6 Bit
01 - untere 4 Bit zum Rotkanal der vorhergehenden Farbe addieren (oder war's multiplizieren oder gar die höherwertigen Bits des Kanals? Weiss nicht mehr).
10 - untere 4 Bit zum Grünkanal der vorhergehenden Farbe addieren.
11 - untere 4 Bit zum Blaukanal der vorhergehenden Farbe addieren.

solange untere 4 Bit 0 und die oberen 2 Bit ungleich 0 sind Farbinformation aus vorhergehenden Pixel nehmen (halten).
Klar, unkomprimiert hat auch dieses Format wie jedes andere Truecolorformat auch natürlich mehr als 1 Byte pro Pixel. Komprimiert aber garantiert nur Breite * Höhe * 16 (bzw. 64 bei HAM8) * 3 Bytes, nicht gerechnet die IFF-Chunknamen nebst Längenangaben und Identifiers. Diese gehören zum Dateiformat, nicht aber zum Verfahren.


----------

