ראייה ממוחשבת – זיהוי ומעקב מהיר אחר אובייקט

תגיות: , , , , , , ,

בפוסט זה אתאר תהליך הוספת אלמטים של ראייה ממוחשבת לפלטפורמה הרובוטית שבניתי. רציתי לאפשר לרובוט לבצע שני דברים. הראשון הוא מעקב של הרובוט אחרי אוביקט, לצורך העניין כדור, עם מצלמת הראש שלו. מצלמת הראש מותקנת על מערכת Pan-Tilt מבוססת מנועי סרוו שמאפשרת למצלמה לנוע ימינה, שמאלה, למעלה ולמטה, כמו שניתן לראות בתמונה. מעקב אחרי האוביקט עם מצלמת הראש הוא הזזת המצלמה בהתאם למיקום הכדור.

המטרה השניה והיותר מורכבת היא שימוש ביכולות המעקב של המצלמה יכולות ראייה ממוחשבת נוספות, על מנת לאפשר לרובוט כולו לרדוף אחרי כדור מושלך, בדומה למשחק עם כלב.

מימוש

כדי לממש את יכולות הראייה הממוחשבת של הרובוט השתמשתי במספר אלגוריתמי עיבוד תמונה וראייה ממוחשבת בסיסיים. השתמשתי בספריית OpenCV – ספריית ראייה ממוחשבת בעלת קוד פתוח, שפותחה במקור על ידי אינטל. הספרייה היא Cross Platform (השתמשתי בה גם ב-PC וגם בלוחות מבוססי ARM כדוגמת ה-Beagleboard). ספריית OpenCV נוחה לשימוש לבעלי רקע וקצת נסיון עם עיבוד תמונה.

כאמור, האוביקט הראשון שרציתי ללמד את הרובוט לזהות הוא כדור פשוט בצבע כתום.

מעקב מבוסס צבע

מעקב מבוסס צבע מומלץ לשמוש כאשר ניתן להפריד צבע אחד משאר צבעי הרקע בתמונה – לדוגמה, כדור בצבע כתום בחדר עם קירות לבנים, אריחי רצפה לבנים וכו'. סינון שאר הצבעים מהתמונה, והשארת הצבע הכתום בלבד הוא תהליך מהיר מאד שמאיץ את אלגוריתמי עיבוד התמונה שיורצו בשלב מאוחר יותר, ולכן הוא מומלץ כפילטר ראשוני. אגב, ניתן לומר ששלט Wii משתמש גם הוא ביהוי מבוסס צבע למציאה מהירה של מיקום השלט (שם "הצבע" שבשימוש הוא גלי אור אינפרא-אדומים).

כדי לפלטר ולהשאיר בתמונה רק את הצבעים הכתומים, העברתי את התמונה למרחב צבע HSV, ולאחר מכן השתמשתי בפילטר cvInRange כדי להותיר רק את טווח הצבעים הכתום. בתמונות למטה ניתן לראות את התמונה לאחר מעבר למרחב הצבע HSV, ולאחר מכן לאחר הרצת פילטר הצבע והמרה לשחור-לבן (כאשר לבן מיצג צבע כתום בתמונה המקורית ושחור את כל שאר הצבעים).

להלן הקוד ב-Python הרלוונטי:


#declare necessary objects

hsv_frame = cvCreateImage(size, IPL_DEPTH_8U, 3)
thresholded = cvCreateImage(size, IPL_DEPTH_8U, 1)
thresholded2 = cvCreateImage(size, IPL_DEPTH_8U, 1)
hsv_min = cvScalar(0, 50, 170, 0)
hsv_max = cvScalar(10, 180, 256, 0)
hsv_min2 = cvScalar(170, 50, 170, 0)
hsv_max2 = cvScalar(256, 180, 256, 0)

# convert to HSV for color matching
# as hue wraps around, we need to match it in 2 parts and OR together
cvCvtColor(frame, hsv_frame, CV_BGR2HSV)
cvInRangeS(hsv_frame, hsv_min, hsv_max, thresholded)
cvInRangeS(hsv_frame, hsv_min2, hsv_max2, thresholded2)
cvOr(thresholded, thresholded2, thresholded)

