使用 C/C++的OpenCV 实时播放火柴人爱心舞蹈动画

使用 C++/OpenCV 实时播放火柴人爱心舞蹈动画

本文将介绍如何使用 C++/OpenCV 库实时创建一个动画窗口:一个火柴人捧着爱心跳舞,同时另一个爱心从远处飞来并逐渐变大。动画会实时在 OpenCV 窗口中播放,直到用户按下按键退出。
在这里插入图片描述

准备工作

确保你的系统上已经安装了 OpenCV 库,并且配置好了 C++ 的编译环境。

C++ 实时动画代码

以下代码会创建一个窗口并直接在其中循环播放动画。

stickman_heart_realtime.cpp:

#include <iostream>
#include <vector>
#include <cmath>
#include <string>
#include <opencv2/opencv.hpp>
#ifndef M_PI
const double M_PI = 3.14159265358979323846;
#endif
using namespace cv;
using namespace std;// --- 动画参数 ---
const int WIDTH = 800;
const int HEIGHT = 600;
const int FPS = 30; // 动画帧率 (Frames Per Second)
const Scalar BG_COLOR = Scalar(28, 28, 28); // 深灰色背景
const Scalar STICKMAN_COLOR = Scalar(255, 255, 255); // 白色火柴人
const Scalar HEART_COLOR = Scalar(70, 70, 255); // 红色爱心 (BGR)
const int STICKMAN_SCALE = 40;
const Point STICKMAN_POS(WIDTH / 3, HEIGHT - 2 * STICKMAN_SCALE);/*** @brief 绘制火柴人* @param img 要绘制的图像* @param pos 火柴人的基准位置 (脚底中心)* @param scale 控制火柴人大小* @param angle 身体的摆动角度*/
void drawStickman(Mat& img, Point pos, int scale, double angle = 0.0) {Point head_center = pos - Point(0, scale * 2);circle(img, head_center, scale / 2, STICKMAN_COLOR, 3, LINE_AA);Point body_top = head_center + Point(0, scale / 2);Point body_bottom = body_top + Point(0, scale);line(img, body_top, body_bottom, STICKMAN_COLOR, 3, LINE_AA);// 摇摆的手臂Point arm_joint = body_top + Point(0, scale / 10);Point arm_left = arm_joint + Point(-scale * 0.7 * cos(angle), scale * 0.7 * sin(angle));Point arm_right = arm_joint + Point(scale * 0.7 * cos(angle), scale * 0.7 * sin(angle));line(img, arm_joint, arm_left, STICKMAN_COLOR, 3, LINE_AA);line(img, arm_joint, arm_right, STICKMAN_COLOR, 3, LINE_AA);// 摇摆的腿Point leg_left = body_bottom + Point(-scale * 0.8* 1, scale * 0.8 * 1);Point leg_right = body_bottom + Point(scale * 0.8 *1, scale * 0.8 * 1);line(img, body_bottom, leg_left, STICKMAN_COLOR, 3, LINE_AA);line(img, body_bottom, leg_right, STICKMAN_COLOR, 3, LINE_AA);
}/*** @brief 绘制实心爱心* @param img 要绘制的图像* @param center 爱心中心点* @param radius 爱心大小*/
void drawHeart(Mat& img, Point center, int radius) {vector<Point> heart_points;for (double t = 0; t <= 2 * M_PI; t += 0.01) {double x = center.x + radius * 1.2 * sin(t) * sin(t) * sin(t);double y = center.y - radius * (1.1 * cos(t) - 0.4 * cos(2 * t) - 0.2 * cos(3 * t) - 0.1 * cos(4 * t));heart_points.push_back(Point(x, y));}fillPoly(img, vector<vector<Point>>{heart_points}, HEART_COLOR, LINE_AA);
}int main() {// 在循环外创建窗口string winName = "Stickman Heart Dance";namedWindow(winName, WINDOW_AUTOSIZE);int frame = 0;// 动画主循环while (true) {// 1. 创建每一帧的画布Mat img(HEIGHT, WIDTH, CV_8UC3, BG_COLOR);// 2. 计算动画参数// 火柴人跳舞动画 (身体和四肢摇摆)double dance_angle = sin(2 * M_PI * frame / (FPS * 2.0)) * 0.8; // 摇摆角度drawStickman(img, STICKMAN_POS, STICKMAN_SCALE, dance_angle);// 火柴人手中捧着的爱心,随着身体跳动double bounce = sin(2 * M_PI * frame / (FPS * 1.0)) * 5;Point holding_heart_offset = Point(0, -STICKMAN_SCALE * 2.5 + bounce);drawHeart(img, STICKMAN_POS + holding_heart_offset, STICKMAN_SCALE / 3);// 飞过来的爱心动画int animation_duration = FPS * 5; // 动画总时长为5秒if (frame < animation_duration) {double progress = static_cast<double>(frame) / animation_duration;// 初始大小和最终大小double start_radius = STICKMAN_SCALE * 0.1;double end_radius = STICKMAN_SCALE * 2.5;// 初始位置和最终位置Point start_pos(WIDTH * 0.9, HEIGHT * 0.2);Point end_pos = STICKMAN_POS + Point(STICKMAN_SCALE * 2, -STICKMAN_SCALE * 2.5);// 使用缓动函数 (ease-out) 使动画更自然double ease_progress = 1 - pow(1 - progress, 3);int current_radius = static_cast<int>(start_radius + (end_radius - start_radius) * ease_progress);Point current_pos = start_pos * (1.0 - ease_progress) + end_pos * ease_progress;drawHeart(img, current_pos, current_radius);}// 添加操作提示putText(img, "Press 'q' or ESC to exit", Point(10, 25), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(200, 200, 200), 1, LINE_AA);// 3. 显示当前帧imshow(winName, img);// 4. 控制帧率并等待按键// 计算每帧的延迟时间 (毫秒)int delay = 1000 / FPS;int key = waitKey(delay);// 如果按下 'q' 键或 ESC 键,则退出循环if (key == 'q' || key == 27) {break;}frame++; // 帧计数器增加}// 关闭所有窗口destroyAllWindows();cout << "Animation finished. Window closed." << endl;return 0;
}

