入出力とGUI

画像を表示する

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  cv::Mat src_img = cv::imread("../../image/lenna.png", 1);
  if(src_img.empty()) return -1;

  /// 画像を表示するウィンドウ
  // ウィンドウの名前,プロパティ
  // CV_WINDOW_AUTOSIZE : ウィンドウサイズを画像サイズに合わせる
  // CV_WINDOW_FREERATIO : ウィンドウのアスペクト比を固定しない
  cv::namedWindow("image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  // ウィンドウ名でウィンドウを指定して,そこに画像を描画
  cv::imshow("image1", src_img);
   
  // デフォルトのプロパティで表示
  cv::imshow("image2", src_img);

  // キー入力を(無限に)待つ
  cv::waitKey(0);
}

実行結果:

_images/lenna.png _images/lenna.png

ファイルから画像を読み込む

サポートフォーマットでも,さまざまなバリエーションを持つものがあります. 画像読み込みは,大半が外部ライブラリ依存ですが,例えばLinux環境では,jp2(stream),ras(RLE),bmp/dib(MS Windows 3.X packed Device-Independent Bitmap)などは読み込むことができません.

#include <iostream>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  const std::string base = "../../image/lenna";
  std::vector<std::string> files;
  files.push_back(base + ".bmp"); // Windows bitmaps (bmp, dib)
  files.push_back(base + ".jpg"); // JPEG files (jpg, jpeg, jpe)
  files.push_back(base + ".jp2"); // JPEG 2000 files (jp2)
  files.push_back(base + ".png"); // Portable Network Graphics (png)
  files.push_back(base + ".pbm"); // Portable image format (pbm:raw)
  files.push_back(base + "_ascii" + ".pbm"); // Portable image format (pbm:ascii)
  files.push_back(base + ".pgm"); // Portable image format (pgm:raw)
  files.push_back(base + "_ascii" + ".pgm"); // Portable image format (pgm:ascii)
  files.push_back(base + ".ppm"); // Portable image format (ppm:raw)
  files.push_back(base + "_ascii" + ".ppm"); // Portable image format (ppm:ascii)
  files.push_back(base + ".ras"); // Sun rasters (ras, sr)
  files.push_back(base + ".tiff"); // TIFF files (tiff, tif)

  cv::namedWindow("image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::namedWindow("image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::namedWindow("image3", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);

  std::vector<std::string>::iterator it = files.begin();
  for(;it!=files.end(); ++it) {
    std::cout << *it << std::endl;
    // 3チャンネル,カラー画像として読み込む.
    cv::Mat img1 = cv::imread(*it, 1);
    // グレースケール画像として読み込む.
    cv::Mat img2 = cv::imread(*it, 0);
    // 画像をそのまま読み込む.ただし,アルファチャンネルは無視される.
    cv::Mat img3 = cv::imread(*it, -1);

    cv::imshow("image1", img1);
    cv::imshow("image2", img2);
    cv::imshow("image3", img3);

    if(cv::waitKey(0)==27) break;
  }
}

ファイルに画像を書き出す

pbm, pgm, ppm のフォーマットは,いずれの拡張子でも pnm 方式としてまとめられ,カラー画像はカラー画像のまま保存されます.

デフォルトのパラメータで書き出す

#include <iostream>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  const std::string base = "./lenna";
  std::vector<std::string> files;
  files.push_back(base + ".bmp"); // Windows bitmaps (bmp, dib)
  files.push_back(base + ".dib"); // 
  files.push_back(base + ".jpg"); // JPEG files (jpg, jpeg, jpe)
  files.push_back(base + ".jp2"); // JPEG 2000 files (jp2)
  files.push_back(base + ".png"); // Portable Network Graphics (png)
  files.push_back(base + ".pbm"); // Portable any format (pnm)
  files.push_back(base + ".pgm"); // 
  files.push_back(base + ".ppm"); // 
  files.push_back(base + ".ras"); // Sun rasters (ras, sr)
  files.push_back(base + ".tiff"); // TIFF files (tiff, tif)

  cv::Mat src_img = cv::imread("../../image/lenna.png", 1);
  if(src_img.empty()) return -1; 

  std::vector<std::string>::iterator it = files.begin();
  for(;it!=files.end(); ++it) {
    if(cv::imwrite(*it, src_img))
      std::cout << "imwrite:" << *it << " ... success" << std::endl;
    else
      std::cout << "imwrite:" << *it << " ... failure" << std::endl;
  }
}

