Bitmap Header auslesen
-
Hoffe ich bin hier im richtigen Forum gelandet.
Also ich habe folgendes Problem: komme irgendwie mit den im Header eines BMP ausgelesenen Daten nicht sonderlich gut zurecht. Eigentlich möchte ich nur wissen, wie groß das Bild nun eigentlich ist. Dazu habe ich das hier gefunden:Dez Hex
18 12 LONG 4 Byte biWidth Breite der Bitmap in Pixel.
22 16 LONG 4 Byte biHeight Der Betrag gibt die Höhe der Bitmap in Pixel an.Also lese ich das ganze einfach mal ein und hau es in einen Buffer.
const char *bmp = readFile("test.bmp"); //readFile arbeitet hier noch mit fopen, aber ich bekomm das richtige zurück cout << System::Convert::ToDouble(bmp[18]) << " " << System::Convert::ToDouble(bmp[19]) << " " << System::Convert::ToDouble(bmp[20]) << " " << System::Convert::ToDouble(bmp[21]) << endl;
gibt mir dann zurück: -128 7 0 0.
Nur was soll ich jetzt damit anfangen um an die Width des Bildes zu kommen? Kann mir da jemand helfen, also zumindest n Denkanstoss wäre sehr nett.
-
Dieser Thread wurde von Moderator/in volkard aus dem Forum C++ (auch C++0x und C++11) in das Forum C++/CLI mit .NET verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Du schreibst doch selber, dass die Breite bzw. Höhe nicht in jeweils einem Byte (=char) gespeichert ist.
Die Lösung sind also Casts, entweder in eine jeweils 4 Byte große Variable (uin32_t*) oder eben Verwendung von BITMAPFILEHEADER und BITMAPINFOHEADER (google).const char *bmp = readFile("test.bmp"); const BITMAPINFOHEADER* infoheader = reinterpret_cast<const BITMAPINFOHEADER*>(bmp+sizeof(BITMAPFILEHEADER)); // infoHeader.biWidth und infoHeader.biHeight enthalten nun die Werte
Ich bin mir im Moment nicht ganz sicher, ob man noch den Umweg über den BITMAPCOREHEADER gehen sollte, um zu prüfen, ob es sich um die Urversion, slso eine OS/2-Bitmap, handelt. Dann wären Breite und Höhe jeweils nur zwei Byte groß und die Offsets natürlich andere.
Außerdem, warum liest du eigentlich die komplette Datei ein, wenn dich doch nur der Dateikopf interessiert?
-
Danke für die schnelle Antwort, das war genau, wonach ich gesucht habe.
Arbeite aber im Moment nur mit neueren Bitmaps, sollte so also auch schon reichen.Die ganze Datei wird eingelesen, weil ich hinterher die Bilder Pixelweise miteinander vergleichen will.
Das klappt aber auch schon. Hatte nur vorher immer die System::Drawing::Bitmap genommen um height und width zu bekommen, jedoch bei mehr als 1000 Bildern dauerte das etwas lang.
So sollte das ja wesentlich schneller gehen.
-
const char *readFile(const char *path) { FILE * fFile; FILE * pFile; char *buffer = new char[7000000]; char *buffer2 = new char[10000000]; unsigned long bufferpoint = 0; pFile = fopen (path , "rb"); fFile = fopen (path , "rb"); if (pFile == NULL) perror ("Error opening file"); else { filesize = fread(buffer2, 1, 7000000, fFile); free (buffer2); fclose (fFile); fread(buffer , filesize,1 , pFile); fclose (pFile); return buffer; } return 0; } int comparePicturesB(string path1, string path2) { int defectPixC = 0; const char *bmp = readFile(path1); //funktioniert so net, aber die Typumwandlung hab ich mal weg gelassen const char *bmp2 = readFile(path2); //s.o. const BITMAPINFOHEADER* infoheader = reinterpret_cast<const BITMAPINFOHEADER*>(bmp+sizeof(BITMAPFILEHEADER)); int widthbild = infoheader->biWidth; int heightbild = infoheader->biHeight; int byteLine = widthbild * 3; long bild1data = widthbild * heightbild * 3; int point, d, point2; int b = 0; double c; string color; int colorvalue, colorvalue2, colorfault; for (int i = bild1data; i >= 0; i--) { if (bmp[i] != bmp2[i]) { char pix1 = bmp[i]; char pix2 = bmp2[i]; d = i % 3; switch (d) { case 0:{ color = "B"; break;} case 1:{ color = "G"; break;} case 2:{ color = "R"; break;} } colorvalue = Convert::ToDouble(pix1); colorvalue2 = Convert::ToDouble(pix2); if ((colorvalue > 256) || (colorvalue2 > 256)) { mostfaulty = colorvalue; } if (colorvalue <= colorvalue2) { colorfault = colorvalue2 - colorvalue; } else { colorfault = colorvalue - colorvalue2; } if (colorfault > 127) { colorfault = fabs(Convert::ToDouble(colorfault -256)); } c = ((i / byteLine)); point = fabs(ceil(c) +2) - heightbild; point2 = (((i % byteLine) /3) -18); //defectPicDiff.push_back(0); if (point2 < 0) { point2 = point2 + 1 + widthbild; } if (point < 0) { point = point++ - point *2; } if (colorfault > mostfaulty) { mostfaulty = colorfault; worstpicture = path2.c_str(); worstcolor = color; worstpix[0] = point2; worstpix[1] = point; } if (colorfault > defectPixC) { defectPicPath[defectPicturesCount] = path2; defectPicColor[defectPicturesCount] = color; defectPicDiff[defectPicturesCount] = colorfault; defectPicPixX[defectPicturesCount] = point2; defectPicPixY[defectPicturesCount] = point; defectPixC = colorfault; } b++; } } delete(bmp); delete(bmp2); if (b > 0) return b; else{ return -2; } }
so sieht nun meine komplette Lösung aus, falls es wen interessiert.
Ansonsten bin ich für alle Verbesserungsvorschläge offen.
Bin noch nicht lange in c++ und grade bei Speicherverwaltung muss ich noch viel lernen.
Wenn keinem was einfällt, Problem ist gelöst.
Ach ja, arbeite derzeit nur mit 24bit BMPs, sonndst müsste man entsprechend andere Werte berechnen.
-
Hallo Amluriel,
um es ehrlich zu sagen, dein Code ist ganz großer Mist:
- du mischst C mit C++/CLI (!)
- deine Speicherverwaltung ist komplett falsch:
- Rückgabe eines Zeigers auf lokale Arrays -> undefined behaviour!!!
- Aufruf von 'delete' auf ein lokales Stack-Objekt!?!Und auch deine comparePicturesB-Funktion ist unnötig kompliziert.
Zuerst einmal solltest du dich für genau 1 Sprache entscheiden: C oder C++ (aber nicht C++/CLI)! (Convert::ToDouble ist hier völlig unnötig!)
Außerdem ist mir nicht klar, wieso dein Code jetzt schneller sein soll, als System::Drawing::Bitmap, da du ja bisher 2 mal dieselbe Datei einliest (nur um die Datei-Größe zu bestimmen, gibt es viel bessere Methoden -> z.B. für C: fseek und ftell).
Aber persönlich würde ich dir sogar zu C# raten - da gäbe es dann schon zwei interessante Komponenten:
GetPixel und SetPixel um Längen geschlagen. 800 mal schneller
Bitmap-Manipulation (MemBitmap)Ich weiß jeder Anfang ist schwer, aber wenn so elementare Fehler in einem Programm sind, so sollte man sich zuerst intensiv mit den Grundlagen beschäftigen!
Trotzdem viel Erfolg noch - und ich hoffe, ich habe dir jetzt dadurch nicht die Freude am Programmieren (und Lernen
genommen.
-
Keine Sorge, bin noch Anfänger und freu mich über jede Art der Kritik.
Das da noch C-Code drin ist, wusste ich schon. Ich will halt erstmal nur was lernen.
Und freu mich, wenn es compiliert und funktioniert.fseek und ftell hab ich mir angeschaut und den Code verändert; danke für den Tipp!
Ein allgemeintes Danke, dass Du dir meinen Mist überhaupt anschaust.Könntest mir vielleicht noch nen Tipp zu der Speicherverwaltung sagen, also ob da noch Fehler sind? Ich wäre sehr dankbar.
-
Die Datei zweimal zu öffnen und sie dann auch noch zweimal komplett zu lesen
ist ja wirklich eine schräge Nummer.Am kompatibelsten wäre wohl tatsächlich die fseek() Lösung, wobei ich die lstat() Variante bevorzuge, da ich das rumgeschiebe nicht mag.
Unter Windows sähe das so aus:
// 1 pFile = fopen(path, "rb"); fseek(pFile, 0, SEEK_END); filesize = ftell(pFile); fseek(pFile, 0, SEEK_SET);
oder:
// 2 struct _stat st; int result = _stat(path, &st); filesize = st.st_size;
Wenns nur für Windows sein soll (performant):
// 3 HANDLE hFile; DWORD dwFileSize; hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); dwFileSize = GetFileSize(hFile, NULL); ... CloseHandle(hFile);
Alle drei Varianten liefern mit VS2010 genau das gleiche Ergebnis ...
-
Dieser Thread wurde von Moderator/in Jochen Kalmbach aus dem Forum C++/CLI mit .NET in das Forum WinAPI verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Warum ist das Performanter:
HANDLE hFile; DWORD dwFileSize; hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); dwFileSize = GetFileSize(hFile, NULL); CloseHandle(hFile);
Als das hier?
pFile = fopen(path, "rb"); fseek(pFile, 0, SEEK_END); filesize = ftell(pFile); fseek(pFile, 0, SEEK_SET);
Stimmt das?
-
Also die Frage interessiert mich auch. Will ja ne möglichst schnelle Möglichkeit finden, deswegen scheidet auch cC# aus.
Zum anderen von The69 hab ich auch noch ne Frage:
Th69 schrieb:
- Aufruf von 'delete' auf ein lokales Stack-Objekt!?!
Sicher, dass es da im Stack landet?
Sobald ich das delete wegnehme, klappt der Bildvergleich bei ca. 1000 HD Bitmaps nicht mehr, weil mein Speicher voll ist.Durfte das ganze (in einer verbesserten Version als) oben heute dank eines Freundes auf nem schnellen Server testen. 7000 BMPs in 15 min. War für mich OK, aber wenn noch wem was einfällt, bin ich für alles offen (auch wenn ich den Server wohl nicht noch einmal bekomme)
-
Th69 schrieb:
- Rückgabe eines Zeigers auf lokale Arrays -> undefined behaviour!!!
- Aufruf von 'delete' auf ein lokales Stack-Objekt!?!Du musst dich irren. Die werden mit new angelegt.