Wednesday 8 July 2015

Code to draw Analog wall clock using OpenCV and C++ code synchronised with system time

This tutorial is about making an Analog wall clock synchronized with the system clock using OpenCV.

The idea behind the Algorithm is very simple:
 Decide the size of the window in which you want to draw the Analog clock.The size of the window here is 640 * 640.
Thus the centre of this window is (320,320) which is also taken as the centre of a circle
 Draw a Circle (The size of the circle should be less than that of the window)
Now we need to have the markings for Hour and Seconds hands  on the circumference of the circle
The coordinates of Hours and Second hand can be obtained by using circles parametric equation:
           i.e

                 x=rCos(θ)  ;  y=rsin(θ)
opencv analog clock


Since co-ordinates of the centre is (Cx,Cy)
Hence the co-ordinates of the point would be
 x1=Cx+rCos(θ)
 y1= Cy + rsin(θ)

Thus after getting the Co-ordinates we need to draw a line for the minute and hour marking

For the marking of an Hour hand:
We take the radius of outer circle as:315
And Inner Circle’s radius as :275

For the marking of a minute hand:
We take the radius of the outer circle as :315
And inner circle as :275

Note: Since 12 hours corresponds to 360 degree rotation of an hour hand
         Thus for an hour minute hand should rotate by 360/12=30 degree
         Thus the angle, the hour hand would vary by is 30 degree

         For every 30 degree rotation we have 5 minutes
         Thus for each minute, the minute hand should rotate by 30/5=6   
         degree
         Thus the angle, the minute hand would vary by is 6 degree

 180 degree = π radians
  Hence θ degrees= (θ*π/180) radians

Next we need to update our hour,minute and second hand with that of the system clock.



Here is the Code below:
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/imgproc/imgproc.hpp"
#include <stdio.h>
#include <ctime>
#include <sys/timeb.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(0,0);
int rad =320;
float sec_angle,min_angle,hour_angle;



//Draw second markings
int s1[60][2];
for (int i=0; i<60; i++)
          for (int j=0; j<2; j++)
                   if(j%2==0)
                   s1[i][j]=320+295*cos(6.0*i*3.14/180) ;
                   else
                   s1[i][j]=320+295*sin(6.0*i*3.14/180);

int s2[60][2];
for (int i=0; i<60; i++)
          for (int j=0; j<2; j++)
                   if(j%2==0)
                   s2[i][j]=320+315*cos(6.0*i*3.14/180) ;
        else
        s2[i][j]=320+315*sin(6.0*i*3.14/180);

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];
for (int i=0; i<12; i++)
          for (int j=0; j<2; j++)
                   if(j%2==0)
                   h1[i][j]=320+275*cos(30.0*i*3.14/180) ;
                   else
                   h1[i][j]=320+275*sin(30.0*i*3.14/180);

int h2[12][2];
for (int i=0; i<12; i++)
          for (int j=0; j<2; j++)
                   if(j%2==0)
                   h2[i][j]=320+315*cos(30.0*i*3.14/180) ;
        else
        h2[i][j]=320+315*sin(30.0*i*3.14/180);
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); //Draw 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;


while(1){
//Access system time and store it to a local variable
    ftime(&tmb);
    rawtime=tmb.time;
    timeinfo = localtime ( &rawtime );

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


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

   minute=minute+second/60;
   min_angle=minute*6+270;                        //Convert minute to angle

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


   if(sec_angle>360)sec_angle=sec_angle-360;
   if(min_angle>360)min_angle=min_angle-360;
   if(hour_angle>360)hour_angle=hour_angle-360;

//Find out the co-ordinates in the circle perimeter for second and draw the line from center
perim.x =  (int)(cent.x + (rad-5) * cos(sec_angle * CV_PI / 180.0));
perim.y =  (int)(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);


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


//Find out the co-ordinates on the circle perimeter for hour and draw the line from center
perim.x =  (int)(cent.x + (rad-75) * cos(hour_angle * CV_PI / 180.0));
perim.y =  (int)(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.
if(c==27)break;
}

    return 0;
}


Output:

Friday 3 July 2015

How to draw Histogram of an Image/ Method of doing Histogram equalisation /Importance of Histogram Equalisation and Its Advantages

This opencv tutorial is about  histogram equalization along with the  significance of a histogram equalized image.

In the last article you might have wondered how to draw the histogram of an image. 
What does histogram equalization actually mean and the underlying algorithms by which it is done.Simply said, a histogram is a bar graph of raw data that creates a picture of the data distribution. 

Or in other words histogram describes the distribution of the pixel element in the image.

Importance of Histogram:

  • It helps us to get a better view of photographs that are over or under-exposed.
E.g. We can obtain the better view of bone structure in X-ray image.

  • Secondly it helps us to adjust the contrast of the image.

Understanding Histograms:
For a Binary image:
Pixel value of 0 indicates dark image.
Pixel value of 1 indicates bright image.

For a Gray scale image:
Pixel value of 0 indicates dark.
Pixel value of 255 indicates bright.

Thus we can say that when there are more dark pixels in the image, they would show up on the left hand side.
And the lighter pixels will show up on the right hand side of the histogram.
Dark Exposure Image:
Dark Exposure Image OpenCV
Dark Exposure Image Histogram:
Dark Exposure Image Histogram OpenCV

Light Exposure Image:
Light Exposure Image OpenCV
Dark Exposure Image Histogram:
Light Exposure Image Histogram OpenCV

Now you all might be thinking what do all these observations conclude to?
·        Well, if most of the pixels are found on the left hand side of your image, it is most likely that your image is under-exposed and will be too dark and lack details in the shadow areas.
·        If the majority of the pixels are found on the right hand side then the photo is most likely to be over-exposed and will be too light with little or no detail in bright parts of the image.


An image with low contrast will generally have a sharp peak on the histogram while an image with more contrast will have a more a rounded peak.

Low Contrast Image:
Low Contrast Image OpenCV
Low Contrast Image Histogram:
Low Contrast Image Histogram OpenCV
_________________________________________________________

High Contrast Image:
High Contrast Image OpenCv
High Contrast Image Histogram:
High Contrast Image Histogram OpenCV



Note: There is nothing called as perfect histogram. The right histogram of one scene may be not right for the other scene.

e.g. A photo of the moon in the night where almost all the pixels are on the left hand side of the histogram though the image was properly exposed.
Concept of histogram Calculation:
E.g.: Consider the following matrix of an image :-
5
2
4
0
5
2
4
2
4
1
2
3
1
4
1
7
1
4
0
1
2
3
2
7
4
7
3
7
2
6
7
5
2
7
4
5
2
3
7
4
3
1
3
1
1
6
7
3
4
4
0
0
0
6
7
2
6
2
2
0
0
3
0
3
2
6
7
2
0
0
0
7

Plot the table of the pixel value and the frequency of its occurrence:
Pixel Value
(Grey Levels)
No. Of Pixels
(nk)
0
11
1
8
2
14
3
9
4
10
5
4
6
5
7
11

Calculate

  1. The probability of occurrence of each pixel (PrRk).
  2.  Its cumulative density function (PrSk).
  3. Cumulative density function multiplied by the Gray levels minus 1.
  4. Finally rounding off the calculated value to get the new grey levels.
Grey Levels
nk
PrRk
(nk/Total)
PrSk
PrSk * (L-1)
Final

0
11
0.15278
0.15278
1.06946
1
1
8
0.11111
0.26389
1.84723
2
2
14
0.19444
0.45833
3.20831
3
3
9
0.125
0.58333
4.08331
4
4
10
0.13889
0.72222
5.05554
5
5
4
0.05555
0.77778
5.44446
5
6
5
0.06944
0.84722
5.93054
6
7
11
0.15278
1
7
7
Total
72






Old Grey Levels
nk
New Grey Levels
New nk
Modified Grey Levels
0
11
1
11
0-->1
1
8
2
8
1-->2
2
14
3
14
2-->3
3
9
4
9
3 -->4
4
10
5
14
4 -->5
5
4
5
14
5 -->5
6
5
6
5
6 -->6
7
11
7
11
7 -->7

New Grey Levels
 New nk
0
0
1
11
2
8
3
14
4
9
5
14
6
5
7
11


Original Histogram

Histogram Equalized