代码逻辑解析

  1. 窗口创建 (namedWindow): 在进入主循环之前,我们先创建一个窗口。这样可以避免在每次循环时都重复创建和销毁窗口,提高效率。

  2. 动画主循环 (while(true)): 程序的核心是一个无限循环。在这个循环中,我们不断地生成和显示动画的每一帧。

  3. 实时显示 (imshow): 在循环的内部,我们用 imshow() 函数将绘制好的图像 img 显示到之前创建的窗口中。每次循环都会刷新窗口的内容,从而形成连续的动画。

  4. 控制速度与响应 (waitKey): waitKey(delay) 是实现实时动画的关键。

    • 控制速度: 它会使程序暂停 delay 毫秒。我们通过 1000 / FPS 计算出每帧应该停留的时间,从而精确控制动画的播放速度。
    • 事件处理与响应: 在暂停期间,waitKey 还会处理窗口的事件,比如检测键盘输入。我们将它的返回值存入 key 变量。
  5. 退出条件: 我们检查 key 的值。如果用户按下了 ‘q’ 键(ASCII码)或 Esc 键(ASCII码为 27),while 循环就会通过 break 语句中断,程序随之结束。

如何编译和运行

  1. 保存代码: 将以上代码保存为 stickman_heart_realtime.cpp
  2. 编译 (使用g++):
    g++ stickman_heart_realtime.cpp -o stickman_app `pkg-config opencv4 --cflags --libs`
    
    (如果你的 OpenCV 不是版本4,可以尝试 pkg-config opencv --cflags --libs)
  3. 运行:
    ./stickman_app
    

运行效果

运行程序后,你会看到一个窗口弹出,并开始播放动画:

  • 一个白色的火柴人在深灰色的背景下左右摇摆四肢,模拟跳舞。
  • 他胸前捧着一个红色的小爱心,随着他的舞步上下跳动。
  • 一个更大的爱心从屏幕右上角缓缓飞入,它的轨迹是一条平滑的曲线,并且在飞行过程中体积不断增大,最终停留在火柴人附近。
  • 动画会一直循环播放。

当你想关闭时,请确保窗口是激活状态(用鼠标点击一下窗口),然后按下键盘上的 ‘q’ 键或 Esc 键即可退出程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/84580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

复现论文报错解决

文章目录 一、 The detected CUDA version (12.9) mismatches the version that was used to compile PyTorch (11.8)二、error -- unsupported GNU version! gcc versions later than 11 are not supported!三、Unknown encoder libx264四、下载速度太慢、无法递归下载项目 一…

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …

MPMA:Preference Manipulation Attack Against Model Context Protocol

概要 MCP作为提供给LLM作为工具调用的标准化协议被广泛应用&#xff0c;但是MCP在安全方面还比较脆弱。本文提出了一种MPMA攻击方法通过在工具的名字或描述中插入具有操控性的文字、短语来操控LLM选择MCP server的过程。 不了解MCP的具体细节可以看看我写的这篇文章MCP 场景…

Vim 替换命令完整学习笔记

Vim 替换命令完整学习笔记 文章目录 Vim 替换命令完整学习笔记1. 核心概念2. 替换命令基本语法2.1 基本格式2.2 分隔符选择2.3 范围指定 3. 替换标志详解3.1 基础标志3.2 高级标志3.3 标志组合 4. 正则表达式在替换中的应用4.1 基础正则表达式4.2 字符类和量词4.3 分组和引用 5…

@RequestBody和@ResponseBody注解的作用是什么

@RequestBody和@ResponseBody注解的作用是什么 文章目录 @RequestBody和@ResponseBody注解的作用是什么@RequestBody和@ResponseBody注解的作用是什么SpringMVC的请求与响应模型1. 请求的处理流程1.1 DispatcherServlet作为入口1.2 Handler处理器与Controller1.3 HandlerAdapte…

质因数分解_java

什么是质因数&#xff1f; 说的通俗一点就是&#xff0c;这个数既是因数&#xff0c;又是质数。但是1不是质因数(不是质数)。然后比如122*2*3&#xff0c;里边的2&#xff0c;3都是质数&#xff0c;并且也是12的因数&#xff0c;所以2、3就是12的质因数。 因为本人很菜&#xf…

SpringDoc集成到Springboot

1.Maven引入jar包 <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-ui</artifactId><version>1.6.14</version> </dependency> 2.新建OpenApiConfig文件&#xff0c;代码如下 package com.elan…

Memory Repair (五)

Compression Algorithm and Fuse Box Organization 通常情况下&#xff0c;这部分信息对于实现BISR&#xff08;内置自修复&#xff09;并非必需&#xff0c;但对于诊断问题可能有所帮助。 Compression and Fuse Box Organization Overview BISR controller采用的压缩算法基于两…

双系统(win+linux)根目录扩容(不掉GPU驱动)

先看效果&#xff0c;原来的根目录仅50G&#xff0c;从/home节点分出扩容后变为250GB&#xff1b; 因为根分区是系统当前运行的文件系统&#xff0c;Linux系统启动后会锁定根分区&#xff0c;防止对其进行修改。这时使用系统内的工具&#xff08;如gparted&#xff09;调整根分…

oracle 23ai对象注释新特性ANNOTATIONS

我们知道以前数据库版本注释方式是用COMMENT&#xff0c;如下 COMMENT ON COLUMN 表名.字段名 IS 字段注释; oracle 23ai对象注释有新增了ANNOTATIONS&#xff0c;比如创建如下表&#xff1a; SQL> create table test.t_user( id number, name varchar(200) ANNOTATIONS …

VMware 虚拟机开机自启动配置指南

方法一&#xff1a;通过启动文件夹设置&#xff08;简单版&#xff09; 打开启动文件夹&#xff1a; 按下 Win R 快捷键输入 shell:startup 并回车 创建启动脚本&#xff1a; 在打开的文件夹中右键新建一个文本文件命名为 vm_start.bat输入以下内容&#xff1a; echo off &…

2025pmx文件怎么打开blender和虚幻

pmx文件怎么打开blender 前言 一坑接一坑。 意思很简单&#xff0c;就是给Blender、3dsmax装插件&#xff0c;然后就可以打开了。但是报错就要多走很多错路。 记录一下。 内容 参考文章&#xff1a;https://blog.csdn.net/c858845275/article/details/144180555 我的Ble…

Kotlin基础语法五

继承与重载的open关键字 KT所有的类&#xff0c;默认是final修饰的&#xff0c;不能被继承&#xff0c;和Java相反 open&#xff1a;移除final修饰 类型转换 open class Person2(private val name: String) {fun showName() "父类 的姓名是【$name】"// KT所有的…

MySQL数据库:关系型数据库的基石

文章目录 每日一句正能量前言一、MySQL简介&#xff08;一&#xff09;什么是MySQL&#xff1f;&#xff08;二&#xff09;MySQL的历史 二、MySQL的特点&#xff08;一&#xff09;开源与免费&#xff08;二&#xff09;高性能&#xff08;三&#xff09;跨平台支持&#xff0…

【kafka】Golang实现分布式Masscan任务调度系统

要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收扫描任务信息通过调用masscan启动探测任务,获取进度和结果信息,…

ARM 和 x86_64是什么关系

什么是 ARM 和 x86_64&#xff1f; 它们都是 CPU 指令集架构&#xff08;ISA&#xff09; 指令集架构&#xff08;Instruction Set Architecture&#xff09;就是&#xff1a; CPU 能够理解和执行的“语言”和“命令格式”。 类比解释&#xff1a;指令集就像“语言” 类比对…

nginx配置中有无‘‘/’’的区别

在Nginx配置中&#xff0c;location指令末尾的斜杠/和proxy_pass目标地址末尾的斜杠/组合使用会产生显著差异。以下是四种组合的区别详解&#xff1a; ​​核心区别对比表​​ 配置方案匹配规则请求URI传递逻辑实际转发效果示例location /api/ proxy_pass ...701/仅匹配/api/…

系统安全之身份认证

本篇我们对常用的身份认证协议做简要的梳理&#xff0c;包括主流的 HTTP 相关认证协议以及证书密钥对、新兴的 WebAuthn 认证。 HTTP 协议认证 RFC 7235 中定义了 HTTP 协议的认证框架&#xff0c;要求在支持 HTTP 协议的服务器&#xff0c;如果访问服务的身份验证失败&#…

部署http服务

使用flask搭建一个http服务&#xff0c;能够通过本地的另外一个终端访问对应接口&#xff0c;拿到服务端的计算结果 服务端&#xff1a; 创建一个test_http_dtw.py并运行 from flask import Flask, request, jsonifyapp Flask(__name__)# 示例分析函数 def analysis(data):…

WLAN 技术指南:从入门到原理

文章目录 目录 文章目录 前言 一.WLAN 基本概念 有线侧组网概念 AP-AC 组网方式 AC 连接方式 CAPWAP 协议 无线侧组网概念 无线信道 ​编辑 BSS/SSID/BSSID ​编辑 VAP ESS 二.WLAN 组网架构 基本的 WLAN 组网架构 四.WLAN 工作原理 AP 上线 AP 获取 IP 地址阶段 CAP…