זיהוי אוביקט מבוסס צורה

כמובן, שזיהוי מבוסס צבע לא מספיק ויכול לשמש רק כשלב ראשוני באלגוריתם לזיהוי צורה. למשל במקרה שבו אדם לובש חולצה כתומה לא נרצה שהרובוט יעקוב אחרי האדם (למרות שזה יכול להיות ניסוי די מעניין :) ). לכן השלב הבא הוא זיהוי צורה ספציפית, ובמקרה הזה עיגול (היצוג הדו ממדי של הכדור). לשם כך השתמשתי בהתמרת Hough, התמרה מתמטית שבסיסה זיהוי אוביקטים המתאימים למשוואה המתימטית שמיצגת אותם. לפני השימוש בהתמרת Hough הפעלתי פילטר ריכוך לתמונה, שמנסיוני מטייב את התוצאות של התמרת Hough.

להלן הקוד, ב-Python.


# pre-smoothing improves Hough detector
cvSmooth(thresholded, thresholded, CV_GAUSSIAN, 9, 9)
circles = cvHoughCircles(thresholded, storage, CV_HOUGH_GRADIENT, 2, thresholded.height/4, 100, 40, 20, 200)

התוצאה

התוצאה של הקוד הקצר הזה היתה בעיני מרשימה ביותר – הרובוט זיהה במהירות ובדיוק את הכדור, ללא תלות במרחק הכדור מהמצלמה, מהירות התנועה, או חדות התנועה. את התוצאה אפשר לראות בסרטון הבא:

הקוד במלואו, כולל הזזת מצלמת הראש ע"י הפעלת מנועי הסרוו

/*****************************************************************************************
*  Name    : Fast object tracking using the OpenCV library                               *
*  Author  : Lior Chen <chen.lior@gmail.com>                                             *
*  Notice  : Copyright (c) Jun 2010, Lior Chen, All Rights Reserved                      *
*          :                                                                             *
*  Site    : http://www.lirtex.com                                                       *
*  WebPage : http://www.lirtex.com/robotics/fast-object-tracking-robot-computer-vision   *
*          :                                                                             *
*  Version : 1.0                                                                         *
*  Notes   : By default this code will open the first connected camera.                  *
*          : In order to change to another camera, change                                *
*          : CvCapture* capture = cvCaptureFromCAM( 0 ); to 1,2,3, etc.                  *
*          : Also, the code is currently configured to tracking RED objects.             *
*          : This can be changed by changing the hsv_min and hsv_max vectors             *
*          :                                                                             *
*  License : This program is free software: you can redistribute it and/or modify        *
*          : it under the terms of the GNU General Public License as published by        *
*          : the Free Software Foundation, either version 3 of the License, or           *
*          : (at your option) any later version.                                         *
*          :                                                                             *
*          : This program is distributed in the hope that it will be useful,             *
*          : but WITHOUT ANY WARRANTY; without even the implied warranty of              *
*          : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
*          : GNU General Public License for more details.                                *
*          :                                                                             *
*          : You should have received a copy of the GNU General Public License           *
*          : along with this program.  If not, see <http://www.gnu.org/licenses/>        *
******************************************************************************************/
#!/usr/bin/python
# -*- coding: utf-8 -*-

from opencv.cv import *
from opencv.highgui import *
from threading import Thread
#import serial

