图像金字塔
图像的金字塔是储存图像的一种数据结构,它是一系列图像的集合,都来源于同一个源图像,并通过向上采样形成一系列依次减小的图像序列。金字塔分为M型金字塔(矩阵型金字塔)和T型金字塔(树形金字塔),具体不在这里展开。这里主要探讨通过OpenCV用金字塔进行图像的缩放。OpenCV中有两个函数pyrUp和pyrDown,就是利用高斯金字塔实现图像的缩放。
pyrDown即向下采样将图像缩小,既然要缩小,就要丢弃一些行和列,但是在丢弃之前,要先用5×5的高斯内核卷积,然后再丢弃偶数行和列,这样图像的rows和cols各减少一半,即面积变成原来的1/4。之所以要用到卷积,其实就是在进行采样,按照我的通俗理解,就是不能把丢弃的行和列中的信息全部丢掉,而是在卷积的过程中对后来形成的像素有一定的贡献,这样得到的缩小图片将更为科学。
pyrUp则是将图像放大,与上面的过程相反,先隔行和隔列插入全0行和全0列,然后再用高斯卷积,其中滤波器乘以4做插值。故得到面积为原来4倍的图像。
下面是实现代码:
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using std::cin;
using namespace cv;
int main()
{
Mat src,dst;
src=imread("1.jpg");
imshow("original", src);
int c;
while (true)
{
c=waitKey();
if ((char)c=='u')
{
pyrUp(src,dst, Size(src.rows*2, src.cols*2));
imshow("pyramid up", dst);
}
else if ((char)c=='d')
{
pyrDown(src,dst,Size(src.rows/2,src.cols/2));
imshow("pyramid down", dst);
}
else break;
src=dst;
}
return 0;
}
有一点值得注意的是,在while loop中使用waitKey接受的按键输入,而不是cin,如果用cin接受输入则会出现图片窗口没法显示图像,原因我还不清楚。但是在有图片窗口的时候用waitKey应该是一个good practice,其返回值为int型,所以在与字符比较时最好先强制转化为char型(我试过不转也可以)。
resize
resize函数也同样可以改变图片尺寸,而且不像金字塔缩放需要长和宽都能被缩放倍数整除,大小可以任意改变。其基本的缩放原理也是插值法,不过实现过程是逆向的,即不是求从源图片到目标图片的映射,而是反过来求目标图片到原图片的映射,这样就可以减少信息丢失,对于目标图片上的每一点,都可以找到原图片上某些点转化过来的映射。OpenCV的documentation是这样说的:
In fact, to avoid sampling artifacts, the mapping is done in the reverse order, from destination to the source. That is, for each pixel of
the destination image, the functions compute coordinates of the corresponding “donor” pixel in the source image and copy the pixel value:
In case when you specify the forward mapping ,
the OpenCV functions first compute the corresponding inverse mapping
and
then use the above formula.
其函数原型是这样的:void resize(InputArray src, OutputArray dst, Size dsize, double fx, double fy, int interpolation=INTER_LINEAR)。 dsize即目标图片的大小, fx和fy则为缩放的比例,二者不能同时为0, 给出其中一个另一个就可以计算出来, interpolation则表示插值的方法,如果希望缩小图片,则使用INTER_AREA比较好,如果希望放大图片,则用INTER_CUBIC或者INTER_LINEAR比较好。下面是我自己写的一个将图片长宽都缩小一半的程序:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>
using namespace std;
using namespace cv;
int main()
{
string srcname;
freopen("input.txt", "r", stdin);
while (cin>>srcname) //可以批量处理一系列图片
{
Mat src=imread(srcname.c_str()), dst;
resize(src, dst, Size(), 0.5, 0.5, CV_INTER_AREA);
string dstname=srcname+"s.jpg";
imwrite(dstname.c_str(), dst);
}
return 0;
}