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