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 のパッケージが出てくるので,インストール.ってか,これだけで導入できるなんて,すごい楽ちん.

f:id:rkoichi2001:20160525033507p:plain

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);
        }
    }
}


以下,実行結果

f:id:rkoichi2001:20160525040232p:plain

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 つが対応可能であった.

  1. V4L2_PIX_FMT_SGRBG8
  2. V4L2_PIX_FMT_GREY
  3. 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 を開始したが,実際にはバッファを渡してあげないとカメラからデータを取ることはできない.流れとしては下記のようになると思う.

  1. VIDIOC_QBUF で使用可能なバッファをカメラに伝えてあげる.(ストリームを開始した直後はカメラが使用可能なバッファがひとつもない状態なので, while 文の上で index=0 を指定してその後の VIDIOC_QBUF でこのバッファが使用可能であることを伝えている.複数個のバッファをリクエストした場合は,最初のステップとして利用可能なすべてのバッファをカメラに伝える必要がある.)
  2. VIDIOC_DQBUF でカメラがデータを詰めたバッファを取得する.この時,buf.index にはカメラによって該当の index 番号が渡されているので,これを辿ってデータを取得する.
  3. アプリ側でデータを吸い上げ完了したら,不要になったバッファ (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がつながったということで,バンザイ!
f:id:rkoichi2001:20160525025235p:plain

OpenCV 2.4 を用いた USB カメラの取り込み1

実装前にいろいろと試す用途で,EclipseOpenCV 用プロジェクトを作成する.ここはそのメモ.

EclipseC++ プロジェクトを作成する.

Eclipse を開いてからの...

  • "Project Exploer 右クリック" -> "New" -> "C/C++" を選択 -> "C++ Project" を選択.

f:id:rkoichi2001:20160522215514p:plain

  • "Project Name" を指定 -> "Location" を決定 -> "Executable" を選択 -> "Hello World C++ Project" を選択 -> "Linux GCC" を選択

f:id:rkoichi2001:20160522215949p:plain

OpenCV のインクルードパス,ライブラリパスを通す.

作成したプロジェクトを選択してからの...

  • "Project" 右クリック -> "Properties" -> "C/C++ General" を選択 -> "Paths and Symbols" を選択.

f:id:rkoichi2001:20160522223315p:plain

  • インクルードパスの追加

環境設定で構築した OpenCV の include フォルダのパスを追加.

f:id:rkoichi2001:20160522223532p:plain

  • ライブラリパスの追加

環境設定で構築した OpenCV の lib フォルダのパスを追加

f:id:rkoichi2001:20160522230051p:plain

  • ライブラリの追加

環境設定で構築した OpenCV の lib フォルダ内にあるライブラリを追加.今回はどれを使うかはっきりしなかったので,とりあえず全部追加.ここで,ライブラリ名は先頭の "lib" と末尾の ".so" を除いた形で追加する.

f:id:rkoichi2001:20160522230123p:plain

以上, "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 を使って調整する必要があるみたい.とりあえず開発環境が整ってバンザイということで,それは次回に!

f:id:rkoichi2001:20160522234459p:plain

環境設定 - 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 つのバージョンをダウンロードしてきた.

opencv-2.2
opencv-2.4.13
opencv-3.1.0
opencv_contrib-master

パッケージの解凍

$ 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

上記パラメータで撮影した画像が下記の写真.全体的に暗く,露光時間を上げる必要がある.
f:id:rkoichi2001:20160522132041p:plain

露光時間を上げる設定を実施.

hoge@hogepc:~$ v4l2-ctl -d /dev/videoLeftImgSrc --set-ctrl=exposure_absolute=100

露光時間を上げたあとの写真.(テレビはさんまのまんま.今日のゲストは出川です.)
f:id:rkoichi2001:20160522132309p:plain

ROS を使ったカメラキャリブレーション

 ここからは,ROS と自作キャリブレーションボードを使ったカメラキャリブレーションを実施する.

キャリブレーションボードの作成

自作キャリブレーションには下記の chesspattern を使用.
http://opencv.jp/sample/pics/chesspattern_7x10.pdf

リンク先の資料は A4 サイズようになっているので,4倍に拡大して継ぎ接ぎすることで A2 サイズのボードを作成.作成後のボードは下記.
f:id:rkoichi2001:20160522133811p:plain

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 項目が全部緑になったらとりあえずキャリブレーション実行.
f:id:rkoichi2001:20160522140329p:plain


以下,キャリブレーション結果

[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 後の画像.画面端の窓枠が曲線から直線に治っていることがわかる.
(ただ,画面の本当に端の部分はまだ歪んでいるので,もう少し改善の余地があるのかも.)
f:id:rkoichi2001:20160522143431p:plain

環境設定 - 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 のインストール

 PythonIDE には,前から使っていた 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 等でカメラをいじる前に,簡単にカメラのサポートしている機能などを確認する方法をメモ.

www.theimagingsource.com

1. カメラビューアーのインストール

 兎にも角にも,イメージが見れないと困るので,下記のソフトウェアをインストール.おすすめのソフトは,"GTK UVC video viewer".こいつは Ubuntu Software Center からインストールできるので,素直にインストール.早速自宅実験場の様子をアップ(笑)

f:id:rkoichi2001:20160516015109p:plain

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 をインストールすることを決断...