【OpenCV C++ 案例实战一】实现双人篮球游戏
【OpenCV C++ 案例实战一】实现双人篮球游戏
- 概述
- 一、 预处理
- 1.1定义结构体
- 1.2背景处理
- 1.3篮球处理
- 二、项目实现
- 2.1篮球放置(角色放置同理)
- 2.2篮球移动(角色移动同理,但是需要通过键盘监听)
- 2.3角色与篮球的碰撞检测
- 2.4角色移动
- 三、项目总结
- 3.1 复习内容
- 3.2项目亮点
- 3.3项目不足
- 四、源码下载
概述
本小游戏类似于4399中的火柴人打羽毛球游戏,在背景图上,左右双方通过键盘来进行接球操作。
实现方法比较简单,其中的知识点包括霍夫圆检测,键盘事件响应,背景填充等。
做这个小游戏的目的主要是为了用于OpenCV学习的阶段性展示。
演示视频如下所示:
basketBallGame
一、 预处理
1.1定义结构体
为了增强代码的可读性以及后续对程序的修改,我们对项目中的一些重要对象定义结构体。
包括:游戏角色、背景图、篮球。
// 定义篮球的结构体(把篮球看作圆形。x,y为篮球位置;d为篮球直径)
typedef struct ballLoc {double x;double y;double d;
};
// 人物结构体(人物的高度,宽度,位置)
typedef struct kun {double heigth;double width;double x;double y;
};
// 背景图结构体(背景图的宽度,高度)
typedef struct backgroundI {int bg_width;int bg_height;
};
1.2背景处理
背景图使用 resize() 函数调整到合适的大小
1.3篮球处理
篮球如下图所示,为了让篮球能够完美地贴合到背景图中,我们这里的先使用霍夫圆检测,把篮球单独从图像之中抠出来备用。
篮球处理分为以下步骤:
1. 预处理(灰度化、高斯模糊)
2. 霍夫圆检测
3. 在检测出来的圆中根据特征提取出我们需要的圆
4. 将提取出来的圆单独定义为一张图像,调整大小并返回给主函数
实现代码如下:
// 篮球处理
Mat ballDetect(Mat image) {// 1. 预处理(灰度化,滤波)Mat gray_image,gauss_image;cvtColor(image, gray_image, COLOR_BGR2GRAY);GaussianBlur(gray_image, gauss_image, Size(3, 3), 0, 0);// 2. 霍夫圆检测// 圆的表示:前两个参数为圆心坐标,第三个参数为半径vector<Vec3f> ball_circles;HoughCircles(gauss_image, ball_circles, HOUGH_GRADIENT, 1, 50, 80, 80, 50, 200);// 3. 找到需要的篮球的圆(最大的圆)Vec3f ball_circle = ball_circles[0];for (int i = 0; i < ball_circles.size(); i++){if (ball_circles[i][2] > ball_circle[2]) {ball_circle = ball_circles[i];}}// 4. 这里的(x,y)为通过圆参数计算出来的正方形的左上角坐标,d为正方形的边长double x = ball_circle[0] - ball_circle[2];double y = ball_circle[1] - ball_circle[2];double d = ball_circle[2]*2;Rect rect(x, y,d ,d);// 单独把篮球提取出成一张图像Mat det_ball;image(rect).copyTo(det_ball);resize(det_ball, det_ball, Size(ball_01.d, ball_01.d));return det_ball;
}
二、项目实现
2.1篮球放置(角色放置同理)
如果使用常规的ball(ROI).copyTo(background(ROI)); 需要把感兴趣区域设置为圆形,但是由于没有看懂这块是怎么用的(我不会!)。所以使用数学的方法暴力进行背景填充。
如下图所示,给出一个圆形和他的外接正方形(正方形边长与圆直径相同),将中心视作图像的原点,那么我们就可以通过判断 x,y的坐标与圆半径r的关系得出这个点是否落在圆内。
但是由于图形的默认坐标是从左上角算起。即 (0,0)点 为图像的左上点。所以我们在遍历图像像素点的过程中应当使用额外的参数来进行判断。
实现代码如下:
圆形区域(篮球)像素点不变;圆形区域外的像素用背景图的对应像素进行替换
// 画球(主要用来分离背景)
// 有坐标位置,有背景图
void drawBall(Mat & ball) { int r = ball_01.d / 2;for (int i = -r, ix = 0; i < ball_01.d - r; i++, ix++) {for (int j = -r, iy = 0; j < ball_01.d - r; j++, iy++) {if (i * i + j * j > r * r) {for (int c = 0; c < 3; c++) {ball.at<Vec3b>(iy, ix)[c] = background.at<Vec3b>(ball_01.y + iy, ball_01.x + ix)[c];}}}}
}
2.2篮球移动(角色移动同理,但是需要通过键盘监听)
在主函数中通过while(1){} ,不断循环绘制篮球(通过篮球坐标),由于我们定义了篮球位置的结构体,所以如果想要改变篮球的位置,修改其坐标即可。
// x向着左边越界if (ball_01.x <= 5) {ball_01.x += 1;b_x = -b_x;}// x右边界越界else if (ball_01.x >= (bg_width - ball_01.d - 4)) {ball_01.x -= 1;b_x = -b_x;}// 上边界else if (ball_01.y <= 5) {ball_01.y += 1;b_y = -b_y;}// 下边界else if (ball_01.y >= (bg_height - ball_01.d - 4)) {ball_01.y -= 1;b_y = -b_y;break;}
篮球撞墙后的变向思路如下图所示:
2.3角色与篮球的碰撞检测
角色与篮球碰撞后,篮球的运动方向改变,且角色状态改变。
这里只是通过篮球和角色的位置以及他们各自的高度宽度进行检测,因此如果当篮球的大小设置的过于大时,会出现bug,感觉后续可以通过opencv视觉的方法进行替换。代码如下:
// 检测哥哥与篮球碰撞if (((ball_01.y + ball_01.d) >= kun01.y &&(ball_01.x + ball_01.d) >= kun01.x &&(ball_01.x + ball_01.d) <= (kun01.x + kun01.width))||((ball_01.y + ball_01.d) >= kun_right.y &&(ball_01.x + ball_01.d) >= kun_right.x &&(ball_01.x + ball_01.d) <= (kun_right.x + kun_right.width))){drawKun(background, kunImage2, 0);drawKun(background, kunImage2, 1);b_y = -b_y;}
2.4角色移动
角色移动通过监听键盘响应实现。
使用 int key = waitKeyEx(1); 获取到按键,再通过按键内容进行响应。
int key = waitKeyEx(1);if (key == 2424832){//键盘←键if (kun01.x > 10) {kun01.x -= 10;drawKun(background, kunImage2, 0);}}if (key == 2555904){//键盘→键if (kun01.x < ((bg01.bg_width - 10 - kun01.width)/2)) {kun01.x += 10;drawKun(background, kunImage2, 0);}}if (key == 97){//键盘a键if (kun_right.x > ((bg01.bg_width - 10 - kun_right.width)/2)) {kun_right.x -= 10;drawKun(background, kunImage2, 1);}}if (key == 100){//键盘d键if (kun_right.x < (bg01.bg_width - 10 - kun_right.width)) {kun_right.x += 10;drawKun(background, kunImage2, 1);}}
三、项目总结
3.1 复习内容
- 复习了霍夫圆检测
- 复习了opencv中圆的表示方法:Vec3f(前两个参数表示圆心位置,第三个参数表示圆半径)
vector<Vec3f> ball_circles; HoughCircles(gauss_image, ball_circles, HOUGH_GRADIENT, 1, 50, 80, 80, 50, 200);
- 复习了鼠标监听方法
int key = waitKeyEx(1);key = 100:dkey = 97:akey = 2555904:键盘→键key = 2424832:键盘←键
3.2项目亮点
可以尝试用数学的角度去解决一些图像中的问题。
例如正方形中最大圆的检测以及碰撞中球的运动轨迹变化
3.3项目不足
- 学一下自定义(圆)掩膜层可以更方便高效的解决篮球在背景图的放置问题。
- 利用opencv视觉方法可以解决因为通过坐标判断人物与篮球碰撞带来的bug。
四、源码下载
https://download.csdn.net/download/weixin_46221106/87212956
【OpenCV C++ 案例实战一】实现双人篮球游戏相关推荐
- OpenCV C++案例实战三《二维码检测》
OpenCV C++案例实战三<二维码检测> 前言 一.二维码检测 二.二维码识别 1.通过findContours找到轮廓层级关系 三.二维码绘制 四.源码 总结 前言 本文将使用Ope ...
- OpenCV C++案例实战五《答题卡识别》
OpenCV C++案例实战五<答题卡识别> 前言 一.图像矫正 1.源码 二.获取选项区域 1.扣出每题选项 2.源码 三.获取答案 1.思路 2.辅助函数 3.源码 4.效果 总结 前 ...
- OpenCV C++案例实战二十九《遥感图像分割》
OpenCV C++案例实战二十九<遥感图像分割> 前言 一.准备数据 二.K-Means分类 三.效果显示 四.源码 总结 前言 本案例基于k-means机器学习算法进行遥感图像分割.主 ...
- OpenCV C++案例实战十八《抖音特效——“蓝线挑战”》
OpenCV C++案例实战十八<抖音特效--"蓝线挑战"> 前言 一.图像扫描 二.生成定格图像 三.图像混合 四.效果显示 五.源码 总结 前言 本文将使用Open ...
- OpenCV C++案例实战六《绿幕视频背景替换》
OpenCV C++案例实战六<绿幕视频背景替换> 前言 一.图像预处理 二.HSV色彩空间转换 1. cvtColor色彩空间转换 2. inRange抠图 三.背景替换 四.源码 总结 ...
- OpenCV C++案例实战二《生成蒙太奇图像》
OpenCV C++案例实战二<生成蒙太奇图像> 前言 一.输入模板图像 二.读取素材图像 三.生成蒙太奇模板 四.生成蒙太奇图像 五.源码 总结 前言 本文将使用OpenCV C++ 生 ...
- OpenCV C++案例实战十九《制作电子相册查看器》
OpenCV C++案例实战十九<制作电子相册查看器> 前言 一.图片读取 二.图片展示 三.键盘控制 四.效果显示 五.源码 总结 前言 本文将使用OpenCV C++ 制作电子相册查看 ...
- c++实战(OpenCV C++案例实战九《对象计数》)
c++实战(OpenCV C++案例实战九<对象计数>) 一.OpenCV C++案例实战九<对象计数> 一.OpenCV C++案例实战九<对象计数> OpenC ...
- OpenCV C++案例实战十六《制作哈哈镜图像》
OpenCV C++案例实战十六<制作哈哈镜图像> 前言 一.凸透镜 1.功能源码 2.效果显示 二.凹透镜 1.功能源码 2.效果显示 三.源码 总结 前言 本文将使用OpenCV C+ ...
最新文章
- 搜索:广搜 词语阶梯
- 计算机基础课程教学创新,【计算机基础论文】大学计算机基础课程教学创新探讨(共5359字)...
- Android edittext 属性inputtype详解
- c++ 中const的使用
- Linux之Nginx
- LINUX性能调优方法总结
- 利用图基Tukey method检测数据集中的异常值
- [转]带花树,Edmonds's matching algorithm,一般图最大匹配
- win10看计算机属性,win10系统查看windows7版本号要比计算机属性具体的图文步骤
- ubuntu16.04升级 vim 8.0
- ionic checkbox 精简用法
- 那年学过的web后端笔记
- Hbase API实现倒序查询
- 什么是WAP PUSH?
- displayTag使用详解
- 多年iOS开发经验总结
- linux内核源码阅读之facebook硬盘加速flashcache之二
- 电脑上怎么压缩GIF动图?简单好用的压缩工具分享给你
- 全国计算机能力挑战赛含金量高吗,大学里,有哪些含金量高,又容易得奖的国家级比赛?...
- 数学规划模型总结(附MatLab代码)