OpenCV 4.x API 详解与C++实例-图像几何变换

第二节 图像几何变换

本节中的功能执行2D图像的各种几何变换。 它们不更改图像内容,而是使像素网格变形并将该变形的网格映射到目标图像。实际上,避免采样伪像,以从目标到源的相反顺序进行映射。 也就是说,对于目标图像的每个像素 ( x , y ) (x,y) (x,y),函数计算源图像中相应原像素的坐标并复制像素值:

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

如果指定前向映射, < g x , g y > : src → dst \left<g_x, g_y\right>: \texttt{src} \rightarrow \texttt{dst} gx,gy:srcdst,OpenCV函数首先计算对应的逆映射 < f x , f y > : dst → src \left<f_x, f_y\right>: \texttt{dst} \rightarrow \texttt{src} fx,fy:dstsrc,然后使用上面的公式。

从最通用的重新映射到最简单,最快的尺寸调整,几何转换的实际实现需要使用上述公式解决两个主要问题:

  • 不存在像素的外推。 与上一部分中描述的过滤功能相似,对于某些 ( x , y ) (x,y) (xy) f x ( x , y ) f_x(x,y) fx(x,y) f y ( x , y ) f_y(x,y) fy(x,y)中的一个可能会落在图像之外。 在这种情况下,需要使用外推方法。 OpenCV提供与过滤功能相同的外推方法选择。 此外,它提供了BORDER_TRANSPARENT方法。 这意味着目标图像中的相应像素将完全不会被修改。
  • 像素值的插值。 通常 f x ( x , y ) f_x(x,y) fx(x,y) f y ( x , y ) f_y(x,y) fy(x,y)是浮点数。 这意味着 ⟨ f x , f y ⟩ ⟨fx,fy⟩ fx,fy可以是仿射或透视变换,也可以是径向镜头畸变校正,依此类推。 因此,需要检索小数坐标处的像素值。 在最简单的情况下,可以将坐标仅舍入为最接近的整数坐标,并可以使用相应的像素。 这称为最近邻插值。 但是,通过使用更复杂的插值方法可以获得更好的结果,其中将多项式函数拟合到计算像素的某些邻域 f x ( x , y ) , f y ( x , y ) ) f_x(x,y),f_y(x,y)) fx(x,y),fy(x,y))中,然后将 将 f x ( x , y ) , f y ( x , y ) ) f_x(x,y),f_y(x,y)) fx(x,y),fy(x,y))的多项式作为插值像素值。 在OpenCV中,提供了几种插值方法。

OpenCV不支持CV_8S和CV_32S类型图像。

1、cv::resize


调整图像大小。

void cv::resize(InputArray src,OutputArray dst,Size dsize,double fx = 0,double fy = 0,int interpolation = INTER_LINEAR)

函数调整大小可将图像src调整为指定大小或最大。 请注意,未考虑初始dst类型或大小。 而是从src,dsize,fx和fy派生大小和类型。 如果要调整src的大小以使其适合预先创建的dst,则可以按以下方式调用该函数:

resize(src, dst, dst.size(), 0, 0, interpolation);

如果要在每个方向上将图像缩小2倍,则可以通过以下方式调用该函数:

resize(src, dst, Size(), 0.5, 0.5, interpolation);

要缩小图像,通常使用INTER_AREA插值效果最佳,而要放大图像,通常使用c :: INTER_CUBIC(速度慢)或INTER_LINEAR(速度更快,但看起来仍然可以)最好。

参数如下:

参数名称 参数描述
src 输入图像
dst 输出图像;与dsize大小相同,(如果为非零)或根据src.size()或fx、fy计算得出的大小,dst与src类型相同
dsize 输出图像大小; 如果为0,则dsize = Size(round(fxsrc.cols), round(fysrc.rows))否则 dsize 或fx 和fy必须为非零。
fx 沿水平轴的比例因子; 当它等于0时,将计算为(double)dsize.width / src.cols
fy 沿垂直轴的比例因子; 当它等于0时,将计算为(double)dsize.height / src.rows
interpolation 插值方法, 请参考:InterpolationFlags

2、cv::convertMaps、cv::remap


1)cv::convertMaps:将图像从一种映射转换为另一种映射。

void cv::convertMaps(InputArray map1,InputArray map2,OutputArray dstmap1,OutputArray dstmap2,int dstmap1type,bool nninterpolation = false)

该函数将一对映射从一个表示转换为另一种表示。 ( (map1.type(), map2.type()) → (dstmap1.type(), dstmap2.type()) )支持以下选项:

  • KaTeX parse error: Expected '}', got '_' at position 12: \texttt{(CV_̲32FC1, CV_32FC1…这是最常用的转换操作,其中原始的浮点图(请参阅remap)被转换为更紧凑,更快的定点表示形式。 第一个输出数组包含舍入坐标,第二个数组(仅在nninterpolation = false时创建)包含插值表中的索引。
  • KaTeX parse error: Expected '}', got '_' at position 12: \texttt{(CV_̲32FC2)} \righta…与上述相同,但原始地图存储在一个2通道矩阵中。
  • 反向转换。 显然,重建的浮点图将与原始图不完全相同。
    参数描述如下
参数名称 参数描述
map1 类型为CV_16SC2,CV_32FC1或CV_32FC2的第一个输入映射。
map2 类型为CV_16SC2,CV_32FC1或CV_32FC2的第二个输入映射。
dstmap1 第一个输出映射的类型为dstmap1type,大小与src相同。
dstmap2 第一个输出映射的类型
dstmap1type 第一个输出映射的类型应该为CV_16SC2,CV_32FC1或CV_32FC2。
nninterpolation 指示定点图是用于最近邻居还是用于更复杂的插值的标志。

2)cv::remap:将通用的几何变换应用于图像。

void cv::remap(InputArray src,OutputArray dst,InputArray map1,InputArray map2,int interpolation,int borderMode = BORDER_CONSTANT,const Scalar & borderValue = Scalar())

函数remap使用指定的映射来转换源图像:

dst ( x , y ) = src ( m a p x ( x , y ) , m a p y ( x , y ) ) \texttt{dst} (x,y) = \texttt{src} (map_x(x,y),map_y(x,y)) dst(x,y)=src(mapx(x,y),mapy(x,y))

其中具有非整数坐标的像素值是使用一种可用的插值方法来计算的。 m a p x map_x mapx m a p y map_y mapy可以分别编码为map1和map2中的单独浮点图,也可以编码为map1中$(x,y)的交错浮点映射,或者使用convertMaps创建的定点映射。

该函数不能就地(in-place)转换

参数如下:

参数名称 参数描述
src 输入图像
dst 目标图像。它的大小与map1相同,类型与src相同。
map1 ( x , y ) (x,y) (xy)点或仅x个值的第一个映射具有类型CV_16SC2,CV_32FC1或CV_32FC2。
map2 分别具有CV_16UC1,CV_32FC1或无类型的y值的第二个映射(如果map1是 ( x , y ) (x,y) (xy)点,则为空映射)。
interpolation 插值方法 (请参考InterpolationFlags).。 不支持INTER_AREA 插值类型
borderMode 像素外推法(请参考BorderTypes)。当borderMode = BORDER_TRANSPARENT时,表示目标图像中的像素“对应于目标图像中的像素” 该功能未修改源图像。
borderValue 边界不变时使用的值。 默认情况下为0。
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;


/// 生成网格
/// \brief meshgrid
/// \param xgv 水平方向范围
/// \param ygv 垂直方向范围
/// \param X 输出水平方向网格
/// \param Y 输出垂直方向网格
///
void meshgrid(const cv::Range &xgv, const cv::Range &ygv, cv::Mat &X, cv::Mat &Y)
{
    std::vector<int> t_x, t_y;
    for(int i = xgv.start; i <= xgv.end; i++) t_x.push_back(i);
    for(int j = ygv.start; j <= ygv.end; j++) t_y.push_back(j);

    cv::repeat(cv::Mat(t_x).t(), t_y.size(), 1, X);
    cv::repeat(cv::Mat(t_y), 1, t_x.size(), Y);

}

/// 计算形变网格
/// \brief deformation_to_transformation
/// \param ux 水平方向范围
/// \param uy 垂直方向范围
/// \param dx 水平方向形变量
/// \param dy 垂直方向形变量
/// \param xgv 水平方向范围
/// \param ygv 垂直方向范围
///
void deformation_to_transformation(cv::Mat& ux,cv::Mat &uy,int dx,int dy,
                                   const cv::Range &xgv, const cv::Range &ygv){
    meshgrid(xgv,ygv,ux,uy);
    ux.convertTo(ux,CV_32FC1);
    ux += dx;
    uy.convertTo(uy,CV_32FC1);
    uy += dy;
}

/// 平移图像
/// \brief dense_image_warp
/// \param src 输入图像
/// \param dst 输出图像
/// \param dx 水平方向平移量
/// \param dy 垂直方向平移量
///
void dense_image_warp(const cv::Mat& src,cv::Mat& dst,int dx,int dy){
    int rows = src.rows;
    int cols = src.cols;
    cv::Mat map_x,map_y,ux,uy;
    deformation_to_transformation(ux,uy,dx,dy,cv::Range(0,cols),cv::Range(0,rows));

    // 计算映射坐标
    cv::convertMaps(ux,uy,map_x,map_y,CV_16SC2);
    // 坐标生重映射,重建图像
    cv::remap(src,dst,map_x,map_y,cv::INTER_NEAREST);
}


int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/f1.jpg");
    cv::Mat dst;
    // 计算平移输出图像
    dense_image_warp(src,dst,-50,50);

    // 显示图像
    cv::imshow("src",src);
    cv::imshow("dst",dst);
    cv::waitKey();
    cv::destroyAllWindows();
    return 0;
}

