PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++]*.bmp-Dateien bearbeiten


pippo
2005-03-31, 18:43:41
Zur Übung haben wir gerade die Aufgabe bekommen, von einer vorgegebenen template.bmp den Header zu übernehmen, eine Koch-Kurve einzufügen und und das ganze als Koch.bmp zu speichern.

Es gab zwar vor 2 Jahren schonmal nen Thread zum Bearbeiten von .bmp-Dateien, aber wie ich finde ist dabei nichts rausgekommen. Zumindest nichts, womit ich etwas anfangen könnte. Der Aufbau einer .bmp-Datei ist mir mehr oder weniger klar nur fehlt es mir grundlegend an Ahnung :D wie man den Header ausliest, ne neue Datei erstellt, den Header wieder einfügt und schwarze Pixel für die Linien der Koch-Kurve erzeugt.

Wär schön, wenn da jemand nen Link/Code hätte

Trap
2005-03-31, 19:09:51
Ich würde schummeln:
Die Datei komplett kopieren und danach die entsprechenden Pixel mit schwarzen überschreiben.

http://www.devx.com/tips/Tip/14262
http://www.cplusplus.com/ref/iostream/fstream/ <= da brauchst du seekp und put

Coda
2005-04-02, 20:52:57
http://openil.sourceforge.net/

pippo
2005-04-03, 12:36:33
Wo genau auf der Seite sollte ich denn fündig werden? Konnte auf anhieb nichts passendes finden.

@ Trab
Die Frau reisst mir den Kopf runter, wenn ich schummle :) Die hat mich letztens schon angesch... weil ich nen Funktionsaufruf nicht Buchstabe für Buchstabe übernommen hab ;D

pippo
2005-04-05, 19:00:08
Hab mir ne Header-Datei angelegt, die wie folgt aussieht:

#pragma pack(1)
struct bmInfoHeader {
long biSize; // Laenge des Info Header
long biWidth; // * Anzahl Pixel horizontal
long biHeight; // * Anzahl Pixel vertikal
short biPlanes; // muss eine 1 enthalten
short biBitCount; // Anzahl Bits je Pixel, hier: 8
long biCompression; // hier: nicht komprimiert
long biSizeImage; // Anzahl der Bytes fuer das Bild
long biXPelsPerMeter; // hor. Aufloesung in Pixel/Meter
long biYPelsPerMeter; // vert. Aufloesung in Pixel/Meter
long biClrUsed; // Anzahl tatsaechlich benutzter Farben (0)
long biClrImportant; // Anzahl unbedingt benoetigter Farben (0)
};

struct bmFileHeader { // (includiert bmInfoHeader)
char bfType1, bfType2; // dort steht 'B' 'M'
long bfSize; // Dateilaenge
short bfReserved1;
short bfReserved2;
long bfOffBits; // * Abstand zum Beginn der Bilddaten
bmInfoHeader bmInfo; // der Info Header schliesst an
};
#pragma pack ()


Ausschnitt aus dem Programm:


...
ifstream Original("template.bmp", ios::binary | ios::in);
ofstream Koch("Koch-Kurve.bmp", ios::binary | ios::out);

bmInfoHeader bIH;
bmFileHeader bFH;

// MAIN PROGRAM ***************************************************************
int main()
{

char * Header = new char[sizeof(bmFileHeader)];
long Breite = bIH.biWidth, Hoehe = bIH.biHeight, Position;
...
}


Wieso wird aber Breite und Hoehe auf 0 gesetzt? Muss man das ganze noch mit der Originaldatei verbinden? Dachte da wird automatisch die zum lesen geöffnete Datei genommen.

Was mich noch etwas verwundert: An der Position 10 steht als 4-Byte-Wert (1024) die Länge des Headers, bzw. die Position der Bilddaten. Jedoch beginnen die Pixel erst bei 1078. Was steht nun in den 54Bytes dazwischen? Konnte hierzu bisher nichts finden, obwohl ich mir schon einige Seite über den Aufbau einer .bmp durchgelesen hab

