Как создать скриншот экрана и сохранить в IplImage?

Компьютерное зрение, OpenCV

Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 20 мар 2014, 16:45

Добрый день. Подскажите, как с помощью OpenCV можно создать скриншот нужной части экрана( предположим мы знаем точные координаты заранее) и сохранить полученный результат в переменную типа IplImage?
За ранее благодарю за помощь.
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение noonv » 20 мар 2014, 17:46

Чтобы сделать скриншот нужно использовать API операционной системы, а уже полученные данные можно будет конвертировать в структуры данных используемых OpenCV - IplImage или cv::Mat.
Аватара пользователя
noonv
Администратор
 
Сообщения: 545
Зарегистрирован: 05 май 2011, 15:44
Откуда: Калининград
programming: С++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 20 мар 2014, 19:49

Благодарю за ответ. Не подтолкнете в какую сторону лучше копать, если программа будет работать на ОС Win7 или XP?
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 21 мар 2014, 18:43

Нашел в сети рабочий код, который может делать скриншот и сохранять его в файл:
Код: Выделить всё
#include <windows.h>
#include <stdio.h>
#include <iostream>

#include <gdiplus.h>

using namespace std;

#pragma comment(lib, "GdiPlus.lib") /* наш многострадальный lib-файл */
using namespace Gdiplus; /* как хочешь, но мне не в кайф постоянно писать Gdiplus:: */

static const GUID png =
{ 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e } };


int main()
{

   Sleep(5000);
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    HDC scrdc, memdc;
    HBITMAP membit;
    // Получаем HDC рабочего стола
    // Параметр HWND для рабочего стола всегда равен нулю.
    scrdc = GetDC(0);
    // Определяем разрешение экрана
    int Height, Width;
    Height = GetSystemMetrics(SM_CYSCREEN);
    Width = GetSystemMetrics(SM_CXSCREEN);
    // Создаем новый DC, идентичный десктоповскому и битмап размером с экран.
    memdc = CreateCompatibleDC(scrdc);
    membit = CreateCompatibleBitmap(scrdc, Width, Height);
   // membit = CreateCompatibleBitmap(scrdc, 100, 100);
    SelectObject(memdc, membit);
    // Улыбаемся... Снято!
    BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);
   // BitBlt(memdc, 0, 0, 100, 100, scrdc, 0, 0, SRCCOPY);
    HBITMAP hBitmap;
    hBitmap =(HBITMAP) SelectObject(memdc, membit);
     Gdiplus::Bitmap bitmap(hBitmap, NULL);
    bitmap.Save(L"D:\\screen11.jpg", &png);

    DeleteObject(hBitmap);

    //GdiplusShutdown(gdiplusToken);
    return 0;
}


Теперь осталось понять, каким образом сохранять bitmap в IplImage, чтобы избавиться от постоянной записи скриншота на жесткий диск. Те варианты, которые я нагуглил не рабочие(или я не смог их запустить), не подскажите рабочий вариант?
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение noonv » 22 мар 2014, 17:51

Остаётся нагуглить как конвертировать HBITMAP в cv::Mat или IplImage :)
решения находятся на stackoverflow.
Вот такой код заработал:
Код: Выделить всё
   BITMAPINFO BMI;
   //ZeroMemory( &BMI, sizeof BMI );
   BMI.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
   BMI.bmiHeader.biWidth = Width;
   BMI.bmiHeader.biHeight = -Height;
   BMI.bmiHeader.biPlanes = 1;
   BMI.bmiHeader.biBitCount = 32;
   BMI.bmiHeader.biCompression = BI_RGB;

   cv::Mat image = cv::Mat(Height, Width, CV_8UC4);
   GetDIBits( scrdc, hBitmap, 0, Height, image.data, &BMI, DIB_RGB_COLORS );


Полный код:
Код: Выделить всё
#include <cv.h>
#include <highgui.h>

#include <windows.h>
#include <stdio.h>
#include <iostream>
//#include <gdiplus.h>

using namespace std;

#include <gdiplus.h>

#pragma comment(lib, "GdiPlus.lib") /* наш многострадальный lib-файл */
using namespace Gdiplus; /* как хочешь, но мне не в кайф постоянно писать Gdiplus:: */

static const GUID png =
{ 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e } };


int main()
{
   //Sleep(5000);
   GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

   HDC scrdc, memdc;
   HBITMAP membit;
   // Получаем HDC рабочего стола
   // Параметр HWND для рабочего стола всегда равен нулю.
   scrdc = GetDC(0);
   // Определяем разрешение экрана
   int Height, Width;
   Height = GetSystemMetrics(SM_CYSCREEN);
   Width = GetSystemMetrics(SM_CXSCREEN);
   // Создаем новый DC, идентичный десктоповскому и битмап размером с экран.
   memdc = CreateCompatibleDC(scrdc);
   membit = CreateCompatibleBitmap(scrdc, Width, Height);
   // membit = CreateCompatibleBitmap(scrdc, 100, 100);
   SelectObject(memdc, membit);
   // Улыбаемся... Снято!
   BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);

   HBITMAP hBitmap;
   hBitmap =(HBITMAP) SelectObject(memdc, membit);
   //Gdiplus::Bitmap bitmap(hBitmap, NULL);
   //bitmap.Save(L"screenshot_bitmap.jpg", &png);

   //DeleteObject(hBitmap);

   //GdiplusShutdown(gdiplusToken);

   BITMAPINFO BMI;
   //ZeroMemory( &BMI, sizeof BMI );
   BMI.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
   BMI.bmiHeader.biWidth = Width;
   BMI.bmiHeader.biHeight = -Height;
   BMI.bmiHeader.biPlanes = 1;
   BMI.bmiHeader.biBitCount = 32;
   BMI.bmiHeader.biCompression = BI_RGB;

   cv::Mat image = cv::Mat(Height, Width, CV_8UC4);
   GetDIBits( scrdc, hBitmap, 0, Height, image.data, &BMI, DIB_RGB_COLORS );

   cv::imshow("screen", image);
   cv::imwrite("screenshot_cv.jpg", image);
   cv::waitKey(0);

        // освобождаем ресурсы
   DeleteObject(hBitmap);
   DeleteDC(memdc);
   DeleteObject(membit);
   ReleaseDC(0, scrdc);

   return 0;
}


и, соответственно, код для просмотра:
Код: Выделить всё
#include <cv.h>
#include <highgui.h>

#include <windows.h>
#include <stdio.h>
#include <iostream>

cv::Mat get_screenshot()
{
   HDC scrdc, memdc;
   HBITMAP membit;
   // Получаем HDC рабочего стола
   // Параметр HWND для рабочего стола всегда равен нулю.
   scrdc = GetDC(0);
   // Определяем разрешение экрана
   int Height, Width;
   Height = GetSystemMetrics(SM_CYSCREEN);
   Width = GetSystemMetrics(SM_CXSCREEN);
   // Создаем новый DC, идентичный десктоповскому и битмап размером с экран.
   memdc = CreateCompatibleDC(scrdc);
   membit = CreateCompatibleBitmap(scrdc, Width, Height);
   SelectObject(memdc, membit);
   // Улыбаемся... Снято!
   BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);

   HBITMAP hBitmap;
   hBitmap =(HBITMAP) SelectObject(memdc, membit);
   //Gdiplus::Bitmap bitmap(hBitmap, NULL);
   //bitmap.Save(L"screenshot_bitmap.jpg", &png);

   //DeleteObject(hBitmap);

   //GdiplusShutdown(gdiplusToken);

   BITMAPINFO BMI;
   //ZeroMemory( &BMI, sizeof BMI );
   BMI.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
   BMI.bmiHeader.biWidth = Width;
   BMI.bmiHeader.biHeight = -Height;
   BMI.bmiHeader.biPlanes = 1;
   BMI.bmiHeader.biBitCount = 32;
   BMI.bmiHeader.biCompression = BI_RGB;

   cv::Mat image = cv::Mat(Height, Width, CV_8UC4);
   GetDIBits( scrdc, hBitmap, 0, Height, image.data, &BMI, DIB_RGB_COLORS );

   // освобождаем ресурсы
   DeleteObject(hBitmap);
   DeleteDC(memdc);
   DeleteObject(membit);
   ReleaseDC(0, scrdc);

   return image;
}

int main()
{
   cv::Mat image;
   while(true) {
      image = get_screenshot();
      cv::imshow("screen", image);
      //cv::imwrite("screenshot_cv.jpg", image);
      int key = cv::waitKey(33);
      if(key == VK_ESCAPE) {
         break;
      }
   }

   return 0;
}
Аватара пользователя
noonv
Администратор
 
Сообщения: 545
Зарегистрирован: 05 май 2011, 15:44
Откуда: Калининград
programming: С++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 23 мар 2014, 20:22

