day4 - 使用图像绘制动态时钟

本期的主要内容是利用OpenCV中包含的绘图函数,例如绘制线段、绘制矩形、绘制圆形等来绘制一个动态时钟的表盘。

完成本期内容,你可以:

  • 掌握OpenCV常见的绘图函数

  • 学会使用绘图函数绘制简单的图像

若要运行案例代码,你需要有:

  • 操作系统:Ubuntu 16 以上 或者 Windows10

  • 工具软件:VScode 或者其他源码编辑器

  • 硬件环境:无特殊要求

  • 核心库:python 3.6.13, opencv-contrib-python 3.4.11.39,opencv-python 3.4.2.16

点击下载源码


绘制线段

OpenCV 中提供的绘制线段的函数是 cv2.line()。

函数原型: img = cv2.line(img, plt1, plt2, color, thickness);

img为绘制线段后得到的图像。

参数描述如下:

参数描述
plt1线段的起点坐标
plt2线段的终点坐标
color绘制线段的线条颜色
thickness绘制线段时的线条宽度

绘制矩形

OpenCV 中提供的绘制矩形的函数是 cv2.rectangle()。

函数原型:img = cv2.rectangle(img, plt1, plt2, color, thickness);

img为绘制矩形后得到的图像。

参数描述如下:

参数描述
plt1矩形的左上角坐标
plt2矩形的右下角坐标
color绘制矩阵时的线条颜色
thickness绘制矩阵时的线条宽度,当thickness的值为-1时,可以绘制一个实心矩形

绘制圆形

OpenCV中提供的绘制圆形的函数是cv2.circle()。

函数原型:img = cv2.circle(img, center, radius, color, thickness);

img为绘制圆形后得到的图像。

参数描述如下:

参数描述
center圆形的圆心坐标
radius圆形的半径
color绘制圆形时的线条颜色
thickness绘制圆形时的线条宽度,当thickness的值为-1时,可以绘制一个实心圆形

绘制多边形

OpenCV中提供的绘制多边形的函数是cv2.polylines()。

函数原型:img = cv2.polylines(img, pts, isClosed, color, thickness);

img为绘制多边形后得到的图像。

参数描述如下:

参数描述
pts由多边形各个顶点坐标组成的一个列表。
isClosed指示绘制的折线是否闭合的标志。
color绘制多边形时的线条颜色
thickness绘制多边形时的线条宽度

绘制文字

OpenCV 中提供的绘制文字的函数是cv2.putText()。

函数原型:img = cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType,bottomLeftOrigin)

img为绘制文字后得到的图像。

参数描述如下:

参数描述
text要绘制的文本内容
org文字在画布中的左下角坐标
fontFace字体样式
fontScale字体大小
color绘制文字时的线条颜色
thickness绘制文字时的线条宽度
lineType线型
bottomLeftOrigin绘制文字的方向

可选的字体样式,如下:

字体样式含义
cv.FONT_HERSHEY_SIMPLEX正常大小sans-serif字体
cv.FONT_HERSHEY_PLAIN小尺寸sans-serif字体
cv.FONT_HERSHEY_DUPLEX正常大小的sans-serif字体(比FONT_HERSHEY_SIMPLEX更复杂)
cv.FONT_HERSHEY_COMPLEX正常大小serif字体
cv.FONT_HERSHEY_TRIPLEX正常大小serif字体(比FONT_HERSHEY_COMPLEX更复杂)
cv.FONT_HERSHEY_COMPLEX_SMALLFONT_HERSHEY_COMPLEX的简化版本
cv.FONT_HERSHEY_SCRIPT_SIMPLEX手写样式字体
cv.FONT_HERSHEY_SCRIPT_COMPLEX更复杂的FONT_HERSHEY_SCRIPT_SIMPLEX变体
cv.FONT_ITALIC斜体字体

4.5 表盘刻度线

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NkNNqUPt-1684908365737)(image\md_img.png)]

通过观察可以发现,时钟主要由两部分组成,分别是静态表盘和动态表针。整个表盘其实只有3根表针在动,所以可以先画出静态表盘,然后获取系统当前时间,根据时间实时动态绘制3根表针就解决了。

4.5.1 绘制刻度

表盘上只有60条分/秒刻线和12条小时刻线,当然还有表盘的外部轮廓圆,也就是重点在如何画72根线。

前面我们使用OpenCV画直线的时候,需知道直线的起点和终点坐标,那么画72根线就变成了获取72组坐标。

在平面坐标系下,已知半径和角度的话,A点的坐标可以表示为:


$\ x = r \times cos\alpha $ $\ y = r \times sin\alpha $

请添加图片描述

先只考虑将坐标系原点移动到左上角,角度依然是平面坐标系中的逆时针计算,那么新坐标是:

x = r + r ×   c o s α x= r+ r\times\ cos\alpha x=r+r× cosα y = r + r ×   s i n α y =r+r\times\ sin\alpha y=r+r× sinα

对于60条分/秒刻线,刻线间的夹角是360°/60=6°,对于小时刻线,角度是360°/12=30°,这样就得到了72组起点坐标,那怎么得到终点坐标呢?其实同样的原理,用一个同心的小圆来计算得到终点:

  • 角度换算

    接下来算是一个小难点,首先时钟的起始坐标在正常二维坐标系的90°方向,其次时钟跟图像一样,都是顺时针计算角度的,所以三者需要统一下:

请添加图片描述

因对于时钟坐标和图像坐标,时钟0的0°对应图像的270°,时钟15的90°对应图像的360°,时钟30的180°对应图像的450°(270°+180°)…

所以两者之间的关系便是:

计算角度 = 时钟角度+270°
计算角度 = 计算角度 if 计算角度<=360° else 计算角度-360°

