Сопоставление шаблона со снимком экрана окна

Что я сделал

У меня есть небольшое изображение шаблона, которое предназначено для поиска координат совпадающих субизображений в более крупном скриншоте. Сам снимок экрана записывается в контроллер домена памяти с помощью BitBlt, затем преобразуется в cv::Mat с помощью GetDIBits, например:

HDC windowDc = GetWindowDC(hwndTarget);
HDC memDc = CreateCompatibleDC(windowDc);

// ...

HBITMAP hbmp = CreateCompatibleBitmap(windowDc, width, height);
SelectObject(memDc, hbmp);
BITMAPINFOHEADER bi =
{
    sizeof(BITMAPINFOHEADER), // biSize
    width,                    // biWidth
    -height,                  // biHeight
    1,                        // biPlanes
    32,                       // biBitCount
    BI_RGB,                   // biCompression
    0,                        // biSizeImage
    0,                        // biXPelsPerMeter
    0,                        // biYPelsPerMeter
    0,                        // biClrUser
    0                         // biClrImportant
};

// ...

BitBlt(memDc, 0, 0, width, height, windowDc, offsetX, offsetY, SRCCOPY);
matScreen.create(height, width, CV_8UC4);
GetDIBits(memDc, hbmp, 0, (UINT)height, matScreen.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

Кажется, это работает нормально, и быстрый imshow("Source", matScreen) отображает изображение правильно.

Тем не мение...

Поскольку этот снимок экрана был создан как 32-битная растровая карта памяти RGB и помещен внутри cv::Mat с использованием флага CV_8UC4, он не проходит несколько утверждений в OpenCV (а также выводит странные результаты при использовании нескольких методов OpenCV). В частности, matchTemplate всегда терпит неудачу в следующей строке:

CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 );

Я пытался сопоставить флаги глубины снимка экрана и матов шаблона/результата, и в лучшем случае я могу отобразить все 3 изображения, но сопоставление шаблона не работает, потому что я предполагается, что исходное изображение (скриншот) отображается в цвете, а остальные — в оттенках серого. Другие преобразования либо терпят неудачу, либо отображают странные результаты и не соответствуют шаблону (или просто вызывают ошибку)... И изменение Mat изображения скриншота для использования любого другого флага глубины приводит к неправильному отображению изображения.

Вопрос

Что я могу сделать, чтобы использовать шаблон OpenCV, соответствующий снимку экрана, сделанному на С++? Есть ли какое-то ручное преобразование, которое я должен сделать для изображения скриншота, или что-то в этом роде?

Код:

Код скриншота взят из: github.com/acdx/opencv-screen-capture
Мой основной код: codeshare.io/vLio1


person RectangleEquals    schedule 24.04.2015    source источник
comment
Я, вероятно, должен отметить, что изображение шаблона представляет собой 32-битный файл PNG, если это имеет значение.   -  person RectangleEquals    schedule 24.04.2015
comment
@meneldal Верно, но что я могу сделать, чтобы исправить эту проблему, не повредив исходное изображение? CV_8UC4 кажется единственным флагом, который правильно отображает исходное изображение... Так что, возможно, есть способ преобразовать растровое изображение для работы, скажем, с CV_32F?   -  person RectangleEquals    schedule 24.04.2015


Ответы (2)


Я включил свой код для поиска изображения шаблона из изображения рабочего стола. Надеюсь, это решит вашу проблему!

#include <fstream>
#include <memory>
#include <string>
#include <iostream>
#include <strstream>
#include <functional>
#include <Windows.h>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

Mat hwnd2mat(HWND hwnd){

    HDC hwindowDC,hwindowCompatibleDC;

    int height,width,srcheight,srcwidth;
    HBITMAP hbwindow;
    Mat src;
    BITMAPINFOHEADER  bi;

    hwindowDC=GetDC(hwnd);
    hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);
    SetStretchBltMode(hwindowCompatibleDC,COLORONCOLOR);  

    RECT windowsize;    // get the height and width of the screen
    GetClientRect(hwnd, &windowsize);

    srcheight = windowsize.bottom;
    srcwidth = windowsize.right;
    height = windowsize.bottom;  //change this to whatever size you want to resize to
    width = windowsize.right;

    src.create(height,width,CV_8UC4);

    // create a bitmap
    hbwindow = CreateCompatibleBitmap( hwindowDC, width, height);
    bi.biSize = sizeof(BITMAPINFOHEADER);    //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
    bi.biWidth = width;    
    bi.biHeight = -height;  //this is the line that makes it draw upside down or not
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;

    // use the previously created device context with the bitmap
    SelectObject(hwindowCompatibleDC, hbwindow);
    // copy from the window device context to the bitmap device context
    StretchBlt( hwindowCompatibleDC, 0,0, width, height, hwindowDC, 0, 0,srcwidth,srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
    GetDIBits(hwindowCompatibleDC,hbwindow,0,height,src.data,(BITMAPINFO *)&bi,DIB_RGB_COLORS);  //copy from hwindowCompatibleDC to hbwindow

    // avoid memory leak
    DeleteObject (hbwindow); DeleteDC(hwindowCompatibleDC); ReleaseDC(hwnd, hwindowDC);

    return src;
}

