Friday, May 3, 2013

Analog Clock Using OpenCV

       Here is a simple code that create an analog clock synchronized with system clock using OpenCV.

      The idea is simple first we create an image of size 640X640 and draw second, minute and hour markings. These marking are drawn using OpenCV line and the co-ordinates are find-out using parametric equation of circle . Before that we need to draw a circle from the centre of the image with a radius r=image->width/2=320;

The parametric equation of a circle can be written as
x = cx + r * cos(a * CV_PI / 180.0)
y = cy + r * sin(a * CV_PI / 180.0)

Where "cx" and "cy" are the center of image, "r" is the radius of circle, "a" is the angle.

       As we know the hour hand of a normal 12-hour analogue clock turns 360° in 12 hours so angle between each marking will be 360/12=30 degree. As I said before our hour markings are just lines and we need to find co-ordinates of each line, so as a first step we will find out the inner co-ordinates of each line from center. For this just consider a circle with fixed radius r (less than image width/2) from center, and find out the perimeter co-donates(x,y) of the circle on each 30 degree, that is for different angle value(0,30,60...360) calculate x and y with fixed r
using the above equation. Then note down the values. Here I stored this value in an array h1[][].  Using the same method we can calculate the outer co-ordinates for hour marking by incrementing r with a constant. And stored this value in an array h2[][].

       While in the case of minute hand  60 markings are there and each markings are seperted with 360/60=6 degree. So using the above equation we can calculate inner and outer co-ordinates for  for minute markings. The only difference is that the angle should be incremanetd by 6 degree. Here I stored this values in s1[][] and s2[][]. After finding these co-ordinates just draw these lines to image in a for loop.

    The next thing we need to do is that update hour, minute and second hand with system time and of course these three are also OpenCV lines  and these lines are start from center and the second co-ordinates will change according to system clock tick. So before doing these we need to back up the image we have drawn because on each clock tick we don't need to draw the hour, minute and second markings.

   As a next step we will create a while loop and access system clock and store to a variable. Then we will convert all these value such as hour, minute and second to angle and display in a window.

     Using the above equation we will find out second co-ordinates for hour minute and second hand then draw these lines to the image. Finally using imshow display the image. And before entering to the while loop again we will clear the current image and clone from backup image. 

Here is the full source code.

#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/imgproc/imgproc.hpp"
#include <stdio.h>
#include <time.h>
#include <sys/timeb.h>
#include <sys/time.h>

using namespace std;
using namespace cv;

int main()
Mat clk(640,640,CV_8UC3); //Mat to store clock image
Mat back_up(640,640,CV_8UC3); //Mat to store backup image

Point cent(320,320);
Point perim(320,0);
int rad =320;
float sec_angle=270;
float min_angle=330;
float hour_angle=210;

//Draw second markings
int s1[60][2]={{320,25},{351,27},{381,31},{411,39},{440,51},{468,65},{493,81},{517,101},{539,123}, \
{559,147},{575,173},{589,200},{601,229},{609,259},{613,289},{615,320},{613,351},{609,381},{601,411}, \
{589,440},{575,468},{559,493},{539,517},{517,539},{493,559},{468,575},{440,589},{411,601},{381,609}, \
{351,613},{320,615},{289,613},{259,609},{229,601},{200,589},{173,575},{147,559},{123,539},{101,517}, \
{81,493},{65,468},{51,440},{39,411},{31,381},{27,351},{25,320},{27,289},{31,259},{39,229},{51,200}, \

int s2[60][2]={{320,5},{353,7},{385,12},{417,20},{448,32},{478,47},{505,65},{531,86},{554,109}, \
{575,135},{593,163},{608,192},{620,223},{628,255},{633,287},{635,320},{633,353},{628,385},{620,417}, \
{608,448},{593,478},{575,505},{554,531},{531,554},{505,575},{478,593},{448,608},{417,620},{385,628}, \
{353,633},{320,635},{287,633},{255,628},{223,620},{192,608},{163,593},{135,575},{109,554},{86,531}, \
{65,505},{47,478},{32,448},{20,417},{12,385},{7,353},{5,320},{7,287},{12,255},{20,223},{32,192}, \

for(int i=0;i<60;i++){
    line(clk,Point(s1[i][0],s1[i][1]),Point(s2[i][0],s2[i][1]), Scalar(0,255,0,0), 1.5,CV_AA,0);

//Draw hour markings
int h1[12][2]={{320,45},{458,82},{558,183},{595,320},{558,458},{458,558},{320,595},{183,558}, \
int h2[12][2]={{320,5},{478,47},{593,163},{635,320},{593,478},{478,593},{320,635},{163,593}, \

for(int i=0;i<12;i++){
    line(clk,Point(h1[i][0],h1[i][1]),Point(h2[i][0],h2[i][1]), Scalar(0,255,0,0), 4,CV_AA,0);

circle(clk,cent,rad-5,Scalar(0,0,255,0),4,CV_AA,0); //Dreaw outercircle of clock
circle(clk,cent,1,Scalar(0,255,0,0),5,CV_AA,0); //Draw inner circle

back_up=clk.clone(); // Clone to backup image

time_t rawtime;
struct tm * timeinfo;
float second;
float minute;
float hour;
float millisec;
struct timeb tmb;

//Access system time and store to local variable
    timeinfo = localtime ( &rawtime );

    second     = timeinfo->tm_sec;
    minute     = timeinfo->tm_min;
    hour       = timeinfo->tm_hour;
    millisec   = tmb.millitm;

    sec_angle=(second*6)+270; //Convert second to angle

   min_angle=minute*6+270; //Conver minute to angle

  if(hour>12)hour = hour-12;
   hour_angle=(hour*30)+(minute*.5)+270; //Conver hour to angle


//Fond out the co-ordinates in the circle perimeter for second and draw the line from center
perim.x =  (int)round(cent.x + (rad-5) * cos(sec_angle * CV_PI / 180.0));
perim.y =  (int)round(cent.y + (rad-5) * sin(sec_angle * CV_PI / 180.0));
line(clk,cent,perim, Scalar(0,255,255,0), 1.5,CV_AA,0);

//Fond out the co-ordinates on the circle perimeter for minute and draw the line from center
perim.x =  (int)round(cent.x + (rad-50) * cos(min_angle * CV_PI / 180.0));
perim.y =  (int)round(cent.y + (rad-50) * sin(min_angle * CV_PI / 180.0));
line(clk,cent,perim, Scalar(0,255,255,0), 4,CV_AA,0);

//Fond out the co-ordinates on the circle perimeter for hour and draw the line from center
perim.x =  (int)round(cent.x + (rad-75) * cos(hour_angle * CV_PI / 180.0));
perim.y =  (int)round(cent.y + (rad-75) * sin(hour_angle * CV_PI / 180.0));
line(clk,cent,perim, Scalar(0,255,255,0), 8,CV_AA,0);

imshow("Clock",clk); //Show result in a window
clk.setTo(0); // set clk image to zero for next drawing
clk=back_up.clone(); // Clone the previously drawned markings from back-up image

char c=waitKey(10); // Wait for few millisecond and go back to loop.

    return 0;

Here is the result of the above code.

OpenCV Analog Clock

Hope these post helpful......