画像の幾何学変換

このサブセクションで述べる関数は,2次元画像の様々な幾何学変換を行います.つまり,画像の内容は変更せずにピクセルグリッドだけを変形し,変形したグリッドを出力画像にマッピングします.実際には,サンプリングによる余計な値や不定な値を排除するために,出力画像から入力画像という逆方向へのマッピングが行われます.つまり,出力画像の各ピクセル (x, y) に対して,入力画像中の対応する「ドナー」ピクセルの座標を求め,そのピクセル値をコピーします:

\texttt{dst} (x,y)= \texttt{src} (f_x(x,y), f_y(x,y))

ユーザが正順のマッピング: \left<g_x, g_y\right>: \texttt{src} \rightarrow \texttt{dst} を指定した場合,OpenCV の関数は最初に,対応する逆マッピング: \left<f_x, f_y\right>: \texttt{dst} \rightarrow \texttt{src} を求めてから上述の式を利用します.

幾何学変換の実際の実装では,最も汎用的な remap() から,最も単純で高速な resize() まで,上述の式を用いて2つの主な問題を解く必要があります:

  1. 存在しないピクセルの外挿. と同様に,ある (x,y) に対して, f_x(x,y) または f_y(x,y) のどちらか,あるいは両方が画像の外側にはみ出してしまう可能性があります.この場合,何らかの外挿手法が必要になります.OpenCVでは,フィルタリング関数の場合と同じく,いくつかの外挿手法が提供されています.さらに別の手法も追加されており,その BORDER_TRANSPARENT は,出力画像中の対応するピクセルが全く変更されないことを意味します.
  2. ピクセル値の内挿.通常 f_x(x,y)f_y(x,y) は,浮動小数点数(つまり, \left<f_x, f_y\right> は,アフィン変換,透視変換,または半径方向のレンズ歪み係数など)です.よって,小数点座標上に存在するピクセル値が必要になります.最も単純なケースでは,この座標は単に最も近い整数値の座標に丸められ,そこのピクセル値が利用されます.これは,最近傍補間と呼ばれます.しかし,より洗練された 補間手法 を用いることで,より良い結果が得られます.この場合,求められたピクセル (f_x(x,y), f_y(x,y)) 近傍に対して多項式関数をフィッティングし, (f_x(x,y), f_y(x,y)) における多項式の値を,補間されたピクセル値として利用します.OpenCVでは,複数の補間手法から選択することができます.詳しくは resize() を参照してください.

cv::convertMaps

void convertMaps(const Mat& map1, const Mat& map2, Mat& dstmap1, Mat& dstmap2, int dstmap1type, bool nninterpolation=false)

画像変換マップを,ある表現から別の表現へ変換します.

パラメタ:
  • map1 – 1番目の入力マップ.型は CV_16SC2 , CV_32FC1 , CV_32FC2 のいずれか
  • map2 – 2番目の入力マップ.型は CV_16SC1 , CV_32FC1 , あるいはそれぞれに対する none (空の行列) のいずれか
  • dstmap1 – 1番目の出力マップ. dstmap1type で指定される型, src と同じサイズ
  • dstmap2 – 2番目の出力マップ
  • dstmap1type – 1番目の出力マップの型. CV_16SC2 , CV_32FC1 あるいは CV_32FC2 のいずれか
  • nninterpolation – 最近傍補間,または,より複雑な補間手法に対して,浮動小数点型のマップが利用されるか否かを指定します

この関数は, remap() 用のマップの組を,ある表現から別の表現へ変換します.以下のオプション( (map1.type(), map2.type()) \rightarrow (dstmap1.type(), dstmap2.type()) )がサポートされます:

  1. \texttt{(CV\_32FC1, CV\_32FC1)} \rightarrow \texttt{(CV\_16SC2, CV\_16UC1)} . これは,最も頻繁に利用される変換処理です.元の浮動小数点型マップ( remap() を参照してください)は,よりコンパクトで非常に高速な浮動小数点型の表現に変換されます.1番目の出力配列は丸められた座標値を表し,2番目の出力配列(ただし, nninterpolation=false の場合のみ作られます)は,補間テーブルのインデックスを表します.
  2. \texttt{(CV\_32FC2)} \rightarrow \texttt{(CV\_16SC2, CV\_16UC1)} . 上述の場合と同じですが,元のマップは1つの2チャンネル行列に格納されます.
  3. 逆変換.当然ながら,再構成された浮動小数点型マップは元のマップとは正確には一致しません.

参考: remap() , undisort() , initUndistortRectifyMap()

cv::getAffineTransform

Mat getAffineTransform(const Point2f src[], const Point2f dst[])

3組の対応点からアフィン変換を求めます.

パラメタ:
  • src – 入力画像上の三角形の頂点の座標
  • dst – 出力画像上の対応する三角形の頂点の座標

この関数は,アフィン変換を表す 2 \times 3 の行列を求めます:

\begin{bmatrix} x'_i \\ y'_i \end{bmatrix} = \texttt{map\_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}

ここで

dst(i)=(x'_i,y'_i),
src(i)=(x_i, y_i),
i=0,1,2

となります.

参考: warpAffine() , transform()

cv::getPerspectiveTransform

Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[])

4組の対応点から透視変換を求めます.

パラメタ:
  • src – 入力画像上の四角形の頂点の座標
  • dst – 出力画像上の対応する四角形の頂点の座標

この関数は,透視変換を表す 3 \times 3 の行列を求めます:

\begin{bmatrix} t_i x'_i \\ t_i y'_i \\ t_i \end{bmatrix} = \texttt{map\_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}

ここで

dst(i)=(x'_i,y'_i),
src(i)=(x_i, y_i),
i=0,1,2

となります

参考: findHomography() , warpPerspective() , perspectiveTransform()

cv::getRectSubPix

void getRectSubPix(const Mat& image, Size patchSize, Point2f center, Mat& dst, int patchType=-1)

画像から,矩形領域のピクセル値をサブピクセル精度で取得します.

パラメタ:
  • src – 入力画像
  • patchSize – 抽出される部分のサイズ
  • center – 入力画像から抽出される矩形の中心を表す,浮動小数点型の座標.この中心は,画像内に存在しなければいけません
  • dst – 抽出される部分.サイズは patchSize で, src と同じチャンネル数になります
  • patchType – 抽出されるピクセルのビット深度.デフォルトでは, src のビット深度と同じになります

関数 getRectSubPix は, src からピクセルを抽出します:

dst(x, y) = src(x +  \texttt{center.x} - ( \texttt{dst.cols} -1)*0.5, y +  \texttt{center.y} - ( \texttt{dst.rows} -1)*0.5)

ここで,非整数の座標におけるピクセル値は,バイリニア補間を用いて取得されます.また,マルチチャンネル画像の各チャンネルは,それぞれ個別に処理されます.矩形領域の中心は,必ず画像内部になければいけませんが,矩形領域の一部が画像外部にはみ出していても構いません.その場合は,画像外にある領域のピクセル値を取得するために,複製境界モード( borderInterpolate() を参照してください)が利用されます.

参考: warpAffine() , warpPerspective()

cv::getRotationMatrix2D

Mat getRotationMatrix2D(Point2f center, double angle, double scale)

2次元回転を表すアフィン変換を求めます.

パラメタ:
  • center – 入力画像中にある回転中心
  • angle – 度単位で表される回転角度.正の値は反時計回りを意味します(座標原点が,左上コーナーにあると仮定されます)
  • scale – 等方性のスケールファクタ

この関数は,以下の行列を求める:

\begin{bmatrix} \alpha &  \beta & (1- \alpha )  \cdot \texttt{center.x} -  \beta \cdot \texttt{center.y} \\ - \beta &  \alpha &  \beta \cdot \texttt{center.x} - (1- \alpha )  \cdot \texttt{center.y} \end{bmatrix}

ここで

\begin{array}{l} \alpha =  \texttt{scale} \cdot \cos \texttt{angle} , \\ \beta =  \texttt{scale} \cdot \sin \texttt{angle} \end{array}

となります.

この変換は,回転中心をそれ自身にマップします.そうしたくない場合は,シフトによる調整を行う必要があります.

参考: getAffineTransform() , warpAffine() , transform()

cv::invertAffineTransform

void invertAffineTransform(const Mat& M, Mat& iM)

アフィン変換の逆変換を求めます.

パラメタ:
  • M – 元のアフィン変換
  • iM – 出力される逆アフィン変換

この関数は, 2 \times 3 の行列 M で表されたアフィン変換の逆変換を求めます:

\begin{bmatrix} a_{11} & a_{12} & b_1  \\ a_{21} & a_{22} & b_2 \end{bmatrix}

その結果は, M と同じ型の 2 \times 3 の行列になります.

cv::remap

void remap(const Mat& src, Mat& dst, const Mat& map1, const Mat& map2, int interpolation, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

画像に対して,汎用的な幾何学変換を適用します.

パラメタ:
  • src – 入力画像
  • dst – 出力画像. map1 と同じサイズ, src と同じタイプ
  • map1 – 型 CV_16SC2CV_32FC1 あるいは CV_32FC2 である座標点 (x,y) ,または単なる値 x の1番目のマップ.高速化のために浮動小数点表現から固定小数点表現に変換する方法については, convertMaps() を参照してください
  • map2 – 型 CV_16UC1CV_32FC1 あるいは none(map1 が (x,y) である場合は空のマップ) である値 y の2番目のマップ
  • interpolation – 補間手法. resize() を参照してください. INTER_AREA メソッドは,この関数ではサポートされません
  • borderMode – ピクセル外挿手法. borderInterpolate() を参照してください. borderMode= BORDER_TRANSPARENT の場合,入力画像中の「はずれ値」に対応する出力画像中のピクセルが,この関数によって変更されないことを意味します
  • borderValue – 定数境界モードで利用されるピクセル値.デフォルトでは 0

関数 remap は,指定されたマップを用いて入力画像を変換します:

\texttt{dst} (x,y) =  \texttt{src} (map_x(x,y),map_y(x,y))

非整数の座標値を持つピクセルの値は,有効な補間手法の1つを用いて求められます. map_xmap_y は,それぞれ個別の浮動小数点型マップ map_1map_2 としてエンコードされるか,または (x,y) のインタリーブされた1つの浮動小数点型マップ map_1 ,あるいは convertMaps() を利用して作成された固定小数点型マップとしてエンコードされます.マップが浮動小数点表現から固定小数点表現に変換されるとすれば,その理由は幾何学変換処理が大幅に高速化される(約2倍)からです.変換された場合, map_1(cvFloor(x), cvFloor(y)) というペアを表し, map_2 は補間係数テーブルのインデックスを表します.

この関数は,置換モードでは動作しません.

cv::resize

void resize(const Mat& src, Mat& dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)

画像のサイズを変更します.

パラメタ:
  • src – 入力画像
  • dst – 出力画像.サイズは dsize (0でない場合)か,または src.size() , fx , fy から計算される値になります. dst の型は, src と同じになります
  • dsize

    出力画像サイズ.これが0の場合,次のように計算されます:

    \texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}

    . 必ず dsize が非0,あるいは fxfy の両方が非0,でなければいけません

  • fx

    水平軸方向のスケールファクタ.これが0の場合,次のように計算されます:

    \texttt{(double)dsize.width/src.cols}

  • fy

    垂直軸方向のスケールファクタ.これが0の場合,次のように計算されます:

    \texttt{(double)dsize.height/src.rows}

  • interpolation

    補間手法:

    • INTER_NEAREST 最近傍補間
    • INTER_LINEAR バイリニア補間(デフォルト)
    • INTER_AREA ピクセル領域の関係を利用したリサンプリング.画像を大幅に縮小する場合は,モアレを避けることができる良い手法です.しかし,画像を拡大する場合は, INTER_NEAREST メソッドと同様になります
    • INTER_CUBIC 4x4 の近傍領域を利用するバイキュービック補間
    • INTER_LANCZOS4 8x8 の近傍領域を利用する Lanczos法の補間