bool NMultipleTemplateMatching(Mat mInput,Mat mTemplate,float Threshold,float Closeness,vector<Point2f> &List_Matches)
{
    Mat mResult;
    Size szTemplate= mTemplate.size();
    Size szTemplateCloseRadius((szTemplate.width/2)* Closeness,(szTemplate.height/2)* Closeness);

    matchTemplate(mInput, mTemplate, mResult, TM_CCOEFF_NORMED);
    threshold(mResult, mResult, Threshold, 1.0, THRESH_TOZERO);
    while (true) 
    {
        double minval, maxval ;
        Point minloc, maxloc;
        minMaxLoc(mResult, &minval, &maxval, &minloc, &maxloc);

        if (maxval >= Threshold)
        {
            List_Matches.push_back(maxloc);
            rectangle(mResult,Point2f(maxloc.x-szTemplateCloseRadius.width,maxloc.y-szTemplateCloseRadius.height),Point2f(maxloc.x+szTemplateCloseRadius.width,maxloc.y+szTemplateCloseRadius.height),Scalar(0),-1);
        }
        else
            break;
    }
    //imshow("reference", mDebug_Bgr);
    return true;
}


int main(int argc, char** argv)
{
    Mat mTemplate_Bgr,mTemplate_Gray;
    mTemplate_Bgr= imread("Template.png",1);
    imshow("mTemplate_Bgr",mTemplate_Bgr);

введите здесь описание изображения

    HWND hDesktopWnd;
    hDesktopWnd=GetDesktopWindow();
    Mat mScreenShot= hwnd2mat(hDesktopWnd);
    Mat mSource_Gray,mResult_Bgr= mScreenShot.clone();


    float Threshold= 0.9;
    float Closeness= 0.9;
    vector<Point2f> List_Matches;

    cvtColor(mScreenShot,mSource_Gray,COLOR_BGR2GRAY);
    cvtColor(mTemplate_Bgr,mTemplate_Gray,COLOR_BGR2GRAY);

    namedWindow("Screen Shot",WINDOW_AUTOSIZE);
    imshow("Screen Shot",mSource_Gray);

    NMultipleTemplateMatching(mSource_Gray,mTemplate_Gray,Threshold,Closeness,List_Matches);

    for (int i = 0; i < List_Matches.size(); i++)
    {
        rectangle(mResult_Bgr,List_Matches[i],Point(List_Matches[i].x + mTemplate_Bgr.cols, List_Matches[i].y + mTemplate_Bgr.rows),Scalar(0,255,0), 2);
    }

    imshow("Final Results",mResult_Bgr);

введите здесь описание изображения

    waitKey(0);

    return 0;
}   
person Balaji R    schedule 24.04.2015
comment
То же самое, не проходит проверку утверждения. Как ни странно во время отладки, если я использую Set Next Statement на следующей строке после утверждения, то ничего не падает, изображение (я полагаю) отображается правильно, но координаты соответствия шаблона все еще неверны. - person RectangleEquals; 24.04.2015
comment
Я думаю, что OpenCV в конечном итоге играл бы лучше, если бы мне каким-то образом удалось преобразовать данные растрового изображения в прямые CV_8U или CV_32F, но я не уверен на 100%, как это сделать. - person RectangleEquals; 24.04.2015
comment
Обновленный вопрос для включения исходных ссылок - person RectangleEquals; 24.04.2015
comment
Спасибо! Не уверен, что я делал неправильно, но, по крайней мере, есть рабочее решение. - person RectangleEquals; 24.04.2015

Чтобы расширить очень полезный ответ Баладжи, убедитесь, что все Mat, передаваемые функции, имеют тот же тип, что и функция .type(). Вы можете изменить src.create(height,width,CV_8UC4); на тот же тип, что и шаблон (или наоборот), и ошибка должна исчезнуть. Вот еще одна версия решения.

person Daniel    schedule 26.07.2015