具体步骤

1. 创建项目结构

创建项目名为使用OpenCV绘制动态时钟,项目根目录下新建code文件夹储存代码,新建dataset文件夹储存数据,项目结构如下:

使用OpenCV绘制动态时钟                    # 项目名称
├── code                               # 储存代码文件
├── dataset                            # 储存数据文件

注:如项目结构已存在,无需再创建。

2. 创建画布并绘制表盘

  1. code文件夹下创建clock.py文件;
  2. 导入需要用到的模块datetime, opencv, math, numpy
  3. 设置表盘距上下左右的边距,圆心和半径;
  4. 创建一个450*450的画布,并填充为白色;
  5. 画出表盘,线条颜色为黑色,线条宽度为5;
  6. 展示表盘。

代码实现

# 导入所需模块
import cv2
import math
import datetime
import numpy as np
import matplotlib.pyplot as plt

# 设置边距、半径、圆心
margin = 5  # 上下左右边距
radius = 220  # 圆的半径
center = (center_x, center_y) = (225, 225)  # 圆心

# 新建一个画板并填充成白色
img = np.zeros((450, 450, 3), np.uint8)
img[:] = (255, 255, 255)

# 画出圆盘
cv2.circle(img, center, radius, (0, 0, 0), thickness=5)
cv2.imshow('clocking',img)

请添加图片描述

实验效果

3. 绘制表盘分钟刻度线

  1. 计算每一条秒和分刻度线的起点和终点的坐标;
  2. 画出60条秒和分的刻度线,颜色为黑色,线条宽度为2;
  3. 展示表盘。

代码实现

# 绘制表盘刻度线
pt1 = []
# 3. 画出60条秒和分钟的刻线
for i in range(60):
    # 最外部圆,计算A点
    x1 = center_x+(radius-margin)*math.cos(i*6*np.pi/180.0)
    y1 = center_y+(radius-margin)*math.sin(i*6*np.pi/180.0)
    pt1.append((int(x1), int(y1)))

    # 同心小圆,计算B点
    x2 = center_x+(radius-15)*math.cos(i*6*np.pi/180.0)
    y2 = center_y+(radius-15)*math.sin(i*6*np.pi/180.0)

    cv2.line(img, pt1[i], (int(x2), int(y2)), (0, 0, 0), thickness=2)
cv2.imshow('clocking',img)

请添加图片描述

实验效果

4. 绘制表盘小时刻度线

  1. 计算每一条小时刻度线的起点和终点的坐标;
  2. 画出12条小时刻度线,颜色为黑色,线条宽度为5;
  3. 展示表盘。

代码实现

#  画出12条小时的刻线
for i in range(12):
    # 12条小时刻线应该更长一点
    x = center_x+(radius-25)*math.cos(i*30*np.pi/180.0)
    y = center_y+(radius-25)*math.sin(i*30*np.pi/180.0)
    # 这里用到了前面的pt1
    cv2.line(img, pt1[i*5], (int(x), int(y)), (0, 0, 0), thickness=5)
cv2.imshow('clocking',img)

请添加图片描述

实验效果

5. 同步时间并绘制指针

  1. 拷贝表盘;
  2. 获取系统时间,拆分时-分-秒;
  3. 根据系统时间,绘制秒刻度线;
  4. 根据系统时间,绘制分刻度线;
  5. 根据系统时间,绘制小时刻度线;
  6. 添加当前日期文字;
  7. 控制按键输入,当输入‘esc’按键时退出,并销毁窗口

代码实现

#同步时间
while True:
    # 不断拷贝表盘图,才能更新绘制,不然会重叠在一起
    temp = np.copy(img)

    # 获取系统时间,画出动态的时-分-秒三条刻线
    now_time = datetime.datetime.now()
    hour, minute, second = now_time.hour, now_time.minute, now_time.second

    # 画秒刻线
    # OpenCV中的角度是顺时针计算的,所以需要转换下
    sec_angle = second*6+270 if second <= 15 else (second-15)*6
    sec_x = center_x+(radius-margin)*math.cos(sec_angle*np.pi/180.0)
    sec_y = center_y+(radius-margin)*math.sin(sec_angle*np.pi/180.0)
    cv2.line(temp, center, (int(sec_x), int(sec_y)), (203, 222, 166), 2)

    # 画分刻线
    min_angle = minute*6+270 if minute <= 15 else (minute-15)*6
    min_x = center_x+(radius-35)*math.cos(min_angle*np.pi/180.0)
    min_y = center_y+(radius-35)*math.sin(min_angle*np.pi/180.0)
    cv2.line(temp, center, (int(min_x), int(min_y)), (186, 199, 137), 8)

    # 画时刻线
    hour_angle = hour*30+270 if hour <= 3 else (hour-3)*30
    hour_x = center_x+(radius-65)*math.cos(hour_angle*np.pi/180.0)
    hour_y = center_y+(radius-65)*math.sin(hour_angle*np.pi/180.0)
    cv2.line(temp, center, (int(hour_x), int(hour_y)), (169, 198, 26), 15)

    # 添加当前日期文字
    font = cv2.FONT_HERSHEY_SIMPLEX
    time_str = now_time.strftime("%d/%m/%Y")
    cv2.putText(img, time_str, (135, 275), font, 1, (0, 0, 0), 2)

    cv2.imshow('clocking', temp)
    if cv2.waitKey(300) == 27:  # 按下ESC键退出
        break
cv2.destroyAllWindows()

请添加图片描述

实验效果

OpenCV中包含的绘图函数有很多,我们可以通过这些简单图形的组合,在结合一些之前学习的知识可以画出很多好玩的图像。

点击下载源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值