noonv, большое спасибо за помощь. Однако мне нужен способ конвертации HBITMAP именно в IplImage, т.к я пытаюсь использовать поиск изображения по шаблону по примеру с этого сайта( http://robocraft.ru/blog/computervision/3046.html ), а там для сравнения используется cvMatchTemplate, который принимает на вход переменные типа IplImage. Если не сложно, могли бы вы посоветовать рабочий способ конвертации?
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение noonv » 24 мар 2014, 12:36

Например, вместо cvMatchTemplate можно использовать C++ ный интерфейс: cv::matchTemplate.
Но если хочется именно IplImage, то вместо
image.data нужно указывать image->imageData.
Аватара пользователя
noonv
Администратор
 
Сообщения: 545
Зарегистрирован: 05 май 2011, 15:44
Откуда: Калининград
programming: С++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 25 мар 2014, 18:30

noonv, извини, но у меня не заработало. Если не сложно, можешь привести корректный пример кода конвертации?
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 09 апр 2014, 17:29

Нашел рабочий способ конвертации HBITMAP в IplImage. Вдруг кому то пригодится.
Код: Выделить всё
IplImage * GetIplImage(HBITMAP HBM)
{
        BITMAP BM;       
        LPBITMAPINFO BIP; 
        HDC DC;     
        DWORD DataSize;   
        WORD BitCount;         

        GetObject(HBM, sizeof(BITMAP), (LPSTR)&BM);
       
        BitCount = (WORD)BM.bmPlanes * BM.bmBitsPixel;
        DataSize = ((BM.bmWidth*BitCount+31) & ~31)/8*BM.bmHeight;

        BIP=(LPBITMAPINFO)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(BITMAPINFOHEADER));
        BIP->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        BIP->bmiHeader.biWidth = BM.bmWidth;
        BIP->bmiHeader.biHeight = BM.bmHeight;
        BIP->bmiHeader.biPlanes = 1;
        BIP->bmiHeader.biBitCount = BitCount;
        BIP->bmiHeader.biCompression = 0;
        BIP->bmiHeader.biSizeImage = DataSize;
        BIP->bmiHeader.biXPelsPerMeter = 0;
        BIP->bmiHeader.biYPelsPerMeter = 0;
        if (BitCount < 16)
                BIP->bmiHeader.biClrUsed = (1<<BitCount);
        BIP->bmiHeader.biClrImportant = 0;

        DC = GetDC(0);
       
        BYTE * bits;                    // массив для битов изображения
        long imgsize, pixels;   // Различные размерности для записи файла 

      

        pixels = BIP->bmiHeader.biWidth * BIP->bmiHeader.biHeight;

 /*       switch(BIP->bmiHeader.biBitCount)
        {
        case 4:
                imgsize = pixels / 2;
                break;
        case 8:
                imgsize = pixels;
                break;
        case 16:
                imgsize = pixels * 2;
                break;
        case 24:
                imgsize = pixels * 3;
                break;
        case 32:
                imgsize = pixels * 4;
                break;
        default:
                break;
        }*/

      imgsize = pixels * 4;

        //      выделяем память под биты изображения
        bits = (unsigned char*)GlobalAlloc(GMEM_FIXED,imgsize);

        //Заполняем массив битов изображения
        int i = GetDIBits( DC, // дескриптор контекста устройства
                HBM, // дескриптор изображения
                0, // первая выбираемая линия для изображения назначения
                BIP->bmiHeader.biHeight, // количество выбираемых линий
                bits, // адрес массива битов изображения
                (BITMAPINFO*)&BIP->bmiHeader,// адрес структуры с данными изображения
                DIB_RGB_COLORS // RGB или индекс палитры
                );     

        //      заполняем данные по изображению
        int nChannels = BM.bmBitsPixel == 1 ? 1 : BM.bmBitsPixel/8 ;
        int depth = BM.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U;
        IplImage* img = cvCreateImageHeader(cvSize(BM.bmWidth, BM.bmHeight), depth, nChannels);

        //      выделяем память под биты
        img->imageData = (char*) malloc( BM.bmHeight * BM.bmWidth* nChannels * sizeof(char));           

        //      копируем биты
        memcpy( img->imageData, (char*)(bits), BM.bmHeight * BM.bmWidth * nChannels);

    //  изображение получается перевернутым     
        IplImage * imgModified = cvCreateImage(cvSize(img->width,img->height),img->depth, 3);
        cvCvtColor(img, imgModified, CV_BGRA2BGR); //   в img используеются 4 канала, мне необходимо только три
        IplImage * imgMirror = cvCreateImage(cvSize(img->width,img->height),img->depth, 3);
        cvMirror(imgModified, imgMirror);       //      приводим в нормальный вид
       
        //      убираем мусор
        cvReleaseImage(&imgModified);
        free( img->imageData); 
        cvReleaseImage(&img);   
        ReleaseDC(0, DC);               
        GlobalFree((HGLOBAL)bits);     
        HeapFree(GetProcessHeap(),0,(LPVOID)BIP);               
        return  (imgMirror);           
}
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++

Re: Как создать скриншот экрана и сохранить в IplImage?

Сообщение ivan59 » 10 апр 2014, 11:47

