OpenCVをロボットビジョンに利用する

カメラなどがとらえた画像をコンピュータで処理し、認識や識別などを行う技術/研究は、コンピュータビジョンと呼ばれています。

コンピュータビジョンを利用するためのライブラリとしてOpenCVがあります。

ロボットが対象や周囲の環境を認識したりするロボットビジョンには、コンピュータビジョンの技術が使われています。 ロボットビジョンはコンピュータビジョンの一部の分野と考えてよさそうです。

ROSには、OpenCVを利用してロボットビジョンを実現するためのパッケージが用意されています。

cv_bridgeでOpenCVと画像をやりとり

cv_bridgeパッケージを利用することで、ROSとOpenCV間で画像をやりとりすることができます。

例えば、ROSのカメラノードがpublishする画像をcv_bridgeを通してOpenCVに渡し、OpenCVで画像認識したり、OpenCVで画像処理した画像をROSに戻すことも可能です。

画像処理・認識はOpenCVで行う

画像処理や認識などを行うのはOpenCVです。

そのため、cv_bridgeを通してROSとOpenCVで画像やりとりできるようになれば、あとは「欲しいロボットビジョンの機能をOpenCVを利用してどのように実現するか?」という問題になります。

結果としてロボットビジョン周りの開発は、OpenCVプログラミングが中心になります。

ptcam_visionパッケージを作って実験する

パンチルトカメラを使いながら、ロボットビジョンの実験を行い、それをptcam_visionパッケージとしてまとめていきたいと思います。

ptcam_visionは、GitHubの私のページに置いておきますので、興味のある方はご覧ください。

ゼロからパッケージを作るのではなく、rbx1_visionパッケージに便利なPythonモジュールなどがあるので、これをベースに手を加えていくつもりです。

OpenCVでカメラ画像を処理する

まず手始めに、cv_bridgeを通してOpenCVにカメラ画像を渡し、OpenCVで画像処理して表示します。

必要なものはptcam_visionパッケージに入れてあるので、詳しい説明は省略します。 次のとおり、カメラノード、image_viewそれに, cv_bridgeのデモを起動します。

$ roslaunch ptcam_vision usb_cam.launch
$ rosrun image_view image_view image:=/camera/rgb/image_raw
$ roslaunch ptcam_vision cv_bridge_demo.launch
		

cv_bridge_demo.launchによって、cv_bridge_demo.pyが起動されますが、ここでOpenCVによる画像処理が行われます。cv_bridge_demo.pyの重要部分を抜き出すと次の通りです。

		・・・略・・・
        self.image_sub = rospy.Subscriber("input_rgb_image", Image, self.image_callback, queue_size=1)

		・・・略・・・

    def image_callback(self, ros_image):
        # Use cv_bridge() to convert the ROS image to OpenCV format
        try:
            frame = self.bridge.imgmsg_to_cv2(ros_image, "bgr8")
        except CvBridgeError, e:
            print e
        
        # Convert the image to a numpy array since most cv2 functions
        # require numpy arrays.
        frame = np.array(frame, dtype=np.uint8)
        
        # Process the frame using the process_image() function
        display_image = self.process_image(frame)
                       
        # Display the image.
        cv2.imshow(self.node_name, display_image)

		・・・略・・・

    def process_image(self, frame):
        # Convert to greyscale
        grey = cv2.cvtColor(frame, cv.CV_BGR2GRAY)
        
        # Blur the image
        grey = cv2.blur(grey, (7, 7))
        
        # Compute edges using the Canny edge filter
        edges = cv2.Canny(grey, 15.0, 30.0)
        
        return edges

		・・・略・・・
		

"input_rgb_image"トピックをsubscriberとして登録し、そのコールバック関数としてimage_callback()を指定しています。

画像フレーム、すなわち"input_rgb_image"トピックがくると、image_callback()が呼ばれ、その中のbridge.imgmsg_to_cv2()でOpenCVの画像に変換します。

その画像をprocess_image()で画像処理します。この中で、cv2.blur()などOppenCVの関数が呼ばれていることがわかります。 blurフィルタ(ぼかし)とcannyフィルタ(エッジ検出)が行われます。

処理後の画像、display_imageは、cv2.imshow()でWindowに表示されます。この画像表示もOpenCVによって行われます。

これで、cv_bridgeを使ってOpenCVへ画像を渡す仕組みもわかりました。また、動作確認もできました。

便利なクラスros2opencv2を使う

OpenCVへ画像を渡すことはもちろん、画像の一部をマウスで選択したり、また文字列を画像Window上に表示したりといったことは、ロボットビジョンの実験には必要となる機能です。

これらの機能をクラスとしてまとめたものが、ros2opencv2.pyにあります。

このクラスを継承して機能追加しながら色々な実験をしていきたいと思います。

ros2opencv2の動作を確認します。次のようにカメラノードとros2opencv2のノードを起動します。

$ roslaunch ptcam_vision usb_cam.launch
$ roslaunch ptcam_vision ros2opencv2.launch
		

カメラ画像が表示され、左上にフレームレートなどが表示されます。マウスで画像をドラッグすると、矩形で領域選択されます。

選択された領域は、ROI(Region Of Interest)といって、着目している部分を意味します。このROIは、マウスを使ってOpenCV上で設定したものですが、この情報はros2opencv2によってトピックとしてpublishされます。

$ rostopic echo /roi
x_offset: 252
y_offset: 120
height: 218
width: 168
do_rectify: False
--
		

このように、ROIをROS側で受け取ることることができます。例えばOpenCVで画像中の顔を検出し、その顔の領域をROIとして渡せば、画像のどこに顔が検出されたのかをROS側で知ることができる、といった使い方ができます。

これで、ロボットビジョンの実験をするための準備は整いました。