2015年6月に大望のOpenCVのメジャーバージョンアップ「3.0」が登場した。この新バージョンの新機能をモジュールごとに説明し、次バージョン3.1のロードマップを簡単に紹介する。
ご注意:本記事は、@IT/Deep Insider編集部(デジタルアドバンテージ社)が「www.buildinsider.net」というサイトから、内容を改変することなく、そのまま「@IT」へと転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。
OpenCVは、2015年6月にメジャーバージョンアップが実施され、3.0がリリースされました。また、前回の記事でも述べたようにOpenCV 3.0ではさまざまな機能が追加されています。今回の記事では追加された機能をピックアップして使用例を交えながら紹介します。
ここでは、次バージョン(OpenCV 3.1)のロードマップについても触れておきたいと思います。
公式の開発進捗(しんちょく)議事録であるOpenCV Meeting notes(2015/10/6)に、
OpenCV 3.1 release is planned for end of November-beginning of December
という記述があり、(少なくとも2015/10/6時点では)2015年末にOpenCV 3.1がリリースされる予定となっているようです。
さらに、
- Will include all of the summer of code work
- Will take better advantage of Intel's IPP ICV
- Better HAL (Hardware Acceleration Layer) support.
- Intrinsics to better support SSE and Neon (same code, 2 deployments).
という記述もあることから、OpenCV Google Summer of Code 2015の成果物取り込みによる機能追加だけでなく、IPPICVのさらなる活用やHALの改善によるパフォーマンス向上なども取り組まれるようです。
この章ではOpenCV 3.0の新機能紹介をモジュールごとに紹介します。今回紹介するモジュールは以下の通りです。
また、ユーザーから寄贈された追加機能がまとめられているリポジトリopencv_contribに導入された新機能については別の記事で紹介予定です。
CPU/GPU実装のカプセル化: UMat
OpenCV 3.0よりCPU/GPU実装のカプセル化を行うための仕組みであるT-API(transparent API)が導入されました。そのため、OpenCVユーザーは、T-APIで提供されるUMatと呼ばれるデータ構造を用いて実装することで、CPU/GPUどちらでも動作する処理を同一コードで記述できます。
図1にUMatを用いた場合の内部処理の概要を示します。
ただし、図1の青で囲んだ内部処理はOpenCV内部に隠ぺいされているため、OpenCVユーザーがこれらの処理を記述する必要はありません。
また、図2は、「CVPR 2015チュートリアル資料」(英語)で紹介されているT-APIの計測結果です。この結果よりT-API(OpenCL)を用いることで、Matを用いた場合に比べて高速化できていることが分かります。※ただし、計測環境によって効果は異なるので参考程度とした方がよいでしょう。
●サンプルコード(CPU/GPU実装のカプセル化: UMat)
UMatを使ってグレースケール化を行うサンプルコードを以下に示します。
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
// (1)画像データをファイル(この例では「lena.jpg」)から読み込む
cv::Mat img = cv::imread("lena.jpg", cv::IMREAD_COLOR);
// 画像の読み込みに失敗したらエラー終了する
if (img.empty())
{
std::cerr << "Failed to open image file." << std::endl;
return -1;
}
// (2)UMatのインスタンスを定義する
cv::UMat u_img, u_gray;
// (3)MatのデータをUMatにコピーする
img.copyTo(u_img);
// (4)UMatのインスタンスを用いてグレースケール化を行う
cv::cvtColor(u_img, u_gray, cv::COLOR_BGR2GRAY);
return 0;
}
●参考URL
2D shape matching
OpenCV 3.0より形状に関する処理を行うためのshapeモジュールが追加されています。このモジュールでは、輪郭線(contour)の形状の距離(距離が短いほど形状が近い)を算出するメソッドShapeDistanceExtractor::computeDistanceが提供されています。このメソッドを用いることで形状のマッチングを行うことができます。
●サンプルコード(2D shape matching)
shapeモジュールのShapeDistanceExtractor::computeDistanceメソッドを用いて形状のマッチングを行うサンプルコードを以下に示します。
#include <opencv2/core.hpp>
#include <opencv2/shape.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>
static std::vector<cv::Point> simpleContour(const cv::Mat& src)
{
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Point> contour_points;
cv::findContours(src, contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
size_t contours_size = contours.size();
for (size_t border = 0; border < contours_size; border++)
{
size_t contour_size = contours[border].size();
for (size_t p = 0; p < contour_size; p++)
{
contour_points.push_back(contours[border][p]);
}
}
return contour_points;
}
int main(int argc, char** argv)
{
if (argc < 3)
{
std::cerr << "Usage: " << argv[0] << " <filename1> <filename2>" << std::endl;
return -1;
}
// (1)画像データをファイルから読み込む
cv::Mat img1 = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
cv::Mat img2 = cv::imread(argv[2], cv::IMREAD_GRAYSCALE);
// 画像の読み込みに失敗したらエラー終了する
if (img1.empty() || img2.empty())
{
std::cerr << "Failed to open image file." << std::endl;
return -1;
}
// (2)画像データから輪郭線(contour)データを生成する
std::vector<cv::Point> contour1 = simpleContour(img1);
std::vector<cv::Point> contour2 = simpleContour(img2);
// (3)輪郭線同士の距離を算出する
cv::Ptr<cv::ShapeContextDistanceExtractor> mysc =
cv::createShapeContextDistanceExtractor();
float distance = mysc->computeDistance(contour1, contour2);
std::cout << "Distance between " << argv[1] << " and " <<
argv[2] << " is: " << distance << std::endl;
return 0;
}
●実行結果
前述のサンプルコードの実行結果は以下の通りです。形状が近いものについてdistanceの値が小さくなっていることが分かります。
KAZE/AKAZE特徴量
OpenCV 3.0よりfeatures2dモジュールにKAZE特徴量、AKAZE特徴量の実装が追加されました。KAZE特徴量、AKAZE特徴量については作者のホームページ(英語)を参照してください。
●サンプルコード(AKAZE特徴量)
AKAZE特徴量を用いたサンプルコードを以下に示します。
#include <opencv2/core.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>
#include <vector>
int main(int argc, const char** argv)
{
// (1)画像データをファイル(この例では「lena.jpg」)から読み込む
cv::Mat img = cv::imread("lena.jpg", cv::IMREAD_COLOR);
// 画像の読み込みに失敗したらエラー終了する
if (img.empty())
{
std::cerr << "Failed to open image file." << std::endl;
return -1;
}
cv::Mat dst, descriptors;
std::vector<cv::KeyPoint> keyPoints;
// (2)検出器(AKAZE)を初期化する
cv::Ptr<cv::AKAZE> detector = cv::AKAZE::create();
// (3)キーポイント検出およびディスクリプター計算を行う
detector->detectAndCompute(img, cv::noArray(), keyPoints, descriptors);
// (4)キーポイントを描画する
cv::drawKeypoints(img, keyPoints, dst,
cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
return 0;
}
●実行結果(AKAZE特徴量)
●参考URL
Copyright© Digital Advantage Corp. All Rights Reserved.