使用 C++/OpenCV 制作跳动的爱心动画

article/2025/6/11 11:12:35

使用 C++/OpenCV 制作跳动的爱心动画

本文将引导你如何使用 C++ 和 OpenCV 库创建一个简单但有趣的跳动爱心动画。我们将通过绘制参数方程定义的爱心形状,并利用正弦函数来模拟心跳的缩放效果。
在这里插入图片描述


目录

  1. 简介
  2. 先决条件
  3. 核心概念
    • 参数方程绘制爱心
    • 动画循环
    • 模拟心跳效果
  4. 代码实现
  5. 代码详解
    • drawHeart 函数
    • main 函数
  6. 编译与运行
  7. 效果展示
  8. 总结与扩展

简介

动画是通过快速连续显示一系列静态图像来创建运动的幻觉。在这个项目中,我们将每一帧都绘制一个爱心,并通过周期性地改变爱心的大小来模拟“跳动”的效果。这是一个学习 OpenCV 绘图和基本动画原理的有趣练习。


先决条件

  • C++ 编译器: 如 G++ (MinGW for Windows), Clang, MSVC。
  • OpenCV 库: 版本 3.x 或 4.x,并已正确配置编译环境。
  • CMake (推荐): 用于跨平台编译管理。

核心概念

参数方程绘制爱心

爱心的形状可以通过参数方程精确绘制。一个常用的爱心参数方程是:
x = 16 sin ⁡ 3 ( t ) x = 16 \sin^3(t) x=16sin3(t)
y = 13 cos ⁡ ( t ) − 5 cos ⁡ ( 2 t ) − 2 cos ⁡ ( 3 t ) − cos ⁡ ( 4 t ) y = 13 \cos(t) - 5 \cos(2t) - 2 \cos(3t) - \cos(4t) y=13cos(t)5cos(2t)2cos(3t)cos(4t)

其中 t t t 的范围从 0 0 0 2 π 2\pi 2π。我们将计算出一系列由这些方程产生的 ( x , y ) (x, y) (x,y) 点,然后将这些点连接起来形成多边形,并填充颜色。

动画循环

动画的核心是一个循环,在每次迭代中:

  1. 清除或重新初始化画布。
  2. 根据当前时间或状态计算爱心的大小和/或位置。
  3. 在画布上绘制爱心。
  4. 显示画布。
  5. 短暂延迟,以控制动画速度。
  6. 检查退出条件(例如按键)。

模拟心跳效果

心跳是一个周期性的扩张和收缩过程。我们可以使用正弦函数来模拟这种大小变化。爱心的缩放比例 scale 可以表示为:
current_scale = base_scale * (1 + amplitude * sin(frequency * time_variable))
其中:

  • base_scale 是爱心的基础大小。
  • amplitude 控制跳动的幅度。
  • frequency 控制跳动的快慢。
  • time_variable 是一个随时间递增的变量(例如帧计数器)。

代码实现

