Pynote

Python、機械学習、画像処理について

OpenCV - cv::Mat で部分行列を作成する方法

試した環境

部分行列を作成する方法の一覧

cv::Mat の行及び列のインデックス

f:id:nekobean:20180416164540p:plain

行及び列は 0, 1, ... とインデックスされている。

データの共有

以下で紹介する関数で部分行列を作成した場合、元の行列とデータを共有した状態となる。
そのため、一方を変更するともう一方にも変更が反映される。

cv::Mat matrix = cv::Mat::ones(3, 3, CV_8UC1);
// 部分行列を作成する。
cv::Mat subMatrix = matrix(cv::Rect(0, 0, 2, 2));
// 部分行列の値を変更する。
subMatrix.at<uint8_t>(0, 0) = 5;

std::cout << matrix << std::endl;
std::cout << subMatrix << std::endl;
[  5,   1,   1;
   1,   1,   1;
   1,   1,   1]
[  5,   1;
   1,   1]

元の行列とは独立して作成したい場合は、部分行列を作成したあとに clone() で複製する。

cv::Mat matrix = cv::Mat::ones(3, 3, CV_8UC1);
// 部分行列を作成し、clone() でディープコピーする。
cv::Mat subMatrix = matrix(cv::Rect(0, 0, 2, 2)).clone();
// 部分行列の値を変更する。
subMatrix.at<uint8_t>(0, 0) = 5;

std::cout << matrix << std::endl;
std::cout << subMatrix << std::endl;
[  1,   1,   1;
   1,   1,   1;
   1,   1,   1]
[  5,   1;
   1,   1]

行及び列をそれぞれ範囲指定して部分行列を作成する

Mat(const Mat &m, const Range &rowRange, const Range &colRange=Range::all());
Mat operator() (Range rowRange, Range colRange) const;

cv::Range(begin, end) で指定する場合、begin ~ end - 1 のインデックスが対象で end 自身は含まれないので注意する。

f:id:nekobean:20180416164616p:plain

// 元の行列
uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
cv::Mat mat(3, 3, CV_8UC1, data);

// 部分行列 [1:3, 1:3] を作成する。
// コンストラクタ
cv::Mat subMat(mat, cv::Range(1, 3), cv::Range(1, 3));
std::cout << subMat << std::endl;

// 関数呼び出し演算子
std::cout << mat(cv::Range(1, 3), cv::Range(1, 3)) << std::endl;
[  5,   6;
   8,   9]
[  5,   6;
   8,   9]

cv::Rect で指定して部分行列を作成する

Mat(const Mat &m, const Rect &roi);
Mat operator() (const Rect &roi) const;

// 元の行列
uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
cv::Mat mat(cv::Size(3, 3), CV_8UC1, data);

// 部分配列 [1:3, 1:3] を作成する。
// コンストラクタ
cv::Mat subMat(mat, cv::Rect(1, 1, 2, 2));
std::cout << subMat << std::endl;

// 関数呼び出し演算子
std::cout << mat(cv::Rect(1, 1, 2, 2)) << std::endl;
[  5,   6;
   8,   9]
[  5,   6;
   8,   9]

行または列を範囲指定して部分行列を作成する

Mat rowRange(int startrow, int endrow) const;
Mat rowRange(const Range &r) const;

f:id:nekobean:20180416164636p:plain
f:id:nekobean:20180416164642p:plain

uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
cv::Mat mat(3, 3, CV_8UC1, data);

std::cout << mat.rowRange(1, 3) << std::endl;
std::cout << mat.colRange(0, 2) << std::endl;
[  4,   5,   6;
   7,   8,   9]
[  1,   2;
   4,   5;
   7,   8]

行または列を指定して部分行列を作成する

Mat row(int y) const;
Mat col(int x) const;

f:id:nekobean:20180416164657p:plain
f:id:nekobean:20180416164802p:plain

uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
cv::Mat mat(3, 3, CV_8UC1, data);

std::cout << mat.row(2) << std::endl;
std::cout << mat.col(1) << std::endl;
[  7,   8,   9]
[  2;
   5;
   8]

対角成分から部分行列を作成する

Mat diag(int d=0) const

引数 d を与えた場合、正の場合は上に、負の場合は下に対角成分を d だけずらした部分から作成する。

f:id:nekobean:20180416164814p:plain
f:id:nekobean:20180416164819p:plain
f:id:nekobean:20180416164845p:plain

uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
cv::Mat mat(3, 3, CV_8UC1, data);

std::cout << mat.diag() << std::endl;
std::cout << mat.diag(1) << std::endl;
std::cout << mat.diag(-1) << std::endl;
[  1;
   5;
   9]
[  2;
   6]
[  4;
   8]

部分行列の範囲を変更する。

Mat &adjustROI(int dtop, int dbottom, int dleft, int dright)

部分行列の範囲は後から変更する場合に使用する。

f:id:nekobean:20180416165009p:plain

cv::Mat mat(8, 8, CV_8UC1);
for (int i = 0; i < 64; i++) {
    int row = i / 8;
    int col = i % 8;
    mat.at<uint8_t>(row, col) = i;
}
std::cout << mat << std::endl;

// 部分配列 [1:3, 1:3] を作成する。
cv::Mat subMat(mat, cv::Rect(2, 2, 2, 2));
std::cout << subMat << std::endl;

subMat.adjustROI(1, 1, 2, 2);
std::cout << subMat << std::endl;
[  0,   1,   2,   3,   4,   5,   6,   7;
   8,   9,  10,  11,  12,  13,  14,  15;
  16,  17,  18,  19,  20,  21,  22,  23;
  24,  25,  26,  27,  28,  29,  30,  31;
  32,  33,  34,  35,  36,  37,  38,  39;
  40,  41,  42,  43,  44,  45,  46,  47;
  48,  49,  50,  51,  52,  53,  54,  55;
  56,  57,  58,  59,  60,  61,  62,  63]
[ 18,  19;
  26,  27]
[  8,   9,  10,  11,  12,  13;
  16,  17,  18,  19,  20,  21;
  24,  25,  26,  27,  28,  29;
  32,  33,  34,  35,  36,  37]

行列の一部に別の行列を代入する。

代入したい行列を copyTo() で部分行列にコピーすることで実現できる。

cv::Mat a = cv::Mat::ones(3, 3, CV_8UC1);   // 3x3 の行列
cv::Mat b = cv::Mat(cv::Point3i(2, 3, 4));  // 3x1 の行列
std::cout << a << std::endl;
std::cout << b << std::endl;

b.copyTo(a.col(2));  // 部分行列の部分に代入する。
std::cout << a << std::endl;
[  1,   1,   1;
   1,   1,   1;
   1,   1,   1]
[2;
 3;
 4]
[  1,   1,   2;
   1,   1,   3;
   1,   1,   4]