Home | Course Index | C2 Code Comments | C2 FAQs |
Course IP_MFC: Image Processing with C++/MFC
|
|
Let me know what you think |
Projekt histo1 mit leerem Fenster Präprozessorbefehle und Deklarationen in histo1Doc.h und histo1View.h Code für Serialize in Chisto1Doc Code für OnDraw in Chisto1View Mausereignisse in CHisto1View Beispielbilder |
Microsoft Visual Studio starten
File New Project Project Types: Visual C++ Projects, Templates: MFC Application
Name: histo1
Location: C:\temp
Button OK unten Mitte klicken
Es meldet sich der MFC Application Wizard - histo1 mit der Seite Overview, die uninteressant ist.
Links unter Overview auf Application Type klicken.
Schritt1: Single document einschalten
Schritt 2: Document/View architecture support Checkbox einschalten
Schritt 3: Finish
Klicken Sie im Hauptmenu von Visual Studio auf den Menupunkt View und dann auf den Unterpunkt Class View Ctrl+Shift+C.
Sie sehen im rechten Bereich des Visual Studio-Hauptfensters das Unterfenster Class View - histo1.
An dessen unteren Rand die Registerkarte Klassen klicken.
Darin sehen Sie die Zeile + histo1 , klicken Sie das Pluszeichen.
Sie sehen u.a. + Chisto1Doc, doppelklicken Sie diesen Klassennamen.
Sie editieren jetzt das File histo1Doc.h, Schreiben Sie dort vor die Klasse class Chisto1Doc : public CDocument am besten unter die Zeile #pragma once die Präprozessoranweisung:
#include < vector > //für die dynamischen Arrays der STL
Schreiben Sie in die Klasse direkt unter die sich öffnende geschweifte Klammer die Deklarationen:
public: BITMAPFILEHEADER FH; BYTE IBytes[1200];//bytes for BitmapInfoHeader+Palette BITMAPINFOHEADER* pIH; //pointer on BitmapInfoHeader BITMAPINFO* pI; //pointer on BitmapInfo std::vector< BYTE > Pixel; //dyn. array for pixel std::vector< BYTE > PixelBinary; //dyn. array for binary pixel int Histogram[256];
Doppelklicken Sie auf den Konstruktor der Klasse: Chisto1Doc(void). Sie editieren jetzt das File histo1Doc.cpp. Sie müssen dort vier Variable initialisieren:
Chisto1Doc::Chisto1Doc() { memset( IBytes, 0, sizeof(IBytes) ); pIH = (BITMAPINFOHEADER*) IBytes; pI = (BITMAPINFO*) IBytes; }
Im Class View - histo1- Fenster doppelklicken Sie den Klassennamen Chisto1View.
Sie editieren jetzt das File histo1View.h, Schreiben Sie dort vor die Klasse class Chisto1View : public CView am besten unter die Zeile #pragma once die Präprozessoranweisungen:
#include < vector > //für die dynamischen Arrays der STL
Schreiben Sie in die Klasse direkt unter die sich öffnende geschweifte Klammer die Deklarationen:
private: CRect histo_r; int threshold; BOOL MouseFlag;
Im Class View - histo1- Fenster klicken Sie auf das Pluszeichen vor Chisto1View. Klicken Sie den Konstruktor der Klasse: Chisto1View(void). Sie editieren jetzt das File histo1View.cpp. Sie müssen dort eine Variable initialisieren:
Chisto1View::Chisto1View() { MouseFlag = false; }
Prüfen, ob alles soweit in Ordnung ist: Debug -> Start Without Debugging. Beenden.
Doppelklicken Sie auf Serialize(CArchive& ar). Ersetzen Sie die fast leere Funktionshülse durch folgenden Code:
void Chisto1Doc::Serialize(CArchive& ar) { if (ar.IsStoring()) {} else { ar.Read( & FH, sizeof(BITMAPFILEHEADER) ); if ( FH.bfType != 'MB') { forget_it(); return; } if ( FH.bfSize <= 54 ) { forget_it(); return; } if ( FH.bfOffBits < 54 ) { forget_it(); return; } int nBytesInfo = FH.bfOffBits - sizeof(BITMAPFILEHEADER); int nBytesPixel = FH.bfSize - FH.bfOffBits; ar.Read( IBytes, nBytesInfo ); //BitmapInfoHeader+Palette if ( !(pIH->biBitCount == 8 || pIH->biBitCount ==24) ) { forget_it(); return; } Pixel .resize( nBytesPixel ); PixelBinary.resize( nBytesPixel ); ar.Read( &Pixel.front(), nBytesPixel ); memset( Histogram, 0, sizeof(Histogram) ); //Null setzen int sum, i, hmax = 0; std::vector< BYTE >::iterator pointer; switch ( pIH->biBitCount ) { case 8: for ( pointer=Pixel.begin(); pointer < Pixel.end(); pointer++ ) Histogram[ *pointer ]++; break; case 24: for ( pointer=Pixel.begin(); pointer < Pixel.end(); pointer+=3 ) { sum = *pointer + *(pointer+1) + *(pointer+2); Histogram[ sum / 3 ]++; } } for ( i = 0; i < 256; i++ ) if ( Histogram[i] > hmax ) hmax = Histogram[i]; for ( i = 0; i < 256; i++ ) Histogram[i] = (100*Histogram[i])/hmax; } }
Klicken Sie im Class View - histo1 - Fenster mit der rechten Maustaste auf die Klasse Chisto1Doc. Es öffnet sich ein Kontextmenü. Stellen Sie die Maus auf den 4 Menüpunkt Add und klicken Sie dann auf Add Function... . Es erscheint der "Add Member Function Wizard - histo1". Tragen Sie in seine Dialogfelder ein:
Return type : void
Function name: forget_it
Access : private
.cpp file : histo1doc.cpp
Alle weiteren Felder und Checkboxen bleiben leer. Sie verlassen den Wizard mit dem Button Finish.
Der Wizard erzeugt Ihnen einen Funktionshülle, die Sie folgendermaßen füllen:
void Chisto1Doc::forget_it() { memset( IBytes, 0, sizeof(IBytes) ); for ( int i=0; i < 10; i++ ) MessageBeep(-1); }
Ausführen, lesen muss funktionieren, aber man sieht noch nichts. Falls man eine Datei öffnet, die kein oder ein falsches Bitmap enthält, muss es piepsen.
Nun programmieren Sie in histo1View.cpp die bereits vorhandene Funktion: void Chisto1View::OnDraw(CDC* /*pDC*/) bis sie so aussieht:
void Chisto1View::OnDraw(CDC* pDC) { Chisto1Doc* pDoc = GetDocument(); if ( !pDoc->pIH->biSize ) { pDC->TextOut(0,0,"Open a *.BMP file !"); return; } BYTE * pointer; if ( !MouseFlag ) pointer = &(pDoc->Pixel .front()); else pointer = &(pDoc->PixelBinary.front()); CRect R; GetClientRect( R ); //welche Größe hat die ClientArea? StretchDIBits( pDC->GetSafeHdc(), 0, 0, R.Width(), R.Height(), 0, 0, pDoc->pIH->biWidth, pDoc->pIH->biHeight, pointer, pDoc->pI, DIB_RGB_COLORS, SRCCOPY ); histo_r.right = R.Width() - 10; histo_r.left = histo_r.right - 256; histo_r.bottom = R.Height() - 10; histo_r.top = histo_r.bottom - 100; pDC->Rectangle( histo_r ); pDC->TextOut( histo_r.left+1, histo_r.top+1, "click and move here!" ); for ( int i = 0; i < 256; i++ ) { pDC->MoveTo( histo_r.left + i, histo_r.bottom ); pDC->LineTo( histo_r.left + i, histo_r.bottom - pDoc->Histogram[i] ); } if ( MouseFlag ) { pDC->MoveTo( histo_r.left + threshold, histo_r.top ); pDC->LineTo( histo_r.left + threshold, histo_r.bottom ); } }
histo1 ist vorläufig fertig, ausführen, *.bmp-File öffnen, Sie sehen das Histogramm, aber Sie können noch kein Binärbild erzeugen, beenden.
Klicken Sie im Klassenbaum mit der rechten Maustaste auf Chisto1View.
Es öffnet sich ein Kontextmenü. Klicken Sie ganz unten im Kontextmenü auf Properties.
Es öffnet sich unter dem Class View - histo1 - Fenster ein Properties - Fenster mit der Überschrift Chisto1View VCCodesClass.
Im Toolbar dieses Fensters klicken Sie rechts neben dem gelben Blitz auf das Messages - Symbol, das aussieht wie ein weißer Topf mit blauem Deckel.
Sie sehen nun die Liste der Windows-Messages von Chisto1View.
Klicken Sie auf WM_LBUTTONDOWN und dann auf das schwarze Abwärtsdreieck rechts in dieser Zeile und dann auf Add OnLButtonDown.
Klicken Sie auf WM_MOUSEMOVE und dann auf das schwarze Abwärtsdreieck rechts in dieser Zeile und dann auf Add OnMouseMove.
Klicken Sie auf WM_LBUTTONUP und dann auf das schwarze Abwärtsdreieck rechts in dieser Zeile und dann auf Add OnLButtonUp.
Programmieren Sie die eben generierten Funktionen bis sie so aussehen:
void Chisto1View::OnLButtonDown(UINT nFlags, CPoint point) { if ( !histo_r.PtInRect( point ) ) return; MouseFlag = true; } void Chisto1View::OnMouseMove(UINT nFlags, CPoint point) { if ( !nFlags ) return; if ( !histo_r.PtInRect( point ) ) return; Chisto1Doc* pDoc = GetDocument(); if ( !pDoc->pIH->biSize ) return; threshold = point.x - histo_r.left; std::vector< BYTE >::iterator pointer1 = pDoc->Pixel .begin(); std::vector< BYTE >::iterator pointer2 = pDoc->PixelBinary.begin(); switch ( pDoc->pIH->biBitCount ) { case 8: for ( ; pointer1 < pDoc->Pixel.end(); pointer1++, pointer2++ ) { if ( *pointer1 > threshold ) *pointer2 = 255; else *pointer2 = 0; } break; case 24: for ( ; pointer1 < pDoc->Pixel.end(); pointer1+=3, pointer2+=3 ) { int sum = *pointer1 + *(pointer1+1) + *(pointer1+2); if ( sum > 3*threshold ) *pointer2=*(pointer2+1)=*(pointer2+2)=255; else *pointer2=*(pointer2+1)=*(pointer2+2)= 0; } } Invalidate( false ); } void Chisto1View::OnLButtonUp(UINT nFlags, CPoint point) { MouseFlag = false; Invalidate( false ); }
histo1 ist fertig, ausfuehren, Bitmaps von Harddisk laden, ein Bild öffnen. Kllicken Sie mit der linken Maustaste in das Histogrammrechteck und verschieben Sie die Schwelle. Sie sehen dabei Binärbilder. Nach dem Loslassen der linken Maustaste erscheint wieder das Originalbild.
Die Binärbilder erscheinen nur ruckig langsam. Das Berechnen der Binärbilder geht viel schneller und synchron zur Mausbewegung, wenn Sie das Projekt ohne Debugger im release - mode übersetzen. Gehen Sie dazu im Hauptmenü von VS auf Build -> Configuration Manager. Es öffnet sich ein Fenster Configuration Manager. Sie setzen das Feld Active Solution Configuration auf "Release" und die Spalte Configuration auch auf "Release". Verlassen mit Close. Dann alles neu übersetzen, linken und ausführen mit Debug -> Start Without Debugging.
Erpoben, beenden, löschen, neu programmieren.
Im Prinzip sollte das Programm alle Arten von Bitmaps lesen und anzeigen. Falls Sie eine alte Graphikkarte mit 8 Bit benutzen und/oder falls Sie Ihren Desktop auf 256 Farben eingestellt haben, kann es sein, dass die Farben schlecht aussehen.
Falls Sie keine *.bmp - Dateien auf Ihrer Harddisk finden, benutzen Sie folgende Beispielbilder:
Download: Butterfly.bmp 217 kB 24Bit-TrueColor-Bild
Download: Madonna.bmp 18 kB 8Bit-Grauwert-Bild
Download: Lena256.bmp 66 kB 8Bit-Grauwert-Bild
Download: Lena512.bmp 258 kB 8Bit-Grauwert-Bild
Download: Angiography.bmp 66 kB 8Bit-Grauwert-Bild
top of page: |