DocEW
2005-04-06, 02:46:06
http://openil.sourceforge.net/
Cooler Humor...
So give DevIL a try today. The worst that could happen is that it formats your harddrive after sending porn e-mail to all your friends...just kidding. =]:lol:

pippo
2005-04-09, 10:54:19
So, für alle die es interessiert, wie man nen Header ausliest und ne Koch-Kurve in C++ erzeugt, hier der Code:

// Übungsblatt 2.cpp : Koch-Kurve

// DEFINES ********************************************************************

// HEADER FILES ***************************************************************
#include <iostream>
#include <fstream>
#include <cmath>
#include "bmp.h"
using namespace std;

// FUNKTIONS ******************************************************************
void OriginalEinlesen(char OffBits[], char Pixel[256][512]);
void KochSchreiben(char OffBits[], char Pixel[256][512]);
void RecKoch(int A[], int E[], short Tiefe, char Pixel[256][512]);
void ZeicheStrich(int A[], int E[], char Pixel[256][512]);
void Tausche(int A[], int E[]);

// STREAMS ********************************************************************
ifstream Original("template.bmp", ios::binary | ios::in);
ofstream Koch("Koch-Kurve.bmp", ios::binary | ios::out);

// VARIABLES ******************************************************************
bmFileHeader bFH;

// MAIN PROGRAM ***************************************************************
int main() {
if (!Original.good()) { //Prüfen, ob sich das Original ordnungsgemäß öffnen lässt
cout << "Fehler beim Oeffnen der Datei" << endl;
return 1;
}
Original.read(reinterpret_cast<char*>(&bFH),sizeof(bmFileHeader)); //Einlesen des Headers
Original.seekg(ios::beg); //Zurück zu Pos. 1

char * OffBits = new char[bFH.bfOffBits]; //Array für alle Daten bis zum 1. Pixel
char Pixel[256][512]; //Array für alle Pixel
int A[2], E[2]; //Arrays für den Anfangs- und Endpunkt
short int Tiefe;

cout << "Informatik II - Uebungsblatt 2" << endl << "------------------------------" << endl << endl;
cout << "Geben sie die Anzahl der Schritte ein: "; //Bestimmung der Rekursionstiefe
cin >> Tiefe;
OriginalEinlesen(OffBits, Pixel); //Einlesen des Originals in das jeweilige Array
Original.close(); //Schließen der Originaldatei

A[0]=1; //Festlegen der Grundlinie
A[1]=1;
E[0]=(bFH.biWidth-1);
E[1]=1;

RecKoch(A, E, Tiefe, Pixel); //Berechnung aller Punkte
KochSchreiben(OffBits, Pixel); //Schreiben des neuen Bildes
Koch.close(); //Schließen der erzeugten Datei
delete OffBits; //Löschung des dynamischen Arrays
return 0;
}

void OriginalEinlesen(char OffBits[], char Pixel[256][512]) {
for(long i=0; i<bFH.bfOffBits; i++) Original.get(OffBits[i]);
for(i=0; i<bFH.biHeight; i++) {
for(long j=0; j<bFH.biWidth; j++) Original.get(Pixel[i][j]);
}
}

void KochSchreiben(char OffBits[], char Pixel[256][512]) {
for (long i=0; i<bFH.bfOffBits; i++) Koch.put(OffBits[i]);
for(i=0; i<bFH.biHeight; i++) {
for(long j=0; j<bFH.biWidth; j++) Koch.put(Pixel[i][j]);
}
}

void RecKoch(int A[], int E[], short Tiefe, char Pixel[256][512]) {
int B[2], C[2], D[2];

if(Tiefe==0) ZeicheStrich(A, E, Pixel); //Rekursionsbasis
else {
B[0] = A[0] + (E[0]-A[0])/3; //Berechnung des linken Punktes der Grundlinie des Dreiecks
B[1] = A[1] + (E[1]-A[1])/3;
D[0] = A[0] + ((E[0]-A[0])/3)*2; //Berechnung des rechten Punktes der Grundlinie des Dreiecks
D[1] = A[1] + ((E[1]-A[1])/3)*2;
C[0] = B[0] + int(0.5*(D[0]-B[0]) - sqrt(3.0)/2*(D[1]-B[1])); //Berechnung der Spitze des Dreiecks
C[1] = B[1] + int(sqrt(3.0)/2*(D[0]-B[0]) + 0.5*(D[1]-B[1]));
RecKoch(A, B, Tiefe-1, Pixel); //Rekursionsschritte
RecKoch(B, C, Tiefe-1, Pixel);
RecKoch(C, D, Tiefe-1, Pixel);
RecKoch(D, E, Tiefe-1, Pixel);
}
}

void ZeicheStrich(int A[], int E[], char Pixel[256][512]) {
double Steigung, y, x;
int j, a=0;

if (E[0]-A[0]<0) { //Prüfen, ob der Anfangspunkt links vom Endpunkt ist
Tausche(A, E); //ggf. tauschen beider Punkte
a++;
}
y =(E[1]-A[1]); //Berechnung des y-Wertes der Steigung
x =(E[0]-A[0]); //Berechnung des x-Wertes der Steigung
if (x==0.0) Steigung=0.0; //Abfangen der Division durch 0
else Steigung=y/x;
if (Steigung<0.0){ //Zeichnet die Linie von oben nach unten
Steigung*=(-1); //Negieren der Steigung, damit j nicht negativ wird
for(int i=A[0], k=0; i<=E[0]; i++, k++) {
j=A[1]-int(Steigung*k);
Pixel[j][i]=0; //Ändern der berechneten Pixel auf 0 (schwarz)
}
}
else { //Zeichnet die Linie von unten nach oben
for(int i=A[0], k=0; i<=E[0]; i++, k++) {
j=A[1]+int(Steigung*k);
Pixel[j][i]=0;
}
}
if (a==1) Tausche(A, E); //ggf. zurücktauschen beider Punkte
}

void Tausche(int A[], int E[]) {
int * Temp = new int [2];

Temp[0]=A[0];
Temp[1]=A[1];
A[0]=E[0];
A[1]=E[1];
E[0]=Temp[0];
E[1]=Temp[1];
delete Temp;
}


bmp.h
#pragma pack(1)
struct bmFileHeader {
char bfType1, bfType2; // dort steht 'B' 'M'
long bfSize; // Dateilaenge
short bfReserved1; // Reserve
short bfReserved2; // Reserve
long bfOffBits; // * Abstand zum Beginn der Bilddaten
//Beginn InfoHeader
long biSize; // Laenge des Info Header
long biWidth; // Anzahl Pixel horizontal
long biHeight; // Anzahl Pixel vertikal
short biPlanes; // muss eine 1 enthalten
short biBitCount; // Anzahl Bits je Pixel, hier: 8
long biCompression; // Komprimierung
long biSizeImage; // Anzahl der Bytes fuer das Bild
long biXPelsPerMeter; // hor. Aufloesung in Pixel/Meter
long biYPelsPerMeter; // vert. Aufloesung in Pixel/Meter
long biClrUsed; // Anzahl tatsaechlich benutzter Farben (0)
long biClrImportant; // Anzahl unbedingt benoetigter Farben (0)
};
#pragma pack ()

Sollte jemand Verbesserungsvorschläge haben oder mir erklären können, wie man die Pixel in einen vector packt, damit man nicht so an bestimmte Auflösungen gebunden ist, soll er es bitte sagen

zeckensack
2005-04-09, 22:41:46
Generell:
1)für Dateiformate Wotsit (http://www.wotsit.org) fragen.
2)bei BMPs unbedingt das bescheuerte Alignment/Padding beachten, sonst funzt die Software nur mit den zufällig getesteten Größen. Näheres dazu unter Punkt 1.

Abe Ghiran
2005-04-09, 23:27:55
2)bei BMPs unbedingt das bescheuerte Alignment/Padding beachten, sonst funzt die Software nur mit den zufällig getesteten Größen. Näheres dazu unter Punkt 1.

Dem kann ich mich nur anschließen. Zu ärgerlich, daß sogar der Beispielcode in manchen Büchern fehlerhaft ist, z.B. "OpenGL Game Programming".
Ziemlich doof, wenn man als noob ein Fenster hat, dessen Breite nicht ein vielfaches von 4 ist (wie die Beispiele in dem Buch) und die Screenshots auf einmal komische Streifen enthalten :mad:. Hat mich damals bestimmt ein Wochenende gekostet, bis ich das mit dem alignment raushatte.

Grüße, Jan

pippo
2005-04-10, 02:49:02
Danke, aber das hab ich schon gewusst. Da ich aber im Moment noch nicht weiß wie man die Pixel in einem vector speichert, hab ich das ganze rausgelassen. Wir benutzen eh eine vorgegebene template.bmp, die stehts 512x256 groß ist.

Wär aber super wenn jemand nen guten Link hätte, in dem das Erstellen eines zweidimensionalen vector erklärt wird

Trap
2005-04-10, 11:17:18
Es kommt drauf an was für ein 2D-Array man haben möchte.

Entweder vector<T> und dann mit rechnen (a[i][j]=a[i*zeilenlänge+j], wie bei C-Arrays) oder mit vector<vector<T> >.

pippo
2005-04-11, 21:30:03
// DEFINES ********************************************************************

// HEADER FILES ***************************************************************
#include <iostream>
#include <fstream>
#include <cmath>
#include <vector>
#include "bmp.h"
using namespace std;

// FUNKTIONS ******************************************************************
void OriginalEinlesen(vector<char> &OffBits, vector< vector<char> > &Pixel, short NullBytes);
void KochSchreiben(vector<char> &OffBits, vector< vector<char> > &Pixel, short NullBytes);
void RecKoch(int A[], int E[], short Tiefe, vector< vector<char> > &Pixel);
void ZeicheStrich(int A[], int E[], vector< vector<char> > &Pixel);
void Tausche(int A[], int E[]);

// STREAMS ********************************************************************
ifstream Original("template.bmp", ios::binary | ios::in);
ofstream Koch("Koch-Kurve.bmp", ios::binary | ios::out);

// VARIABLES ******************************************************************
bmFileHeader bFH;

// MAIN PROGRAM ***************************************************************
int main() {
if (!Original.good()) { //Prüfen, ob sich das Original ordnungsgemäß öffnen lässt
cout << "Fehler beim Oeffnen der Datei" << endl;
return 1;
}
Original.read(reinterpret_cast<char*>(&bFH),sizeof(bmFileHeader)); //Einlesen des Headers
Original.seekg(ios::beg); //Zurück zu Pos. 1

int A[2], E[2]; //Arrays für den Anfangs- und Endpunkt
short Tiefe, NullBytes;

A[0]=1; //Festlegen der Grundlinie
A[1]=1;
E[0]=(bFH.biWidth-1);
E[1]=1;
if(bFH.biWidth%4==0) NullBytes=0;
else NullBytes=short(4-bFH.biWidth%4);
vector<char> OffBits(bFH.bfOffBits); //vector für alle Daten bis zum 1. Pixel
vector< vector<char> > Pixel(bFH.biHeight, vector<char>(bFH.biWidth+(bFH.biWidth%4))); //vector für alle Pixel

cout << "Informatik II - Uebungsblatt 2" << endl << "------------------------------" << endl << endl;
cout << "Geben sie die Rekursionstiefe ein: "; //Bestimmung der Rekursionstiefe
cin >> Tiefe;

OriginalEinlesen(OffBits, Pixel, NullBytes); //Einlesen des Originals in den jeweiligen vector
Original.close(); //Schließen der Originaldatei
RecKoch(A, E, Tiefe, Pixel); //Berechnung aller Punkte
KochSchreiben(OffBits, Pixel, NullBytes); //Schreiben des neuen Bildes
Koch.close(); //Schließen der erzeugten Datei
return 0;
}

