画像を単色で塗りつぶす方法を示します. また,この例には示しませんが, 画像中の部分矩形を塗りつぶす場合には、ROIまたは cv::rectangle を利用します. 矩形を描く などを参考にしてください.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
// 初期化時に塗りつぶす
cv::Mat red_img(cv::Size(640, 480), CV_8UC3, cv::Scalar(0,0,255));
cv::Mat white_img(cv::Size(640, 480), CV_8UC3, cv::Scalar::all(255));
cv::Mat black_img = cv::Mat::zeros(cv::Size(640, 480), CV_8UC3);
// 初期化後に塗りつぶす
cv::Mat green_img = red_img.clone();
green_img = cv::Scalar(0,255,0);
cv::namedWindow("red image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("white image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("black image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("green image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("red image", red_img);
cv::imshow("white image", white_img);
cv::imshow("black image", black_img);
cv::imshow("green image", green_img);
cv::waitKey(0);
}
実行結果:
入力画像の型として可能なものは, CV_8U , CV_16U , CV_32F です. RGB画像のチャンネル順序は,変換コードで明示的に指定する必要があります.例えば,RBGからHSVならば, CV_RGB2HSV ,BGRからグレースケールならば CV_BGR2GRAY となります. また,すべての色空間が相互に変換可能なわけではないことに注意してください.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat bgr_img = cv::imread("../../image/lenna.png", 1);
if(bgr_img.empty()) return -1;
cv::Mat dst_img;
// BGR -> HSV
cv::cvtColor(bgr_img, dst_img, CV_BGR2HSV);
// ... 何らかの処理
// BGR -> Lab
cv::cvtColor(bgr_img, dst_img, CV_BGR2Lab);
// ... 何らかの処理
// BGR -> YCrCb
cv::cvtColor(bgr_img, dst_img, CV_BGR2YCrCb);
// ... 何らかの処理
}
補間手法を指定して画像サイズを変更することができます.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat dst_img1;
cv::Mat dst_img2(src_img.rows*0.5, src_img.cols*2.0, src_img.type());
// INTER_LINER(バイリニア補間)でのサイズ変更
cv::resize(src_img, dst_img1, cv::Size(), 0.5, 0.5);
// INTER_CUBIC(バイキュービック補間)でのサイズ変更
cv::resize(src_img, dst_img2, dst_img2.size(), cv::INTER_CUBIC);
cv::namedWindow("resize image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("resize image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("resize image1", dst_img1);
cv::imshow("resize image2", dst_img2);
cv::waitKey(0);
}
入力画像:
実行結果(縦横0.5倍,縦0.5倍+横2.0倍):
2次元行列反転と同様です. 行列を反転する も参照してください.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat v_img, h_img, b_img;
cv::flip(src_img, v_img, 0); // 水平軸で反転(垂直反転)
cv::flip(src_img, h_img, 1); // 垂直軸で反転(水平反転)
cv::flip(src_img, b_img, -1); // 両方の軸で反転
cv::namedWindow("vertical flip image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("horizontal flip image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("both flip image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("vertical flip image", v_img);
cv::imshow("horizontal flip image", h_img);
cv::imshow("both flip image", b_img);
cv::waitKey(0);
}
入力画像:
実行結果(垂直反転,水平反転,垂直反転+水平反転):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/lenna.png", 0);
if(src_img.empty()) return -1;
// NOT演算
cv::Mat dst_img = ~src_img;
cv::namedWindow("src image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("dst image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("src image", src_img);
cv::imshow("dst image", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
画素値を閾値処理して,画像の2値化を行います.閾値処理の手法には,以下のものがあります:
さらに,特殊な値 THRESH_OTSU を,上述のものと組み合わせて使うこともできます.この場合,関数は大津のアルゴリズムを用いて最適な閾値を決定し,それを引数 thresh で指定された値の代わりに利用します.つまり,自分で閾値を決める必要がありません.
また, adaptiveThreshold は適応的な閾値処理を行います.この適応的というのは,閾値が入力によって適応的に決まることを意味します.閾値決定の手法は,以下のものがあります:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat gray_img = cv::imread("../../image/lenna.png", 0);
if(gray_img.empty()) return -1;
// 固定の閾値処理
cv::Mat bin_img, bininv_img, trunc_img, tozero_img, tozeroinv_img;
// 入力画像,出力画像,閾値,maxVal,閾値処理手法
cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU);
cv::threshold(gray_img, bininv_img, 0, 255, cv::THRESH_BINARY_INV|cv::THRESH_OTSU);
cv::threshold(gray_img, trunc_img, 0, 255, cv::THRESH_TRUNC|cv::THRESH_OTSU);
cv::threshold(gray_img, tozero_img, 0, 255, cv::THRESH_TOZERO|cv::THRESH_OTSU);
cv::threshold(gray_img, tozeroinv_img, 0, 255, cv::THRESH_TOZERO_INV|cv::THRESH_OTSU);
// 適応的な閾値処理
cv::Mat adaptive_img;
// 入力画像,出力画像,maxVal,閾値決定手法,閾値処理手法,blockSize,C
cv::adaptiveThreshold(gray_img, adaptive_img, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 7, 8);
// 結果画像表示
cv::namedWindow("Binary", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Binary Inv", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Trunc", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("ToZero", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("ToZero Inv", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Adaptive", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Binary", bin_img);
cv::imshow("Binary Inv", bininv_img);
cv::imshow("Trunc", trunc_img);
cv::imshow("ToZero", tozero_img);
cv::imshow("ToZero Inv", tozeroinv_img);
cv::imshow("Adaptive", adaptive_img);
cv::waitKey(0);
}
入力画像:
実行結果:
2次元のアフィン変換を行う の 変換パラメータを指定して,変換行列を決定する を参照してください.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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;
// 変換前の三点
const cv::Point2f src_pt[] = { cv::Point2f(200, 200), cv::Point2f(250, 200), cv::Point2f(200, 100) };
// 変換後の三点
const cv::Point2f dst_pt[] = { cv::Point2f(300, 100), cv::Point2f(300, 50), cv::Point2f(200, 100) };
// これらから,アフィン変換行列を計算
const cv::Mat affine_matrix = cv::getAffineTransform(src_pt, dst_pt);
std::cout << "affine_matrix=\n" << affine_matrix << std::endl;
cv::Mat dst_img;
cv::warpAffine(src_img, dst_img, affine_matrix, src_img.size());
// 変換前後の座標を描画
cv::line(src_img, src_pt[0], src_pt[1], cv::Scalar(255,255,0), 2);
cv::line(src_img, src_pt[1], src_pt[2], cv::Scalar(255,255,0), 2);
cv::line(src_img, src_pt[2], src_pt[0], cv::Scalar(255,255,0), 2);
cv::line(src_img, dst_pt[0], dst_pt[1], cv::Scalar(255,0,255), 2);
cv::line(src_img, dst_pt[1], dst_pt[2], cv::Scalar(255,0,255), 2);
cv::line(src_img, dst_pt[2], dst_pt[0], cv::Scalar(255,0,255), 2);
cv::namedWindow("src", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("dst", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("src", src_img);
cv::imshow("dst", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
affine_matrix=
[0, 1, 100;
-1, -3.255497723249808e-17, 300.0000000000001]
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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;
// 回転: -40 [deg], スケーリング: 1.0 [倍]
float angle = -40.0, scale = 1.0;
// 中心:画像中心
cv::Point2f center(src_img.cols*0.5, src_img.rows*0.5);
// 以上の条件から2次元の回転行列を計算
const cv::Mat affine_matrix = cv::getRotationMatrix2D( center, angle, scale );
std::cout << "affine_matrix=\n" << affine_matrix << std::endl;
cv::Mat dst_img;
cv::warpAffine(src_img, dst_img, affine_matrix, src_img.size());
cv::namedWindow("src", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("dst", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("src", src_img);
cv::imshow("dst", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
affine_matrix=
[0.766044443118978, -0.6427876096865394, 214.1616488863111;
0.6427876096865394, 0.766044443118978, -108.4042944283088]
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Point2f pts1[] = {cv::Point2f(150,150),cv::Point2f(150,300),cv::Point2f(350,300),cv::Point2f(350,150)};
cv::Point2f pts2[] = {cv::Point2f(200,150),cv::Point2f(200,300),cv::Point2f(340,270),cv::Point2f(340,180)};
// r5014以前ではノイズがのります.
//cv::Point2f pts1[] = {cv::Point2f(150,150.),cv::Point2f(150,300.),cv::Point2f(350,300.),cv::Point2f(350,150.)};
//cv::Point2f pts2[] = {cv::Point2f(200,200.),cv::Point2f(150,300.),cv::Point2f(350,300.),cv::Point2f(300,200.)};
// 透視変換行列を計算
cv::Mat perspective_matrix = cv::getPerspectiveTransform(pts1, pts2);
cv::Mat dst_img;
// 変換
cv::warpPerspective(src_img, dst_img, perspective_matrix, src_img.size(), cv::INTER_LINEAR);
// 変換前後の座標を描画
cv::line(src_img, pts1[0], pts1[1], cv::Scalar(255,255,0), 2, CV_AA);
cv::line(src_img, pts1[1], pts1[2], cv::Scalar(255,255,0), 2, CV_AA);
cv::line(src_img, pts1[2], pts1[3], cv::Scalar(255,255,0), 2, CV_AA);
cv::line(src_img, pts1[3], pts1[0], cv::Scalar(255,255,0), 2, CV_AA);
cv::line(src_img, pts2[0], pts2[1], cv::Scalar(255,0,255), 2, CV_AA);
cv::line(src_img, pts2[1], pts2[2], cv::Scalar(255,0,255), 2, CV_AA);
cv::line(src_img, pts2[2], pts2[3], cv::Scalar(255,0,255), 2, CV_AA);
cv::line(src_img, pts2[3], pts2[0], cv::Scalar(255,0,255), 2, CV_AA);
cv::namedWindow("src", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("dst", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("src", src_img);
cv::imshow("dst", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
画像ピラミッドは,解像度の異なる同一画像の集合から構成されます. このような構造は,画像の拡大縮小表示,空間方向に関する極大点を求める処理の高速化,coarse-to-fine(最初に低解像度に対する荒い処理を行い,徐々に高精度化する)手法などに利用されます.
元画像に対して,ダウンサンプリングするだけの画像ピラミッドの場合 buildPyramid() を利用します.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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;
// level2までの画像ピラミッドを作成
std::vector<cv::Mat> dst_img;
cv::buildPyramid(src_img, dst_img, 2);
cv::namedWindow("level0", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("level1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("level2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("level0", dst_img[0]);
cv::imshow("level1", dst_img[1]);
cv::imshow("level2", dst_img[2]);
cv::waitKey(0);
}
実行結果:
元画像に対して,ダウンサンプリング,アップサンプリングの両方を適用する場合は,それぞれ, PyrDown, PyrUp を利用します.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
const char *imagename = argc > 1 ? argv[1] : "../../image/opencv-logomini.png";
cv::Mat src_img = cv::imread(imagename, 1);
if(src_img.empty()) return -1;
cv::Mat dst_imgUp, dst_imgDown;
cv::pyrUp(src_img, dst_imgUp, cv::Size(src_img.cols*2, src_img.rows*2));
cv::pyrDown(src_img, dst_imgDown, cv::Size(src_img.cols/2, src_img.rows/2));
cv::namedWindow("Up", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Down", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Up", dst_imgUp);
cv::imshow("Down", dst_imgDown);
cv::waitKey(0);
}
入力画像:
実行結果:
マルチチャンネル画像の場合,各チャンネルが個別に処理されます.
デフォルトの構造要素は,3x3の矩形です.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/lenna.pbm", 1);
//cv::Mat src_img = cv::imread(argv[1], 1);
if(src_img.empty()) return -1;
cv::Mat d1_img, d3_img, e1_img, e3_img;
cv::dilate(src_img, d1_img, cv::Mat(), cv::Point(-1,-1), 1);
cv::dilate(src_img, d3_img, cv::Mat(), cv::Point(-1,-1), 3);
cv::erode(src_img, e1_img, cv::Mat(), cv::Point(-1,-1), 1);
cv::erode(src_img, e3_img, cv::Mat(), cv::Point(-1,-1), 3);
//__//cv::namedWindow("Dilated image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
//__//cv::namedWindow("Dilated image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
//__//cv::namedWindow("Eroded image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
//__//cv::namedWindow("Eroded image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imwrite(__FILE__".Dilated image1.png", d1_img);//__//
std::cout<<__FILE__".Dilated image1.png"<<std::endl;//__//
cv::imwrite(__FILE__".Dilated image2.png", d3_img);//__//
std::cout<<__FILE__".Dilated image2.png"<<std::endl;//__//
cv::imwrite(__FILE__".Eroded image1.png", e1_img);//__//
std::cout<<__FILE__".Eroded image1.png"<<std::endl;//__//
cv::imwrite(__FILE__".Eroded image2.png", e3_img);//__//
std::cout<<__FILE__".Eroded image2.png"<<std::endl;//__//
//__//cv::waitKey(0);
}
入力画像(をpngに変換した画像):
実行結果(膨張1回,膨張3回,収縮1回,収縮3回):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/lenna.pbm", 1);
if(src_img.empty()) return -1;
cv::Mat d17_img, d55_img, e17_img, e55_img;
cv::Mat element = cv::Mat::ones(1,7,CV_8UC1);
cv::dilate(src_img, d17_img, element, cv::Point(-1,-1), 1);
cv::erode(src_img, e17_img, element, cv::Point(-1,-1), 1);
element = cv::Mat::eye(5,5, CV_8UC1);
cv::dilate(src_img, d55_img, element, cv::Point(-1,-1), 1);
cv::erode(src_img, e55_img, element, cv::Point(-1,-1), 1);
cv::namedWindow("Dilated image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Dilated image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Eroded image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Eroded image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Dilated image1", d17_img);
cv::imshow("Dilated image2", d55_img);
cv::imshow("Eroded image1", e17_img);
cv::imshow("Eroded image2", e55_img);
cv::waitKey(0);
}
入力画像(をpngに変換した画像):
実行結果(1x7行列+膨張1回,5x5単位行列+膨張1回,1x7行列+収縮1回,5x5単位行列+収縮1回):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
const char *imagename = argc > 1 ? argv[1] : "../../image/ball.png";
cv::Mat src_img = cv::imread(imagename, 1);
if(src_img.empty()) return -1;
// 構造要素
cv::Mat element(3,3,CV_8U, cv::Scalar::all(255));
cv::Mat open_img;
cv::morphologyEx(src_img, open_img, cv::MORPH_OPEN, element, cv::Point(-1,-1), 3);
// close
cv::Mat close_img;
cv::morphologyEx(src_img, close_img, cv::MORPH_CLOSE, element, cv::Point(-1,-1), 3);
// gradient
cv::Mat gradient_img;
cv::morphologyEx(src_img, gradient_img, cv::MORPH_GRADIENT, element, cv::Point(-1,-1), 3);
// tophat
cv::Mat tophat_img;
cv::morphologyEx(src_img, tophat_img, cv::MORPH_TOPHAT, element, cv::Point(-1,-1), 2);
// blackhat
cv::Mat blackhat_img;
cv::morphologyEx(src_img, blackhat_img, cv::MORPH_BLACKHAT, element, cv::Point(-1,-1), 2);
cv::namedWindow("open_img", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("close_img", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("gradient_img", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("tophat_img", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("blackhat_img", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("open_img", open_img);
cv::imshow("close_img", close_img);
cv::imshow("gradient_img", gradient_img);
cv::imshow("tophat_img", tophat_img);
cv::imshow("blackhat_img", blackhat_img);
cv::waitKey(0);
}
入力画像:
実行結果(Open, Close, Gradient, TopHat, BlackHat)
ガウシアンフィルタを用いた平滑化を行います.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat dst_img1, dst_img2;
// ガウシアンを用いた平滑化
// 入力画像,出力画像,カーネルサイズ,標準偏差x, y
cv::GaussianBlur(src_img, dst_img1, cv::Size(11,11), 10, 10);
cv::GaussianBlur(src_img, dst_img2, cv::Size(51,3), 80, 3);
cv::namedWindow("Blur image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blur image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Blur image1", dst_img1);
cv::imshow("Blur image2", dst_img2);
cv::waitKey(0);
}
入力画像:
実行結果:
メディアンフィルタを用いた平滑化を行います.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat dst_img1, dst_img2;
// メディアンフィルタを用いた平滑化
// 入力画像,出力画像,カーネルサイズ
cv::medianBlur(src_img, dst_img1, 11);
cv::medianBlur(src_img, dst_img2, 51);
cv::namedWindow("Blur image1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blur image2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Blur image1", dst_img1);
cv::imshow("Blur image2", dst_img2);
cv::waitKey(0);
}
入力画像:
実行結果:
バイラテラルフィルタを用いた平滑化を行います.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat dst_img1, dst_img2;
// 入力,出力,各ピクセルの近傍領域を表す直径,
// 色空間におけるσ,座標空間におけるσ
cv::bilateralFilter(src_img, dst_img1, 11, 40, 200);
cv::bilateralFilter(src_img, dst_img2, 20, 90, 40);
cv::namedWindow("Blur Image 1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blur Image 2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Blur Image 1", dst_img1);
cv::imshow("Blur Image 2", dst_img2);
cv::waitKey(0);
}
入力画像:
実行結果:
ボックスフィルタを用いた平滑化を行います.
blur(src, dst, ksize, anchor, borderType);
という呼び出しは,
boxFilter(src, dst, src.type(), anchor, true, borderType);
と等価です.つまり,BoxFilterを必ず正規化するメソッドが blur と言えます. ビット深度=8 の画像で正規化を行わない場合,多くの画素が飽和する可能性があります.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat dst_img1, dst_img2;
// 入力,出力,カーネルサイズ(,その他=default)
cv::blur(src_img, dst_img1, cv::Size(5,5));
cv::blur(src_img, dst_img2, cv::Size(2,100));
cv::Mat dst_img3, dst_img4;
// 入力,出力,カーネルサイズ,アンカー,正規化の有無
cv::boxFilter(src_img, dst_img3, src_img.type(), cv::Size(5,5), cv::Point(-1,-1), true);
cv::boxFilter(src_img, dst_img4, src_img.type(), cv::Size(2,2), cv::Point(-1,-1), false);
cv::namedWindow("Blur Image 1", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blur Image 2", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blur Image 3", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blur Image 4", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Blur Image 1", dst_img1);
cv::imshow("Blur Image 2", dst_img2);
cv::imshow("Blur Image 3", dst_img3);
cv::imshow("Blur Image 4", dst_img4);
cv::waitKey(0);
}
入力画像:
実行結果(blur):
実行結果(BoxFilter):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main (int argc, char *argv[])
{
// グレースケール画像として読み込む
cv::Mat src_img = cv::imread("../../image/lenna.png", 0);
if(src_img.empty()) return -1;
// Sobel
cv::Mat tmp_img;
cv::Mat sobel_img;
cv::Sobel(src_img, tmp_img, CV_32F, 1, 1);
cv::convertScaleAbs(tmp_img, sobel_img, 1, 0);
// Laplacian
cv::Mat laplacian_img;
cv::Laplacian(src_img, tmp_img, CV_32F, 3);
cv::convertScaleAbs(tmp_img, laplacian_img, 1, 0);
// Canny
cv::Mat canny_img;
cv::Canny(src_img, canny_img, 50, 200);
cv::namedWindow("Original(Grayscale)", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Sobel", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Laplacian", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Canny", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Original(Grayscale)", src_img);
cv::imshow("Sobel", sobel_img);
cv::imshow("Laplacian", laplacian_img);
cv::imshow("Canny", canny_img);
cv::waitKey(0);
}
入力画像:
実行結果(Sobel,Laplacian,Canny):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Size img_size(500, 500);
cv::Mat img = cv::Mat::zeros(img_size, CV_8UC3);
// 一様分布乱数で座標を生成
const int rand_num = 50;
cv::Mat_<int> points(rand_num, 2);
cv::randu(points, cv::Scalar(100), cv::Scalar(400));
for(int i=0; i<rand_num; ++i) {
// 座標に点を描画
cv::circle(img, cv::Point(points(i,0), points(i,1)), 2, cv::Scalar(200,200,0), -1, CV_AA);
}
// CV_*C2型のMatに変換してから,外接矩形を計算
cv::Rect brect = cv::boundingRect(cv::Mat(points).reshape(2));
// 外接矩形を描画
cv::rectangle(img, brect.tl(), brect.br(), cv::Scalar(100, 100, 200), 2, CV_AA);
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", img);
cv::waitKey(0);
}
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Size img_size(500, 500);
cv::Mat img = cv::Mat::zeros(img_size, CV_8UC3);
// 一様分布乱数で座標を生成
const int rand_num = 50;
cv::Mat_<int> points(rand_num, 2);
cv::randu(points, cv::Scalar(100), cv::Scalar(400));
for(int i=0; i<rand_num; ++i) {
// 座標に点を描画
cv::circle(img, cv::Point(points(i,0), points(i,1)), 2, cv::Scalar(200,200,0), -1, CV_AA);
}
// CV_*C2型のMatに変換してから,外接矩形(回転あり)を計算
cv::Point2f center, vtx[4];
float radius;
cv::RotatedRect box = cv::minAreaRect(cv::Mat(points).reshape(2));
// 外接矩形(回転あり)を描画
box.points(vtx);
for(int i=0; i<4; ++i)
cv::line(img, vtx[i], vtx[i<3?i+1:0], cv::Scalar(100,100,200), 2, CV_AA);
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", img);
cv::waitKey(0);
}
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Size img_size(500, 500);
cv::Mat img = cv::Mat::zeros(img_size, CV_8UC3);
// 一様分布乱数で座標を生成
const int rand_num = 50;
cv::Mat_<int> points(rand_num, 2);
cv::randu(points, cv::Scalar(100), cv::Scalar(400));
for(int i=0; i<rand_num; ++i) {
// 座標に点を描画
cv::circle(img, cv::Point(points(i,0), points(i,1)), 2, cv::Scalar(200,200,0), -1, CV_AA);
}
// CV_*C2型のMatに変換してから,外接円を計算
cv::Point2f center;
float radius;
cv::minEnclosingCircle(cv::Mat(points).reshape(2), center, radius);
// 外接円を描画
cv::circle(img, center, radius, cv::Scalar(100,100,200), 2, CV_AA);
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", img);
cv::waitKey(0);
}
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Size img_size(500, 500);
cv::Mat img = cv::Mat::zeros(img_size, CV_8UC3);
// 一様分布乱数で座標を生成
const int rand_num = 50;
cv::Mat_<int> points(rand_num, 2);
cv::randu(points, cv::Scalar(100), cv::Scalar(400));
for(int i=0; i<rand_num; ++i) {
// 座標に点を描画
cv::circle(img, cv::Point(points(i,0), points(i,1)), 2, cv::Scalar(200,200,0), -1, CV_AA);
}
// CV_*C2型のMatに変換してから,凸包を計算
std::vector<cv::Point> hull;
cv::convexHull(cv::Mat(points).reshape(2), hull);
// 凸包を描画
int hnum = hull.size();
for(int i=0; i<hnum; ++i)
cv::line(img, hull[i], hull[i+1<hnum?i+1:0], cv::Scalar(100,100,200), 2, CV_AA);
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", img);
cv::waitKey(0);
}
実行結果:
以下のいずれかの方法で,ノイズや不要なオブジェクトを削除して画像を修復します.
#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,4,0)
#include <opencv2/imgproc/imgproc.hpp>
#else
#include <opencv2/photo/photo.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/lenna_inpaint.png", 1);
if(src_img.empty()) return -1;
cv::Mat mask_img = cv::imread("../../image/inpaint_mask.png", 0);
if(mask_img.empty()) return -1;
cv::Mat ns_img, telea_img;
// 入力画像,マスク,出力画像,修正時に考慮される近傍範囲を表す半径,手法
cv::inpaint(src_img, mask_img, ns_img, 3, cv::INPAINT_NS);
cv::inpaint(src_img, mask_img, telea_img, 3, cv::INPAINT_TELEA);
cv::namedWindow("inpainted image(NS)", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("inpainted image(TELEA)", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("inpainted image(NS)", ns_img);
cv::imshow("inpainted image(TELEA)", telea_img);
cv::waitKey(0);
}
入力画像:
実行結果(INPAINT_NS, INPAINT_TELEA):
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/building.png", 1);
if(src_img.empty()) return -1;
cv::Mat dst_img, work_img;
dst_img = src_img.clone();
cv::cvtColor(src_img, work_img, CV_BGR2GRAY);
cv::Canny(work_img, work_img, 50, 200, 3);
// (古典的)Hough変換
std::vector<cv::Vec2f> lines;
// 入力画像,出力,距離分解能,角度分解能,閾値,*,*
cv::HoughLines(work_img, lines, 1, CV_PI/180, 200, 0, 0);
std::vector<cv::Vec2f>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
float rho = (*it)[0], theta = (*it)[1];
cv::Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cv::saturate_cast<int>(x0 + 1000*(-b));
pt1.y = cv::saturate_cast<int>(y0 + 1000*(a));
pt2.x = cv::saturate_cast<int>(x0 - 1000*(-b));
pt2.y = cv::saturate_cast<int>(y0 - 1000*(a));
cv::line(dst_img, pt1, pt2, cv::Scalar(0,0,255), 3, CV_AA);
}
cv::namedWindow("HoughLines", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("HoughLines", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/building.png", 1);
if(src_img.empty()) return -1;
cv::Mat dst_img, work_img;
dst_img = src_img.clone();
cv::cvtColor(src_img, work_img, CV_BGR2GRAY);
cv::Canny(work_img, work_img, 50, 200, 3);
// 確率的Hough変換
std::vector<cv::Vec4i> lines;
// 入力画像,出力,距離分解能,角度分解能,閾値,線分の最小長さ,
// 2点が同一線分上にあると見なす場合に許容される最大距離
cv::HoughLinesP(work_img, lines, 1, CV_PI/180, 50, 50, 10);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
cv::Vec4i l = *it;
cv::line(dst_img, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0,0,255), 2, CV_AA);
}
cv::namedWindow("HoughLinesP", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("HoughLinesP", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
直線検出の cv::HoughLines や cv::HoughLinesP が,2値画像から直線を検出するのに対して, cv::HoughCircles はグレースケール画像から円を検出することに注意してください (内部的には2値画像に変換されていますが).
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/circles.png", 1);
if(src_img.empty()) return -1;
cv::Mat dst_img, work_img;
dst_img = src_img.clone();
cv::cvtColor(src_img, work_img, CV_BGR2GRAY);
// Hough変換のための前処理(画像の平滑化を行なわないと誤検出が発生しやすい)
cv::GaussianBlur(work_img, work_img, cv::Size(11,11), 2, 2);
// Hough変換による円の検出と検出した円の描画
std::vector<cv::Vec3f> circles;
cv::HoughCircles(work_img, circles, CV_HOUGH_GRADIENT, 1, 100, 20, 50);
std::vector<cv::Vec3f>::iterator it = circles.begin();
for(; it!=circles.end(); ++it) {
cv::Point center(cv::saturate_cast<int>((*it)[0]), cv::saturate_cast<int>((*it)[1]));
int radius = cv::saturate_cast<int>((*it)[2]);
cv::circle(dst_img, center, radius, cv::Scalar(0,0,255), 2);
}
cv::namedWindow("HoughCircles", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("HoughCircles", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
画像から輪郭を検出し,その輪郭に対して楕円フィッティングを行う.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/stuff.jpg", 1);
if(src_img.empty()) return -1;
cv::Mat gray_img, bin_img;
cv::cvtColor(src_img, gray_img, CV_BGR2GRAY);
std::vector<std::vector<cv::Point> > contours;
// 画像の二値化
cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU);
// 輪郭の検出
cv::findContours(bin_img, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
for(int i = 0; i < contours.size(); ++i) {
size_t count = contours[i].size();
if(count < 150 || count > 1000) continue; // (小さすぎる|大きすぎる)輪郭を除外
cv::Mat pointsf;
cv::Mat(contours[i]).convertTo(pointsf, CV_32F);
// 楕円フィッティング
cv::RotatedRect box = cv::fitEllipse(pointsf);
// 楕円の描画
cv::ellipse(src_img, box, cv::Scalar(0,0,255), 2, CV_AA);
}
cv::namedWindow("fit ellipse", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("bin image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("fit ellipse", src_img);
cv::imshow("bin image", bin_img);
cv::waitKey(0);
}
入力画像:
実行結果(輪郭画像,フィッティング結果):
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
// 探索画像
cv::Mat search_img = cv::imread("../../image/lenna.png", 1);
if(search_img.empty()) return -1;
// テンプレート画像
cv::Mat tmp_img = cv::imread("../../image/lenna_left_eye.png", 1);
if(tmp_img.empty()) return -1;
cv::Mat result_img;
// テンプレートマッチング
cv::matchTemplate(search_img, tmp_img, result_img, CV_TM_CCOEFF_NORMED);
// 最大のスコアの場所を探す
cv::Rect roi_rect(0, 0, tmp_img.cols, tmp_img.rows);
cv::Point max_pt;
double maxVal;
cv::minMaxLoc(result_img, NULL, &maxVal, NULL, &max_pt);
roi_rect.x = max_pt.x;
roi_rect.y = max_pt.y;
std::cout << "(" << max_pt.x << ", " << max_pt.y << "), score=" << maxVal << std::endl;
// 探索結果の場所に矩形を描画
cv::rectangle(search_img, roi_rect, cv::Scalar(0,0,255), 3);
cv::namedWindow("search image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("result image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("search image", search_img);
cv::imshow("result image", result_img);
cv::waitKey(0);
}
入力画像(探索対象画像,テンプレート画像):
実行結果(相関値マップ,最大相関の場所):
(316, 245), score=1
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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)
int
main(int argc, char *argv[])
{
cv::Mat src_img = cv::imread("../../image/lenna.png", 1);
if(src_img.empty()) return -1;
const int cluster_count = 10; /* クラスタ数 */
// 画像を1列の行列に変形
cv::Mat points;
src_img.convertTo(points, CV_32FC3);
points = points.reshape(3, src_img.rows*src_img.cols);
// RGB空間でk-meansを実行
cv::Mat_<int> clusters(points.size(), CV_32SC1);
cv::Mat centers;
// クラスタ対象,クラスタ数,(出力)クラスタインデックス,
// 停止基準,k-meansの実行回数,手法,(出力)クラスタ中心値
#if OPENCV_VERSION_CODE<OPENCV_VERSION(2,3,0)
cv::kmeans(points, cluster_count, clusters,
cvTermCriteria(CV_TERMCRIT_EPS|CV_TERMCRIT_ITER, 10, 1.0), 1, cv::KMEANS_PP_CENTERS, ¢ers);
#else
cv::kmeans(points, cluster_count, clusters,
cvTermCriteria(CV_TERMCRIT_EPS|CV_TERMCRIT_ITER, 10, 1.0), 1, cv::KMEANS_PP_CENTERS, centers);
#endif
// すべてのピクセル値をクラスタ中心値で置き換え
cv::Mat dst_img(src_img.size(), src_img.type());
cv::MatIterator_<cv::Vec3b> itd = dst_img.begin<cv::Vec3b>(),
itd_end = dst_img.end<cv::Vec3b>();
for(int i=0; itd != itd_end; ++itd, ++i) {
cv::Vec3f &color = centers.at<cv::Vec3f>(clusters(i), 0);
(*itd)[0] = cv::saturate_cast<uchar>(color[0]);
(*itd)[1] = cv::saturate_cast<uchar>(color[1]);
(*itd)[2] = cv::saturate_cast<uchar>(color[2]);
}
cv::namedWindow("dst_img", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("dst_img", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main (int argc, char **argv)
{
cv::Mat src_img = cv::imread("../../image/lenna.png", 0);
if(src_img.empty()) return -1;
// ヒストグラムを描画する画像割り当て
const int ch_width = 260, ch_height=200;
cv::Mat hist_img(cv::Size(ch_width, ch_height), CV_8UC3, cv::Scalar::all(255));
cv::Mat hist;
const int hdims[] = {256}; // 次元毎のヒストグラムサイズ
const float hranges[] = {0,256};
const float* ranges[] = {hranges}; // 次元毎のビンの下限上限
double max_val = .0;
// シングルチャンネルのヒストグラム計算
// 画像(複数可),画像枚数,計算するチャンネル,マスク,ヒストグラム(出力),
// ヒストグラムの次元,ヒストグラムビンの下限上限
cv::calcHist(&src_img, 1, 0, cv::Mat(), hist, 1, hdims, ranges);
// 最大値の計算
cv::minMaxLoc(hist, 0, &max_val);
// ヒストグラムのスケーリングと描画
cv::Scalar color = cv::Scalar::all(100);
// スケーリング
hist = hist * (max_val? ch_height/max_val:0.);
for(int j=0; j<hdims[0]; ++j) {
int bin_w = cv::saturate_cast<int>((double)ch_width/hdims[0]);
cv::rectangle(hist_img,
cv::Point(j*bin_w, hist_img.rows),
cv::Point((j+1)*bin_w, hist_img.rows-cv::saturate_cast<int>(hist.at<float>(j))),
color, -1);
}
cv::namedWindow("Image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Histogram", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Image", src_img);
cv::imshow("Histogram", hist_img);
cv::waitKey(0);
}
入力画像:
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
int img_num = argc > 1 ? argc-1 : 2;
const std::string defaultfile[] = {"../../image/lenna.png", "../../image/fruit.png"};
int total_width=0, max_height=0;
// ファイルを読み込み,結合後のサイズを決定
std::vector<cv::Mat> src_img;
for(int i=0; i<img_num; i++) {
src_img.push_back(cv::imread(argc-1?argv[i+1]:defaultfile[i]));
cv::Mat last = src_img.back();
if(last.empty()) return -1;
total_width += last.cols;
max_height = cv::max(max_height, last.rows);
}
// ROIを利用して,実際に画像を結合
cv::Mat combined_img(cv::Size(total_width, max_height), CV_8UC3);
std::vector<cv::Mat>::iterator it = src_img.begin(), it_end = src_img.end();
cv::Rect roi_rect;
for(; it!=it_end; ++it) {
roi_rect.width = it->cols;
roi_rect.height = it->rows;
cv::Mat roi(combined_img, roi_rect);
it->copyTo(roi);
roi_rect.x += it->cols;
}
cv::namedWindow("Combined Image", CV_WINDOW_AUTOSIZE);
cv::imshow("Combined Image", combined_img);
cv::waitKey(0);
}
入力画像(入力画像1,入力画像2):
実行結果:
幅または高さの等しい画像同士ならば,より簡単につなげることができます.
#include <opencv2/core/core.hpp>
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
int img_num = 5;
const char *imagename = argc > 1 ? argv[1] : "../../image/opencv-logomini.png";
cv::Mat src_img[img_num];
for(int i=0; i<img_num; i++) {
src_img[i] = cv::imread(imagename, 1);
if(src_img[i].empty()) return -1;
}
cv::Mat dst_img_h, dst_img_v;
/// 画像をつなげる
// 入力{Mat1, Mat2, ...}, Mat数, 出力Mat
hconcat(src_img, img_num, dst_img_h);
vconcat(src_img, img_num, dst_img_v);
cv::namedWindow("Hconcat Image", CV_WINDOW_AUTOSIZE);
cv::imshow("Hconcat Image", dst_img_h);
cv::namedWindow("Vconcat Image", CV_WINDOW_AUTOSIZE);
cv::imshow("Vconcat Image", dst_img_v);
cv::waitKey(0);
}
実行結果(横結合):
実行結果(縦結合):
#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;
// (x,y)=(200,200), (width,height)=(100,100)
cv::Mat roi_img(src_img, cv::Rect(200, 200, 100, 100));
cv::imwrite("lenna_clipped.png", roi_img);
}
入力画像:
実行結果:
ROI(Region Of Interest) を指定すると,画像の一部を新たな画像のように扱うことができます. この際,例えば畳み込みなどで必要になる 「ROI の外側の画素」は,自動的に元の画像全体からのピクセルが利用されます.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Mat dst_img = src_img.clone();
cv::Rect roi_rect(200,200,100,100); // x,y,w,h
cv::Mat src_roi = src_img(roi_rect);
cv::Mat dst_roi = dst_img(roi_rect);
// 何らかの処理...
cv::blur(src_roi, dst_roi, cv::Size(30,30));
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
ROIを利用して,部分矩形領域のピクセル値をそのまま参照したり,コピーしたりするのとは異なり, cv::getRectSubPix を用いると,バイリニア補間された浮動小数点座標のピクセル値も得ることができます.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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::Size patch_sie(100.0, 100.0);
cv::Point2f center(250.5, 250.8); // 実数値での座標指定
cv::Mat dst_img;
// 矩形領域ピクセル値をサブピクセル精度で取得
cv::getRectSubPix(src_img, patch_sie, center, dst_img);
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", dst_img);
cv::waitKey(0);
}
入力画像:
実行結果:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
const char *imagename = argc > 1 ? argv[1] : "../../image/lenna.png";
cv::Mat img = cv::imread(imagename, 1);
if(img.empty()) return -1;
double scale = 4.0;
cv::Mat gray, smallImg(cv::saturate_cast<int>(img.rows/scale), cv::saturate_cast<int>(img.cols/scale), CV_8UC1);
// グレースケール画像に変換
cv::cvtColor(img, gray, CV_BGR2GRAY);
// 処理時間短縮のために画像を縮小
cv::resize(gray, smallImg, smallImg.size(), 0, 0, cv::INTER_LINEAR);
cv::equalizeHist( smallImg, smallImg);
// 分類器の読み込み
std::string cascadeName = "./haarcascade_frontalface_alt.xml"; // Haar-like
//std::string cascadeName = "./lbpcascade_frontalface.xml"; // LBP
cv::CascadeClassifier cascade;
if(!cascade.load(cascadeName))
return -1;
std::vector<cv::Rect> faces;
/// マルチスケール(顔)探索xo
// 画像,出力矩形,縮小スケール,最低矩形数,(フラグ),最小矩形
cascade.detectMultiScale(smallImg, faces,
1.1, 2,
CV_HAAR_SCALE_IMAGE,
cv::Size(30, 30));
// 結果の描画
std::vector<cv::Rect>::const_iterator r = faces.begin();
for(; r != faces.end(); ++r) {
cv::Point center;
int radius;
center.x = cv::saturate_cast<int>((r->x + r->width*0.5)*scale);
center.y = cv::saturate_cast<int>((r->y + r->height*0.5)*scale);
radius = cv::saturate_cast<int>((r->width + r->height)*0.25*scale);
cv::circle( img, center, radius, cv::Scalar(80,80,255), 3, 8, 0 );
}
cv::namedWindow("result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow( "result", img );
cv::waitKey(0);
}
入力画像:
実行結果:
また,Haar-Like特徴の代わりにLBP(Local Binary Pattern)特徴を利用して学習した結果を用いて顔を検出することもできます.それぞれの特徴を利用した学習結果ファイルは,以下の場所に置かれます.
- Haar-Like:(OpenCV install path)/data/haarcascades
- LBP:(OpenCV install path)/data/lbpcascades
本サンプルでの利用法は,該当ファイルをバイナリと同じ場所にコピーして,対応するXMLファイル(上述のソースでコメントアウトされている箇所)を読み込むだけです.
実行結果:
まず顔を検出して,その検出矩形の中から,さらに目を検出します.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
const char *imagename = argc > 1 ? argv[1] : "../../image/lenna.png";
cv::Mat img = cv::imread(imagename, 1);
if(img.empty()) return -1;
double scale = 2.0;
cv::Mat gray, smallImg(cv::saturate_cast<int>(img.rows/scale), cv::saturate_cast<int>(img.cols/scale), CV_8UC1);
// グレースケール画像に変換
cv::cvtColor(img, gray, CV_BGR2GRAY);
// 処理時間短縮のために画像を縮小
cv::resize(gray, smallImg, smallImg.size(), 0, 0, cv::INTER_LINEAR);
cv::equalizeHist( smallImg, smallImg);
// 分類器の読み込み
std::string cascadeName = "./haarcascade_frontalface_alt.xml";
cv::CascadeClassifier cascade;
if(!cascade.load(cascadeName))
return -1;
std::vector<cv::Rect> faces;
/// マルチスケール(顔)探索
// 画像,出力矩形,縮小スケール,最低矩形数,(フラグ),最小矩形
cascade.detectMultiScale(smallImg, faces,
1.1, 2,
CV_HAAR_SCALE_IMAGE,
cv::Size(30, 30) );
std::string nested_cascadeName = "./haarcascade_eye.xml";
//std::string nested_cascadeName = "./haarcascade_eye_tree_eyeglasses.xml";
cv::CascadeClassifier nested_cascade;
if(!nested_cascade.load(nested_cascadeName))
return -1;
std::vector<cv::Rect>::const_iterator r = faces.begin();
for(; r != faces.end(); ++r) {
// 検出結果(顔)の描画
cv::Point face_center;
int face_radius;
face_center.x = cv::saturate_cast<int>((r->x + r->width*0.5)*scale);
face_center.y = cv::saturate_cast<int>((r->y + r->height*0.5)*scale);
face_radius = cv::saturate_cast<int>((r->width + r->height)*0.25*scale);
cv::circle( img, face_center, face_radius, cv::Scalar(80,80,255), 3, 8, 0 );
cv:: Mat smallImgROI = smallImg(*r);
std::vector<cv::Rect> nestedObjects;
/// マルチスケール(目)探索
// 画像,出力矩形,縮小スケール,最低矩形数,(フラグ),最小矩形
nested_cascade.detectMultiScale(smallImgROI, nestedObjects,
1.1, 3,
CV_HAAR_SCALE_IMAGE,
cv::Size(10,10));
// 検出結果(目)の描画
std::vector<cv::Rect>::const_iterator nr = nestedObjects.begin();
for(; nr != nestedObjects.end(); ++nr) {
cv::Point center;
int radius;
center.x = cv::saturate_cast<int>((r->x + nr->x + nr->width*0.5)*scale);
center.y = cv::saturate_cast<int>((r->y + nr->y + nr->height*0.5)*scale);
radius = cv::saturate_cast<int>((nr->width + nr->height)*0.25*scale);
cv::circle( img, center, radius, cv::Scalar(80,255,80), 3, 8, 0 );
}
}
cv::namedWindow("result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow( "result", img );
cv::waitKey(0);
}
入力画像:
実行結果:
また,メガネを掛けた人物の目を検出するための学習結果ファイルを利用することもできます.顔検出の場合と同様に,本サンプルでの利用法は,該当ファイルをバイナリと同じ場所にコピーして,対応するXMLファイル(上述のソースでコメントアウトされている箇所)を読み込むだけです.
実行結果:
オブジェクト検出の際に,同程度に「オブジェクトらしい」箇所に複数の検出矩形が集まることがよくあります. 多くの場合,「オブジェクトらしい」場所の近隣も同様に「オブジェクトらしい」ためです. このような場合に,複数の矩形をグループ化して,同じような矩形は1つのグループに集めるための手法です.
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
cv::Size img_size(500, 500);
cv::Mat img = cv::Mat::zeros(img_size, CV_8UC3);
// 一様分布乱数で座標を生成
const int rand_num = 5;
cv::Mat_<int> p_tl(rand_num, 2), p_br(rand_num, 2);
cv::randu(p_tl, cv::Scalar(150-5), cv::Scalar(150+5));
cv::randu(p_br, cv::Scalar(350-5), cv::Scalar(350+5));
std::vector<cv::Rect> rects;
for(int i=0; i<rand_num; ++i) {
rects.push_back(cv::Rect(cv::Point(p_tl(i,0), p_tl(i,1)), cv::Point(p_br(i,0),p_br(i,1))));
// グループ化前の矩形を描画
cv::rectangle(img, rects.back().tl(), rects.back().br(), cv::Scalar(200,200,0), 1, CV_AA);
}
/// 矩形のグループ化
// 矩形の集合,1クラスタに最低限含まれる矩形数,マージに必要な矩形端点同士の差異
cv::groupRectangles(rects, 2, 1.0);
// グループ化された矩形を描画
std::vector<cv::Rect>::iterator it = rects.begin();
for(; it != rects.end(); ++it) {
cv::rectangle(img, it->tl(), it->br(), cv::Scalar(0,0,200), 2, CV_AA);
}
cv::namedWindow("image", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("image", img);
cv::waitKey(0);
}
実行結果:
HOG
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
const char *imagename = argc > 1 ? argv[1] : "../../image/pedestrian.png";
cv::Mat img = cv::imread(imagename, 1);
if(img.empty()) return -1;
cv::HOGDescriptor hog;
hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());
std::vector<cv::Rect> found;
// 画像,検出結果,閾値(SVMのhyper-planeとの距離),
// 探索窓の移動距離(Block移動距離の倍数),
// 画像外にはみ出た対象を探すためのpadding,
// 探索窓のスケール変化係数,グルーピング係数
hog.detectMultiScale(img, found, 0.2, cv::Size(8,8), cv::Size(16,16), 1.05, 2);
std::vector<cv::Rect>::const_iterator it = found.begin();
std::cout << "found:" << found.size() << std::endl;
for(; it!=found.end(); ++it) {
cv::Rect r = *it;
// 描画に際して,検出矩形を若干小さくする
r.x += cvRound(r.width*0.1);
r.width = cvRound(r.width*0.8);
r.y += cvRound(r.height*0.07);
r.height = cvRound(r.height*0.8);
cv::rectangle(img, r.tl(), r.br(), cv::Scalar(0,255,0), 3);
}
// 結果の描画
cv::namedWindow("result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow( "result", img );
cv::waitKey(0);
}
実行結果:
様々な手法で特徴点を検出することができます. detector( cv::FeatureDetector を継承した様々なクラス)を変えるだけで,検出手法を変更することができます.
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// 固有値に基づく特徴点検出
// maxCorners=80, qualityLevel=0.01, minDistance=5, blockSize=3
cv::GoodFeaturesToTrackDetector detector(80, 0.01, 5, 3);
detector.detect(gray_img, keypoints);
cv::Scalar color(0,200,255);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("EigenValue Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("EigenValue Features", img);
cv::waitKey(0);
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// Harris 検出器に基づく特徴点検出
// maxCorners=80, qualityLevel=0.01, minDistance=5, blockSize=3, useHarrisDetector=true
cv::GoodFeaturesToTrackDetector detector(80, 0.01, 5, 3, true);
detector.detect(gray_img, keypoints);
cv::Scalar color(255,200,0);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("Harris Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Harris Features", img);
cv::waitKey(0);
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// FAST 検出器に基づく特徴点検出
// threashld=70, nonMaxSuppression=true
cv::FastFeatureDetector detector(70, false);
detector.detect(gray_img, keypoints);
cv::Scalar color(100,255,100);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("FAST Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("FAST Features", img);
cv::waitKey(0);
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// FAST 検出器に基づく特徴点検出
// maxSize=16, responseThreshold=40, lineThresholdProjected=10, lineThresholdBinarized=8, suppressNonMaxSize=5
cv::StarFeatureDetector detector(16, 40);
cv::Scalar color(255,100,50);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("STAR Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("STAR Features", img);
cv::waitKey(0);
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// SIFT 検出器に基づく特徴点検出
// threshold=0.05, edgeThreshold=10.0
cv::SiftFeatureDetector detector(0.05,10.0);
cv::Scalar color(100,50,255);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("SIFT Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("SIFT Features", img);
cv::waitKey(0);
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// SURF 検出器に基づく特徴点検出
// hessianThreshold=4500,
cv::SurfFeatureDetector detector(4500);
cv::Scalar color(100,255,50);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("SIFT Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("SIFT Features", img);
cv::waitKey(0);
}
実行結果:
MSERは本来,領域抽出の手法ですが,OpenCVでは,検出された領域輪郭に楕円をフィッティングさせることで,サイズと方向を考慮する特徴点検出器として利用することもできます.
#include <iostream>
#include <cmath>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// MSER 検出器に基づく特徴点検出
// default parameters
cv::MserFeatureDetector detector;
cv::Scalar color(0,200,200);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("MSER Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("MSER Features", img);
cv::waitKey(0);
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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)
int
main(int argc, char *argv[])
{
#if OPENCV_VERSION_CODE<OPENCV_VERSION(2,3,0)
std::cout << "cannot support this opencv version:"
<< CV_VERSION << std::endl;
return -1;
#else
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// ORB 検出器に基づく特徴点検出
// n_features=300, params=default
cv::OrbFeatureDetector detector(300);
cv::Scalar color(200,250,255);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
}
cv::namedWindow("ORB Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("ORB Features", img);
cv::waitKey(0);
#endif
}
実行結果:
入力画像をグリッド状に分割し,その中で特徴点を検出します.
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// FAST 検出器 + Grid アダプタ に基づく特徴点検出
// maxTotalKeypoints=200, gridRows=10, gridCols=10
cv::GridAdaptedFeatureDetector detector(new cv::FastFeatureDetector(10), 200, 10, 10);
cv::Scalar color(100,255,100);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("GridAdapted Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("GridAdapted Features", img);
cv::waitKey(0);
}
実行結果:
入力画像からガウシアンピラミッドを作成し,各レベルにおいて特徴点を検出します. 特徴点のスケールを指定できない検出器において役立ちます.
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// FAST 検出器 + Pyramid アダプタ に基づく特徴点検出
// levels=3
cv::PyramidAdaptedFeatureDetector detector(new cv::FastFeatureDetector(70), 3);
cv::Scalar color(100,255,100);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("GridAdapted Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("GridAdapted Features", img);
cv::waitKey(0);
}
実行結果:
特徴点検出において,ある決まった個数範囲の特徴点を求めたい場合に,この方法が役にたちます. 求めたい特徴点個数の 最小値 と 最大値 を指定すると,検出器のパラメータを変更しながら, 指定された 繰り返し数 範囲内で探索を行います.
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// threashold=20
cv::FastFeatureDetector _detector(20);
_detector.detect(gray_img, keypoints);
std::cout << "keypoints(FastFeatureDetector): " << keypoints.size() << std::endl;
// FAST 検出器 + Dynamic アダプタ に基づく特徴点検出
// threashld=20, nonMaxSuppression=true,
// min_features=130, max_features=150, max_iters=40
cv::DynamicAdaptedFeatureDetector detector(new cv::FastAdjuster(20), 130, 150, 40);
cv::Scalar color(100,255,100);
detector.detect(gray_img, keypoints);
std::cout << "keypoints(DynamicAdaptedFeatureDetector): " << keypoints.size() << std::endl;
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 1, CV_AA);
if(itk->angle>=0) {
cv::Point pt2(itk->pt.x + cos(itk->angle)*itk->size, itk->pt.y + sin(itk->angle)*itk->size);
cv::line(img, itk->pt, pt2, color, 1, CV_AA);
}
}
cv::namedWindow("GridAdapted Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("GridAdapted Features", img);
cv::waitKey(0);
}
実行結果:
keypoints(FastFeatureDetector): 1614
keypoints(DynamicAdaptedFeatureDetector): 145
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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)
int
main(int argc, char *argv[])
{
#if OPENCV_VERSION_CODE<OPENCV_VERSION(2,3,0)
std::cout << "cannot support this opencv version:"
<< CV_VERSION << std::endl;
return -1;
#else
cv::Mat img = cv::imread("../../image/BigBead.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
// SimpleBlob 検出器に基づく特徴点検出
// thresholdStep=20, other params=default
cv::SimpleBlobDetector::Params params;
params.thresholdStep = 20;
cv::SimpleBlobDetector detector(params);
cv::Scalar color(200,255,100);
detector.detect(gray_img, keypoints);
for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
cv::circle(img, itk->pt, 1, color, -1);
cv::circle(img, itk->pt, itk->size, color, 3, CV_AA);
}
cv::namedWindow("SimpleBlob Features", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("SimpleBlob Features", img);
cv::waitKey(0);
#endif
}
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors;
//
// threshold=0.05, edgeThreshold=10.0
cv::SiftFeatureDetector detector(0.05,10.0);
detector.detect(gray_img, keypoints);
// SIFT に基づくディスクリプタ抽出器
cv::SiftDescriptorExtractor extractor;
cv::Scalar color(100,255,100);
extractor.compute(gray_img, keypoints, descriptors);
// 128次元の特徴量 x keypoint数
for(int i=0; i<descriptors.rows; ++i) {
cv::Mat d(descriptors, cv::Rect(0,i,descriptors.cols,1));
std::cout << i << ": " << d << std::endl;
}
}
実行結果(最初の2行):
0: [0.021233672, 0.0014659651, 0.00054864027, 0.043791994, 0.15031554, 0.0081593897, 0.0052633695, 0.017900508, 0.14055812, 8.8222216e-05, 8.6816208e-06, 0.030280454, 0.079922713, 0.024566842, 0.014150854, 0.13550681, 0.084639646, 0.0055453554, 0.0002427475, 0.00017436311, 0.20782301, 0.12179199, 0.017845433, 0.051384307, 0.24379671, 0.04320721, 0.0031827968, 0.0067592184, 0.017057467, 0.0070692343, 0.00028205666, 0.010635576, 0.026519295, 0.0020695599, 0.00010507141, 0.051817521, 0.24379671, 0.011494503, 0.012467382, 0.022929478, 0.24379671, 0.015562179, 0.00014225872, 0.0075872038, 0.093277723, 0.030935662, 0.0084859077, 0.17412668, 0.086305462, 0.0027722607, 0.00011851717, 0.0001424334, 0.24379671, 0.18022627, 0.022019938, 0.10054632, 0.24379671, 0.020836202, 0.0073310034, 0.014498728, 0.030319119, 0.023211006, 0.012665867, 0.17109598, 0.027040122, 0.015344816, 0.0038383096, 0.053639598, 0.24379671, 0.015584219, 0.00036631722, 0.00097804808, 0.24379671, 0.17657502, 0.0014261669, 0.010763198, 0.062686205, 0.0095831826, 0.001129471, 0.014588176, 0.10341847, 0.05432947, 0.0017211817, 0.0058160201, 0.19489776, 0.12657295, 0.036803395, 0.10844494, 0.05650235, 0.0056605069, 0.0058878455, 0.0046394193, 0.035608321, 0.043195184, 0.032221042, 0.2320625, 0.031031983, 0.019548343, 0.0016009037, 0.0021222478, 0.13484018, 0.10258325, 0.0028180839, 0.0019360944, 0.24379671, 0.082535937, 0.0010450779, 0.001452325, 0.032737724, 0.042230025, 0.0076545845, 0.012181766, 0.11940889, 0.078639783, 0.02931896, 0.031542189, 0.01965417, 0.01233358, 0.0052560121, 0.01419207, 0.02527442, 0.080087811, 0.056695677, 0.011795605, 0.011991151, 0.0063652727, 0.0025214576, 0.017665019]
1: [0.063618973, 0.27865481, 0.19649969, 0.008179483, 0.00023455996, 0.00014861442, 0.00023793994, 0.0001030515, 0.13486731, 0.17437772, 0.084160596, 0.070562989, 0.016319536, 0.032444309, 0.036570705, 0.023056991, 0.075900875, 0.069621712, 0.04741209, 0.01626209, 0.0072119543, 0.069958389, 0.068665504, 0.022338104, 0.2045857, 0.098395817, 0.050180946, 0.003105673, 0.00020554618, 0.0014919095, 0.0046132631, 0.099711582, 0.26318488, 0.087078199, 0.011536893, 0.0062746275, 0.0005164505, 0.00090169167, 0.074670583, 0.045949377, 0.27865481, 0.087247498, 0.0035000516, 0.0051924065, 0.0061974553, 0.031058798, 0.054185811, 0.11179995, 0.067107782, 0.01578919, 0.0089584077, 0.042678677, 0.13425377, 0.17923307, 0.10109202, 0.046970662, 0.037586223, 0.09692584, 0.11265471, 0.015591307, 0.028214036, 0.010593625, 0.0073529598, 0.0073716887, 0.066582181, 0.0010976094, 0, 0, 1.1674022e-06, 0.0073210392, 0.27865481, 0.1735087, 0.27865481, 0.06770201, 0.030971454, 0.029228326, 0.0092106732, 0.023255298, 0.16263069, 0.1806239, 0.040729184, 0.030285547, 0.042511113, 0.1631007, 0.12234834, 0.014959278, 0.012759373, 0.014949872, 0.0036240586, 0.0050182575, 0.0067629395, 0.010970328, 0.024487708, 0.018909603, 0.065328762, 0.022244232, 0.050587136, 0.08213155, 0.012370801, 0.0037464912, 0.0066909697, 0.03632981, 0.24200492, 0.091015652, 0.0084979935, 0.0094920862, 0.0079585062, 0.022813687, 0.11294786, 0.16293645, 0.12560333, 0.023835396, 0.0090456782, 0.0010426415, 0.0040089954, 0.018021021, 0.037192263, 0.027802264, 0.011082977, 0.022885716, 0.019266251, 0.00074749964, 7.8681638e-05, 0.00014372123, 0.00019890479, 0.0030645328, 0.020529719, 0.038005192]
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors;
//
// threshold=4500
cv::SurfFeatureDetector detector(4500);
detector.detect(gray_img, keypoints);
// SURF に基づくディスクリプタ抽出器
cv::SurfDescriptorExtractor extractor;
cv::Scalar color(100,255,50);
extractor.compute(gray_img, keypoints, descriptors);
// 64次元の特徴量 x keypoint数
for(int i=0; i<descriptors.rows; ++i) {
cv::Mat d(descriptors, cv::Rect(0,i,descriptors.cols,1));
std::cout << i << ": " << d << std::endl;
}
}
実行結果(最初の2行):
0: [0.0005440524, 0.00019080873, 0.0011156601, 0.00060204417, 0.031112911, -0.0049687005, 0.03507245, 0.0080976002, -0.0028718193, 0.00090316747, 0.003157127, 0.0027868869, -0.00011331915, -0.00025480194, 0.0001945437, 0.00049618207, -0.0012214067, -0.00029818181, 0.0054713301, 0.0025845629, 0.36256874, 0.071781643, 0.40608761, 0.11346382, -0.065762296, 0.21537137, 0.065762296, 0.22915401, -0.0069807679, 0.029208595, 0.0077104745, 0.031328633, 0.00089156715, 0.0006404923, 0.0038368064, 0.0031534194, 0.51476336, -0.042307768, 0.52824235, 0.075963326, -0.022579186, 0.036412533, 0.02337856, 0.046764888, -0.0039506666, 0.011458799, 0.0040103844, 0.011458799, -0.00056651817, 7.803012e-05, 0.00057466829, 0.00047578587, 0.061804887, -0.043759901, 0.063265294, 0.044868838, 0.0088353241, -0.051317215, 0.0097551076, 0.051738929, 0.00096447999, -0.0053662988, 0.0010464506, 0.0053944737]
1: [-0.00072802493, -0.00041244976, 0.0017245135, 0.0011232586, -0.0067925481, -0.0030960809, 0.0079159923, 0.0044780653, 0.043065421, 0.00064619089, 0.061223406, 0.0055225501, 0.0086779324, 0.00014670294, 0.0088232476, 0.00087578868, 0.00012487371, 0.0264633, 0.016818596, 0.051180985, -0.14471763, 0.028734764, 0.17976919, 0.14659522, 0.31568813, -0.0027507769, 0.41184184, 0.058844961, 0.047577083, -0.0039710426, 0.049062785, 0.0097013526, 0.017899623, -0.016191542, 0.028678821, 0.046525974, -0.074518472, 0.10378933, 0.30330241, 0.49421933, 0.17636862, 0.060175549, 0.43175146, 0.21572286, 0.051260743, 0.00054621755, 0.054715548, 0.006727933, 0.0031269097, -0.00389991, 0.0033542607, 0.0039988817, -0.026279358, -0.0058076805, 0.032108702, 0.025418459, 0.033053506, -0.023869021, 0.050510518, 0.030669102, 0.011189211, -0.00067684194, 0.012327171, 0.0011812798]
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/legacy/legacy.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors;
//
// threshold=4500
cv::SurfFeatureDetector detector(4500);
detector.detect(gray_img, keypoints);
// Calonder に基づくディスクリプタ抽出器(あらかじめ学習させておく必要があります)
cv::CalonderDescriptorExtractor<float> extractor("calonder.dat");
cv::Scalar color(100,255,100);
extractor.compute(gray_img, keypoints, descriptors);
//
for(int i=0; i<descriptors.rows; ++i) {
cv::Mat d(descriptors, cv::Rect(0,i,descriptors.cols,1));
std::cout << i << ": " << d << std::endl;
}
}
実行結果(最初の5行):
[OK] Loaded RTC, quantization=4 bits
[OK] RTC: overall 55673/4325376 (1.287%) zeros in float leaves
overall 243423/4325376 (5.628%) zeros in uint8 leaves
0: [-0.00066007086, -0.0024703937, 0.00051066413, -0.002032117, -0.0016254914, 0.0022725903, -2.1952466e-05, -0.0011181739, 0.0012997336, 0.00014707487, -4.5135701e-05, -0.0015029697, 0.00031392116, -0.0022969458, 0.001976727, -0.00099447055, 0.0012108093, -0.00047411732, -2.6497488e-05, -0.0010783827, 0.00087404321, 0.00072360033, -0.0006009338, 0.001890304, 0.00029500175, 0.003113484, -0.0006059141, 0.00074204541, 0.00057215668, 0.00049296627, -0.00030687905, 0.0017618984, 0.00082324166, 0.0016948842, -0.0041480018, 0.002844325, 3.8008115e-05, -0.0020949445, -0.001950997, 0.00029717243, -0.0020662977, 0.0027430113, -0.0015814503, -0.00027710691, 0.00061151962, -5.7207752e-05, -0.00075050228, 0.001434573, -0.0036784874, 0.00064557674, 0.00088262529, -0.0025574949, 0.00076234736, 0.0016572556, -0.0023775462, 0.00016016405, -0.00079171243, 0.00030554456, 0.00092991948, 0.00064085104, 0.0024300502, 0.00046147266, -0.00022175124, 0.00079395209, 0.00030632596, -0.00040124182, -8.6227868e-05, 0.0014025657, 0.0010878899, -0.00035935585, -1.9277368e-05, -5.9232112e-05, -0.0010884214, -0.00034759883, -0.00017398712, 0.0018306236, -0.0023302666, 0.00063621835, 8.5777523e-05, 0.000631298, -4.7389778e-05, 0.0020115257, 0.00070277468, -0.0018638497, -0.0010511409, -0.00015567684, -0.0011067932, -0.0027190733, -0.00093306886, 0.0016994989, 0.0036870835, -0.0007537643, -0.00056488416, 0.002259993, -0.0016289778, 7.040504e-05, 0.00089445151, -0.0004169695, -0.0024700069, -0.0022734238, 4.4834451e-07, 1.1578998e-05, 0.0012584596, -0.0015955415, -0.00021057886, 0.003623195, -0.00065472699, -0.00067710812, -0.00040601785, 0.0016859263, 0.00081306492, -0.00084803178, 0.00089693139, 0.001117954, -0.0010808489, -0.0015704372, 0.0016996716, 0.0002969857, -0.00021493435, -0.0019525024, -0.00044437489, -0.00054735062, -0.0042922478, 0.0022557213, -0.00042609987, -0.00078931608, -0.00027628333, 6.4732005e-05, -0.00091591617, -0.00025169147, -0.0014460358, -0.0024987783, 0.0026095982, 0.0035187399, -0.0032224783, 0.0011347263, -0.0021197691, 0.0014817567, -0.0015274514, 0.0013261451, 0.0020656122, 0.0019335005, -3.3104869e-05, 0.0030435326, -0.0014547043, -0.0014690449, -0.00033831145, -4.2241649e-05, -0.0015512211, 0.00060716557, -0.0040006777, -0.0017807428, 0.00076706358, 0.0010047187, 0.0011281611, -0.001454946, 0.0019023614, 0.0015685922, -0.0027419019, 0.00063813577, -0.00085954246, 0.00061636593, 0.0021170245, 0.00099111756, -0.00044671181, -0.00091111677, -0.0010112411, -0.00020761949, 0.00062171422, -0.0012111417, -0.0021619105, 0.0015374956, -0.00019102466, -0.00041628841, 0.0019997358, 0.0018624921]
1: [-0.0017131848, -0.0017694887, 0.0038728307, -0.0011573619, -0.001508927, 0.00055965217, 0.0027727291, -0.0001537947, -0.00061144569, -0.00027270502, -0.00077621639, -0.0018652329, -9.9153061e-05, -0.002982822, 0.00042818589, 0.00075839396, 0.0037067428, 4.4658614e-07, 0.00076624664, 0.00089692342, -0.00014199778, 0.00091094914, -0.0019691263, -0.00047743542, -0.0023056534, 0.00037580857, 0.00031019296, 0.0019658417, 0.0015228369, 0.00041580017, 0.00013475995, 0.0010534819, -0.0010911704, 0.0003334959, -0.0027049223, 0.0013364753, 0.00016094309, -0.0002824782, -0.00096225593, 0.0025072019, -0.00040832502, 0.0028687126, -0.00045640478, 0.0010744483, 4.2792712e-05, -0.0015350333, -0.0034802575, 0.0021858169, -0.00078705518, -0.001551567, 0.0017340543, 0.0012833631, -0.00094231707, 0.0010057853, -0.0036348205, 0.00053347909, 0.0027175115, -0.00019107746, 0.0026801985, -0.0005192922, 0.00057908299, -0.0011273101, -0.0026315066, -0.0018757157, 0.0020011256, -0.00055518252, -0.00087493024, 0.0028906113, 0.0006187135, -0.00099275506, -0.0010975956, -0.00032995982, 0.0010934341, 0.0017741012, 0.00026472489, 0.00025876681, 0.0011092479, -0.00022165776, -0.00033365621, -0.00044268343, 0.001081433, -0.00026227822, -0.00081609882, -0.0029998657, -0.0010666655, -0.00013648646, 0.00015024703, 0.00090111612, 0.00013877143, -0.00086062384, 0.0020823823, -0.0005365744, -0.0013149502, 0.00044973768, 0.00083720469, -0.00015207315, -5.1880746e-05, 0.001652954, -0.0013588372, -0.003022237, 0.00086524105, 0.0010019534, 0.0017731227, -3.6405643e-05, -0.0017166324, 0.0026721063, -0.0011735077, -0.00072048139, 0.00032740657, 0.00048561511, 0.0017567994, -0.0013743151, 0.00090102694, 0.0011765239, 0.00048251209, -0.0023282918, 0.0017043905, -0.00025485293, -0.00069832691, -0.00069154584, 0.0010746883, 0.00082947873, -0.0003351222, -0.001322386, 0.0018752591, -0.00035994544, 2.0959653e-05, 0.00076145661, -6.0290142e-05, 0.00061863399, -0.00048026783, -0.0011446426, 0.0024944434, 0.0026537599, -0.00067963422, -0.0024214219, -0.0027384192, -0.00013052628, -0.00054970017, -7.778986e-05, 0.0023211818, 0.0029513522, 0.00062964443, -4.4173023e-05, -0.0019382994, -0.0017759009, 0.00143599, 0.0011939075, 0.00077985041, -0.00087809726, -0.0023401917, -0.00020799012, 0.0025424829, 0.0020968353, -0.0020334772, 0.001881829, -0.00044259927, 0.0024173013, -0.0023374304, 0.0013871035, -0.0023284138, -0.00060242362, 0.00013056258, -0.00079482776, -0.00054403808, -0.00039382861, 0.0018237618, 0.00063636556, 0.0015730833, -0.0015360246, -0.0010277134, 0.00059501949, -0.00040481714, -0.0010504216, -0.00044775088, 0.002655538]
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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)
int
main(int argc, char *argv[])
{
#if OPENCV_VERSION_CODE<OPENCV_VERSION(2,3,0)
std::cout << "cannot support this opencv version:"
<< CV_VERSION << std::endl;
return -1;
#else
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors;
//
// n_features=300, params=default
cv::OrbFeatureDetector detector(300);
detector.detect(gray_img, keypoints);
// ORB に基づくディスクリプタ抽出器
cv::OrbDescriptorExtractor extractor;
cv::Scalar color(200,250,255);
extractor.compute(gray_img, keypoints, descriptors);
// 32次元の特徴量 x keypoint数
for(int i=0; i<descriptors.rows; ++i) {
cv::Mat d(descriptors, cv::Rect(0,i,descriptors.cols,1));
std::cout << i << ": " << d << std::endl;
}
#endif
}
実行結果(最初の2行):
0: [180, 137, 181, 234, 55, 126, 183, 253, 99, 155, 106, 4, 14, 190, 91, 118, 178, 110, 239, 152, 201, 31, 246, 132, 242, 214, 175, 35, 227, 55, 43, 159]
1: [252, 215, 78, 130, 207, 217, 231, 175, 222, 53, 46, 119, 80, 17, 107, 45, 124, 21, 99, 62, 107, 31, 52, 171, 124, 26, 160, 103, 230, 97, 83, 163]
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img = cv::imread("../../image/lenna.png", 1);
if(img.empty()) return -1;
cv::Mat gray_img;
cv::cvtColor(img, gray_img, CV_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors;
//
// threshold=0.05, edgeThreshold=10.0
cv::SiftFeatureDetector detector(0.05,10.0);
detector.detect(gray_img, keypoints);
// Brief に基づくディスクリプタ抽出器
cv::BriefDescriptorExtractor extractor;
cv::Scalar color(50,50,155);
extractor.compute(gray_img, keypoints, descriptors);
// 32次元の特徴量 x keypoint数
for(int i=0; i<descriptors.rows; ++i) {
cv::Mat d(descriptors, cv::Rect(0,i,descriptors.cols,1));
std::cout << i << ": " << d << std::endl;
}
}
実行結果(最初の2行):
0: [72, 198, 7, 190, 7, 54, 94, 100, 25, 243, 31, 128, 247, 240, 129, 40, 94, 130, 80, 30, 102, 47, 128, 45, 13, 43, 174, 59, 44, 56, 162, 215]
1: [25, 198, 32, 84, 33, 59, 42, 84, 31, 10, 218, 40, 112, 137, 177, 11, 166, 250, 90, 189, 136, 155, 223, 33, 249, 93, 37, 179, 168, 61, 44, 93]
ある画像から局所特徴量の集合が計算されると,それらを複数の画像間で比較することができます. matcher( cv::DescriptorMatcher または cv::GenericDescriptorMatcher を継承した様々なクラス)を変えるだけで,比較手法を変更することができます.
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img1 = cv::imread("../../image/lenna.png", 1);
if(img1.empty()) return -1;
cv::Mat img2 = cv::imread("../../image/lenna_rotated.png", 1);
if(img2.empty()) return -1;
cv::Mat gray_img1, gray_img2;
cv::cvtColor(img1, gray_img1, CV_BGR2GRAY);
cv::cvtColor(img2, gray_img2, CV_BGR2GRAY);
cv::normalize(gray_img1, gray_img1, 0, 255, cv::NORM_MINMAX);
cv::normalize(gray_img2, gray_img2, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints1, keypoints2;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors1, descriptors2;
// SURF
// threshold=2000
cv::SurfFeatureDetector detector(2000);
detector.detect(gray_img1, keypoints1);
detector.detect(gray_img2, keypoints2);
// SURF に基づくディスクリプタ抽出器
cv::SurfDescriptorExtractor extractor;
cv::Scalar color(100,255,50);
extractor.compute(gray_img1, keypoints1, descriptors1);
extractor.compute(gray_img2, keypoints2, descriptors2);
// FlannBasedMatcher によるマッチ
cv::FlannBasedMatcher matcher;
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
// マッチング結果の描画
cv::Mat dst_img;
cv::drawMatches(img1, keypoints1, img2, keypoints2, matches, dst_img);
cv::namedWindow("Match", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Match", dst_img);
cv::waitKey(0);
}
入力画像(入力画像1,入力画像2):
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img1 = cv::imread("../../image/lenna.png", 1);
if(img1.empty()) return -1;
cv::Mat img2 = cv::imread("../../image/lenna_rotated.png", 1);
if(img2.empty()) return -1;
cv::Mat gray_img1, gray_img2;
cv::cvtColor(img1, gray_img1, CV_BGR2GRAY);
cv::cvtColor(img2, gray_img2, CV_BGR2GRAY);
cv::normalize(gray_img1, gray_img1, 0, 255, cv::NORM_MINMAX);
cv::normalize(gray_img2, gray_img2, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints1, keypoints2;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors1, descriptors2;
// SURF
// threshold=2000
cv::SurfFeatureDetector detector(2000);
detector.detect(gray_img1, keypoints1);
detector.detect(gray_img2, keypoints2);
// SURF に基づくディスクリプタ抽出器
cv::SurfDescriptorExtractor extractor;
cv::Scalar color(100,255,50);
extractor.compute(gray_img1, keypoints1, descriptors1);
extractor.compute(gray_img2, keypoints2, descriptors2);
// FlannBasedMatcher によるKNNマッチを利用したクロスチェック
cv::FlannBasedMatcher matcher;
std::vector<cv::DMatch> matches;
std::vector<std::vector<cv::DMatch> > matches12, matches21;
int knn = 1;
matcher.knnMatch(descriptors1, descriptors2, matches12, knn);
matcher.knnMatch(descriptors2, descriptors1, matches21, knn);
// KNN探索で,1->2と2->1が一致するものだけがマッチしたとみなされる
for( size_t m = 0; m < matches12.size(); m++ ) {
bool findCrossCheck = false;
for( size_t fk = 0; fk < matches12[m].size(); fk++ ) {
cv::DMatch forward = matches12[m][fk];
for( size_t bk = 0; bk < matches21[forward.trainIdx].size(); bk++ ) {
cv::DMatch backward = matches21[forward.trainIdx][bk];
if( backward.trainIdx == forward.queryIdx ) {
matches.push_back(forward);
findCrossCheck = true;
break;
}
}
if( findCrossCheck ) break;
}
}
// マッチング結果の描画
cv::Mat dst_img;
cv::drawMatches(img1, keypoints1, img2, keypoints2, matches, dst_img);
cv::namedWindow("Match", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Match", dst_img);
cv::waitKey(0);
}
入力画像(入力画像1,入力画像2):
実行結果:
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.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,4,0)
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/legacy/legacy.hpp>
#endif
int
main(int argc, char *argv[])
{
cv::Mat img1 = cv::imread("../../image/lenna.png", 1);
if(img1.empty()) return -1;
cv::Mat img2 = cv::imread("../../image/lenna_rotated.png", 1);
if(img2.empty()) return -1;
cv::Mat gray_img1, gray_img2;
cv::cvtColor(img1, gray_img1, CV_BGR2GRAY);
cv::cvtColor(img2, gray_img2, CV_BGR2GRAY);
cv::normalize(gray_img1, gray_img1, 0, 255, cv::NORM_MINMAX);
cv::normalize(gray_img2, gray_img2, 0, 255, cv::NORM_MINMAX);
std::vector<cv::KeyPoint> keypoints1, keypoints2;
std::vector<cv::KeyPoint>::iterator itk;
cv::Mat descriptors1, descriptors2;
// SURF
// threshold=2000
cv::SurfFeatureDetector detector(2000);
detector.detect(gray_img1, keypoints1);
detector.detect(gray_img2, keypoints2);
// SURF に基づくディスクリプタ抽出器
cv::SurfDescriptorExtractor extractor;
cv::Scalar color(100,255,50);
extractor.compute(gray_img1, keypoints1, descriptors1);
extractor.compute(gray_img2, keypoints2, descriptors2);
// BruteForceMatcher によるマッチ
// 比較にL2距離を指定
// 他には L1<class T>, Hamming, HammingLUT など
cv::BruteForceMatcher<cv::L2<float> > matcher;
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
// マッチング結果の描画
cv::Mat dst_img;
cv::drawMatches(img1, keypoints1, img2, keypoints2, matches, dst_img);
cv::namedWindow("Match", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Match", dst_img);
cv::waitKey(0);
}
入力画像(入力画像1,入力画像2):
実行結果:
画像のチャンネル(R,G,Bなど)の合成と分離も,行列の場合と同様です. cv::Mat のチャンネルの合成と分離 を参照してください.
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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;
// 画像境界の値を複製してコピー aaaaaa|abcdefgh|hhhhhhh
cv::Mat repBD_img;
copyMakeBorder(src_img, repBD_img, 20,20,20,20, cv::BORDER_REPLICATE);
// 画像境界で反射するようにコピー fedcba|abcdefgh|hgfedcb
cv::Mat refBD_img;
copyMakeBorder(src_img, refBD_img, 20,20,20,20, cv::BORDER_REFLECT);
// 画像境界で反射するようにコピー(境界を共有) gfedcb|abcdefgh|gfedcba
cv::Mat ref101BD_img;
copyMakeBorder(src_img, ref101BD_img, 20,20,20,20, cv::BORDER_REFLECT_101);
// 画像自身を繰り返しコピー cdefgh|abcdefgh|abcdefg
cv::Mat wrapBD_img;
copyMakeBorder(src_img, wrapBD_img, 20,20,20,20, cv::BORDER_WRAP);
// 固定値をコピーして埋める
cv::Mat constBD_img;
copyMakeBorder(src_img, constBD_img, 20,20,20,20, cv::BORDER_CONSTANT, cv::Scalar(200,100,0));
cv::namedWindow("replicate border", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("replicate border", repBD_img);
cv::namedWindow("reflect border", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("reflect border", refBD_img);
cv::namedWindow("reflect101 border", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("reflect101 border", ref101BD_img);
cv::namedWindow("wrap border", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("wrap border", wrapBD_img);
cv::namedWindow("constant border", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("constant border", constBD_img);
cv::waitKey(0);
}
入力画像(入力画像1,入力画像2):
実行結果:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
const char *imagename = argc > 1 ? argv[1] : "../../image/star_objects.png";
const int contour_index = argc > 2 ? atoi(argv[2]) : 1;
const int max_level = argc > 3 ? atoi(argv[3]) : 0;
cv::Mat src_img = cv::imread(imagename, 1);
if(src_img.empty()) return -1;
// 2値化
cv::Mat gray_img, bin_img;
cv::cvtColor(src_img, gray_img, CV_BGR2GRAY);
cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU);
/// 輪郭の検出
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
// 2値画像,輪郭(出力),階層構造(出力),輪郭抽出モード,輪郭の近似手法
cv::findContours(bin_img, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
std::cout << "contour index = " << contour_index << std::endl;
std::cout << "max level = " << max_level << std::endl;
std::cout << "num of contours = " << contours.size() << std::endl;
/// 輪郭の描画
// 画像,輪郭,描画輪郭指定インデックス,色,太さ,種類,階層構造,描画輪郭の最大レベル
cv::drawContours(src_img, contours, contour_index, cv::Scalar(0, 0, 200), 3, CV_AA, hierarchy, max_level);
cv::namedWindow("Contours", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Contours", src_img);
cv::waitKey(0);
}
入力画像:
実行結果(index=0, max_level=0):
./sample_contours ../../image/star_objects.png 0 0
contour index = 0
max level = 0
num of contours = 8
実行結果(index=2, max_level=0):
./sample_contours ../../image/star_objects.png 2 0
contour index = 2
max level = 0
num of contours = 8
実行結果(index=2, max_level=1):
./sample_contours ../../image/star_objects.png 2 1
contour index = 2
max level = 1
num of contours = 8
実行結果(index=-1, max_level=1):
./sample_contours ../../image/star_objects.png -1 1
contour index = -1
max level = 1
num of contours = 8
OpenCVのC++インタフェースには,オプティカルフローを計算する二種類のメソッドが用意されています (CやPythonインタフェースからは,以前の cvCalcPoticalFlowBM や cvCalcPoticalFlowHS, cvCalcPoticalFlowLK なども利用できます).
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/video/tracking.hpp>
#include <opencv2/imgproc/imgproc.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)
int
main(int argc, char *argv[])
{
// image1
cv::Mat img1 = cv::imread("../../image/blocks1.png", 1);
if(img1.empty()) return -1;
// image2
cv::Mat img2 = cv::imread("../../image/blocks2.png", 1);
if(img2.empty()) return -1;
cv::Mat prev, next;
cv::cvtColor(img1, prev, CV_BGR2GRAY);
cv::cvtColor(img2, next, CV_BGR2GRAY);
std::vector<cv::Point2f> prev_pts;
std::vector<cv::Point2f> next_pts;
// 初期化
cv::Size flowSize(30,30);
cv::Point2f center = cv::Point(prev.cols/2., prev.rows/2.);
for(int i=0; i<flowSize.width; ++i) {
for(int j=0; j<flowSize.width; ++j) {
cv::Point2f p(i*float(prev.cols)/(flowSize.width-1),
j*float(prev.rows)/(flowSize.height-1));
prev_pts.push_back((p-center)*0.9f+center);
}
}
// Lucas-Kanadeメソッド+画像ピラミッドに基づくオプティカルフロー
// parameters=default
#if OPENCV_VERSION_CODE > OPENCV_VERSION(2,3,0)
cv::Mat status, error;
#else
std::vector<uchar> status;
std::vector<float> error;
#endif
cv::calcOpticalFlowPyrLK(prev, next, prev_pts, next_pts, status, error);
// オプティカルフローの表示
std::vector<cv::Point2f>::const_iterator p = prev_pts.begin();
std::vector<cv::Point2f>::const_iterator n = next_pts.begin();
for(; n!=next_pts.end(); ++n,++p) {
cv::line(img1, *p, *n, cv::Scalar(150,0,0),2);
}
cv::namedWindow("optical flow", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("optical flow", img1);
cv::waitKey(0);
}
入力画像:
実行結果:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/video/tracking.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int
main(int argc, char *argv[])
{
// image1
cv::Mat img1 = cv::imread("../../image/blocks1.png", 1);
if(img1.empty()) return -1;
// image2
cv::Mat img2 = cv::imread("../../image/blocks2.png", 1);
if(img2.empty()) return -1;
cv::Mat prev, next;
cv::cvtColor(img1, prev, CV_BGR2GRAY);
cv::cvtColor(img2, next, CV_BGR2GRAY);
std::vector<cv::Point2f> prev_pts;
// 初期化
cv::Size flowSize(30,30);
cv::Point2f center = cv::Point(prev.cols/2., prev.rows/2.);
for(int i=0; i<flowSize.width; ++i) {
for(int j=0; j<flowSize.width; ++j) {
cv::Point2f p(i*float(prev.cols)/(flowSize.width-1),
j*float(prev.rows)/(flowSize.height-1));
prev_pts.push_back((p-center)*0.9f+center);
}
}
// Gunnar Farnebackのアルゴリズムに基づくオプティカルフロー
// 画像ピラミッド作成のスケール(<1),ピラミッドの層数,
// 平均化窓サイズ,各層での繰り返し数,
// 各ピクセルの隣接領域サイズ.通常は 5 or 7,
// ガウシアンの標準偏差,フラグ
cv::Mat flow;
std::vector<float> error;
cv::calcOpticalFlowFarneback(prev, next, flow, 0.5, 3, 15, 3, 5, 1.1, 0);
// オプティカルフローの表示
std::vector<cv::Point2f>::const_iterator p = prev_pts.begin();
for(; p!=prev_pts.end(); ++p) {
const cv::Point2f& fxy = flow.at<cv::Point2f>(p->y, p->x);
cv::line(img1, *p, *p+fxy, cv::Scalar(150,0,0),2);
}
cv::namedWindow("optical flow", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("optical flow", img1);
cv::waitKey(0);
}
入力画像:
実行結果:
OpenCV2.3.1で,アルファチャンネル付きPNG画像がサポートされました. ただし,現時点ではUndocumentedなので,正式なサポートか否かは不明です. アルファチャンネル付き画像を読み込む際に指定するフラグ(-1)は,highgui.hpp で
enum
{
...
IMREAD_UNCHANGED =-1,
...
}
と定義されている値です.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.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)
int
main(int argc, char *argv[])
{
#if OPENCV_VERSION_CODE<OPENCV_VERSION(2,3,1)
std::cout << "cannot support this opencv version:"
<< CV_VERSION << std::endl;
return -1;
#else
// -1 を指定してアルファチャンネルを読み込む
cv::Mat img1 = cv::imread("../../image/cvlogo.png", -1);
if(img1.empty()) return -1;
cv::Mat img2 = cv::imread("../../image/lenna.png", 1);
if(img2.empty()) return -1;
// BGRAチャンネルに分離
std::vector<cv::Mat> mv;
cv::split(img1, mv);
/// 合成処理
std::vector<cv::Mat> tmp_a;
cv::Mat alpha, alpha32f;
// 4番目のチャンネル=アルファ
alpha = mv[3].clone();
mv[3].convertTo(alpha32f, CV_32FC1);
cv::normalize(alpha32f, alpha32f, 0., 1., cv::NORM_MINMAX);
for(int i=0; i<3; i++) tmp_a.push_back(alpha32f);
cv::Mat alpha32fc3, beta32fc3;
cv::merge(tmp_a, alpha32fc3);
cv::Mat tmp_ones = cv::Mat::ones(cv::Size(img2.rows, img2.cols*3), CV_32FC1);
beta32fc3 = tmp_ones.reshape(3,img2.rows) - alpha32fc3;
cv::Mat img1_rgb, img1_32f, img2_32f;
mv.resize(3);
cv::merge(mv, img1_rgb);
img1_rgb.convertTo(img1_32f, CV_32FC3);
img2.convertTo(img2_32f, CV_32FC3);
// 二つの画像の重み付き和
cv::Mat blend32f = img1_32f.mul(alpha32fc3) + img2_32f.mul(beta32fc3);
cv::Mat blend;
blend32f.convertTo(blend, CV_8UC3);
// 表示
cv::namedWindow("Alpha channel", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::namedWindow("Blend", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
cv::imshow("Alpha channel", alpha);
cv::imshow("Blend", blend);
cv::waitKey(0);
#endif
}
入力画像:
実行結果(アルファチャンネル,合成画像):