実行結果:

imwrite:./lenna.bmp ... success
imwrite:./lenna.dib ... success
imwrite:./lenna.jpg ... success
imwrite:./lenna.jp2 ... success
imwrite:./lenna.png ... success
imwrite:./lenna.pbm ... success
imwrite:./lenna.pgm ... success
imwrite:./lenna.ppm ... success
imwrite:./lenna.ras ... success
imwrite:./lenna.tiff ... success

パラメータを指定して書き出す

  • jpg の場合:品質を表す CV_IMWRITE_JPEG_QUALITY .0-100 の値を取り,値が高いほど画像品質が高くなります.デフォルトは,95です.
  • png の場合:圧縮レベルを表す CV_IMWRITE_PNG_COMPRESSION .0-9 の値を取り,値が高いほど圧縮率が上がります.デフォルトは,3です.
  • ppm,pgm,pbm の場合,バイナリ(raw)フォーマット形式を表すフラグ CV_IMWRITE_PXM_BINARY .0/1の値を取り,0の場合はascii形式で保存されます.デフォルトは,1です.
#include <iostream>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  const std::string base = "./lenna";
  std::vector<std::string> files;
  files.push_back(base + "_q30.jpg"); // JPEG files (jpg, jpeg, jpe)
  files.push_back(base + "_l9.png"); // Portable Network Graphics (png)
  files.push_back(base + "_ascii.ppm"); // Portable any format (pnm)

  cv::Mat src_img = cv::imread("../../image/lenna.png", 1);
  if(src_img.empty()) return -1; 
  
  std::vector<int> params(2);
  // jpg
  params[0] = CV_IMWRITE_JPEG_QUALITY;
  params[1] = 10;
  cv::imwrite(base+"_q10.jpg", src_img, params);
  // png
  params[0] = CV_IMWRITE_PNG_COMPRESSION;
  params[1] = 9;
  cv::imwrite(base+"_l9.png", src_img, params);
  // pnm
  params[0] = CV_IMWRITE_PXM_BINARY;
  params[1] = 0;
  cv::imwrite(base+"_ascii.ppm", src_img, params);
}

入力画像:

_images/lenna.png

実行結果(jpg, png):

_images/lenna_q10.jpg _images/lenna_l9.png
$ ls lenna*.jpg
-rw-r--r-- 1 user user 100915 2011-04-01 00:00 lenna.jpg
-rw-r--r-- 1 user user   9001 2011-04-01 00:00 lenna_q10.jpg

$ ls lenna*.png
-rw-r--r-- 1 user user 488272 2011-04-01 00:00 lenna.png
-rw-r--r-- 1 user user 447492 2011-04-01 00:00 lenna_l9.png

$ ls lenna*.ppm
-rw-r--r-- 1 user user  737295 2011-04-19 00:00 lenna.ppm
-rw-r--r-- 1 user user 3441135 2011-04-19 00:00 lenna_ascii.ppm

$ head -n 3 lenna.ppm
P6
512 480
255

$ head -n 3 lenna_ascii.ppm
P3
512 480
255

カメラ画像をキャプチャする

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  cv::VideoCapture cap(0);
  // 様々な設定...
  cap.set(CV_CAP_PROP_FRAME_WIDTH, 640);
  cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
  // カメラがオープンできたかの確認
  if(!cap.isOpened()) return -1;

  cv::namedWindow("Capture", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  while(1) {
    cv::Mat frame;
    cap >> frame;  // キャプチャ
    // 様々な処理
    // ...
    cv::imshow("Capture", frame); // 表示
    if(cv::waitKey(30) >= 0) 
       {
	  cv::imwrite("cap.png", frame);
       break;
       }
  }
}

ファイルに動画を書き出す

FOURCCについて書く.

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  cv::VideoCapture cap(0);
  // 様々な設定...
  cv::Size cap_size(640, 480);
  cap.set(CV_CAP_PROP_FRAME_WIDTH, cap_size.width);
  cap.set(CV_CAP_PROP_FRAME_HEIGHT, cap_size.height);
  // カメラがオープンできたかの確認
  if(!cap.isOpened()) return -1;

  // ビデオライタ
  int fps = 15;
  cv::VideoWriter writer("capture.avi", CV_FOURCC('X','V','I','D'), fps, cap_size);

  cv::namedWindow("Capture", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::Mat frame;   
  while(1) {
    cap >> frame;  // キャプチャ
    // 様々な処理
    // ...
    writer << frame;
    cv::imshow("Capture", frame);
    if(cv::waitKey(30) >= 0) break;
  }
}

ファイルから動画を読み込む

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  cv::VideoCapture cap("capture.avi");
  // ファイルがオープンできたかの確認
  if(!cap.isOpened()) return -1;

  cv::namedWindow("Capture", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  while(1) {
    cv::Mat frame;
    cap >> frame;  // キャプチャ
    // 様々な処理
    // ...
    cv::imshow("Capture", frame);
    //
    if(cv::waitKey(30) >= 0) break;
  }
}

画像をメモリ上でエンコード/デコードする

画像のエンコードで指定できる拡張子やパラメータは, imwrite() で指定できるものと同じです.

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int
main(int argc, char *argv[])
{
  cv::Mat img = cv::imread("../../image/lenna.png", 1);
  if(img.empty()) return -1; 
  
  std::vector<uchar> buf;
  std::vector<int> params(2);
  int size = img.rows*img.cols;

  // エンコード(jpg)
  params[0] = CV_IMWRITE_JPEG_QUALITY;
  params[1] = 10;
  cv::imencode(".jpg", img, buf, params);
  std::cout << "jpg" << std::endl;
  std::cout << "Original Data Size: " << (img.isContinuous()? (img.elemSize()*size):0) << " [byte]" << std::endl;
  std::cout << "Encoded Image Size: " << buf.size() << " [byte] (quality=" << params[1] << ")" << std::endl << std::endl;
  
  /// エンコード(png)
  // 拡張子,画像,エンコードバッファ,エンコードパラメータ
  params[0] = CV_IMWRITE_PNG_COMPRESSION;
  params[1] = 9;
  cv::imencode(".png", img, buf, params);
  std::cout << "png" << std::endl;
  std::cout << "Original Data Size: " << (img.isContinuous()? (img.elemSize()*size):0) << " [byte]" << std::endl;
  std::cout << "Encoded Image Size: " << buf.size() << " [byte] (level=" << params[1] << ")" << std::endl << std::endl;

  /// エンコード(pnm:ascii)
  params[0] = CV_IMWRITE_PXM_BINARY;
  params[1] = 0;
  cv::imencode(".pnm", img, buf, params);
  std::cout << "pnm:ascii" << std::endl;
  std::cout << "Original Data Size: " << (img.isContinuous()? (img.elemSize()*size):0) << " [byte]" << std::endl;
  std::cout << "Encoded Image Size: " << buf.size() << " [byte] (binary=" << (params[1]?"true":"false") << ")" << std::endl << std::endl;

  /// エンコード(jpg2000)  
  cv::imencode(".jp2", img, buf, params);
  std::cout << "jpeg2000" << std::endl;
  std::cout << "Original Data Size: " << (img.isContinuous()? (img.elemSize()*size):0) << " [byte]" << std::endl;
  std::cout << "Encoded Image Size: " << buf.size() << " [byte]" << std::endl << std::endl;

  // ...
  // ネットワークにデータを流したりとか...

  // ...
  // ネットワークからデータ受け取ったりとか...

  /// デコード(from jpeg200)
  // バッファ,imreadと同じフラグ
  cv::Mat dst_img = cv::imdecode(cv::Mat(buf), 1);
  
  cv::namedWindow("both flip image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::imshow("both flip image", dst_img);
  cv::waitKey(0);
}

入力画像:

_images/lenna.png

実行結果:

jpg
Original Data Size: 737280 [byte]
Encoded Image Size: 9001 [byte] (quality=10)

png
Original Data Size: 737280 [byte]
Encoded Image Size: 447492 [byte] (level=9)

pnm:ascii
Original Data Size: 737280 [byte]
Encoded Image Size: 3441135 [byte] (binary=false)

jpeg2000
Original Data Size: 737280 [byte]
Encoded Image Size: 418611 [byte]

YAML/XMLを読み込む・書き出す

YAMLを書き出す

YAMLの場合,シーケンス(名前なしリスト)やマップ(名前付きリスト)に対して,BlockとFlowという2種類のスタイルが存在します. 例えばすべての要素がスカラである場合リストなどは,よりコンパクトなFlowスタイルで保存する方が良いでしょう.

(例)Block スタイルのシーケンス:

- Mark McGwire
- Sammy Sosa
- Ken Griffey

(例)Flow スタイルのシーケンス:

[Mark McGwire, Sammy Sosa, Ken Griffey]

(例)Block スタイルのマップ:

hr:  65
avg: 0.278
rbi: 147

(例)Flow スタイルのマップ:

{hr: 65, avg: 0.278, rbi: 147}

OpenCVのFileStorageに書き込む際は, [ ] がBlockスタイルのシーケンス, [: ] がFlowスタイルのシーケンス, { } がBlockスタイルのマップ, {: } がFlowスタイルのマップ,を意味します.

#include <iostream>
#include <opencv2/core/core.hpp>

int
main(int argc, char *argv[])
{
  // 書き込み用にファイルをオープン
  // ファイルの種類は拡張子で決定
  cv::FileStorage fs("test.yml", cv::FileStorage::WRITE);
  
  // 整数,実数,文字列
  fs << "test_int" << 5 << "test_real" << 3.1 << "test_string" << "ABCDEFGH";
  // Mat
  fs << "test_mat" << cv::Mat::eye(3,3,CV_32F);
  // "[" BLOCK SEQUENCE
  fs << "test_sequence" << "[" << CV_PI << "1+1"
      // "{:" FLOW MAP
     << "{:" << "month" << 12 << "day" << 31 << "year" << 1969 << "}" << "]";
  // "{" BLOCK MAP
  fs << "test_map" << "{" << "x" << 1 << "y" << 2 <<
    // "[:" FLOW SEQUENCE
    "width" << 100 << "height" << 200 << "lbp" << "[:";
  const uchar arr[] = {0, 1, 1, 0, 1, 1, 0, 1};
  fs.writeRaw("u", arr, static_cast<int>(sizeof(arr)/sizeof(arr[0])));
  fs << "]" << "}";
}

実行結果:

%YAML:1.0
test_int: 5
test_real: 3.1000000000000001e+00
test_string: ABCDEFGH
test_mat: !!opencv-matrix
   rows: 3
   cols: 3
   dt: f
   data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1. ]
