1 cv::Matの基本処理

1.1 cv::Matを初期化する

1 カンマ区切り初期化子

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

int
main(int argc, char *argv[])
{
  // 3x3 単位行列 (= cv::Mat::eye(3,3, CV_64F) )
  cv::Mat m = (cv::Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);
  // 2x2 回転行列
  double angle = 30, a = std::cos(angle*CV_PI/180), b = std::sin(angle*CV_PI/180);
  cv::Mat r = (cv::Mat_<double>(2,2) << a, -b, b, a);
  
  std::cout << "m=" << m << std::endl << std::endl;
  std::cout << "r=" << r << std::endl;
}

実行結果:

m=[1, 0, 0;
  0, 1, 0;
  0, 0, 1]

r=[0.8660254037844387, -0.4999999999999999;
  0.4999999999999999, 0.8660254037844387]

2 MATLABスタイルの行列初期化関数

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

int
main(int argc, char *argv[])
{
   // 要素がすべて 3 の 5x5 行列
   cv::Mat mat1 = cv::Mat::ones(5, 5, CV_8U)*3;
   // 要素がすべて 0 の 5x5 行列
   cv::Mat mat2 = cv::Mat::zeros(5, 5, CV_8U);
   // 5x5 の単位行列
   cv::Mat mat3 = cv::Mat::eye(5, 5, CV_8U);
   
   std::cout << "mat1=" << mat1 << std::endl << std::endl;
   std::cout << "mat2=" << mat2 << std::endl << std::endl;
   std::cout << "mat3=" << mat3 << std::endl;
}

実行結果:

mat1=[3, 3, 3, 3, 3;
  3, 3, 3, 3, 3;
  3, 3, 3, 3, 3;
  3, 3, 3, 3, 3;
  3, 3, 3, 3, 3]

mat2=[0, 0, 0, 0, 0;
  0, 0, 0, 0, 0;
  0, 0, 0, 0, 0;
  0, 0, 0, 0, 0;
  0, 0, 0, 0, 0]

mat3=[1, 0, 0, 0, 0;
  0, 1, 0, 0, 0;
  0, 0, 1, 0, 0;
  0, 0, 0, 1, 0;
  0, 0, 0, 0, 1]

3 配列の値で初期化する

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

int
main(int argc, char *argv[])
{
  float data[] = {1,2,3,4,5,6,7,8,9};
  cv::Mat m1(3, 3, CV_32F, data);

  // この方法で確保したユーザデータは,参照カウントされません.
  // データの解放は,ユーザが責任を持ちます.
  CV_Assert(m1.refcount == NULL);

  std::cout << "m1=" << m1 << std::endl;
}

実行結果:

m1=[1, 2, 3;
  4, 5, 6;
  7, 8, 9]

4 定数で初期化する

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

int
main(int argc, char *argv[])
{
  cv::Mat m1(3, 3, CV_32F, cv::Scalar(5));
  std::cout << "m1=" << m1 << std::endl;

  cv::Mat m2(3, 3, CV_32F);
  m2 = cv::Scalar(5);
  std::cout << "m2=" << m2 << std::endl;

  cv::Mat m3 = cv::Mat::ones(3, 3, CV_32F)*5;
  std::cout << "m3=" << m3 << std::endl;

  cv::Mat m4 = cv::Mat::zeros(3, 3, CV_32F)+5;
  std::cout << "m4=" << m4 << std::endl;

  return 0;
}

実行結果:

m1=[5, 5, 5;
  5, 5, 5;
  5, 5, 5]
m2=[5, 5, 5;
  5, 5, 5;
  5, 5, 5]
m3=[5, 5, 5;
  5, 5, 5;
  5, 5, 5]
m4=[5, 5, 5;
  5, 5, 5;
  5, 5, 5]

5 乱数で初期化する

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

int
main(int argc, char *argv[])
{
  cv::Mat mat(3, 2, CV_8UC1);

  // 一様分布乱数,[0,256)
  cv::randu(mat, cv::Scalar(0), cv::Scalar(256));
  std::cout << mat << std::endl << std::endl;
  
  // 正規分布乱数,mean=128, stddev=10
  cv::randn(mat, cv::Scalar(128), cv::Scalar(10));
  std::cout << mat << std::endl << std::endl;  

  // RNG初期化
  cv::RNG gen(cv::getTickCount());

  // 一様分布乱数,[0,256)
  gen.fill(mat, cv::RNG::UNIFORM, cv::Scalar(0), cv::Scalar(256));
  std::cout << mat << std::endl << std::endl;

  // 正規分布乱数,mean=128, stddev=10
  gen.fill(mat, cv::RNG::NORMAL, cv::Scalar(128), cv::Scalar(10));
  std::cout << mat << std::endl << std::endl;
}

実行結果:

[246, 156;
  192, 7;
  165, 231]

[124, 140;
  125, 122;
  131, 133]

[161, 236;
  102, 177;
  15, 200]

[118, 138;
  113, 126;
  134, 148]