関数 resize は,画像 src を指定されたサイズに縮小,あるいは拡大します.

dst の型やサイズは考慮されないことに注意してください.その代わり,型やサイズは src , dsize , fx そして fy から求められます.あらかじめ用意しておいた dst とぴったり同じになるように src のサイズを変更したい場合,次のような関数呼び出しができます:

// dsize=dst.size(); を明示的に指定します. fx と fy はここから計算されます.
resize(src, dst, dst.size(), 0, 0, interpolation);

各方向に二分の一に縮小したい場合は,次のような関数呼び出しができます:

// fx とfy を定して,関数に出力画像サイズを計算させます.
resize(src, dst, Size(), 0.5, 0.5, interpolation);

参考: warpAffine() , warpPerspective() , remap() .

cv::warpAffine

void warpAffine(const Mat& src, Mat& dst, const Mat& M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

画像のアフィン変換を行います.

パラメタ:
  • src – 入力画像
  • dst – サイズが dsizesrc と同じタイプの出力画像
  • M2\times 3 の変換行列
  • dsize – 出力画像のサイズ
  • flags – 補間手法( resize() を参照してください)と, M が逆変換( dst \rightarrow src )であることを意味するオプションフラグ WARP_INVERSE_MAP の組み合わせ
  • borderMode – ピクセル外挿手法. borderInterpolate() を参照してください. borderMode = BORDER_TRANSPARENT の場合,入力画像中の「はずれ値」に対応する出力画像中のピクセルが,この関数では変更されないことを意味します
  • borderValue – 定数境界モードで利用されるピクセル値.デフォルトでは 0 です

関数 warpAffine は,指定された行列を用いて入力画像を変換します:

\texttt{dst} (x,y) =  \texttt{src} ( \texttt{M} _{11} x +  \texttt{M} _{12} y +  \texttt{M} _{13},  \texttt{M} _{21} x +  \texttt{M} _{22} y +  \texttt{M} _{23})

ここでは,フラグ WARP_INVERSE_MAP が設定されています.そうでない場合は,まず invertAffineTransform() によって逆変換が求められ,それが上式の M の代わりに利用されます.

この関数は,置換モードでは動作しません.

参考: warpPerspective() , resize() , remap() , getRectSubPix() , transform()

cv::warpPerspective

void warpPerspective(const Mat& src, Mat& dst, const Mat& M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

画像の透視変換を行います.

パラメタ:
  • src – 入力画像
  • dst – サイズが dsizesrc と同じタイプの出力画像
  • M2\times 3 の変換行列
  • dsize – 出力画像のサイズ
  • flags – 補間手法( resize() を参照)と, M が逆変換( dst \rightarrow src )であることを意味するオプションフラグ WARP_INVERSE_MAP の組み合わせ
  • borderMode – ピクセル外挿手法. borderInterpolate() を参照してください. borderMode=BORDER_TRANSPARENT の場合,入力画像中の「はずれ値」に対応する出力画像中のピクセルが,この関数では変更されないことを意味します
  • borderValue – 定数境界モードで利用されるピクセル値.デフォルトでは 0 です

関数 warpPerspective は,指定された行列を用いて入力画像を変換します:

\texttt{dst} (x,y) =  \texttt{src} \left ( \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} ,
     \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} \right )

ここでは,フラグ WARP_INVERSE_MAP が設定されています.そうでない場合は,まず invert() によって逆変換が求められ,それが上式の M の代わりに利用されます.

この関数は,置換モードでは動作しない.

参考: warpAffine() , resize() , remap() , getRectSubPix() , perspectiveTransform()