一、flip()函数原型介绍
void cv::flip(InputArray src,OutputArray dst, int flipCode)
各参数含义
src:输入图像。
dst:输出图像。
flip:翻转方式标志。数值大于0,表示绕y轴进行翻转;数值等于0,表示绕x轴进行翻转;数值小于0,表示绕两个轴翻转。
以上就是OpenCV中flip()函数的原型,函数的功能和参数都比较简单,就是实现对图像进行一个翻转功能,得到翻转后的图像。
二、二维图形的几何变换
若我们不使用flip()函数如何实现图像的翻转。首先就必须了解二维图像的几何变换。使用齐次坐标表示点的变换非常方便。下面说明在实现图像翻转过程中使用的二维齐次坐标变换矩阵。
二维齐次坐标变换矩阵为3*3的矩阵,其形式为:
该矩阵中每个元素都有特殊含义。其中,2*2矩阵{a,b;c,d}可以对图像进行缩放、旋转、对称、和错切等变换;[c f]^T对图像进行平移变换;[g h]则是对图像进行投影变换;[i]则是对图像整体进行缩放变化。要实现图像的翻转,需要对图像进行对称变换和平移变换。只需要用到a,b,c,d,e,f六个元素。我们根据变换矩阵对平移变换和对称变换进行讨论,暂不涉及其它变换。
设对像素点P0(y0,x0)进行变换后得到的坐标为(y1,x1).
1:平移变换
以图像左上角为坐标原点对像素点P0(y0,x0)向y方向平移ty个像素单位,向x方向平移tx个像素单位。x1,y1表示平移后的坐标。则点P0与变换矩阵之间的计算为:
注:在下文中始终以图像或矩阵左上角作为坐标原点
2:对称变换
对称变换其实只是a,b,d,e取0、1、-1等这些特殊值产生的一些特殊效果。例如:
(1) 当b=d=0,a=-1,e=1时,有x1=-x0,y1=y0,产生与y轴对称的图形;
(2) 当b=d=0,a=1,e=-1时,有x1=x0,y1=-y0,产生与x轴对称的图形;
(3) 当b=d=0,a=e=-1时,有x1=-x0,y1=-y0,产生与原点对称的图形;
(4) 当b=d=1,a=e=0时,有x1=y0,y1=x0,产生与直线y=x对称的图形;
三、自定义flip函数的思路
图像由一个个像素组成,这些像素就形成了以像素为基本单位的二维矩阵。将图像进行翻转就是将这个二维矩阵进行翻转。我们先在一个大小为rows*cols矩阵A对元素p(y,x)进行关于x轴翻转的变换形成矩阵B。在关于x轴对称变换过程中x值保持不变,y值变为原值的相反数。在矩阵B中不存在坐标为(-y,x)的点,需要进行一个平移操作,将点向y轴方向平移rows个单位。得到点p(y,x)关于x轴翻转后对应坐标为(-y+rows,x)的点。确定好翻转后的坐标,最后进行赋值B[-y+rows][x]=A[y][x],就完成了这一个元素关于x轴的翻转。
对矩阵A的每一个元素都进行如上操作就可以实现矩阵A关于x轴的翻转得到矩阵B。
三、算法代码
#define xsymmetry 0//x轴对称
#define ysymmetry 1//y轴对称
#define xysymmetry -1//先以x对称,再以y轴对称
void coordinateTransform(int x0, int y0, int &x1, int &y1, int transformType, int x_offset, int y_offset)
{/*x0,y0为原图像中某一像素点的坐标
*x1,y1,为图像像素点(y,x)经过类型为transformType的变换后对应的坐标
*x_offset,y_offset分别为x(横)方向,y(纵)方向的偏移量
*/
if (transformType == xsymmetry)
{//x对称变换
x1 = x0;
y1 = -y0 + y_offset;
}
else if (transformType == ysymmetry)
{//y对称变换
x1 = -x0 + x_offset;
y1 = y0;
}
else if (transformType == xysymmetry)
{//先x轴再y轴对称(原点对称)
x1 = -x0 + x_offset;
y1 = -y0 + y_offset;
}
}
void myFlip(const InputArray &src, OutputArray &dst, int symmetryType)
{
int dataType = src.type();
vector<Mat> imgs;
vector<Mat> imgs1;
split(src, imgs);//先将初始图像进行通道分离
//图像的宽度和高度
int width = imgs[0].cols;
int height = imgs[0].rows;
int type = imgs[0].type();
int x, y;//原图像中像素点的坐标,(以图像左上角为原点)
int x1, y1;//像素点(y,x)变换后对应的坐标
for (int t = 0;t < src.channels();t++)
{
//定义一个与输入图像src大小一致的Mat变量用于保存翻转后单个通道形成的图像
Mat tempImg(Size(width,height),type);
for (y = 0;y < height;y++)
{
for (x = 0;x < width;x++)
{
coordinateTransform(x, y, x1, y1, symmetryType, width, height);
*(tempImg.data + tempImg.step[0] * y + tempImg.step[1] * x) =
*(imgs[t].data + imgs[t].step[0] * y1 + imgs[t].step[1] * x1);
}
}
imgs1.push_back(tempImg);
}
merge(imgs1, dst);//合并通道
}
多通道图像先进行通道分离,再对每个通道都要进行翻转变换,最后合并成翻转后的完整图像。
直接使用像素坐标进行元素访问,避免了对Mat变量类型的讨论。其它访问Mat元素的方式都需要明确数据类型。
四、自定义函数与库函数flip()的效果对比
int main()
{
Mat img = imread("../lena.png", IMREAD_GRAYSCALE);
Mat img0 = imread("../lena.png");
if (img.empty())
{
cout << "打开图像文件失败,请确认文件名称是否正确" << endl;
return -1;
}
Mat img_x, img_y, img_xy;//灰度图像
Mat img_x0, img_y0, img_xy0;//彩色图像
//
Mat myimg_x, myimg_y, myimg_xy;//灰度图像
Mat myimg_x0, myimg_y0, myimg_xy0;//彩色图像
//灰度图像的翻转操作
flip(img, img_x, 0);
flip(img, img_y, 1);
flip(img, img_xy, -1);
myFlip(img, myimg_x, 0);
myFlip(img, myimg_y, 1);
myFlip(img, myimg_xy, -1);
//彩色图像的翻转操作
flip(img0, img_x0, 0);//翻转 x轴对称
flip(img0, img_y0, 1);//翻转 y轴对称
flip(img0, img_xy0, -1);//翻转 先x轴对称再y轴对称
myFlip(img0, myimg_x0, 0);//翻转 x轴对称
myFlip(img0, myimg_y0, 1);//翻转 x轴对称
myFlip(img0, myimg_xy0, -1);//翻转 x轴对称
//灰度图像的显示
imshow("img", img);
imshow("myimg_x", myimg_x);
imshow("myimg_y", myimg_y);
imshow("myimg_xy", myimg_xy);
imshow("img_x", img_x);
imshow("img_y", img_y);
imshow("img_xy", img_xy);
//彩色图像显示
imshow("img0", img0);
imshow("img_x0", img_x0);
imshow("myimg_x0", myimg_x0);
imshow("myimg_y0", myimg_y0);
imshow("myimg_xy0", myimg_xy0);
imshow("img_y0", img_y0);
imshow("img_xy0", img_xy0);
waitKey();
return 0;
}
原灰度图和原彩色图
关于x轴翻转后的图像灰度图像(以my开头的是自定义函数myFlip()的测试效果)
文章来源:https://www.toymoban.com/news/detail-765303.html
关于x轴翻转再关于y轴翻转后的彩色图像(以my开头的是自定义函数myFlip()的测试效果)
效果一致,测试成功。文章来源地址https://www.toymoban.com/news/detail-765303.html
到了这里,关于OpenCV中flip函数实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!