test_sequence:
   - 3.1415926535897931e+00
   - "1+1"
   - { month:12, day:31, year:1969 }
test_map:
   x: 1
   y: 2
   width: 100
   height: 200
   lbp: [ 0, 1, 1, 0, 1, 1, 0, 1 ]

YAMLを読み込む

#include <iostream>
#include <opencv2/core/core.hpp>

int
main(int argc, char *argv[])
{
  // ファイルの種類は,内容から決定
  cv::FileStorage fs("test.yml", cv::FileStorage::READ);
  
  // 整数,実数,文字列,Mat
  int i1 = static_cast<int>(fs["test_int"]); 
  double r1 = static_cast<double>(fs["test_real"]);
  std::string str1 = static_cast<std::string>(fs["test_string"]);
  // スカラ以外(ここではMat)を >> で読み込む
  cv::Mat M; 
  fs["test_mat"] >> M;
  std::cout << i1 << ", " << r1 << ", " << str1 << std::endl;
  std::cout << M << std::endl;

  /// Sequence
  cv::FileNode ts = fs["test_sequence"];
  CV_Assert(ts.type() == cv::FileNode::SEQ && ts.size() == 3);
  double ts0 = static_cast<double>(ts[0]);
  std::string ts1 = static_cast<std::string>(ts[1]);
  int month = static_cast<int>(ts[2]["month"]);
  int day = static_cast<int>(ts[2]["day"]);
  int year = static_cast<int>(ts[2]["year"]);
  std::cout << ts0 << std::endl;
  std::cout << ts1 << std::endl;
  std::cout << "{month:" << month << ", day:" << day << ", year:" << year << "}" << std::endl;
  
  /// Map
  cv::FileNode tm = fs["test_map"];
  CV_Assert(tm.type() == cv::FileNode::MAP && tm.size() == 5);
  cv::Rect r; 
  r.x = static_cast<int>(tm["x"]), r.y = static_cast<int>(tm["y"]);
  r.width = static_cast<int>(tm["width"]), r.height = static_cast<int>(tm["height"]);
  int lbp_val = 0;
  cv::FileNodeIterator it = tm["lbp"].begin();
  // リストをイテレータで読み込む
  for(int k = 0; k < 8; k++, ++it)
    lbp_val |= (static_cast<int>(*it)) << k;
  std::cout << "x:" << r.x << std::endl;
  std::cout << "y:" << r.y << std::endl;
  std::cout << "width:" << r.width << std::endl;
  std::cout << "height:" << r.height << std::endl;
  std::cout << "lbp:" << lbp_val << std::endl;
}

