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

[复制链接]
发表于 2025-6-11 07:42:01 | 显示全部楼层 |阅读模式
利用 C++/OpenCV 实时播放火柴人爱心舞蹈动画

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

预备工作

确保你的体系上已经安装了 OpenCV 库,而且配置好了 C++ 的编译环境。
C++ 实时动画代码

以下代码会创建一个窗口并直接在此中循环播放动画。
stickman_heart_realtime.cpp:
  1. #include <iostream>
  2. #include <vector>
  3. #include <cmath>
  4. #include <string>
  5. #include <opencv2/opencv.hpp>
  6. #ifndef M_PI
  7. const double M_PI = 3.14159265358979323846;
  8. #endif
  9. using namespace cv;
  10. using namespace std;
  11. // --- 动画参数 ---
  12. const int WIDTH = 800;
  13. const int HEIGHT = 600;
  14. const int FPS = 30; // 动画帧率 (Frames Per Second)
  15. const Scalar BG_COLOR = Scalar(28, 28, 28); // 深灰色背景
  16. const Scalar STICKMAN_COLOR = Scalar(255, 255, 255); // 白色火柴人
  17. const Scalar HEART_COLOR = Scalar(70, 70, 255); // 红色爱心 (BGR)
  18. const int STICKMAN_SCALE = 40;
  19. const Point STICKMAN_POS(WIDTH / 3, HEIGHT - 2 * STICKMAN_SCALE);
  20. /**
  21. * @brief 绘制火柴人
  22. * @param img 要绘制的图像
  23. * @param pos 火柴人的基准位置 (脚底中心)
  24. * @param scale 控制火柴人大小
  25. * @param angle 身体的摆动角度
  26. */
  27. void drawStickman(Mat& img, Point pos, int scale, double angle = 0.0) {
  28.     Point head_center = pos - Point(0, scale * 2);
  29.     circle(img, head_center, scale / 2, STICKMAN_COLOR, 3, LINE_AA);
  30.     Point body_top = head_center + Point(0, scale / 2);
  31.     Point body_bottom = body_top + Point(0, scale);
  32.     line(img, body_top, body_bottom, STICKMAN_COLOR, 3, LINE_AA);
  33.     // 摇摆的手臂
  34.     Point arm_joint = body_top + Point(0, scale / 10);
  35.     Point arm_left = arm_joint + Point(-scale * 0.7 * cos(angle), scale * 0.7 * sin(angle));
  36.     Point arm_right = arm_joint + Point(scale * 0.7 * cos(angle), scale * 0.7 * sin(angle));
  37.     line(img, arm_joint, arm_left, STICKMAN_COLOR, 3, LINE_AA);
  38.     line(img, arm_joint, arm_right, STICKMAN_COLOR, 3, LINE_AA);
  39.     // 摇摆的腿
  40.     Point leg_left = body_bottom + Point(-scale * 0.8* 1, scale * 0.8 * 1);
  41.     Point leg_right = body_bottom + Point(scale * 0.8 *1, scale * 0.8 * 1);
  42.     line(img, body_bottom, leg_left, STICKMAN_COLOR, 3, LINE_AA);
  43.     line(img, body_bottom, leg_right, STICKMAN_COLOR, 3, LINE_AA);
  44. }
  45. /**
  46. * @brief 绘制实心爱心
  47. * @param img 要绘制的图像
  48. * @param center 爱心中心点
  49. * @param radius 爱心大小
  50. */
  51. void drawHeart(Mat& img, Point center, int radius) {
  52.     vector<Point> heart_points;
  53.     for (double t = 0; t <= 2 * M_PI; t += 0.01) {
  54.         double x = center.x + radius * 1.2 * sin(t) * sin(t) * sin(t);
  55.         double y = center.y - radius * (1.1 * cos(t) - 0.4 * cos(2 * t) - 0.2 * cos(3 * t) - 0.1 * cos(4 * t));
  56.         heart_points.push_back(Point(x, y));
  57.     }
  58.     fillPoly(img, vector<vector<Point>>{heart_points}, HEART_COLOR, LINE_AA);
  59. }
  60. int main() {
  61.     // 在循环外创建窗口
  62.     string winName = "Stickman Heart Dance";
  63.     namedWindow(winName, WINDOW_AUTOSIZE);
  64.     int frame = 0;
  65.     // 动画主循环
  66.     while (true) {
  67.         // 1. 创建每一帧的画布
  68.         Mat img(HEIGHT, WIDTH, CV_8UC3, BG_COLOR);
  69.         // 2. 计算动画参数
  70.         // 火柴人跳舞动画 (身体和四肢摇摆)
  71.         double dance_angle = sin(2 * M_PI * frame / (FPS * 2.0)) * 0.8; // 摇摆角度
  72.         drawStickman(img, STICKMAN_POS, STICKMAN_SCALE, dance_angle);
  73.         // 火柴人手中捧着的爱心,随着身体跳动
  74.         double bounce = sin(2 * M_PI * frame / (FPS * 1.0)) * 5;
  75.         Point holding_heart_offset = Point(0, -STICKMAN_SCALE * 2.5 + bounce);
  76.         drawHeart(img, STICKMAN_POS + holding_heart_offset, STICKMAN_SCALE / 3);
  77.         // 飞过来的爱心动画
  78.         int animation_duration = FPS * 5; // 动画总时长为5秒
  79.         if (frame < animation_duration) {
  80.             double progress = static_cast<double>(frame) / animation_duration;
  81.             // 初始大小和最终大小
  82.             double start_radius = STICKMAN_SCALE * 0.1;
  83.             double end_radius = STICKMAN_SCALE * 2.5;
  84.             // 初始位置和最终位置
  85.             Point start_pos(WIDTH * 0.9, HEIGHT * 0.2);
  86.             Point end_pos = STICKMAN_POS + Point(STICKMAN_SCALE * 2, -STICKMAN_SCALE * 2.5);
  87.             // 使用缓动函数 (ease-out) 使动画更自然
  88.             double ease_progress = 1 - pow(1 - progress, 3);
  89.             int current_radius = static_cast<int>(start_radius + (end_radius - start_radius) * ease_progress);
  90.             Point current_pos = start_pos * (1.0 - ease_progress) + end_pos * ease_progress;
  91.             drawHeart(img, current_pos, current_radius);
  92.         }
  93.         // 添加操作提示
  94.         putText(img, "Press 'q' or ESC to exit", Point(10, 25), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(200, 200, 200), 1, LINE_AA);
  95.         // 3. 显示当前帧
  96.         imshow(winName, img);
  97.         // 4. 控制帧率并等待按键
  98.         // 计算每帧的延迟时间 (毫秒)
  99.         int delay = 1000 / FPS;
  100.         int key = waitKey(delay);
  101.         // 如果按下 'q' 键或 ESC 键,则退出循环
  102.         if (key == 'q' || key == 27) {
  103.             break;
  104.         }
  105.         frame++; // 帧计数器增加
  106.     }
  107.     // 关闭所有窗口
  108.     destroyAllWindows();
  109.     cout << "Animation finished. Window closed." << endl;
  110.     return 0;
  111. }
