Monday 5 March 2012

Pencil Sketches using C++ and OpenCV


When I changed my profile picture in FB to a thresholded image of a grayscale image of a picture of me looking at the computer monitor, taken from a Kinect at 45 degree angle and kept at table height (which is much lower than my neck), little did I know that my friends wouldn't believe that I indeed did it using C++ and OpenCV and not with photoshop.

Here is my present Profile Pic on FB :-
In order to make a sketch of a scene,  all you have to do is to take a picture of it and do a little bit of image processing on it. A picture is a combination of one to four two-dimensional matrices. We call each matrix as a channel. An RGB image has 3 channels. A QImage(a QT abstraction for images) has 4 channels by default, one extra channel for alpha. 

The Algorithm to do this would be something like this :-

1. Capture an RGB image using a camera
2. Convert it into a grayscale image
3. Threshold it to convert it into a binary image

Ta-Da!! That's it. Looks simpler than the algorithms we wrote in 1st semester of college. 

The first step is capturing a RGB image. Plug-in your camera to your computer. A driver is required to use the camera with the computer. The drivers might already be available for your camera, or easily available at the vendor websites. Okay, now OpenCV can  use the drivers available to get the picture from your camera. Thank god for OpenCV. 

OpenCV's API for this is cvCaptureFromCAM(int index).

This API returns a structure of type CvCapture. You can use this to read images from, by using another API cvQueryFrame, which returns a Image in Intel Picture Library Format. 

Too much technical stuff here, so what I talked about so far was just the first part. Put this in a loop and you can capture streaming video.

This is a Picture of my new Iphone 4S taken with my laptop's iSight Camera.



The next step is to convert the image into a grayscale image. I wanted to explain this part in as much detail as possible, so went through the OpenCV source files to really understand how this works under the hood, and this is what I understood.

The formula used by OpenCV to convert RGB to Gray scale is given by :

RGB[A]->Gray: Y<-0.299*R + 0.587*G + 0.114*B

This is done by the command cvCvtColor(srcImg, destImg, CV_RGB2GRAY);
Pretty straight forward. Now, to the important question here, why convert this image into a GrayScale image and not directly threshold it. Thresholding directly works on three channels independently. We would not end up with our desired results if we thresholded the 3 channel image, but rather with a color picture which has messed up color composition of the picture. Therefore, we convert it into a single channel 8-bit image from a 3 channel 8-bit image using the above formula.

This is the Image after converting it to a single channeled grayscale image :-


The next step is convert this into a Binary Image, or the Sketch. This is done by using a simple thresholding logic such as :
pixel(x,y) = White, if pixel(x,y)>threshold 
Black, otherwise

This is also called as Binary Threshold.  This is done by the command Threshold(srcImg, destImg, thresholdValue, maxValue, thresholdType). It is important that this threshold value is carefully chosen. The value here mostly varies upon lighting conditions. In very bright conditions, set it to a value around 30 - 50. In very Dark Condition, it should be between 7  and 20. The intermediate value is for intermediate lighting. Now, obviously threshold is  pixel value and here, each pixel is  represented by a 8-bit number so we can have 256 different values(Range : 0- 255). In my program, this is normalized to a scale of 0-1 for a more intuitive experience.

This is the Sketch of the image previously taken with threshold = 16 :-

I could have had better results, if I had set the threshold properly. This is another sketch of the same subject. This is a little better (Threshold = 128) :-



So there you go, that's all folks. See, making a sketch of is pretty simple with C++.
You can download the code from this link :-