75

微信【跳一跳】 opencv视觉识别 + 物理外挂 - HongYi_Liang

 6 years ago
source link: https://www.cnblogs.com/HongYi-Liang/p/8299460.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

视频连接:http://v.youku.com/v_show/id_XMzMyNDQxNTA0OA==.html?spm=a2h3j.8428770.3416059.1



 

993423-20180116224949146-1500535006.png

初入门C++ 与 opencv视觉库,写了一个跳一跳的物理挂,现在识别率还比较差,先记录下过程,以后在慢慢修改整理。

一、外挂结构

993423-20180116234611896-1635548372.png

上位机:USB摄像头连接windows电脑,用作处理识别拍摄到图像数据。

下位机:STM32单片机,用于控制陀机附带电容笔进行物理点击。

单片机部分很简单,所以下文主要记录上位机的内容。


二、上位机程序框架

开发平台:Visual Studio 2012 

  1. 读取摄像头数据;
  2. 提取图像中的手机屏幕、进行屏幕矫正;
  3. 识别人物和方块并计算它们的距离(现方案识别部分采用模板匹配、模板使用photoshop预先裁剪好,加载入程序中);
  4. 把计算结果通过串口交给下位机,由下位机带动陀机进行物理点击;

三、程序源码

993423-20180116220919724-948445750.png

ScreenExtraction.cpp用于识别屏幕边缘、提取内容和矫正

ContractedBlock.gifExpandedBlockStart.gif

View Code

ScreenExtraction.h为头文件

ContractedBlock.gifExpandedBlockStart.gif

View Code

ImageMatch.cpp用于识别内容

ContractedBlock.gifExpandedBlockStart.gif

View Code

ImageMatch.h为头文件

ContractedBlock.gifExpandedBlockStart.gif

View Code

SeralPort.cpp为串口相关内容,用于与下位机通信

ContractedBlock.gifExpandedBlockStart.gif

View Code

SeralPort.h为头文件

ContractedBlock.gifExpandedBlockStart.gif

View Code

TimeOperation.cpp用于计时、定时等操作

ContractedBlock.gifExpandedBlockStart.gif

View Code

TimeOperation.h为头文件

ContractedBlock.gifExpandedBlockStart.gif

View Code

main.cpp主程序

ContractedBlock.gifExpandedBlockStart.gif

View Code

main.h头文件

ContractedBlock.gifExpandedBlockStart.gif

View Code

模板匹配的素材下载:

链接:https://pan.baidu.com/s/1c3YXygC 密码:oeal


三、关键程序记录

3.1主程序的步骤

1、预处理(加载图片用于模板匹配)

ContractedBlock.gifExpandedBlockStart.gif

View Code

2、打开摄像头、串口

ContractedBlock.gifExpandedBlockStart.gif

View Code

3、主循环部分,在以下5种状态中切换

typedef enum
{
    Step_ReadCammer = 0,
    Step_CorrectScreen = 1,
    Step_MatchFeature = 2,
    Step_Communication = 3,
    Step_FastReadCammer =4,
    Step_FastCorrectScreen =5,
}mainFunctionStep;
  • Step_ReadCammer:读取摄像头,在此状态中读取摄像头数据显示到屏幕上,等待用户开始程序:
  • Step_CorrectScreen:矫正屏幕,在此状态中进行屏幕的边缘识别、屏幕内容提取矫正等操作:
  • Step_MatchFeature:匹配特征,在此状态中进行识别操作,识别人物、方块
  • Step_Communication :进行通信,告诉下位机需要点击多长时间
  • Step_FastReadCammer:快速读取摄像头,此步骤与第一步基本相同,不过不需要在进行人为干预,程序会快速读取图像进行处理
  • Step_FastCorrectScreen:快速矫正屏幕,此步骤与第二步相似,但不再做边缘识别角点检测等操作,会跟据第二步的记录数据进行快速矫正

ContractedBlock.gifExpandedBlockStart.gif

View Code

3.2关键处理:屏幕识别与提取

在ScreenExtraction类中实现了屏幕识别与提取

其中runExtract()方式为第一次识别时的操作。

runFastExtract()为第二次及以后操作,使用上一次runExtract()中储存的数据,只会保留透视变换操作。

ContractedBlock.gifExpandedBlockStart.gif

View Code

runExtract()思路如下:

  • 使用Canny边缘检测绘制摄像头的所有边缘。
  • 使用霍夫变换勾勒出4条边,所构成的矩形为屏幕部分。
  • 使用角点检测找到图片中的角点,并使用circle()函数把所有角点变为实心圆形,此时在附近的角点会连成一个很大的点,图中会剩下4个大点(如图红点)。
  • 使用边缘检测对上一步的图操作,并求出质心,效果如图绿点。
  • 使用warpPerspective()等函数进行透视变换,还原屏幕。
  • 在上述操作中,很容易出现检测失败,屏幕中多了很多点等等情况,当检测失败时,自动调整第一步中Canny的阈值,重新进行边缘检测

 

993423-20180116230525209-1720936871.png
993423-20180116230545740-999788938.png
993423-20180116230637506-1608927708.png
993423-20180116230702787-1820950858.png

 

993423-20180116230740834-1047549073.png
993423-20180116230809990-1147110716.png

3.3关键处理:识别小人和方块

识别函数在ImageMatch类中。

预先使用Photoshop提取小人和方块的图片,一张一张进行对比,选出匹配度比较高的一张,事实证明,这样做太麻烦,这个方法实在太蠢,识别率也低,以后再改成别的。

据不完全统计,游戏中的方块有如下种类,提取了一些比较特征,可以在以下素材图片第三节中的连接中下载

 

993423-20180116231158537-1332853488.png
993423-20180116231628724-758009635.png

runMatch()函数操作思路:

  • 匹配小人,计算小人的脚底坐标(如图红圈)。
  • 判断小人处于屏幕的左边还是右边,我们只需要搜索一边,缩小搜索范围减少计算量(例如在下图中,小人站在屏幕的左半区,方块匹配只需要搜索屏幕的右半区)。
  • 为了减少匹配时间,将裁减好的图转为灰度图,循环使用模板匹配上面的模板、挑出匹配率高的模板、计算方块表面中心点坐标(如图方框为识别最高的模板,和绿色圈为方块表面的中心点)。在实际匹配中发现有些方块靠的很近会被遮挡(如下图的闹钟左边被档),容易造成匹配失败,所以匹配失败的时候用那些模板的左/右半边进行再次匹配。
  • 对于容易出错的模板,单独分开来最后识别,并提高阈值。
  • 若上述操作都没高匹配的图片出现,则选择匹配度最高的一个。
  • 计算小人和方块的距离(红圈和绿圈的距离)。

 

993423-20180116232540568-1551347124.png
993423-20180116233118334-160719850.png
993423-20180116232959553-349176874.png

3.3其他程序

距离和按压时间的转换

  一次函数y=ax+b,实际使用需要根据下位机误差调整

float ImageMatch::getDistance()
{
    float result = m_fDistance*2.1f+0;
    if(result<0)
        result=0;
    return result;
}

计时类TimeOperation.cpp:

993423-20180116234000943-1114822290.png

通过获取Windows的时间函数GetLocalTime()为基础,实现两个功能

  1. 秒表(红色):用来计算某部分程序的运行时间,看看用时多久。
  2. 等待超时(蓝色):在串口发送后,需要等待一段时间才进行下一次获取,使用等待超时功能。

 串口类 SerialPort.cpp:

ContractedBlock.gifExpandedBlockStart.gif

View Code

用于打开关闭串口、数据打包发送等等。使用的通信协议如下(十六进制表示)

993423-20180116235307662-675018243.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK