Real test on square or circle object determination


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.

blob.png  blob.png

outside contained rectangle(green) circle(blue) 

Besides,  wavelet transformation is used to minimize interference from outside.

blob.png

test picture

blob.png

Canny Before wavelet transformation

blob.png

Canny After wavelet transformation

blob.png

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)

blob.png

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

Last Article Next article

Comment 评论



Share 分享

New Users 最新加入

  • hokurikustr

  • refrain

New comments 最新评论

test123: aasdas Details Apr 13 16:39
admin: Thanks! Details Apr 09 11:46
admin: Google map api Details Apr 09 11:46
lqj12: cooooooooool Details Apr 08 21:34
Yunhan Huang: 这个功能是如何实现的? Details Apr 08 13:23