在这里插入图片描述

3、cv::warpAffine、cv::invertAffineTransform、cv::getAffineTransform、cv::getRotationMatrix2D、cv::getRotationMatrix2D_


1)cv::warpAffine:对图像应用仿射变换。

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

函数warpAffine使用指定的矩阵转换源图像:

dst ( x , y ) = src ( M 11 x + M 12 y + M 13 , M 21 x + M 22 y + M 23 ) \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}) dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23)

当设置标志WARP_INVERSE_MAP时。 否则,首先使用invertAffineTransform反转转换,然后将其放在上面的公式中,而不是M。该函数无法就地操作。

参数如下:

参数名称 参数描述
src 输入图像
dst 输出图像,输出图像,其大小为dsize,并且类型与src相同。
M 2×3转换矩阵
dsize 输出图像大小
flags 插值方法的组合 (参考InterpolationFlags) 当flag为 WARP_INVERSE_MAP 时, M 是反向转换 ( dst→src ).
borderMode 像素外推法 (参考BorderTypes);当 borderMode=BORDER_TRANSPARENT,时,这意味着目标图像中与源图像中的“离群值”相对应的像素不会被该功能修改。
borderValue 在边界不变的情况下使用的值; 默认情况下为0。

2)cv::invertAffineTransform:反转仿射变换。

void cv::invertAffineTransform(InputArray M,OutputArray iM)

该函数计算由2×3矩阵M表示的逆仿射变换:

[ a 11 a 12 b 1 a 21 a 22 b 2 ] \begin{bmatrix} a_{11} & a_{12} & b_1 \\ a_{21} & a_{22} & b_2 \end{bmatrix} [a11a21a12a22b1b2]

结果与M相同类型的 2 × 3 2×3 2×3矩阵。

参数如下:

参数名称 参数描述
M 原始仿射变换。
iM 输出反向仿射变换。

3)cv::getAffineTransform:根据三对对应点计算仿射变换。

  • Mat cv::getAffineTransform(const Point2f src[],const Point2f dst[])
  • Mat cv::getAffineTransform(InputArray src,InputArray dst)

该函数计算仿射变换的2×3矩阵,如下:

KaTeX parse error: Expected '}', got '_' at position 57: …} = \texttt{map_̲matrix} \cdot \…

其中, d s t ( i ) = ( x i ′ , y i ′ ) , s r c ( i ) = ( x i , y i ) , i = 0 , 1 , 2 dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2 dst(i)=(xi,yi),src(i)=(xi,yi),i=0,1,2
参数如下:

参数名称 参数描述
src 源图像中三角形顶点的坐标。
dst 目标图像中相应三角形顶点的坐标。

示例如下:

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;


