来自截图的模板匹配 window
Template matching from a screenshot of a window
我做了什么
我有一个小模板图像,用于在较大的屏幕截图图像中查找匹配子图像的坐标。屏幕截图本身在 BitBlt
的帮助下被捕获到内存 DC,然后通过 GetDIBits
转换为 cv::Mat
,如下所示:
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_8UC4
标志放置在 cv::Mat
中,因此它在 OpenCV 中的几个断言失败(以及输出使用几种 OpenCV 方法时会出现古怪的结果)。最值得注意的是,matchTemplate
总是在以下行失败:
CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 );
我已经尝试匹配屏幕截图的深度标志和 template/result 垫子,最好的情况是我能够显示所有 3 个图像,但模板匹配不起作用因为 我假设 源(屏幕截图)图像以彩色显示,而其他图像以灰度显示。其他转换要么失败,要么显示奇怪的结果并导致模板匹配失败(或简单地引发错误)...更改屏幕截图图像的 Mat
以使用任何其他深度标志最终会错误地显示图像。
问题
如何利用 OpenCV 的模板匹配 C++ 截取的屏幕截图?我应该对屏幕截图图像进行某种手动转换吗?
代码:
截图代码取自:github.com/acdx/opencv-screen-capture
我的主要代码:codeshare.io/vLio1
我已经包含了我的代码,用于从您的桌面图像中查找模板图像。希望这能解决您的问题!
#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;
}
要扩展 Balaji 非常有用的答案,请确保传递给函数的所有 Mat
与 .type()
函数的类型相同。您可以将 src.create(height,width,CV_8UC4);
更改为与模板相同的类型(反之亦然),错误应该消失了。
我做了什么
我有一个小模板图像,用于在较大的屏幕截图图像中查找匹配子图像的坐标。屏幕截图本身在 BitBlt
的帮助下被捕获到内存 DC,然后通过 GetDIBits
转换为 cv::Mat
,如下所示:
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_8UC4
标志放置在 cv::Mat
中,因此它在 OpenCV 中的几个断言失败(以及输出使用几种 OpenCV 方法时会出现古怪的结果)。最值得注意的是,matchTemplate
总是在以下行失败:
CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 );
我已经尝试匹配屏幕截图的深度标志和 template/result 垫子,最好的情况是我能够显示所有 3 个图像,但模板匹配不起作用因为 我假设 源(屏幕截图)图像以彩色显示,而其他图像以灰度显示。其他转换要么失败,要么显示奇怪的结果并导致模板匹配失败(或简单地引发错误)...更改屏幕截图图像的 Mat
以使用任何其他深度标志最终会错误地显示图像。
问题
如何利用 OpenCV 的模板匹配 C++ 截取的屏幕截图?我应该对屏幕截图图像进行某种手动转换吗?
代码:
截图代码取自:github.com/acdx/opencv-screen-capture
我的主要代码:codeshare.io/vLio1
我已经包含了我的代码,用于从您的桌面图像中查找模板图像。希望这能解决您的问题!
#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;
}
要扩展 Balaji 非常有用的答案,请确保传递给函数的所有 Mat
与 .type()
函数的类型相同。您可以将 src.create(height,width,CV_8UC4);
更改为与模板相同的类型(反之亦然),错误应该消失了。