class RobotVision:
	cvSize size
	cvImage hsv_frame, thresholded, thresholded2
	cvScalar hsv_min, hsv_max, hsv_min2, hsv_max2
	cvCapture capture;

	def InitBallTracking():
		globals size,  hsv_frame, thresholded, thresholded2, hsv_min, hsv_max, hsv_min2, hsv_max2, capture
		print "Initializing ball Tracking"
		size = cvSize(640, 480)
		hsv_frame = cvCreateImage(size, IPL_DEPTH_8U, 3)
		thresholded = cvCreateImage(size, IPL_DEPTH_8U, 1)
		thresholded2 = cvCreateImage(size, IPL_DEPTH_8U, 1)

		hsv_min = cvScalar(0, 50, 170, 0)
		hsv_max = cvScalar(10, 180, 256, 0)
		hsv_min2 = cvScalar(170, 50, 170, 0)
		hsv_max2 = cvScalar(256, 180, 256, 0)

		storage = cvCreateMemStorage(0)

		# start capturing form webcam
		capture = cvCreateCameraCapture(-1)

		if not capture:
			print "Could not open webcam"
			sys.exit(1)

		#CV windows
		cvNamedWindow( "Camera", CV_WINDOW_AUTOSIZE );

	def TrackBall(i):
		t = Thread(target=TrackBallThread, args=(i,))
		t.start()

	def TrackBallThread(num_of_balls):
		globals size,  hsv_frame, thresholded, thresholded2, hsv_min, hsv_max, hsv_min2, hsv_max2, capture
		while 1:
			# get a frame from the webcam
			frame = cvQueryFrame(capture)

			if frame is not None:

				# convert to HSV for color matching
				# as hue wraps around, we need to match it in 2 parts and OR together
				cvCvtColor(frame, hsv_frame, CV_BGR2HSV)
				cvInRangeS(hsv_frame, hsv_min, hsv_max, thresholded)
				cvInRangeS(hsv_frame, hsv_min2, hsv_max2, thresholded2)
				cvOr(thresholded, thresholded2, thresholded)

				# pre-smoothing improves Hough detector
				cvSmooth(thresholded, thresholded, CV_GAUSSIAN, 9, 9)
				circles = cvHoughCircles(thresholded, storage, CV_HOUGH_GRADIENT, 2, thresholded.height/4, 100, 40, 20, 200)

				# find largest circle
				maxRadius = 0
				x = 0
				y = 0
				found = False
				for i in range(circles.total):
					circle = circles[i]
					if circle[2] > maxRadius:
						found = True
						maxRadius = circle[2]
						x = circle[0]
						y = circle[1]

				cvShowImage( "Camera", frame );

				if found:
					print "ball detected at position:",x, ",", y, " with radius:", maxRadius

					if x > 420:
						# need to pan right
						servoPos += 5
						servoPos = min(140, servoPos)
						servo(2, servoPos)
					elif x < 220:
						servoPos -= 5
						servoPos = max(40, servoPos)
						servo(2, servoPos)
					print "servo position:", servoPos
				else:
					print "no ball"

קוד מקור בשפת C++

להלן קוד המקור בשפת C++. התוכנית מקבלת כקלט תזרים וידאו כלשהו (כאן ספציפית המימוש הוא ממצלמת וידאו), ומציגה אותו. לאחר מכן הוידאו מומר למרחב צבע HSV והתוצאה מוצגת. לאחר מכן מפולטר הצבע הכתום, ושוב התוצאה מוצגת בחלון נפרד. בשלב הסופי האוביקט מזוהה, ומסומן על גבי תזרים הוידאו המקורי.

/*****************************************************************************************
*  Name    : Fast object tracking using the OpenCV library                               *
*  Author  : Lior Chen <chen.lior@gmail.com>                                             *
*  Notice  : Copyright (c) Jun 2010, Lior Chen, All Rights Reserved                      *
*          :                                                                             *
*  Site    : http://www.lirtex.com                                                       *
*  WebPage : http://www.lirtex.com/robotics/fast-object-tracking-robot-computer-vision   *
*          :                                                                             *
*  Version : 1.0                                                                         *
*  Notes   : By default this code will open the first connected camera.                  *
*          : In order to change to another camera, change                                *
*          : CvCapture* capture = cvCaptureFromCAM( 0 ); to 1,2,3, etc.                  *
*          : Also, the code is currently configured to tracking RED objects.             *
*          : This can be changed by changing the hsv_min and hsv_max vectors             *
*          :                                                                             *
*  License : This program is free software: you can redistribute it and/or modify        *
*          : it under the terms of the GNU General Public License as published by        *
*          : the Free Software Foundation, either version 3 of the License, or           *
*          : (at your option) any later version.                                         *
*          :                                                                             *
*          : This program is distributed in the hope that it will be useful,             *
*          : but WITHOUT ANY WARRANTY; without even the implied warranty of              *
*          : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
*          : GNU General Public License for more details.                                *
*          :                                                                             *
*          : You should have received a copy of the GNU General Public License           *
*          : along with this program.  If not, see <http://www.gnu.org/licenses/>        *
******************************************************************************************/