6 マルチチャンネル配列を初期化する

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

int
main(int argc, char *argv[])
{
  // CV32SC2, 3x3 行列
  // 初期化+reshapeを用いた変形
  cv::Mat m0 = (cv::Mat_<int>(3,6) << 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18);
  cv::Mat m1 = m0.reshape(2);
  
  // 一応,こういう風にもできる…
  cv::Vec2i data[] = {cv::Vec2i(1,2), cv::Vec2i(3,4),cv::Vec2i(5,6),cv::Vec2i(7,8),cv::Vec2i(9,10),
		      cv::Vec2i(11,12),cv::Vec2i(13,14),cv::Vec2i(15,16),cv::Vec2i(17,18)};
  cv::Mat m2 = cv::Mat(3, 3, CV_32SC2, data).clone();
  //
  cv::Mat m3 = (cv::Mat_<cv::Vec2i>(3,3) << cv::Vec2i(1,2),cv::Vec2i(3,4),cv::Vec2i(5,6),cv::Vec2i(7,8),
		cv::Vec2i(9,10),cv::Vec2i(11,12),cv::Vec2i(13,14),cv::Vec2i(15,16),cv::Vec2i(17,18));
  
  std::cout << "m1=" << m1 << std::endl << std::endl;
  std::cout << "m2=" << m1 << std::endl << std::endl;
  std::cout << "m3=" << m1 << std::endl << std::endl;
}

実行結果:

m1=[1, 2, 3, 4, 5, 6;
  7, 8, 9, 10, 11, 12;
  13, 14, 15, 16, 17, 18]

m2=[1, 2, 3, 4, 5, 6;
  7, 8, 9, 10, 11, 12;
  13, 14, 15, 16, 17, 18]

m3=[1, 2, 3, 4, 5, 6;
  7, 8, 9, 10, 11, 12;
  13, 14, 15, 16, 17, 18]

1.3 cv::Matの様々なプロパティ

1 シングルチャンネル,2次元配列

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

int
main(int argc, char *argv[])
{
  cv::Mat m1(3, 4, CV_64FC1);
  
  // 行数
  std::cout << "rows:" << m1.rows <<std::endl;
  // 列数
  std::cout << "cols:" << m1.cols << std::endl;
  // 次元数
  std::cout << "dims:" << m1.dims << std::endl;
  // サイズ(2次元の場合)
  std::cout << "size[]:" << m1.size().width << "," << m1.size().height << std::endl;
  // ビット深度ID
  std::cout << "depth (ID):" << m1.depth() << "(=" << CV_64F << ")" << std::endl;
  // チャンネル数
  std::cout << "channels:" << m1.channels() << std::endl;
  // (複数チャンネルから成る)1要素のサイズ [バイト単位]
  std::cout << "elemSize:" << m1.elemSize() << "[byte]" << std::endl;
  // 1要素内の1チャンネル分のサイズ [バイト単位]
  std::cout << "elemSize1 (elemSize/channels):" << m1.elemSize1() << "[byte]" << std::endl;
  // 要素の総数
  std::cout << "total:" << m1.total() << std::endl;
  // ステップ数 [バイト単位]
  std::cout << "step:" << m1.step << "[byte]" << std::endl;
  // 1ステップ内のチャンネル総数
  std::cout << "step1 (step/elemSize1):" << m1.step1()  << std::endl;
  // データは連続か?
  std::cout << "isContinuous:" << (m1.isContinuous()?"true":"false") << std::endl;
  // 部分行列か?
  std::cout << "isSubmatrix:" << (m1.isSubmatrix()?"true":"false") << std::endl;
  // データは空か?
  std::cout << "empty:" << (m1.empty()?"true":"false") << std::endl;
}

実行結果:

rows:3
cols:4
dims:2
size[]:4,3
depth (ID):6(=6)
channels:1
elemSize:8[byte]
elemSize1 (elemSize/channels):8[byte]
total:12
step:32[byte]
step1 (step/elemSize1):4
isContinuous:true
isSubmatrix:false
empty:false

2 マルチチャンネル,2次元配列の部分配列

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

int
main(int argc, char *argv[])
{
  cv::Mat m1(4, 5, CV_32FC(5));  //5x4
  cv::Rect roi_rect(0, 0, 3, 4); //4x3
  cv::Mat r1(m1, roi_rect);

  // 行数
  std::cout << "rows:" << r1.rows <<std::endl;
  // 列数
  std::cout << "cols:" << r1.cols << std::endl;
  // 次元数
  std::cout << "dims:" << r1.dims << std::endl;
  // サイズ(2次元の場合)
  std::cout << "size[]:" << r1.size().width << "," << r1.size().height << std::endl;
  // ビット深度ID
  std::cout << "depth (ID):" << r1.depth() << "(=" << CV_32F << ")" << std::endl;
  // チャンネル数
  std::cout << "channels:" << r1.channels() << std::endl;
  // (複数チャンネルから成る)1要素のサイズ [バイト単位]
  std::cout << "elemSize:" << r1.elemSize() << "[byte]" << std::endl;
  // 1要素内の1チャンネル分のサイズ [バイト単位]
  std::cout << "elemSize1 (elemSize/channels):" << r1.elemSize1() << "[byte]" << std::endl;
  // 要素の総数
  std::cout << "total:" << r1.total() << std::endl;
  // ステップ数 [バイト単位]
  std::cout << "step:" << r1.step << "[byte]" << std::endl;
  // 1ステップ内のチャンネル総数
  std::cout << "step1 (step/elemSize1):" << r1.step1()  << std::endl;
  // データは連続か?
  std::cout << "isContinuous:" << (r1.isContinuous()?"true":"false") << std::endl;
  // 部分行列か?
  std::cout << "isSubmatrix:" << (r1.isSubmatrix()?"true":"false") << std::endl;
  // データは空か?
  std::cout << "empty:" << (r1.empty()?"true":"false") << std::endl;
}

実行結果:

rows:4
cols:3
dims:2
size[]:3,4
depth (ID):5(=5)
channels:5
elemSize:20[byte]
elemSize1 (elemSize/channels):4[byte]
total:12
step:100[byte]
step1 (step/elemSize1):25
isContinuous:false
isSubmatrix:true
empty:false

3 マルチチャンネル,5次元配列

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

int
main(int argc, char *argv[])
{
  // 32S, channles=2, 2x3x3x4x6 の5次元配列
  const int sizes[] = {2, 3, 3, 4, 6};
  cv::Mat m1(sizeof(sizes)/sizeof(int), sizes, CV_32SC2);

  // 行数
  std::cout << "rows:" << m1.rows <<std::endl;
  // 列数
  std::cout << "cols:" << m1.cols << std::endl;
  // 次元数
  std::cout << "dims:" << m1.dims << std::endl;
  // 各次元のサイズ
  std::cout << "size[]:";
  for(int i=0; i<m1.dims; ++i)  std::cout << m1.size[i] << ",";
  std::cout << std::endl;
  // ビット深度ID
  std::cout << "depth (ID):" << m1.depth() << "(=" << CV_32S << ")" << std::endl;
  // チャンネル数
  std::cout << "channels:" << m1.channels() << std::endl;
  // (複数チャンネルから成る)1要素のサイズ [バイト単位]
  std::cout << "elemSize:" << m1.elemSize() << "[byte]" << std::endl;
  // 1要素内の1チャンネル分のサイズ [バイト単位]
  std::cout << "elemSize1 (elemSize/channels):" << m1.elemSize1() << "[byte]" << std::endl;
  // 要素の総数
  std::cout << "total:" << m1.total() << std::endl;
  // ステップ数 [バイト単位]
  std::cout << "step[]:";
  for(int i=0; i<m1.dims; ++i)  std::cout << m1.step[i] << ",";
  std::cout << "[byte]" << std::endl;
  // 1ステップ内のチャンネル総数
  std::cout << "step1 (step/elemSize1):" << m1.step1()  << std::endl;
  // データは連続か?
  std::cout << "isContinuous:" << (m1.isContinuous()?"true":"false") << std::endl;
  // 部分行列か?
  std::cout << "isSubmatrix:" << (m1.isSubmatrix()?"true":"false") << std::endl;
  // データは空か?
  std::cout << "empty:" << (m1.empty()?"true":"false") << std::endl;
}

実行結果:

rows:-1
cols:-1
dims:5
size[]:2,3,3,4,6,
depth (ID):4(=4)
channels:2
elemSize:8[byte]
elemSize1 (elemSize/channels):4[byte]
total:432
step[]:1728,576,192,48,8,[byte]
step1 (step/elemSize1):432
isContinuous:true
isSubmatrix:false
empty:false

1.4 std::cout への出力

1 デフォルトの出力

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

int
main(int argc, char *argv[])
{
  cv::Mat m1 = cv::Mat::eye(3,3,CV_32FC1);
  cv::Mat m2 = (cv::Mat_<cv::Vec2i>(2, 2) << cv::Vec2i(1,1), cv::Vec2i(2, 4), cv::Vec2i(3, 9), cv::Vec2i(4, 16));
  cv::Point_<int> p1(1,2);
  cv::Point3_<double> p2(1.5, 2.2, 3.9);
   
   // cv::Mat
   std::cout << "m1=" << m1 << std::endl << std::endl;
   // cv::Mat (multi-channel)
   std::cout << "m2=" << m2 << std::endl << std::endl;
   // cv::Point_, cv::Point3_
   std::cout << "p1=" << p1 << std::endl << std::endl;
   std::cout << "p2=" << p2 << std::endl << std::endl;
   // cv::MatExpr
   std::cout << "m1+m1+1=" << m1+m1+1 << std::endl;
}

実行結果:

m1=[1, 0, 0;
  0, 1, 0;
  0, 0, 1]

m2=[1, 1, 2, 4;
  3, 9, 4, 16]

p1=[1, 2]

p2=[1.5, 2.2, 3.9]

m1+m1+1=[3, 1, 1;
  1, 3, 1;
  1, 1, 3]

2 出力フォーマットの指定

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

int
main(int argc, char *argv[])
{
  cv::Mat m(4, 2, CV_8UC3);
  cv::randu(m, cv::Scalar::all(0), cv::Scalar::all(255));
  
  std::cout << "m (default) = " << m << ";" << std::endl << std::endl;
  std::cout << "m (python) = " << cv::format(m,"python") << ";" << std::endl << std::endl;
  std::cout << "m (numpy) = " << cv::format(m,"numpy") << ";" << std::endl << std::endl;
  std::cout << "m (csv) = " << cv::format(m,"csv") << ";" << std::endl << std::endl; 
  std::cout << "m (c) = " << cv::format(m,"C") << ";" << std::endl << std::endl;
}

実行結果:

r (default) = [91, 2, 79, 179, 52, 205;
  236, 8, 181, 239, 26, 248;
  207, 218, 45, 183, 158, 101;
  102, 18, 118, 68, 210, 139];

r (python) = [[[91, 2, 79], [179, 52, 205]], 
  [[236, 8, 181], [239, 26, 248]], 
  [[207, 218, 45], [183, 158, 101]], 
  [[102, 18, 118], [68, 210, 139]]];

r (numpy) = array([[[91, 2, 79], [179, 52, 205]], 
  [[236, 8, 181], [239, 26, 248]], 
  [[207, 218, 45], [183, 158, 101]], 
  [[102, 18, 118], [68, 210, 139]]], type='uint8');

r (csv) = 91, 2, 79, 179, 52, 205
  236, 8, 181, 239, 26, 248
  207, 218, 45, 183, 158, 101
  102, 18, 118, 68, 210, 139
;

r (c) = {91, 2, 79, 179, 52, 205,
  236, 8, 181, 239, 26, 248,
  207, 218, 45, 183, 158, 101,
  102, 18, 118, 68, 210, 139};

1.5 cv::Matの型(ビット深度)を変換する

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

int
main(int argc, char *argv[])
{
  // 3x3 の行列
  cv::Mat m1 = (cv::Mat_<double>(3,3) << 1.1, 1.2, 1.3, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3);
  std::cout << "m1(CV_64FC1)" << std::endl << m1 << std::endl << std::endl;
  
  cv::Mat m2, m3;
  // データはコピーされる
  // 出力行列,型(ビット深度)
  m1.convertTo(m2, CV_8U);
  std::cout << "m2(CV_8UC1)" << std::endl << m2 << std::endl << std::endl;
  
  // データはコピーされる
  // 出力行列,型(ビット深度),α(スケールファクタ),β(足される値)
  m1.convertTo(m3, CV_8U, 2, 10);
  std::cout << "m3(CV_8UC1)" << std::endl << m3 << std::endl << std::endl;
}

実行結果:

m1(CV_64FC1)
[1.1, 1.2, 1.3;
  2.1, 2.2, 2.3;
  3.1, 3.2, 3.3]

m2(CV_8UC1)
[1, 1, 1;
  2, 2, 2;
  3, 3, 3]

m3(CV_8UC1)
[12, 12, 13;
  14, 14, 15;
  16, 16, 17]

1.6 cv::Matをリサイズする

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

int
main(int argc, char *argv[])
{
  // 3x3 行列
  cv::Mat m1 = (cv::Mat_<double>(3,3) << 1, 2, 0, 3, 1, 4, 1, 2, 1);

  std::cout << "m1=" << m1 << std::endl << std::endl;  

  m1.resize(2);
  // 2x3 行列
  std::cout << "m1=" << m1 << std::endl << std::endl;

  m1.resize(4);
  // 4x3 行列
  std::cout << "m1=" << m1 << std::endl << std::endl;

  m1.resize(5, cv::Scalar(100));
  // 5x3 行列(要素指定)
  std::cout << "m1=" << m1 << std::endl << std::endl;
}

実行結果:

m1=[1, 2, 0;
  3, 1, 4;
  1, 2, 1]

m1=[1, 2, 0;
  3, 1, 4]

m1=[1, 2, 0;
  3, 1, 4;
  0, 0, 0;
  0, 0, 0]

m1=[1, 2, 0;
  3, 1, 4;
  0, 0, 0;
  0, 0, 0;
  100, 100, 100]

1.7 cv::Matを変形する

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

int
main(int argc, char *argv[])
{
  // 1チャンネル,3x4 行列
  cv::Mat m1 = (cv::Mat_<double>(3,4) << 1,2,3,4,5,6,7,8,9,10,11,12);

  // 2チャンネル,3x2 行列
  cv::Mat m2 = m1.reshape(2);

  // 1チャンネル, 2x6 行列
  cv::Mat m3 = m1.reshape(1,2);

  // 1チャンネル, 2x6 行列
  cv::Mat m4 = m1.reshape(1,2);

  std::cout << "m1=" << m1 << std::endl << "ch=" << m1.channels() << std::endl << std::endl;
  std::cout << "m2=" << m2 << std::endl << "ch=" << m2.channels() << std::endl << std::endl; 
  std::cout << "m3=" << m3 << std::endl << "ch=" << m3.channels() << std::endl << std::endl;
  std::cout << "m4=" << m4 << std::endl << "ch=" << m4.channels() << std::endl << std::endl;
}

実行結果:

m1=[1, 2, 3, 4;
  5, 6, 7, 8;
  9, 10, 11, 12]
ch=1

m2=[1, 2, 3, 4;
  5, 6, 7, 8;
  9, 10, 11, 12]
ch=2

m3=[1, 2, 3, 4, 5, 6;
  7, 8, 9, 10, 11, 12]
ch=1

m4=[1, 2, 3, 4, 5, 6;
  7, 8, 9, 10, 11, 12]
ch=1

1.8 cv::Matの要素をシャッフルする

1 シングルチャンネル行列のシャッフル

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