実行結果:

5, 3.1, ABCDEFGH
[1, 0, 0;
  0, 1, 0;
  0, 0, 1]
3.14159
1+1
{month:12, day:31, year:1969}
x:1
y:2
width:100
height:200
lbp:182

XMLを書き出す

XMLはYAMLと異なり,各ノードに Block や Flow といった種別はありません.

#include <iostream>
#include <opencv2/core/core.hpp>

int
main(int argc, char *argv[])
{
  // 書き込み用にファイルをオープン
  // ファイルの種類は拡張子で決定
  cv::FileStorage fs("test.xml", cv::FileStorage::WRITE);
  
  // 整数,実数,文字列
  fs << "test_int" << 5 << "test_real" << 3.1 << "test_string" << "ABCDEFGH";
  // Mat
  fs << "test_mat" << cv::Mat::eye(3,3,CV_32F);
  // "[" SEQUENCE
  fs << "test_sequence" << "[" << CV_PI << "1+1"
    // "{" MAP
     << "{" << "month" << 12 << "day" << 31 << "year" << 1969 << "}" << "]";
  // "{" MAP
  fs << "test_map" << "{" << "x" << 1 << "y" << 2 <<
    // "[" SEQUENCE
    "width" << 100 << "height" << 200 << "lbp" << "[";
  const uchar arr[] = {0, 1, 1, 0, 1, 1, 0, 1};
  fs.writeRaw("u", arr, static_cast<int>(sizeof(arr)/sizeof(arr[0])));
  fs << "]" << "}";
}

実行結果:

<?xml version="1.0"?>
<opencv_storage>
<test_int>5</test_int>
<test_real>3.1000000000000001e+00</test_real>
<test_string>ABCDEFGH</test_string>
<test_mat type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>f</dt>
  <data>
    1. 0. 0. 0. 1. 0. 0. 0. 1.</data></test_mat>
<test_sequence>
  3.1415926535897931e+00 "1+1"
  <_>
    <month>12</month>
    <day>31</day>
    <year>1969</year></_></test_sequence>
<test_map>
  <x>1</x>
  <y>2</y>
  <width>100</width>
  <height>200</height>
  <lbp>
    0 1 1 0 1 1 0 1</lbp></test_map>
</opencv_storage>

XMLを読み込む

#include <iostream>
#include <opencv2/core/core.hpp>

int
main(int argc, char *argv[])
{
  // ファイルの種類は,内容から決定
  cv::FileStorage fs("test.xml", cv::FileStorage::READ);
  
  // 整数,実数,文字列,Mat
  int i1 = static_cast<int>(fs["test_int"]); 
  double r1 = static_cast<double>(fs["test_real"]);
  std::string str1 = static_cast<std::string>(fs["test_string"]);
  // スカラ以外(ここではMat)を >> で読み込む
  cv::Mat M; 
  fs["test_mat"] >> M;
  std::cout << i1 << ", " << r1 << ", " << str1 << std::endl;
  std::cout << M << std::endl;

  /// Sequence
  cv::FileNode ts = fs["test_sequence"];
  CV_Assert(ts.type() == cv::FileNode::SEQ && ts.size() == 3);
  double ts0 = static_cast<double>(ts[0]);
  std::string ts1 = static_cast<std::string>(ts[1]);
  int month = static_cast<int>(ts[2]["month"]);
  int day = static_cast<int>(ts[2]["day"]);
  int year = static_cast<int>(ts[2]["year"]);
  std::cout << ts0 << std::endl;
  std::cout << ts1 << std::endl;
  std::cout << "{month:" << month << ", day:" << day << ", year:" << year << "}" << std::endl;
  
  /// Map
  cv::FileNode tm = fs["test_map"];
  CV_Assert(tm.type() == cv::FileNode::MAP && tm.size() == 5);
  cv::Rect r; 
  r.x = static_cast<int>(tm["x"]), r.y = static_cast<int>(tm["y"]);
  r.width = static_cast<int>(tm["width"]), r.height = static_cast<int>(tm["height"]);
  int lbp_val = 0;
  cv::FileNodeIterator it = tm["lbp"].begin();
  // リストをイテレータで読み込む
  for(int k = 0; k < 8; k++, ++it)
    lbp_val |= (static_cast<int>(*it)) << k;
  std::cout << "x:" << r.x << std::endl;
  std::cout << "y:" << r.y << std::endl;
  std::cout << "width:" << r.width << std::endl;
  std::cout << "height:" << r.height << std::endl;
  std::cout << "lbp:" << lbp_val << std::endl;
}

