【OpenCVC++案例实战一】实现双人篮球游戏-创新互联

【OpenCV C++ 案例实战一】实现双人篮球游戏
  • 概述
  • 一、 预处理
    • 1.1定义结构体
    • 1.2背景处理
    • 1.3篮球处理
  • 二、项目实现
    • 2.1篮球放置(角色放置同理)
    • 2.2篮球移动(角色移动同理,但是需要通过键盘监听)
    • 2.3角色与篮球的碰撞检测
    • 2.4角色移动
  • 三、项目总结
    • 3.1 复习内容
    • 3.2项目亮点
    • 3.3项目不足
  • 四、源码下载

成都创新互联主要为客户提供服务项目涵盖了网页视觉设计、VI标志设计、营销网站、网站程序开发、HTML5响应式网站建设公司成都做手机网站、微商城、网站托管及网站建设维护、WEB系统开发、域名注册、国内外服务器租用、视频、平面设计、SEO优化排名。设计、前端、后端三个建站步骤的完善服务体系。一人跟踪测试的建站服务标准。已经为成都石牌坊行业客户提供了网站开发服务。
概述

本小游戏类似于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. 霍夫圆检测
    // 圆的表示:前两个参数为圆心坐标,第三个参数为半径
    vectorball_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(iy, ix)[c] = background.at(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 复习内容
  1. 复习了霍夫圆检测
  2. 复习了opencv中圆的表示方法:Vec3f(前两个参数表示圆心位置,第三个参数表示圆半径)
    vectorball_circles;
    HoughCircles(gauss_image, ball_circles, HOUGH_GRADIENT, 1, 50, 80, 80, 50, 200);
  3. 复习了鼠标监听方法
    int key = waitKeyEx(1);
    	key = 100:d
    	key = 97:a
    	key = 2555904:键盘→键
    	key = 2424832:键盘←键
3.2项目亮点

可以尝试用数学的角度去解决一些图像中的问题。
例如正方形中大圆的检测以及碰撞中球的运动轨迹变化

3.3项目不足
  1. 学一下自定义(圆)掩膜层可以更方便高效的解决篮球在背景图的放置问题。
  2. 利用opencv视觉方法可以解决因为通过坐标判断人物与篮球碰撞带来的bug。
四、源码下载

https://download.csdn.net/download/weixin_46221106/87212956

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前题目:【OpenCVC++案例实战一】实现双人篮球游戏-创新互联
文章网址:http://ybzwz.com/article/eecos.html