In real time situation, the production line may produce more than single products at the same time. It should be able to determine the object type and location.
Circle or square determination is the most basic one.
Comparing outside contained rectangle or circle area with original contour area, we can easily decide the shape.

outside contained rectangle(green) circle(blue)
Besides, wavelet transformation is used to minimize interference from outside.

test picture

Canny Before wavelet transformation

Canny After wavelet transformation

find region of interest one by one
Linux and Windows versions should be united for maintenance, so using #ifdef function, I combine two versions together.
A usb camera is used to capture picture.
A robot arm is also assembled for test(using serial communication)

Final version of the whole project:
//capture picture from usb camera and determine its shape(rectangle or circle) and send serial info to robot arm
//using opencv 2.4.9
//author: Chen Li
//v1.0
//2014.10
#include "opencv2/opencv.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <vector>
//if windows
#ifdef _WIN32
#include <windows.h>
//if linux
#else
#include <unistd.h>
#endif
//serial communication
#include "rs232.h"
//canny edge detection min thresh. smaller,more detailed
#define THRESH 45
//min target area
#define MINSELECTAREA 10000
//max object number in a picture
#define MAXOBJECT 10
//gap time between two serial send
#define GAPTIME 5 //seconds
//serial buffer size
#define SERIALBUFFER 100
//camera number
#define CAMERAID 0
//if show windows during process -- comment to
#define DEMO 1
using namespace cv;
using namespace std;
//to store object info
class TP{
public:
float x,y;
unsigned int gx,gy;
char type; //Undefined/Circle/Rectangle
float attri; //Radius/Angle
char text[30];
int grid[4][6];
int areano;
TP(){
x=y=gx=gy=attri=0;
type='U';
grid[0][0]=1313;
grid[0][1]=1640;
grid[0][2]=1500;
grid[0][3]=1407;
grid[0][4]=1520;
grid[0][5]=1663;
}
void getpos(char typ,Point tmp,float attr){
x=tmp.x;
y=tmp.y;
attri=attr;
type=typ;
}
void calgrid(){
if(x>0&&x<320)
gx=0;
else if(x>=240&&x<640)
gx=1;
if(y>0&&y<240)
gy=1;
else if(x>=240&&x<480)
gy=0;
areano=gx+gy*2;
}
char* formtext(){
//sprintf(text,"#8 P%d #9 P%d #10 P%d #11 P%d #12 P%d #13 P%d T2000\r\n",grid[areano][0],grid[areano][1],grid[areano][2],grid[areano][3],grid[areano][4],grid[areano][5]);
sprintf(text,"PL 0 SQ %d ONCE\r\n",areano);
cout<<text<<endl;
return this->text;
}
};
//Variables
TP tp[MAXOBJECT];
//circle number
unsigned int countcircle=0;
//rectangle number
unsigned int countrect=0;
void piccapture(){
cv::VideoCapture cap(CAMERAID);
if(!cap.isOpened())
{
std::cout<<"Can not open camera"<<endl;
}
Mat frame;
#ifdef DEMO
while(1){
cap>>frame;
imshow("video",frame);
char c=cvWaitKey(33);
//press ESC to capture
if(c==27)
break;
}
#else
#ifdef _WIN32
Sleep(500);
#else
usleep(500*1000);
#endif
cap>>frame;
#endif
imwrite("0.Capturedpic.jpg",frame);
}
void imageProcess(){
//read from last capture
Mat frame = imread ("0.Capturedpic.jpg");
Mat image;
cvtColor (frame,image,CV_BGR2GRAY);
Mat imgCanny;
Canny(image, imgCanny, THRESH, THRESH * 3);
imwrite("1.Canny.jpg",imgCanny);
Mat img_dilate,img_erode;
dilate ( imgCanny, img_dilate, Mat());
//imshow("Imagedilate", img_dilate);
imwrite("2.ImageDilate.jpg",img_dilate);
erode ( img_dilate, img_erode, Mat());
//imshow("Imageerode", img_erode);
imwrite("3.ImageErode.jpg",img_erode);
vector< vector<Point> > contours;
findContours(img_erode, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
Mat contmat = Mat::zeros(imgCanny.rows, imgCanny.cols, CV_8UC3);
for (int j = 0; j < contours.size(); j++){
Scalar color(255,0,0);
drawContours(contmat, contours, j, color);
}
imwrite("4.FindContours.jpg",contmat);
#ifdef DEMO
namedWindow ("canny");
imshow ("canny",contmat);
waitKey(0);
cv::destroyAllWindows();
#endif
for (int j = 0; j < contours.size(); j++){
double contarea=contourArea(contours[j]);
if(contarea<MINSELECTAREA){
std::cout<<"Ignore contour"<<j<<" which has an area of "<<contarea<<endl;
continue;
}
Mat singlecont = Mat::zeros(imgCanny.rows, imgCanny.cols, CV_8UC3);
drawContours(singlecont, contours, j, Scalar(0,0,255),CV_FILLED);
Mat singlecontgray;
cvtColor (singlecont,singlecontgray,CV_BGR2GRAY);
imwrite("5.SingleObject.jpg",singlecontgray);
#ifdef DEMO
namedWindow ("SingleObject");
imshow ("SingleObject",singlecontgray);
#endif
Mat img_erode_single;
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(10,10));
erode(singlecontgray, img_erode_single, element);
imwrite("6.SingleObjectErode.jpg",img_erode_single);
#ifdef DEMO
imshow("Imagedilate", img_erode_single);
#endif
Canny(image, imgCanny, THRESH, THRESH * 3);
vector< vector<Point> > contourssingle;
findContours(img_erode_single, contourssingle, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
//find the max contour in a single object(in case an object divided into more)
double maxsinglearea=0;
unsigned int maxposition=0;
for (unsigned int sing = 0; sing < contourssingle.size(); sing++){
double contareatmp=contourArea(contourssingle[sing]);
if(contareatmp>MINSELECTAREA&&contareatmp>maxsinglearea){
maxsinglearea=contareatmp;
maxposition=sing;
}
}
Mat singleselectcontour = Mat::zeros(imgCanny.rows, imgCanny.cols, CV_8UC3);;
drawContours(singleselectcontour,contourssingle,maxposition,Scalar(76,76,76),CV_FILLED);
imwrite("7.SingleObjectSelected.jpg",singleselectcontour);
#ifdef DEMO
imshow("SingleObjectSelected", singleselectcontour);
#endif
//find minEnclosingCircle
float radius;
Point2f circlecenter;
minEnclosingCircle(contourssingle[maxposition], circlecenter, radius);
Point circlecenterdraw;
circlecenterdraw.x=circlecenter.x;
circlecenterdraw.y=circlecenter.y;
circle(frame, circlecenterdraw, static_cast<int>(radius), Scalar(255,0,0), 3);
double circlearea=3.14*radius*radius;
//find minAreaRect
RotatedRect recprofile = minAreaRect(contourssingle[maxposition]);
Point2f vertices[4];
recprofile.points(vertices);
for (int i = 0; i < 4; i++){
line(frame, vertices[i], vertices[(i+1)%4],Scalar(0,255,0),3);
}
double rectarea=recprofile.size.height*recprofile.size.width;
std::cout<<"Circle area:"<<circlearea<<endl;
std::cout<<"Rectangle area:"<<rectarea<<endl;
std::cout<<"Contour area:"<<contarea<<endl;
char location[100];
if(circlearea-contarea<rectarea-contarea){
tp[countcircle+countrect].getpos('C',circlecenter,radius);
sprintf(location,"Circle Center:x=%.1f, y=%.1f Radius=%.1f",circlecenter.x,circlecenter.y,radius);
cout<<"Circle Center:x="<<circlecenter.x<<", y="<<circlecenter.y<<" Radius="<<radius<<endl;
putText(frame,location,Point(0,frame.rows-5-20*(countcircle+countrect)),FONT_HERSHEY_SIMPLEX,0.4,Scalar(255,0,0));
putText(frame,"Circle",circlecenter,FONT_HERSHEY_SIMPLEX,1,Scalar(255,0,0));
circle( frame, circlecenter, 3, Scalar(255,0,0), -1, 8, 0 );
//char tmpString[30];
//sprintf(tmpString,"C,x=%.1f,y=%.1f,r==%.1f",circlecenter.x,circlecenter.y,radius);
//strcat(Results, tmpString);
countcircle++;
}
else{
tp[countcircle+countrect].getpos('R',recprofile.center,recprofile.angle);
sprintf(location,"Rect Center:x=%.1f, y=%.1f, Angle=%.1f",recprofile.center.x,recprofile.center.y,recprofile.angle);
cout<<"Rectangle Center:x="<<recprofile.center.x<<", y="<<recprofile.center.y<<", Angle"<<recprofile.angle<<endl;
putText(frame,location,Point(0,frame.rows-5-20*(countcircle+countrect)),FONT_HERSHEY_SIMPLEX,0.4,Scalar(255,0,0));
putText(frame,"Rectangle",recprofile.center,FONT_HERSHEY_SIMPLEX,1,Scalar(0,255,0));
circle( frame, recprofile.center, 3, Scalar(0,255,0), -1, 8, 0 );
//char tmpString[30];
//sprintf(tmpString,"R,x=%.1f,y=%.1f,a==%.1f",recprofile.center.x,recprofile.center.y,recprofile.angle);
//strcat(Results, tmpString);
countrect++;
}
imwrite("8.CombinedResult.jpg",frame);
#ifdef DEMO
namedWindow ("CombinedResult");
imshow ("CombinedResult",frame);
waitKey (0);
destroyAllWindows();
#endif
}
}
bool SendResult(){
int i=0,
bdrate=115200;
char mode[]={'8','N','1',0},
str[SERIALBUFFER];
int cport_nr;
for( cport_nr=0;cport_nr<29;cport_nr++){
if(RS232_OpenComport(cport_nr, bdrate, mode))
continue;
else{
cout<<"Connect Success:"<<cport_nr<<endl;
break;
}
if(cport_nr==29){
printf("Can not open comport\n");
return 0;
}
}
#ifdef _WIN32
Sleep(3000);
#else
usleep(2000*1000);
#endif
for(unsigned int r=0;tp[r].type!='U'&&r<MAXOBJECT;r++){
tp[r].calgrid();
strcpy(str,tp[r].formtext());
RS232_cputs(cport_nr,str);
printf("sent: %s\n", str);
#ifdef _WIN32
Sleep(GAPTIME*1000);
#else
usleep(GAPTIME*1000*1000);
#endif
}
return 1;
}
int main(){
do{
//piccapture();
imageProcess();
#ifdef _WIN32
Sleep(2000);
#else
usleep(2000*1000);
#endif
cout<<"can not find!"<<endl;
}while(countcircle+countrect==0);
cout<<"Total object:"<<countcircle+countrect<<endl;
cout<<"Circle:"<<countcircle<<endl;
cout<<"Rectangle:"<<countrect<<endl;
if(SendResult()){
cout<<"send Success!"<<endl;
}
return 0;
}This article is among the series of FDUROP project. More information: http://104.131.150.53/thinkcmfx/index.php?g=&m=article&a=index&id=17