Here is the opencv code for histogram equalisation: 
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
 
using namespace std;
using namespace cv;
 
void imhist(Mat image, int histogram[])
{
 
    // initialize all intensity values to 0
    for(int i = 0; i < 256; i++)
    {
        histogram[i] = 0;
    }
 
    // calculate the no of pixels for each intensity values
    for(int y = 0; y < image.rows; y++)
        for(int x = 0; x < image.cols; x++)
            histogram[(int)image.at<uchar>(y,x)]++;
}
     
void histDisplay(int histogram[], const char* name)
{
    int hist[256];
    for(int i = 0; i < 256; i++)
    {
        hist[i]=histogram[i];
    }
    // draw the histograms
    int hist_w = 512; int hist_h = 400;
    int bin_w = cvRound((double) hist_w/256);
 
    Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(255, 255, 255));
 
    // find the maximum intensity element from histogram
    int max = hist[0];
    for(int i = 1; i < 256; i++){
        if(max < hist[i]){
            max = hist[i];
        }
    }

    // normalize the histogram between 0 and histImage.rows
    for(int i = 0; i < 256; i++)
 {
        hist[i] = ((double)hist[i]/max)*histImage.rows;
    }
 
 
    // draw the intensity line for histogram
    for(int i = 0; i < 256; i++)
    {
        line(histImage, Point(bin_w*(i), hist_h), Point(bin_w*(i), hist_h - hist[i]),Scalar(0,0,0), 1, 8, 0);
    }
 
    // display histogram
    namedWindow(name, CV_WINDOW_AUTOSIZE);
    imshow(name, histImage);
}
 
int main()
{
    // Load the image
    Mat image = imread("C:\\Users\\arjun\\Desktop\\input.jpg", CV_LOAD_IMAGE_GRAYSCALE);
 
    // Generate the histogram
    int histogram[256];
    imhist(image, histogram);
 
    // Calculate the size of image
    int size = image.rows * image.cols;

    // Calculate the probability of each intensity
    float PrRk[256];
    for(int i = 0; i < 256; i++)
    {
        PrRk[i] = (double)histogram[i] / size;
    }
 
    // Generate the equalized histogram
    float PsSk[256];
    for(int i = 0; i < 256; i++)
    {
        PsSk[i] = 0;
    }
 
    for(int i = 0; i < 256; i++)
  for(int j=0; j<=i; j++)
          PsSk[i] += PrRk[j];

    int final[256];
    for(int i = 0; i < 256; i++)
        final[i] = cvRound(PsSk[i]*255);

 for(int i = 0; i < 256; i++)
  for(int j=0; j<=255; j++)
          if (final[i]==final[j] && i!=j)
           {
           final[i]+=final[j];
        } 

 int final1[256];
    for(int i = 0; i < 256; i++)
 {
  final1[i]=0;
 }
  
    for(int i = 0; i < 256; i++)
 {
        final1[cvRound(PsSk[i]*255)] =cvRound(PrRk[i]*size);
 }

    for(int i = 0; i < 256; i++)
  for(int j=0; j<256; j++)
          if (final1[i]==final[j] && i!=j)
           {
          final1[i]+=final1[j];
          cout<<"final1["<<i<<"]="<<final1[i]<<endl;
        }

    // Generate the equlized image
    Mat new_image = image.clone();

    for(int y = 0; y < image.rows; y++)
        for(int x = 0; x < image.cols; x++)
            new_image.at<uchar>(y,x) = saturate_cast<uchar>(final[image.at<uchar>(y,x)]);
 
   // Display the original Image
    namedWindow("Original Image");
    imshow("Original Image", image);
 
    // Display the original Histogram
    histDisplay(histogram, "Original Histogram");

    // Display the equilzed histogram
    histDisplay(final1, "Equilized Histogram");

    // Display equilized image
    namedWindow("Equilized Image");
    imshow("Equilized Image",new_image);

    waitKey(0);
    return 0;
}
Opencv Input Image:
Image Before Histogram Equalization OpenCV

Histogram Equalised Image:
Image After Histogram Equalization OpenCV

Opencv Input Image Histogram:
Histogram of Image
Opencv Equalised Histogram:
Histogram Equalized OpenCV