現在、作成中…
もくじ
- 行列の作成と操作
- 部分行列の操作
1. 行列の作成と操作
1.1 Matによる行列の作成
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 空の行列を宣言する cv::Mat mat1; // 大きさと型を指定して行列を宣言する(2行3列、float型) // 初期化をしない // # 型の表記 // # type値 型名 C言語型名 // # 0 CV_8U uchar (= unsigned char) // # 1 CV_8S char // # 2 CV_16U ushort (= unsigned short) // # 3 CV_16S short // # 4 CV_32S int // # 5 CV_32F float // # 6 CV_64F double cv::Mat mat2(2, 3, CV_32F); // 大きさと型と初期値を指定して行列を宣言する(3行4列、float型) cv::Mat mat3(3, 4, CV_32F, cv::Scalar::all(1.5)); // 行列を表示する cout << "mat1=\n" << mat1 << endl; // 空のため、値は表示されない cout << "mat2=\n" << mat2 << endl; // 初期化されていないため、デタラメな値が表示される cout << "mat3=\n" << mat3 << endl; // 行列の大きさと型を表示する cout << "mat3の行数 = " << mat3.rows << endl; cout << "mat3の列数 = " << mat3.cols << endl; cout << "mat3の型 = " << mat3.type() << endl; // 行列を解放する mat3.release(); cout << "mat3を解放した\n" "mat3=\n" << mat3 << endl; // mat3が空になっている // 零行列、単位行列の設定 cv::Mat mat4, mat5, mat6; mat4 = cv::Mat::zeros(3, 3, CV_32F); // 零行列 mat5 = cv::Mat::eye(3, 3, CV_32F); // 単位行列 mat6 = cv::Mat::ones(3, 3, CV_32F); // 全要素が1の行列 // 行列を表示する cout << "mat4=\n" << mat4 << endl; cout << "mat5=\n" << mat5 << endl; cout << "mat6=\n" << mat6 << endl; return 0; } |
1.2 Mat_<Type>による行列の作成
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // cv::Mat_<Type>型で行列を宣言する // # Typeに指定する型名 // # uchar (= unsigned char) // # char // # ushort (= unsigned short) // # short // # float // # double cv::Mat_<float> mat1; // 大きさを指定して行列を宣言する cv::Mat_<float> mat2(2, 3); // 大きさと初期値を指定して行列を宣言する cv::Mat_<float> mat3(2, 3, 1.5); // 行列を表示する cout << "mat1=\n" << mat1 << endl; // 空のため、値は表示されない cout << "mat2=\n" << mat2 << endl; // 初期化されていないため、デタラメな値が表示される cout << "mat3=\n" << mat3 << endl; // 行列の大きさと型を表示する cout << "mat3の行数 = " << mat3.rows << endl; cout << "mat3の列数 = " << mat3.cols << endl; cout << "mat3の型 = " << mat3.type() << endl; // 行列を解放する mat3.release(); cout << "mat3を解放した\n" "mat3=\n" << mat3 << endl; // mat3が空になっている // 零行列、単位行列の設定 cv::Mat_<float> mat4, mat5, mat6; mat4 = cv::Mat_<float>::zeros(3, 3); // 零行列 mat5 = cv::Mat_<float>::eye(3, 3); // 単位行列 mat6 = cv::Mat_<float>::ones(3, 3); // 全要素が1の行列 // 行列を表示する cout << "mat4=\n" << mat4 << endl; cout << "mat5=\n" << mat5 << endl; cout << "mat6=\n" << mat6 << endl; return 0; } |
1.3 行列の要素の読み書き
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 64 65 66 67 68 69 70 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // ushort(CV_16U)型の行列を宣言する cv::Mat mat1(2, 3, CV_16U); cv::Mat_<ushort> mat2(2, 3); // 全要素に同じ値を代入する mat1 = 1; mat2 = 2; // 行列を表示する cout << "**** 初期値 ****" << endl; cout << "mat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // 型を確認 cout << "型を確認 CV_16U = " << CV_16U << endl; cout << " mat1の型 = " << mat1.type(); cout << ", mat2の型 = " << mat2.type() << endl; // 要素ごとに値を代入する // # cv::Mat_<Type>の方がシンプルに書ける mat1 = (cv::Mat_<ushort>(2, 3) << 2, 3, 5, 7, 11, 13); mat2 << 2, 3, 5, 7, 11, 13; // 行列を表示する cout << "**** 全要素を変更 ****" << endl; cout << "mat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // 1要素の値を読み出す // # 0行2列目の値を表示する // # cv::Mat_<Type>の方がシンプルに書ける cout << "**** 0行2列目を読み出し ****" << endl; cout << "mat1(0,2) = " << mat1.at<ushort>(0, 2) << endl; cout << "mat2(0,2) = " << mat2(0, 2) << endl; // 1要素の値を書き込む // # 0行2列目の要素を 101 にする // # cv::Mat_<Type>の方がシンプルに書ける mat1.at<ushort>(0, 2) = 101; mat2(0, 2) = 101; // 行列を表示する cout << "**** 0行2列目を変更 ****" << endl; cout << "mat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // 大きさと型を変える // # 2行2列、float(CV_32F)型の行列に変える // # cv::Matで宣言したオブジェクトは、別の型、別の大きさに変更できるが、 // # cv::Mat<Type>で宣言したオブジェクトは、型を変えることができない mat1 = (cv::Mat_<float>(2, 2) << 0.5, 2.2, -5.0, 999999); mat2 = (cv::Mat_<float>(2, 2) << 0.5, 2.2, -5.0, 999999); // 行列を表示する cout << "**** 行列の型、大きさ、値を変更 ****" << endl; cout << "mat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // 型を確認 cout << "型を確認 CV_32F = " << CV_32F << endl; cout << " mat1の型 = " << mat1.type(); // 値が正しく設定できている cout << ", mat2の型 = " << mat2.type() << endl; // 型を変えられない。値は表現可能な範囲に丸められる。 return 0; } |
1.4 行列の計算
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 64 65 66 67 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 行列を宣言する cv::Mat mat1 = (cv::Mat_<float>(2, 2) << 1, 2, 3, 4); cv::Mat mat2 = (cv::Mat_<float>(2, 2) << 1, 3, 4, 2); cv::Mat matAns; cout << "mat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // 行列の和を計算する matAns = mat1 + mat2; cout << "mat1+mat2=\n" << matAns << endl; // 行列の積を計算する matAns = mat1 * mat2; cout << "mat1*mat2=\n" << matAns << endl; // 行列と定数の和を計算する matAns = mat1 + 5; cout << "mat1+5=\n" << matAns << endl; // 行列と定数の積を計算する matAns = mat1 * 2; cout << "mat1*2=\n" << matAns << endl; // 行列の要素ごとに積を計算する matAns = mat1.mul(mat2); cout << "mat1xmat2=\n" << matAns << endl; // 転置行列を求める matAns = mat1.t(); cout << "mat1.t()=\n" << matAns << endl; // 逆行列を求める matAns = mat1.inv(); cout << "mat1.inv()=\n" << matAns << endl; // 行列式を求める double det = cv::determinant(mat1); cout << "det(mat1)=" << det << endl; // ベクトル(1行3列の行列)を宣言する cv::Mat vec1 = (cv::Mat_<float>(1, 3) << 1, 2, 3); cv::Mat vec2 = (cv::Mat_<float>(1, 3) << 6, 3, 1); cout << "vec1 = " << vec1 << endl; cout << "vec2 = " << vec2 << endl; // ベクトルの内積を計算する double dot = vec1.dot(vec2); cout << "vec1とvec2の内積 = " << dot << endl; // ベクトルの外積を計算する // # 与える行列は、1行3列、もしくは、3行1列でなければならない matAns = vec1.cross(vec2); cout << "vec1とvec2の外積 = " << matAns << endl; // ベクトルのノルムを計算する double norm = cv::norm(vec1, cv::NORM_L2); cout << "vec1のL2ノルム = " << norm << endl; return 0; } |
1.5 行列の浅いコピーと深いコピー
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 行列を宣言する cv::Mat mat1 = (cv::Mat_<float>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9); cv::Mat mat2, mat3; cout << "変更前のmat1=\n" << mat1 << endl; // 行列の浅いコピー // # mat2は、mat1の記憶域にあるデータを参照する mat2 = mat1; // 行列の深いコピー // # mat1の複製を作り、それをmat3へ代入する // # mat1.copyTo(mat3) でも可。 mat3 = mat1.clone(); // mat1のデータを変えてみる mat1 = cv::Mat_<float>::ones(3, 3); cout << "変更後のmat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // mat2は変更後のmat1と同じ cout << "mat3=\n" << mat3 << endl; // mat3は変更前のmat1と同じ // mat1を解放する // # mat1とmat2は同じデータを共有しており、まだmat2がそのデータを参照しているため、 // # ここではmat1からの参照が外されるだけでデータは存続する mat1.release(); cout << "解放後のmat1=\n" << mat1 << endl; cout << "mat2=\n" << mat2 << endl; // mat1を解放してもmat2は変化しない return 0; } |
1.6 線形代数の問題を解く
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 2変数の連立1次方程式 cout << " x+2y = 9\n"; cout << "2x-3y = 4 を解く" << endl; // | 1 2 | | x | | 9 | // | | | | = | | // | 2 -3 | | y | | 4 | // // 行列[1, 2; 2, -3]をlhand、行列[9; 4]をrhandとする // 行列[x; y]をansとする cv::Mat lhand = (cv::Mat_<float>(2, 2) << 1, 2, 2, -3); cv::Mat rhand = (cv::Mat_<float>(2, 1) << 9, 4); cv::Mat ans; // 解を求める cv::solve(lhand, rhand, ans); cout << "x=" << ans.at<float>(0, 0) << endl; cout << "y=" << ans.at<float>(1, 0) << endl << endl; // 3点の座標から曲線の式を求める cout << "3点 (1,0),(-1,8),(4,3) を通る2次曲線 y=ax^2+bx+c を求める" << endl; lhand = (cv::Mat_<float>(3, 3) << 1 * 1, 1, 1, (-1)*(-1), -1, 1, 4 * 4, 4, 1); rhand = (cv::Mat_<float>(3, 1) << 0, 8, 3); cv::solve(lhand, rhand, ans); cout << "a=" << ans.at<float>(0, 0) << endl; cout << "b=" << ans.at<float>(1, 0) << endl; cout << "c=" << ans.at<float>(2, 0) << endl << endl; // 4点の座標から近似直線を求める cout << "4点 (1,1),(3,6),(5,9),(7,11) の近似直線 y=ax+b を求める" << endl; lhand = (cv::Mat_<float>(4, 2) << 1, 1, 3, 1, 5, 1, 7, 1); rhand = (cv::Mat_<float>(4, 1) << 1, 6, 9, 11); cv::solve(lhand, rhand, ans, cv::DECOMP_SVD); // 特異値分解(SVD)で解く cout << "a=" << ans.at<float>(0, 0) << endl; cout << "b=" << ans.at<float>(1, 0) << endl; return 0; } |
2. 部分行列の操作
2.1 1行、1列を取り出す
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 4行4列の行列を宣言する cv::Mat mat1 = (cv::Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); cv::Mat mat2 = cv::Mat::eye(4, 4, CV_32S); cv::Mat mat3, mat4, mat5; // mat1の第1行をmat3へ代入(浅いコピー) // # 一番上が第0行 mat3 = mat1.row(1); // mat1の第2列をmat4へ代入(浅いコピー) // # 一番左が第0列 mat4 = mat1.col(2); // mat1の第1行をmat5へ代入(深いコピー) mat1.row(1).copyTo(mat5); // mat3, mat4, mat5を表示 cout << "mat3=\n" << mat3 << endl; cout << "mat4=\n" << mat4 << endl; cout << "mat5=\n" << mat5 << endl; // mat1の値を変える mat1 = 9; cout << "mat1=\n" << mat1 << endl; // mat3とmat4を表示 // mat1の浅いコピーなのでmat3とmat4の値も変わる cout << "mat3=\n" << mat3 << endl; cout << "mat4=\n" << mat4 << endl; // mat5を表示 // mat1の深いコピーなので値は変わらない cout << "mat5=\n" << mat5 << endl; cout << endl; // mat2の第2行をmat1の第1行に代入【誤った書き方】 mat1.row(1) = mat2.row(2); cout << "mat1=\n" << mat1 << endl; // 代入できていない // mat2の第2行をmat1の第1行に代入【正しい書き方】 mat2.row(2).copyTo(mat1.row(1)); cout << "mat1=\n" << mat1 << endl; // 代入できている return 0; } |
2.2 複数行、複数列を取り出す
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 4行4列の行列を宣言する cv::Mat mat1 = (cv::Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); cv::Mat mat2 = cv::Mat::eye(4, 4, CV_32S); cv::Mat mat3, mat4, mat5; // mat1の第1行から第2行をmat3へ代入(浅いコピー) // # rowRange(r1, r2) r1 <= r < r2 の範囲 mat3 = mat1.rowRange(1, 3); // mat1の第1列から第3列をmat4へ代入(浅いコピー) // # colRange(c1, c2) c1 <= c < c2 の範囲 mat4 = mat1.colRange(1, 4); // mat1の第1行から第2行をmat5へ代入(深いコピー) mat1.rowRange(1, 3).copyTo(mat5); // mat3, mat4, mat5を表示 cout << "mat3=\n" << mat3 << endl; cout << "mat4=\n" << mat4 << endl; cout << "mat5=\n" << mat5 << endl; // mat1の値を変える mat1 = 9; cout << "mat1=\n" << mat1 << endl; // mat3とmat4を表示 // mat1の浅いコピーなのでmat3とmat4の値も変わる cout << "mat3=\n" << mat3 << endl; cout << "mat4=\n" << mat4 << endl; // mat5を表示 // mat1の深いコピーなので値は変わらない cout << "mat5=\n" << mat5 << endl; cout << endl; // mat2の第0行から第2行をmat1の第1行から第3行に代入【誤った書き方】 mat1.rowRange(1, 4) = mat2.rowRange(0, 3); cout << "mat1=\n" << mat1 << endl; // 代入できていない // mat2の第0行から第2行をmat1の第1行から第3行に代入【正しい書き方】 mat2.rowRange(0, 3).copyTo(mat1.rowRange(1, 4)); cout << "mat1=\n" << mat1 << endl; // 代入できている return 0; } |
2.3 ROIを設定して部分行列を作る
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 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 4行4列の行列を宣言する cv::Mat mat1 = (cv::Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); cv::Mat mat2 = cv::Mat::eye(4, 4, CV_32S); cv::Mat mat3, mat4, mat5; // mat1の第2行第0列から第3行第1列の範囲をmat3へ代入(浅いコピー) // # 範囲は cv::Rectで指定する // # cv::Rect(列, 行, 列数, 行数) // # Rectはxy座標系で表すため、列, 行の順になることに注意 // # この範囲のことを ROI (Region Of Interest) (関心領域、対象領域)と呼ぶ cv::Rect rect(0, 2, 2, 2); mat3 = mat1(rect); // mat1の第2行第0列から第3行第1列の範囲をmat3へ代入(深いコピー) mat1(rect).copyTo(mat4); // mat3, mat4を表示 cout << "mat3=\n" << mat3 << endl; cout << "mat4=\n" << mat4 << endl; // mat1の値を変える mat1 = 9; cout << "mat1=\n" << mat1 << endl; // mat3を表示 // mat1の浅いコピーなのでmat3の値も変わる cout << "mat3=\n" << mat3 << endl; // mat4を表示 // mat1の深いコピーなので値は変わらない cout << "mat4=\n" << mat4 << endl; cout << endl; // mat2の第0行第0列から第3行第1列までをmat1の第0行第2列から第3行第3列に代入 cv::Rect rect1(2, 0, 2, 4), rect2(0, 0, 2, 4); mat2(rect2).copyTo(mat1(rect1)); cout << "mat1=\n" << mat1 << endl; return 0; } |