Wednesday, September 2, 2015

Android: Visual Soccer and Goal Detection



Soccer is pingpang ball, since it is semi-transparent and has black letter on it, it is very hard to filter colour under OpenCV, so I painted it with water colour. 
Same with goal, to easy distinguish, painted it with blue water colour.

OpenCV is very powerful, it has plenty of functions for object detection. The main idea is, with smart phone camera, convert image to grey under HSV space, then filter colour, blur it then detect circle for soccer and rectangle for goal.  

Following is source code for visual soccer and goal detection under Android with JNI c code.

Using OpenCV library under Android, ball is coloured to red and net is coloured to blue.


int detectDoor(Mat & img_rgba, int & door_x, int & door_y)
{
Mat img_temp;
Mat img_work;
Mat img_gray;
int sel_id;
int max_area;

Mat img_3;
Mat kernel_ero = getStructuringElement(MORPH_RECT, Size(2,2));

int h;
int s;
int v;

door_x = 0;
door_y = 0;

h = 237/2;
s = 100*2 + 55;
v = 25*2 + 55;

cv::cvtColor(img_rgba, img_temp, CV_RGB2HSV); // convert to HSV
  inRange(img_temp, Scalar(266/2-20, 0, 0), Scalar(266/2+20, 255, 255), img_work); // filter color
  cv::blur(img_work, img_gray, Size(30, 30));

threshold(img_gray, img_gray, 100, 255, CV_THRESH_BINARY);
  erode(img_gray, img_gray, kernel_ero);

Size sz;
vector<vector<Point> > contours;
    vector<vector<Point> > contours0;
    vector<Vec4i> hierarchy;

    findContours( img_gray, contours0, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    int idx = 0;

    sel_id = 0;
    max_area = 0;
    for( idx = 0; idx < contours0.size(); idx++ )
    {
    Rect r = boundingRect(contours0[idx]);
        Scalar color( 255, 0, 0 );
        if(hierarchy[idx][2] < 0) //Check if there is a child contour
           rectangle(img_rgba,Point(r.x-10,r.y-10), Point(r.x+r.width+10,r.y+r.height+10), Scalar(0,0,255),2,8,0); //Opened contour
         else
           rectangle(img_rgba,Point(r.x-10,r.y-10), Point(r.x+r.width+10,r.y+r.height+10), Scalar(0,255,0),2,8,0); //closed contour

        if( max_area < r.width * r.height)
        {
        max_area = r.width * r.height;
        sel_id = idx;
        }
    }
// choose area > 400
    if( max_area > 400 )
    {
    Rect r = boundingRect(contours0[sel_id]);
    door_x = r.x + r.width/2;
    door_y = r.y + r.height/2;
        circle(img_rgba,Point(door_x,door_y),20, Scalar(255,0,0), 3, 8, 0); //closed contour
    }
return 0;
}

JNIEXPORT jintArray JNICALL Java_com_example_balltrace_BallTrace_BallDetect(JNIEnv* env, jobject thiz,
jint width, jint height, jlong yuv, jlong rgba, jlong outPtr)
{
jint outArray[5];
Mat mGray = *((Mat*)yuv);
Mat mRgba = *((Mat*)rgba); // original camera image

int door_x = 0;
int door_y = 0;

Mat hsv_frame;

outArray[0] = 0;
outArray[1] = 0;
outArray[2] = 0;
outArray[3] = -1; // door rect center X
outArray[4] = -1; // door rect center y

detectDoor(mRgba, door_x, door_y);

outArray[3] = door_x;
outArray[4] = door_y;

//Mat thresholded;
cv::Mat* thresholded = new cv::Mat(cv::Size(width, height), CV_8UC1);
cv::Rect roi( cv::Point( 0, 0 ), Size(width, height) );
    CvSize size = cvSize(width, height);
    cvtColor(mRgba, hsv_frame, CV_RGB2HSV);

    inRange(hsv_frame, Scalar(1, 80, 80), Scalar(7, 250, 250), *thresholded);

    GaussianBlur(*thresholded, *thresholded, Size(9,9), 0, 0);
    vector<Vec3f> circles;
    HoughCircles(*thresholded, circles, CV_HOUGH_GRADIENT,1.5, height/4, 100, 40, 15, 80 );

    for( size_t i = 0; i < circles.size(); i++ )
    {
    Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);

        if( radius < 5 )
        continue;
        // draw the circle center
        circle( mRgba, center, 3, Scalar(0,255,0), -1, 8, 0 );
        // draw the circle outline
        circle( mRgba, center, radius, Scalar(0,0,255), 3, 8, 0 );
        outArray[0] = cvRound(circles[i][0]);
        outArray[1] = cvRound(circles[i][1]);
        outArray[2] = radius;
        break;
    }

    delete thresholded;

    jintArray retArray = env->NewIntArray(5);
    env->SetIntArrayRegion(retArray, 0 , 5, outArray);

    return retArray;
}


No comments:

Post a Comment