実行結果:

5, 3.1, ABCDEFGH
[1, 0, 0;
  0, 1, 0;
  0, 0, 1]
3.14159
1+1
{month:12, day:31, year:1969}
x:1
y:2
width:100
height:200
lbp:182

マウスイベントを取得する

OpenCVのhighguiで作成されたウィンドウ上の通常のキーイベントは,waitKey() メソッドで取得できますが,(CTRLやSIFTなどの)修飾キーやマウスのイベントは,コールバック関数を設定して取得します. このコールバック関数を設定するには, setMouseCallback を利用します.

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#define OPENCV_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#define OPENCV_VERSION_CODE OPENCV_VERSION(CV_MAJOR_VERSION, CV_MINOR_VERSION, CV_SUBMINOR_VERSION)

#if OPENCV_VERSION_CODE < OPENCV_VERSION(2,3,1)
namespace cv
{
  enum {
    EVENT_MOUSEMOVE      =CV_EVENT_MOUSEMOVE,
    EVENT_LBUTTONDOWN    =CV_EVENT_LBUTTONDOWN,
    EVENT_RBUTTONDOWN    =CV_EVENT_RBUTTONDOWN,
    EVENT_MBUTTONDOWN    =CV_EVENT_MBUTTONDOWN,
    EVENT_LBUTTONUP      =CV_EVENT_LBUTTONUP,
    EVENT_RBUTTONUP      =CV_EVENT_RBUTTONUP,
    EVENT_MBUTTONUP      =CV_EVENT_MBUTTONUP,
    EVENT_LBUTTONDBLCLK  =CV_EVENT_LBUTTONDBLCLK,
    EVENT_RBUTTONDBLCLK  =CV_EVENT_RBUTTONDBLCLK,
    EVENT_MBUTTONDBLCLK  =CV_EVENT_MBUTTONDBLCLK
  };
  enum {
    EVENT_FLAG_LBUTTON   =CV_EVENT_FLAG_LBUTTON,
    EVENT_FLAG_RBUTTON   =CV_EVENT_FLAG_RBUTTON,
    EVENT_FLAG_MBUTTON   =CV_EVENT_FLAG_MBUTTON,
    EVENT_FLAG_CTRLKEY   =CV_EVENT_FLAG_CTRLKEY,
    EVENT_FLAG_SHIFTKEY  =CV_EVENT_FLAG_SHIFTKEY,
    EVENT_FLAG_ALTKEY    =CV_EVENT_FLAG_ALTKEY
  };
}
#endif

void onMouse( int event, int x, int y, int flag, void* )
{
  std::string desc;

  // マウスイベントを取得
  switch(event) {
  case cv::EVENT_MOUSEMOVE:
    desc += "MOUSE_MOVE";
    break;
  case cv::EVENT_LBUTTONDOWN:
    desc += "LBUTTON_DOWN";
    break;
  case cv::EVENT_RBUTTONDOWN:
    desc += "RBUTTON_DOWN";
    break;
  case cv::EVENT_MBUTTONDOWN:
    desc += "MBUTTON_DOWN";
    break;
  case cv::EVENT_LBUTTONUP:
    desc += "LBUTTON_UP";
    break;
  case cv::EVENT_RBUTTONUP:
    desc += "RBUTTON_UP";
    break;
  case cv::EVENT_MBUTTONUP:
    desc += "MBUTTON_UP";
    break;
  case cv::EVENT_LBUTTONDBLCLK:
    desc += "LBUTTON_DBLCLK";
    break;
  case cv::EVENT_RBUTTONDBLCLK:
    desc += "RBUTTON_DBLCLK";
    break;
  case cv::EVENT_MBUTTONDBLCLK:
    desc += "MBUTTON_DBLCLK";
    break;
  }

  // マウスボタン,及び修飾キーを取得
  if(flag & cv::EVENT_FLAG_LBUTTON)
    desc += " + LBUTTON";
  if(flag & cv::EVENT_FLAG_RBUTTON)
    desc += " + RBUTTON";
  if(flag & cv::EVENT_FLAG_MBUTTON)
    desc += " + MBUTTON";
  if(flag & cv::EVENT_FLAG_CTRLKEY)
    desc += " + CTRL";
  if(flag & cv::EVENT_FLAG_SHIFTKEY)
    desc += " + SHIFT";
  if(flag & cv::EVENT_FLAG_ALTKEY)
    desc += " + ALT";

  std::cout << desc << " (" << x << ", " << y << ")" << std::endl;
}

