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

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


前言

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

一、绘制表盘

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

	Point center(background.cols / 2, background.rows / 2);//圆心
	int radius = min(background.cols / 2, background.rows / 2) - 20; //时钟半径
Mat mask <span class="token operator">=</span> <span class="token class-name">Mat</span><span class="token double-colon punctuation">::</span><span class="token function">zeros</span><span class="token punctuation">(</span>background<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> CV_8UC3<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">circle</span><span class="token punctuation">(</span>mask<span class="token punctuation">,</span> center<span class="token punctuation">,</span> radius<span class="token punctuation">,</span> <span class="token class-name">Scalar</span><span class="token double-colon punctuation">::</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token number">255</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//掩模</span>

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(i6.0CV_PI / 180.0);
    int y1 = center.y + (radius - margin) sin(i6.0CV_PI / 180.0);
    int x2 = center.x + (radius - minute_len) cos(i6.0CV_PI / 180.0);
    int y2 = center.y + (radius - minute_len) sin(i6.0CV_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(i30.0CV_PI / 180.0);
    int y1 = center.y + (radius - margin) sin(i30.0CV_PI / 180.0);
    int x2 = center.x + (radius - hour_len)cos(i30.0CV_PI / 180.0);
    int y2 = center.y + (radius - hour_len)sin(i30.0CV_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)
      {
      //当秒针处于015秒时,对应角度应处于270360°,每隔6°走一刻线
      sec_angle = second 6 + 270;
      }
      else
      {
      sec_angle = (second - 15) 6;
      }
      int sec_x = center.x + (radius - margin 12) cos(sec_angleCV_PI / 180);
      int sec_y = center.y + (radius - margin 12) sin(sec_angleCV_PI / 180);
      line(clockImg, center, Point(sec_x, sec_y), Scalar(0, 255, 0), 2, LINE_AA);

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

      //时针
      int hour_angle = 0;
      if (hour <= 3)
      {
      //当时针处于03时,对应角度应处于270360°,每隔30°走一刻线
      hour_angle = hour 30 + 270;
      }
      else
      {
      hour_angle = (hour - 3) 30;
      }
      int hour_x = center.x + (radius - margin 24)cos(hour_angleCV_PI / 180);
      int hour_y = center.y + (radius - margin 24)sin(hour_angleCV_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(i6.0CV_PI / 180.0);
        int y1 = center.y + (radius - margin) sin(i6.0CV_PI / 180.0);
        int x2 = center.x + (radius - minute_len) cos(i6.0CV_PI / 180.0);
        int y2 = center.y + (radius - minute_len) sin(i6.0CV_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(i30.0CV_PI / 180.0);
        int y1 = center.y + (radius - margin) sin(i30.0CV_PI / 180.0);
        int x2 = center.x + (radius - hour_len)cos(i30.0CV_PI / 180.0);
        int y2 = center.y + (radius - hour_len)sin(i30.0CV_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)
        {
        //当秒针处于015秒时,对应角度应处于270360°,每隔6°走一刻线
        sec_angle = second 6 + 270;
        }
        else
        {
        sec_angle = (second - 15) 6;
        }
        int sec_x = center.x + (radius - margin 12) cos(sec_angleCV_PI / 180);
        int sec_y = center.y + (radius - margin 12) sin(sec_angleCV_PI / 180);
        line(clockImg, center, Point(sec_x, sec_y), Scalar(0, 255, 0), 2, LINE_AA);

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

        //时针
        int hour_angle = 0;
        if (hour <= 3)
        {
        //当时针处于03时,对应角度应处于270360°,每隔30°走一刻线
        hour_angle = hour 30 + 270;
        }
        else
        {
        hour_angle = (hour - 3) 30;
        }
        int hour_x = center.x + (radius - margin 24)cos(hour_angleCV_PI / 180);
        int hour_y = center.y + (radius - margin 24)sin(hour_angleCV_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、获取系统时间,注意角度与时间之间的转换

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

          请填写红包祝福语或标题

          红包个数最小为10个

          红包金额最低5元

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

          抵扣说明:

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

          余额充值