OpenCV (C++) の基本的なデータ型について記載します。固定長の配列と、動的にメモリ領域を確保する可変長の配列があります。
点クラスは、メンバ変数に .x,y,z
でアクセスできる、固定長配列の一つです。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Point3i p(1, 2, 3);
std::cout << p.x << std::endl; //=> 1
std::cout << p.y << std::endl; //=> 2
std::cout << p.z << std::endl; //=> 3
return 0;
}
内積 (単精度、倍精度)
float dot = p.dot(p);
double ddot = p.ddot(p);
外積
p.cross(p)
固定長配列の一つです。要素数は 4 です。四元数を表現できます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Scalar s(1, 2, 3, 4);
std::cout << s[0] << std::endl; //=> 1
std::cout << s[1] << std::endl; //=> 2
std::cout << s[2] << std::endl; //=> 3
std::cout << s[3] << std::endl; //=> 4
return 0;
}
メンバ変数に .width,height
でアクセスできる、固定長配列の一つです。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Size2i sz(2, 5);
std::cout << sz.width << std::endl; //=> 2
std::cout << sz.height << std::endl; //=> 5
std::cout << sz.area() << std::endl; //=> 10
return 0;
}
x軸、y軸と平行な長方形について、左上の点と辺の長さを指定します。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Rect r(0, 0, 2, 5);
std::cout << r.x << std::endl; //=> 0
std::cout << r.y << std::endl; //=> 0
std::cout << r.width << std::endl; //=> 2
std::cout << r.height << std::endl; //=> 5
std::cout << r.area() << std::endl; //=> 10
std::cout << r.tl() << std::endl; //=> [0, 0] ←左上の点
std::cout << r.br() << std::endl; //=> [2, 5] ←右下の点
std::cout << r.contains(cv::Point2i(1, 1)) << std::endl; //=> 1
return 0;
}
長方形の中心点、サイズ、回転角度の三つで、x軸とy軸に平行ではない長方形を表現するクラスも存在します。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Point2f center(0, 0);
cv::Size2f size(2, 5);
float angle = 3.14 / 2.0;
cv::RotatedRect rr(center, size, angle);
std::cout << rr.center << std::endl; //=> [0, 0]
std::cout << rr.size << std::endl; //=> [2 x 5]
std::cout << rr.angle << std::endl; //=> 1.57
return 0;
}
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Matx33f m = cv::Matx33f::eye();
std::cout << m << std::endl;
std::cout << cv::Matx33f::all(1) << std::endl;
std::cout << cv::Matx33f::ones() << std::endl;
std::cout << cv::Matx33f::zeros() << std::endl;
std::cout << m * m << std::endl;
std::cout << m(0, 2) << std::endl;
std::cout << m.get_minor<3, 1>(0, 2) << std::endl; // 部分行列
std::cout << m.row(2) << std::endl;
std::cout << m.col(2) << std::endl;
std::cout << m.diag() << std::endl;
std::cout << m.t() << std::endl; // 転置行列
std::cout << m.inv() << std::endl;
return 0;
}
列数が 1 の固定長行列です。外積演算が可能です。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Vec3f v3f(1, 1, 1);
std::cout << v3f(0) << std::endl; //=> 1
std::cout << v3f[0] << std::endl; //=> 1
std::cout << v3f.cross(v3f) << std::endl; //=> [0, 0, 0]
return 0;
}
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Complexd z(0, 0);
std::cout << z.re << std::endl; //=> 0
std::cout << z.im << std::endl; //=> 0
return 0;
}
蜜な行列は cv::Mat
で表現できます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat m;
m.create(5, 5, CV_32F);
m.setTo(cv::Scalar(0.0)); // 初期化
std::cout << m.at<float>(0, 0) << std::endl;
return 0;
}
単位行列などは以下のように作成します。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat m = cv::Mat::eye(5, 5, CV_32F);
// cv::Mat m = cv::Mat::zeros(5, 5, CV_32F);
// cv::Mat m = cv::Mat::ones(5, 5, CV_32F);
std::cout << m.at<float>(0, 0) << std::endl;
return 0;
}
例えば CV_32F
ではなく CV_32FC3
を指定すると、行列を 3 チャンネル作成できます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat m;
m.create(5, 5, CV_32FC3);
m.setTo(cv::Scalar(1.0, 2.0, 3.0)); // 各チャンネルの行列をそれぞれ初期化
std::cout << m.at<cv::Vec3f>(0, 0)[0] << std::endl; //=> 1
std::cout << m.at<cv::Vec3f>(0, 0)[1] << std::endl; //=> 2
std::cout << m.at<cv::Vec3f>(0, 0)[2] << std::endl; //=> 3
return 0;
}
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat m = cv::Mat::eye(5, 5, CV_32F);
std::cout << m.row(0) << std::endl;
std::cout << m.col(0) << std::endl;
std::cout << m.rowRange(0, 2) << std::endl;
std::cout << m.colRange(0, 2) << std::endl;
std::cout << m(cv::Range(0, 2), cv::Range(0, 2)) << std::endl;
return 0;
}
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
int size[] = {10, 10};
cv::SparseMat sm(2, size, CV_32F);
sm.ref<float>(1, 1) = 5;
std::cout << sm.value<float>(1, 1) << std::endl; //=> 5
std::cout << sm.nzcount() << std::endl; //=> 1 (ゼロ以外の要素数)
sm.erase(1, 1);
return 0;
}
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat m = cv::Mat::eye(5, 5, CV_32F);
cv::Mat m2;
std::cout << cv::abs(m + m) << std::endl;
cv::normalize(m, m2);
std::cout << cv::norm(m2) << std::endl;
std::cout << cv::sum(m) << std::endl;
std::cout << cv::trace(m) << std::endl;
return 0;
}
画像ファイルは蜜な可変長行列 cv::Mat
として読み込めます。Python から OpenCV を扱った場合と同様にテキストなどを描画できます。
#include <opencv2/opencv.hpp>
int main() {
cv::Mat img = cv::imread("aaa.png", -1);
if(img.empty()) {
return -1;
}
std::cout << img.size() << std::endl; //=> [200 x 200]
std::cout << img.channels() << std::endl; //=> 4
cv::circle(img, cv::Point(100, 100), 100, cv::Scalar(255, 0, 0)); // BGR
cv::namedWindow("Example", cv::WINDOW_AUTOSIZE);
cv::imshow("Example", img);
cv::waitKey(0);
cv::destroyWindow("Example");
return 0;
}
テキストの描画
#include <opencv2/opencv.hpp>
int main() {
cv::Mat img = cv::imread("aaa.png", -1);
if(img.empty()) {
return -1;
}
std::cout << img.size() << std::endl; //=> [200 x 200]
std::cout << img.channels() << std::endl; //=> 4
cv::putText(img, cv::String("Hello"), cv::Point(0, 200), cv::FONT_HERSHEY_PLAIN, 5, cv::Scalar(255, 0, 0));
cv::namedWindow("Example", cv::WINDOW_AUTOSIZE);
cv::imshow("Example", img);
cv::waitKey(0);
cv::destroyWindow("Example");
return 0;
}
三次元空間における回転の表現には、3x3 回転行列 の他に、回転軸を表現するベクトルを用いるものがあります。ベクトルの大きさで回転量を表現します。ベクトルによる回転の表現は、行列の場合と比較してより直感的です。
両者は相互に変換できます。OpenCV における実装は cv::Rodrigues
です。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Vec3f v(0, 0, CV_PI / 2.0);
cv::Mat R;
cv::Rodrigues(v, R);
std::cout << R << std::endl;
cv::Mat R2 = (cv::Mat_<float>(3, 3) <<
cos(CV_PI / 2.0), -sin(CV_PI / 2.0), 0,
sin(CV_PI / 2.0), cos(CV_PI / 2.0), 0,
0, 0, 1);
cv::Vec3f v2;
cv::Rodrigues(R2, v2);
std::cout << v2 << std::endl;
return 0;
}
実行例
[-4.3711388e-08, -1, 0;
1, -4.3711388e-08, 0;
0, 0, 1]
[0, 0, 1.5708]