#include <opencv/cvaux.h>
#include <opencv/highgui.h>
#include <opencv/cxcore.h>
#include <stdio.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>

int main(int argc, char* argv[])
{

    // Default capture size - 640x480
    CvSize size = cvSize(640,480);

    // Open capture device. 0 is /dev/video0, 1 is /dev/video1, etc.
    CvCapture* capture = cvCaptureFromCAM( 0 );
    if( !capture )
    {
            fprintf( stderr, "ERROR: capture is NULL \n" );
            getchar();
            return -1;
    }

    // Create a window in which the captured images will be presented
    cvNamedWindow( "Camera", CV_WINDOW_AUTOSIZE );
    cvNamedWindow( "HSV", CV_WINDOW_AUTOSIZE );
    cvNamedWindow( "EdgeDetection", CV_WINDOW_AUTOSIZE );

    // Detect a red ball
    CvScalar hsv_min = cvScalar(150, 84, 130, 0);
    CvScalar hsv_max = cvScalar(358, 256, 255, 0);

    IplImage *  hsv_frame    = cvCreateImage(size, IPL_DEPTH_8U, 3);
    IplImage*  thresholded   = cvCreateImage(size, IPL_DEPTH_8U, 1);

    while( 1 )
    {
        // Get one frame
        IplImage* frame = cvQueryFrame( capture );
        if( !frame )
        {
                fprintf( stderr, "ERROR: frame is null...\n" );
                getchar();
                break;
        }

        // Covert color space to HSV as it is much easier to filter colors in the HSV color-space.
        cvCvtColor(frame, hsv_frame, CV_BGR2HSV);
        // Filter out colors which are out of range.
        cvInRangeS(hsv_frame, hsv_min, hsv_max, thresholded);

        // Memory for hough circles
        CvMemStorage* storage = cvCreateMemStorage(0);
        // hough detector works better with some smoothing of the image
        cvSmooth( thresholded, thresholded, CV_GAUSSIAN, 9, 9 );
        CvSeq* circles = cvHoughCircles(thresholded, storage, CV_HOUGH_GRADIENT, 2,
                                        thresholded->height/4, 100, 50, 10, 400);

        for (int i = 0; i < circles->total; i++)
        {
            float* p = (float*)cvGetSeqElem( circles, i );
            printf("Ball! x=%f y=%f r=%f\n\r",p[0],p[1],p[2] );
            cvCircle( frame, cvPoint(cvRound(p[0]),cvRound(p[1])),
                                    3, CV_RGB(0,255,0), -1, 8, 0 );
            cvCircle( frame, cvPoint(cvRound(p[0]),cvRound(p[1])),
                                    cvRound(p[2]), CV_RGB(255,0,0), 3, 8, 0 );
        }

        cvShowImage( "Camera", frame ); // Original stream with detected ball overlay
        cvShowImage( "HSV", hsv_frame); // Original stream in the HSV color space
        cvShowImage( "After Color Filtering", thresholded ); // The stream after color filtering

        cvReleaseMemStorage(&storage);

        // Do not release the frame!

        //If ESC key pressed, Key=0x10001B under OpenCV 0.9.7(linux version),
        //remove higher bits using AND operator
        if( (cvWaitKey(10) & 255) == 27 ) break;
    }

     // Release the capture device housekeeping
     cvReleaseCapture( &capture );
     cvDestroyWindow( "mywindow" );
     return 0;
   }

2 Responses to "ראייה ממוחשבת – זיהוי ומעקב מהיר אחר אובייקט"

  • גלעד says:
Leave a Comment

No results