BMP-Bilder einlesen



  • Hallo c++ freunde

    ich habe da ein problem mit dem einlesen von bmp-dateien. ich möchte mit meinem script ein bild pixel für pxel in eine liste speichern, die farbwerte zu graustufen ändern und danach das ganze wieder ausgeben, zu einem bild. das eigendliche problem ist, dass mein script für bilder bis 255 pixel breite und höhe funktioniert. wenn eine länge 256 pixel hat, so erkennt das script den header schon nicht mehr richtig. die bilder haben 24 bit farbtiefe.
    ich denke, das es sinnvoll ist den quelltext mal zu posten:

    ========================================================================

    #include <iostream>
    #include <fstream>
    
    #include <cmath>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    #include <iomanip>
    
    using namespace std;
    
    ifstream inputFile("mein.bmp", ios::binary | ios::in);//oeffnet den lesestream
    ofstream file("outgoing.bmp", ios::out|ios::binary);
    int ***imagearray;//array der pixel: hoehe breite des pixels + 1ster,2ter,3ter farbwert desses
    int hoehe,breite;
    
    void sleep()
    {
    	int t;
    	for (t=0;t<100000000;++t)
    	{};
    }
    
    void printer()
    {
    	int i,j;
    	for (i=0;i<hoehe;++i)							//Ausgabe des Inhaltes des Arrays
    	{
    		for(j=0;j<breite;++j)
    		{
    			cout<<imagearray[i][j][0]<<" , "<<imagearray[i][j][1]<<" , "<<imagearray[i][j][2]<<endl;
    		}
    		cout<<endl;
    		sleep();
    	}
    };
    
    void einlesen()//das bild wird von links unten nach rechts oben ausgelesen
    {
    	unsigned char currByte;//hat den index dst
    	int dst=1,i,j,pixel=1,h;//dst ist dateistelle
    	float tripel[3];//enthaelt die 3 farbwerte eines pixels
    
    	while (dst<55)//auselen des headers
    	{
    		currByte = inputFile.get();//ein element der datei wird in currByte geschrieben
    		cout<<int(currByte)<<endl;
    		if (dst==19)
    			breite=int(currByte);
    		else if (dst==23)
    			hoehe=int(currByte);
    		++dst;
    		file.write( (char*) &currByte,1);//Uebertragen des Headers in die Datei
    	}
    
    	cout<<breite<<" - "<<hoehe<<endl;
    	imagearray=new int**[hoehe];
    	for(j=0;j<hoehe;++j)//Allokieren des Array Image, das die bildpixelinformationen speichert
    	{
    	   imagearray[j]=new int*[breite];
    	   for(i=0;i<breite;++i)
    		 imagearray[j][i]=new int[3];
    	}
    	h=0;
    	cout<<"Noch einzulesene Zeilen: "<<endl;
    	float *zeile;				//allokieren des array,
    	zeile= new float[breite];	//der eine zeile des bildes speichert.
    	const unsigned long 
    		linesize =3*breite,
    		fillbytes = (4-linesize%4)%4;
    
    	while (dst<=breite*hoehe*3+55)//abfragen der farbwerte aller bilpunkte 
    	{
    		currByte = inputFile.get();//siehe oben
    		if ((((dst-55)%(3*breite))==0)&&(dst>55)) //wenn zeile zu ende
    		{
    			for(i=0;i<breite;++i)/////// ausgabe der graustufen fuer alle pixel in einer zeile,
    				imagearray[h][i][0]=imagearray[h][i][1]=imagearray[h][i][2]=zeile[i];//Uebertragen der Grauwerte in das Array
    			cout<<hoehe-h<<", ";
    
    			for(i=0;i<fillbytes;++i)		//herausstreichen der zusatzinformationen am ende der zeile
    				currByte = inputFile.get();
    
    			++h;
    		}
    		tripel[(dst-55)%3]=int(currByte);//einlesen der farbinformation eines pixels, format: B G R
    		if ((((dst-55)+1)%3)==0) //jeden dritten eingabewert (nach b-r-g) //(dst-55) ergibt die pixelnummer, da der header(55) abgezogen wurde
    		{
    			zeile[(pixel-1)%breite]=((tripel[0]+tripel[1]+tripel[2])/3);//berechnung der graustufe, als mittelwert der farben(falls ein farbiges bild eingegeben wird), da eine graustufe bei R,G,B die selben werte hat
    			++pixel;
    		}
    		++dst;
    	}
    	cout<<endl<<"Fertig mit einlesen"<<endl;
    };
    
    void ausgeben()
    {	
    	int i,i2;
    	unsigned char currByte;
    	const unsigned long 
    		linesize =3*breite,   // 24 bit per pixel
    		fillbytes = (4-linesize%4)%4, // at line end to quad boundary
    		imagesize = (linesize+fillbytes)*hoehe;
    
    	/*struct BMPheader
    	  {
    		// file info
    		char ID[2];
    		unsigned long filesize, reserved, offset;
    
    		// BMP info
    		unsigned long  infosize, width, height;
    		unsigned short planes, bitsperpixel;
    		unsigned long  compression, imagesize, xPixelPerMeter, yPixelPerMeter,colorsUsed, colorsImportant;
    	  } header =
    	  { // file info
    		{ 'B', 'M' },
    		sizeof(header) + imagesize, // 24 bits per pixel
    		0, sizeof(header), // 0x36
    
    		// BMP info
    		40, breite, hoehe,
    		1, 24,
    		0, imagesize,
    		1000, 1000,
    		0, 0
    	  };
    	file.write( (char*)&header, sizeof(header));*/
    
    	cout<<"Noch auszugebene Zeilen: "<<endl;
    	for (i=0;i<hoehe;++i)
    	{
    		for(i2=0;i2<breite;++i2)
    		{
    			currByte=imagearray[i][i2][0];
    			file.write((char*) &currByte,1);
    
    			currByte=imagearray[i][i2][1];
    			file.write((char*) &currByte,1);
    
    			currByte=imagearray[i][i2][2];
    			file.write((char*) &currByte,1);
    		}
    
    		if(fillbytes)
    		{
    		  int zero = 0;
    		  file.write( (char*) &zero, fillbytes);
    		}
    
    		cout<<hoehe-i<<", ";
    	}
    	cout<<endl<<"Fertig mit ausgeben"<<endl<<endl;
    };
    
    void main ()
    {
    	einlesen();
    	ausgeben();
    
    }
    

    =============================================================

    ich denke, dass in meinem fall nur die void einlesen das problem darstellt.

    danke schonmal im voraus.
    wäre schön, wenn das problem behoben werden könnte.



  • unsigned char currByte;//hat den index dst
        int dst=1,i,j,pixel=1,h;//dst ist dateistelle
        float tripel[3];//enthaelt die 3 farbwerte eines pixels
    
        while (dst<55)//auselen des headers
        {
            currByte = inputFile.get();//ein element der datei wird in currByte geschrieben 
    if (dst==19)
                breite=int(currByte);// was ist mit Byte 20,21,22 ?
            else if (dst==23)
                hoehe=int(currByte);
    

    Natürlich bekommst du probleme, wenn du den Header nicht korrekt einliest.
    unsigned char geht von 0 bis 255. Mehr kann es nicht darstellen,
    also funktioniert das Skript dann nicht mehr. Du solltest evtl.
    mal den Aufbau eines Bitmapheaders dir anschauen, und dann den entsprechenden
    Datentypen auswählen.

    Du liest evtl. nur das erste Byte ein, aber die größenangabe ist evtl. mehrere
    Bytes lang.

    Devil



  • Hallo,

    Dateiformate kannst du hier suchen:

    http://www.wotsit.org/

    mfg
    v R



  • Danke erst einmal für die tipps. ich habe nun mal genauer nachgelesen und gemerkt, dass 4 bytes die breite speichern:

    if (dst==19)
                breite=int(currByte);// was ist mit Byte 20,21,22 ?
            else if (dst==23)
                hoehe=int(currByte);
    

    der kommentar hat sich damit auch erübrigt, da auch diese für die breite verwendet werden. ich muss also nicht nur eins, sondern 4 bytes für höe bzw. breite auslesen. mir ist nun aber schleirhaft, wie ich das realisieren soll, zumal ich ja mit meinen mitteln die bytes einzeln auslesen kann. ich stehe nun der sache mit den 4 bytes auszulesen ratlos gegenüber. 😕
    könntet ihr mir da auch nochmal helfen, wie man das macht?



  • Auf 32bit Plattformen ist int 4 bytes groß, du könntest es also in
    einen int einlesen, welches aber dann wieder unschön ist.
    Im Bitmap header den du gepostet hast, war es als unsigned long deklariert,
    evtl. solltest du es dann auch als unsigned long einlesen.
    Für das einlesen mehrerer Bytes musst du dir mal die funktion read von
    ostream angucken, damit kannst du daten in ein char* array einlesen.
    Schau dir evtl. mal die FAQ an, da steht mehr über Ströme und Dateien.

    Devil



  • alles klar, ich werde ma schauen. danke nochmals
    vielleicht melde ich mich ja mal wieder 🙂 ...



  • ich habe mal versucht auf dem wege des char* array einzulesen. leider gottes komme ich auf keine grünen zweig. ich scheitere einfach diese 4 byte auszulesen, um die richtige hoehe zu bekommen 😞
    bei einem 300x300 pixel bild enthält dann das "unsigned char currByteS[4]" die werte 195 - 14 - 0 - 0, wenn ich sie in integer konvertiere.
    könnte mir bitte jemand vielleicht nochmal genau sagen, wie ich aus den 4 bytes die höhe mache, oder wie ich die hoehe genau auslese?



  • Mußt es halt noch zurechtshiften. Unter DOS/Windows wird das niederstwertige Byte zuerst gespeichert. Müßte also 0-0-14-195 sein, dann kannst's in ein int umrechnen.

    P.S.: Oder war's Intel-spezifisch? Weiß nicht mehr...



  • devil81 schrieb:

    Auf 32bit Plattformen ist int 4 bytes groß, du könntest es also in
    einen int einlesen, welches aber dann wieder unschön ist.
    Im Bitmap header den du gepostet hast, war es als unsigned long deklariert,
    evtl. solltest du es dann auch als unsigned long einlesen.

    Am besten wären hier aber typedefs, damit man es leicht für andere Plattformen anpassen kann.



  • mit dem shiften bringts aber irgendwie nix. wenn ich das char array mit 195-14-0-0 umwandle, erhalte ich für die breite 1244964. wenn ich das char array mit 0-0-14-195 umwandele erhalte ich 1244960 pixel breite.
    (wollte nur nochmal anmerken, dann die angaben 195, 14 und 0 schon mit "int(currByteS2[1])" geschrieben sind)

    hilfe ich komme hier nicht mehr weiter... 😞



  • Die Werte verstehe ich nicht.
    195-14-0-0 entspricht C3 0E 00 00 in Hex. Das sind 3 Milliarden-irgendwas. Mußt es halt so zusammenbasteln, daß da 00 00 0E C3 (= 3779 dezimal) steht. Ist das die richtige Breite deiner Bitmap?



  • nein, das bild ist 300x300 pixel groß...



  • hier mal die header:
    das ist header nr1

    struct BITMAPFILEHEADER { /* bmfh */
    UINT Type;
    DWORD Size;
    UINT Reserved1;
    UINT Reserved2;
    DWORD OffBits;
    };

    das ist header nr2(direkt unterm ersten header)

    struct BITMAPINFOHEADER { /* bmih */
    DWORD Size;
    LONG Width;
    LONG Height;
    WORD Planes;
    WORD BitCount;
    DWORD Compression;
    DWORD SizeImage;
    LONG XPelsPerMeter;
    LONG YPelsPerMeter;
    DWORD ClrUsed;
    DWORD ClrImportant;
    };

    und so kann man einen header einlesen(da das scheinbar auch ein problem war):

    ifstream a("hallo.bmp");
    BITMAPFILEHEADER header1;
    a.read(reinterpret_cast<char*>(&header1),sizeof(BITMAPFILEHEADER));
    

    alternative:

    ifstream a("hallo.bmp");
    BITMAPFILEHEADER header1;
    a>>header1.Size;
    a>>header1.Width;
    //usw
    

    achja, die header sind in der windows.h bereits enthalten^^



  • ja, das ist ja ok, nur ist mir schleierhaft, wie ich die werte dort hineinbekomme...



  • mein edit bringt klarheit 🤡



  • Dann musst du aber sicher sein, dass die struct packed ist ⚠ .



  • erstmal danke für die super hilfe von otze...
    nur nimmt er zum beispiel sieses UINT nicht an. da kommt dann immer sone fehlermeldung wie

    error C2146: Syntaxfehler : Fehlendes ';' vor Bezeichner 'Type'

    muss ich da vorher noch großartig was neues importieren oder so?

    @mastah: was meinst du denn mit packed? das kenne ich nicht...



  • Man muss einstellen, dass die Member des Struct-Objektes direkt hintereinander im Speicher liegen, also ohne padding-bytes. Das ist aber compilerspezifisch.



  • sry, doppelpost 🙄



  • alx0 schrieb:

    erstmal danke für die super hilfe von otze...
    nur nimmt er zum beispiel sieses UINT nicht an. da kommt dann immer sone fehlermeldung wie

    error C2146: Syntaxfehler : Fehlendes ';' vor Bezeichner 'Type'

    muss ich da vorher noch großartig was neues importieren oder so?

    das sind andere bezeichner für die standardtypen, schau mal in der windef.h da sind die typen drin(imho gibts für linux eine äquivalente liste)


Log in to reply