int
main(int argc, char *argv[])
{
  // 画像を初期化
  cv::Mat black_img = cv::Mat::zeros(cv::Size(800, 500), CV_8UC3); 
  
  cv::namedWindow("mouse event demo", 0 );
  // マウスイベントに対するコールバック関数を登録
  cv::setMouseCallback("mouse event demo", onMouse, 0);
  imshow("mouse event demo", black_img);
  
  int key;
  while(1) {
    key = cv::waitKey(0);
    // 'Esc'が押された場合に終了
    if(key==27) break;
  }
}

実行結果:

MOUVE_MOVE + ALT (5, 58)
MOUVE_MOVE + ALT (8, 55)
MOUVE_MOVE + ALT (10, 55)
LBUTTON_DOWN + ALT (10, 55)
LBUTTON_UP + LBUTTON + ALT (10, 55)
LBUTTON_DOWN + ALT (10, 55)
LBUTTON_UP + LBUTTON + ALT (10, 55)
LBUTTON_DOWN + ALT (10, 55)
LBUTTON_DBLCLK + ALT (10, 55)
LBUTTON_UP + LBUTTON + ALT (10, 55)

また,Windowの作成にQtを利用している場合,右クリックがメニューに割り当てられるために,通常は該当するイベントの取得ができません.

_images/qt_window_left_click.png

トラックバーを追加する

トラックバーをウィンドウに追加し,同時にそれに対するコールバックイベントを設定することで,トラックバーの変化に応じた処理を行うことができます. 以下の例では,トラックバーで 0 - 100 [%] の画像混合比アルファを設定し,2枚の画像をブレンディングします.

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

class AlphaBlend
{
private:
  cv::Mat src1, src2, dst;  
public:
  AlphaBlend():alpha_slider(0),alpha_slider_max(100) {};
  int alpha_slider;
  const int alpha_slider_max;
  bool loadSourceImages(std::string s1, std::string s2);
  void onTrackbar_impl(int val);
  static void onTrackbar(int val, void* data);
};

/// トラックバーイベント
void AlphaBlend::onTrackbar_impl(int val)
{
  double alpha = cv::saturate_cast<double>(val)/alpha_slider_max;
  double beta = (1.0 - alpha);

  cv::addWeighted(src1, alpha, src2, beta, 0.0, dst);
  cv::imshow( "Linear Blend", dst);
}

void AlphaBlend::onTrackbar(int val, void* data)
{
  AlphaBlend *ab = reinterpret_cast<AlphaBlend*>(data);
  ab->onTrackbar_impl(val);
}

/// 画像読み込み
bool AlphaBlend::loadSourceImages(std::string s1, std::string s2)
{
  src1 = cv::imread(s1);
  src2 = cv::imread(s2);
  if(src1.empty() || src2.empty()) return false;
  return true;
}

int main( int argc, char** argv )
{
  AlphaBlend ab;

  // 画像の読み込み
  ab.loadSourceImages("../../image/lenna.png", "../../image/mandrill.png");

  // ウィンドウの作成
  cv::namedWindow("Linear Blend", 1);

  // トラックバーの作成
  std::stringstream TrackbarName;
  TrackbarName << "alpha x " << ab.alpha_slider_max;
  // テキスト,追加するウィンドウ名,値を格納する変数へのポインタ,最大値,コールバック関数,データポインタ
  cv::createTrackbar(TrackbarName.str(), "Linear Blend", &ab.alpha_slider, ab.alpha_slider_max, AlphaBlend::onTrackbar, &ab);

  // 最初の表示
  AlphaBlend::onTrackbar(0, &ab);

  cv::waitKey(0);
}

実行結果(20%,50%,80%):

_images/trackbar_20parcent.png _images/trackbar_50parcent.png _images/trackbar_80parcent.png