Ubuntu16.04LTSのopenFrameworksでPS3EyeCameraを入力にして、ArUco3.0.4を動かす。(その1)カメラキャリブレーション編
ArUcoがVer.3でかなり高速に動くようになったらしい、ということはちょっと前に調べました。ところが、実験環境に使ったIntelEuclidのRGBカメラではフレームレートが上がらず、その実力はよくわかりませんでした。そこで、今回は部屋の隅に転がっていたPS3EyeCameraが、320x240の画像サイズならば170fpsを稼げて、Linuxならドライバーも用意されていることを思い出し、これを使ってArUco3を動かしてみたいと思います。
openFrameworksでArUco
もうすでに誰かが環境を整えているんじゃないの?と思い調べてみると、ご本家arturocさんのofxArUcoがしっかりあります。つい最近のタイムスタンプもあり、しっかりメンテナンスもされているのですが、ArUcoのバージョンは1.3.0のママのようです。
github.com
でも、多少話題になってるぐらいだから、ofxArUco3もあるんじゃないの?と思い調べてみると、elliotwoodsさんのフォークっぽいものが見つかります。
github.com
ところが、中を見ると様子が多少おかしくて
- this addon doesn't do anything but provide libs to use directly
なんてことが書かれています。どういうことか考えてみると、ArUcoはもともとopenCVをベースにしていますので、openFrameworks用のインターフェースがあった方が便利なのかもしれないが、別になくても困らないから、openFrameworksでライブラリをリンクするためだけのaddonを用意しておけば十分じゃね?と言っているんだと解釈しました。なので、これに倣います。
ofxAruco3の準備
1. ArUco3のソースのダウンロード
https://sourceforge.net/projects/aruco/files/
から最新版をダウンロードして解凍します。
2. ofxAruco3ディレクトリの作成
まず、openFrameworks/addonsの下に移動して、ofxAruco3という名前の空のディレクトリを作ります。次に、作法に則って、その下にsrcとlibsフォルダを作ります。
cd ~/workspace/openFrameworks/addons
mkdir ofxAruco3
cd ofxAruco3
mkdir src
mkdir libs
3. ソースのコピー
libsの中にArUcoのSrcをまるまるコピーします。
cd libs
cp -r ~/workspace/aruco304/src/* ./
これだけで完了です。あとは、自分のappsのaddons.makeにofxAruco3を追加すれば、openFrameworksと一緒にビルドされるようになります。
exampleとしてaruco_calibration.cppをopenFrameworksで動かしてみる
ofxPS3EyeGrabberの準備
ここでは、手軽なハイスピードカメラとしてPS3Eyeを使ってみます。画像取り込みインターフェースは,ofxVideoGrabberと同等なので、ちょっと書き換えるだけで普通のUSBカメラでも問題なく動きます。
github.com
cd ~/workspace/openFramewroks/addons
git clone https://github.com/bakercp/ofxPS3EyeGrabber
addons.makeに依存ライブラリを追加します。適当なプロジェクトフォルダをコピーして、プロジェクト環境を準備しておきます。addons.makeを編集して、
cd ~/workspace/openFrameworks/apps/vscode_of/myAruco3-calibration
nano addons.make
ofxKinect
ofxOpenCv
ofxPS3EyeGrabber
ofxAruco3
のようにofxAruco3も含めて4つ追加しておきます。
ofxAruco3-calibrationのソース
main.cpp, ofApp.h, ofApp.cppを用意します。また、calibrator.cppとcalibrator.hをArUco3のutils_calibrationからコピーして持ってきます。
main.cpp
#include "ofApp.h" int main(int argc, char* argv[]) { ofGLWindowSettings settings; settings.width = 640; settings.height = 480; ofCreateWindow(settings); return ofRunApp(std::make_shared<ofApp>(argc, argv)); }
ofApp.h
#pragma once #include "ofMain.h" #include "aruco.h" #include "calibrator.h" class ofApp: public ofBaseApp { public: ofApp(int argc, char* argv[]){} void setup(); void update(); void draw(); void keyPressed(int key); ofVideoGrabber grabber; cv::Mat TheInputImage, TheInputImageCopy; aruco::CameraParameters TheCameraParameters; aruco::MarkerDetector TheMarkerDetector; aruco::Calibrator calibrator; std::vector<aruco::Marker> detected_markers; aruco::CameraParameters camp; // camera parameters estimated int width, height, fps; bool saveImages; string saveFile; float realsize; };
ofApp.cpp
// // Copyright (c) 2014 Christopher Baker <https://christopherbaker.net> // // SPDX-License-Identifier: MIT // /** Copyright 2017 Rafael Muñoz Salinas. All rights reserved. Redistribution and use in source and binary forms, with or without modification$ permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, th$ conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice,$ of conditions and the following disclaimer in the documentation and/or ot$ provided with the distribution. THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR $ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTAB$ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz$ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY$ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE$ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUS$ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUD$ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EV$ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those$ authors and should not be interpreted as representing official policies, either$ or implied, of Rafael Muñoz Salinas. */ #include "ofApp.h" #include "ofxPS3EyeGrabber.h" #include <opencv2/highgui/highgui.hpp> void ofApp::setup() { // ofSetFrameRate(0); // ofSetVerticalSync(0); // Camera Settings width = 320; // 320x240, 640x480 height = 240; fps = 187; // 187Hz, 75Hz // Aruco Settings realsize = 0.038; // [m] saveImages = 1; saveFile = "calib.yml"; // ofxPS3EyeGrabber settings. grabber.setGrabber(std::make_shared<ofxPS3EyeGrabber>()); grabber.setPixelFormat(OF_PIXELS_BGR); //OF_PIXELS_RGB, BGR, GRAY, NATIVE grabber.setDesiredFrameRate(fps); grabber.setup(width, height); grabber.getGrabber<ofxPS3EyeGrabber>()->setAutogain(true); grabber.getGrabber<ofxPS3EyeGrabber>()->setAutoWhiteBalance(true); // ofxAruco3 settings // set specific parameters for this configuration TheMarkerDetector.setDictionary( "ARUCO_MIP_36h12"); TheMarkerDetector.setDetectionMode(aruco::DM_VIDEO_FAST,0.05); // read first image to get the dimensions grabber.update(); ofPixels pix = grabber.getPixels(); TheInputImage = cv::Mat(pix.getHeight(), pix.getWidth(), CV_8UC3, pix.getData()); //configure the calibrator calibrator.setParams(cv::Size(width, height), realsize); } void ofApp::update() { grabber.update(); ofPixels pix = grabber.getPixels(); TheInputImage = cv::Mat(pix.getHeight(), pix.getWidth(), CV_8UC3, pix.getData()); if(!TheInputImage.empty()){ TheInputImageCopy = TheInputImage.clone(); } // detect and print detected_markers.clear(); detected_markers = TheMarkerDetector.detect(TheInputImage); if(!TheInputImageCopy.empty()){ for (auto m: detected_markers) m.draw(TheInputImageCopy, cv::Scalar(0, 0, 255), 1); } } void ofApp::draw() { ofBackground(0); ofSetColor(255); // print markers from the board if(!TheInputImageCopy.empty()){ cv::imshow("draw", TheInputImageCopy); cv::waitKey(1); } grabber.draw(0, 0); std::stringstream ss; ss << " App FPS: " << ofGetFrameRate() << std::endl; ss << " Cam FPS: " << grabber.getGrabber<ofxPS3EyeGrabber>()->getFPS() << std::endl; ss << "Real FPS: " << grabber.getGrabber<ofxPS3EyeGrabber>()->getActualFPS() << std::endl; ss << " id: 0x" << ofToHex(grabber.getGrabber<ofxPS3EyeGrabber>()->getDeviceId()); ss << endl; ss << "'a' add current image for calibration" << std::endl; ss << calibrator.getInfo() << std::endl; ofDrawBitmapStringHighlight(ss.str(), ofPoint(10, 315)); } void ofApp::keyPressed(int key) { if (key == 'a'){ calibrator.addView(detected_markers); if (saveImages){ string number=std::to_string(calibrator.getNumberOfViews()-1); while(number.size()!=5) number="0"+number; cv::imwrite("calib-"+number+".png",TheInputImage); } } if (key == 27) { aruco::CameraParameters camp; if ( calibrator.getCalibrationResults(camp)){ camp.saveToFile(saveFile); cout<<"results saved to " << saveFile << endl; } else cerr<<"Could not obtain calibration"<<endl; cout<<"Final error="<<calibrator.getReprjError()<<endl; } }