Очень часто стала появляться эта ошибка (см.прикрепленный скриншот). Ошибка стала возникать после использования вот этого кода конвертации для сохранения скриншота экрана в IplImage. Кто то может подсказать слабые места этого кода конвертации? Поясню, РОИ в коде программы не устанавливаю, программа просто сравнивает сцену с несколькими шаблонами. Сцена всегда одна, но программа работает минуту-две и потом вылетает с этой ошибкой.
Код: Выделить всё
IplImage * GetIplImage(HBITMAP HBM)
{
        BITMAP BM;       
        LPBITMAPINFO BIP; 
        HDC DC;     
        DWORD DataSize;   
        WORD BitCount;         

        GetObject(HBM, sizeof(BITMAP), (LPSTR)&BM);
       
        BitCount = (WORD)BM.bmPlanes * BM.bmBitsPixel;
        DataSize = ((BM.bmWidth*BitCount+31) & ~31)/8*BM.bmHeight;

        BIP=(LPBITMAPINFO)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(BITMAPINFOHEADER));
        BIP->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        BIP->bmiHeader.biWidth = BM.bmWidth;
        BIP->bmiHeader.biHeight = BM.bmHeight;
        BIP->bmiHeader.biPlanes = 1;
        BIP->bmiHeader.biBitCount = BitCount;
        BIP->bmiHeader.biCompression = 0;
        BIP->bmiHeader.biSizeImage = DataSize;
        BIP->bmiHeader.biXPelsPerMeter = 0;
        BIP->bmiHeader.biYPelsPerMeter = 0;
        if (BitCount < 16)
                BIP->bmiHeader.biClrUsed = (1<<BitCount);
        BIP->bmiHeader.biClrImportant = 0;

        DC = GetDC(0);
       
        BYTE * bits;                    // массив для битов изображения
        long imgsize, pixels;   // Различные размерности для записи файла 

     

        pixels = BIP->bmiHeader.biWidth * BIP->bmiHeader.biHeight;

 /*       switch(BIP->bmiHeader.biBitCount)
        {
        case 4:
                imgsize = pixels / 2;
                break;
        case 8:
                imgsize = pixels;
                break;
        case 16:
                imgsize = pixels * 2;
                break;
        case 24:
                imgsize = pixels * 3;
                break;
        case 32:
                imgsize = pixels * 4;
                break;
        default:
                break;
        }*/

      imgsize = pixels * 4;

        //      выделяем память под биты изображения
        bits = (unsigned char*)GlobalAlloc(GMEM_FIXED,imgsize);

        //Заполняем массив битов изображения
        int i = GetDIBits( DC, // дескриптор контекста устройства
                HBM, // дескриптор изображения
                0, // первая выбираемая линия для изображения назначения
                BIP->bmiHeader.biHeight, // количество выбираемых линий
                bits, // адрес массива битов изображения
                (BITMAPINFO*)&BIP->bmiHeader,// адрес структуры с данными изображения
                DIB_RGB_COLORS // RGB или индекс палитры
                );     

        //      заполняем данные по изображению
        int nChannels = BM.bmBitsPixel == 1 ? 1 : BM.bmBitsPixel/8 ;
        int depth = BM.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U;
        IplImage* img = cvCreateImageHeader(cvSize(BM.bmWidth, BM.bmHeight), depth, nChannels);

        //      выделяем память под биты
        img->imageData = (char*) malloc( BM.bmHeight * BM.bmWidth* nChannels * sizeof(char));           

        //      копируем биты
        memcpy( img->imageData, (char*)(bits), BM.bmHeight * BM.bmWidth * nChannels);

    //  изображение получается перевернутым     
        IplImage * imgModified = cvCreateImage(cvSize(img->width,img->height),img->depth, 3);
        cvCvtColor(img, imgModified, CV_BGRA2BGR); //   в img используеются 4 канала, мне необходимо только три
        IplImage * imgMirror = cvCreateImage(cvSize(img->width,img->height),img->depth, 3);
        cvMirror(imgModified, imgMirror);       //      приводим в нормальный вид
       
        //      убираем мусор
        cvReleaseImage(&imgModified);
        free( img->imageData); 
        cvReleaseImage(&img);   
        ReleaseDC(0, DC);               
        GlobalFree((HGLOBAL)bits);     
        HeapFree(GetProcessHeap(),0,(LPVOID)BIP);               
        return  (imgMirror);           
}
Вложения
Безымянный.png
Безымянный.png (2.53 КБ) Просмотров: 14602
ivan59
 
Сообщения: 8
Зарегистрирован: 19 мар 2014, 14:02
programming: C++


Вернуться в Компьютерное зрение

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron
© 2009-2017 |  О проекте  |  Политика Конфиденциальности  |