[BC++] Problem mit Gaußfilter bei Farbbildern (BMP)

Bl@ckD0G

Lieutenant
Registriert
Nov. 2002
Beiträge
730
Ich benutze den Borland C++ Version 6 da damit Objektorientiertes Programmieren kinderleicht ist.

Habe nun folgendes Problem mit meinem Gauss Filter.

Beim Filtern werden die Farben verändert!

Er glättet zwar wunderbar, aber wie gesagt die Farben ändern sich dadurch auch. Bei s/w Bildern bleiben die Grauwerte wie gehabt.

Ich denke ich mache irgendwas falsch beim Zugriff auf die Farben. Soweit ich weiß liegen die Farben RGB nebeneinander quasi in einem Pixel. Deswegen kopiere ich auch mein Bild in ein dynamisches UCHAR - Array, mit 3*Width (Breite des Bildes) und versuche dann jeden Farbkanal einzeln zu bearbeiten. Aber irgenwo hab ich da nen Wurm drin und finde ihn nicht.

Code folgt:


Code:
if(OpenDialog1->FileName != ""){

 BYTE* SpaltenPtr;
        
     CImage pic_gauss((Image1->Width)*3, Image1->Height);   //CImage Objekt anlegen //zum arbeiten

        for(int i=0; i<=Form1->Image1->Height-1; ++i){  //Bild in mein CImage Objekt //kopieren

        SpaltenPtr=(BYTE*)Form1->Image1->Picture->Bitmap->ScanLine[i];
                for(int j = 0; j<=(Form1->Image1->Width-1)*3; ++j)
                        pic_gauss.ret_Bild()[i][j]=SpaltenPtr[j];
                        }


        pic_gauss.gauss_filter();   //filter aufrufen Sprung zum 2. Code


        for(int i=0; i<=Form1->Image1->Height-1; ++i){     // das geänderte Bild in mein //ursprüngliches Bild kopieren

        SpaltenPtr=(BYTE*)Form1->Image1->Picture->Bitmap->ScanLine[i];
                for(int j = 0; j<=(Form1->Image1->Width-1)*3; ++j)
                        SpaltenPtr[j]=pic_gauss.ret_Bild()[i][j];
        }

        Image1->Refresh();  // Bild refreshen...
        }
        else
        ShowMessage("Kein Bild geladen!");

Jetzt Sprung in die CImage.cpp, dort wo der Filter bearbeitet wird.

Code:
void CImage::gauss_filter() {
	
	//Maske 1 2 1 2 4 2 1 2 1
       CImage GAUSS(m_width,m_height);

	vector<int> vec_mask_r;  // jeweils ein Vector für jeden Farbkanal rgb
        vector<int> vec_mask_g;
        vector<int> vec_mask_b;

	for(int i = 1; i< m_height-1; ++i)
		for(int j = 1; j < (m_width-1); ++j){

			vec_mask_r.push_back(m_pBild[i-1][j-1]);
			vec_mask_r.push_back(2*m_pBild[i-1][j]);
			vec_mask_r.push_back(m_pBild[i-1][j+1]);
			vec_mask_r.push_back(2*m_pBild[i][j-1]);
			vec_mask_r.push_back(4*m_pBild[i][j]);
			vec_mask_r.push_back(2*m_pBild[i][j+1]);
			vec_mask_r.push_back(m_pBild[i+1][j-1]);
			vec_mask_r.push_back(2*m_pBild[i+1][j]);
			vec_mask_r.push_back(m_pBild[i+1][j+1]);


                        vec_mask_g.push_back(m_pBild[i-1][j-1]);
			vec_mask_g.push_back(2*m_pBild[i-1][j]);
			vec_mask_g.push_back(m_pBild[i-1][j+1]);
			vec_mask_g.push_back(2*m_pBild[i][j-1]);
			vec_mask_g.push_back(4*m_pBild[i][j]);
			vec_mask_g.push_back(2*m_pBild[i][j+1]);
			vec_mask_g.push_back(m_pBild[i+1][j-1]);
			vec_mask_g.push_back(2*m_pBild[i+1][j]);
			vec_mask_g.push_back(m_pBild[i+1][j+1]);


                        vec_mask_b.push_back(m_pBild[i-1][j-1]);
			vec_mask_b.push_back(2*m_pBild[i-1][j]);
			vec_mask_b.push_back(m_pBild[i-1][j+1]);
			vec_mask_b.push_back(2*m_pBild[i][j-1]);
			vec_mask_b.push_back(4*m_pBild[i][j]);
			vec_mask_b.push_back(2*m_pBild[i][j+1]);
			vec_mask_b.push_back(m_pBild[i+1][j-1]);
			vec_mask_b.push_back(2*m_pBild[i+1][j]);
			vec_mask_b.push_back(m_pBild[i+1][j+1]);

		       GAUSS.ret_Bild()[i][j] = gauss(vec_mask_r);  /soll dem roten kanal den neuen //berechneten Wert zuweisen.  Analog für die beiden folgenden Zeilen.
                        GAUSS.ret_Bild()[i][j+1] = gauss(vec_mask_g);
                        GAUSS.ret_Bild()[i][j+2] = gauss(vec_mask_b);   // Funktion zum berechnen //des gauss aufrufen


			vec_mask_r.clear();
                        vec_mask_g.clear();
                        vec_mask_b.clear();
		}

	       	memcpy(m_pBuffer, GAUSS.m_pBuffer, m_width*m_height);
}

