标签:style blog http color ar 使用 for strong sp
图像的几何变换是在不改变图像内容的前提下对图像像素的进行空间几何变换,主要包括了图像的平移变换、镜像变换、缩放和旋转等。本文首先介绍了图像几何变换的一些基本概念,然后再OpenCV2下实现了图像的平移变换、镜像变换、缩放以及旋转,最后介绍几何的组合变换(平移+缩放+旋转)。
图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算:
对于第一种计算,只要给出原图像上的任意像素坐标,都能通过对应的映射关系获得到该像素在变换后图像的坐标位置。将这种输入图像坐标映射到输出的过程称为“向前映射”。反过来,知道任意变换后图像上的像素坐标,计算其在原图像的像素坐标,将输出图像映射到输入的过程称为“向后映射”。但是,在使用向前映射处理几何变换时却有一些不足,通常会产生两个问题:映射不完全,映射重叠
要解决上述两个问题可以使用“向后映射”,使用输出图像的坐标反过来推算改坐标对应于原图像中的坐标位置。这样,输出图像的每个像素都可以通过映射关系在原图像找到唯一对应的像素,而不会出现映射不完全和映射重叠。所以,一般使用向后映射来处理图像的几何变换。从上面也可以看出,向前映射之所以会出现问题,主要是由于图像像素的总数发生了变化,也就是图像的大小改变了。在一些图像大小不会发生变化的变换中,向前映射还是很有效的。
对于数字图像而言,像素的坐标是离散型非负整数,但是在进行变换的过程中有可能产生浮点坐标值。例如,原图像坐标(9,9)在缩小一倍时会变成(4.5,4.5),这显然是一个无效的坐标。插值算法就是用来处理这些浮点坐标的。常见的插值算法有最邻近插值法、双线性插值法,二次立方插值法,三次立方插值法等。本文主要介绍最邻近插值和双线性插值,其他一些高阶的插值算法,以后再做研究。
图像的平移变换就是将图像所有的像素坐标分别加上指定的水平偏移量和垂直偏移量。平移变换根据是否改变图像大小分为两种
左边平移图像的大小发生了,在保证图像平移的同时,也保存了完整的图像信息。右边的平移图像大小没有变化,故图像右下角的部分被截除了。
设dx为水平偏移量,dy为垂直偏移量,(x0,y0)为原图像坐标,(x,y)为变换后图像坐标,则平移变换的坐标映射为
这是向前映射,即将原图像的坐标映射到变换后的图像上。
其逆变换为
,向后映射,即将变换后的图像坐标映射到原图像上。在图像的几何变换中,一般使用向后映射。
图像的平移变换实现还是很简单的,这里不再赘述.
平移后图像的大小不变
void GeometricTrans::translateTransform(cv::Mat const& src, cv::Mat& dst, int dx, int dy) { CV_Assert(src.depth() == CV_8U); const int rows = src.rows; const int cols = src.cols; dst.create(rows, cols, src.type()); Vec3b *p; for (int i = 0; i < rows; i++) { p = dst.ptr<Vec3b>(i); for (int j = 0; j < cols; j++) { //平移后坐标映射到原图像 int x = j - dx; int y = i - dy; //保证映射后的坐标在原图像范围内 if (x >= 0 && y >= 0 && x < cols && y < rows) p[j] = src.ptr<Vec3b>(y)[x]; } } }
平移后图像的大小变化
void GeometricTrans::translateTransformSize(cv::Mat const& src, cv::Mat& dst, int dx, int dy) { CV_Assert(src.depth() == CV_8U); const int rows = src.rows + abs(dy); //输出图像的大小 const int cols = src.cols + abs(dx); dst.create(rows, cols, src.type()); Vec3b *p; for (int i = 0; i < rows; i++) { p = dst.ptr<Vec3b>(i); for (int j = 0; j < cols; j++) { int x = j - dx; int y = i - dy; if (x >= 0 && y >= 0 && x < src.cols && y < src.rows) p[j] = src.ptr<Vec3b>(y)[x]; } } }
ps:这里图像变换的代码以三通道图像为例,单通道的于此类似,代码中没有做处理。
图像的镜像变换分为两种:水平镜像和垂直镜像。水平镜像以图像垂直中线为轴,将图像的像素进行对换,也就是将图像的左半部和右半部对调。垂直镜像则是以图像的水平中线为轴,将图像的上半部分和下班部分对调。效果如下:
设图像的宽度为width,长度为height。(x,y)为变换后的坐标,(x0,y0)为原图像的坐标
水平镜像的实现
void GeometricTrans::hMirrorTrans(const Mat &src, Mat &dst) { CV_Assert(src.depth() == CV_8U); dst.create(src.rows, src.cols, src.type()); int rows = src.rows; int cols = src.cols; switch (src.channels()) { case 1: const uchar *origal; uchar *p; for (int i = 0; i < rows; i++){ origal = src.ptr<uchar>(i); p = dst.ptr<uchar>(i); for (int j = 0; j < cols; j++){ p[j] = origal[cols - 1 - j]; } } break; case 3: const Vec3b *origal3; Vec3b *p3; for (int i = 0; i < rows; i++) { origal3 = src.ptr<Vec3b>(i); p3 = dst.ptr<Vec3b>(i); for(int j = 0; j < cols; j++){ p3[j] = origal3[cols - 1 - j]; } } break; default: break; } }
分别对三通道图像和单通道图像做了处理,由于比较类似以后的代码只处理三通道图像,不再做特别说明。
在水平镜像变换时,遍历了整个图像,然后根据映射关系对每个像素都做了处理。实际上,水平镜像变换就是将图像坐标的列换到右边,右边的列换到左边,是可以以列为单位做变换的。同样垂直镜像变换也如此,可以以行为单位进行变换。
垂直镜像变换
void GeometricTrans::vMirrorTrans(const Mat &src, Mat &dst) { CV_Assert(src.depth() == CV_8U); dst.create(src.rows, src.cols, src.type()); int rows = src.rows; for (int i = 0; i < rows; i++) src.row(rows - i - 1).copyTo(dst.row(i)); }
src.row(rows - i - 1).copyTo(dst.row(i));
上面一行代码是变换的核心代码,从原图像中取出第i行,并将其复制到目标图像。
顶不住了啊,写理论部分太痛苦了啊,明天继续几何变换的后续几种:转置、缩放、旋转以及组合变换。
OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)
标签:style blog http color ar 使用 for strong sp
原文地址:http://www.cnblogs.com/wangguchangqing/p/4039095.html