void OriginalEinlesen(vector<char> &OffBits, vector< vector<char> > &Pixel, short NullBytes) {
for(long i=0; i<bFH.bfOffBits; i++) Original.get(OffBits[i]);
for(i=0; i<bFH.biHeight; i++) {
for(long j=0; j<(bFH.biWidth+NullBytes); j++) Original.get(Pixel[i][j]);
}
}

void KochSchreiben(vector<char> &OffBits, vector< vector<char> > &Pixel, short NullBytes) {
for (long i=0; i<bFH.bfOffBits; i++) Koch.put(OffBits[i]);
for(i=0; i<bFH.biHeight; i++) {
for(long j=0; j<(bFH.biWidth+NullBytes); j++) Koch.put(Pixel[i][j]);
}
}

void RecKoch(int A[], int E[], short Tiefe, vector< vector<char> > &Pixel) {
int B[2], C[2], D[2];

if(Tiefe==0) ZeicheStrich(A, E, Pixel); //Rekursionsbasis
else {
B[0] = A[0] + (E[0]-A[0])/3; //Berechnung des linken Punktes der Grundlinie des Dreiecks
B[1] = A[1] + (E[1]-A[1])/3;
D[0] = A[0] + ((E[0]-A[0])/3)*2; //Berechnung des rechten Punktes der Grundlinie des Dreiecks
D[1] = A[1] + ((E[1]-A[1])/3)*2;
C[0] = B[0] + int(0.5*(D[0]-B[0]) - sqrt(3.0)/2*(D[1]-B[1])); //Berechnung der Spitze des Dreiecks
C[1] = B[1] + int(sqrt(3.0)/2*(D[0]-B[0]) + 0.5*(D[1]-B[1]));
RecKoch(A, B, Tiefe-1, Pixel); //Rekursionsschritte
RecKoch(B, C, Tiefe-1, Pixel);
RecKoch(C, D, Tiefe-1, Pixel);
RecKoch(D, E, Tiefe-1, Pixel);
}
}

void ZeicheStrich(int A[], int E[], vector< vector<char> > &Pixel) {
double Steigung, y, x;
int j, a=0;

if (E[0]-A[0]<0) { //Prüfen, ob der Anfangspunkt links vom Endpunkt ist
Tausche(A, E); //ggf. tauschen beider Punkte
a++;
}
y =(E[1]-A[1]); //Berechnung des y-Wertes der Steigung
x =(E[0]-A[0]); //Berechnung des x-Wertes der Steigung
if (x==0.0) Steigung=0.0; //Abfangen der Division durch 0
else Steigung=y/x;
if (Steigung<0.0){ //Zeichnet die Linie von oben nach unten
Steigung*=(-1); //Negieren der Steigung, damit j nicht negativ wird
for(int i=A[0], k=0; i<=E[0]; i++, k++) {
j=A[1]-int(Steigung*k);
Pixel.at(j).at(i)=0; //Ändern der berechneten Pixel auf 0 (schwarz)
}
}
else { //Zeichnet die Linie von unten nach oben
for(int i=A[0], k=0; i<=E[0]; i++, k++) {
j=A[1]+int(Steigung*k);
Pixel.at(j).at(i)=0;
}
}
if (a==1) Tausche(A, E); //ggf. zurücktauschen beider Punkte
}

void Tausche(int A[], int E[]) {
int * Temp = new int [2];

Temp[0]=A[0];
Temp[1]=A[1];
A[0]=E[0];
A[1]=E[1];
E[0]=Temp[0];
E[1]=Temp[1];
delete Temp;
}

So, hab den Code jetz noch etwas geändert. Jetz werden die NullBytes beachtet und die Pixelgröße kann nun beliebig sein