OpenCV图像金字塔
OpenCV计算机视觉开发实践:基于Qt C++ - 商品搜索 - 京东
学习基于Qt C++开发OpenCV应用,建议先掌握Qt C++编程。
《Qt 6.x从入门到精通》(朱文伟)【摘要 书评 试读】- 京东图书
图像金字塔是一种以多个分辨率来表示图像的有效且概念简单的结构。图像金字塔最初用于机器视觉和图像压缩,一个图像金字塔是一系列以金字塔形状排列、分辨率逐步降低的图像集合。
图像金字塔背后的思想是,在一种分辨率下可能无法检测到的特征,可以在其他分辨率下轻松检测到。例如,如果感兴趣区域尺寸较大,则低分辨率图像或粗略视图就足够了;而对于小物体,以高分辨率检查它们是有意义的。如果图像中同时存在大型和小型物体,以多种分辨率分析图像可能是有益的。
10.1 图像金字塔概述
一般情况下,我们要处理的是具有固定分辨率的图像。但是在有些情况下,我们需要对同一图像的不同分辨率的子图像进行处理。比如,需要在一幅图像中查找某个目标,如脸,我们不知道目标在图像中的大小。在这种情况下,我们需要创建一组图像,这些图像是具有不同分辨率的原始图像。我们把这组图像叫作图像金字塔(简单来说,就是同一图像的不同分辨率的子图集合)。如果把最大的图像放在底部,最小的放在顶部,看起来就像一座金字塔,故而得名图像金字塔,如图10-1所示。
图像金字塔的底部是原始图像,分辨率最高,而顶部则为其低分辨率的近似图像。通常情况下,每向上移动一级,图像的宽和高都降低为原来的二分之一。
图像金字塔最初用于机器视觉和图像压缩。一个图像金字塔是一系列以金字塔形状排列的、分辨率逐步降低的图像集合。如图10-2所示,它包括了四层图像。图像金字塔可以通过梯次向下采样获得,直到达到某个终止条件才停止采样。在向下采样中,层级越高,图像越小,分辨率越低。
生成图像金字塔主要有两种方式:向下采样和向上采样。
- 向下采样:将图像从G0转换为G1、G2、G3,图像分辨率不断降低。
- 向上采样:将图像从G3转换为G2、G1、G0,图像分辨率不断增大,如图10-3所示。
图10-3
图像金字塔有两种,第一种是高斯金字塔(Gaussian Pyramid),第二种是拉普拉斯金字塔(Laplacian Pyramid)。高斯金字塔用来向下采样,是主要的图像金字塔。拉普拉斯金字塔用来从金字塔低层图像重建上层未采样图像,在数字图像处理中即是预测残差,可以对图像进行最大程度的还原,它配合高斯金字塔一起使用。
两者的简单区别:高斯金字塔用来向下采样图像,而拉普拉斯金字塔则用来从金字塔底层图像中向上采样以重建图像。
10.2 高斯金字塔
高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像。最下面的图像分辨率最高,越往上图像分辨率越低。高斯金字塔的向下采样过程是:对于给定的图像先做一次高斯平滑处理,也就是使用一个卷积核对图像进行卷积操作,然后对图像取样,去除图像中的偶数行和偶数列,就得到一幅图片,对这幅图片再进行上述操作,就可以得到高斯金字塔。
OpenCV官方推荐的卷积核如图10-4所示。
图10-4
假设原先的图片的长和宽分别为M、N,经过一次取样后,图像的长和宽会分别变成[M+1]/2、[N+1]/2。由此可见,图像的面积变为了原来的1/4,图像的分辨率变成了原来的1/4。
我们可以使用函数pyrDown()和pyrUp()构建图像金字塔。函数pyrDown()构建从高分辨率到低分辨率的金字塔,也称向下采样。函数pyrUp()构建从低分辨率到高分辨率的金字塔(尺寸变大,但分辨率不变),也称向上采样。如果一幅图片经过下采样,那么图片的分辨率就会降低,图片里的信息就会损失。向上采样与向下采样的过程相反,向上采样先在图像中插入值为0的行与列,再进行高斯模糊。高斯模糊所使用的卷积核等于向下采样中使用的卷积核乘以4,也就是上面给出的卷积核乘以4。
(详见图书相关内容,主要看下边的代码示例)
10.3 普拉斯金字塔
拉普拉斯金字塔可以从高斯金字塔计算得来。OpenCV 4版本中的拉普拉斯金字塔位于imgproc模块的Image Filtering子模块中。拉普拉斯金字塔主要应用于图像融合。
拉普拉斯金字塔是高斯金字塔的修正版,目的是还原到原图。它通过计算残差图来达到还原。拉普拉斯金字塔第i层的数学定义如下:
L(i)=G(i) - PyrUp(G(i+1))
将向下采样之后的图像再进行向上采样操作,然后与之前还没向下采样的原图做差,以得到残差图,为还原图像做信息的准备。也就是说,拉普拉斯金字塔是通过原图像减去先缩小再放大的图像的一系列图像构成的,保留的是残差。在OpenCV中拉普拉斯金字塔的函数原型:
CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
其中参数src表示原图;dst表示目标图像;ddepth表示目标图像的深度;ksize表示用于计算二阶导数滤波器的孔径大小,大小必须为正数和奇数;scale用于计算拉普拉斯值的可选比例因子,默认情况下,不应用缩放;delta表示在将结果存储到dst之前添加到结果中的可选增量值;borderType用于决定在图像发生几何变换或者滤波操作(卷积)时边缘像素的处理方式。
【例10.4】实战拉普拉斯
打开Qt Creator,新建一个控制台工程,工程名是test。
打开main.cpp,输入如下代码:
#include <iostream>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui_c.h>
using namespace std;
using namespace cv;// 拉普拉斯边缘计算
void TLaplacian()
{Mat img1, img2, gray_img, edge_img;const char* win1 = "window1";const char* win2 = "window2";const char* win3 = "window3";const char* win4 = "window4";namedWindow(win1, CV_WINDOW_AUTOSIZE);namedWindow(win2, CV_WINDOW_AUTOSIZE);namedWindow(win3, CV_WINDOW_AUTOSIZE);namedWindow(win4, CV_WINDOW_AUTOSIZE);img1 = imread("img.jpg"); // 读取图片if (img1.empty())
{cout << "could not found image" << endl;return;
}// 高斯模糊,去掉噪点GaussianBlur(img1, img2, Size(3, 3), 0, 0);// 转为灰度图cvtColor(img2, gray_img, CV_BGR2GRAY);// 拉普拉斯Laplacian(gray_img, edge_img, CV_16S, 3);convertScaleAbs(edge_img, edge_img);threshold(edge_img, edge_img, 2, 255, THRESH_OTSU | THRESH_BINARY);imshow(win1, img1);imshow(win2, img2);imshow(win3, gray_img);imshow(win4, edge_img);
}int main()
{TLaplacian();waitKey(0);return 0;
}
在上述代码中,我们利用函数Laplacian进行了拉普拉斯边缘计算。
保存工程并运行,结果如图10-12所示。
图10-12