OpenCVSharp を使った画像の切り貼り
OpenCVSharp の導入と,画像の切り貼りについて,調べたのでメモ.
OpenCVSharp の導入
1. Visual Studio 2013 Community のインストール
個人用なら完全タダで Professional と同等機能を提供!素晴らしい!インストールは素直に下記のリンクから.
2014-Nov 12 Release Notes | Visual Studio
Download Visual Studio Community 2013 というリンクに飛べば,インストーラがダウンロードされる.
2. C# プロジェクトの作成
Visual Studio 起動後,「ファイル」->「新規作成」->「プロジェクト」->「Visual C#」->「コンソールアプリケーション」とたどり,適当な名前を入力して OK ボタンクリック.
3. OpenCVSharp の導入.
「ツール」->「Nu Get パッケージマネージャー」->「ソリューションの Nu Get パッケージの管理」->「オンライン」とたどり,検索ボックスに "OpenCVSharp" と入力.OpenCVSharp のパッケージが出てくるので,インストール.ってか,これだけで導入できるなんて,すごい楽ちん.
OpenCVSharp を使った画像の切り貼り
複数の画像を集めて,切り貼りして一枚の画像にするという作業が必要になったんだけど,直接画素をいじって編集するというのもなんだかスマートじゃないと思ったのでやり方を調べてみた.やり方としてはそんなに複雑でなく,SetROI 関数を使って対象となるエリアを限定してあげたあとに,Cv.Copy でコピーしてあげれば完了だった.ただ,注意点が一つあり,Cv.Copy やその他の ROI の設定が内部で用いられる関数を呼ぶときには二つの画像の ROI が完全に一致していないといけない.今回の場合だと, targetImage の ROI とコピー元となる whiteImage, grayImage が該当する.以下,簡単にコードの内容をチェック.
1. イメージの生成と画素情報の設定.
恥ずかしいことに,画面全面で画素情報を設定する方法をしらず,危うく for ループで設定するところだった.こんな時には Cv.Set 関数が役立つ.
// Target Image.
targetImage = new IplImage(new CvSize(500, 500), BitDepth.U8, 1);
// Set the iamge completely black.
Cv.Set(targetImage, new CvScalar(0));
2. ROI の設定と,コピー
コピーしたい領域を ROI として指定する.そのあとに,Cv.Copy を呼べば OK.SetROI した後の ResetROI のコールを忘れずに!
targetImage.SetROI(new CvRect(100, 100, 100, 100));
Cv.Copy(whiteImage, targetImage);
targetImage.ResetROI();
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using OpenCvSharp; namespace OpenCVSharpExample { class Program { public static IplImage targetImage; public static IplImage whiteImage; public static IplImage grayImage; static void Main(string[] args) { // 1. Image Instance Generation. { // Target Image. targetImage = new IplImage(new CvSize(500, 500), BitDepth.U8, 1); // Set the iamge completely black. Cv.Set(targetImage, new CvScalar(0)); whiteImage = new IplImage(new CvSize(100, 100), BitDepth.U8, 1); // Set the iamge completely black. Cv.Set(whiteImage, new CvScalar(255)); grayImage = new IplImage(new CvSize(100, 100), BitDepth.U8, 1); // Set the iamge completely black. Cv.Set(grayImage, new CvScalar(80)); } // 2. Set ROI and Copy { targetImage.SetROI(new CvRect(100, 100, 100, 100)); Cv.Copy(whiteImage, targetImage); targetImage.ResetROI(); targetImage.SetROI(new CvRect(300, 300, 100, 100)); Cv.Copy(grayImage, targetImage); targetImage.ResetROI(); } Cv.ShowImage("Target Image", targetImage); Cv.WaitKey(0); } } }
以下,実行結果
OpenCV 2.4 を用いた USB カメラの取り込み2
前回のエントリで OpenCV2.4 を使って USB カメラの取り込みを実施したけど, Pixel Format がうまくコントロールできなかったのでこのエントリでは V4L2 の API からカメラの設定を変更するコードを作る.
参考にしたのは下記のホームページ
Capture images using V4L2 on Linux — Jay Rambhia
完成形のコードは最後に乗せておくとして, V4L2 でビデオからデータをキャプチャするまでには大きく分けて下記の 10 個くらいのステップをふまないといけなかった.
1. Video デバイスをオープンする.
当然のことながら,まずはデバイスファイルをオープンする必要がある.
コード該当部
// 1. Open Video Device. int fd; fd = open("/dev/videoLeftImgSrc", O_RDWR, 0); if (fd == -1) { std::cout << "Failed to open video device." << std::endl; return 1; }
2. デバイス情報をデバイスから抜き出す.
次に,どうやらデバイス情報をクエリするのがお作法となっているみたい.確かに,素性の分からないカメラを使うこともあるだろうから,クエリして事前情報を掴んで置くことは必要なのかも.ただ,クエリ情報をその後のコードで使わないなら, v4l2-ctl コマンドで調べるほうが早いかも.
コード該当部
// 2. Querying video capabilities. struct v4l2_capability caps; memset(&caps, 0, sizeof(caps)); if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &caps)) { std::cout << "Failed to query capabilities." << std::endl; return 1; } std::cout << "bus_info : " << caps.bus_info << std::endl; std::cout << "card : " << caps.card << std::endl; std::cout << "driver : " << caps.driver << std::endl; std::cout << "version : " << caps.version << std::endl;
3. Video に対して,フォーマットのリクエストをする.
つぎに,やり取りしたいビデオのフォーマットをデバイスに対してリクエストする.今回使ったカメラだと,例えばピクセルフォーマットは下記の 3 つが対応可能であった.
- V4L2_PIX_FMT_SGRBG8
- V4L2_PIX_FMT_GREY
- V4L2_PIX_FMT_Y16
今回はカラー画像を使いたかったので,ベイヤーパターンの V4L2_PIX_FMT_SGRBG8 を指定した.また,最大サイズ 1280 x 960 を指定.
コード該当部
// 3. Format Specification. { struct v4l2_format fmt; memset(&(fmt), 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 1280; fmt.fmt.pix.height = 960; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGRBG8; //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; fmt.fmt.pix.field = V4L2_FIELD_NONE; if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) { std::cout << "Failed to set pixel format." << std::endl; return 1; } }
4. Video に対してバッファのリクエストをする.
デバイス内部で保持されるバッファのリクエストをする.memory では,バッファの I/O を指定.count ではバッファ数, type は Stream/Buffer の種類を指定する...と書いてみたものの,このあたりの詳しい話は自分もちゃんとは理解できず...たぶん下記のままでそんなに問題無いかと.詳しい説明は下記をどーぞ!
ioctl VIDIOC_REQBUFS
コード該当部
// 4. Request Buffer { struct v4l2_requestbuffers req; memset(&(req), 0, sizeof(req)); req.count = 1; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { std::cout << "Failed to request buffer." << std::endl; return 1; } }
5. Video のバッファ情報を抜き出す.
4 のステップでバッファリクエストをしたが,ここではそのリクエストに基づいて作成されたバッファの情報を抜き出す.今回のケースでは, 4 のステップで一つのバッファしかリクエストしなかったので index = 0 としているが,複数個のバッファをリクエストした場合,複数個のバッファに対して複数回クエリをする形になる.最後の mmap という関数がバッファのアドレスを返すので,そのアドレスを用意したアドレス変数 "buffer" に渡してやって,アプリの側ではこのポインタの先を見に行くことでキャプチャが見れるようになる.
コード該当部
// 5. Query Buffer struct v4l2_buffer buf; memset(&(buf), 0, sizeof(buf)); { buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; //buf.index = bufferindex; buf.index = 0; if(-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) { std::cout << "Failed to query buffer." << std::endl; return 1; } std::cout << "buf.length : " << buf.length << std::endl; std::cout << "buf.m.offset : " << buf.m.offset << std::endl; buffer = (unsigned char*)mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); }
6. Streaming を開始する.
ついに Streaming を開始できるとこまで来た!やった!
コード該当部
// 6. Start Streaming { if(-1 == xioctl(fd, VIDIOC_STREAMON, &buf.type)) { std::cout << "Start Capture" << std::endl; return 1; } }
7. Buffer をキューにつなぐ, 8. Buffer をキューから外す
下記, while ループが閉じてない中途半端なコードの一部分になってしまったが,,, 6 のステップで Streaming を開始したが,実際にはバッファを渡してあげないとカメラからデータを取ることはできない.流れとしては下記のようになると思う.
- VIDIOC_QBUF で使用可能なバッファをカメラに伝えてあげる.(ストリームを開始した直後はカメラが使用可能なバッファがひとつもない状態なので, while 文の上で index=0 を指定してその後の VIDIOC_QBUF でこのバッファが使用可能であることを伝えている.複数個のバッファをリクエストした場合は,最初のステップとして利用可能なすべてのバッファをカメラに伝える必要がある.)
- VIDIOC_DQBUF でカメラがデータを詰めたバッファを取得する.この時,buf.index にはカメラによって該当の index 番号が渡されているので,これを辿ってデータを取得する.
- アプリ側でデータを吸い上げ完了したら,不要になったバッファ (index = buf.index) をカメラに返してあげる.これには再び VIDIOC_QBUF を用いる.
コード該当部
struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; while (true) { // 7. Capture Image { // Connect buffer to queue for next capture. if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { std::cout << "VIDIOC_QBUF" << std::endl; } fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); struct timeval tv = {0}; tv.tv_sec = 2; int r = select(fd+1, &fds, NULL, NULL, &tv); if(-1 == r) { std::cout << "Waiting for Frame" << std::endl; return 1; } memset(&(buf), 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if(-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { std::cout << "Retrieving Frame" << std::endl; return 1; } } ....
8. 適切なイメージフォーマットに変換する.
デバイスからのデータは 8bit Bayer Pattern なので,OpenCV 提供の cvColor 関数を使って RGB 情報に変換する.
コード該当部
// 8. Store Image in OpenCV Data Type { memcpy(bayerRaw.data, buffer, 1280 * 960); cv::cvtColor(bayerRaw, color, CV_BayerGB2BGR); }
9. 表示!
ここは説明不要かと.
コード該当部
// 9. Display Image { cv::imshow("edges", color); if (-1 != cv::waitKey(1000)) { break; } }
10. Streaming を終了する.
最後にカメラにストリーミングの終了を宣言して,終了!
コード該当部
// 10. Turn off streaming. if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &buf.type)) { std::cout << "VIDIOC_STREAMOFF" << std::endl; }
とりあえず作成したコードは下記の通り.実行するところまではできたので,雛形としては使ってもらえるかも.
#include <fcntl.h> #include <errno.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <linux/videodev2.h> #include "opencv2/opencv.hpp" static int xioctl(int fd, int request, void *arg) { int r; do { r = ioctl (fd, request, arg); if (request == VIDIOC_DQBUF) { std::cout << "r : " << r << std::endl; } } while (-1 == r && EINTR == errno); return r; } int main() { unsigned char *buffer; // 1. Open Video Device. int fd; fd = open("/dev/videoLeftImgSrc", O_RDWR, 0); if (fd == -1) { std::cout << "Failed to open video device." << std::endl; return 1; } // 2. Querying video capabilities. struct v4l2_capability caps; memset(&caps, 0, sizeof(caps)); if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &caps)) { std::cout << "Failed to query capabilities." << std::endl; return 1; } std::cout << "bus_info : " << caps.bus_info << std::endl; std::cout << "card : " << caps.card << std::endl; std::cout << "driver : " << caps.driver << std::endl; std::cout << "version : " << caps.version << std::endl; // 3. Format Specification. { struct v4l2_format fmt; memset(&(fmt), 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 1280; fmt.fmt.pix.height = 960; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGRBG8; //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; fmt.fmt.pix.field = V4L2_FIELD_NONE; if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) { std::cout << "Failed to set pixel format." << std::endl; return 1; } } // 4. Request Buffer { struct v4l2_requestbuffers req; memset(&(req), 0, sizeof(req)); req.count = 1; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { std::cout << "Failed to request buffer." << std::endl; return 1; } } // 5. Query Buffer { struct v4l2_buffer buf; memset(&(buf), 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; //buf.index = bufferindex; buf.index = 0; if(-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) { std::cout << "Failed to query buffer." << std::endl; return 1; } std::cout << "buf.length : " << buf.length << std::endl; std::cout << "buf.m.offset : " << buf.m.offset << std::endl; buffer = (unsigned char*)mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); } // 6. Start Streaming { struct v4l2_buffer buf; memset(&(buf), 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(-1 == xioctl(fd, VIDIOC_STREAMON, &buf.type)) { std::cout << "Start Capture" << std::endl; return 1; } } cv::namedWindow("edges",1); cv::Mat bayerRaw(960, 1280, CV_8UC1); cv::Mat color(960, 1, CV_8UC3); struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; while (true) { // 7. Capture Image { // Connect buffer to queue for next capture. if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { std::cout << "VIDIOC_QBUF" << std::endl; } fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); struct timeval tv = {0}; tv.tv_sec = 2; int r = select(fd+1, &fds, NULL, NULL, &tv); if(-1 == r) { std::cout << "Waiting for Frame" << std::endl; return 1; } memset(&(buf), 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if(-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { std::cout << "Retrieving Frame" << std::endl; return 1; } } // 8. Store Image in OpenCV Data Type { memcpy(bayerRaw.data, buffer, 1280 * 960); cv::cvtColor(bayerRaw, color, CV_BayerGB2BGR); } // 9. Display Image { cv::imshow("edges", color); if (-1 != cv::waitKey(1000)) { break; } } } // 10. Turn off streaming. if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &buf.type)) { std::cout << "VIDIOC_STREAMOFF" << std::endl; } return 0; }
下記の写真が実際に上記プログラムを実行してとった写真.(洗濯物とか写ってて,生活感出ててすみません(笑))
とりあえず,ようやくカメラとOpenCVがつながったということで,バンザイ!
OpenCV 2.4 を用いた USB カメラの取り込み1
実装前にいろいろと試す用途で,Eclipse の OpenCV 用プロジェクトを作成する.ここはそのメモ.
Eclipse で C++ プロジェクトを作成する.
Eclipse を開いてからの...
- "Project Name" を指定 -> "Location" を決定 -> "Executable" を選択 -> "Hello World C++ Project" を選択 -> "Linux GCC" を選択
OpenCV のインクルードパス,ライブラリパスを通す.
作成したプロジェクトを選択してからの...
- "Project" 右クリック -> "Properties" -> "C/C++ General" を選択 -> "Paths and Symbols" を選択.
- インクルードパスの追加
環境設定で構築した OpenCV の include フォルダのパスを追加.
- ライブラリパスの追加
環境設定で構築した OpenCV の lib フォルダのパスを追加
- ライブラリの追加
環境設定で構築した OpenCV の lib フォルダ内にあるライブラリを追加.今回はどれを使うかはっきりしなかったので,とりあえず全部追加.ここで,ライブラリ名は先頭の "lib" と末尾の ".so" を除いた形で追加する.
以上, "Apply" を押下して, "OK" で再ビルドが走る.
OpenCV を使っての USB カメラの取り込み.
下記のサンプルコードを参考に作成した下記のコードをコンパイルして,カメラ画像が表示されることを確認.
Reading and Writing Images and Video — OpenCV 2.4.13.0 documentation
#include <iostream> #include "opencv2/opencv.hpp" using namespace std; using namespace cv; int main(int, char**) { const string fileName = "/dev/videoLeftImgSrc"; //VideoCapture cap(fileName); // open the default camera VideoCapture cap(0); // open the default camera if(!cap.isOpened()) { // check if we succeeded cout << "Can not open device." << endl; return -1; } Mat edges; namedWindow("edges",1); for(;;) { Mat frame; cap >> frame; // get a new frame from camera cvtColor(frame, edges, CV_BGR2GRAY); //cvtColor(frame, edges, CV_YUV2RGB_YUYV); //cvtColor(frame, edges, CV_BayerBG2BGR); //GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5); //Canny(edges, edges, 0, 30, 3); imshow("edges", edges); if(waitKey(30) >= 0) break; } // the camera will be deinitialized automatically in VideoCapture destructor return 0; }
下記実行結果.どうやら Pixel Format がおかしいみたいなので,V4L2 の API を使って調整する必要があるみたい.とりあえず開発環境が整ってバンザイということで,それは次回に!
環境設定 - OpenCV 2.4 & OpenCV 3.1 & OpenCV 2.2
OpenCV のビルドとインストール
自分の環境には ROS Kinetic Kame を入れているので,すでに opencv3 のライブラリが一部インストールされている.ただ,開発を進めていくうえでバイナリパッケージのみでなくソースを見たくなる時があるので OpenCV2.4 と OpenCV3.1 をソースビルドしてローカルにインストールした.以下にその手順をまとめておく.(Graph Cut Stereo を試してみたかったので,あとから OpenCV2.2 もインストール.)
OpenCV ダウンロード
以下,ダウンロード先リンク.下記からほしいバージョンを選んでダウンロードする.
github.com
OpenCV Non Free モジュールのダウンロード
一部新しいライブラリや特許で抑えられている特徴量の計算ライブラリ等は別のリポジトリで管理されているみたい.自分は必要になるので下記のリンク先からダウンロードした.
github.com
自分は下記の 3 つのバージョンをダウンロードしてきた.
パッケージの解凍
$ cd ~/workspace/reps/ $ unzip opencv-2.4.13.zip $ unzip opencv-3.1.0.zip $ unzip opencv_contrib-master.zip $ unzip opencv-2.2.zip
cmake の実行.(OpenCV2.2 は古いので,コンパイルを通すために変更が必要でした.詳しくは本エントリの最後を参照のこと.)
$ cd ~/workspace/reps/opencv-2.4.13 $ mkdir build install $ cd build $ cmake -D CMAKE_INSTALL_PREFIX=/home/koichi/workspace/reps/opencv-2.4.13/install -DBUILD_EXAMPLES=ON -DINSTALL_C_EXAMPLES=ON -DWITH_OPENGL=ON -DWITH_V4L=ON -DWITH_QT=ON .. $ $ cd ~/workspace/reps/opencv-3.1.0 $ mkdir build install $ cd build $ cmake -DCMAKE_INSTALL_PREFIX=../install -DBUILD_EXAMPLES=ON -DINSTALL_C_EXAMPLES=ON -DWITH_OPENGL=ON -DWITH_V4L=ON -DWITH_GSTREAMER=ON -DWITH_QT=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DWITH_CUDA=YES -DCUDA_GENERATION=Auto -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules/ ../opencv make install -j8 $ $ cd ~/workspace/reps/opencv-2.2 $ mkdir build install $ cmake -D CMAKE_INSTALL_PREFIX=/home/koichi/workspace/reps/opencv-2.2/install -DBUILD_EXAMPLES=ON -DINSTALL_C_EXAMPLES=ON -DWITH_OPENGL=ON -DWITH_V4L=ON -DWITH_QT=ON -DWITH_FFMPEG=OFF .. $
ビルド & インストールの実行.
$ cd ~/workspace/reps/opencv-2.4.13/build $ make install $ $ cd ~/workspace/reps/opencv-3.1.0/build $ make install $ $ cd ~/workspace/reps/opencv-2.2/build $ make install
上記のようにインストールすれば, "install/share/OpenCV" 配下に CMake の config ファイルが作成されるので, CMake を使ってビルドする場合はこの Config ファイルを指定してビルドすれば必要なパス等を読み込んでくれる.
OpenCV2.2 の変更点
-
- 1. opencv-2.2/cvconfig.h.cmake
L22, L23 に下記を追記.
/* V4L/V4L2 capturing support via libv4l */ #cmakedefine HAVE_LIBV4L
-
- 2. opencv-2.2/modules/highgui/src/cap.cpp
L174 の下記の記述を削除し,代わりにその下の記述を追記する.
#if defined (HAVE_CAMV4L) || defined (HAVE_CAMV4L2)
#if defined HAVE_LIBV4L || (defined (HAVE_CAMV4L) && defined (HAVE_CAMV4L2))
-
- 3. opencv-2.2/modules/highgui/src/cap.cpp
削除 L227
#if !defined WIN32 && defined HAVE_CAMV4L && defined HAVE_CAMV4L2
追記 L227
#if !defined WIN32 && defined HAVE_LIBV4L
追記 L244
#ifdef HAVE_CAMV4L
追記 L246, L247
#endif #ifdef HAVE_CAMV4L2
追記 L249
#endif
-
- 4. opencv-2.2/modules/highgui/src/window_QT.cpp
削除 L308
usleep(1000);
追記 L308
struct timespec req; req.tv_sec = (time_t)0; req.tv_nsec = 1000000; nanosleep(&req, NULL);
-
- 5. opencv-2.2/modules/highgui/src/cap_v4l.cpp
削除 L217
#include <linux/videodev.h>
追記 L217
#include <libv4l1-videodev.h>
カメラキャリブレーション
前回の投稿から一週間近くたってしまったが,今日はカメラのキャリブレーションを実施しようと思う.
V4L2 を使ったカメラコントロール
キャリブレーションをする前に,カメラの設定をしないといけない.ピント,絞りやゲインの設定などである.設定可能なコントロールのリストを下記のコマンドで調べる.
hoge@hogepc:~$ v4l2-ctl -d /dev/videoLeftImgSrc --list-ctrls-menus brightness (int) : min=0 max=4095 step=1 default=16 value=16 gain (int) : min=32 max=2047 step=1 default=32 value=32 exposure_absolute (int) : min=1 max=10000 step=1 default=333 value=10
上記パラメータで撮影した画像が下記の写真.全体的に暗く,露光時間を上げる必要がある.
露光時間を上げる設定を実施.
hoge@hogepc:~$ v4l2-ctl -d /dev/videoLeftImgSrc --set-ctrl=exposure_absolute=100
露光時間を上げたあとの写真.(テレビはさんまのまんま.今日のゲストは出川です.)
ROS を使ったカメラキャリブレーション
ここからは,ROS と自作キャリブレーションボードを使ったカメラキャリブレーションを実施する.
キャリブレーションボードの作成
自作キャリブレーションには下記の chesspattern を使用.
http://opencv.jp/sample/pics/chesspattern_7x10.pdf
リンク先の資料は A4 サイズようになっているので,4倍に拡大して継ぎ接ぎすることで A2 サイズのボードを作成.作成後のボードは下記.
ROS キャリブレーションの実施
具体的な手順は下記を参照.
camera_calibration - ROS Wiki
ROS カメラのドライバノードを立ち上げる.
hoge@hogepc:~$ roslaunch usb_cam usb_cam_mod-test.launch
以下,起動 launch ファイル (usb_cam_mod-test.launch)
<launch> <node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" > <param name="camera_name" value="videoLeftImgSrc" /> <param name="video_device" value="/dev/videoLeftImgSrc" /> <param name="image_width" value="1280" /> <param name="image_height" value="960" /> <!-- <param name="pixel_format" value="grey" /> --> <param name="pixel_format" value="bayer_grbg8" /> <param name="camera_frame_id" value="usb_cam" /> <param name="io_method" value="userptr"/> <param name="camera_info_url" value="file:///xxxx/usb_cam/launch/camera_info/videoLeftImgSrc_1280x960.yaml"/> </node> <node name="image_view" pkg="image_view" type="image_view" respawn="false" output="screen"> <!--<remap from="image" to="/usb_cam/image_raw"/>--> <remap from="image" to="/usb_cam/image_raw"/> <param name="autosize" value="true" /> </node> </launch>
ROS camera_calibration ノードを立ち上げる.
hoge@hogepc:~$ rosrun camera_calibration cameracalibrator.py --size 10x7 --square 0.445 image:=/usb_cam/image_raw camera:=/usb_cam
上記コマンドを実行後,下記のキャリブレーション Window が立ち上がるので,画像内でボードをいろいろと動かしてサンプル数を増やす.適切にサンプル数を増やしていくことによって,右側のバーの値が上がっていく.最終的に,x, y, skew, size の 4 項目が全部緑になったらとりあえずキャリブレーション実行.
以下,キャリブレーション結果
[image] width 1280 height 960 [narrow_stereo] camera matrix 631.970382 0.000000 666.559439 0.000000 634.739853 507.227851 0.000000 0.000000 1.000000 distortion -0.224393 0.036108 -0.002063 -0.002725 0.000000 rectification 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 projection 434.929535 0.000000 666.586920 0.000000 0.000000 533.741333 514.566055 0.000000 0.000000 0.000000 1.000000 0.000000
上記結果を元に, camera_calibration ファイルを作成する.
hoge@hogepc:~$ gedit videoLeftImgSrc_1280x960.yaml
image_width: 1280 image_height: 960 camera_name: videoLeftImgSrc camera_matrix: rows: 3 cols: 3 data: [631.970382, 0, 666.559439, 0, 634.739853, 507.227851, 0, 0, 1] distortion_model: plumb_bob distortion_coefficients: rows: 1 cols: 5 data: [-0.224393, 0.036108, -0.002063, -0.002725 , 0] rectification_matrix: rows: 3 cols: 3 data: [1, 0, 0, 0, 1, 0, 0, 0, 1] projection_matrix: rows: 3 cols: 4 data: [434.929535, 0, 666.586920, 0, 0, 533.741333, 514.566055, 0, 0, 0, 1, 0]
ROS キャリブレーション結果を用いて,画像の Undistort
作成した camera_calibration ファイルを使用して,画像の Rectification を実施.
カメラドライバノードの起動.
hoge@hogepc:~$ roslaunch usb_cam usb_cam_mod-test.launch
Rectification 画像作成ノードの起動.
hoge@hogepc:~$ ROS_NAMESPACE=usb_cam rosrun image_proc image_proc
Rectification 画像表示ノードの起動.
hoge@hogepc:~$ rosrun image_view image_view image:=/usb_cam/image_rect
Rectification 後の画像.画面端の窓枠が曲線から直線に治っていることがわかる.
(ただ,画面の本当に端の部分はまだ歪んでいるので,もう少し改善の余地があるのかも.)
環境設定 - ROS Kinetic Kame, PyCharm, Eclipse, git
Ubuntu 14.04 をインストールして使うつもりだったので,ROS Indigo を使用していたが, Ubuntu 16.04 へのアップグレードに伴い ROS Kinetic Kame をインストールすることに.ROS Kinetic Kame 自体はまだベータ版リリースで,正直いろいろとはまりそうだったので,使用をためらっていたんだけど....
ROS のインストール
インストール自体は,下記に沿ってやっていけばすんなり終わるかと.
http://wiki.ros.org/kinetic/Installation/Ubuntu
ROS のローカルワークスペースの作成
下記のように自分の ROS ワークスペースを作成した.
hogehoge@hogepc:~$ mkdir -p workspace/catkin_ws/src hogehoge@hogepc:~$ cd .. hogehoge@hogepc:~$ catkin_init_workspace
上記コマンドの実行で, catkin 用のワークスペースができる.ここで, devel フォルダに setup.bash ファイルが作られるので,これを.bashrc ファイルに追記しておくことで自分の ROS ワークスペースのパッケージ情報を ROS システムに伝えることができる.
hogehoge@hogepc:~$ gedit .bashrc
下記の一行を追記.
source ~/workspace/catkin_ws/devel/setup.bash
Python IDE PyCharm のインストール
Python の IDE には,前から使っていた PyCharm をインストール.
PPA を追加する.
hogehoge@hogepc:~$ sudo add-apt-repository ppa:mystic-mirage/pycharm
システムパッケージキャッシュの更新.
hogehoge@hogepc:~$ sudo apt-get update
Pycharm-Community のインストール
hogehoge@hogepc:~$ sudo apt-get install pycharm-community
Eclipse のインストール
Eclipse のダウンロード
使用する Eclipse には,最新の Mars を選択.
https://eclipse.org/downloads/
ダウンロードした圧縮ファイルを解凍し,移動.
hogehoge@hogepc:~$ tar zxvf eclipse-cpp-mars-2-linux-gtk-x86_64.tar.gz hogehoge@hogepc:~$ sudo mv eclipse /opt/
eclipse 実行ファイルのパスを通す.
hogehoge@hogepc:~$ gedit .profile
上記のファイルの末尾に下記の一行を付け足すことでパスを通します.
PATH="$PATH:/opt/eclipse/eclipse"
Git のインストール
Git のインストールは apt-get install のみで完了.
$ sudo apt-get update $ sudo apt-get install git
UBS カメラセッティング 2 - カメラ詳細情報の確認
つくばチャレンジ 2015 に参加するにあたり,LSD SLAM を使ってやろうと思い下記のカメラを購入.かつステレオもやろうと思ったので 2 つ買った.(レンズ込みで1つ 9 万円 orz...) ここでは,まず OpenCV 等でカメラをいじる前に,簡単にカメラのサポートしている機能などを確認する方法をメモ.
1. カメラビューアーのインストール
兎にも角にも,イメージが見れないと困るので,下記のソフトウェアをインストール.おすすめのソフトは,"GTK UVC video viewer".こいつは Ubuntu Software Center からインストールできるので,素直にインストール.早速自宅実験場の様子をアップ(笑)
2. Video 4 Linux のインストール
カメラの詳細情報や種々の設定をコマンドで実施するために,Video 4 Linux をインストール.
sudo apt-get install v4l-utils
3. カメラ情報の取得
v4l-utils をインストールすることで,v4l2 の API をコマンドから叩くことが可能なる.まずは,カメラの情報を取得.
hogehoge:~$ v4l2-ctl -d /dev/videoLeftImgSrc --info Driver Info (not using libv4l2): Driver name : uvcvideo Card type : DFK 23UM021 Bus info : usb-0000:00:14.0-3 Driver version: 4.4.8 Capabilities : 0x84200001 Video Capture Streaming Extended Pix Format Device Capabilities Device Caps : 0x04200001 Video Capture Streaming Extended Pix Format
4. 対応しているカメラフォーマットの取得
hogehoge:~$ v4l2-ctl -d /dev/videoLeftImgSrc --list-formats ioctl: VIDIOC_ENUM_FMT Index : 0 Type : Video Capture Pixel Format: 'GRBG' Name : 8-bit Bayer GRGR/BGBG Index : 1 Type : Video Capture Pixel Format: 'GREY' Name : 8-bit Greyscale Index : 2 Type : Video Capture Pixel Format: 'Y16 ' Name : 16-bit Greyscale
Ubuntu 14.04 に付いてくる v4l-utils では,GRBG の Pixel Format が対応してなかったみたいで,表示されなかった.このために,Ubuntu 16.04 をインストールすることを決断...