一、尺度调整
顾名思义,即对源图像的尺寸进行放大或者缩小变换。在opencv里面可以用resize函数,将源图像精准地转化为指定尺寸的目标图像。要缩小图像,一般推荐使用CV_INETR_AREA(区域插值)来插值;若要放大图像,推荐使用CV_INTER_LINEAR(线性插值)。这个函数可以用来做简单的图像尺度变换。而下面要说的图像金字塔的用处很大,在特征检测中都是基础理论和技术;
Opencv里面的API介绍:
void resize(src,dst,size,int interpolation)
//src:源图像;dst:目标图像,
//size目标图像大小,可以是指定的尺寸或者放大缩小的比例
//指定插值方式,一般有四种插值方式可供选择,默认为线性插值法
二、图像金字塔(Image Pyramid)
图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。
常见两类图像金字塔
-
高斯金字塔 ( Gaussian pyramid): 用来向下/降采样,主要的图像金字塔
-
拉普拉斯金字塔(Laplacian pyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。
示例图形金字塔
两者的简要区别:高斯金字塔用来向下降采样图像,注意降采样其实是由金字塔底部向上采样,分辨率降低,它和我们理解的金字塔概念相反(注意);而拉普拉斯金字塔则用来从金字塔底层图像中向上采样重建一个图像。
要从金字塔第i层生成第i+1层(我们表示第i+1层为G_i+1),我们先要用高斯核对G_1进行卷积,然后删除所有偶数行和偶数列。新得到图像面积会变为源图像的四分之一。按上述过程对输入图像G_0执行操作就可产生出整个金字塔。
当图像向金字塔的上层移动时,尺寸和分辨率就降低。OpenCV中,从金字塔中上一级图像生成下一级图像的可以用PryDown。而通过PryUp将现有的图像在每个维度都放大两遍。
图像金字塔中的向上和向下采样分别通过OpenCV函数 pyrUp 和 pyrDown 实现。概括起来就是:
- 对图像向上采样:pyrUp函数
- 对图像向下采样:pyrDown函数
这里的向下与向上采样,是对图像的尺寸而言的(和金字塔的方向相反),向上就是图像尺寸加倍,向下就是图像尺寸减半。而如果我们按上图中演示的金字塔方向来理解,金字塔向上图像其实在缩小,这样刚好是反过来了。
但需要注意的是,PryUp和PryDown不是互逆的,即PryUp不是降采样的逆操作。这种情况下,图像首先在每个维度上扩大为原来的两倍,新增的行(偶数行)以0填充。然后给指定的滤波器进行卷积(实际上是一个在每个维度都扩大为原来两倍的过滤器)去估计“丢失”像素的近似值。PryDown( )是一个会丢失信息的函数。为了恢复原来更高的分辨率的图像,我们要获得由降采样操作丢失的信息,这些数据就和拉普拉斯金字塔有关系了。
2.1 高斯金字塔(Gaussian pyramid)
高斯金字塔是通过高斯平滑和亚采样获得一系列下采样图像,也就是说第K层高斯金字塔通过平滑、亚采样就可以获得K+1层高斯图像,高斯金字塔包含了一系列低通滤波器,其截至频率从上一层到下一层是以因子2逐渐增加,所以高斯金字塔可以跨越很大的频率范围。金字塔的图像如下:
另外,每一层都按从下到上的次序编号, 层级 G_i+1 (表示为 G_i+1尺寸小于第i层G_i)。
- 对图像的向下取样操作,即缩小图像。
为了获取层级为 G_i+1 的金字塔图像,方法步骤如下:
<1> 对图像G_i进行高斯内核卷积,进行高斯模糊;
<2> 将所有偶数行和列去除。
得到的图像即为G_i+1的图像,显而易见,结果图像只有原图的四分之一。通过对输入图像G_i(原始图像)不停迭代以上步骤就会得到整个金字塔。同时我们也可以看到,向下取样会逐渐丢失图像的信息。以上就是对图像的向下取样操作,即缩小图像。
- 对图像的向上取样,即放大图像
- 方法步骤如下:
<1> 将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
<2> 使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值
得到的图像即为放大后的图像,但是与原来的图像相比会发觉比较模糊,因为在缩放的过程中已经丢失了一些信息,如果想在缩小和放大整个过程中减少信息的丢失,这些数据形成了拉普拉斯金字塔。
要求:拉普拉斯金字塔时,图像大小必须是2的n次方*2的n次方,不然会报错
dst=cv2.pyrDown(src)
dst:取样结果
src:原始图像
import cv2 as cv
import numpy as np
#高斯金字塔
def pyramid_demo(image):
level = 3#金字塔的层数
temp = image.copy()#拷贝图像
pyramid_images = []
for i in range(level):
dst = cv.pyrDown(temp)
pyramid_images.append(dst)
cv.imshow("pyramid_down_"+str(i), dst)
temp = dst.copy()
return pyramid_images
src = cv.imread("lena.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
c=pyramid_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
截图如下:
高斯滤波器可以看做一个低通滤波器,那么每经过一次的高斯滤波,图像中仅能够保留某个频率值以下的频率部分,所以高斯金字塔也可以看做一个低通金字塔(每一级只保留某个频率以下的成分)。
2.2 拉普拉斯金字塔(Laplacian pyramid)
在进行高斯金字塔运算时,由于不断的进行高斯滤波和下采样,我们丢失了很多高频信号,而拉普拉斯金字塔的目的就是保存这些高频信号,保存这些高频信号所采用的方式就是保存差分图像。比如,拉普拉斯金字塔的第0层,就是原始图像和原始图像下采样(Reduce)后再次上采样(Expand)的图像的差值。
另外再提一点,关于图像金字塔非常重要的一个应用就是实现图像分割。图像分割的话,先要建立一个图像金字塔,然后在G_i和G_i+1的像素直接依照对应的关系,建立起”父与子“关系。而快速初始分割可以先在金字塔高层的低分辨率图像上完成,然后逐层对分割加以优化。
注意:上采样和下采样是非线性处理,不可逆,有损的处理!
结果=原始图像-先向下再向上
向下:尺寸变小
向上:尺寸变大
#图像金字塔
#高斯金字塔:reduce=2倍的高斯模糊+降采样(必须是一级一级来,不可越级);
#拉普拉斯金字塔:expand=扩大+卷积(升采样)
'''
图像金字塔方法的原理是:将参加融合的的每幅图像分解为多尺度的金字塔图像序列,将低分辨率的图像在上层,
高分辨率的图像在下层,上层图像的大小为前一层图像大小的1/4。层数为0,1,2……N。将所有图像的金字塔在
相应层上以一定的规则融合,就可得到合成金字塔,再将该合成金字塔按照金字塔生成的逆过程进行重构,得到融合金字塔。
'''
import cv2 as cv
import numpy as np
'''
拉普拉斯字塔:首先将原图像作为最底层图像G0(高斯金字塔的第0层),利用高斯核(5*5)对其进行卷积,
然后对卷积后的图像进行下采样(去除偶数行和列)得到上一层图像G1,将此图像作为输入,
重复卷积和下采样操作得到更上一层图像,反复迭代多次,形成一个金字塔形的图像数据结构
'''
def pyramid_demo(image):#高斯金字塔
level = 3
temp = image.copy()#复制一张
pyramid_images = []
for i in range(level):
dst = cv.pyrDown(temp)#降 API,原理在上方
pyramid_images.append(dst)#加入列表
#cv.imshow("pyramid_down_"+str(i), dst)
temp = dst.copy()#为temp赋新值
return pyramid_images
def lapalian_demo(image)::#拉普拉斯金字塔
pyramid_images = pyramid_demo(image)#首先得到高斯金字塔结果
level = len(pyramid_images)
for i in range(level-1, -1, -1):#从小图到大图
if (i-1) < 0 :#最后一层特殊处理
expand = cv.pyrUp(pyramid_images[i], dstsize=image.shape[:2])#内插放大后图像
lpls = cv.subtract(image, expand)#它的每一层L0图像是高斯金字塔本层G0图像与其高一层图像G1经内插放大后图像*G1的差
cv.imshow("lapalian_down_" + str(i), lpls)
else:
expand = cv.pyrUp(pyramid_images[i], dstsize=pyramid_images[i-1].shape[:2])
lpls = cv.subtract(pyramid_images[i-1], expand)
cv.imshow("lapalian_down_"+str(i), lpls)
#注意:图像的大小最好是2的n次方的倍数,否则结果会出问题,可以用resize调节大小
src = cv.imread("lena.png")#图必须要是2的N次倍数
#src=cv.resize(src,(512,512))
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
lapalian_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行截图:
注意图像尺寸必须是2的倍数,否则报错
三、图像梯度
sobel算子理论
如图,图中红色点区域,从头发到皮肤。开先处于头发区域,头发是黑色,像素点,然后处于皮肤,像素相对较高,得到下列图2,对图2求取一阶导数得到图3,可以发现边缘处导数最高。
一阶导数就是做差的意思。
算子和为0
laplacian算子理论
算子和为0
import cv2 as cv
import numpy as np
def sobel_demo(image):
grad_x = cv.Sobel(image, cv.CV_32F, 1, 0) #x和y方向的结果
grad_y = cv.Sobel(image, cv.CV_32F, 0, 1)
#Scharr算子是Sobel算子的增强版,轮廓更加明显
# grad_x = cv.Scharr(image, cv.CV_32F, 1, 0) # x和y方向的结果
# grad_y = cv.Scharr(image, cv.CV_32F, 0, 1)
gradx = cv.convertScaleAbs(grad_x)#上一步的计算结果有正有负,把他们全部转化为绝对值
grady = cv.convertScaleAbs(grad_y)#最后全部转换到6位的结果上去
cv.imshow("gradient_x", grad_x)
cv.imshow("gradient_y", grad_y)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("gradient", gradxy)
src = cv.imread("C:/Users/lenovo/Desktop/opencv/daima/banknum/template-matching-ocr/images/lena.jpg") #读取图片位置
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
sobel_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行截图:
scharr算子是sobel算子的增强版本。无须再讲,当sobel得到的边缘不是很好时,考虑scharr算子。
import cv2 as cv
import numpy as np
def sobel_demo(image):
#Scharr算子是Sobel算子的增强版,轮廓更加明显
grad_x = cv.Scharr(image, cv.CV_32F, 1, 0) # x和y方向的结果
grad_y = cv.Scharr(image, cv.CV_32F, 0, 1)
gradx = cv.convertScaleAbs(grad_x)#上一步的计算结果有正有负,把他们全部转化为绝对值
grady = cv.convertScaleAbs(grad_y)#最后全部转换到6位的结果上去
cv.imshow("gradient_x", grad_x)
cv.imshow("gradient_y", grad_y)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("gradient", gradxy)
src = cv.imread("C:/Users/lenovo/Desktop/opencv/daima/banknum/template-matching-ocr/images/lena.jpg") #读取图片位置
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
sobel_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行截图:
laplacian算子实现
import cv2 as cv
import numpy as np
def laplacian_demo(image):
# dst = cv.Laplacian(image, cv.CV_32F)
# lpls = cv.convertScaleAbs(dst)
#手动定义一个拉普拉斯算子
#kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])#laplacian默认用的4邻域算子,即中间的值是-4
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])#用8邻域是增强版的
dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
lpls = cv.convertScaleAbs(dst)
cv.imshow("laplacian_demo", lpls)
print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("daqiu.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
lapalian_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行截图:
使用Laplacian函数时:
使用laplacian默认的4邻域算子
使用laplacian增强后的8邻域算子
参考博客:
图像金字塔
图像中的各种金字塔
图像梯度算子的使用