変換行列を元に、画像を変形する
- void warpPerspective(InputArray before, OutputArray after, InputArray matrix, Size dsize, int flags, int borderMode, const Scalar& borderValue);
- void warpAffine(InputArray before, OutputArray after, InputArray matrix, Size dsize, int flags, int borderMode, const Scalar& borderValue);
- 解説
- サンプルコード
- 実体ファイル
void warpPerspective(InputArray before, OutputArray after, InputArray matrix, Size dsize, int flags, int borderMode, const Scalar& borderValue);†
- beforeをMatrix(ホモグラフィ)に基づいて変換
- before:Mat型の画像。座標変換前の画像
- after:Mat型の画像。座標変換後の画像
- matrix:Mat型の変換行列(ホモグラフィ)
- dsize:Size型の出力画像のサイズ
- flag:int型で変換方法のフラグ。詳細は後述。省略した場合はINTER_LINEAR
- borderMode:int型の、境界の対処方法。省略した場合はBORDER_CONSTANT
- borderValue:Scalar型の値。WARP_FILL_OUTLIERSを指定した場合、この色で空いた部分を塗りつぶす
返り値†
void warpAffine(InputArray before, OutputArray after, InputArray matrix, Size dsize, int flags, int borderMode, const Scalar& borderValue);†
- beforeをMatrix(アフィン行列)に基いて変換
- 基本的にwarpPerspectiveと同じ
- before:Mat型の画像。座標変換前の画像
- after:Mat型の画像。座標変換後の画像
- matrix:Mat型の変換行列。2x3(2 rows , 3 columns)の変換行列
- dsize:Size型の出力画像のサイズ
- flag:int型で変換方法のフラグ。詳細は後述。省略した場合はINTER_LINEAR
- borderMode:int型の、境界の対処方法。省略した場合はBORDER_CONSTANT
- borderValue:Scalar型の値。WARP_FILL_OUTLIERSを指定した場合、この色で空いた部分を塗りつぶす
返り値†
- flag、borderMode、borderValueの引数については下記の通り
引数flagについて†
- flagのデフォルト値はINTER_LINEAR。取れる値は以下の通り
- INTER_NEAREST:最近傍補間(0)
- INTER_LINEAR:線型補間(1)
- INTER_CUBIC:バイキュービック補間(2)
- INTER_LANCZOS4:Lanczos補間(4)
- WARP_INVERSE_MAP:逆変換による補間(16)
- INTER_AREAを指定すると、INTER_LINEARに置き換えられる。
- flagsは基本的に補間方法を指定する
- WARP_INVERSE_MAPは、ホモグラフィ、もしくはアフィン変換を逆方向に行う場合に使う
- WARP_FILL_OUTLIERSは、C時代の名残。指定されても、内部では参照されていないっぽい。
- 代わりに、borderModeのBORDER_CONSTANTを使え、ということであろう。
- ちなみに両関数ともCインタフェースはC++インタフェースのラッパー
引数borderModeについて†
- borderModeのデフォルト値はBORDER_CONSTANT。取れる値は以下の通り
- BORDER_CONSTANT:指定した値(borderValue)で塗りつぶす(0)
- BORDER_REPLICATE:縁の画素値を、繰り返し並べる(1)
- BORDER_REFLECT:同じ画像を、反転させながら並べる(2)
- BORDER_WRAP:同じ画像を繰り返し並べる(3)
- BORDER_REFLECT_101:同じ画像を反転させながら並べる(4)
- BORDER_TRANSPARENT:画像の外側には触れない(5)
- 画像を変換させるので、もともとの画像の外側、縁をどう扱うか、がborderModeの意味するところ
- 各borderModeで生成されうる画像についてはサンプルコードの実行結果を参照のこと
- BORDER_REFLECTとBORDER_REFLECT_101は非常に似た挙動を示すが、微妙に違う
- BORDER_REFLECTは、縁の画素が繰り返されるため、2度現れる
- BORDER_REFLECT_101は、縁の画素は繰り返されない
- サンプルコードで、この挙動の違いが見えるので、参照のこと
- 以下2つの定義も存在するが、エイリアスである
- BORDER_REFLECT101とBORDER_DEFAULT、BORDER_REFLECT_101のエイリアス
- ちなみに省略した場合はBORDER_CONSTANTであり、BORDER_DEFAULTでは無いので注意
- このenum定数群を使うのはwarpPerspectiveやwarpAffine以外にもたくさんあり、主にフィルタ処理する関数では、BORDER_DEFAULTが省略した場合のデフォルト値として利用される。
引数borderValueについて†
- borderValueのデフォルトは黒(Scalar())
変換方法について†
- ホモグラフィを用いて変換する場合はwarpPerspectiveを、アフィン行列を用いて変換する場合はwarpAffineを使用する
- warpPerspectiveは以下の数式に基づいて変換を行う
- &mimetex(\left(\begin{array}{c} x^{\prime} \\ y^{\prime} \\ 1 \end{array}\right) = s\left(\begin{array}{ccc} M_{11} & M_{12} & M_{13} \\ M _{21} & M _{22} & M _{23} \\ M _{31} & M _{32} & 1 \end{array}\right)\left(\begin{array}{c} x \\ y \\ 1 \end{array}\right));
- 書き換えると、ヘッダ/ドキュメントにある様な、下記の式になる
- &mimetex(\left(\begin{array}{c} x^{\prime} \\ y^{\prime} \end{array}\right) = \left(\begin{array}{c} \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + 1} \\ \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + 1} \end{array} \right));
- warpAffineは以下の数式に基づいて変換を行う
- &mimetex(\left(\begin{array}{c} x^{\prime} \\ y^{\prime} \end{array}\right) = \left(\begin{array}{ccc} M _{11} & M _{12} & M _{13} \\ M _{21} & M _{22} & M _{23} \end{array}\right)\left(\begin{array}{c} x \\ y \end{array}\right));
- 書き換えると、ヘッダ/ドキュメントにある様な、下記の式になる
- &mimetex(\left(\begin{array}{c} x^{\prime} \\ y^{\prime} \end{array}\right) = \left(\begin{array}{c} M _{11} x + M _{12} y + M _{13} \\ M _{21} x + M _{22} y + M _{23}\end{array}\right));
サンプルコード†
#geshi(c++){{
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
const char filename[] = "/path/to/opencv/samples/data/lena.jpg";
const char windowName[] = "output";
const float srcPoints[] = { 0.0, 0.0,1.0, 512.0, 0.0,1.0, 512.0,512.0,1.0, 0.0,512.0,1.0};
const float dstPoints[] = {218.0,110.0,1.0, 451.0,202.0,1.0, 328.0,395.0,1.0, 112.0,313.0,1.0};
const float dstPoints2[] = {502.0,-10.0,1.0, 522.0,-10.0,1.0, 522.0, 10.0,1.0, 502.0, 10.0,1.0};
const unsigned int cPoints = 4;
void warpAffineAndShow(cv::Mat& before, cv::Mat& after, cv::Mat& matrix, cv::Size& dstSize, int flag, int borderMode = cv::BORDER_CONSTANT, int fillval = 0)
{
using namespace cv;
// warp the image
warpAffine(before, after, matrix, dstSize, flag, borderMode, fillval);
imshow(windowName, after);
waitKey(0);
}
void warpAndShow(cv::Mat& before, cv::Mat& after, cv::Mat& matrix, cv::Size& dstSize, int flag, int borderMode = cv::BORDER_CONSTANT, int fillval = 0)
{
using namespace cv;
// warp the image
warpPerspective(before, after, matrix, dstSize, flag, borderMode, fillval);
imshow(windowName, after);
waitKey(0);
}
int main(int argc, char **argv)
{
using namespace cv;
Mat lena = imread(filename, IMREAD_GRAYSCALE);
// prepare the corresponding points
Mat src = Mat(cPoints, 3, CV_32FC1, (void*)srcPoints);
Mat dst = Mat(cPoints, 3, CV_32FC1, (void*)dstPoints);
// find the homography
Mat homography = findHomography(src, dst);
// prepare the result image
Mat warpImage;
Size dstSize = lena.size();
// make a window
namedWindow(windowName);
// warp the image using homography
warpPerspective(lena, warpImage, homography, dstSize);
// show the result
imshow(windowName, warpImage);
// wait
waitKey(0);
// cut out 3 points from corresponding points
// Affine matrix only acceps 3 x 2 matrix
Mat src1 = src.colRange(0,2).rowRange(0,3).clone();
Mat dst1 = dst.colRange(0,2).rowRange(0,3).clone();
// get affine matrix
Mat affine = getAffineTransform(src1, dst1);
// warp the image using affine matrix
warpAffineAndShow(lena, warpImage, affine, dstSize, INTER_LINEAR);
warpAffineAndShow(lena, warpImage, affine, dstSize, INTER_LINEAR, BORDER_REPLICATE);
// try various border types
warpAndShow(lena, warpImage, homography, dstSize, INTER_LINEAR);
warpAndShow(lena, warpImage, homography, dstSize, INTER_LINEAR, BORDER_REPLICATE);
warpAndShow(lena, warpImage, homography, dstSize, INTER_LINEAR, BORDER_WRAP);
warpAndShow(lena, warpImage, homography, dstSize, INTER_LINEAR, BORDER_REFLECT);
warpAndShow(lena, warpImage, homography, dstSize, INTER_LINEAR, BORDER_REFLECT_101);
warpAndShow(lena.clone(), lena, homography, dstSize, INTER_LINEAR, BORDER_TRANSPARENT);
// find the homography
dst = Mat(cPoints, 3, CV_32FC1, (void*)dstPoints2);
homography = findHomography(src, dst);
// try various interpolation method
warpAndShow(lena, warpImage, homography, dstSize, INTER_NEAREST +WARP_INVERSE_MAP);
warpAndShow(lena, warpImage, homography, dstSize, INTER_LINEAR +WARP_INVERSE_MAP);
warpAndShow(lena, warpImage, homography, dstSize, INTER_CUBIC +WARP_INVERSE_MAP);
warpAndShow(lena, warpImage, homography, dstSize, INTER_LANCZOS4+WARP_INVERSE_MAP);
warpAndShow(lena, warpImage, homography, dstSize, INTER_AREA +WARP_INVERSE_MAP);
// check difference of BORDER_REFLECT and BORDER_REFLECT_101
warpAndShow(lena, warpImage, homography, dstSize, INTER_NEAREST+WARP_INVERSE_MAP, BORDER_REFLECT);
warpAndShow(lena, warpImage, homography, dstSize, INTER_NEAREST+WARP_INVERSE_MAP, BORDER_REFLECT_101);
destroyAllWindows();
return 0;
}
}}
実行結果†
実体ファイル†
OpenCV 2.4系列†
- warpPerspective、warpAffine
- modules/imgproc/include/opencv2/imgproc/iimgproc.hpp
- modules/imgproc/src/imgwarp.cpp
OpenCV 3.0系列†
- warpPerspective、warpAffine
- modules/imgproc/include/opencv2/imgproc/iimgproc.hpp
- modules/imgproc/src/imgwarp.cpp
ジャンル:OpenCV:OpenCV 2.4:OpenCV 3.0:OpenCV 3.1準拠