实验 4.1:图像变形
•记[x’, y’]=f([x, y])为像素坐标的一个映射,实现 f 所表示的图像形变,并采
用双线性插值进行重采样。f 的逆映射为:
问题一:用怎样的方式比较方便实现转换?
解决:为了计算每个像素在源图像中的位置,需要将当前像素在目标图像中
的位置(i,j)转换为其在源图像中的位置(
srcx,srcy)。这里的变形使用了极坐标变换的方法,可以将 x、y 坐标转换为半径和角度。
变量 tempx 和 tempy 用来计算当前像素在目标图像中的位置。这里的 0.5
是为了将像素位置的范围缩小到[-1,1]之间,以便于进行极坐标变换。变量 r
表示当前像素在目标图像中到中心点的距离,即半径;angle 表示当前像素的角
度。
问题二:将上述转换公式转换代码。
解决:对于半径超过 1 的像素,直接使用目标像素的位置,而对于半径在 1
以内的像素,需要通过计算得到其在源图像中的位置。这里使用了旋转变换,将
目标图像中的像素转换为源图像中的像素。
通过将 srcx 和 srcy 转换为对应的源图像位置 sx 和 sy,将源图像中的像素
复制到 dest 矩阵中对应的位置上。
问题三:怎样进行双线性插值
解决:双线性插值是一种基于周围四个像素点的灰度值来推算中心像素点灰
度值的插值方法。假设我们需要将一个原始图像缩小到目标图像的大小,并且原
始图像中的一个像素点(x,y)要映射到目标图像的位置(x',y')上。这时我们首先
需要确定原始图像中的四个像素点(p1, p2, p3, p4),它们是离目标像素点
(x',y')最近的四个像素点,接着,我们需要根据距离和灰度值的加权平均来计
算目标像素点(x',y')的灰度值。具体地,我们先根据距离分别计算出目标像素
点(x',y')与原始图像四个像素点的距离 d1, d2, d3, d4,然后按照这四个像素
点的距离对它们的灰度值进行加权平均,得到目标像素点(x',y')的灰度值,最
后,将四个加权平均值相加即可得到目标像素点的灰度值。
仿照实验 4.2,自己设计变换函数,对输入视频进行变换,生成哈哈镜的效果。
–采用 cv::VideoCapture 读取摄像头视频,并进行实时处理和显示结果。
–优化代码执行效率,改善实时性(不要忘了打开编译优化,vc 请用 release
模式编译)。
–实验报告可以录制一个视频一起提交,不用太长,大小不要超过 10M(可以采
用 cv::VideoWriter 保存视频)。
问题三:怎么模仿实验 4.1 实现哈哈镜效果
解决:由于哈哈镜是非常搞笑的,扭曲程度非常大,所以可以选择一个夸张的扭曲公式进行变换.
这里的 for 循环迭代图像中的每个像素,根据像素的坐标计算出变换后的坐
标。x 和 y 是像素在图像中的位置,将其转换为以中心为原点的极坐标,计算出
半径 r 和极角 theta。然后通过变换函数 r_new = r * (1 + k * cos(3 * theta))
计算出新的半径,最后将新坐标转换回笛卡尔坐标系并将其存储在 map_x 和
map_y 中。
问题四:怎么使用 cv::VideoWriter 录制保存视频
解决:从摄像头捕捉视频、对每一帧视频进行处理并输出到文件中,同时在
窗口中显示处理后的视频帧。其中需要注意的是要根据自己的需求设置输出视频
文件的编码器类型、帧率、宽度和高度等参数,并且要检查每一帧视频是否为空,
避免程序崩溃。
VideoWriter writer("output.avi", fourcc, fps, Size(width, height),
true);
创建一个名为 writer 的 VideoWriter 对象,将要输出的文件名为
"output.avi"。
fourcc 是四个字符代码,表示视频编码器的类型,常用的有 XVID 和 MJPG
等。该代码中没有给出 fourcc 的定义,因此需要在其他地方定义该变量。
fps 表示视频的帧率,即每秒显示的帧数。
Size(width, height) 指定了视频帧的宽度和高度。
true 表示输出的视频文件是彩色的。
if (!writer.isOpened())
判断视频文件是否成功打开,如果没有成功打开则输出错误信息并结束程
序。
namedWindow("HaHa Mirror", WINDOW_AUTOSIZE);
创建一个名为 "HaHa Mirror" 的窗口,使用 WINDOW_AUTOSIZE 标志自动调
整窗口大小以适应图像大小。
Mat frame, result;
定义两个 Mat 对象,用于存储原始帧和处理后的结果帧。
`float k = 0 while (waitKey(1) != 'q')
循环读取每一帧视频,直到用户按下键盘上的 "q" 键退出循环。
waitKey(1) 表示等待 1 毫秒,如果用户按下了某个键,返回该键的 ASCII
码;否则返回-1。
cap >> frame;
从摄像头捕捉一帧视频并存储到 frame 中。
if (frame.empty())
判断读取的视频帧是否为空,如果为空则输出错误信息并退出循环。
fun(frame, result, k);
将读取的视频帧 frame 传递给 fun 函数进行处理,结果存储到 result
中。
writer.write(result);
将处理后的结果帧 result 写入输出视频文件中。
imshow("HaHa Mirror", result);
在名为 "HaHa Mirror" 的窗口中显示处理后的结果帧 result。结果分析与体会:
实验 4.1 结果:
下面的是实验要求的原图:
下面是图像经过变换后的结果,可以发现在中心 1 半径外的像素未变,而靠
近中心的像素都经过了一定角度的扭曲:
实验 4.2 结果:
下面是从结果视频中截取的几张图,可以发现,实现的哈哈镜效果是比较夸
张的,很搞笑:
1. 第一个实验是一个基本的图像处理任务,需要对一张图片进行旋转、缩
放和裁剪等操作。该实验主要考察对 OpenCV 库的使用以及对基本图像处理方法
的掌握程度。通过这个实验可以学习到如何使用 OpenCV 库中的函数对图像进行
操作,并且可以加深对图像处理的理解。最后,我们通过双线性插值来进行图像
的重采样,从而得到更加平滑的变形效果。
第二个实验是一个实时视频处理任务,需要对摄像头捕捉到的视频帧进行处
理,并将处理后的视频帧输出到一个.avi 格式的视频文件中,同时在窗口中实
时显示处理后的视频帧。该实验主要考察对 OpenCV 库中视频处理模块的使用以
及实时视频处理的能力。通过这个实验可以学习到如何使用 OpenCV 库中的视频
处理模块,并且可以加深对实时视频处理的理解。总体而言,实现哈哈镜的图像处理实验不仅可以帮助我们更加深入地理解图像变形操作的原理,还可以让我们
更加熟练地掌握相关的图像处理技术,为我们在日后的图像处理任务中提供更多
的思路和方法。同时,该实验还有一定的趣味性,可以带来一定的娱乐性和创意
性,让我们更加深入地体验到图像处理技术的魅力。
2.双线性插值是一种常用的图像缩放技术,可以将图像缩小或放大到目标大
小。其基本原理是通过周围的四个像素点进行加权平均来估计目标像素的灰度
值。下面将详细讲解双线性插值的原理和实现方法。
双线性插值原理
双线性插值的基本思想是在图像中找到距离目标像素最近的四个像素,然后
根据这四个像素的灰度值进行加权平均,得到目标像素的灰度值。这四个像素通
常被称为左上角像素、右上角像素、左下角像素和右下角像素,设目标像素在原
图像中的位置为(x,y),其中 x 和 y 为实数,表示其在原图像中的浮点坐标。则
该像素在目标图像中的位置为(x',y'),其中 x'和 y'也是实数,表示其在目标图
像中的浮点坐标。接下来我们需要找到距离目标像素最近的四个像素,然后根据
这四个像素的灰度值进行加权平均,计算目标像素的灰度值。
设左上角像素的坐标为(xi, yj),右下角像素的坐标为(xi+1, yj+1),则左
下角像素的坐标为(xi, yj+1),右上角像素的坐标为(xi+1, yj)。我们需要根据
这四个像素的灰度值来计算目标像素的灰度值。具体地,双线性插值的公式为:
其中 u 和 v 是距离目标像素最近的像素与目标像素之间的距离比例。它们的
计算公式如下:
可以看出,双线性插值的计算需要涉及到目标像素周围四个像素的坐标和灰
度值,以及目标像素在原图像中的坐标。因此,实现双线性插值需要对图像进行
逐像素的遍历和计算。
3.双线性插值的优缺点
双线性插值的优点是计算速度较快,且插值结果比较平滑,不会出现锯齿状
的现象。因此在图像缩放、图像旋转等操作中广泛应用。但双线性插值也有其缺点。首先,双线性插值只能在局部范围内进行插值计
算,因此在图像缩放比较大的情况下,可能会出现插值结果不够精确的问题。其
次,在图像存在大幅度变形时,双线性插值可能会产生比较明显的变形效果。因
此在一些对精度要求比较高的场景下,可能需要使用更为精确的插值算法。