Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 3 sur 3
  1. #1
    Futur Membre du Club
    Profil pro Anthony Pasche
    Inscrit en
    juin 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Nom : Anthony Pasche

    Informations forums :
    Inscription : juin 2010
    Messages : 32
    Points : 18
    Points
    18

    Par défaut Traitement sur capture de webcam (sans DirectShow et openCV)

    Bonjour,

    Aujourd'hui j'ai voulu réaliser une application C++ visant à modifier en temps réel le flux vidéo de ma webcam.

    Pour ça j'utilise une méthode callback visant à récupérer le buffer de la frame précédente avant que celle-ci ne remplace la frame courante (si j'ai bien lu la doc).

    Voici l'aperçu de cette fonction.
    Code :
    LRESULT CALLBACK capVideoStreamCallback(HWND hWnd, LPVIDEOHDR lpVHdr)
    Donc comme paramètre je récupère une structure VIDEOHDR (Aperçu ci-dessous).
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    typedef struct videohdr_tag {
      LPBYTE    lpData;
      DWORD     dwBufferLength;
      DWORD     dwBytesUsed;
      DWORD     dwTimeCaptured;
      DWORD_PTR dwUser;
      DWORD     dwFlags;
      DWORD_PTR dwReserved[4];
    } VIDEOHDR, *PVIDEOHDR, *LPVIDEOHDR;
    Je récupère donc le pointer lpData qui contient les données de la frame.

    Mais le problème vient du faite que ces données sont déjà compressée au format supporté par les drivers de la webcam (ici j'ai 2 format supporté: YUY2 et MJPG)

    Donc en théorie je devrais m'adapter par rapport au format ou passer sur un format RGB pour traiter les données plus les renvoyé dans le bon format.

    En faite, c'est à cette partie là que je coince. Le mieux serait que je puisse passer au format RGB peut importe le format de base. Il y avait une
    solution qui consistait entre la réception de message(PeekMessage) de créer un bitmap à partir du contexte d'affichage (code ci-dessous).

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
     
    void captureFrame()
    {
    	HDC hdcWin = GetDC(hWindow); // Contexte d'affichage de la fenêtre
    	HDC hdc = GetDC(hWebcam); // Contexte d'affichage de la webcam
    	HDC hdcMem = CreateCompatibleDC(hdc);	// Contexte de données
     
    	// Donne les infos sur le bitmap
    	BITMAPINFO bmpinfo;
    	bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    	bmpinfo.bmiHeader.biWidth = 320;
    	bmpinfo.bmiHeader.biHeight = 240;
    	bmpinfo.bmiHeader.biPlanes = 1;
    	bmpinfo.bmiHeader.biBitCount = 32;
    	bmpinfo.bmiHeader.biCompression = BI_RGB;
    	bmpinfo.bmiHeader.biSizeImage = 0;
    	bmpinfo.bmiHeader.biXPelsPerMeter = 0;
    	bmpinfo.bmiHeader.biYPelsPerMeter = 0;
    	bmpinfo.bmiHeader.biClrUsed = 0;
    	bmpinfo.bmiHeader.biClrImportant = 0;
     
    	// Création du bitmap
    	void *pBits;
    	HBITMAP hbmp = CreateDIBSection(hdc, &bmpinfo, DIB_PAL_COLORS, &pBits, NULL, 0);
     
    	// Sélection du bitmap
    	SelectObject(hdcMem, hbmp);
     
    	// Copie l'image
    	BitBlt(hdcMem, 0, 0, 320, 240, hdc, 0, 0, SRCCOPY);
     
    	// Initialisation du tableau de pixels
    	int len = 320 * 240 * 4;
    	BYTE *pixels = new BYTE[len];
     
    	// Récupère les pixels
    	GetDIBits(hdcMem, hbmp, 0, 240, pixels, &bmpinfo, DIB_RGB_COLORS);
     
    	// Traitement sur les pixels
    	for (int i = 0; i < len; i++)
    		pixels[i] = 255 - pixels[i];
     
    	// Afficher l'image
    	SetDIBitsToDevice(hdcWin, 320, 0, 320, 240, 0, 0, 0, 240, pixels, &bmpinfo, DIB_RGB_COLORS);
     
    	// Vidage mémoire
    	delete[] pixels;
    	DeleteDC(hdcMem);
    	DeleteDC(hdc);
    }
    Je sais pas si il est fonctionnel mais c'était pour vous faire une idée. Cette méthode fonctionne mais après le problème est que je n'arrive pas à afficher seulement le rendu final de la capture. Je suis obliger de la laisser la preview de la webcam donc je me retrouve constamment avec rendu.

    Y a-t-il un moyen d'avoir que le rendu final après le traitement de l'image (en modifiant directement le buffer ?) ou d'utiliser la technique décrite ci-dessus mais en affichant que le rendu final ?

    Voici le code de mon application !

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
     
    #include <Windows.h>
    #include <Vfw.h>
     
    const int widthCapture = 640;
    const int heightCapture = 480;
     
    HWND hWebcam;
     
    LRESULT CALLBACK capVideoStreamCallback(HWND hWnd, LPVIDEOHDR lpVHdr)
    {
    	// C'est ici que je récupère le buffer de la frame avant que celle-ci ne s'affiche
    	return 0;
    }
     
    LRESULT CALLBACK winProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch(uMsg)
    	{
    		case WM_CREATE:
    		{
    			hWebcam = capCreateCaptureWindow("Webcam capture", WS_CHILD | WS_VISIBLE, 0, 0, widthCapture, heightCapture, hwnd, 0);
     
    			if (SendMessage(hWebcam, WM_CAP_DRIVER_CONNECT, 0, 0))
    			{
    				//SendMessage(hWebcam, WM_CAP_DLG_VIDEOFORMAT, 0, 0);
    				SendMessage(hWebcam, WM_CAP_SET_CALLBACK_FRAME, 0, (LPARAM)&capVideoStreamCallback);
    				SendMessage(hWebcam, WM_CAP_SET_PREVIEWRATE, 33, 0);
    				SendMessage(hWebcam, WM_CAP_SET_PREVIEW, true, 0);
    			}
    			else SendMessage(hwnd, WM_DESTROY, 0, 0);
    			break;
    		}
     
    		case WM_DESTROY:
    		{
    			SendMessage(hWebcam, WM_CAP_SET_PREVIEW, false, 0);
    			SendMessage(hWebcam, WM_CAP_SET_CALLBACK_FRAME, 0, NULL);
    			SendMessage(hWebcam, WM_CAP_DRIVER_DISCONNECT, 0, 0);
     
    			PostQuitMessage(0);
    			break;
    		}
     
    		default:
    			return DefWindowProc(hwnd, uMsg, wParam, lParam);
    	}
     
    	return 0;
    }
     
    int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    	WNDCLASS wc;
    	memset(&wc, 0, sizeof(WNDCLASS));
     
    	wc.style = CS_HREDRAW | CS_VREDRAW;
    	wc.lpfnWndProc = winProc;
    	wc.hInstance = hInstance;
    	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    	wc.lpszClassName = "classOfWindow";
     
    	RegisterClass(&wc);
     
    	HWND hWindow = CreateWindow(wc.lpszClassName, "Rectangle reconizer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, widthCapture + 20, heightCapture + 40, NULL, NULL, NULL, NULL);
     
    	ShowWindow(hWindow, SW_SHOW);
    	UpdateWindow(hWindow);
     
    	MSG msg;
     
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
     
    	DestroyWindow(hWindow);
     
    	return msg.wParam;
    }
    Je ne sais pas jusqu'où je peux aller pour avoir quelque chose qui puissent être rapide et qui ne surcharge pas trop la machine.

    Merci d'avance pour vos réponses !

    Cordialement,

    firepolo

  2. #2
    Inactif


    Homme Profil pro
    Inscrit en
    novembre 2008
    Messages
    5 308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 308
    Points : 15 141
    Points
    15 141

    Par défaut

    Bonjour

    Ta question est très spécifique, pas sur que arrive à trouver de l'aide ici. Demande peut être dans le forum programmation système.
    Pourquoi pas OpenCV ? Fais peut être une première version avec OpenCV, puis modifie quand ça fonctionne par du code perso. Tu peux également t'inspirer du code source d'OpenCV

    Bon courage

  3. #3
    Expert Confirmé Sénior
    Avatar de dragonjoker59
    Homme Profil pro Sylvain DOREMUS
    Développeur informatique
    Inscrit en
    juin 2005
    Messages
    959
    Détails du profil
    Informations personnelles :
    Nom : Homme Sylvain DOREMUS
    Âge : 33
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : juin 2005
    Messages : 959
    Points : 4 247
    Points
    4 247

    Par défaut

    Si je me souviens bien (et si j'ai bien compris que tu utilises VideoForWindows), tu peux créer une fenêtre de capture que tu n'affiches pas (grâce à la fonction capCreateCaptureWindow).

    Ensuite, que tu utilises ou pas le mode preview (je ne l'utilise pas, je mets un timer pour charger les frames) tu peux traiter la frame (donc soit récupérée via la callback soit par capGrabFrame) et n'afficher que celle que tu traites toi-même.

    Par contre la compression, il faut que ce soit toi qui la gères. En YUY2 il y a pas mal de sources qui traînent traitant de la conversion vers RGB, mais le MJPG est plus tordu, je suis passé par LibJPEG qui donne d'excellents résultats.
    Si vous ne trouvez plus rien, cherchez autre chose...

    Chef de projet (et unique exécutant pour l'instant) pour Castor 3D
    Des tutoriels OpenGL

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •