视频连接: http://v.youku.com/v_show/id_XMzMyNDQxNTA0OA==.html?spm=a2h3j.8428770.3416059.1
初入门 C++ 与 opencv 视觉库, 写了一个跳一跳的物理挂,现在识别率还比较差,先记录下过程,以后在慢慢修改整理.
一,外挂结构
上位机:USB 摄像头连接 windows 电脑,用作处理识别拍摄到图像数据.
下位机:STM32 单片机,用于控制陀机附带电容笔进行物理点击.
单片机部分很简单,所以下文主要记录上位机的内容.
二,上位机程序框架
开发平台:Visual Studio 2012
思路:
读取摄像头数据;
提取图像中的手机屏幕,进行屏幕矫正;
识别人物和方块并计算它们的距离(现方案识别部分采用模板匹配,模板使用 photoshop 预先裁剪好,加载入程序中);
把计算结果通过串口交给下位机,由下位机带动陀机进行物理点击;
三,程序源码
ScreenExtraction.cpp 用于识别屏幕边缘,提取内容和矫正
View Code
ScreenExtraction.h 为头文件
View Code
ImageMatch.cpp 用于识别内容
View Code
ImageMatch.h 为头文件
View Code
SeralPort.cpp 为串口相关内容,用于与下位机通信
View Code
SeralPort.h 为头文件
View Code
TimeOperation.cpp 用于计时,定时等操作
View Code
TimeOperation.h 为头文件
View Code
main.cpp 主程序
View Code
main.h 头文件
View Code
模板匹配的素材下载:
链接: https://pan.baidu.com/s/1c3YXygC 密码:oeal
三,关键程序记录
3.1 主程序的步骤
1,预处理(加载图片用于模板匹配)
View Code
2,打开摄像头,串口
View Code
3,主循环部分,在以下 5 种状态中切换
Step_ReadCammer:读取摄像头,在此状态中读取摄像头数据显示到屏幕上,等待用户开始程序:
typedef enum {
Step_ReadCammer = 0,
Step_CorrectScreen = 1,
Step_MatchFeature = 2,
Step_Communication = 3,
Step_FastReadCammer = 4,
Step_FastCorrectScreen = 5,
}
mainFunctionStep;
Step_CorrectScreen:矫正屏幕,在此状态中进行屏幕的边缘识别,屏幕内容提取矫正等操作:
Step_MatchFeature:匹配特征,在此状态中进行识别操作,识别人物,方块
Step_Communication :进行通信,告诉下位机需要点击多长时间
Step_FastReadCammer:快速读取摄像头,此步骤与第一步基本相同,不过不需要在进行人为干预,程序会快速读取图像进行处理
Step_FastCorrectScreen:快速矫正屏幕,此步骤与第二步相似,但不再做边缘识别角点检测等操作,会跟据第二步的记录数据进行快速矫正
View Code
3.2 关键处理:屏幕识别与提取
在 ScreenExtraction 类中实现了屏幕识别与提取
其中 runExtract() 方式为第一次识别时的操作.
runFastExtract() 为第二次及以后操作,使用上一次 runExtract() 中储存的数据,只会保留透视变换操作.
View Code
runExtract() 思路如下:
使用 Canny 边缘检测绘制摄像头的所有边缘.
使用霍夫变换勾勒出 4 条边, 所构成的矩形为屏幕部分.
使用角点检测找到图片中的角点,并使用 circle()函数把所有角点变为实心圆形,此时在附近的角点会连成一个很大的点,图中会剩下 4 个大点(如图红点).
使用边缘检测对上一步的图操作,并求出质心,效果如图绿点.
使用 warpPerspective()等函数进行透视变换,还原屏幕.
在上述操作中,很容易出现检测失败,屏幕中多了很多点等等情况,当检测失败时,自动调整第一步中 Canny 的阈值,重新进行边缘检测
3.3 关键处理:识别小人和方块
识别函数在 ImageMatch 类中.
预先使用 Photoshop 提取小人和方块的图片,一张一张进行对比,选出匹配度比较高的一张,事实证明,这样做太麻烦,这个方法实在太蠢,识别率也低,以后再改成别的.
据不完全统计,游戏中的方块有如下种类,提取了一些比较特征,可以在以下素材图片第三节中的连接中下载
runMatch() 函数操作思路:
匹配小人,计算小人的脚底坐标 (如图红圈).
判断小人处于屏幕的左边还是右边,我们只需要搜索一边,缩小搜索范围减少计算量(例如在下图中,小人站在屏幕的左半区,方块匹配只需要搜索屏幕的右半区).
为了减少匹配时间,将裁减好的图转为灰度图,循环使用模板匹配上面的模板,挑出匹配率高的模板,计算方块表面中心点坐标(如图方框为识别最高的模板,和绿色圈为方块表面的中心点).在实际匹配中发现有些方块靠的很近会被遮挡(如下图的闹钟左边被档),容易造成匹配失败,所以匹配失败的时候用那些模板的左 / 右半边进行再次匹配.
对于容易出错的模板,单独分开来最后识别,并提高阈值.
若上述操作都没高匹配的图片出现,则选择匹配度最高的一个.
计算小人和方块的距离(红圈和绿圈的距离).
3.3 其他程序
距离和按压时间的转换:
一次函数 y=ax+b,实际使用需要根据下位机误差调整
计时类 TimeOperation.cpp:
float ImageMatch::getDistance()
{
float result = m_fDistance*2.1f+0;
if(result<0)
result=0;
return result;
}
通过获取 Windows 的时间函数 GetLocalTime()为基础,实现两个功能
秒表 (红色):用来计算某部分程序的运行时间,看看用时多久.
等待超时 (蓝色):在串口发送后,需要等待一段时间才进行下一次获取,使用等待超时功能.
串口类 SerialPort.cpp:
View Code
用于打开关闭串口,数据打包发送等等.使用的通信协议如下(十六进制表示)
来源: https://www.cnblogs.com/HongYi-Liang/p/8299460.html