int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/f1.jpg");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat dst;

    int rows = src.rows;
    int cols = src.cols;
    //源图像和目标图像对应映射的三点
    cv::Point2f srcPoint[3];
    cv::Point2f resPoint[3];
    srcPoint[0] = cv::Point2f(0, 0);
    srcPoint[1] = cv::Point2f(cols - 1, 0);
    srcPoint[2] = cv::Point2f(0, rows - 1);
    resPoint[0] = cv::Point2f(cols * 0, rows*0.33);
    resPoint[1] = cv::Point2f(cols*0.85, rows*0.25);
    resPoint[2] = cv::Point2f(cols*0.15, rows*0.7);

    // 定义仿射变换矩阵2X3
    cv::Mat warpMat(cv::Size(2, 3), CV_32F);
    dst = cv::Mat::zeros(rows, cols, src.type());
    // 计算仿射变换矩阵,即仿射变换的2*3数组
    warpMat = cv::getAffineTransform(srcPoint, resPoint);

    // 根据仿射矩阵计算图像仿射变换
    cv::warpAffine(src, dst,
                   warpMat, dst.size());
    cv::imshow("dst", dst);
    cv::imshow("src",src);

    cv::waitKey();
    cv::destroyAllWindows();

    return 0;
}

在这里插入图片描述

3)cv::getRotationMatrix2D:计算2D旋转的仿射矩阵。

该函数计算以下矩阵:

[ α β ( 1 − α ) ⋅ center.x − β ⋅ center.y − β α β ⋅ center.x + ( 1 − α ) ⋅ center.y ] \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} [αββα(1α)center.xβcenter.yβcenter.x+(1α)center.y]

其中, α = scale ⋅ cos ⁡ angle , β = scale ⋅ sin ⁡ angle \begin{array}{l} \alpha = \texttt{scale} \cdot \cos \texttt{angle} , \\ \beta = \texttt{scale} \cdot \sin \texttt{angle} \end{array} α=scalecosangle,β=scalesinangle

变换将旋转中心映射到其自身。如果不是目标,调整转变。

参数如下:

参数名称 参数描述
center 输入图像旋转中心
angle 旋转角度(度)。 正值表示逆时针旋转(假设坐标原点为左上角)。
scale 各向同性比例因子。
cv::Point2f centerPoint = cv::Point2f(cols / 2, rows / 2);
double angle = -50;
double scale = 0.7;

// 获取旋转仿射变换矩阵
warpMat = getRotationMatrix2D(centerPoint, angle, scale);
// 根据仿射矩阵计算图像仿射变换
cv::warpAffine(src, dst,warpMat, dst.size());

在这里插入图片描述

4、cv::warpPerspective、cv::getPerspectiveTransform


1)cv::warpPerspective:将透视变换应用于图像。

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

函数warpPerspective使用指定的矩阵转换源图像:

dst ( x , y ) = src ( M 11 x + M 12 y + M 13 M 31 x + M 32 y + M 33 , M 21 x + M 22 y + M 23 M 31 x + M 32 y + M 33 ) \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 ) dst(x,y)=src(M31x+M32y+M33M11x+M12y+M13,M31x+M32y+M33M21x+M22y+M23)

当设置标志WARP_INVERSE_MAP时。 否则,首先使用invert反转转换,然后将其放在上面的公式中,而不是M。函数无法就地操作。

Parameters

参数名称 参数描述
src 输入图像
dst 输出图像,其大小为dsize,并且类型与src相同。
M 3×3 转换矩阵
dsize 输出图像大小
flags 插值方法(INTER_LINEAR或INTER_NEAREST)与可选标志WARP_INVERSE_MAP的组合,将M设置为逆变换(dst→src)。
borderMode 像素外推法 (BORDER_CONSTANT or BORDER_REPLICATE).
borderValue 在边界不变的情况下使用的值; 默认情况下为0。

2)cv::getPerspectiveTransform:根据四对对应点计算透视变换。

该函数计算透视变换的3×3矩阵,如下:

KaTeX parse error: Expected '}', got '_' at position 72: …} = \texttt{map_̲matrix} \cdot \…

其中: d s t ( i ) = ( x i ′ , y i ′ ) , s r c ( i ) = ( x i , y i ) , i = 0 , 1 , 2 , 3 dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2,3 dst(i)=(xi,yi),src(i)=(xi,yi),i=0,1,2,3

参数如下:

