Hallo Besucher, der Thread wurde 7,5k mal aufgerufen und enthält 35 Antworten

letzter Beitrag von Yadgar am

C++: Problem mit Umrechnung von RGB-Bildern in C 64-Multicolor

  • Hi(gh)!


    Seit einigen Monaten bastele ich an einem C++-Programm (auf dem PC unter Linux und Windows, nicht auf dem C 64!), das aus (vorher auf 160 x 200 skalierten) RGB-Bildern C 64-Bilder im Multicolor-Modus erzeugen soll. Die bloße Umrechnung auf die C 64-Palette funktioniert einwandfrei - allerdings müssen danach ja die insgesamt 1000 4 x 8-Pixel-Bereich des Bildes darauf untersucht werden, ob sie mehr als 4 Farben enthalten und gegebenfalls die überzähligen Farben durch eine oder mehrere der vier häufigsten Farben ersetzt werden.


    In meinem Programm (yip - Yadgar's Image Processor, das Programm hat auch noch viele andere Funktionen, die nichts mit C 64-Retrografik zu tun haben), ist dafür die Funktion c64multicolor_correct() (ganz am Ende des Quelltexts mit dem Hauptprogramm und den Funktionsdefinitionen) zuständig.


    Zur besseren Verdeutlichung hier erst mal ein Link auf das komplette
    Paket mit Quelltexten, Log und einem Beispielbild:


    http://www.rock-o-data.de/yip_20180707.tar.gz


    Das Bild muss mit folgendem yip-Kommando bearbeitet werden:


    yip -c64 koeln_160x200.tga multicolor 0 1


    Konkret passiert gegenwärtig leider Folgendes: wenn ich eine Reihe von 4 x
    8-Pixelbereichen auf überzählige Farben prüfe, dann stimmen die Pixelanzahlen für die gefundenen
    Farben, teilweise sogar die gefundenen Farben selbst nicht mit dem
    (vorläufig ohne diese Korrektur) abgespeicherten Bild überein.


    Zwei Beispiele:



    Im 4 x 8-Bereich 8/128 bis 12/136 sollten laut gespeichertem "C
    64-Rohbild" gefunden werden:


    1 x türkisgrün - rgb <48,230,198>
    4 x "weiß" - rgb <253,254,252>
    12 x hellgrau - rgb <164,167,162>
    10 x mittelgrau - rgb <112,116,111>
    4 x schwarz - rgb <0, 0, 0>
    1 x dunkelblau - rgb <33,27,174>



    Tatsächlich findet c64multicolor_correct():


    1 x türkisgrün - rgb <48,230,198>
    4 x "weiß" - rgb <253,254,252>
    12 x hellgrau - rgb <164,167,162>
    10 x mittelgrau - rgb <112,116,111>
    4 x dunkelgrau - rgb <66,69,64> // Abweichung!
    1 x dunkelblau - rgb <33,27,174>


    Ein anderes Beispiel, 4 x 8-Bereich 16/128 bis 20/136:


    Sollwerte laut gespeichertem Bild:


    14 x mittelgrau - rgb <112,116,111>
    8 x schwarz - rgb <0, 0, 0>
    7 x dunkelgrau - rgb <66,69,64>
    2 x hellrot - rgb <254, 74, 87>
    1 x orange - rgb <184, 65, 4>



    Tatsächlich gefunden:


    14 x mittelgrau - rgb <112,116,111>
    4 x schwarz - rgb <0,0,0> // Abweichung!
    7 x dunkelgrau - rgb <66,69,64>
    4 x hellgrau - rgb <164,167,162> // Abweichung!
    2 x hellrot - rgb <254,74,87>
    1 x orange - rgb <184,65,4>



    Hier stimmt nicht einmal die Anzahl der Farben überein!


    Fällt euch dazu etwas ein?


    Bis bald im Khyberspace!


    Yadgar

  • Ohne in Deinen Source geschaut zu haben: sieht nach einem Bug aus ^^ . Wie würde ich die Suche angehen: hast Du Dich schon mal mit Unittests beschäftigt? Wenn nicht, wäre jetzt der Moment zu überlegen, ein entsprechendes Framework aufzusetzen. Damit kannst Du dann direkt die Methode die Probleme macht mit Cornercases füttern, also z.B. alle Pixel im zu untersuchenden Bereich schwarz, bis auf eins in der Ecke etc. Dadurch schaltest Du alle anderen Fehlerquellen (wie das Lesen der Bilddatei, evtl. weiterverarbeiten vor dem Aufruf der Funktion etc.) aus und kannst den Fehler eingrenzen.


    Ich weiß, das hilft Dir jetzt nicht direkt, aber wenn ich Dir den Fehler suchen sollte, wäre genau das der erste Schritt den ich machen würde. Wenn das Unittest-Framework erstmal steht, wird es Dir noch sehr oft bei zukünftiger Fehlersuche helfen. Daher ist das Aufsetzen hervorragend investierte Zeit.

  • Ohne in Deinen Source geschaut zu haben: sieht nach einem Bug aus ^^ . Wie würde ich die Suche angehen: hast Du Dich schon mal mit Unittests beschäftigt? Wenn nicht, wäre jetzt der Moment zu überlegen, ein entsprechendes Framework aufzusetzen. Damit kannst Du dann direkt die Methode die Probleme macht mit Cornercases füttern, also z.B. alle Pixel im zu untersuchenden Bereich schwarz, bis auf eins in der Ecke etc. Dadurch schaltest Du alle anderen Fehlerquellen (wie das Lesen der Bilddatei, evtl. weiterverarbeiten vor dem Aufruf der Funktion etc.) aus und kannst den Fehler eingrenzen.


    Ich weiß, das hilft Dir jetzt nicht direkt, aber wenn ich Dir den Fehler suchen sollte, wäre genau das der erste Schritt den ich machen würde. Wenn das Unittest-Framework erstmal steht, wird es Dir noch sehr oft bei zukünftiger Fehlersuche helfen. Daher ist das Aufsetzen hervorragend investierte Zeit.

    Wie soll ich denn die Funktion mit Cornercases füttern, ohne diese Dateien vorher eingelesen zu haben? Die Fehlerquelle "Einlesen der Datei" lässt sich doch gar nicht ausschalten!


    Zwischenzeitlich habe ich ein Testprogramm erstellt, das wirklich nur aus den für dieses Problem benötigten Funktionen bestand - und da war dann von den oben aufgelisteten Bespielen nur noch das erste fehlerhaft... absurd!


    Je länger ich mich mit diesem ganzen Kram herumquäle, desto mehr sehe ich ein: ich tauge nicht zum Programmierer! Ich kann das einfach nicht, ich bin zu dumm dazu! Daher vergesst auch alles, was Ihr im Februar über meine größenwahnsinnigen Phantasien zu einer Retrocomputing-Datenbank gelesen habt, die wird es auch niemals geben...

  • Zwischenzeitlich habe ich ein Testprogramm erstellt, das wirklich nur aus den für dieses Problem benötigten Funktionen bestand - und da war dann von den oben aufgelisteten Bespielen nur noch das erste fehlerhaft... absurd!

    Klingt ein wenig nach einer nicht initialisierten Variablen.


    Edit: Ausserdem würde ich empfehlen, beim Compilieren das Flag "-Wall" (oder vergleichbar) zu verwenden und darauf hinzuarbeiten, dass der Compiler keine Warnungen mehr auswirft.

  • Klingt ein wenig nach einer nicht initialisierten Variablen.
    Edit: Ausserdem würde ich empfehlen, beim Compilieren das Flag "-Wall" (oder vergleichbar) zu verwenden und darauf hinzuarbeiten, dass der Compiler keine Warnungen mehr auswirft.

    Der Compiler (g++) schmeißt auch so schon keine Warnungen... will heißen, formell ist alles in Ordnung!


    Hiermal die Testversion meines Programmes mit der Klassendefinition und einem Beispielbild zum Testen:
    http://www.rock-o-data.de/yiptest_20180713.tar.gz

  • Visual Studio haut mir reihenweise Warnings um die Ohren, alleine die automatische Konversion von double in float müsstest du angemeckert kriegen :)


    Benutzt du den Debugger? Bei der derzeitigen Einschränkung auf Koordinaten, die die eingetragen hast, ist doch schon das erste Pixel auf einem Wert, der laut deiner Angabe ungültig ist.


    Da würde ich ansetzen.


    Entweder sind die Original-Koordinaten daneben oder die Palettenfunktionen zerwursten die Farbwerte.

  • Nach der Statistik gibt es ein Problem mit schwarzen Pixeln. Erster Schritt: Nachsehen ob das stimmt, oder ob er die Farben beliebig zulost. Zweiter Schritt, falls es wirklich an Schwarz liegt: Gucken, was bei dieser Farbe besonderes auftritt. Die Vermutung mit der nicht initialisierten Variablen klingt recht plausibel.


    Dritter Schritt: Mapping auf vier Farben nicht nach Häufigkeit vornehmen. Du kannst hellgrau, Hellblau und Hellgrün auf eins mappen, ohne allzu viel zu verlieren- aber in dem einen roten Pixel steckt wahrscheinlich die gesamte Information in dem Bild... ich vermisse bei solchen Farbtiefereduktionen meist eine manuelle Vorgabe der Zielpalette. Nur mal so als Idee...

  • Nach der Statistik gibt es ein Problem mit schwarzen Pixeln. Erster Schritt: Nachsehen ob das stimmt, oder ob er die Farben beliebig zulost. Zweiter Schritt, falls es wirklich an Schwarz liegt: Gucken, was bei dieser Farbe besonderes auftritt. Die Vermutung mit der nicht initialisierten Variablen klingt recht plausibel.


    Dritter Schritt: Mapping auf vier Farben nicht nach Häufigkeit vornehmen. Du kannst hellgrau, Hellblau und Hellgrün auf eins mappen, ohne allzu viel zu verlieren- aber in dem einen roten Pixel steckt wahrscheinlich die gesamte Information in dem Bild... ich vermisse bei solchen Farbtiefereduktionen meist eine manuelle Vorgabe der Zielpalette. Nur mal so als Idee...

    O.k., dann hier besser mal das ganze Programm, nicht bloß die Testversion: http://www.rock-o-data.de/yip_20180707.tar.gz


    Die Palettenzuweisung funktioniert ja, und wenn ich Bilder im "Blockmodus" (also 40 x 25 Pixel) erzeuge, sind die Ergebnisse auch einwandfrei... vielleicht sollte ich mich damit abfinden, dass Multicolor einfach zu schwierig für mich ist...

  • Du hast mal erwähnt, dass du das auch beim C 64-Programmieren machst. Wie gehst du denn da genau vor?

    Ich habs mal in einem separaten Thread zusammengefasst.

  • Anbei mal nen Screenshot von meinem alten KOALA (MC bitmap) konverter.
    Neben der Moeglichkeit im RGB oder YUV Raum zu waehlen und in Echtzeit Helligkeit/Kontrast etc anzupassen,
    kann man auch manuell zuordnen. Das sieht man rechts bei den drei Farbspalten.
    Ganz links C64, dann die optimalen 16 Farben vom Bild und rechts daneben die Zuordnung zu einer C64 Farbe (automatische Vorauswahl).
    Aber es verbessert das Ergebnis oft ganz gewaltig wenn man da eben von Hand ersetzen kann. Man klickt einfach die Farbe in der C64 Spalte an und dann auf die anzupassende Farbe rechts.

  • Anbei mal nen Screenshot von meinem alten KOALA (MC bitmap) konverter.
    Neben der Moeglichkeit im RGB oder YUV Raum zu waehlen und in Echtzeit Helligkeit/Kontrast etc anzupassen,
    kann man auch manuell zuordnen. Das sieht man rechts bei den drei Farbspalten.
    Ganz links C64, dann die optimalen 16 Farben vom Bild und rechts daneben die Zuordnung zu einer C64 Farbe (automatische Vorauswahl).
    Aber es verbessert das Ergebnis oft ganz gewaltig wenn man da eben von Hand ersetzen kann. Man klickt einfach die Farbe in der C64 Spalte an und dann auf die anzupassende Farbe rechts.

    Ist das Programm scriptingfähig? D. h., lassen sich damit auch ganze Bildsequenzen automatisch bearbeiten? Ich hatte yip nämlich hauptsächlich programmiert, um damit lange Folgen von aus Videos extrahierten Einzelbildern zu bearbeiten...

  • Wie soll ich denn die Funktion mit Cornercases füttern, ohne diese Dateien vorher eingelesen zu haben? Die Fehlerquelle "Einlesen der Datei" lässt sich doch gar nicht ausschalten!

    Deine Funktion erwartet ja Vektoren von Vektoren von Pixeln ^^ :



    Code
    1. void c64multicolor_correct(vector<vector<pixel> >& c64img, vector<vector<pixel> >& c64_area)

    Die kannst Du doch als Test einfach im Programm erzeugen, dazu musst Du nichts von Datei lesen.

  • Ganz links C64, dann die optimalen 16 Farben vom Bild und rechts daneben die Zuordnung zu einer C64 Farbe (automatische Vorauswahl).
    Aber es verbessert das Ergebnis oft ganz gewaltig wenn man da eben von Hand ersetzen kann. Man klickt einfach die Farbe in der C64 Spalte an und dann auf die anzupassende Farbe rechts.

    Schick :) Sowas könnte ich bei meinen selbst gemischten Konsolenfarben gebrauchen. Gibts da ein Stichwort für, wie man automatische Vorauswahl macht?

  • Sowas könnte ich bei meinen selbst gemischten Konsolenfarben gebrauchen. Gibts da ein Stichwort für, wie man automatische Vorauswahl macht?

    Ich würde sowas wohl zuerst mit einem K-Means-Clustering-Algorithmus versuchen.

  • Ich würde sowas wohl zuerst mit einem K-Means-Clustering-Algorithmus versuchen.

    Echt jetzt? Ich würde einfach die C64-Farbe mit dem geringsten euklidischen Abstand zur Originalfarbe nehmen, also die Summe der quadrierten Differenzen für R, G und B minimieren.

  • Deine Funktion erwartet ja Vektoren von Vektoren von Pixeln ^^ :


    Code
    1. void c64multicolor_correct(vector<vector<pixel> >& c64img, vector<vector<pixel> >& c64_area)

    Die kannst Du doch als Test einfach im Programm erzeugen, dazu musst Du nichts von Datei lesen.

    Das funktioniert leider auch nicht:


    #include <stdlib.h>
    #include <iostream>
    #include <string.h>
    #include <fstream>
    #include <vector>
    #include <math.h>
    #include <string>
    #include "yip.conf"
    #include "yip_classes.h"



    using namespace std;



    bool loadTGA(vector<vector<pixel> >&, string);
    bool saveTGA(vector<vector<pixel> >&, string);
    double coldist(rgb, rgb);
    void qsrt(vector<short>&, vector<rgb>&, unsigned int, unsigned int); // descending!
    int partition(vector<short>&, vector<rgb>&, unsigned int, unsigned int); // descending!
    void c64multicolor_correct(vector<vector<pixel> >&, vector<vector<pixel> >&);



    vector<rgb> palette;



    int main()
    {
    vector<vector<pixel> > image;
    vector<vector<pixel> > c64image;
    vector<vector<pixel> > c64_4x8area;
    c64_4x8area.resize(8);

    /*if (!loadTGA(image, "koeln_160x200_c64_multicolor_floydsteinberg.tga"))
    return -1; */

    image.resize(8);
    short a=0;
    pixel p;

    for (short y=0; y<8; y++)
    {
    for (short x=0; x<8; x++)
    {
    p.set_all(palette[a%7].red, palette[a%7].green, palette[a%7].blue);
    image[y].push_back(p);
    a++;
    }
    }

    c64multicolor_correct(image, c64_4x8area);



    if (!saveTGA(image, "koeln_160x200_c64_multicolor_floydsteinberg_corrected.tga"))
    return -1;

    return 0;
    }



    bool loadTGA(vector<vector<pixel> > &img, string filename)
    {

    ifstream Source;
    unsigned short idfield, width, height, corner;
    // rgb triple;

    Source.open(filename.c_str(), ios::binary);
    if (!Source)
    {
    cerr << filename;
    cerr << " cannot be opened!\n";
    return false;
    }
    char ch;

    Source.get(ch); // reads in first byte: length of image identification field (0 - 255)
    idfield = (unsigned short)(unsigned char)ch;;

    Source.seekg(10, ios_base::beg); // file pointer is set to 10th byte - begin of Y origin of image
    Source.get(ch);
    corner = (unsigned short)(unsigned char)ch;
    Source.get(ch);
    corner += ((unsigned short)(unsigned char)ch)*256;

    // Source.seekg(12, ios_base::beg); // file pointer is set to 12th byte
    Source.get(ch);
    width = (unsigned short)(unsigned char)ch;
    Source.get(ch);
    width += ((unsigned short)(unsigned char)ch)*256;
    Source.get(ch);
    height = (unsigned short)(unsigned char)ch;
    Source.get(ch);
    height += ((unsigned short)(unsigned char)ch)*256;



    unsigned int imgsize = width*height;
    unsigned int c; // counter

    img.resize(height);

    c=0;
    pixel p;

    Source.seekg(18+idfield, ios_base::beg); // file pointer is set to first byte of pixel data
    while (Source.get(ch) && Source.tellg() <= 18+idfield+imgsize*3 )
    {
    switch (c%3)
    {
    case 0:
    p.set_blue((unsigned char)ch);
    // cout << (int)(unsigned char)p.get_blue() << endl;
    break;
    case 1:
    p.set_green((unsigned char)ch);
    // cout << (int)(unsigned char)p.get_green() << endl;
    break;
    case 2:
    p.set_red((unsigned char)ch);
    // cout << (int)(unsigned char)p.get_red() << endl;
    if (corner != 0)
    img[(c/3)/width].push_back(p);
    else
    img[height-1-(c/3)/width].push_back(p);
    }
    c++;
    }
    return true;
    }



    bool saveTGA(vector<vector<pixel> > &img, string filename)
    {
    unsigned short width = img[0].size();
    unsigned short height = img.size();
    unsigned int c;
    unsigned char widthLo = width%256;
    unsigned char widthHi = width/256;
    unsigned char heightLo = height%256;
    unsigned char heightHi = height/256;
    unsigned char header[18] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 };

    header[10] = heightLo;
    header[11] = heightHi;
    header[12] = widthLo;
    header[13] = widthHi;
    header[14] = heightLo;
    header[15] = heightHi;
    header[16] = 24;
    header[17] = 32; // image origin in upper left corner!

    ofstream Destination;

    Destination.open(filename.c_str(), ios::binary);
    if (!Destination)
    {
    cerr << filename;
    cerr << " cannot be opened!\n";
    return false;
    }

    c = 0;

    while(Destination.tellp() < 18+width*height*3)
    {
    while (Destination.tellp() < 18)
    {
    Destination.put(header[c]);
    c++;
    }
    if (Destination.tellp() == 18)
    c = 0; // couter set back to count solely color tripels
    switch(c%3)
    {
    case 0:
    Destination.put(img[(c/3)/width].at((c/3)%width).get_blue());
    break;
    case 1:
    Destination.put(img[(c/3)/width].at((c/3)%width).get_green());
    break;
    case 2:
    Destination.put(img[(c/3)/width].at((c/3)%width).get_red());
    break;
    }
    c++;
    }
    return true;
    }




    double coldist(rgb c1, rgb c2)
    {
    double reddpow, greendpow, bluedpow, powsum;
    reddpow = pow((c2.red-c1.red), 2);
    greendpow = pow((c2.green-c1.green), 2);
    bluedpow = pow((c2.blue-c1.blue), 2);
    powsum = reddpow + greendpow + bluedpow;

    return sqrt(powsum);
    }





    void qsrt(vector<short> &cfs, vector<rgb> &colors, unsigned int l, unsigned int r)
    {
    unsigned int q;

    if (l < r)
    {
    q = partition(cfs, colors, l, r);
    qsrt(cfs, colors, l, q);
    qsrt(cfs, colors, q+1, r );
    }
    }



    int partition(vector<short> &cfs, vector<rgb> &colors, unsigned int l, unsigned int r)
    {
    short x, buf1;
    unsigned int i, j;
    rgb buf2;

    x = cfs[(l+r)/2];
    i = l-1;
    j = r+1;
    while (1)
    {
    do
    j--;
    while (cfs[j] < x);
    do
    i++;
    while (cfs[i] > x);

    if (i < j)
    {
    buf1 = cfs[i];
    buf2 = colors[i];
    cfs[i] = cfs[j];
    colors[i] = colors[j];
    cfs[j] = buf1;
    colors[j] = buf2;
    }
    else
    return j;
    }
    }



    void c64multicolor_correct(vector<vector<pixel> >& c64img, vector<vector<pixel> >& c64_area)
    {
    short a, b, i, j, k, l, m, n, colnum_area, freq;
    pixel p;
    rgb c;
    vector<rgb> areacols;
    vector<short> colfreqs;
    vector<rgb> fourcolors;
    vector<rgb> excesscolors;
    bool match;




    for (a=0; a<8; a+=8)
    {
    for (b=0; b<8; b+=4)
    {
    for (i=0; i<8; i++)
    c64_area[i].resize(0);

    for (i=a; i<a+8; i++) // pixels from image are copied into temporary 4 by 8 pixel vector
    {
    for (j=b; j<b+4; j++)
    {

    p.set_red(c64img[i].at(j).get_red());
    p.set_green(c64img[i].at(j).get_green());
    p.set_blue(c64img[i].at(j).get_blue());
    c64_area[i-a].push_back(p);
    }
    }
    areacols.resize(0);
    for (k=0; k<8; k++) // vector containing different colors in temporary 4 by 8 pixel vector is created
    {
    for (l=0; l<4; l++)
    {
    c.red = c64_area[k].at(l).get_red();
    c.green = c64_area[k].at(l).get_green();
    c.blue = c64_area[k].at(l).get_blue();



    match=false;
    if (k==0 && l==0)
    {
    areacols.push_back(c);
    }
    else
    {
    for (m=0; m<areacols.size(); m++)
    {
    if (c.red == areacols[m].red && c.green == areacols[m].green && c.blue == areacols[m].blue)
    {
    match=true;
    }
    }
    if (match == false) // new color found
    {
    areacols.push_back(c);
    }
    }
    }
    }
    cout << endl;

    colnum_area = areacols.size();
    // cout << "Anzahl der Farben: " << colnum_area << endl;
    if (colnum_area > 4)
    {
    fourcolors.resize(4);
    excesscolors.resize(colnum_area-4);
    colfreqs.resize(colnum_area);
    for (m=0; m<colfreqs.size(); m++)
    colfreqs[m] = 0;
    m=0;
    for (k=0; k<8; k++)
    {
    for (l=0; l<4; l++)
    {
    if ( k == 0 && l == 0)
    {
    colfreqs[0] = 1;
    }
    else
    {
    c.red = c64_area[k].at(l).get_red();
    c.green = c64_area[k].at(l).get_green();
    c.blue = c64_area[k].at(l).get_blue();
    while (!(c.red == areacols[m].red && c.green == areacols[m].green && c.blue == areacols[m].blue))
    m++;
    colfreqs[m]++;
    m = 0;
    }
    }
    }
    /* cout << "------------------------" << endl;
    cout << "Farbh" << auml << "ufigkeiten:" << endl;
    cout << "------------------------" << endl;
    for (m=0; m<colfreqs.size(); m++)
    cout << colfreqs[m] << endl;
    cout << endl; */
    cout << "---------------------------------------------------------------" << endl;
    cout << "Zelle " << b << "/" << a << " bis " << b+4 << "/" << a+8 << endl;
    cout << "---------------------------------------------------------------" << endl;
    for (m = 0; m < colnum_area; m++)
    cout << "Farbe #" << m << ": rgb <" << areacols[m].red << "," << areacols[m].green << "," << areacols[m].blue << "> - " << colfreqs[m] << " Pixel" << endl;
    qsrt(colfreqs, areacols, 0, colfreqs.size()-1);

    /* for (m = 0; m < colnum_area; m++)
    cout << colfreqs[m] << " Pixel: rgb <" << areacols[m].red << "," << areacols[m].green << "," << areacols[m].blue << ">" << endl; */


    for (m = 0; m < 4; m++)
    fourcolors.push_back(areacols[m]);
    for (m = 4; m < colnum_area; m++)
    excesscolors.push_back(areacols[m]);



    /* rgb closest = fourcolors[0];
    for (m = 4; m < colnum_area; m++)
    {
    for (n = 0; n < 4; n++)
    {
    if (coldist(areacols[m], fourcolors[n]) < coldist(areacols[m], closest))
    {
    closest = fourcolors[n];
    cout << "rgb <" << areacols[m].red << "," << areacols[m].green << "," << areacols[m].blue << "> wird ersetzt durch <"<< fourcolors[n].red << "," << fourcolors[n].green << "," << fourcolors[n].blue << ">" << endl;
    }
    }
    } */




    /* cout << Uuml << "bersch" << uuml << "ssige Farben:" << endl;
    for (m = 0; m < colnum_area-4; m++)
    cout << "rgb <" << excesscolors[m].red << "," << excesscolors[m].green << "," << excesscolors[m].blue << ">" << endl; */


    for (k=0; k<8; k++)
    {
    for (l=0; l<4; l++)
    {
    match = false;
    m = 0;
    c.red = c64_area[k].at(l).get_red();
    c.green = c64_area[k].at(l).get_green();
    c.blue = c64_area[k].at(l).get_blue();
    while (match == false && m < colnum_area-4)
    {
    // cout << c.red << " " << c.green << " " << c.blue << " | " << excesscolors[m].red << " " << excesscolors[m].green << " " << excesscolors[m].blue << endl;
    if ((c.red == excesscolors[m].red) && (c.green == excesscolors[m].green) && (c.blue == excesscolors[m].blue))
    match = true;
    m++;
    }
    // cout << match << endl;
    if (match == true)
    {
    rgb closest = fourcolors[0];
    for (n=0; n<4; n++)
    {
    if (coldist(c, fourcolors[n]) < coldist(c, closest))
    closest = fourcolors[n];
    }
    //cout << a+k << endl;
    //cout << b+l << endl;
    c64img[a+k].at(b+l).set_all(closest.red, closest.green, closest.blue);
    }
    }
    }
    }
    }
    }
    }


    Ich bekomme immer einen Speicherzugriffsfehler zur Laufzeit...