复制代码
代码逻辑解析


  • 窗口创建 (namedWindow): 在进入主循环之前,我们先创建一个窗口。这样可以制止在每次循环时都重复创建和烧毁窗口,提高效率。
  • 动画主循环 (while(true)): 程序的核心是一个无限循环。在这个循环中,我们不断地天生和显示动画的每一帧。
  • 实时显示 (imshow): 在循环的内部,我们用 imshow() 函数将绘制好的图像 img 显示到之前创建的窗口中。每次循环都会刷新窗口的内容,从而形成连续的动画。
  • 控制速度与响应 (waitKey): waitKey(delay) 是实现实时动画的关键。

    • 控制速度: 它会使程序暂停 delay 毫秒。我们通过 1000 / FPS 盘算出每帧应该停留的时间,从而准确控制动画的播放速度。
    • 变乱处置惩罚与响应: 在暂停期间,waitKey 还会处置惩罚窗口的变乱,比如检测键盘输入。我们将它的返回值存入 key 变量。

  • 退出条件: 我们查抄 key 的值。如果用户按下了 ‘q’ 键(ASCII码)或 Esc 键(ASCII码为 27),while 循环就会通过 break 语句停止,程序随之竣事。
怎样编译和运行


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

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


  • 一个白色的火柴人在深灰色的背景下左右摇摆四肢,模拟跳舞。
  • 他胸前捧着一个红色的小爱心,随着他的舞步上下跳动。
  • 一个更大的爱心从屏幕右上角缓缓飞入,它的轨迹是一条平滑的曲线,而且在飞行过程中体积不断增大,最终停留在火柴人附近。
  • 动画会一直循环播放。
当你想关闭时,请确保窗口是激活状态(用鼠标点击一下窗口),然后按下键盘上的 ‘q’ 键或 Esc 键即可退出程序。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表