#include <opencv2/opencv.hpp>
#include <vector>
#include <cmath> // For std::sin, std::cos, std::pow, M_PI (or define PI)#ifndef M_PI
#define M_PI 3.14159265358979323846
#endifconst int WINDOW_WIDTH = 600;
const int WINDOW_HEIGHT = 600;
const std::string WINDOW_NAME = "Beating Heart";// 函数:绘制一个爱心
void drawHeart(cv::Mat& image, double scale, const cv::Point& center, const cv::Scalar& color) {std::vector<cv::Point> heart_points;for (double t = 0; t <= 2 * M_PI; t += 0.01) { // t 从 0 到 2*PIdouble x_param = 16 * std::pow(std::sin(t), 3);double y_param = 13 * std::cos(t) - 5 * std::cos(2 * t) - 2 * std::cos(3 * t) - std::cos(4 * t);// 转换到 OpenCV 坐标系并应用缩放和中心偏移// 注意 y_param 是负的,因为参数方程通常y轴向上,而OpenCV图像y轴向下int cv_x = static_cast<int>(center.x + scale * x_param);int cv_y = static_cast<int>(center.y - scale * y_param); // 注意这里的负号heart_points.push_back(cv::Point(cv_x, cv_y));}if (!heart_points.empty()) {// cv::polylines(image, heart_points, true, color, 2); // 仅绘制轮廓cv::fillPoly(image, std::vector<std::vector<cv::Point>>{heart_points}, color); // 填充爱心}
}int main() {cv::Mat frame(WINDOW_HEIGHT, WINDOW_WIDTH, CV_8UC3, cv::Scalar(20, 20, 20)); // 深灰色背景double time = 0.0;double base_scale = 10.0;        // 爱心的基础缩放系数double beat_amplitude = 0.2;     // 心跳振幅 (相对于1的比例)double beat_frequency = 0.15;    // 心跳频率 (数值越大跳动越快)cv::Scalar heart_color = cv::Scalar(0, 0, 255); // BGR: 红色while (true) {// 1. 清除画布 (用背景色重新填充)frame = cv::Scalar(20, 20, 20);// 2. 计算当前心跳的缩放比例// (1 + sin(t)) 范围是 [0, 2], (1 + sin(t))/2 范围是 [0, 1]// 这里我们用 1.0 +/- amplitude*sin(t) 来使大小在 (1-amplitude)*base_scale 和 (1+amplitude)*base_scale 之间变化double current_beat_factor = 1.0 + beat_amplitude * std::sin(beat_frequency * time);double current_scale = base_scale * current_beat_factor;// 3. 绘制爱心cv::Point center(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2 + 30); // 稍微向下移动一点,让爱心尖端更居中drawHeart(frame, current_scale, center, heart_color);// 4. 显示帧cv::imshow(WINDOW_NAME, frame);// 5. 延迟并检查按键int key = cv::waitKey(30); // 大约 33 FPSif (key == 27) { // ESC键break;}time += 1.0; // 更新时间变量}cv::destroyAllWindows();return 0;
}

代码详解

drawHeart 函数

  • cv::Mat& image: 要在其上绘制爱心的画布。
  • double scale: 控制爱心整体大小的缩放因子。
  • const cv::Point& center: 爱心在画布上的中心点。
  • const cv::Scalar& color: 爱心的颜色。
  • 循环变量 t02 * M_PI 步进,计算参数方程定义的点 (x_param, y_param)
  • cv_x = static_cast<int>(center.x + scale * x_param);
  • cv_y = static_cast<int>(center.y - scale * y_param);:将参数点转换为 OpenCV 窗口坐标。特别注意 y_param 前的负号,这是因为参数方程中定义的 y 轴通常指向上方,而 OpenCV 图像的 y 轴指向下方。
  • 计算出的点存储在 std::vector<cv::Point> heart_points 中。
  • cv::fillPoly(...): 使用指定的颜色填充由 heart_points 定义的多边形(爱心)。cv::polylines 可以用来只画轮廓。

main 函数

  • cv::Mat frame(...): 创建一个 CV_8UC3 类型(8位无符号字符型,3通道彩色)的图像作为画布,并用深灰色初始化背景。
  • time: 一个简单的时间变量/帧计数器,用于驱动心跳动画的周期性变化。
  • base_scale: 爱心参数方程中固有大小的一个基础乘数。调整它会改变爱心的整体大小。
  • beat_amplitude: 控制心跳时大小变化的幅度。例如,0.2 表示大小在基础大小的 (1-0.2)倍 到 (1+0.2)倍之间变化。
  • beat_frequency: 控制心跳的快慢。值越大,跳动越快。
  • while (true): 主动画循环。
    • frame = cv::Scalar(20, 20, 20);: 每帧开始时,用背景色重置(清空)画布。
    • current_beat_factor = 1.0 + beat_amplitude * std::sin(beat_frequency * time);: 使用 std::sin 函数计算当前帧的缩放因子。sin 函数产生 [-1, 1] 范围内的值,因此 current_beat_factor 会在 [1.0 - beat_amplitude, 1.0 + beat_amplitude] 之间平滑地变化。
    • current_scale = base_scale * current_beat_factor;: 计算最终应用于 drawHeart 函数的缩放值。
    • drawHeart(...): 调用函数绘制当前帧的爱心。
    • cv::imshow(WINDOW_NAME, frame);: 显示包含爱心的帧。
    • cv::waitKey(30);: 等待 30 毫秒。这既控制了动画的帧率,也为 OpenCV 处理窗口事件(如按键)提供了机会。如果用户按下 ESC 键(ASCII码 27),循环中断。
    • time += 1.0;: 递增时间变量,为下一帧的计算做准备。
  • cv::destroyAllWindows();: 关闭所有 OpenCV 创建的窗口。

编译与运行

假设你的 C++ 文件名为 beating_heart.cpp

使用 CMake (推荐):

  1. 创建 CMakeLists.txt 文件:
    cmake_minimum_required(VERSION 3.10)
    project(BeatingHeart)set(CMAKE_CXX_STANDARD 14) # C++14 或更高find_package(OpenCV REQUIRED)include_directories(${OpenCV_INCLUDE_DIRS})add_executable(BeatingHeartApp beating_heart.cpp)
    target_link_libraries(BeatingHeartApp ${OpenCV_LIBS})
    
  2. 编译:
    mkdir build && cd build
    cmake ..
    make  # 或者在 Visual Studio 中打开生成的项目并编译
    ./BeatingHeartApp # Linux/macOS
    # .\Release\BeatingHeartApp.exe # Windows (Visual Studio Release build)
    

直接使用 g++ (Linux/macOS):

g++ beating_heart.cpp -o BeatingHeartApp $(pkg-config --cflags --libs opencv4)
./BeatingHeartApp

(如果你的 pkg-config 对应 OpenCV 3.x,请使用 opencv 而不是 opencv4)


效果展示

运行程序后,你将看到一个窗口,其中有一个红色的爱心在深灰色背景上平滑地放大和缩小,模拟心跳的效果。按 ESC 键可以关闭窗口并退出程序。


总结与扩展

通过这个简单的项目,我们学习了如何使用 OpenCV 的绘图功能和参数方程来创建形状,并通过周期性改变其属性来制作动画。

可以尝试的扩展:

  • 颜色变化: 让爱心的颜色随心跳一起脉动。
  • 更复杂的心跳: 模拟更真实的心跳曲线(例如,快速收缩,然后稍慢舒张)。
  • 背景效果: 添加动态背景或粒子效果。
  • 用户交互: 允许用户通过鼠标点击改变心跳速率或颜色。
  • 多个爱心: 绘制多个以不同节奏或相位跳动的爱心。

希望你喜欢这个教程!


http://www.hkcw.cn/article/AEkoQvOOms.shtml

相关文章

入门AJAX——XMLHttpRequest(Get)

一、什么是 AJAX AJAX Asynchronous JavaScript And XML&#xff08;异步的 JavaScript 和 XML&#xff09;。 1、XML与异步JS XML: 是一种比较老的前后端数据传输格式&#xff08;已经几乎被 JSON 代替&#xff09;。它的格式与HTML类似&#xff0c;通过严格的闭合自定义标…

MDP的observations部分

文章目录 1.isaaclab的observations1.1 根状态相关观测base_pos_zbase_lin_vel &#xff08;use&#xff09;base_ang_vel &#xff08;use&#xff09;projected_gravity (use)root_pos_wroot_quat_wroot_lin_vel_wroot_ang_vel_w 1.2 关节状态相关观测joint_posjoint_pos_rel…

Rhino插件大全下载指南:解锁犀牛潜能,提升设计效率

Rhinoceros&#xff08;简称Rhino&#xff0c;犀牛&#xff09;以其强大的NURBS曲面建模能力、灵活的脚本环境以及与Grasshopper参数化设计工具的无缝集成&#xff0c;在全球工业设计、建筑设计、珠宝设计、船舶设计等领域备受推崇。为了进一步拓展Rhino的功能&#xff0c;满足…

百万级临床试验数据库TrialPanorama发布!AI助力新药研发与临床评价迎来新基石

2025年5月22日&#xff0c;伊利诺伊大学厄巴纳-香槟分校的研究团队在《arXiv》上发表了一篇前瞻性研究论文《TrialPanorama: Database and Benchmark for Systematic Review and Design of Clinical Trials》&#xff0c;该研究建立了一个临床试验数据库TrialPanorama&#xff…

运维 vm 虚拟机ip设置

虚拟网络设置 nat 模式 网卡 主机设置网卡地址 虚拟机绑定网卡

问题七、isaacsim中添加IMU传感器

0 前言 NVIDIA Isaac Sim 中的 IMU 传感器可跟踪车身运动并输出模拟加速度计和陀螺仪读数。与真实 IMU 传感器一样,模拟 IMU 可通过平台单元提供局部 x、y、z 轴的加速度和角速度测量值。 1 创建IMU传感器 按照下述步骤依次点击 使用python创建 基于IsaacSensorCreateImuSe…

AutoGenTestCase - 借助AI大模型生成测试用例

想象一下&#xff0c;你正在为一个复杂的支付系统编写测试用例&#xff0c;需求文档堆积如山&#xff0c;边缘场景层出不穷&#xff0c;手动编写让你焦头烂额。现在&#xff0c;有了AutoGenTestCase&#xff0c;这个AI驱动的“测试用例生成机”可以从需求文档中自动生成数百个测…

警惕假冒 CAPTCHA 攻击通过多阶段payload链部署远控和盗窃信息

在过去几个月中&#xff0c;Trend Micro 托管检测与响应&#xff08;MDR&#xff09;调查中发现假冒 CAPTCHA 的案例激增。这些 CAPTCHA 通过钓鱼邮件、URL 重定向、恶意广告或 SEO 污染投放。所有观察到的案例均表现出类似行为&#xff1a;指导用户将恶意命令复制粘贴到 Windo…

2024-2025-2-《移动机器人设计与实践》-复习资料-8……

2024-2025-2-《移动机器人设计与实践》-复习资料-1-7-CSDN博客 08 移动机器人基础编程 单选题&#xff08;6题&#xff09; 在ROS中&#xff0c;用于移动机器人速度控制的消息类型通常是&#xff1f; A. std_msgs/StringB. geometry_msgs/TwistC. sensor_msgs/ImageD. nav_ms…

楼宇自控系统赋能设备管理智能化集中化,驱动建筑节能高效运行

在建筑行业追求可持续发展与高效运营的当下&#xff0c;传统建筑设备管理模式因信息分散、调控粗放等问题&#xff0c;导致能源浪费严重、设备维护成本高企。而楼宇自控系统凭借对建筑内各类设备的智能化、集中化管理能力&#xff0c;成为解决这些难题的关键。通过实时监测、智…

三、kafka消费的全流程

五、多线程安全问题 1、多线程安全的定义 使用多线程访问一个资源&#xff0c;这个资源始终都能表现出正确的行为。 不被运行的环境影响、多线程可以交替访问、不需要任何额外的同步和协同。 2、Java实现多线程安全生产者 这里只是模拟多线程环境下使用生产者发送消息&…

集合类基础概念

目录 集合类概述 集合框架的体系结构 单列集合&#xff08;Collection&#xff09; List接口 Set接口 双列集合&#xff08;Map&#xff09; Map接口 线程安全与性能考虑 集合与数组的区别 大小是否固定 数据类型与存储 操作方法丰富性 内存与性能 类型安全与泛型…

1500多个免费的HTML模板

1500多个免费的HTML模板 用于网站&#xff0c;着陆页&#xff0c;博客&#xff0c;投资组合&#xff0c;电子商务和管理仪表板 Free HTML Website Templates on HTMLrev https://htmlrev.com/

博客操作规范

一、博客内容规范 专有名词&#xff1a;深蓝粗体&#xff0c;一级专有名词。 专有名词&#xff1a;靛蓝粗体&#xff0c;二级专有名词。 一般名词&#xff1a;浅蓝粗体&#xff0c;一般名词。 标记名词&#xff0c;蓝色粗体&#xff0c;标记性的名词。 重点句子&#xff1…

秋招Day12 - 计算机网络 - IP

IP协议的定义和作用&#xff1f; IP协议用于在计算机网络中传递数据包&#xff0c;定义了数据包的格式和处理规则&#xff0c;确保数据能够从一个设备传递到另一个设备&#xff0c;中间可能经过多个不同的设备&#xff08;路由器&#xff09;。 IP协议有哪些作用&#xff1f;…

电阻电容的选型

一、电阻选型 1.1安装方式 贴片电阻体积小&#xff0c;适用于SMT生产&#xff1b;功率小&#xff1b;易拆解插件电阻体积大&#xff1b;功率大&#xff1b;不易脱落 1.2阻值 电阻的阻值是离散的&#xff0c;其标称阻值根据精度分为E6、E12、E24、E48、E96、E192六大系列&am…

【网络安全】SRC漏洞挖掘思路/手法分享

文章目录 Tip1Tip2Tip3Tip4Tip5Tip6Tip7Tip8Tip9Tip10Tip11Tip12Tip13Tip14Tip15Tip16Tip17Tip18Tip19Tip20Tip21Tip22Tip23Tip24Tip25Tip26Tip27Tip28Tip29Tip30Tip1 “复制该主机所有 URL”:包含该主机上的所有接口等资源。 “复制此主机里的链接”:包括该主机加载的第三…

论文中pdf图片文件太大怎么办

文章目录 1.使用pdf文件的打印功能将文件导出2.操作3.前后文件大小对比 1.使用pdf文件的打印功能将文件导出 该方法在保证清晰度的同时&#xff0c;内存空间也能实现减少&#xff08;如果使用线上的压缩pdf工具&#xff0c;清晰度会直线下降&#xff09; 2.操作 点击文件—&…

力扣刷题 -- 232. 用栈实现队列

1. 题目 2. 思路分析 1&#xff09;创建两个栈空间&#xff0c;PushST&#xff0c;PopST&#xff1b; 2&#xff09;插入数据往PushST插&#xff0c;判断PopST是否为空&#xff0c;如果为空直接往PopST出数据&#xff1b;如PopST不为空&#xff0c;就先把PopST的数据先出栈&a…

结构型设计模式之Decorator(装饰器)

结构型设计模式之Decorator&#xff08;装饰器&#xff09; 前言&#xff1a; 本案例通过李四举例&#xff0c;不改变源代码的情况下 对“才艺”进行增强。 摘要&#xff1a; 摘要&#xff1a; 装饰器模式是一种结构型设计模式&#xff0c;允许动态地为对象添加功能而不改变其…