Screenshot В Cv::mat

  • Автор темы Автор темы rs4i
  • Дата начала Дата начала
R

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 промежуточных буфера.
И хотя, скорость работы меня уже устраивает,
хотелось бы знать, нет ли более простого способа?

Итого:
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. Форум при публикации этого сообщения опять глюкнул.
 
Выкинул один буфер.
Лучше сделать нельзя.
С темы ухожу.
Спасибо за внимание.

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;
}
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!