[w6d2] Transformation
(Ubuntu 18.04.6 LTS)
2022.03.22.
C++、VSコードを使用
プログラマー自主走行Defcos 3期
Translation transformation
ピクセル値位置のみを移動する演算.値がcv::Matオブジェクトの最大値を超える場合は、値の入力を避けるために処理する必要があります.
Shear transformation
これは画像の1つの領域を押し開く演算です.
Scale transformationは、サイズを変換する演算です.
dst 1は、srcのイメージに基づいた順方向マッピングである.この場合、大きさの間の各部分が空白になり、上の部分が黒の明るさで表示され、サイズの変化が正常ではありません.dst 2は、dst 2参照src値に基づく逆マッピングを用いる.上記の画像ではdst 1よりも優れた結果が見られた.
サイズを大きくすると、上記のように空の画素をどのように処理するかを実現します.同様に、ビデオを縮小する場合も、重複する画素の輝度値を調整する.OpenCVはresize関数で補間方法を決定することができる. INTER_NEAREST: nearest neighbor interpolation INTER_LINEAR: bilinear interpolation INTER_CUBIC: bicubic interpolation INTER_AREA: resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire'-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method. INTER_LANCZOS4: Lanczos interpolation over 8x8 neighborhood Linear、cubic、Lanczosを使用すると、参照画素数が増加し、演算量も増加するので、よりスムーズに拡大できます.縮小する場合は面積方式が望ましい.
Interpolation全体の方法は以下の通りです.
https://docs.opencv.org/4.x/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121
複数の補間手法を用いて,4倍増幅後,部分領域のみをチェックするコードを記述した.実行結果を確認すると,最近接点から最近接点までの場合,差は確認されやすいが,その後急激な差は見にくい.画質が重要であれば、より高いフィルターを使うことができますが、一般的にはLinearだけで十分です.
回転変換は、特定のポイントを基準にして回転する変換です.また、回転変換では、反転マッピングまたは補間を使用して、空白のピクセルを回避する必要があります.
以前に使用した形式のコピーは次のように表示されます.
回転変換とサイズ変換のみを用いると、bがゼロベクトルとなり、複数回の演算を経ても、その変換を行列の積で表すしかない.しかし,平行シフトを大きくするとbはゼロベクトルではなく,行列の積以外の項が生じるので表現が不便である.整列座標は(x,y,1)の代わりに(x,y)で座標を表す従来の方法であり、平行移動はマトリクスの積形式で表すこともできる.次に、各演算の新しい定義の方法を示します.
Translation transform
Shear transfrom
Scale transform
Rotation transform
各演算は行列の積で表すことができ、以前のcp::warpAffine関数は2 x 3行列を受け入れてこれらの演算を実行した.
0は上下対称、正数は左右対称、負数は上下対称.
シミュレーション変換は、直線と平行性を保持するジオメトリ変換であり、同一性、平行移動、反射、スケール、回転、せん断が対応します.
https://en.wikipedia.org/wiki/Affine_transformation
cv::point 2 f src[3]またはベクトルsrcを値として与えることができる.
パース変換は、3 D空間内の物体を1点基準で2 D平面に投影する線形投影です.
solveMethodは下に見えます.
https://docs.opencv.org/4.x/d2/de8/group__core__array.html#gaaf9ea5dcc392d5ae04eacb9920b9674c
上のコードは、表示された矩形パースをdstの矩形に変換した結果です.
2022.03.22.
C++、VSコードを使用
プログラマー自主走行Defcos 3期
Geometric transformation
ピクセル値位置のみを移動する演算.値がcv::Matオブジェクトの最大値を超える場合は、値の入力を避けるために処理する必要があります.
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_GRAYSCALE);
if (src.empty()){
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst1(src.rows,src.cols,CV_8UC1,cv::Scalar(0));
for (int y=0;y<src.rows;y++){
for (int x=0;x<src.cols;x++){
int x_ = x + 50;
int y_ = y + 100;
if (y_>=dst1.rows || y<0) continue;
if (x_>=dst1.cols || x<0) continue;
dst1.at<uchar>(y_,x_) = src.at<uchar>(y,x);
}
}
cv::Mat dst2(src.rows+100,src.cols+50,CV_8UC1,cv::Scalar(0));
for (int y=0;y<src.rows;y++){
for (int x=0;x<src.cols;x++){
int x_ = x + 50;
int y_ = y + 100;
if (y_>=dst2.rows || y<0) continue;
if (x_>=dst2.cols || x<0) continue;
dst2.at<uchar>(y_,x_) = src.at<uchar>(y,x);
}
}
cv::imshow("src",src);
cv::imshow("dst1",dst1);
cv::imshow("dst2",dst2);
while(cv::waitKey()!=27) continue;
cv::destroyAllWindows();
return 0;
}
コード実行結果は以下の画像に表示されます.dst 1には、領域の外にあるため、確認コードが存在する.dst 2はdst 1ウィンドウの下に存在する.dst 2はcv::Matオブジェクトのサイズを増加させ、dst 1とdst 2のオーバーラップを決定するために同じサイズを平行に移動することができる.これは画像の1つの領域を押し開く演算です.
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_GRAYSCALE);
if (src.empty()){
std::cerr << "Image load failed!" << std::endl;
return -1;
}
double m = 0.5;
cv::Mat dst1(src.rows,src.cols*3/2,CV_8UC1,cv::Scalar(0));
for (int y=0;y<src.rows;y++){
for (int x=0;x<src.cols;x++){
int x_ = int(x+m*y);
int y_ = y;
if (y_>=dst1.rows || y<0) continue;
if (x_>=dst1.cols || x<0) continue;
dst1.at<uchar>(y_,x_) = src.at<uchar>(y,x);
}
}
cv::Mat dst2;
float aff_[] = {1,0,0,0.5,1,0};
cv::Mat aff(2,3,CV_32F,aff_);
cv::warpAffine(src,dst2,aff,cv::Size(src.cols,src.rows*3/2));
cv::imshow("src",src);
cv::imshow("dst1",dst1);
cv::imshow("dst2",dst2);
while (cv::waitKey()!=27) continue;
cv::destroyAllWindows();
return 0;
}
実行結果は以下のとおりです.dst 2の作成時にwarpAffineを使用して作成し、その後Affine変換でコンテンツを処理します.Scale transformation
Scale transformationは、サイズを変換する演算です.
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_GRAYSCALE);
if (src.empty()){
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst1(src.rows * 2, src.cols * 2, CV_8UC1, cv::Scalar(0));
for (int y=0;y<src.rows;y++){
for (int x=0;x<src.cols;x++){
int x_ = x * 2;
int y_ = y * 2;
dst1.at<uchar>(y_,x_) = src.at<uchar>(y,x);
}
}
cv::Mat dst2 = cv::Mat::zeros(src.rows*2,src.cols*2,CV_8UC1);
for (int y_=0;y_<dst2.rows;y_++){
for (int x_=0;x_<dst2.cols;x_++){
int x = x_ / 2;
int y = y_ / 2;
dst2.at<uchar>(y_,x_) = src.at<uchar>(y,x);
}
}
cv::imshow("src",src);
cv::imshow("dst1",dst1);
cv::imshow("dst2",dst2);
while (cv::waitKey()!=27) continue;
cv::destroyAllWindows();
return 0;
}
dst 1は、srcのイメージに基づいた順方向マッピングである.この場合、大きさの間の各部分が空白になり、上の部分が黒の明るさで表示され、サイズの変化が正常ではありません.dst 2は、dst 2参照src値に基づく逆マッピングを用いる.上記の画像ではdst 1よりも優れた結果が見られた.
Interpolation
サイズを大きくすると、上記のように空の画素をどのように処理するかを実現します.同様に、ビデオを縮小する場合も、重複する画素の輝度値を調整する.OpenCVはresize関数で補間方法を決定することができる.
void cv::resize ( InputArray src,
OutputArray dst,
Size dsize,
double fx = 0,
double fy = 0,
int interpolation = INTER_LINEAR
)
OpenCVは上記の関数を提供します.dsizeは結果画像のサイズであり、cv::size()はfx、fyによって決定される.fxとfyはそれぞれx方向とy方向のスケール係数である.Interpotationには次のようなものがあります.Interpolation全体の方法は以下の通りです.
https://docs.opencv.org/4.x/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_GRAYSCALE);
cv::Mat dst1,dst2,dst3,dst4;
cv::resize(src,dst1,cv::Size(),4,4,cv::INTER_NEAREST);
cv::resize(src,dst2,cv::Size(),4,4,cv::INTER_LINEAR);
cv::resize(src,dst3,cv::Size(),4,4,cv::INTER_CUBIC);
cv::resize(src,dst4,cv::Size(),4,4,cv::INTER_LANCZOS4);
cv::putText(dst1,"INTER_NEAREST",cv::Point(410,540),cv::FONT_HERSHEY_DUPLEX,0.7,cv::Scalar(0),2);
cv::putText(dst2,"INTER_LINEAR",cv::Point(410,540),cv::FONT_HERSHEY_DUPLEX,0.7,cv::Scalar(0),2);
cv::putText(dst3,"INTER_CUBIC",cv::Point(410,540),cv::FONT_HERSHEY_DUPLEX,0.7,cv::Scalar(0),2);
cv::putText(dst4,"INTER_LANCZOS4",cv::Point(410,540),cv::FONT_HERSHEY_DUPLEX,0.7,cv::Scalar(0),2);
cv::imshow("dst1",dst1(cv::Rect(400,500,400,400)));
cv::imshow("dst2",dst2(cv::Rect(400,500,400,400)));
cv::imshow("dst3",dst3(cv::Rect(400,500,400,400)));
cv::imshow("dst4",dst4(cv::Rect(400,500,400,400)));
while (cv::waitKey()!=27) continue;
cv::destroyAllWindows();
return 0;
}
複数の補間手法を用いて,4倍増幅後,部分領域のみをチェックするコードを記述した.実行結果を確認すると,最近接点から最近接点までの場合,差は確認されやすいが,その後急激な差は見にくい.画質が重要であれば、より高いフィルターを使うことができますが、一般的にはLinearだけで十分です.
Rotation transformation
回転変換は、特定のポイントを基準にして回転する変換です.また、回転変換では、反転マッピングまたは補間を使用して、空白のピクセルを回避する必要があります.
Mat cv::getRotationMatrix2D ( Point2f center,
double angle,
double scale
)
上記OpenCV関数を中心とした点の中心、反時計回りの回転角度、スケールを入力し、以下の形式の2 x 3 cv::Matオブジェクトを返します.void cv::warpAffine ( InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
その後、cv::warpAffine関数を使用して変換できます.InputArrayMに2 x 3行列を入力すればよいので、cv::getRotationMatrix 2 D関数の結果値を使用できます.dsizeは結果画像のサイズを設定し、cv::Size()に設定するとsrcと同様にflagsを使用して補間方法を設定できます.#include <iostream>
#include "opencv2/opencv.hpp"
void trackbar_rotate(int pos, void* userdata);
int main()
{
cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_COLOR);
if (src.empty()){
std::cerr << "src load failed!" << std::endl;
return -1;
}
cv::namedWindow("dst");
cv::createTrackbar("rotate: ","dst",0,360,trackbar_rotate,(void*)&src);
trackbar_rotate(0,(void*)&src);
cv::waitKey();
cv::destroyAllWindows();
return 0;
}
void trackbar_rotate(int pos, void* userdata){
const cv::Mat& src = *((cv::Mat*)userdata);
cv::Mat dst;
float degree = (float)pos;
cv::Point2f pt(src.cols/2.f,src.rows/2.f);
cv::Mat rot = cv::getRotationMatrix2D(pt,degree,1.0);
cv::warpAffine(src,dst,rot,cv::Size());
cv::imshow("dst",dst);
}
上のコードは、ビデオをロードし、タスクバーで回転を実現するコードで、実行結果は以下の通りです.Homogeneous coordinates
以前に使用した形式のコピーは次のように表示されます.
回転変換とサイズ変換のみを用いると、bがゼロベクトルとなり、複数回の演算を経ても、その変換を行列の積で表すしかない.しかし,平行シフトを大きくするとbはゼロベクトルではなく,行列の積以外の項が生じるので表現が不便である.整列座標は(x,y,1)の代わりに(x,y)で座標を表す従来の方法であり、平行移動はマトリクスの積形式で表すこともできる.次に、各演算の新しい定義の方法を示します.
Translation transform
Shear transfrom
Scale transform
Rotation transform
各演算は行列の積で表すことができ、以前のcp::warpAffine関数は2 x 3行列を受け入れてこれらの演算を実行した.
Flip, reflection
void cv::flip ( InputArray src,
OutputArray dst,
int flipCode
)
OpenCVはflipに関数を提供し、flipcodeに基づいて対称方向を指定します.0は上下対称、正数は左右対称、負数は上下対称.
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_COLOR);
if (src.empty()){
std::cerr << "src load failed!" << std::endl;
return -1;
}
cv::Mat dst1, dst2, dst3;
cv::flip(src,dst1,0);
cv::flip(src,dst2,1);
cv::flip(src,dst3,-1);
cv::imshow("src",src);
cv::imshow("dst1",dst1);
cv::imshow("dst2",dst1);
cv::imshow("dst3",dst1);
while (cv::waitKey()!=27) continue;
cv::destroyAllWindows();
return 0;
}
Affine transfromation
シミュレーション変換は、直線と平行性を保持するジオメトリ変換であり、同一性、平行移動、反射、スケール、回転、せん断が対応します.
https://en.wikipedia.org/wiki/Affine_transformation
Mat cv::getAffineTransform ( const Point2f src[],
const Point2f dst[]
)
Mat cv::getAffineTransform ( InputArray src,
InputArray dst
)
3つの座標をsrcとdstで受け入れ,2×3シミュレーション行列を得た.cv::point 2 f src[3]またはベクトル
void cv::warpAffine ( InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
Perspective transformation
パース変換は、3 D空間内の物体を1点基準で2 D平面に投影する線形投影です.
Mat cv::getPerspectiveTransform ( InputArray src,
InputArray dst,
int solveMethod = DECOMP_LU
)
Mat cv::getPerspectiveTransform ( const Point2f src[],
const Point2f dst[],
int solveMethod = DECOMP_LU
)
上のgetaffineTransformと似ていますが、違いは4つの座標が必要です.solveMethodは下に見えます.
https://docs.opencv.org/4.x/d2/de8/group__core__array.html#gaaf9ea5dcc392d5ae04eacb9920b9674c
void cv::warpPerspective ( InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
InputArrayMはCV 32 FまたはCV 64 Fタイプの3 x 3行列であり,他のパラメータは以前と類似している.//bird eye view
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::VideoCapture cap("./resources/test_video.mp4");
if (!cap.isOpened()){
std::cerr << "video load failed!" << std::endl;
return -1;
}
cv::Mat frame,dst;
int w=500, h = 260;
std::vector<cv::Point2f> src_pts(4);
std::vector<cv::Point2f> dst_pts(4);
src_pts[0] = cv::Point2f(474, 400);
src_pts[1] = cv::Point2f(710, 400);
src_pts[2] = cv::Point2f(866, 530);
src_pts[3] = cv::Point2f(366, 530);
dst_pts[0] = cv::Point2f(0, 0);
dst_pts[1] = cv::Point2f(w-1, 0);
dst_pts[2] = cv::Point2f(w-1, h-1);
dst_pts[3] = cv::Point2f(0, h-1);
std::vector<cv::Point> pts;
for (auto pt : src_pts){
pts.push_back(cv::Point(pt.x,pt.y));
}
cv::Mat per_mat = cv::getPerspectiveTransform(src_pts,dst_pts);
while(true){
cap >> frame;
if (frame.empty()){
std::cout << "empty frame" << std::endl;
break;
}
cv::warpPerspective(frame,dst,per_mat,cv::Size(w,h));
cv::polylines(frame,pts,true,cv::Scalar(255,0,0),2,cv::LINE_AA);
cv::imshow("frame",frame);
cv::imshow("dst",dst);
if(cv::waitKey(10)==27) break;
}
}
上のコードは、表示された矩形パースをdstの矩形に変換した結果です.
Reference
この問題について([w6d2] Transformation), 我々は、より多くの情報をここで見つけました https://velog.io/@anecjong/w6d2-Transformationテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol