記事ディレクトリ
I. 概要
画像のズームや反転を紹介したら、次は画像の回転を紹介しますが、OpenCV
4では画像の回転専用の機能はなく、画像のアフィン変換によって画像の回転を実現しています。画像の回転を実現するには、まず回転角度と回転中心を決定し、次に回転行列を決定し、最後にアフィン変換によって画像の回転を実現します。
二、GetRotationMatrix2D
このプロセスのために、OpenCV4 は回転行列を計算する getRotationMatrix2D() 関数と、画像のアフィン変換を実装する warpAffine() 関数を提供します。まず、回転行列を計算する関数 getRotationMatrix2D() を紹介しますが、この関数の関数プロトタイプをコードリスト 3-31 に示します。
Mat cv::getRotationMatrix2D (Point2f center,double angle,double scale)
- center: 画像回転の中心位置。
- angle: 画像が回転される角度 (度単位)。正の値は反時計回りに回転します。
- スケール: 回転プロセス中に画像のスケーリングを実現できる 2 つの軸のスケール係数。スケーリングしない場合は 1 を入力します。
3. warpAffine()
画像の回転を実現するためにアフィン変換を行う関数であり、warpAffine()関数の関数プロトタイプをコードリスト3-32に示します。
void cv::warpAffine(InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar()
)
- src: 入力画像。
- dst: アフィン変換後の出力画像。データ型は src と同じですが、サイズは dsize と同じです。
- M: 2×3 変換行列。
- dsize: 出力画像のサイズ。
- flags: 補間方式のフラグ、オプションのパラメータ、およびそれらの意味を表 3-3 および表 3-4 に示します。
- borderMode: ピクセル境界外挿方法のフラグ。
- borderValue: 境界線を埋めるために使用される値。デフォルトでは 0 です。
この関数にはいくつかのパラメータがありますが、それらのほとんどは上記で説明した画像のサイズ変更と同じ意味を持ちます。関数の 3 番目のパラメータは以前に取得した画像回転行列で、4 番目のパラメータは出力画像のサイズです。関数の第5引数はアフィン変換補間方式のフラグであり、画像サイズ変換と比較して、他の補間方式と併用できる2種類が追加されており、表3-4に示します。関数の 6 番目のパラメータはピクセル境界外挿方法のフラグであり、可能なフラグと対応する方法を表 3-5 に示します。第 7 パラメータは、外挿フラグが BORDER_CONSTANT を選択した場合の固定値で、デフォルトでは 0 です。
関数の各パラメータの意味を理解した後、関数の機能をより深く理解するために、アフィン変換の概念を導入する必要があります。アフィン変換とは画像の回転、平行移動、拡大縮小操作の総称であり、線形変換と平行移動変換の重ね合わせで表現できます。アフィン変換の数学的表現は、線形変換行列と平行移動ベクトルを乗算することであり、線形変換行列は 2×2 行列、平行移動ベクトルは 2×1 ベクトルです。関数に 2×3 変換行列を入力する必要がある理由を理解します。線形変換行列と変換行列があるとします。この 2 つと入力行列の関係は式 (3.13) に示されます。
回転行列、平行移動行列、および画像ピクセル値に従って、アフィン変換の数学的原理は式 (3.14) で表すことができます。
- src[]: 元の画像内の 3 つのピクセル座標。
- dst[]: 宛先イメージ内の 3 つのピクセル座標。
この関数の 2 つの入力は浮動小数点座標を格納する配列です。配列を生成するとき、ピクセルの入力順序は関係ありませんが、ピクセル間の対応関係は保証される必要があります。関数の戻り値は 2×3 変換ですマトリックス。
先ほどの変換行列の計算で、warpAffine()関数を使用して行列のアフィン変換を実現することができ、ルーチン内で画像の回転と画像の3点マッピングのアフィン変換を実現しました。コードリスト 3-34 の結果を図 3-23 に示します。
#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"
using namespace std;
using namespace cv;
int main(int argc,char** argv) {
cout<<"OpenCv Version: "<<CV_VERSION<<endl;
Mat img=imread("699342568.jpg");
if(img.empty()){
cout<<"请确认输入的图像;路径是否正确"<<endl;
return -1;
}
Mat img_;
resize(img,img_,Size(img.rows/2,img.cols/2));
imshow("src",img_);
Mat rotation0,rotation1,img_warp0,img_warp1;
double angle=30;//设置图像旋转的角度
Size dst_size(img_.rows,img_.cols);//设置输出图像的尺寸
Point2f center(img_.rows/2.0,img_.cols/2.0);//设置图像的旋转中心
rotation0=getRotationMatrix2D(center,angle,1);//计算放射变换矩阵
warpAffine(img_,img_warp0,rotation0,dst_size);//进行仿射变换
imshow("img_warp0",img_warp0);
//根据定义的三个点进行仿射变换
Point2f src_points[3];
Point2f dst_points[3];
src_points[0]=Point2f(0,0);//原始图像的三个点
src_points[1]=Point2f(0,(float)(img_.cols-1));
src_points[2]=Point2f((float)(img_.rows-1),(float)(img_.cols-1));
//仿射变换后图像中的三个点
dst_points[0]=Point2f((float)(img_.rows)*0.11,(float)(img_.cols)*0.20);
dst_points[1]=Point2f((float)(img_.rows)*0.15,(float)(img_.cols)*0.70);
dst_points[0]=Point2f((float)(img_.rows)*0.81,(float)(img_.cols)*0.85);
rotation1=getAffineTransform(src_points,dst_points);//根据对应点求取放射变换矩阵
warpAffine(img_,img_warp1,rotation1,dst_size);//进行放射变换
imshow("img_warp1",img_warp1);
waitKey(0);
return 0;
}