int
main(int argc, char *argv[])
{
  // 4x5 の行列
  cv::Mat m1 = (cv::Mat_<double>(4,5) << 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
  std::cout << "m1(original)" << std::endl << m1 << std::endl << std::endl;
  
  cv::RNG gen(cv::getTickCount());
  // 行列,反復ファクタ,乱数生成器
  cv::randShuffle(m1, 2.0, &gen);
  // シャッフルされた行列を表示
  std::cout << "m1(shuffle)" << std::endl << m1 << std::endl << std::endl;

  // 部分行列のシャッフル
  cv::Mat m2 = m1(cv::Rect(1,1,3,2));
  std::cout << "m2(sub-matrix)" << std::endl << m2 << std::endl << std::endl;
  cv::randShuffle(m2, 2.0, &gen);
  std::cout << "m2(shuffle sub-matrix)" << std::endl << m2 << std::endl << std::endl;
  std::cout << "m1" << std::endl << m1 << std::endl << std::endl;
}

実行結果:

m1(original)
[1, 2, 3, 4, 5;
  6, 7, 8, 9, 10;
  11, 12, 13, 14, 15;
  16, 17, 18, 19, 20]

m1(shuffle)
[15, 3, 14, 11, 17;
  1, 8, 6, 2, 10;
  19, 18, 20, 7, 5;
  4, 16, 13, 12, 9]

m2(sub-matrix)
[8, 6, 2;
  18, 20, 7]

m2(shuffle sub-matrix)
[20, 2, 7;
  6, 18, 8]

m1
[15, 3, 14, 11, 17;
  1, 20, 2, 7, 10;
  19, 6, 18, 8, 5;
  4, 16, 13, 12, 9]

2 マルチチャンネル行列のシャッフル

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

int
main(int argc, char *argv[])
{
  // channels=2, 4x2の行列
  cv::Mat m0 = (cv::Mat_<double>(4,4) << 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
  cv::Mat m1 = m0.reshape(2);
  std::cout << "m1(original)" << std::endl << m1 << std::endl << std::endl;
  
  cv::RNG gen(cv::getTickCount());
  // 行列,反復ファクタ,乱数生成器
  cv::randShuffle(m1, 2.0, &gen);
  // シャッフルされた行列を表示
  std::cout << "m1(shuffle)" << std::endl << m1 << std::endl << std::endl;
}

実行結果:

m1(original)
[1, 2, 3, 4;
  5, 6, 7, 8;
  9, 10, 11, 12;
  13, 14, 15, 16]

m1(shuffle)
[3, 4, 1, 2;
  11, 12, 7, 8;
  13, 14, 5, 6;
  15, 16, 9, 10]

1.9 cv::Mat_<_Tp>を使う

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

int
main(int argc, char *argv[])
{
  cv::Mat m1 = (cv::Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
  cv::Mat_<double> m2 = m1.clone();
  
  /// 行列要素の直接指定
  // Mat の場合
  std::cout << "m1(1,1) = " << m1.at<double>(1,1) << std::endl;
  // Mat_<_Tp> の場合
  std::cout << "m2(1,1) = " << m2(1,1) << std::endl;

  /// 行列要素の,ポインタを介した指定
  // Mat の場合
  std::cout << "m1(1,2) = " << m1.ptr<double>(1)[2] << std::endl;
  // Mat_<_Tp> の場合
  std::cout << "m2(1,2) = " << m2[1][2] << std::endl;
}

実行結果:

m1(1,1) = 5
m2(1,1) = 5
m1(1,2) = 6
m2(1,2) = 6

1.10 cv::Matxを使う

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

int
main(int argc, char *argv[])
{
  // float型,1x2 の行列
  cv::Matx21f m1(1,2,10,20);
  // double型,3x3 の行列
  cv::Matx33d m2(1,3,5,2,4,6,7,8,9);
  // double型,5x1 の行列
  cv::Matx<double,5,1> m3(10,11,12,13,14);
  // float型, 2x11 の行列
  cv::Matx<float,2,11> m4;
  for(int j=0; j<2; ++j) 
    for(int i=0; i<11; ++i) 
      m4(j,i) = i+j;
  
  // Matに変換してstdに出力することができる
  std::cout << "m1=" << cv::Mat(m1) << std::endl << std::endl;
  std::cout << "m2=" << cv::Mat(m2) << std::endl << std::endl;
  std::cout << "m3=" << cv::Mat(m3) << std::endl << std::endl;
  std::cout << "m4=" << cv::Mat(m4) << std::endl << std::endl;
}

実行結果:

m1=[1; 2]

m2=[1, 3, 5;
  2, 4, 6;
  7, 8, 9]

m3=[10; 11; 12; 13; 14]

m4=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

1.11 cv::Vecを使う

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

int
main(int argc, char *argv[])
{
  // 320x240 の3チャンネルカラー画像を確保して,緑色で埋めます.
  cv::Mat_<cv::Vec3b> img(300, 300, cv::Vec3b(0,200,0));

  // 対角線上に白い線を描きます.
  for(int i=0; i<img.cols; ++i)
    img(i,i)=cv::Vec3b(255, 255, 255);
  
  cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::imshow("image", img);
  cv::waitKey(0);
}

実行結果:

_images/sample_vec.png

1.12 cv::MatとIplImageの相互変換

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

int
main(int argc, char *argv[])
{
  // IplImage -> Mat(コンストラクタ)
  cv::Ptr<IplImage> ipl_img1 = cvLoadImage("../../image/lenna.png", CV_LOAD_IMAGE_COLOR);
  cv::Mat mat_img1a(ipl_img1);       // データを共有する
  CV_Assert(reinterpret_cast<uchar*>(ipl_img1->imageData) == mat_img1a.data);
  cv::Mat mat_img1b(ipl_img1, true); // データをコピーする
  CV_Assert(reinterpret_cast<uchar*>(ipl_img1->imageData) != mat_img1b.data);

  // IplImage -> Mat(関数で変換)
  cv::Mat mat_img1c = cv::cvarrToMat(ipl_img1);  // データをコピーする
  CV_Assert(reinterpret_cast<uchar*>(ipl_img1->imageData) != mat_img1b.data);

  // Mat -> IplImage 
  cv::Mat mat_img2 = cv::imread("../../image/lenna.png", 1);
  IplImage ipl_img2 = mat_img2; // データを共有する
  CV_Assert(reinterpret_cast<uchar*>(ipl_img2.imageData) == mat_img2.data);
}

1.13 cv::MatとCvMatの相互変換

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

int
main(int argc, char *argv[])
{
  double data[] = { 1, 2, 3, 4, 5, 6, 7, 8,  9, 10, 11, 12 };
  CvMat cv_mat1;
  cvInitMatHeader(&cv_mat1, 3, 4, CV_64FC1, data);

  // CvMat -> cv::Mat
  cv::Mat mat1(&cv_mat1); // データを共有する
  CV_Assert(cv_mat1.data.ptr == mat1.data);
  cv::Mat mat2(&cv_mat1, true); // データをコピーする
  CV_Assert(cv_mat1.data.ptr != mat2.data);

  std::cout << "mat1=" << mat1 << std::endl << std::endl;

  // cv::Mat -> CvMat
  CvMat cv_mat2 = mat2; // データを共有する
  CV_Assert(cv_mat2.data.ptr == mat2.data);
}

1.14 cv::MatとSTL vectorの相互変換

#include <iostream>
#include <opencv2/core/core.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)

int
main(int argc, char *argv[])
{
   cv::Mat m1 = (cv::Mat_<double>(3,3) << 1, 2, 0, 3, 1, 4, 1, 2, 1);
   std::vector<double> v1;

   // Mat -> vector
   m1 = m1.reshape(0, 1); // 1行の行列に変形
#if OPENCV_VERSION_CODE<OPENCV_VERSION(2,3,0)
   m1.copyTo<double>(v1); // データをコピー
#else
   m1.copyTo(v1); // データをコピー
#endif

   std::cout << "m1=" << m1 << std::endl << std::endl;
   for(std::vector<double>::iterator it=v1.begin(); it!=v1.end(); ++it)
     std::cout << *it << ", ";
   std::cout << std::endl;
   
   // vector -> Mat
   cv::Mat m2(v1); // データを共有
   cv::Mat m3(v1, true); // データをコピー
}

1.15 cv::Matとcv::Matxの相互変換

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

int
main(int argc, char *argv[])
{
  // double型,3x3 の行列
  cv::Matx33d mx1(1,3,5,2,4,6,7,8,9);

  // Matx -> Mat
  cv::Mat m1(mx1, false); // データを共有
  cv::Mat m2(mx1); // データをコピー
    
  // Mat -> Matx
  cv::Matx33d mx2 = m1; // データを共有
}

1.16 cv::Matとcv::SparseMatの相互変換

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

int
main(int argc, char *argv[])
{
  // 3x3 の行列
  cv::Mat mat1 = (cv::Mat_<double>(3,3) << 1, 2, 0, 0, 0, 6, 7, 8, 9);

  // Mat -> SparseMat
  cv::SparseMat sparse_mat1(mat1);
  cv::SparseMat sparse_mat2;
  sparse_mat2 = mat1;

  // SparseMat -> Mat
  cv::Mat mat2;
  sparse_mat1.copyTo(mat2);
  CV_Assert(mat1.data != mat2.data);

  // 表示
  std::cout << "mat1" << mat1 << std::endl << std::endl;
  std::cout << "sparse_mat1" << std::endl;
  cv::SparseMatConstIterator it1 = sparse_mat1.begin();
  for(; it1!=sparse_mat1.end(); ++it1)
    std::cout << it1.value<double>() << ", ";
  std::cout << std::endl;
  std::cout << "sparse_mat2" << std::endl;
  cv::SparseMatConstIterator it2 = sparse_mat2.begin();
  for(; it2!=sparse_mat2.end(); ++it2)
    std::cout << it2.value<double>() << ", ";
  std::cout << std::endl << std::endl;
  std::cout << "mat2" << mat2 << std::endl << std::endl;
}

実行結果:

mat1[1, 2, 0;
  0, 0, 6;
  7, 8, 9]

sparse_mat1
1, 2, 7, 8, 9, 6, 
sparse_mat2
1, 2, 7, 8, 9, 6, 

mat2[1, 2, 0;
  0, 0, 6;
  7, 8, 9]

1.17 cv::MatとEigen::Matrixの相互変換

1 cv::MatからEigen::Matrixへの変換

#include <iostream>
#include <Eigen/Core>
#include <opencv2/core/core.hpp>
#include <opencv2/core/eigen.hpp>

int
main(int argc, char *argv[])
{
  // 3x3, double の行列
  Eigen::Matrix3d eigen_mat;
  eigen_mat << 1.1, 1.2, 1.3,
               2.1, 2.2, 2.3,
               3.1, 3.2, 3.3;
  cv::Mat cv_mat;
  // convert from Eigen::Matrix to cv::Mat
  cv::eigen2cv(eigen_mat, cv_mat);

  std::cout << "cv_mat:\n" << cv_mat << std::endl << std::endl;
  std::cout << "eigen_mat:\n" << eigen_mat << std::endl << std::endl;
}

実行結果:

cv_mat:
[1.1, 1.2, 1.3;
  2.1, 2.2, 2.3;
  3.1, 3.2, 3.3]

eigen_mat:
1.1 1.2 1.3
2.1 2.2 2.3
3.1 3.2 3.3

2 Eigen::Matrixからcv::Matへの変換

#include <iostream>
#include <Eigen/Core>
#include <opencv2/core/core.hpp>
#include <opencv2/core/eigen.hpp>

int
main(int argc, char *argv[])
{
  // 3x3 の行列
  cv::Mat cv_mat = (cv::Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
  Eigen::Matrix<double, 3, 3> eigen_mat1; // コンパイル時に行列サイズが既知の場合
  // convert from cv::Mat to Eigen::Matrix
  cv::cv2eigen(cv_mat, eigen_mat1);

  std::cout << "cv_mat:\n" << cv_mat << std::endl << std::endl;
  std::cout << "eigen_mat1:\n" << eigen_mat1 << std::endl << std::endl;

  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> eigen_mat2; // 動的な行列
  // convert from cv::Mat to Eigen::Matrix
  cv::cv2eigen(cv_mat, eigen_mat2);
  std::cout << "eigen_mat2:\n" << eigen_mat2 << std::endl << std::endl;
}

実行結果:

cv_mat:
[1, 2, 3;
  4, 5, 6;
  7, 8, 9]

eigen_mat1:
1 2 3
4 5 6
7 8 9

eigen_mat2:
1 2 3
4 5 6
7 8 9

1.18 チャンネルの合成と分離

1 単純な合成:merge

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

int
main(int argc, char *argv[])
{
  // 2x2 の行列
  cv::Mat m1 = (cv::Mat_<double>(2,2)<<1.0, 2.0, 3.0, 4.0);
  cv::Mat m2 = (cv::Mat_<double>(2,2)<<1.1, 2.1, 3.1, 4.1);
  cv::Mat m3 = (cv::Mat_<double>(2,2)<<1.2, 2.2, 3.2, 4.2);

  // mergeに与えるのは,cv::Matのvectorでも配列でも構わない
  std::vector<cv::Mat> mv;
  mv.push_back(m1);
  mv.push_back(m2);
  mv.push_back(m3);
  
  // マージ
  cv::Mat m_merged;
  cv::merge(mv, m_merged);

  std::cout << "m_merged=" << m_merged << std::endl << std::endl;
}

実行結果:

m_merged=[1, 1.1, 1.2, 2, 2.1, 2.2;
  3, 3.1, 3.2, 4, 4.1, 4.2]

2 複雑な合成:mixChannels

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

int
main(int argc, char *argv[])
{
  // 2x2 の行列
  cv::Mat m1 = (cv::Mat_<double>(2,2)<<1.0, 2.0, 3.0, 4.0);
  cv::Mat m2 = (cv::Mat_<double>(2,2)<<1.1, 2.1, 3.1, 4.1);
  cv::Mat m3 = (cv::Mat_<double>(2,2)<<1.2, 2.2, 3.2, 4.2);

  // mixChannlesに与えるのは,cv::Matのvectorでも配列でも構わない
  std::vector<cv::Mat> mv;
  mv.push_back(m1);
  mv.push_back(m2);
  mv.push_back(m3);
  
  // 出力用 Mat は,必ず割り当てる必要がある
  cv::Mat m_mixed1(2,2, CV_64FC2);
  cv::Mat m_mixed2(2,2, CV_64FC2);
  int fromTo[] = {0,0, 1,1, 1,3, 2,2};
  // mixChannlesに与えるのは,cv::Matのvectorでも配列でも構わない
  std::vector<cv::Mat> mixv;
  mixv.push_back(m_mixed1);
  mixv.push_back(m_mixed2);

  // ミックス
  cv::mixChannels(mv, mixv, fromTo, 4);

  std::cout << "m_mixed1=" << m_mixed1 << std::endl << std::endl;
  std::cout << "m_mixed2=" << m_mixed2 << std::endl << std::endl;
}

実行結果:

m_mixed1=[1, 1.1, 2, 2.1;
  3, 3.1, 4, 4.1]

m_mixed2=[1.2, 1.1, 2.2, 2.1;
  3.2, 3.1, 4.2, 4.1]

3 分離

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

int
main(int argc, char *argv[])
{
  cv::Mat m0 = (cv::Mat_<int>(3,6) << 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18);
  // channels=3, 3x2 の行列
  cv::Mat m1 = m0.reshape(3,2);
  std::cout << "m1" << std::endl << m1 << std::endl << std::endl;;

  std::vector<cv::Mat> planes;
  // チャンネル毎に分離
  cv::split(m1, planes);
  std::vector<cv::Mat>::const_iterator it = planes.begin();
  for(; it!=planes.end(); ++it) {
    std::cout << *it << std::endl << std::endl;;
  }
}

実行結果:

[1, 4, 7;
  10, 13, 16]

[2, 5, 8;
  11, 14, 17]

[3, 6, 9;
  12, 15, 18]

1.19 列/行ごとの合計,平均値,最小,最大値を求める

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

int
main(int argc, char *argv[])
{
  // 3x3 の行列
  cv::Mat m1 = (cv::Mat_<double>(3,3) << 1, 5, 3, 4, 2, 6, 7, 8, 9);

  cv::Mat v1, v2, v3 ,v4;
  // 3x3の行列を1行に縮小
  cv::reduce(m1, v1, 0, CV_REDUCE_SUM); // 各列の合計値
  cv::reduce(m1, v2, 0, CV_REDUCE_AVG); // 各列の平均値
  cv::reduce(m1, v3, 0, CV_REDUCE_MIN); // 各列の最小値
  cv::reduce(m1, v4, 0, CV_REDUCE_MAX); // 各列の最大値
  
  std::cout << "m1=" << m1 << std::endl << std::endl;
  std::cout << "v1(sum)=" << v1 << std::endl;
  std::cout << "v2(avg)=" << v2 << std::endl;
  std::cout << "v3(min)=" << v3 << std::endl;
  std::cout << "v4(max)=" << v4 << std::endl;

  // 3x3の行列を1列に縮小
  cv::reduce(m1, v1, 1, CV_REDUCE_SUM);
  cv::reduce(m1, v2, 1, CV_REDUCE_AVG);
  cv::reduce(m1, v3, 1, CV_REDUCE_MIN);
  cv::reduce(m1, v4, 1, CV_REDUCE_MAX);
  
  std::cout << "m1=" << m1 << std::endl << std::endl;
  std::cout << "v1(sum)=" << v1 << std::endl;
  std::cout << "v2(avg)=" << v2 << std::endl;
  std::cout << "v3(min)=" << v3 << std::endl;
  std::cout << "v4(max)=" << v4 << std::endl;
}

実行結果:

m1=[1, 5, 3;
  4, 2, 6;
  7, 8, 9]

v1(sum)=[12, 15, 18]
v2(avg)=[4, 5, 6]
v3(min)=[1, 2, 3]
v4(max)=[7, 8, 9]
m1=[1, 5, 3;
  4, 2, 6;
  7, 8, 9]

v1(sum)=[9; 12; 24]
v2(avg)=[3; 4; 8]
v3(min)=[1; 2; 7]
v4(max)=[5; 6; 9]