Saturday, 12 April 2014

#10a. Point Polygon Test: Part I

Point Polygon Test determines whether the point is inside a contour, outside, or lies on an edge (or coincides with a vertex). It returns positive (inside), negative (outside), or zero (on an edge) value, correspondingly. When flag is turned off , the return value is +1, -1, and 0, respectively. Otherwise, the return value is a signed distance between the point and the nearest contour edge.
I've divided the tutorial in two parts to make it easy to understand.
---------------------------------------------------
#include<iostream>
#include<highgui.h>
#include<cv.h>
using namespace std;
using namespace cv;

Mat img_all_contours;
vector<vector<Point> > closed_contours;
vector<Vec4i> heirarchy;

vector<vector<Point> > make_contours_closed(vector<vector<Point> > contours){
vector<vector<Point> > closed_contours;
closed_contours.resize(contours.size());
for(int i=0;i<contours.size();i++)
approxPolyDP(contours[i],closed_contours[i],0.1,true);
return closed_contours;
}
int main()
{
Mat img=imread("images/circles3.jpg");
img_all_contours=img.clone();

Mat imgb;
cvtColor(img,imgb,CV_RGB2GRAY);

Mat edges;
Canny(imgb,edges,50,100);
vector<vector<Point> > contours;

findContours(edges,contours,heirarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE);

closed_contours=make_contours_closed(contours);

drawContours(img_all_contours,closed_contours,-1,Scalar(0,255,0));
imshow("Contours",img_all_contours);

while(char(waitKey(1))!='q'){}
return 0;

}

---------------------------------------------
Here, the Main() function is similar to the Hierarchical Contour Detection code(#9a).
I've introduced a function 'make_contours_closed' which basically closes all the open contours(if any), so that whenever it is clicked, there's always a closed contour associated with the position of click.
approxPolyDP(contours[i],closed_contours[i],0.1,true);
contours[i]: Input Array
closed_contours[i]: Output Array
0.1Parameter specifying the approximation accuracy. This is the maximum distance between the original curve and its approximation.
truethe approximated curve is closed (its first and last vertices are connected). If false, it is not closed
You may look at the documentation of 'approxPolyDP' here.
------------------------------------------------------
Input Image:

Output Image:


Sunday, 30 March 2014

#9a. Hierarchical contour extraction

Contours are curves joining all the continuous points (along the boundary), having same color or intensity.


#include
#include
#include
using namespace std;
using namespace cv;

Mat img;
vector > contours;
vector heirarchy;
int levels=0;

void on_trackbar(int, void *){
	Mat img_show=img.clone();
	drawContours(img_show,contours,-1,Scalar(0,0,255),3,8,heirarchy,levels);
	imshow("Contours",img_show);
}

int main()
{
	img=imread("images/circles3.jpg");
	Mat imgb;
	cvtColor(img,imgb,CV_RGB2GRAY);
	Mat edges;
	Canny(imgb,edges,50,100);
	findContours(edges,contours,heirarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE);

	namedWindow("Contours");
	createTrackbar("levels","Contours",&levels,15,on_trackbar);
	on_trackbar(0,0);
	while(char(waitKey(1))!='q'){}
	return 0;
}



Firstly, in Main(), the image is read, converted to gray scale, and edges are detected by Canny edge detection algorithm.

findContours() finds all the contours in the image.
You may look at the documentation here.

Now, the contours have a hierarchy, in the sense that, there are outermost contours and there are contours inside contours which may be noise.
You should have the concept of 'contours' clear.
You may go through the documentation here.

Then we created a trackbar by which we can change the level of hierarchy.
drawContours() simply draws the contours on the image.
Find the documentation of drawContours() here.
-------------------------------------
Input Image:


Output:
Level 1:


Level 5:



Level 11(Saturation Point, for this image):


Wednesday, 19 March 2014

#8c. Object Detection: Morphological Opening and Closing

In our Object Detection application, you may have noticed quite some amount of noise.
When it is supposed to detect red color, it also detects some noise, and gives wrong output,sometimes.


Opening is obtained by eroding an image followed by dilating it. It will have an effect of removing small white regions in the image. Closing is obtained by dilating an image followed by eroding it; this will have the opposite effect. Both these operations are used frequently to remove noise
from an image. Opening removes small white pixels while closing removes small black “holes.”

The OpenCV function morphologyEX()can be used to perform advanced morphological operations such as opening and closing on images. So we can open and close the output of the inRange() function to remove black and white dots by updating the 'main' function:

int main()
{

 VideoCapture cap(0);
 namedWindow("Video");
 namedWindow("Segmentation");

  createTrackbar("0.R\n1.G\n2.B","Segmentation",&rgb_slider, 2 ,on_rgb_trackbar);

 createTrackbar("Low threshold","Segmentation",&low_slider,255,on_low_thresh_trackbar);

 createTrackbar("High threshold","Segmentation",&high_slider,255,on_high_thresh_trackbar);

 while(char(waitKey(1)) != 'q') {
  cap>>frame;
  if(frame.empty())
   break;
  inRange(frame,Scalar(low_b,low_g,low_r),Scalar(high_b,high_g,high_r),frame_threshold);

//Morphological Opening and Closing
  Mat str_el=getStructuringElement(MORPH_RECT,Size(3,3));
  morphologyEx(frame_threshold,frame_threshold,MORPH_OPEN,str_el);
  morphologyEx(frame_threshold,frame_threshold,MORPH_CLOSE,str_el);


  imshow("Video",frame);
  imshow("Segmentation",frame_threshold);
 }

 return 0;
}
---------------------------------------------------------

Tuesday, 18 March 2014

Project 1.1. PaintBox 2.0

Lets create PaintBox version 2.0.
In this version, I have introduced trackbars for changing colors and size of the brush(:P).
Again, this code is self-explanatory.
You just need to know the concept of trackbars!

#include
#include
#include
#include
using namespace std;
using namespace cv;
Mat img;
Point pt;
int red,green,blue;
int slider=0, slider_max=255;
int size_min=4,size_max=40,size;
bool lup=false, ldown=false;
void on_trackbar_r(int pos, void *){
 red=pos;
}

void on_trackbar_g(int pos, void *){
 green=pos;
}

void on_trackbar_b(int pos, void *){
 blue=pos;
}

void on_trackbar_size(int pos, void *){
 size=pos;
}
static void mouse_callback(int event, int x, int y,int, void *){
if(event==EVENT_LBUTTONDOWN){
 ldown=true;
 pt.x=x;
 pt.y=y;
 circle(img,pt,size,CV_RGB(red,green,blue),-3);
}

if(ldown==true && lup==false){
 pt.x=x;
 pt.y=y;
 Mat local_img=img.clone();
 circle(img,pt,size,CV_RGB(red,green,blue),-3);
 imshow("PaintBox",local_img);
}

if(event==EVENT_LBUTTONUP){
 lup=true;
}
if(ldown==true && lup==true){
 ldown=false;
 lup=false;
}
}
int main()
{

 img=Mat(550,1300,CV_8UC3,Scalar(255,255,255));
//Here, I've created a white image of 550x1300 size
// which has 3 channels(R,G,B)
// each R,G,B color represented in Unsigned Char in 8 Bits
//So the strength of each channel varies from 0 to 255.
// color of the image is white(255,255,255)

 namedWindow("PaintBox");
 imshow("PaintBox",img);
 createTrackbar("Size","PaintBox",&size_min,size_max,on_trackbar_size);
 createTrackbar("Red","PaintBox",&slider,slider_max,on_trackbar_r);
 createTrackbar("Green","PaintBox",&slider,slider_max,on_trackbar_g);
 createTrackbar("Blue","PaintBox",&slider,slider_max,on_trackbar_b);
 setMouseCallback("PaintBox",mouse_callback);
 while(char(waitKey(1))!='q'){}
 return 0;
}

---------------------------------------------------------------------------

The 'Blue' trackbar is not visible as the image was too large for the Cropping Program to handle.

Note: For an eraser, just set the RGB values to (255,255,255).

Also, I just figured out that you can even use this as an image editor.

Just set img to the image you wish to edit. e.g.

img=imread("images/attitude.jpg");



And now, that's my fb cover pic!

Project 1. Creating your own PaintBox!

Hi guys!
Lets take a look at how a simple Paint application can be implemented using OpenCV.
I shall combine the concepts of tutorial #1(MouseCallback) and tutorial #8, (circle).
The code is quite easy and self explanatory, provided, you are thorough with all the tutorials until now.
If you have any doubts, feel free to comment.
#include
#include
#include
#include
using namespace std;
using namespace cv;

Mat img;
Point pt;

bool lup=false, ldown=false;

static void mouse_callback(int event, int x, int y,int, void *){
if(event==EVENT_LBUTTONDOWN){
 ldown=true;
 pt.x=x;
 pt.y=y;
 circle(img,pt,4,CV_RGB(255,0,0),-3);
}

if(ldown==true && lup==false){
 pt.x=x;
 pt.y=y;
 Mat local_img=img.clone();
 circle(img,pt,4,CV_RGB(255,0,0),-3);
 imshow("PaintBox",local_img);
}

if(event==EVENT_LBUTTONUP){
 lup=true;
}
if(ldown==true && lup==true){
 ldown=false;
 lup=false;
}
}
int main()
{
 img=imread("images/white.png");
 namedWindow("PaintBox");
 imshow("PaintBox",img);
 setMouseCallback("PaintBox",mouse_callback);
 while(char(waitKey(1))!='q'){}
 return 0;
}



Monday, 17 March 2014

#8b. Object Detection version 0.1

Although the title is Object Detection, the following code won't actually detect objects.
This is just to focus on the use of 'inRange()' function.
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
 Mat frame, frame_segmented;
 VideoCapture cap(0);
 namedWindow("Video");
 namedWindow("Video_Segmented");

 while(char(waitKey(1))!='q'){
  cap>>frame;

  inRange(frame, Scalar(30,30,30),Scalar(100,100,100),frame_segment);
  
                imshow("Video",frame);
  imshow("Video_Segmented",frame_segmented);
 }

 return 0;
}


Sunday, 16 March 2014

#8a. Object Detection version 1.0

Moving on to object detection, you will find this post very interesting, provided you understand it.
In this tutorial, I will show you how to detect an object based on difference in color of the object and that of the surroundings.

I hope you've read 'A piece of advice', my previous post.
Try it in this tutorial. See if you understand the program in one shot.


#include
#include
#include
#include
using namespace std;
using namespace cv;

Mat frame , frame_threshold;
//Refer Main

int rgb_slider=0;
int low_slider=30, high_slider=100;

int low_r=30, low_g=30, low_b=30, high_r=100, high_g=100, high_b=100;
//r g b r g b

void on_rgb_trackbar(int ,void *){
 switch(rgb_slider){
 case 0:
  setTrackbarPos("Low threshold","Segmentation",low_r);
  setTrackbarPos("High threshold","Segmentation",high_r);
  break;
 case 1:
  setTrackbarPos("Low threshold", "Segmentation", low_g);
  setTrackbarPos("High threshold","Segmentation", high_g);
  break;
 case 2:
  setTrackbarPos("Low threshold","Segmentation",low_b);
  setTrackbarPos("High threshold", "Segmentation",high_b);
  break;
 }
}

//go to Main again

void on_low_thresh_trackbar(int, void*){
 switch(rgb_slider){
 case 0:
  low_r=min(high_slider-1, low_slider);
  setTrackbarPos("Low threshold","Segmentation",low_r);
  break;
 case 1:
  low_g=min(high_slider-1,low_slider);
  setTrackbarPos("Low threshold","Segmentation",low_g);
  break;
 case 2:
  low_b=min(high_slider-1,low_slider);
  setTrackbarPos("Low threshold","Segmentation",low_b);
  break;
 }
}

//go to Main again

void on_high_thresh_trackbar(int, void*){
 switch(rgb_slider){
 case 0:
  high_r=max(high_slider, low_slider+1);
  setTrackbarPos("High threshold","Segmentation",high_r);
  break;
 case 1:
  high_g=max(high_slider,low_slider+1);
  setTrackbarPos("High threshold","Segmentation",high_g);
  break;
 case 2:
  high_b=max(high_slider,low_slider+1);
  setTrackbarPos("High threshold","Segmentation",high_b);
  break;
 }
}


int main()
{

 VideoCapture cap(0);
 namedWindow("Video");
 namedWindow("Segmentation");

  createTrackbar("0.R\n1.G\n2.B","Segmentation",&rgb_slider, 2 ,on_rgb_trackbar);
//observe the variables and callback function.
// go back to where you've left.

 createTrackbar("Low threshold","Segmentation",&low_slider,255,on_low_thresh_trackbar);
//observe the variables and callback function.
// go back to where you've left.

 createTrackbar("High threshold","Segmentation",&high_slider,255,on_high_thresh_trackbar);
//observe the variables and callback function.
// go back to where you've left.

 while(char(waitKey(1)) != 'q') {
  cap>>frame;
  if(frame.empty())
   break;
  inRange(frame,Scalar(low_b,low_g,low_r),Scalar(high_b,high_g,high_r),frame_threshold);

//inRange function, refer text.

  imshow("Video",frame);
  imshow("Segmentation",frame_threshold);
 }

 return 0;
}


So, inRange function basically filters out an image based on the color channels.
  • The color channel sequence in opencv is B, G ,R  and not R,G,B.
  • Scalar(B,G,R) defines the strength of an image in terms of B, G, R.\
  • If you remember, we had done a tutorial on increasing brightness of an image using TrackBar(Tutorial #6). The same function was used. I hope you got the idea.
  • If you understood Scalar(), you've understood inRange.
  • If not, refer DOCUMENTATION.
----------------------------------------------------------
Screenshot of the input video:
(My room is completely messed up. So I cropped the screenshot. Ubuntu doesn't have a cropping utility, but, we know OpenCV now! First Tutorial, Cropping app!)

Output:
e.g. Red color detector
Wonderful, isn't it?