Screenshot В Cv::mat

Тема в разделе "MS Visual C++", создана пользователем rs4i, 22 апр 2013.

  1. rs4i

    rs4i Гость

    Собственно, название темы. Как?
    К сожалению, я не силен в графике и с соответствующими библиотеками знаком слабо.
    Думал использовать OpenGL, нашел красивый код:
    Код (C++):
    //glReadBuffer( GL_FRONT );
    cv::Mat img( 200, 200, CV_8UC3 );
    glPixelStorei( GL_PACK_ALIGNMENT, ( img.step & 3 )? 1: 4 );
    glPixelStorei( GL_PACK_ROW_LENGTH, img.step/img.elemSize() );
    glReadPixels( 0, 0, img.cols, img.rows, GL_BGR_EXT, GL_UNSIGNED_BYTE, img.data );
    //cv::Mat flipped( img );
    //cv::flip( img, flipped, 0 );
    //cv::imwrite( "C:\\Projects\\OpenCV\\snapshot.png", img );
    Выдает чёрный квадрат.
    Заставить работать не удалось.

    Пробовал Win32api, наворочал костылей, получил тормоза.
    Вот кривой и тормозной, но рабочий код:
    Код (C++):
    std::auto_ptr< cv::Mat > getScreen(){
    HWND hDW = GetDesktopWindow(); 
    HDC hDWDC = GetWindowDC( hDW );
    RECT rect; GetWindowRect( hDW, &rect );
    unsigned width = rect.right;
    unsigned height = rect.bottom;
    HDC hScreen = CreateCompatibleDC( hDWDC );
    HBITMAP hBM = CreateCompatibleBitmap( hDWDC, width, height );
    HGDIOBJ temp = SelectObject( hScreen, hBM );
    BitBlt( hScreen, 0, 0, width, height, hDWDC, 0, 0, SRCCOPY );

    std::auto_ptr< cv::Mat > screen( new cv::Mat( height, width, CV_8UC3 ) );

    for( unsigned y = 0; y < height; ++y )
    for( unsigned x = 0; x < width; ++x ){
    COLORREF color = GetPixel( hScreen, x, y );
    screen->data[ ( y * screen->cols + x ) * screen->elemSize() + 0 ] = GetBValue( color ); // B
    screen->data[ ( y * screen->cols + x ) * screen->elemSize() + 1 ] = GetGValue( color ); // G
    screen->data[ ( y * screen->cols + x ) * screen->elemSize() + 2 ] = GetRValue( color ); // R
    }

    // нужно поудалять созданные объекты!!!

    return screen;
    }
    Пробовал задействовать GetDIBits, картинка скосилась.
    Видимо в Bitmap строки выровнены по границе слова (кратны 4).
    Код (C++):
    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 = 24;
    BMI.bmiHeader.biCompression = BI_RGB;

    int result = GetDIBits( hScreen, hBM, 0, height, screen->data, &BMI, DIB_RGB_COLORS );
    Подскажите пожалуйста
    1. Как заставить работать glReadPixels (или другую функцию OpenGL) с экраном Windows.
    2. Как заставить GDI BitBlt или GetDIBits копировать данные в cv::Mat с учетом выравнивания строк.

    Добавлено: Сожалею что создал одинаковые темы.
    Какой-то глюк форума.
    При создании темы выдавало ошибку.
     
  2. rs4i

    rs4i Гость

    Пришлось копировать через 2 промежуточных буфера.
    И хотя, скорость работы меня уже устраивает,
    хотелось бы знать, нет ли более простого способа?

    Итого:
    Код (C++):
    std::auto_ptr< cv::Mat > getScreen(){
    // Получить параметры экрана
    HWND hDW = GetDesktopWindow(); 
    HDC hDWDC = GetWindowDC( hDW );
    int width = GetSystemMetrics( SM_CXSCREEN );
    int height = GetSystemMetrics( SM_CYSCREEN );

    // Скопировать экран в буфер Windows
    HDC hScreen = CreateCompatibleDC( hDWDC ); // - DeleteDC( hScreen );
    HBITMAP hBM = CreateCompatibleBitmap( hDWDC, width, height ); // - DeleteObject( hBM );
    HGDIOBJ temp = SelectObject( hScreen, hBM ); // - SelectObject( hScreen, temp );
    BitBlt( hScreen, 0, 0, width, height, hDWDC, 0, 0, SRCCOPY );

    // Скопировать в свой буфер
    const int colorSize = 3;
    const int alignment = 4;
    int step = ( width * colorSize / alignment + ( width * colorSize % alignment? 1: 0 ) ) * alignment;
    char* buffer = new char[ step * height ]; // - delete[] buffer;
    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 = 24;
    BMI.bmiHeader.biCompression = BI_RGB;  
    GetDIBits( hScreen, hBM, 0, height, buffer, &BMI, DIB_RGB_COLORS );

    // Заполнить cv::Mat из буфера
    std::auto_ptr< cv::Mat > screen( new cv::Mat( height, width, CV_8UC3 ) );
    for( int y = 0; y < height; ++y )
    for( int x = 0; x < width; ++x ){
    int pScreen = ( y * screen->cols + x ) * screen->elemSize();
    int pBuffer = y * step + x * colorSize;
    screen->data[ pScreen + 0 ] = buffer[ pBuffer + 0 ]; // B
    screen->data[ pScreen + 1 ] = buffer[ pBuffer + 1 ]; // G
    screen->data[ pScreen + 2 ] = buffer[ pBuffer + 2 ]; // R
    }

    // Удалить не нужные объекты
    SelectObject( hScreen, temp );
    DeleteObject( hBM );
    DeleteDC( hScreen );
    delete[] buffer;

    return screen;
    }
    P.S. Есть тут модераторы?
    Прошу удалить дубли.

    P.P.S. Форум при публикации этого сообщения опять глюкнул.
     
  3. rs4i

    rs4i Гость

    Выкинул один буфер.
    Лучше сделать нельзя.
    С темы ухожу.
    Спасибо за внимание.

    Код (C++):
    #include< windows.h >
    #include< opencv2/opencv.hpp >

    class Screen{
    public:
    Screen(){
    // Параметры экрана
    HWND hwnd = GetDesktopWindow();
    hWDC = GetWindowDC( hwnd );
    width = GetSystemMetrics( SM_CXSCREEN );
    height = GetSystemMetrics( SM_CYSCREEN );

    // Совместимый контекст в памяти
    hScreen = CreateCompatibleDC( hWDC ); // - DeleteDC( hScreen );
    hBM = CreateCompatibleBitmap( hWDC, width, height ); // - DeleteObject( hBM );
    hBM_Temp = SelectObject( hScreen, hBM ); // - SelectObject( hScreen, hBM_Temp );

    // Описание рабочего массива бит совместимого контекста в памяти
    BMI.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
    BMI.bmiHeader.biWidth = width;
    BMI.bmiHeader.biHeight = -height;
    BMI.bmiHeader.biPlanes = 1;
    BMI.bmiHeader.biBitCount = 24;
    BMI.bmiHeader.biCompression = BI_RGB;

    // Массив данных скриншота
    const int colorSize = 3; // Размер пикселя
    const int alignment = 4; // Выравнивание строки        
    int step = ( int )ceil( width * colorSize / ( double )alignment ) * alignment; // Шаг строки
    //int step = ( width * colorSize / alignment + ( width * colorSize % alignment? 1: 0 ) ) * alignment; // Шаг строки
    data = new char[ step * height ]; // - delete[] data;

    // Скриншот
    screen = new cv::Mat( height, width, CV_8UC3, data, step ); // - delete screen;
    };

    ~Screen(){
    SelectObject( hScreen, hBM_Temp );
    DeleteObject( hBM );
    DeleteDC( hScreen );
    delete screen; screen = NULL;
    delete[] data; data = NULL;
    };

    cv::Mat& get(){
    BitBlt( hScreen, 0, 0, width, height, hWDC, 0, 0, SRCCOPY );
    GetDIBits( hScreen, hBM, 0, height, data, &BMI, DIB_RGB_COLORS );
    return *screen;
    };

    private:
    HDC hWDC; // Контекст экрана
    HDC hScreen; // Совместимый с экраном контекст в памяти
    HBITMAP hBM; // Рабочий массив бит совместимого контекста в памяти
    BITMAPINFO BMI; // Описание рабочего массива бит совместимого контекста в памяти

    int width; // Ширина экрана в пикселях
    int height; // Высота экрана в пикселях

    char* data; // Массив данных скриншота
    cv::Mat* screen; // Скриншот

    HGDIOBJ hBM_Temp; // Массив бит совместимого контекста в памяти созданный по умолчанию
    };

    int main(){
    Screen sh;
    cv::Mat screen = sh.get();
    cv::imwrite( "C:\\MyPath\\snapshot.png", screen );
    return 0;
    }
     
Загрузка...
Похожие Темы - Screenshot mat
  1. motogarri
    Ответов:
    6
    Просмотров:
    327
  2. SvetlanaL
    Ответов:
    0
    Просмотров:
    165
  3. SvetlanaL
    Ответов:
    0
    Просмотров:
    203
  4. itincorp
    Ответов:
    0
    Просмотров:
    204
  5. SvetlanaL
    Ответов:
    0
    Просмотров:
    256

Поделиться этой страницей