参数名称 参数描述
src 源图像中四边形顶点的坐标。
dst 目标图像中相应四边形顶点的坐标。
solveMethod 求解方法,传递给 cv::solve (DecompTypes)
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/f1.jpg");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat dst;

    cv::Point2f src_points[] = {
        cv::Point2f(165, 640),
        cv::Point2f(835, 640),
        cv::Point2f(360, 125),
        cv::Point2f(615, 125) };

    cv::Point2f dst_points[] = {
        cv::Point2f(165, 640),
        cv::Point2f(835, 640),
        cv::Point2f(165, 30),
        cv::Point2f(835, 30) };

    cv::Mat M = cv::getPerspectiveTransform(src_points, dst_points);

    cv::warpPerspective(src, dst, M, cv::Size(960, 640), cv::INTER_LINEAR);

    cv::imshow("src",src);
    cv::imshow("dst",dst);
    cv::waitKey();
    cv::destroyAllWindows();

    return 0;
}

在这里插入图片描述

5、cv::linearPolar、cv::logPolar、cv::warpPolar


1)cv::linearPolar:将图像重新映射到极坐标空间。

void cv::linearPolar(InputArray src,OutputArray dst,Point2f center,double maxRadius,int flags)

注意,这个函数已经过时,与函数**cv::warpPolar(src, dst, src.size(), center, maxRadius, flags)**调用的结果相同。

#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>
using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/f1.jpg");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat dst;

    // 计算最半径
    cv::Point2f center( (float)src.cols / 2, (float)src.rows / 2 );
    double maxRadius = 0.7*min(center.y, center.x);
    int flags = cv::INTER_LINEAR + cv::WARP_FILL_OUTLIERS;
    cv::linearPolar(src,dst,center,maxRadius,flags);

    // 显示图像
    cv::imshow("src",src);
    cv::imshow("dst",dst);
    cv::waitKey();
    cv::destroyAllWindows();
    return 0;
}

在这里插入图片描述

2)cv::logPolar:将图像重新映射到半对数极坐标空间。

void cv::logPolar(InputArray src,OutputArray dst,Point2f center,double M,int flags)

注意,这个函数已经过时,与函数**cv::warpPolar(src, dst, src.size(), center, maxRadius, flags+WARP_POLAR_LOG)**调用的结果相同。

#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>
using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/f1.jpg");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat dst;

    // 计算最半径
    cv::Point2f center( (float)src.cols / 2, (float)src.rows / 2 );
    double maxRadius = 0.7*min(center.y, center.x);
    int flags = cv::INTER_LINEAR + cv::WARP_FILL_OUTLIERS;
    cv::logPolar(src,dst,center,maxRadius,flags);

    // 显示图像
    cv::imshow("src",src);
    cv::imshow("dst",dst);
    cv::waitKey();
    cv::destroyAllWindows();
    return 0;
}

在这里插入图片描述

3)cv::warpPolar:将图像重新映射到极坐标或半对数坐标空间。

void cv::warpPolar(InputArray src,OutputArray dst,Size dsize,Point2f center,double maxRadius,int flags)

wrapPolar函数通过如下函数进行转换:

d s t ( ρ , ϕ ) = s r c ( x , y ) dst(\rho , \phi ) = src(x,y) dst(ρ,ϕ)=src(x,y)

其中:

I ⃗ = ( x − c e n t e r . x ,    y − c e n t e r . y ) ϕ = K a n g l e ⋅ angle ( I ⃗ ) ρ = { K l i n ⋅ magnitude ( I ⃗ ) d e f a u l t K l o g ⋅ l o g e ( magnitude ( I ⃗ ) ) i f    s e m i l o g \begin{array}{l} \vec{I} = (x - center.x, \;y - center.y) \\ \phi = Kangle \cdot \texttt{angle} (\vec{I}) \\ \rho = \left\{\begin{matrix} Klin \cdot \texttt{magnitude} (\vec{I}) & default \\ Klog \cdot log_e(\texttt{magnitude} (\vec{I})) & if \; semilog \\ \end{matrix}\right. \end{array} I =(xcenter.x,ycenter.y)ϕ=Kangleangle(I )ρ={ Klinmagnitude(I )Klogloge(magnitude(I ))defaultifsemilog

K a n g l e = d s i z e . h e i g h t / 2 Π K l i n = d s i z e . w i d t h / m a x R a d i u s K l o g = d s i z e . w i d t h / l o g e ( m a x R a d i u s ) \begin{array}{l} Kangle = dsize.height / 2\Pi \\ Klin = dsize.width / maxRadius \\ Klog = dsize.width / log_e(maxRadius) \\ \end{array} Kangle=dsize.height/2ΠKlin=dsize.width/maxRadiusKlog=dsize.width/loge(maxRadius)

在这里插入图片描述

线性与半对数映射:

极坐标映射可以是线性或半对数。 将WarpPolarMode之一添加到标志以指定极坐标映射模式。线性是默认模式。

半对数映射模拟了人类的“中心凹”视觉,与视力较弱的周围视觉相反,该视线允许很高的视线(中央视力)。

输出图像大小dsize选项如下:

  • 如果dsize <= 0的两个值(默认),则目标图像将具有(几乎)源边界圆的相同区域: d s i z e . a r e a ← ( m a x R a d i u s 2 ⋅ Π ) d s i z e . w i d t h = cvRound ( m a x R a d i u s ) d s i z e . h e i g h t = cvRound ( m a x R a d i u s ⋅ Π ) \begin{array}{l} dsize.area \leftarrow (maxRadius^2 \cdot \Pi) \\ dsize.width = \texttt{cvRound}(maxRadius) \\ dsize.height = \texttt{cvRound}(maxRadius \cdot \Pi) \\ \end{array} dsize.area(maxRadius2Π)dsize.width=cvRound(maxRadius)dsize.height=cvRound(maxRadiusΠ)
  • 如果只是dsize.height <= 0,则目标图像区域将与边界圆区域成比例,但按Kx * Kx缩放: d s i z e . h e i g h t = cvRound ( d s i z e . w i d t h ⋅ Π ) \begin{array}{l} dsize.height = \texttt{cvRound}(dsize.width \cdot \Pi) \\ \end{array} dsize.height=cvRound(dsize.widthΠ)
  • 如果dsize中的两个值均大于0,则目标图像将具有给定的大小,因此边界圆的面积将缩放为dsize。

参数说明如下:

参数名称 参数描述
src 输入图像
dst 输出图像,与src大小相同 。
dsize 指定输出图像大小,见前面描述
center 变换中心位置
maxRadius 要变换的边界圆的半径。 它也确定反幅度标度参数。
flags 插值方法的组合, InterpolationFlags + WarpPolarModeWARP_POLAR_LINEAR 为线性;加WARP_POLAR_LOG为半对数;加WARP_INVERSE_MAP 为反向变换。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/f1.jpg");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat dst1,dst2,dst3;

    cv::Point2f center( (float)src.cols / 2, (float)src.rows / 2 );
    double maxRadius = min(center.y, center.x);
    cv::warpPolar(src, dst1, src.size(),
                  center,maxRadius,cv::INTER_LINEAR + cv::WARP_POLAR_LINEAR);

    cv::warpPolar(dst1, dst2, src.size(),
                  center,maxRadius,cv::INTER_LINEAR+cv::WARP_INVERSE_MAP);

    cv::warpPolar(src, dst3, src.size(),
                  center,maxRadius,cv::INTER_LINEAR+cv::WARP_POLAR_LOG);

    cv::imshow("src",src);
    cv::imshow("linear",dst1);
    cv::imshow("linear-invert",dst2);
    cv::imshow("log",dst3);

    cv::waitKey();
    cv::destroyAllWindows();

    return 0;
}

在这里插入图片描述

注意:

  • 该函数无法就地操作。
  • 为了计算度数和角度(以度为单位),内部使用cartToPolar,因此可以从0到360度测量角度,精度约为0.3度。
  • 此函数使用重映射。 由于当前的实现限制,输入和输出图像的大小应小于32767x32767。

另外,如果需要从极坐标映射到计算原始坐标映射,可以按如下操作:

		double angleRad, magnitude;
        double Kangle = dst.rows / CV_2PI;
        angleRad = phi / Kangle;
        if (flags & WARP_POLAR_LOG)
        {
            double Klog = dst.cols / std::log(maxRadius);
            magnitude = std::exp(rho / Klog);
        }
        else
        {
            double Klin = dst.cols / maxRadius;
            magnitude = rho / Klin;
        }
        int x = cvRound(center.x + magnitude * cos(angleRad));
        int y = cvRound(center.y + magnitude * sin(angleRad));

猜你喜欢

转载自blog.csdn.net/wujuxKkoolerter/article/details/112185970