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
-
-
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 nr1struct 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 wieerror 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 wieerror 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)