1. ホーム
  2. OpenCV

[Learn opencv3] 11章を読む

2022-03-17 23:18:44
<パス

記事目次

一般的な画像変換

アフィン変換

アフィン変換には2つのケースがあります。



(a) 一つ目は、変形したい画像(または関心領域)があり、例えば、パン、ズーム、回転、反転、千鳥の5つの変形を組み合わせて変形する場合。

https://blog.csdn.net/u010159842/article/details/54408739

2つ目では、変換結果を計算するために考えるべきポイントが並んでいます。



これらのケースは、コンセプトは似ていますが、実際の実装は大きく異なります。これらのケースに対して、opencvは2つの異なる機能を持っています。

<ブロッククオート

こちらはよくカバーされています



https://www.zhihu.com/question/20666664

密なアフィン変換関数 HashMap<Integer, Character> map=new HashMap<Integer,Character>();

HashMap<Integer, char> map=new HashMap<Integer,char>();

のアフィン写像行列を計算する。 char ch='a'; Character ch1=ch;//auto-seal Character c=new Character(a); char c1=c;//auto unseal

 int t=10;
Integer t1=t;//auto-seal

Integer t=new Integer(10);
int t1=t//auto unseal

マッピングマトリックスを計算するもう一つの方法 cv::getRotationMatrix2D()

cv::Mat cv::getRotationMatrix2D( // Return 2-by-3 matrix
	cv::Point2f center // Center of rotation
	double angle, // Angle of rotation
	double scale // Rescale after rotation
);


cv::transform() 疎なアフィン変換の場合

cv::warpAffine() は、密なマッピングを処理する正しい方法です。疎なマッピング(つまり、個々の点のリストのマッピング)の場合は、以下のようにするのがよいでしょう。 cv::transform()

void cv::transform(
	cv::InputArray src, // Input N-by-1 array (Ds channels) N*1 matrix of Ds channels
	cv::OutputArray dst, // Output N-by-1 array (Dd channels) N*1 matrix of Dd channels
	cv::InputArray mtx // Transform matrix (Ds-by-Dd) Transform matrix is a Ds * Dd matrix
);


cv::transform() 配列の各点に作用するチャンネルインデックス.この問題では,配列は基本的にこのマルチチャンネル・オブジェクトの大きなベクトルであると仮定します(N 1 または 1 N). ここで重要なことは,変換行列は,大きな配列の "ベクトル" のインデックスではなく,チャンネルのインデックスに対する相対値であるということです.

cv::invertAffineTransform() 逆アフィン変換の場合

2*3行列のアフィン変換が与えられると、通常、逆変換を計算することができます。これは、変換されたすべての点を元の位置に戻すために使用することができます。

void cv::invertAffineTransform(
	cv::InputArray M, // Input 2-by-3 matrix
	cv::OutputArray iM // Output also a 2-by-3 matrix
);


パースペクティブの変換

透視変換はすべてのアフィン変換を含む

まず、透視投影が完全に1つの行列で指定されている場合でも、変換を最終次元(通常はZ)で割る必要があるため、実際には1つの次元が失われ、透視投影は1次変換ではないことに注意しましょう。

cv::warpPerspective() 高密度な透視変換の場合

密な透視変換は,密なアフィン変換のために提供されるものと同様のOpenCV関数を使用します.違いは,小さいながらも重要なマッピング行列が 3*3 でなければならないことです.

void cv::warpPerspective(
cv::InputArray src, // Input image
cv::OutputArray dst, // Result image
cv::InputArray M, // 3-by-3 transform mtx 3*3 transformation matrix, mapping matrix
cv::Size dsize, // Destination image size Destination image size
int flags = cv::INTER_LINEAR, // Interpolation, inverse select the interpolation method
int borderMode = cv::BORDER_CONSTANT, // Extrapolation method Extrapolation method
const cv::Scalar& borderValue = cv::Scalar() // For constant borders border value, constant
);



cv::getPerspectiveTransform() パースペクティブマッピングマトリックスの算出に使用

cv::Mat cv::getPerspectiveTransform( // Return 3-by-3 matrix returns a 3*3 matrix
const cv::Point2f* src, // Coordinates of *four* vertices of the source image, the four vertices are in the array
const cv::Point2f* dst // Target coords, four vertices Target image, coordinates of four vertices, four vertices in array
);


引数 src と dst は 4 点の配列になり、src の(典型的な)長方形の一角が dst の(通常)ひし形にどのように写るかを独立に制御することができます。この変換は、4 つの入力点の指定された移動先によって、完全に定義されます。

透視変換の実装例

#include 

#include 


using namespace std;

int main(int argc, char** argv) {
	if (argc ! = 2) {
		cout << "Perspective Warp\nUsage: " << argv[0] << " 
\n"
 << endl;
		return -1;
	}

	cv::Mat src = cv::imread(argv[1], 1);
	if (src.empty()) { cout << "Can not load " << argv[1] << endl; return -1; }

	cv::Point2f srcQuad[] = {
		cv::Point2f(0,0),
		cv::Point2f(src.cols - 1,0),
		cv::Point2f(src.cols - 1,src.rows - 1),
		cv::Point2f(0,src.rows - 1)
	};

	cv::Point2f dstQuad[] = {
		cv::Point2f(src.cols*0.05f,src.rows*0.33f),
		cv::Point2f(src.cols*0.9f,src.rows*0.25f),
		cv::Point2f(src.cols*0.8f,src.rows*0.9f),
		cv::Point2f(src.cols*0.2f,src.rows*0.7f)
	};

	cv::Mat warp_mat = cv::getPerspectiveTransform(srcQuad, dstQuad);
	cv::Mat dst;
	cv::warpPerspective(src, dst, warp_mat, src.size(), cv::INTER_LINEAR,
		cv::BORDER_CONSTANT, cv::Scalar());

	for (int i = 0; i < 4; i++) {
		cv::CIRCLE(dst, dstQuad[i], 5, cv::Scalar(255, 0, 255), -1, cv::LINE_AA);
	}

	double scale = 0.5;
	cv::Size resize = cv::Size(dst.rows*scale, dst.cols*scale);
	cv::Mat input_img = cv::Mat(resize, dst.type());
	cv::resize(dst, input_img,resize);
	cv::imshow("Perspective Transform Test", input_img);
	cv::waitKey();
	return 0;
}


cv::perspectiveTransform() 疎な透視変換の場合

cv::perspectiveTransform() は、点のリストに対して透視変換を行う特殊な関数である。なぜなら cv::transform() は線形演算に限定されているため、透視変換を正しく扱えない。このような変換は、第3座標の同次表現(x = f * x / Z , y = f * y / Z )で分割する必要があるからである。特殊関数 cv::perspectiveTransform() は、このことを私たちのために考慮してくれます。

void cv::perspectiveTransform(
	cv::InputArray src, // Input N-by-1 array (2 or 3 channels) N*1 array (2 or 3 channels)
	cv::OutputArray dst, // Output N-by-1 array (2 or 3 channels) N*1 array (2 or 3 channels)
	cv::InputArray mtx // Transform matrix (3-by-3 or 4-by-4) Transform (mapping) matrix, 3*3 or 4*4. If the matrix is 3*3 then the projection is from 2D to 2D; if the matrix is 4*4 then the projection is from 3D to 3D.
);


src と dst パラメータは,それぞれ変換される元ポイントの配列と,変換によって生成されるターゲットポイントの配列です.これらの配列は,2チャンネルまたは3チャンネルの配列でなければいけません.行列 mat は,3 3または4 4行列です。もし行列が3 3 の場合は,2 次元から 2 次元への投影となり,行列が 4 4であれば、3Dから3Dへの投影となる。

透視変換とは、基本的に3次元空間に埋め込まれた2次元平面上の点を、(異なる)2次元部分空間にマッピングして戻すことである。これは、カメラの撮像の原理と類似していると考えてください。カメラは3次元の点を取り、それをカメラ・イメージャーの両次元にマッピングします。これは本質的に、ソースポイントが"カイ二乗座標"にあるとみなされるときを意味します。Z次元を導入し、すべてのZ値を1に設定することによって、これらの点に次元を追加します。投影変換は、この空間から出力の2次元空間へと戻ります。これはかなり面倒で、ある画像の点を別の画像の点にマッピングする方法を説明するためには、3×3の行列が必要です。

一般的な変換

アフィン変換と透過変換は、より一般的な処理の特殊な例です。基本的に、これら2つの変換は、ピクセルをソース画像のある場所からターゲット画像の別の場所にマッピングするという、類似した性質を持っています。

opencvに独自のマッピング変換を実装させます。

極座標マッピング

cart 直角座標;polar 極座標。



機能一覧 cv::cartToPolar() cv::polarToCart() は、点配列のデカルト座標とポーラー座標の相互変換を実装しています。

極座標マッピング関数、透視変換関数、アフィン変換関数、それぞれの微妙な違い。



極座標マッピング機能では、2次元ベクトルを表現する場合、1次元の行列の連なりしか使用できず、多次元の行列は使用できない。この違いは、2つの関数の機能の本質的な違いではなく、伝統的な使い方に起因するものです。

cv::cartToPolar() 直交座標から極座標への変換用

関数を使用する cv::cartToPolar() 直方体座標を極座標に変換する。

void cv::cartToPolar(
	cv::InputArray x, // Input single channel x-array Input single channel x-array
	cv::InputArray y, // Input single channel y-array
	cv::OutputArray magnitude, // Output single channel mag-array 
	cv::OutputArray angle, // Output single channel angle-array
	bool angleInDegrees = false // Set true for degrees, else radians
);


cv::logPolar() 機能

void cv::logPolar(
	cv::InputArray src, // Input image
	cv::OutputArray dst, // Output image
	cv::Point2f center, // Center of transform log-polar Transformation/transformation of center (x,y)
	double m, // Scale factor scale factor
	int flags = cv::INTER_LINEAR // interpolation and fill modes flag bits, set the interpolation and fill modes
				| cv::WARP_FILL_OUTLIERS
);


cv::INTER_LINEAR (対数極座標系から直角座標系への変換の逆マッピングを求めます).

cv::WARP_FILL_OUTLIERS (未定義の点を塗りつぶす)

対数極性変換の実装例。

#include


#include


using namespace std;

int main(int argc, char** argv) {
	if (argc ! = 3) {
		cout << "LogPolar\nUsage: " << argv[0] << "
\n"

			<< "
~30 is usually good enough\n"
;
		return -1;
	}

	cv::Mat src = cv::imread(argv[1], 1);

	if (src.empty()) { cout << "Can not load " << argv[1] << endl; return -1; }

	double M = atof(argv[2]);
	cv::Mat dst(src.size(), src.type()), src2(src.size(), src.type());

	cv::logPolar(
		src,
		dst,
		cv::Point2f(src.cols*0.5f, src.rows*0.5f),
		M,
		cv::INTER_LINEAR | cv::WARP_FILL_OUTLIERS
	);

	cv::logPolar(
		dst,
		src2,
		cv::Point2f(src.cols*0.5f, src.rows*0.5f),
		M,
		cv::INTER_LINEAR | cv::WARP_INVERSE_MAP
	);



	double scale = 0.5;
	cv::Size resize = cv::Size(dst.rows*scale, dst.cols*scale);
	cv::Mat input_img = cv::Mat(resize, dst.type());

	cv::resize(src, input_img, resize);
	cv::imshow("src img", input_img);

	cv::resize(dst, input_img, resize);
	cv::imshow("log-polar", input_img);

	cv::resize(src2, input_img, resize);
	cv::imshow("imshow log-polar", input_img);
	
	cv::waitKey();

	return 0;
}






任意のマッピング

プログラム的に補間を実装したい場合があります。これは、マッピングを実装できる既知のアルゴリズムを使いたいことを意味する。一方、そのようなマッピングを自分で実装したいと思うこともある。マッピングを計算してくれる(そして適用してくれる)これらのメソッドを見る前に、他のメソッドが依存している、マッピングを実装できる関数について見てみよう。

この関数は cv::remap() 通常、キャリブレーションされたステレオ画像を補正するために使用します。

cv::remap() 通常の画像再描画の場合

void cv::remap(
	cv::InputArray src, // Input image 
	cv::OutputArray dst, // Output image
	cv::InputArray map1, // target x for src pix the x and y positions of any point on the source image that needs to be relocated. This allows you to make your own general mapping.
	cv::InputArray map2, // target y for src pix
	int interpolation = cv::INTER_LINEAR, // Interpolation, inverse interpolation
	int borderMode = cv::BORDER_CONSTANT, // Extrapolation method
	const cv::Scalar& borderValue = cv::Scalar() // For constant borders
);


これらのマップは、ソース画像およびターゲット画像と同じサイズでなければならず、以下のデータ型のいずれかを使用する必要があります。CV::S16C2, CV::F32C1, CV::F32C2 のいずれか(整数以外のデータマップも可能)でなければならず,関数 cv::remap() は自動的に補間計算を行います

画像の復元

画像はノイズで壊れることが多い。レンズに埃や水濡れがあったり、古い画像には傷があったり、画像の一部が破損していることもあります。画像修復は、このようなダメージを、ダメージ部分のエッジで色や質感を拾い、ダメージ部分の内部までブレンドを伝播させることで除去する方法です。

画像修復

機能 cv::inpaint() ソースコード

void cv::inpaint(
	cv::InputArray src, // Input image: 8-bit, 1 or 3 channels 8-bit, 1 or 3 channels image
	cv::InputArray inpaintMask, // 8-bit, 1 channel. inpaint nonzeros 
	cv::OutputArray dst, // Result image
	double inpaintRadius, // Range to consider around pixel
	int flags // Select NS or TELEA to choose the repair method
);


inpaintMask は src と同じサイズの 8 ビット 1 次元画像で,破損箇所は 0 ではない要素でマークされます.

inpaintRadiusは、各レンダリングピクセルの周囲で、そのピクセルの結果としての出力色に分解される領域です。

デノイジング

多くのアプリケーションにおいて、ノイズの主な原因は低照度条件の影響に起因します。低照度下では、デジタルイメージャーのゲインを上げる必要があり、その結果、ノイズが増幅されます。このノイズは、明るすぎたり暗すぎたりするランダムな孤立画素が特徴で、カラー画像では変色することもあります。

opencvに実装されているノイズ除去アルゴリズムは、Fast Non-Local Mean Denoising (FNLMD)と呼ばれるものです。単純なノイズ除去アルゴリズムは基本的に、近傍の個々のピクセルの平均化と逆行性に依存していますが、FNLMDの中心概念は、画像内の他の場所で類似したピクセルを見つけ、それらを平均化することです。この場合、画素が類似しているとみなされるのは、画素や強度が類似しているからではなく、その環境において類似しているからです。ここで重要なのは、多くの画像には重複した構造が含まれているため、たとえその画素がノイズによって破損していたとしても、他にも類似した画素が多数存在することです。





FNLMD基本アルゴリズム cv::fastNlMeansDenoising()

void cv::fastNlMeansDenoising(
cv::InputArray src, // Input image
cv::OutputArray dst, // Output image
float h = 3, // Weight decay parameter weight decay parameter
int templateWindowSize = 7, // Size of patches used for comparison
int searchWindowSize = 21 // Maximum patch distance to consider The maximum value of patches within the allowed range
);


templateWindowSize のパッチ領域と減衰パラメータ h を用いて,入力配列 src から結果配列 dst を計算し, searchWindowSize の距離内にあるパッチを考慮します.画像は,1次元,2次元,3次元のいずれでも構いませんが,型は cv::U8 でなければいけません.

FNLMDカラー画像アルゴリズム cv::fastNIMeansDenoisingColor()

void cv::fastNlMeansDenoisingColored(
	cv::InputArray src, // Input image
	cv::OutputArray dst, // Output image
	float h = 3, // Luminosity weight decay parameter
	float hColor = 3, // Color weight decay parameter Color weight decay parameter
	int templateWindowSize = 7, // Size of patches used for comparison
	int searchWindowSize = 21 // Maximum patch distance to consider
);


FNIMDアルゴリズムの2番目のバリエーションは、カラー画像に使用されます。これは,cv::U8C3 型の画像のみを受け付けます.原理的には,このアルゴリズムを RGB 画像に多少なりとも直接適用することができますが,実際には,画像を別の色空間に変換して計算したほうがよいでしょう.この関数は cv::fastNIMeansDenoisingColorred() この主な利点は、色に関して、実際には3つの減衰パラメータがあることです。しかし、RGB表現では、そのどれもが異なる値に設定されることはまずありません。しかし、LAB空間では、色成分に対して、輝度成分に対して、異なる減衰パラメータを指定することが自然である。そのため、関数 cv::fastNIMeansDenoisingColorred() を使用すると、これを行うことができます。パラメータ h は輝度減衰パラメータに使用され、新しいパラメータ hColor はカラーチャンネルに使用されます。一般に、hColor の値は h よりもずっと小さい値になります。ほとんどの場合、10 が適切な値です。

FNLMDビデオ画像アルゴリズム cv::fastNlMeansDenoisingMulti() cv::fastNlMeansDenoisingColorMulti()

void cv::fastNlMeansDenoisingMulti(
	cv::InputArrayOfArrays srcImgs, // Sequence of several images An array of images with multiple images
	cv::OutputArray dst, // Output image
	int imgToDenoiseIndex, // Index of image to denoise The index specifies the image to be denoised
	int temporalWindowSize, // Num images to use (odd) Number of images in the sequence used in denoising
	float h = 3, // Weight decay parameter
	int templateWindowSize = 7, // Size of comparison patches 
	int searchWindowSize = 21 // Maximum patch distance Maximum patch distance
);

void cv::fastNlMeansDenoisingColoredMulti(
	cv::InputArrayOfArrays srcImgs, // Sequence of several images
	cv::OutputArray dst, // Output image
	int imgToDenoiseIndex, // Index of image to denoise
	int temporalWindowSize, // Num images to use (odd)
	float h = 3, // Weight decay param
	float hColor = 3, // Weight decay param for color
	int templateWindowSize = 7, // Size of comparison patches
	int searchWindowSize = 21 // Maximum patch distance
);


3つ目と4つ目のバリエーションは、動画からキャプチャしたような連続した画像に使用されます。連続した画像の場合、現在のフレーム以外のフレームに、ノイズ除去されたピクセルに関する有用な情報が含まれている可能性があると考えるのは自然なことである。多くの場合、ノイズは画像間で一定ではなく、信号は類似しているか、あるいは同一である可能性があります。このような場合、関数 cv::fastNlMeansDenoisingMulti() cv::fastNlMeansDenoisingColorMulti() 1枚の画像ではなく、画像の配列 srcImage を期待する。また,この関数は,引数 imgToDenoiseIndex によって,シーケンス中のどの画像がノイズ除去されるかを知らされます.最後に,ノイズ除去に利用されるシーケンス中の画像の数を示す時間窓を与えなければいけません.このパラメータは奇数でなければならず、暗黙の窓は常に imgToDenoiseIndex を中心とします(従って、imgToDenoiseIndex を 4 に設定し temporalWindowSize を 5 にすると、ノイズ除去に使用する画像は 2, 3, 4, 5, 6 となります)。

ヒストグラム等化

画像のダイナミックレンジを広げてコントラストを上げようと、さまざまな工夫が施されています。最もよく使われる手法は、ヒストグラム・イコライゼーションです。

ヒストグラム・イコライズの数学的背景は、ある分布(強度値の与えられたヒストグラム)を別の分布(強度値のより広い、理想的には均一な分布)に写像することです。つまり、元の分布のy値を新しい分布にできるだけ一様に分布させたいと考えています。分布の値を拡張する問題を解決する良い方法は、再マッピング関数を累積分布関数にすることであることがわかりました。

累積分布関数は、元の分布の各Y値を見て、一様分布のどこで実行すべきかを見るだけで、元の分布を一様分布に再マッピングするのに利用できます。連続分布の場合、結果は正確な平衡になりますが、数値/離散分布の場合、結果は非常に矛盾したものになる可能性があります。

cv::equalizeHist() イコライザー比較用

opencvは全ての処理を1つの関数にまとめています。

void cv::equalizeHist(
	const cv::InputArray src, // Input image
	cv::OutputArray dst // Result image
);


ソース画像は8ビット1次元画像であること。ターゲット画像も8bitの1次元画像である。カラー画像の場合は、チャンネルごとに処理する必要がある。