// GAUSS - RECHNER 

int CImage::gauss(std::vector<int> vec_gauss){

	int sum = 0;
	for(int i = 0; i < vec_gauss.size() ; ++i)
		sum = sum + vec_gauss[i];

	return (sum / 16);


}

Ich hoffe der QuellText ist verständlich. Bei Unklarheiten bitte fragen.

Hoffe jemand kann mir auf die Sprünge helfen, danke.

Gut nacht erstmal...
 
Hi,
nur mal so (habe keine Ahnung von Deinem Code) kann es sein das es nicht RGB BGR ist oder das der Type nicht 24Bit sondern 32Bit hat BGRA.

Nur als Versuch gemeint.

Ich selber programmiere 'nur' in Assembler unter LINUIX

Noch eine schöne Zeit

Joshy
 
Du kannst nicht einfach so mit einem Pointer über die Bilddaten reiten - das geht schief. Falls es ein True-Color bild ist, sollte der normale Aufbau ABGR sein. Wobei es keinen wirklichen Alpha-Channel gibt. Diesen musst Du einfach nur kopieren. Bei 16 Bit können Dir aber schon exotischere Formate über den Weg laufen, wie z.B. 565. (5 Bits Blau, 6 Bits Grün, 5 Bits Rot). Du solltest Dich also erstmal über das Bildformat schlau machen.

Danach solltest Du die Bilddaten in ein einheitliches Format konvertieren, den Filter anwenden, und wieder zurückkonvertieren.
 
Ich benutze nur Bitmaps und mit BGR anstatt RGB das hab ich schon versucht.
Ich konvertiere jedes Bild in ein 24Bit Bitmap-Format (egal ob das OriginalBild 8Bit oder 32Bit war), dann hab ich 8 Bit für jeden Kanal. Ein Alphakanal sollte eigentlich nicht existieren, zumindest wird in Photoshop keiner angezeigt. Das Bild wird in 24Bit gelassen, also nicht zurück konvertiert.

EDIT: nur so als Info, GAUSS.ret_Bild()[j] macht folgende:
Ich Rufe die Funktion meines CImage Objektes auf, die mir einfach nur einen UCHAR ** auf mein UCHAR* (von meinem dyn. Array) zurück gibt, quasi Zeilen*Spalten. Dies muss so sein, da es eine private Membervariable ist.
 
Zuletzt bearbeitet:
Hab es nun hingekommen hatte einen Denkfehler.
Trotzdem danke für eure Hilfe.
 
Dazu ist zu wissen, dass bei einem RGB 24Bit Truecolorbild (8bit / Kanal) die Farben quasi nebeneinander oder hintereinander liegen. Deswegen musste ich das Array auch 3* so breit machen, wie die Breite des Originalbildes war, als ich die Bilddaten in ein neues dyn. UCHAR Array kopiert habe.

Mein Fehler war, das ich im Prinzip immer auf den selben Pixel zugrgriffen habe in dem ich:

Code:
for(int i = 1; i< m_height-1; ++i)
for(int j = 1; j < (m_width-1); ++j){

vec_mask_r.push_back(m_pBild[i-1][j-1]);
vec_mask_r.push_back(2*m_pBild[i-1][j]);
vec_mask_r.push_back(m_pBild[i-1][j+1])
....

vec_mask_g.push_back(m_pBild[i-1][j[B]+1[/B]-1]);
vec_mask_g.push_back(2*m_pBild[i-1][j[B]+1[/B]]);
vec_mask_g.push_back(m_pBild[i-1][j+1[B]+1[/B]])

selbe für vec_mask_b nur [B]+2[/B]
....

geschrieben habe. Ich dachte ich würde so auf die verschiedenen Kanäle RGB zugreifen. Aber ich bin immer komplett durch die gesammte Breite gelaufen, was nicht nur nicht das machte, was es tun sollte, sondern auch ennorm lange gadauert hat. Das ist eigentlich auch kein Wunder, wenn man von einem Bild 1280x1024x24 ausgeht (gute 3MB!).

Naja, jedenfalls - ein CODE sagt mehr als 1000 Worte - ist es so richtig:

Code:
for(int i = 1; i< m_height-1; ++i)
for(int j = 1; j < (m_width-1)[B]/3[/B]; ++j){

vec_mask_r.push_back(m_pBild[i-1][j-1[B]*3[/B]]);
vec_mask_r.push_back(2*m_pBild[i-1][j[B]*3[/B]]);
vec_mask_r.push_back(m_pBild[i-1][j+1[B]*3[/B]])
....

vec_mask_g.push_back(m_pBild[i-1][(j-1)[B]*3+1[/B]]);
vec_mask_g.push_back(2*m_pBild[i-1][j[B]*3+1[/B]]);
vec_mask_g.push_back(m_pBild[i-1][(j+1)[B]*3+1[/B]]);

analog für vec_mask_b nur [B]*3+2[/B]

Mit [j] greife ich auf den pixel selbst zu, mit [j]*3 auf den roten kanal und mit [j]*3 +1 auf den Grünen und mit [j]*3 +2 auf den Blauen. Wenn ihr die beiden oberen Quellcods vergleicht seht ihr, was ich falsch gemacht habe.

Ich möchte bei der Gelegenheit mal die Funktionsweise eines Filters beschreiben, einigen mag das sicher nichts neues sein, aber wer vielleicht mal einen selbst schreiben möchte und nicht weiß wie es geht, weiß vielleicht gleich wie es geht. :p

Also ein Filter besitzt eine Maske, die über das komplette Bild "geschoben" wird und dadurch für jeden Pixel einen neuen Wert berechnet. Der standard Gauß-Filter hat z.bsp. die Maske 1/16*(1 2 1 2 4 2 1 2 1)^T (T steht für Transponiert, das müsst ihr euch wie eine 3x3 Matrix vorstellen) also kann ich wenn ich von meinem Pixel [j] ausgehe jeden Pixel der um diesen in einer s.g. 8er Nachbarschaft (erklärt sich eingentlich durch den Namen) herum liegt zugreifen. Dies geht dann ganz einfach wie folgt:
Code:
i = HeightIndex (y-achse)
j = WidthIndex (x-achse)

Dann sieht die Maske allgemein so aus.

[i-1][j-1]  [i-1][j]  [i-1][j+1]

[i][j-1]     [B][i][j][/B]   [i][j+1]

[i+1][j-1]  [i+1][j]  [i-1][j+1]

So kann auf jeden umliegenden Pixel und den zu bearbeitenden Pixel zugreifen. im falle des Gauß-Filters kommt noch zu jedem Pixel ein Multiplikator dazu.

Code:
i = HeightIndex (y-achse)
j = WidthIndex (x-achse)

Dann sieht die Maske allgemein so aus.

1*[i-1][j-1]  2*[i-1][j]  1*[i-1][j+1]

2*[i][j-1]     [B]4*[i][j][/B]   2*[i][j+1]

1*[i+1][j-1]  2*[i+1][j]  1*[i-1][j+1]

Dieser Filter ist "gewichtet" d.h. die Summe der Faktoren ergibt nicht Null sondern in diesem Fall 16.

Berechnung des neuen Pixels geht nun wie folgt:

Code:
[i][j] = 1/16 * (1*[i-1][j-1] + 2*[i-1][j] + 1*[i-1][j+1] + 2*[i][j-1]  +   [B]4*[i][j][/B]  + 2*[i][j+1] +1*[i+1][j-1] +  2*[i+1][j] + 1*[i-1][j+1] ).

Das ist die grundlegene Funktionsweise eines Filteres. Manche Filter sind gewichtet, manche nicht. Bei Filtern deren Summe 0 ist wie z.bsp. beim Laplace (0 1 0 1 -4 1 0 1 0)^T wird natürlich nicht durch 0 geteilt sonder einfach durch 1 also einfach die Summe nehmen. Dazu kommt, dass bei manchen Filtern noch ein Offset hinzuaddiert werden muss (i.d.R. 128) um z.bsp. Kanten hervorzuheben.

Ich hoffe dieser kleine CrashCourse hilft einigen weiter.
Und ich hoffe ich habe deine Frage damit beantwortet @d.j.peters ;)

mfg
 

Ähnliche Themen

Antworten
11
Aufrufe
4.193
A
Zurück
Oben