PictureControl und Bitmap
-
hm... ich stell mich offenbar recht dämlich an...
GUI wird normalerwiese durch LabVIEW gemacht und dieses kleine Programm sollte nur zum Debuggen der Kamera bzw. Bilderbetachtung ohne Kamera dienen... aber offenbar ist das mit Bilddarstellung und PictureControl und Windows wensentlich schwieriger als ich dachte...
//variables RECT myRect; // get coordinates for rectangle GetDlgItem(IDC_IMG1)->GetClientRect(&myRect); myRect.left; myRect.right; myRect.top; myRect.bottom; int width = myRect.right - myRect.left; int height = myRect.bottom - myRect.top; CDC *pDC = GetDC(); CDC mDC; mDC.CreateCompatibleDC(pDC); CBitmap scale_bitmap; // Bitmap erzeugen scale_bitmap.CreateCompatibleBitmap(pDC, width, height); // Bits einfügen scale_bitmap.SetBitmapBits(1280 * 1024, &m_pcImageMemory); CBitmap *pob = mDC.SelectObject(&scale_bitmap); CDC *pDC_CTRL = GetDlgItem(IDC_IMG1)->GetDC(); CDC mDC_CTRL; mDC_CTRL.CreateCompatibleDC(pDC_CTRL); mDC.SelectObject(pob); BitBlt(mDC_CTRL.m_hDC, 0, 0, width, height, mDC.m_hDC, 0, 0, SRCCOPY); ((CStatic *) GetDlgItem(IDC_IMG1))->SetBitmap((HBITMAP) scale_bitmap); ReleaseDC(pDC);
gibt 'n schwarzes Bild... irgendwas ist da faul... oder ich habe n Knoten im Kopf...
ich glaube ich gebe auf
-
Das kann schon so gehen wenn du nicht gerade CBitmap scale_bitmap; auf dem Stack erzeugst und diese nach beenden der Funktion dann terminiert wird, was soll das das PictureCtrl anzeigen? Denn dieses macht keine kopie von deinem CBitmap. Also verschieb mal CBitmap scale_bitmap; in den Header und dann Versuch noch mal, wenn du dann net noch wo anders nen Fehler hast dann solltest du schon mal was anderes sehen als ein schwarzes Bild
-
Danke für eure Geduld mit mir.
Ich denke, da ich mit DC und MFC nicht so den Durchblick habe, sollte ich es so einfach wie möglich halten.
Also starte ich mal meine Idee ganz von vorne:
1.) Daten werden von der Kamera aufgenommen bzw. aus Binärdatei eingelesen
2.) Daten in CBitmap verpacken
3.) CBitmap in CImage verpacken (attach)
4.) CImage auch Größe von PictureControl stretchen/komprimieren
5.) CImage entpacken und ausgeben (detach)
CImage kann man ja auch direkt speichern und schauen, ob alles funktioniert
h-File [...] CBitmap scale_bitmap; CImage image1; [...] cpp-File [...] // nur zur Info, damit der Datentyp klar ist // char *m_pcImageMemory; // char *inBuf UINT sz = UINT(1280 * 1024); [...] // Daten sind eingelesen memcpy(m_pcImageMemory, inBuf, sz); // Daten in CBitmap verpacken scale_bitmap.CreateBitmap(1280, 1024, 1, 8, &m_pcImageMemory); // CBitmap in CImage verpacken image1.Attach(HBITMAP(scale_bitmap)); // CImage für Debugzwecke speichern //image1.Save(CString("D:\\temp\\test.bmp")); // CIamge stretechen/komprimieren RECT myRect; GetDlgItem(IDC_IMG1)->GetClientRect(&myRect); int width = myRect.right - myRect.left; int height = myRect.bottom - myRect.top; CDC *pDC_CTRL = GetDlgItem(IDC_IMG1)->GetDC(); CDC mDC_CTRL; mDC_CTRL.CreateCompatibleDC(pDC_CTRL); img1.StretchBlt(mDC_CTRL.m_hDC, 0, 0, width, height, SRCCOPY); // CImage für Debugzwecke speichern //image1.Save(CString("D:\\temp\\test1.bmp")); // CBitmap entpacken und in PictureControl ausgeben ((CStatic *) GetDlgItem(IDC_IMG1))->SetBitmap(HBITMAP(img1.Detach())); ReleaseDC(pDC_CTRL); [...]
Ich flige allerdings mit Debug Assertion Fail! in Zeile 21 (Attach) raus.
File: c:\...\atlimage.h
Line: 758
Expression: hBitmap!=0
-
Callstack?
Wie der ASSERT schon sagt ist die Bitmap nicht gültig. Einfach Wiederholen anklicken und im Callstack schauen was DU gerade machst.
-
> QCam_Test.exe!ATL::CImage::Attach(HBITMAP__ * hBitmap, ATL::CImage::DIBOrientation eOrientation) Zeile 758 C++
inline void CImage::Attach( _In_ HBITMAP hBitmap, _In_ DIBOrientation eOrientation) throw() { ATLASSUME( m_hBitmap == NULL ); ATLASSERT( hBitmap != NULL ); m_hBitmap = hBitmap; UpdateBitmapInfo(eOrientation); }
(in diesem Post ist es Zeile 6).
-
Callstack sag ich....
Also da Du in Deinem Code Zeile 21 einen Attach benutzt gehe ich davon aus, dass der Call zuvor fehlschlägt... Du prüfst ja auch nicht ob alles klappt...
-
sorry...
ums Fehlerabfangen habe ich mir keine Gedanken gemacht bzw. die Rückgabewerte ignoriert.
Ich habe Zeile 18 ersetzt durch:
CDC *pDC = GetDC(); CDC mDC; mDC.CreateCompatibleDC(pDC); // Bitmap erzeugen scale_bitmap.CreateCompatibleBitmap(pDC, 1280, 1024); // Bits einfügen scale_bitmap.SetBitmapBits(1280*1024, m_pcImageMemory);
Nun läuft das Programm einmal ohne Fehler durch (beim 2. Mal tritt ein neuer Fehler auf), aber das Ergebnis ist noch nicht das Wahre:
- Das Bild ist sichtbar, aber irgendwie sieht es aus als ob der Kontrast nicht stimmt (sieht aus wie tw ausgeblendet/transparent)...
- Die Größe an sich stimmt nicht (viel größer als das PictureControl ich würde sagen immer noch 12801024), also scheint das Komprimieren noch nicht zu funktionieren
- Urspründlich liegt ein Bild (12801024) im Speicher nun ist das Bild aber gleich vier mal aneinandergereiht in einer Reihe da.Wenn ich die image1 vor und nach dem komprimieren Speichere sehe ich keine Veränderung und auch vier mal das gleiche Bild, aber da stimmt immerhin der Kontrast.
irgendwie gehts voran, aber nur in sehr kleinen Schritten...
Leider habe ich nicht die Zeit mich noch Tageweise damit zu beschäftigen. Ich verbuche die Idee dann also gescheitert.
Sollte jemand ein Tutorial oder ein Beispiel kennen/haben, wo genau das gemacht wird, sprich ein Byte/char-Array in einem PictureControl anzuzeigen, dann kann er das gerne hier posten, ich lese das Thema/Forum weiterhin.
@ Martin Richter danke für deine Antworten/Hinweise und deine Geduld
-
Mal meine Vermutung. Du hast image1 in der Klasse definiert und nicht lokal.
Dadurch ist das Objekt natürlich vorhanden und Attach schlägt natürlich beim zweiten Mal fehl. Attach geht nur an ein leeres Objekt.
Das ist der Nachteil, wenn man nur rudimentären Code postet.
-
// ImageTestDlg.h: Headerdatei // #pragma once // CImageTestDlg-Dialogfeld class CImageTestDlg : public CDialog { private: char *m_pcImageMemory[2]; // Konstruktion public: CImageTestDlg(CWnd* pParent = NULL); // Standardkonstruktor // Dialogfelddaten enum { IDD = IDD_IMAGETEST_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung // Implementierung protected: HICON m_hIcon; // Generierte Funktionen für die Meldungstabellen virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedFileopen(); };
// ImageTestDlg.cpp: Implementierungsdatei // #include "stdafx.h" #include "ImageTest.h" #include "ImageTestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CAboutDlg-Dialogfeld für Anwendungsbefehl "Info" class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialogfelddaten enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung // Implementierung protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() // CImageTestDlg-Dialogfeld CImageTestDlg::CImageTestDlg(CWnd* pParent /*=NULL*/) : CDialog(CImageTestDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CImageTestDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CImageTestDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_FILEOPEN, &CImageTestDlg::OnBnClickedFileopen) END_MESSAGE_MAP() // CImageTestDlg-Meldungshandler BOOL CImageTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // Hinzufügen des Menübefehls "Info..." zum Systemmenü. // IDM_ABOUTBOX muss sich im Bereich der Systembefehle befinden. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Symbol für dieses Dialogfeld festlegen. Wird automatisch erledigt // wenn das Hauptfenster der Anwendung kein Dialogfeld ist SetIcon(m_hIcon, TRUE); // Großes Symbol verwenden SetIcon(m_hIcon, FALSE); // Kleines Symbol verwenden for (int i = 0; i < 2; i++) { m_pcImageMemory[i] = (char *) malloc(1280 * 1024); } return TRUE; // TRUE zurückgeben, wenn der Fokus nicht auf ein Steuerelement gesetzt wird } void CImageTestDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // Wenn Sie dem Dialogfeld eine Schaltfläche "Minimieren" hinzufügen, benötigen Sie // den nachstehenden Code, um das Symbol zu zeichnen. Für MFC-Anwendungen, die das // Dokument/Ansicht-Modell verwenden, wird dies automatisch ausgeführt. void CImageTestDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // Gerätekontext zum Zeichnen SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Symbol in Clientrechteck zentrieren int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Symbol zeichnen dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // Die System ruft diese Funktion auf, um den Cursor abzufragen, der angezeigt wird, während der Benutzer // das minimierte Fenster mit der Maus zieht. HCURSOR CImageTestDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CImageTestDlg::OnBnClickedFileopen() { char *inBuf; CFile file; CString path; CString strFilter = CString("Bilddatei|*.img||"); CString Type = CString("*.img"); UINT sz = UINT(1280 * 1024); CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER, strFilter); if (dlg.DoModal() == IDOK) { path = dlg.GetPathName(); if (file.Open(path, CFile::modeRead)) { inBuf = (char *) malloc(sz); for (int i = 0; i < 2; i++) { if (file.Read(inBuf, sz) == sz) { memcpy(m_pcImageMemory[i], inBuf, sz); } else { AfxMessageBox(L"Wrong imagesize!", MB_ICONERROR | MB_OK); break; } } file.Close(); free(inBuf); } else { AfxMessageBox(L"Failed to open file!", MB_ICONERROR | MB_OK); } } else { return; } CBitmap scale_bitmap; CImage image1; BOOL retCreBmp = scale_bitmap.CreateBitmap(1280, 1024, 1, 8, m_pcImageMemory[0]); // CBitmap in CImage verpacken image1.Attach((HBITMAP) scale_bitmap); // CImage für Debugzwecke speichern image1.Save(CString("D:\\temp\\test.bmp")); // CIamge stretechen/komprimieren RECT myRect; GetDlgItem(IDC_IMG1)->GetClientRect(&myRect); int width = myRect.right - myRect.left; int height = myRect.bottom - myRect.top; CDC *pDC_CTRL = GetDlgItem(IDC_IMG1)->GetDC(); CDC mDC_CTRL; mDC_CTRL.CreateCompatibleDC(pDC_CTRL); BOOL retStrBlt=image1.StretchBlt(mDC_CTRL.m_hDC, 0, 0, width, height, SRCCOPY); // CImage für Debugzwecke speichern image1.Save(CString("D:\\temp\\test1.bmp")); // CBitmap entpacken und in PictureControl ausgeben ((CStatic *) GetDlgItem(IDC_IMG1))->SetBitmap(HBITMAP(image1.Detach())); ReleaseDC(pDC_CTRL); }
Das ist der Quellcode den ich heute neu angelegt habe.
den haut es bei StretchBlt raus, mit:
Debug Assertion Failed!
File: c:\...\atlimage.h
Line: 1607
Expression: hBitmap == m_hBitmap
-
Und was soll der Quatsch mit StretchBit an dieser Stelle?
In dieser Form wird der Inhalt des DCs in die Bitmap übertragen. Diese ist aber schon gefüllt. Deshalb der ASSERT.Was also bezweckst Du mit dem StretchBlt. Wenn DU nur das Image zuweisen willst, dann tu das und lass das was dazwischen liegt weg.
Ich gehe mal davon aus, dass Du das Bild anpassen möchtest. Dann ist der Code aber falsch. Etwas Googlen und siehe da:
http://www.codeproject.com/Questions/398007/How-to-use-CImage-class-to-resize-a-bitmapBZW: Deine Form mit Speicher in einem C++ Programm umzugehen ist sehr eigentümlich. Warum malloc? Warum keine smart pointer, keine vectoren?