OpenCV C++案例实战三十一《动态时钟》

news2024/12/27 16:20:21

OpenCV C++案例实战三十一《动态时钟》

  • 前言
  • 一、绘制表盘
  • 二、绘制刻线
  • 三、获取系统时间
  • 四、结果展示
  • 五、源码
  • 总结


前言

本案例将使用OpenCV C++实现动态时钟效果。原理也很简单,主要分为绘制表盘、以及获取系统时间两步。

一、绘制表盘

首先为了效果显示美观一点,选取一张背景图。
在这里插入图片描述
接着需要绘制一个圆形表盘,直接上代码、效果演示。注释都在源码上有标注。
在这里插入图片描述

	Point center(background.cols / 2, background.rows / 2);//圆心
	int radius = min(background.cols / 2, background.rows / 2) - 20; //时钟半径

	Mat mask = Mat::zeros(background.size(), CV_8UC3);
	circle(mask, center, radius, Scalar::all(255), -1);//掩模
	Mat canvas = Mat(background.size(), CV_8UC3, Scalar(175, 165, 0));//创建画布
	background.copyTo(canvas, mask);//将背景拷贝到画布中,形成表盘背景

	circle(canvas, center, radius, Scalar::all(0), 3);//表盘

二、绘制刻线

接下来,需要在表盘上绘制时针、分针刻线。其中原理就是计算点的旋转坐标。可以参考一下我的这篇博文OpenCV C++案例实战二十七《角度测量》

	int margin = 5;//若margin为0,则点在表盘上

	//画分针刻线
	int minute_len = 10; //刻线长度
	for (int i = 0; i < 60; i++)
	{
		//圆上坐标点计算公式,对于分针刻线,360/60=6,即每隔6°一刻线
		int x1 = center.x + (radius - margin) * cos(i*6.0*CV_PI / 180.0);
		int y1 = center.y + (radius - margin) * sin(i*6.0*CV_PI / 180.0);
		int x2 = center.x + (radius - minute_len) * cos(i*6.0*CV_PI / 180.0);
		int y2 = center.y + (radius - minute_len) * sin(i*6.0*CV_PI / 180.0);
		line(canvas, Point(x1, y1), Point(x2, y2), Scalar::all(0), 2, LINE_AA);
	}

	//画时针刻线
	int hour_len = 20;
	for (int i = 0; i < 12; i++)
	{
		//对于时针刻线,每隔360/12=30,即每隔30°一刻线
		int x1 = center.x + (radius - margin) * cos(i*30.0*CV_PI / 180.0);
		int y1 = center.y + (radius - margin) * sin(i*30.0*CV_PI / 180.0);
		int x2 = center.x + (radius - hour_len)*cos(i*30.0*CV_PI / 180.0);
		int y2 = center.y + (radius - hour_len)*sin(i*30.0*CV_PI / 180.0);
		line(canvas, Point(x1, y1), Point(x2, y2), Scalar::all(0), 3, LINE_AA);

		//在表盘上显示3、6、9、12时,坐标位置自行根据图像大小设定
		if (i == 0)
		{
			putText(canvas, to_string(i + 3), Point(x2 - 30, y2 + 10), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
		else if (i == 3)
		{
			putText(canvas, to_string(i + 3), Point(x2 - 10, y2 - 10), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
		else if (i == 6)
		{
			putText(canvas, to_string(i + 3), Point(x2 + 10, y2 + 10), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
		else if (i == 9)
		{
			putText(canvas, to_string(i + 3), Point(x2 - 20, y2 + 30), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
	}

在这里插入图片描述
效果如上图所示,至此前期的预处理工作已经完成了。接下来,需要获取系统时间,然后绘制到表盘上。

三、获取系统时间

当我们获取到相应的系统时间后,有一点需要注意的是,opencv是以3点钟方向为起点,即0°方向,且为顺时针旋转,故秒针、分针、时针在0~3点钟区间需要换算。具体换算请看源码注释。

	//使用while循环,不断更新时间
	while (true)
	{	

		char key = waitKey(1000);
		if (key == 27)break;

		Mat clockImg = canvas.clone();//将表盘复制一份,用于不断更新时钟刻线

		//获取系统时间
		SYSTEMTIME Time;
		GetLocalTime(&Time);
		int second = Time.wSecond; //秒
		int minute = Time.wMinute; //分
		int hour = Time.wHour; //时
		int day = Time.wDay; //日
		int month = Time.wMonth; //月
		int year = Time.wYear; //年
		printf("%4d/%02d/%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);


		//由于opencv是以3点钟方向为起点,且为顺时针旋转,故秒针、分针、时针在0~3点钟区间需要换算
		
		//秒针
		int sec_angle = 0;
		if (second <= 15)
		{
			//当秒针处于0~15秒时,对应角度应处于270~360°,每隔6°走一刻线
			sec_angle = second * 6 + 270;
		}
		else
		{
			sec_angle = (second - 15) * 6;
		}
		int sec_x = center.x + (radius - margin * 12) *cos(sec_angle*CV_PI / 180);
		int sec_y = center.y + (radius - margin * 12) *sin(sec_angle*CV_PI / 180);
		line(clockImg, center, Point(sec_x, sec_y), Scalar(0, 255, 0), 2, LINE_AA);


		//分针
		int min_angle = 0;
		if (minute <= 15)
		{
			//当分针处于0~15分时,对应角度应处于270~360°,每隔6°走一刻线
			min_angle = minute * 6 + 270;
		}
		else
		{
			min_angle = (minute - 15) * 6;
		}
		int min_x = center.x + (radius - margin*18)*cos(min_angle*CV_PI / 180);
		int min_y = center.y + (radius - margin*18)*sin(min_angle*CV_PI / 180);
		line(clockImg, center, Point(min_x, min_y), Scalar(0, 255, 255), 4, LINE_AA);


		//时针
		int hour_angle = 0;
		if (hour <= 3)
		{
			//当时针处于0~3时,对应角度应处于270~360°,每隔30°走一刻线
			hour_angle = hour * 30 + 270;
		}
		else
		{
			hour_angle = (hour - 3) * 30;
		}
		int hour_x = center.x + (radius - margin * 24)*cos(hour_angle*CV_PI / 180);
		int hour_y = center.y + (radius - margin * 24)*sin(hour_angle*CV_PI / 180);
		line(clockImg, center, Point(hour_x, hour_y), Scalar(255, 255, 0), 6, LINE_AA);
		
		circle(clockImg, center, 5, Scalar::all(0), -1);

		//将时间显示在表盘上
		char text1[100], text2[100];
		sprintf_s(text1, "%04d%s%02d%s%02d", year, "/", month, "/", day);
		sprintf_s(text2, "%02d%s%02d%s%02d", hour, ":", minute, ":", second);
		putText(clockImg, text1, Point(center.x-100, center.y+200), FONT_HERSHEY_SIMPLEX, 1, Scalar(21, 23, 161), 3);
		putText(clockImg, text2, Point(center.x-70, center.y+250), FONT_HERSHEY_SIMPLEX, 1, Scalar(21, 23, 161), 2);
		imshow("dynamic clock", clockImg);
		imwrite("dynamic clock.jpg", clockImg);
	}

四、结果展示

在这里插入图片描述

五、源码

#include<iostream>
#include<opencv2/opencv.hpp>
#include<Windows.h>
using namespace std;
using namespace cv;

int main()
{
	Mat background = imread("background.jpg");
	if (background.empty())
	{
		cout << "can not read the image..." << endl;
		system("pause");
		return -1;
	}

	Point center(background.cols / 2, background.rows / 2);//圆心
	int radius = min(background.cols / 2, background.rows / 2) - 20; //时钟半径

	Mat mask = Mat::zeros(background.size(), CV_8UC3);
	circle(mask, center, radius, Scalar::all(255), -1);//掩模
	Mat canvas = Mat(background.size(), CV_8UC3, Scalar(175, 165, 0));//创建画布
	background.copyTo(canvas, mask);//将背景拷贝到画布中,形成表盘背景

	circle(canvas, center, radius, Scalar::all(0), 3);//表盘

	int margin = 5;//若margin为0,则点在表盘上

	//画分针刻线
	int minute_len = 10; //刻线长度
	for (int i = 0; i < 60; i++)
	{
		//圆上坐标点计算公式,对于分针刻线,360/60=6,即每隔6°一刻线
		int x1 = center.x + (radius - margin) * cos(i*6.0*CV_PI / 180.0);
		int y1 = center.y + (radius - margin) * sin(i*6.0*CV_PI / 180.0);
		int x2 = center.x + (radius - minute_len) * cos(i*6.0*CV_PI / 180.0);
		int y2 = center.y + (radius - minute_len) * sin(i*6.0*CV_PI / 180.0);
		line(canvas, Point(x1, y1), Point(x2, y2), Scalar::all(0), 2, LINE_AA);
	}

	//画时针刻线
	int hour_len = 20;
	for (int i = 0; i < 12; i++)
	{
		//对于时针刻线,每隔360/12=30,即每隔30°一刻线
		int x1 = center.x + (radius - margin) * cos(i*30.0*CV_PI / 180.0);
		int y1 = center.y + (radius - margin) * sin(i*30.0*CV_PI / 180.0);
		int x2 = center.x + (radius - hour_len)*cos(i*30.0*CV_PI / 180.0);
		int y2 = center.y + (radius - hour_len)*sin(i*30.0*CV_PI / 180.0);
		line(canvas, Point(x1, y1), Point(x2, y2), Scalar::all(0), 3, LINE_AA);

		//在表盘上显示3、6、9、12时,坐标位置自行根据图像大小设定
		if (i == 0)
		{
			putText(canvas, to_string(i + 3), Point(x2 - 30, y2 + 10), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
		else if (i == 3)
		{
			putText(canvas, to_string(i + 3), Point(x2 - 10, y2 - 10), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
		else if (i == 6)
		{
			putText(canvas, to_string(i + 3), Point(x2 + 10, y2 + 10), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
		else if (i == 9)
		{
			putText(canvas, to_string(i + 3), Point(x2 - 20, y2 + 30), FONT_HERSHEY_SIMPLEX, 1, Scalar::all(0), 3);
		}
	}

	//使用while循环,不断更新时间
	while (true)
	{	

		char key = waitKey(1000);
		if (key == 27)break;

		Mat clockImg = canvas.clone();//将表盘复制一份,用于不断更新时钟刻线

		//获取系统时间
		SYSTEMTIME Time;
		GetLocalTime(&Time);
		int second = Time.wSecond; //秒
		int minute = Time.wMinute; //分
		int hour = Time.wHour; //时
		int day = Time.wDay; //日
		int month = Time.wMonth; //月
		int year = Time.wYear; //年
		printf("%4d/%02d/%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);


		//由于opencv是以3点钟方向为起点,且为顺时针旋转,故秒针、分针、时针在0~3点钟区间需要换算
		
		//秒针
		int sec_angle = 0;
		if (second <= 15)
		{
			//当秒针处于0~15秒时,对应角度应处于270~360°,每隔6°走一刻线
			sec_angle = second * 6 + 270;
		}
		else
		{
			sec_angle = (second - 15) * 6;
		}
		int sec_x = center.x + (radius - margin * 12) *cos(sec_angle*CV_PI / 180);
		int sec_y = center.y + (radius - margin * 12) *sin(sec_angle*CV_PI / 180);
		line(clockImg, center, Point(sec_x, sec_y), Scalar(0, 255, 0), 2, LINE_AA);


		//分针
		int min_angle = 0;
		if (minute <= 15)
		{
			//当分针处于0~15分时,对应角度应处于270~360°,每隔6°走一刻线
			min_angle = minute * 6 + 270;
		}
		else
		{
			min_angle = (minute - 15) * 6;
		}
		int min_x = center.x + (radius - margin*18)*cos(min_angle*CV_PI / 180);
		int min_y = center.y + (radius - margin*18)*sin(min_angle*CV_PI / 180);
		line(clockImg, center, Point(min_x, min_y), Scalar(0, 255, 255), 4, LINE_AA);


		//时针
		int hour_angle = 0;
		if (hour <= 3)
		{
			//当时针处于0~3时,对应角度应处于270~360°,每隔30°走一刻线
			hour_angle = hour * 30 + 270;
		}
		else
		{
			hour_angle = (hour - 3) * 30;
		}
		int hour_x = center.x + (radius - margin * 24)*cos(hour_angle*CV_PI / 180);
		int hour_y = center.y + (radius - margin * 24)*sin(hour_angle*CV_PI / 180);
		line(clockImg, center, Point(hour_x, hour_y), Scalar(255, 255, 0), 6, LINE_AA);
		
		circle(clockImg, center, 5, Scalar::all(0), -1);

		//将时间显示在表盘上
		char text1[100], text2[100];
		sprintf_s(text1, "%04d%s%02d%s%02d", year, "/", month, "/", day);
		sprintf_s(text2, "%02d%s%02d%s%02d", hour, ":", minute, ":", second);
		putText(clockImg, text1, Point(center.x-100, center.y+200), FONT_HERSHEY_SIMPLEX, 1, Scalar(21, 23, 161), 3);
		putText(clockImg, text2, Point(center.x-70, center.y+250), FONT_HERSHEY_SIMPLEX, 1, Scalar(21, 23, 161), 2);
		imshow("dynamic clock", clockImg);
		imwrite("dynamic clock.jpg", clockImg);
	}

	destroyAllWindows();
	system("pause");
	return 0;
}

总结

本文使用OpenCV C++ 进行动态时钟绘制,主要操作有以下几点。
1、图像预处理,绘制表盘
2、绘制表盘刻线
3、获取系统时间,注意角度与时间之间的转换

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

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

相关文章

数据驱动测试、结果报告生成,Python接口自动化测试全方位解析

B站首推&#xff01;2023最详细自动化测试合集&#xff0c;小白皆可掌握&#xff0c;让测试变得简单、快捷、可靠https://www.bilibili.com/video/BV1ua4y1V7Db 目录 一、背景 二、准备工作 三、编写测试脚本 四、数据驱动测试 五、结果报告生成 六、总结 七、参考链接 …

线性表,顺序表,链表

线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线 …

阿里云对象存储OSS使用 HTTPS访问

阿里云对象存储OSS使用 HTTPS ​ 在部署项目的时候遇到了一个问题&#xff0c;就是https页面访问http资源报错的问题。 问题&#xff1a; 写了一个前端项目在云服务器部署&#xff0c;我的域名申请了ssl证书并在云服务器nginx部署&#xff0c;所以页面是https页面&#xff0c;但…

Ansible的脚本-playbook 剧本

目录 1.剧本&#xff08;playbook&#xff09; 1.playbook介绍 2. playbooks 的组成 3.案例&#xff1a;编写httpd的playbook 4.定义、引用变量 5.指定远程主机sudo切换用户 6.when条件判断 7.迭代 2.playbook的模块 1.Templates 模块 2.tags 模块 3.Roles 模块 1.…

TCP协议——这篇文章GET全

TCP协议文章目录 1. UDP和TCP协议的比较1.1 UDP协议1.2 TCP协议1.3 特点比较 2. TCP协议建立连接的三次握手3. TCP协议断开连接的四次挥手4. TCP协议的几个特性4.1 确认应答4.2 超时重传4.3 连接管理4.4 滑动窗口4.5 流量控制4.6 拥塞控制 1. UDP和TCP协议的比较 UDP和TCP作为…

多维时序 | MATLAB实现BP神经网络多变量时间序列预测(考虑历史特征的影响,多指标、多图输出)

多维时序 | MATLAB实现BP神经网络多变量时间序列预测(考虑历史特征的影响,多指标、多图输出) 目录 多维时序 | MATLAB实现BP神经网络多变量时间序列预测(考虑历史特征的影响,多指标、多图输出)预测效果基本介绍程序设计学习总结参考资料预测效果 基本介绍 MATLAB实现BP神经网…

Java中的Reflection(反射)、暴力反射

文章目录 1. 反射(Reflection)的概念1.1 反射的出现背景1.2 反射概述1.3 Java反射机制研究及应用1.4 反射相关的主要API1.5 反射的优缺点 2. Class类并获取Class实例2.1 理解Class2.1.1 理论上2.1.2 内存结构上 2.2 获取Class类的实例(四种方法)2.3 哪些类型可以有Class对象2.4…

Windows安装rabbitmq

Windows安装rabbitmq 一、下载1、下载erlang2、下载rabbitmq 二、安装1、安装erlang2、安装rabbitmq3、简单使用 一、下载 1、下载erlang 点击右侧下载地址&#xff0c;跳转下载&#xff0c;点击下载 跳转后&#xff0c;点击download windows install即可下载。 2、下载rab…

微服务---分布式缓存redis进阶-redis集群部署

分布式缓存 – 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题&#xff1a; 0.学习目标 1.Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xf…

基于Kali搭建SSH弱口令靶机并用Nmap实现排查

前言正文1、为宿主主机配置nmap环境变量2、为宿主主机和靶机配置同一局域网环境3、为靶机配置弱口令4、为靶机配置SSH服务[^2]5、主机用Nmap对靶机进行扫描5、主机用弱口令工具对靶机渗透6、验证弱口令 参考文献 前言 有时候&#xff0c;需要我们搭建SSH弱口令环境&#xff0c…

【数据结构】超详细之单向链表(C语言实现)

文章目录 前言一、单向链表是什么&#xff1f;二、单向链表实现步骤 1.打印链表数据以及实现链表头插2.实现链表尾插3.实现链表头删尾删4.实现链表查找5.实现链表在pos之前/之后插入6.实现链表删除pos位置的值7.实现链表删除pos之后位置的值总结 前言 今天我要介绍单向链表&am…

可变参数列表的使用与原理

序言 我们自己编写的函数通常参数是固定的&#xff0c;这样使得某些功能不能得到我们想要的结果&#xff0c;比如我们想求出2个数的最大值的代码不能用于求处3个数的最大值&#xff0c;因此&#xff0c;C语言定义了可变参数列表来编写参数个数不确定的函数。具有可变参数列表的…

Golang每日一练(leetDay0054)

目录 157. 用 Read4 读取 N 个字符 Read-n-characters-given-read4 &#x1f31f;&#x1f31f; 158. 用 Read4 读取 N 个字符 II Read-n-characters-given-read4-ii-call-multiple-times II &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Gol…

拿捏SQL:以“统计连续登录天数超过3天的用户“为例拿捏同类型SQL需求

文章目录 [TOC](文章目录) 一、介绍案例&#xff1a;以"统计连续登录天数超过3天的用户"为需求。数据准备方案1&#xff1a;常规思路针对对数据user_id分组&#xff0c;根据用户的活动日期排序用登录日期与rn求date_sub&#xff0c;得到的差值日期如果是相等的&#…

Python非线性回归预测模型实验完整版

非线性回归预测模型 实验目的 通过非线性回归预测模型&#xff0c;掌握预测模型的建立和应用方法&#xff0c;了解非线性回归模型的基本原理 实验内容 非线性回归预测模型 实验步骤和过程 (1)第一步&#xff1a;学习非线性回归预测模型相关知识。 非线性回归预测模型是指…

Spring框架中的单例Beans是线程安全的么?

在Spring框架中&#xff0c;单例Beans默认是线程安全的。 当你在Spring框架中声明一个单例Bean并配置为默认的单例作用域时&#xff0c;Spring会确保对该Bean的并发访问是线程安全的。以下是一个简单的代码演示&#xff1a; 假设我们有一个名为 SingletonBean 的单例 Bean 类…

Mysql目录结构

一、目录结构 <1> 主要目录结构 find / -name mysql<2> 数据库文件目录 目录&#xff1a;/var/lib/mysql/ 配置方式&#xff1a;show variables like ‘datadir’; <3> 相关命令目录 目录&#xff1a;/usr/bin&#xff08;mysqla…

VMware安装CentOS7遇到的问题记录

文章目录 1、执行ifconfig后&#xff0c;ip地址不显示解决方法&#xff1a; 2、CentOS ip地址老是变动&#xff0c;配置固定ip解决方法 1、执行ifconfig后&#xff0c;ip地址不显示 问题背景&#xff1a;VMware安装centos7后&#xff0c;启动虚拟机&#xff0c;在终端中执行if…

MySQL示例数据库(MySQL Sample Databases) 之 sakila数据库

文章目录 MySQL示例数据库(MySQL Sample Databases) 之 sakila数据库官方示例数据介绍sakila数据库sakila数据库安装sakila/sakila-schema.sql的脚本内容sakila的结构参考 MySQL示例数据库(MySQL Sample Databases) 之 sakila数据库 官方示例数据介绍 MySQL 官方提供了多个示…

Mysql存储json格式数据需要掌握的

目录 一、前言二、什么是 JSON三、Mysql当中json函数四、JSON值部分更新4.1.使用 Partial Updates 的条件4.2.如何在 binlog 中开启 Partial Updates4.3.关于 Partial Updates 的性能测试 五、如何对 JSON 字段创建索引六、mybatis取json类型的数据七、总结 一、前言 最近做的一…