opencv图像的采样和量化过程,OpenCV对数字图像进行仿射变换
opencv图像的采样和量化过程,OpenCV对数字图像进行仿射变换平移操作如下式所示,其中(tx ty)T 是平移向量。仿射变换通过一系列原子变换复合实现,具体包括:平移(Translation)、缩放(Scale)、旋转(Rotation)、翻转(Flip)和错切(Shear)。对应的齐次坐标矩阵表示形式为:式3. 仿射变换的齐次坐标形式仿射变换保持了图像的“平直性”(直线经仿射变换后依然为直线)和“平行性”(直线之间的相对位置关系保持不变,平行线经仿射变换后依然为平行线,且直线上点的位置顺序不会发生变化)。非共线的三对对应点确定一个唯一的仿射变换。
仿射变换(Affine Transformation 或Affine Map)是一种二维坐标(x y)到二维坐标 (u v)的线性变换。图像处理中,可应用仿射变换对二维图像进行平移、缩放、旋转等操作。仿射变换的数学表达式如下:
式1. 仿射变换基本公式
写成矩阵形式如下式所示:
式2: 仿射变换矩阵形式
对应的齐次坐标矩阵表示形式为:
式3. 仿射变换的齐次坐标形式
仿射变换保持了图像的“平直性”(直线经仿射变换后依然为直线)和“平行性”(直线之间的相对位置关系保持不变,平行线经仿射变换后依然为平行线,且直线上点的位置顺序不会发生变化)。非共线的三对对应点确定一个唯一的仿射变换。
仿射变换通过一系列原子变换复合实现,具体包括:平移(Translation)、缩放(Scale)、旋转(Rotation)、翻转(Flip)和错切(Shear)。
平移操作如下式所示,其中(tx ty)T 是平移向量。
式4. 平移变换公式
缩放操作如下式所示,其中Sx ,Sy 是缩放系数:
式5. 缩放操作变换公式
旋转操作公式如下式所示,其中 θ是旋转角度:
式6. 旋转操作变换公式
翻转操作将图像上下左右颠倒,如下式所示:
式7. 翻转操作变换公式
错切操作如下式所示,包含水平错切和垂直错切,常用于产生弹性物体的变形处理:
式8. 错切变换公式
基本的原子变换可以级联进行,连续多个变换可借助于矩阵的相乘最后用一个单独的3×3变换矩阵来表示,最后用到的实际上是3×3的前两行的2×3矩阵。
OpenCV中使用warpAffine()函数来进行仿射变换,其函数原型为:
void warpAffine(
InputArray src //输入图像
OutputArray dst //输出图像,位深度与图像一致
InputArray M //仿射变换矩阵
Size dsize //输出图像大小
int flags = INTER_LINEAR //像素插值方法,默认是双线性插值
int borderMode = BORDER_CONSTANT //边界扩充方法,默认取常数
const Scalar& borderValue = Scalar() //边界填充值
);
在warpAffine()函数当中,目标图像dst中的每个像素值都是从源图像中计算得到的,其计算公式如式(2)所示。一般来说,仿射变换之后得到的坐标值都不是整数,这就需设置 flags标记,对源图像相邻像素插值从而得到目标像素的值。若flags设置为cv::WARP_INVERSE_MAP,则表示从dst到src的反向仿射变换。
仿射变换的关键是确定仿射变换矩阵M,这里有几种方法可以确定。如果已知变换前后的多组特征点的坐标,矩阵M可以用cv::getAffineTransform()来确定,这里需要输入变换前后的3个点对;如果知道图像的旋转角度和缩放比例,则可以利用函数cv::getRotationMatrix2D()来确定;如果知道确切的仿射变换类型,则也可以根据式(4)到式(8)直接写出矩阵M。
cv::getAffineTransform()函数的原型为:
Mat getAffineTransform(
const Point2f src[] //变换前3个点的坐标
const Point2f dst[] //变换后3个点的坐标
);
cv::getRotationMatrix2D()函数的原型为:
Mat getRotationMatrix2D(
Point2f center //旋转中心
double angle //旋转角度
double scale //缩放比例
);
仿射变换实例
void ImageAffineTransform()
{
//加载原图像
Mat src = IMREAD("fruits.png" IMREAD_COLOR);
cv::Point2f srcTri[] = {
cv::Point2f(0 0) //原图像左上角坐标
cv::Point2f(src.cols - 1 0) //原图像右上角坐标
cv::Point2f(0 src.rows - 1) //原图像左下角坐标
};
cv::Point2f dstTri[] = {
//目标图像左上角坐标
cv::Point2f(src.cols*0.f src.rows*0.33f)
//目标图像右上角坐标
cv::Point2f(src.cols*0.85f src.rows*0.25f)
//目标图像左下角坐标
cv::Point2f(src.cols*0.15f src.rows*0.7f)
};
//由3组点对得到仿射变换矩阵
cv::Mat m1 = cv::getAffineTransform(srcTri dstTri);
cv::Mat dst1;
cv::warpAffine(src dst1 m1 src.size());//第1种仿射变换
cv::imshow("Image1" dst1);
float scale = 1.1;
float angle = -15;
float radian = (float)(angle / 180.0 * CV_PI); //将角度转换到弧度
//计算图像旋转之后包含图像的最大的矩形
float sinVal = abs(sin(radian))*scale; //sinθ
float cosVal = abs(cos(radian))*scale; //cosθ
//目标图像大小
int dstWidth = int(src.cols * cosVal src.rows * sinVal);
int dstHeight = int(src.cols * sinVal src.rows * cosVal);
//旋转中心位于目标图像中心
cv::Point2f center((float)(dstWidth / 2) (float)(dstHeight / 2));
//求得仿射变换矩阵
cv::Mat m2 = getRotationMatrix2D(center angle scale);
int dx = (dstWidth - src.cols) / 2;
int dy = (dstHeight - src.rows) / 2;
cv::Mat dst2; //dst是旋转之后的图像
//将图像拷贝到目标图像中心,并扩大图像
copyMakeBorder(src dst2 dy dy dx dx BORDER_CONSTANT);
//就地执行仿射变换 第2种仿射变换
warpAffine(dst2 dst2 m2 Size(dstWidth dstHeight));
imshow("image2" dst2);
//仿射矩阵 由缩放操作和错切操作组成
cv::Matx23f m3(1.5 0.5 10 0.1 1 10);
//直接用公式计算仿射变换后图像大小
int nDstWid3 = int(m3(0 0)*src.cols m3(0 1)*src.rows m3(0 2));
int nDstDep3 = int(m3(1 0)*src.cols m3(1 1)*src.rows m3(1 2));
Size dstSize3 = Size(nDstWid3 nDstDep3);
Mat dst3;
warpAffine(src dst3 m3 dstSize3); //第3种仿射变换
imshow("image3" dst3);
waitKey(0);
}
程序运行结果如下列图像所示:
原图像
第1种仿射变换结果
第2种仿射变换结果
第3种仿射变换结果