HWND zu IplImage konvertieren



  • Guten Tag,
    wie im Titel zu lesen ist möchte ich von einem Fensterhandle (eigl. Fensterklasse)zu einem IplImage kommen. Es geht mir nur um den Client-Bereich.
    Ich bin mir jetzt nicht ganz sicher ob das hier das richtige Forum ist, da mein Problem in der memcpy- Funktion liegt.
    Die Funktion hab ich mir größtenteils aus Code-Schnipsel zusammengebaut und abgeändert.

    Beim Aufruf entsteht folgender Fehler:
    "Unbehandelte Ausnahme bei 0x6353f2ca (msvcr90d.dll) in Screenshot.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000000."

    Hier ist die Funktion:

    IplImage *CWndtoIpl(CWnd *pWnd)
    {
        HWND hWnd = pwindow->GetSafeHwnd();
    
    	CRect rect;
    	pWnd->GetClientRect(rect);
    
    	int     nWidth  = rect.right;
    	int     nHeight = rect.bottom;
    
        	HDC     hdc     = ::GetDC(hWnd);
    	HDC     memDC   = ::CreateCompatibleDC(hdc);
    	HBITMAP hbm     = ::CreateCompatibleBitmap(hdc, nWidth, nHeight);
    	HBITMAP hbmOld  = (HBITMAP)::SelectObject(memDC, hbm);
    
    	BITMAPINFO bmi;
    	ZeroMemory(&bmi, sizeof(bmi));
    	bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
    	bmi.bmiHeader.biWidth        = nWidth;
    	bmi.bmiHeader.biHeight       = nHeight;
    	bmi.bmiHeader.biBitCount     = 24;
    	bmi.bmiHeader.biPlanes       = 1;
    	bmi.bmiHeader.biCompression  = BI_RGB;
    	bmi.bmiHeader.biSizeImage    = 32 * nWidth * nHeight / 8;
    
            BYTE *pbBits = new BYTE[bmi.bmiHeader.biSizeImage];
    
    	BITMAPFILEHEADER bfh;
    	bfh.bfType      = ('M' << 8) + 'B';
    	bfh.bfSize      = sizeof(BITMAPFILEHEADER)  +
                              bmi.bmiHeader.biSizeImage +
                              sizeof(BITMAPINFOHEADER);
    	bfh.bfReserved1 = 0;
    	bfh.bfReserved2 = 0;
    	bfh.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    
    //  BitBlt(*hDC, 0, 0, GetSystemMetrics(SM_CXSCREEN)-1,GetSystemMetrics(SM_CYSCREEN)-1,memDC, 0, 0, SRCCOPY);
    
      HANDLE hfile = CreateFile( "__TMP__SCRNSHOT__2357__.BMP",
                                 GENERIC_WRITE,
                                 0,
                                 0,
                                 CREATE_ALWAYS,
                                 0,
                                 0 );
    
    	  DWORD dwWritten;
    	  WriteFile(hfile,&bfh,           sizeof(bfh),               &dwWritten, NULL);
    	  WriteFile(hfile,&bmi.bmiHeader, sizeof(BITMAPINFOHEADER),  &dwWritten, NULL);
    	  WriteFile(hfile,pbBits,         bmi.bmiHeader.biSizeImage, &dwWritten, NULL);
    
    	BITMAP bmp;
    
    	::GetObject(hbm,sizeof(BITMAP),&bmp);
    
    	int nChannels = bmp.bmBitsPixel == 1 ? 1 : bmp.bmBitsPixel/8 ;
    	int depth = bmp.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U;
    
    	IplImage* img = cvCreateImageHeader( cvSize(bmp.bmWidth, bmp.bmHeight), depth, nChannels );
    
    	img->imageData = (char*)malloc(bmp.bmHeight * bmp.bmWidth * nChannels * sizeof(char));
    
    //_____________________________________________________________________________________________	
    	memcpy(img->imageData,(char*)(bmp.bmBits), bmp.bmHeight * bmp.bmWidth * nChannels);
        //memcpy(img->imageData,(char*)(bmp.bmBits),bmp.bmHeight*bmp.bmWidth*nChannels*sizeof(char));
    //_____________________________________________________________________________________________
    
    	CloseHandle(hfile);
    	  ::SelectObject(memDC, hbmOld);
    	  ::DeleteDC(memDC);
    	  ::ReleaseDC(hWnd,hdc);
    	  ::DeleteDC(hdc);
    	  ::DeleteObject(hbm);
    	  ::DeleteObject(hbmOld);
    	  ::DeleteObject(hWnd);
    	  hbm=NULL;
    	  hbmOld=NULL;
    	  delete[] pbBits; 
    
          return img;
    
    }
    


  • Ich habe mitlerweile den Fehler gefunden. bmp.bmBits ist ein Null-Pointer. Ich bin sehr dankbar,
    wenn mir jemand zeigen könnte wie ich den Zeiger richtig initialisiere.

    MfG, Benni


  • Mod

    Du musst die Bitmap Bits separat anfordern:
    http://msdn.microsoft.com/en-us/library/dd144850(v=vs.85).aspx



  • Ich habe das mitlerweile einmal so und so versucht, bin aber
    leider zu keinem Ergebnis gekommen. Sprich der Inhalt der
    Inhalt von bm.bmpBits ist immernoch 0x0000....

    //long x = GetBitmapBits(hbm, bmp.bmHeight * bmp.bmWidth * nChannels, bmp.bmBits);
    
    //GetDIBits( hdc,hbm,0,bmi.bmiHeader.biHeight,bmp.bmBits,&bmi,DIB_RGB_COLORS);
    

    Was bedeutet es denn wenn im Debugger, nach dem Inhalt einer Variable "{unused=???}" steht? Dies ist bei hbm, hdc, memDC und hbmOld der Fall.
    GetBitmapBits liefert nämlich einen richtigen Wert zurück.

    Falls es weiterhelfen sollte, so wird die Funktion aufgerufen:

    void CScreenshotDlg::OnLButtonDown(UINT nFlags, CPoint point)
    { ...
    GetCursorPos(&point); 
    pwindow = WindowFromPoint(point);
    ...
    test = HWNDtoIpl(pwindow);
    

    Schonmal Danke für die weitere Hilfe!
    MfG, Benni



  • Servus,

    sorry für denjenigen dem jetzt eventuell 10€ durch die Finger gehen (http://www.c-plusplus.net/forum/293454), aber hier wirst du gehelft:

    http://forum.openframeworks.cc/index.php?topic=1414.0

    Das sollte dein Problem lösen.

    gruß
    Hellsgore



  • Hey Hellsgore,
    vielen Dank für den Link.
    Ich habs jetzt nicht genaus so gemacht
    wie dort aber ich hab mir die Lösung ableiten können.

    Endlich hab ich das hinbekommen, vielen vielen Dank.

    MfG, Benni



  • Bitteschön,

    aber fein wäre es, wenn du jetzt noch die Lösung posten könntest. Ich denke mal, es könnte noch mehr Leute interessieren...

    gruß
    Hellsgore



  • OkiliDokili
    Soo diese Funktion macht aus einem *CWnd ein IplImage.

    IplImage *CWndtoIpl(CWnd *pWnd)
    {
    	CRect rect;
        HWND hWnd =  pwindow->GetSafeHwnd();
        pWnd     ->  GetClientRect(rect);
    
        int nWidth  = rect.right;
        int nHeight = rect.bottom;
    
    	HDC hDC = GetDC(hWnd);
    	HDC hMemDC = CreateCompatibleDC(hDC); 
    
    	IplImage *img;
    
    	HBITMAP hBitmap = CreateCompatibleBitmap( hDC, nWidth, nHeight);
    
    	BITMAPINFO bmi;
        ZeroMemory(&bmi, sizeof(bmi));
        bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth        = nWidth;
        bmi.bmiHeader.biHeight       = -(nHeight);        // Negativ damit das Bild von oben nach unten gemalt wird
        bmi.bmiHeader.biBitCount     = 32;                // Farbtiefe des Monitors
        bmi.bmiHeader.biPlanes       = 1;
        bmi.bmiHeader.biCompression  = BI_RGB;
        bmi.bmiHeader.biSizeImage    = 32 * nWidth * nHeight / 8;
    	BYTE *pbBits = new BYTE[bmi.bmiHeader.biSizeImage]; 
    
    	if( hBitmap)
    	{
    		HBITMAP	 hOld = (HBITMAP)SelectObject( hMemDC, hBitmap);
    		BitBlt( hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
    		SelectObject( hMemDC, hOld);
    
    		BITMAP bbmp;
    		GetObject( hBitmap, sizeof(BITMAP), &bbmp);
    
    		int nChannels = bbmp.bmBitsPixel == 1 ? 1 : bbmp.bmBitsPixel/8 ;
    		int depth = bbmp.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U;8U;
    
    		GetDIBits(hDC, hBitmap, 0, nHeight, pbBits, &bmi,DIB_RGB_COLORS); 
    
    		DeleteObject(hBitmap); 
    
    		img = cvCreateImageHeader( cvSize(bbmp.bmWidth, bbmp.bmHeight), depth, nChannels );
    		img ->imageData = (char*)malloc(bbmp.bmHeight * bbmp.bmWidth * nChannels * sizeof(char));
    		memcpy( img->imageData,(char*)(pbBits), bbmp.bmHeight * bbmp.bmWidth * nChannels);
    	}
    
    	::DeleteDC(hDC);
        ::DeleteDC(hMemDC);
    	::DeleteObject(hWnd);
    
    	return img;
    }
    

    MfG,Benni


  • Mod

    👎

    Memory Leak von pbBits!



  • Statt manueller Speicherverwaltung bietet sich hier natürlich

    std::vector<BYTE> bits(bmi.bmiHeader.biSizeImage);
    

    an.
    Und den Cast

    HBITMAP     hOld = (HBITMAP)SelectObject( hMemDC, hBitmap);
    

    kannste dir sparen, wenn hOld ein HGDIOBJ ist.

    Und deklarier Variablen immer so spät wie möglich und initialisiere sie sofort, wann immer dies möglich ist.



  • Gut, ich hab mich jezt über diese Leaks
    informiert. Ist das Problem mit

    delete pbBits;
    

    gelöst?



  • Nein, pbBits zeigt ja nicht nur auf ein BYTE, sondern auf ein dynamisch reserviertes Array. Folglich müsste es

    delete[] pbBits;
    

    heißen.

    Aber spar dir doch diese Frickelei (sie ist in C++ unüblich und weist auf einen Codestil der 90er hin) und verwende Container der Standardbibliothek (hier in diesem Beispiel ist der std::vector die bessere Wahl).


Anmelden zum Antworten