的几何操作,即来自三维几何和投影几何的变换。 此操作包括调整均匀和不均匀的大小。 后者被称为扭曲。 这些操作有很多原因。 例如,扭曲或旋转图像,以便可以将图像重叠到现有场景中,或者人为放大一系列用于对象识别的训练图像。 拉伸、压缩、扭曲、旋转图像等功能称为几何变换。 平面区域包括两种类型的几何变换:使用23矩阵的变换(称为仿射变换)和仿射变换。 基于33矩阵进行变换,这称为透视变换。
仿射变换是可以用矩阵乘法和矢量加法的形式表现的变换。 在OpenCV中,表示该变换的标准样式是23矩阵。 定义:
可以容易看出仿射变换AXB的效果等同于将向量x扩展到向量x’并将x’简单地左乘t。
仿射变换可以表示如下。 平面上的任意平行四边形ABCD可以通过几个仿射变换映射到其他任意平行四边形A'B'C'D '。 如果这些平行四边形的面积不为零,则隐式仿射变换由两个平行四边形的“三个顶点”唯一定义。 想象一下仿射变换。 在较大的橡胶片上描绘图像,通过变形可以形成不同种类的平行四边形。
如果多个图像是同一对象的不同视图,则需要计算与不同视图相关联的实际变换。 在这种情况下,经常使用仿射变换而不是透视变换来对视图建模。 这是因为参数少,容易解决。 缺点是,由于只能以单应性模拟现实视角的失真,因此基于仿射变换的显示无法满足视图之间的所有可能的关系。
仿射变换可以将矩形变换为平行四边形。 虽然可以挤出形状,但必须使两侧保持平行; 可以旋转或缩放。 透视变换提高了灵活性。透视变换可以将矩形变为任意四边形
仿射变换有两种情况。 在第一种情况下,有想要转换的图像; 在第二种情况下,有想要计算转换结果的点的列表。 这些情况在概念上非常相似,但实际上非常不同。 因此,OpenCV有两个不同的功能。
在第一种情况下,导入和导出的格式是图像,隐含的要求是假设像素是底层图像的密集表示。 也就是说,为了使输出图像看起来平滑自然,图像变形需要进行插值处理。 OpenCV提供的用于高密度变换的仿射变换函数是warpAffine (。
void cv :扭曲仿射(
cv :输入阵列存储器,
cv :输出阵列磁盘,
cv :输入阵列m、
cv :大小尺寸,
int flags=cv :3360互联网。
intbordermode=cv :3360边框_常数、
const cv :3360 scalarbordervalue=cv :3360 scalar () ) ) ) ) ) ) ) ) )。
);
其中,src和dst分别是源和目标的图像。 输入m是前面介绍的23矩阵,用于量化所需的变换。 目标图像的每个元素都根据原始图进行计算,如下所示:
但是,一般来说,该方程式右边所指的位置不是整数像素。 在这种情况下,必须使用插值为dst(x,y )找到适当的值。 另一个选择是WARP_INVERSE_MAP 此选项允许从dst到src的反向扭曲,而不是从src到dst的反向扭曲。 最后一个参数用于边界,其含义与图像卷积的参数相同。
OpenCV提供了两个帮助生成矩阵m的函数。 第一个是在已经有两个图像的情况下使用的,我们知道它们通过仿射变换相关或类似。
cv : matcv :3360 getaffinetransform (
const cv :点2f * src,
常数cv :点2f * DST
);
其中,src和dst是包含三个二维点的数组。 返回值是数组,是根据这些点计算的仿射变换。
示例1显示了使用这些函数的代码。 本示例首先构建两个三分量点,然后使用getAffineTransform ()将其转换为实际的变换矩阵,从而获取warpAffine ) )矩阵参数。 然后进行仿射变形,旋转图像。 对于源图像的典型点阵列,取(0,0 )、) 0、height-1 )和) width-1,0,0 )三个点。 然后,指定这些点映射到相应数组中的位置。
例1仿射变换的示意图
# #包括opencv2/opencv.HPP
# #包含iostream
单一名称空间固态硬盘;
单一名称空间光碟;
int main (英特尔航空(英特尔航空* *航空) ) ) ) ) ) ) ) ) ) ) )。
{
mat src=im读取(e : /仿射变换. jpg ),1 );
命名窗口(“原图”,0
); imshow("原图", src); Point2f srcTri[] = { Point2f(0,0), Point2f(src.cols - 1, 0), Point2f(0, src.rows - 1) }; Point2f dstTri[] = { Point2f(src.cols*0.f, src.rows*0.33f), Point2f(src.cols*0.85f, src.rows*0.25f), Point2f(src.cols*0.15f, src.rows*0.7f) }; // 计算仿射矩阵 Mat warp_mat = cv::getAffineTransform(srcTri, dstTri); Mat dst, dst2; warpAffine( src, dst, warp_mat, src.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar() ); for (int i = 0; i < 3; ++i) circle(dst, dstTri[i], 5, cv::Scalar(255, 0, 255), -1); namedWindow("仿射变换", 0); imshow("仿射变换", dst); waitKey(); for (int frame = 0;; ++frame) { Point2f center(src.cols*0.5f, src.rows*0.5f); double angle = frame * 3 % 360, scale = (cos((angle - 60)* CV_PI / 180) + 1.05)*0.8; Mat rot_mat = getRotationMatrix2D(center, angle, scale); warpAffine( src, dst2, rot_mat, src.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar() ); namedWindow("仿射变换图像", 0); imshow("仿射变换图像", dst2); if (waitKey(30) >= 0) break; } waitKey(0); return 0; }计算映射矩阵的第二种方法是使用cv :: getRotationMatrix2D(),它可以计算围绕某个任意点的旋转的映射矩阵,并结合可选的缩放比例。
cv::Mat cv::getRotationMatrix2D(
cv::Point2f center,
double angle,
double scale
);
第一个参数center是旋转的中心点。 接下来的两个参数给出了旋转的大小和缩放比例。 该函数返回映射矩阵M,它是浮点数的2×3矩阵。
warpAffine()是处理密集映射,对于稀疏映射,最好使用transform()。
void cv::transform(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray mtx
);
通常,src是具有Ds通道的N×1阵列,其中N是要变换的点的数量,Ds是这些源点的维度。 输出数组dst将具有相同的大小,但可能具有不同数量的通道Dd。 变换矩阵mtx是一个Ds×Dd矩阵,然后将其应用于src的每个元素,之后将结果放入dst。
给定以2×3矩阵表示的仿射变换,通常希望能够计算逆变换,其可用于将所有变换点“放回”到它们来自的地方。这是用invertAffineTransform()实现的:
void cv::invertAffineTransform(
cv::InputArray M,
cv::OutputArray iM
);
这个函数需要一个2×3的数组M,并返回另一个2×3的数组iM,它反转M。注意,cv :: invertAffineTransform()实际上不作用于任何图像,它只提供逆变换。一旦有了iM,就可以像使用M一样使用它,可以使用cv :: warpAffine()或cv :: transform()。
在实际应用中,可以利用仿射变换将倾斜的图像旋正。如下图所示,如果对于识别的字符是倾斜的,要进行字符分割可能不准确,尤其是分割算法要适应在线检测的大量图像的时候,这时,可以采用仿射变换将字符区域旋转到水平位置,这时再利用分割算法就可以适应大规模的图像分割。