hello friends, I am carrying out a project that is based on tracking a ball inside an acrylic tube. At the bottom of the tube is placed a motor that moves a ping pong ball, the goal is after the user sets the setpoint through the camera control the displacement of the ball in the tube until the set point. The code that I will share with you is fully functional, however I face some problems the roi is drawn based on the max and min, and the color of the ball based on the HoughCircles, but sometimes the inner and outer contour of the ball disappears, which it causes me enough problems. If I can solve these problems I can calculate the distance between the ball and the point defined by the user. By getting the distance without problems it is possible to regulate the motor speed to reach a value close to the setpoint value.
code:
#include <opencv2/highgui.hpp>
include <opencv2 imgproc.hpp="">
include <opencv2 opencv.hpp="">
include <iostream>
/#NAMESPACE/ using namespace cv; using namespace std; int cntr = 0; /VAR INCREMENT COUNT/
/DEFINE/
define OUTPUT_WINDOW_NAME "PID CONTROLLER" /DEFINE WINDOW NAME/
define USB_PORT_SERIAL "/dev/cu.usbmodemM4321001" /DEFINE SERIAL PORT/
/FUNCTION/ void onMouse(int event, int x, int y, int flags, void param); /BUTTON COORDINATE EVENT/ void ball_tracker(const cv::Mat& frame); /ORANGE BALL TRACKING*/ void regionOfInterest(const cv::Mat& frame);
/VALUES TO SET MAX AND MIN HSV TRACK BALL/ int lowH = 10; /SET LOW BALL HUE/ int highH = 25; /SET HIGH BALL HUE/
int lowS = 100; /SET LOW BALL SATURATION/ int highS = 255; /SET HIGH BALL SATURATION/
int lowV = 20; /SET LOW BALL VALUE/ int highV = 255; /SET HIGH BALL VALUE/
/VALUES TO CREATE AUTOMATIC ROI/ int hueValue = 65; int hueRange = 1;
int minSaturation = 20; int minValue = 0; /**/
int main(int argc, char* argv) { CvCapture *CAM_CAP; Point point_marker(-10,-10); /START POINTER OF THE SETPOINT*/
int retorno = 0;
/*VERIFY IF EXISTS ANY CAMERA*/
if(!(CAM_CAP = cvCaptureFromCAM(0))){
cout << "Failed to capture from camera" << endl;
retorno = 1;
goto exitCameraOpenFailed;
}
cout << "Camera opened successfully" << endl;
/*DEFINE IMAGE SIZE AND NAME*/
cvNamedWindow(OUTPUT_WINDOW_NAME, CV_WINDOW_AUTOSIZE);
IplImage *cameraFrame;
while(true){
if((cameraFrame = cvQueryFrame(CAM_CAP))){
IplImage * ipl = cameraFrame;
cv::Mat matrix = cv::cvarrToMat(ipl);
/*CALL BALL TRACKING FUNCTION*/
ball_tracker(matrix);
/*CALL REGION OF INTEREST FUNCTION*/
regionOfInterest(matrix);
/*SHOW MOUSE EVENT SETPOINT*/
if (point_marker.x != -1){
cv::drawMarker(matrix, point_marker, cv::Scalar(0, 0, 255), MARKER_CROSS, 10, 1);
}
/*MOUSE CALLBACKFUNCTION*/
cvSetMouseCallback(OUTPUT_WINDOW_NAME, onMouse, (void*) (&point_marker));
/*OPEN IMAGE AND SHOW WINDOW*/
cvShowImage(OUTPUT_WINDOW_NAME, cameraFrame);
/**/
}
if(cvWaitKey(60)!= -1){
cout << "Camera disable successfully" <<endl;
break;
}
}
cout << "INPUT" <<endl;
cvReleaseCapture(&CAM_CAP);
cvDestroyWindow(OUTPUT_WINDOW_NAME);
exitCameraOpenFailed:
return retorno;
}
/MOUSE EVENT CALLBACK/ void onMouse(int event, int x, int y, int flags, void param){ Pointpoint_marker = (Point*)param; if(event == CV_EVENT_LBUTTONDOWN){
if(cntr == 0){
point_marker->x = x;
point_marker->y = y;
cout << "POSITION:\n"<<point_marker->y<<" "<<endl;
}else{
point_marker->x = x;
point_marker->y = y;
cout <<point_marker->y<<endl;
}
cntr = cntr + 1;
}
}
/CONSTRUCT A ROI/ void regionOfInterest(const cv::Mat& frame){ Mat IMAGE_HSV; /CONVERT ORIGINAL IMAGE TO HSV COLOR SPACE/ cvtColor(frame, IMAGE_HSV, CV_BGR2HSV); /SPLIT THE CHANNELS/ std::vector<cv::mat>HSV_CHANNELS; split(IMAGE_HSV, HSV_CHANNELS);
Mat hueImage = HSV_CHANNELS[0]; // [hue, saturation, value]
// is the color within the lower hue range?
Mat hueMask;
inRange(hueImage, hueValue - hueRange, hueValue + hueRange, hueMask);
// if the desired color is near the border of the hue space, check the other side too:
// TODO: this won't work if "hueValue + hueRange > 180" - maybe use two different if-cases instead... with int lowerHueValue = hueValue - 180
if (hueValue - hueRange < 0 || hueValue + hueRange > 180){
Mat hueMaskUpper;
int upperHueValue = hueValue + 180; // in reality this would be + 360 instead
inRange(hueImage, upperHueValue - hueRange, upperHueValue + hueRange, hueMaskUpper);
// add this mask to the other one
hueMask = hueMask | hueMaskUpper;
}
// now we have to filter out all the pixels where saturation and value do not fit the limits:
Mat saturationMask = HSV_CHANNELS[1] > minSaturation;
Mat valueMask = HSV_CHANNELS[2] > minValue;
hueMask = (hueMask & saturationMask) & valueMask;
/*LINE DETECTION*/
std::vector<cv::Vec4i> lines;
HoughLinesP(hueMask, lines, 1, CV_PI / 360, 50, 50, 10);
// draw the result as big green lines:
for (unsigned int i = 0; i < lines.size(); ++i){
Point(lines[i][0], lines[i][1]);
Point(lines[i][2], lines[i][3]);
/*line(frame, cv::Point(lines[i][0], lines[i][1]), cv::Point(lines[i][2], lines[i][3]), cv::Scalar(0, 255, 0), 2);*/
}
vector<Point> pts;
for (unsigned int i = 0; i < lines.size(); ++i){
pts.push_back( Point(lines[i][0], lines[i][1]) );
pts.push_back( Point(lines[i][2], lines[i][3]) );
}
/*GET THE PREVIOUS POINTS DETECTED IN THE IMAGE*/
Rect box = boundingRect(pts);
/*DRAW RECTANGLE REGION OF INTEREST*/
cv::rectangle(frame, box.tl(), box.br(), cv::Scalar(0, 255, 0), 2, CV_AA);
} /END CONSTRUCT A ROI/
/BALL TRACKER FUNCTION/ void ball_tracker(const cv::Mat& frame){
Mat HSV_IMAGE;
Mat TRESH_IMAGE;
vector<Vec3f> circles;
cvtColor( frame, HSV_IMAGE, CV_BGR2GRAY );
GaussianBlur( HSV_IMAGE, HSV_IMAGE, Size(9, 9), 2, 2 );
inRange(HSV_IMAGE, cv::Scalar(lowH, lowS, lowV), cv::Scalar(highH, highS, highV), TRESH_IMAGE);
GaussianBlur(TRESH_IMAGE, TRESH_IMAGE, cv::Size(3, 3), 0);
dilate(TRESH_IMAGE, TRESH_IMAGE, 0);
erode(TRESH_IMAGE, TRESH_IMAGE, 0);
HoughCircles( HSV_IMAGE, circles, CV_HOUGH_GRADIENT, 2, HSV_IMAGE.rows/4, 200, 100, 0, 0 );
for( size_t i = 0; i < circles.size(); i++ ){
/*DRAW SMALL CIRCLE IN CENTER OF SPHERE*/
cv::circle(frame,cv::Point((int)circles[i][0], (int)circles[i][1]),3,cv::Scalar(0, 255, 0), CV_FILLED);
/*DRAW RED CIRCLE AROUND THE BALL*/
cv::circle(frame, cv::Point((int)circles[i][0], (int)circles[i][1]), (int)circles[i][2], cv::Scalar(0, 0, 255),3);
}
} /END BALL TRACKER FUNCTION/