图像预处理是计算机视觉任务中的关键步骤,它通过对原始图像进行处理,以提高后续图像分析、特征提取和识别的准确性。本文详细介绍了图像预处理方面的所有操作的原理及实现方法,希望能够帮助到正在学习计算机视觉的小伙伴们。
示例图片
目录
cv2.adaptiveThreshold()自适应阈值法图像二值化
cv2.getPerspectiveTransform()函数
cv2.pyrUP()与cv2.pyrDown()实现图像金字塔上下采样
常见图像处理方法
灰度化处理
灰度化处理是指将彩色图像转换为灰度图像的过程,是一个常见的预处理步骤。这里我们需要注意的是,我们在使用计算机视觉处理图像任务时,绝大多数情况下都使用灰度图像,一方面是因为我们关心的是后序算法(比如边缘检测)得到的结果,而不是图像的质量,这有别于美颜P图。另一方面则是因为它简化了图像的复杂度,使得后续的处理更为高效。灰度图像仅包含亮度信息,而不包含颜色信息,这使得算法在处理时能够更专注于图像的形状和结构特征。
法一
#灰度化处理
#法1,直接读取灰度图
import cv2
gray_image=cv2.imread('test.jpg',cv2.IMREAD_GRAYSCALE)
cv2.imshow('gray_image',gray_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果
法二
#灰度化处理
#法2,读取时不指定 flags(默认 cv2.IMREAD_COLOR),然后使用 cv2.cvtColor 转换为灰度图
import cv2
image=cv2.imread('test.jpg',cv2.IMREAD_UNCHANGED)
grayt_image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray_image',gray_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果
说明
两种方法都可对彩色图像进行灰度化处理,法一是在读取图像的同时将其转换为灰度图像。法二则是先读取彩色图像,再进行颜色空间转换。法一相对于法二更加节省内存,因为法一在图像处理的读取时就进行了灰度化处理,避免了彩色图像占用的大量内存空间。
这两种处理方法的结果是一致的:
切片截取部分图像数据
读取后的图像是(高,宽,通道数)的ndarray结构,若我们对图像的某一部分感兴趣,那我们只需要在高和宽对应的数组上进行切片即可获取到图像的部分数据。
#截取部分图像数据
import cv2
image=cv2.imread('test.jpg',cv2.IMREAD_UNCHANGED)
cv2.imshow('image',image[100:500,0:300])
cv2.waitKey(0)
cv2.destroyAllWindows()
原图像的尺寸为500x500,这里我们对其进行切片截取。截取区域为高[100:500]x宽[0:300]。
cv2.cvtColor() 颜色空间转换
cv2.cvtColor()是OpenCV库中一个非常实用的函数,它能够将图像从一个颜色空间转换到另一个颜色空间。这种转换在图像处理中非常有用,因为不同的颜色空间对于特定的图像处理任务可能具有不同的优势。例如,在边缘检测中,灰度图像往往比彩色图像更有效。
#cv2.cvtColor()颜色空间转化
'''
常用的颜色转化code
BGR->Gray cv2.COLOR_BGR2GRAY
BGR->HSV cv2.COLOR_BGR2HSV
BGR->RGB cv2.COLOR_BGR2RGB
BGR->CMYK cv2.COLOR_BGR2CMYK
RGB->BGR cv2.COLOR_RGB2BGR
RGB->HSV cv2.COLOR_RGB2HSV
RGB->GRAY cv2.COLOR_RGB2GRAY
HSV->BGR cv2.COLOR_HSV2BGR
HSV->RGB cv2.COLOR_HSV2RGB
GRAY->BGR cv2.COLOR_GRAY2BGR
GRAY->RGB cv2.COLOR_GRAY2RGB
'''
import cv2
image=cv2.imread('test.jpg')
image=cv2.cvtColor(src=image,code=cv2.COLOR_BGR2HSV)
cv2.imshow('image',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
我们常用的一些颜色空间有BGR,RGB,HSV,CMYK,GRAY这几种,在使用cv2.cvtColor()函数进行变换时,我们只需要按照cv2.COLOR_原颜色空间2新颜色空间的格式(2表示to)传入给code便可将图像从一种颜色空间转换到另一种。
将图像由BGR颜色空间变换到HSV空间后
cv2.split():颜色通道提取
颜色通道提取是图像处理中的一个基本且重要的步骤,它涉及到从图像中分离出红色、绿色、蓝色或其他颜色通道的信息。由于读取得到的图像结果是ndarray,因此我们既可以使用cv2内置函数split分离颜色通道,又可以使用切片的方式来获取三个颜色通道的信息。
import cv2
image=cv2.imread('test.jpg',cv2.IMREAD_UNCHANGED)
b,g,r=cv2.split(image)
直接使用cv2.split()函数对图像的3个颜色通道数据进行提取
import cv2
image=cv2.imread('test.jpg',cv2.IMREAD_UNCHANGED)
b,g,r=image[:,:,0],image[:,:,1],image[:,:,2]
通过切片的方法对图像的3个颜色通道数据进行提取
二者得到的结果是一致的。
当然,如果我们只想保留图像在某个颜色通道上的数据,只需将该通道以外的所有数据置0即可。
import cv2
image=cv2.imread('test.jpg')
#将图像数据copy一份给blue_image,
#这样不会影响到后续可能要对原图像进行的操作
blue_image=image.copy()
blue_image[:,:,1]=blue_image[:,:,2]=0
cv2.imshow('blue_image',blue_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果
cv2.resize():修改图像尺寸
cv2的内置函数resize可以方便地调整图像的大小,值得注意的是,在修改图像尺寸时,为了尽量避免对图像质量造成过多的损失,可以通过选择适当的插值算法来保持图像的清晰度和细节。对此,cv2.resize函数的默认方法为
放大时:cv2.INTER_CUBIC(双三次插值)
缩小时:cv2.INTER_AREA(区域插值)
#cv2.resize()修改图像尺寸
'''
src:原图像ndarray
dsize元祖形式,指定图像的高和宽
fx:x轴上图像放大或缩小倍数
fy:y轴上图像放大或缩小倍数
interploation:图像插值方法
需要注意的是当dsize为(0,0)或不存在时,fx,fy才会起作用,也就是说resize函数优先使用dsize
常用的interploation参数
cv2.INTER_NEAREST:最近邻插值(速度快,但可能导致锯齿)
cv2.INTER_LINEAR:双线性插值(默认,适用于大多数情况)
cv2.INTER_CUBIC:双三次插值(高质量,计算成本较高)
cv2.INTER_AREA:区域插值(缩小图像时效果较好)
cv2.INTER_LANCZOS4:Lanczos插值(高质量,适用于放大)
'''
import cv2
import matplotlib.pyplot as plt
#plt.imshow的图像格式为RGB,因此读取图像数据时我们指定flags按照RGB格式
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)#原始图像大小为500x500
#resize函数参数:
image=cv2.resize(src=image, dsize=(600, 600),fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
plt.imshow(image)
cv2.resize()函数参数详解:
src | 原图像ndarray |
dsize | 元祖形式,指定调整后图像的高和宽 |
fx | x轴上图像放大或缩小倍数 |
fy | y轴上图像放大或缩小倍数 |
interploation | 图像插值方法 |
需要注意的是当dsize为(0,0)或不存在时,fx,fy才会起作用,也就是说resize函数优先使用dsize
resize后的图像,原图像尺寸500x500,dsize=(0,0),按照fx,fy取值对图像大小进行调整
cv2.addWeighted()图像融合
在某些场景下我们需要对两张图片进行融合,考虑到图像经过cv2.imread()函数读取后均为ndarray,因此我们可以直接对两张图片的读取结果进行加减乘除运算,一般而言,图像进行融合时我们都采用线性融合的方式,比如我们可以直接将二者简单相加:
#图像融合,直接相加
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image1=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
image2=cv2.imread('test1.jpg',cv2.IMREAD_COLOR_RGB)
image1=cv2.resize(image1,(800,800))
image2=cv2.resize(image2,(800,800))
newimage=image1+image2
print(image1[:3,:,0],'\n')
print(image2[:3,:,0],'\n')
print(newimage[:3,:,0])
plt.subplot(1,3,1),plt.imshow(image1),plt.title('图像1')
plt.subplot(1,3,2),plt.imshow(image2),plt.title('图像2')
plt.subplot(1,3,3),plt.imshow(newimage),plt.title('图像1+图像2')
将两个图像直接相加
结果
当然,cv2还提供了addWeighted方法实现上述效果:
#图像融合,使用cv2.addWeighted函数
'''
src1:图像1
alpha:图像1系数
src2:图像2
beta:图像2系数
gamma;常数项
newimage=0.5*image1+0.8*image2+5
'''
import cv2
plt.rcParams['font.sans-serif']=['SimHei']
image1=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
image2=cv2.imread('test1.jpg',cv2.IMREAD_COLOR_RGB)
image1=cv2.resize(image1,(800,800))
image2=cv2.resize(image2,(800,800))
newimage=cv2.addWeighted(src1=image1,alpha=0.6,src2=image2,beta=0.8,gamma=5)
plt.subplot(1,3,1),plt.imshow(image1),plt.title('图像1')
plt.subplot(1,3,2),plt.imshow(image2),plt.title('图像2')
plt.subplot(1,3,3),plt.imshow(newimage),plt.title('图像1+图像2')
cv2.addWeighted()函数参数解析:
src1 | 图像1矩阵 |
alpha | 图像1系数 |
src2 | 图像2矩阵 |
beta | 图像2系数 |
gamma | 常数项 |
结果
cv2.merge()图像合并
cv2.merge()是OpenCV库中的一个函数,主要用于将多个单通道图像合并为一个多通道图像。该函数通常接受一个包含若干图像的元组或列表作为输入,这些图像需要具有相同的尺寸。
#图像合并
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
r,g,b=cv2.split(image)#先将图片拆分为3个单通道的图像
merged_image=cv2.merge([r,g,b])#将3个单通道图像合并为原图像
#在合并单通道图像时需要将各个通道的图像数据按照元祖或列表的形式传入,且每个图像数据的尺寸需要一致
#合并后的图像shape为:(高,宽,各图像通道数之和)
plt.imshow(merged_image)
结果
当然,多通道图像之间也可以合并,只要他们的形状一致即可,比如上述代码中的merged_image我们可以使用索引切片来将图像r+g与b这两个图像(2通道+1通道)进行合并。
randg=image[:,:,0:2]
b=image[:,:,2]
merged_image=cv2.merge([rang,b])
但是,对于RGB图像而言,通道数最多不超过4(png图像多一个alpha通道),所以当你试图将两个RGB图象使用cv2.merge()函数合并后,无论是cv2.imshow()还是plt.imshow()都将无法正常显示图像,因为合并后的图像的通道数>=4.
cv2.copyMakeBorder()边界填充
边界填充是图像处理中的一种常见操作,它通过在图像的边界周围添加额外的像素来改变图像的大小。这一操作在许多图像处理任务中都是必不可少的。在卷积神经网络(CNN)中进行图像分类或目标检测时,为了保持输入图像尺寸的一致性,常常需要对原始图像进行边界填充。此外,边界填充还可以用于创建图像的镜像效果,或者在图像拼接时减少接缝处的突兀感。
cv2.copyMakeBorder函数便是opencv内置用来进行图像填充的函数,它可以在图像的四周或特定的一侧添加指定宽度和类型的边框。
#边界填充cv2.copyMakeBorder()
'''
cv2.copyMakeBorder()参数详解:
src:原图像数据
top:图像顶部要添加的边框宽度
bottom:图像底部要添加的边框宽度
left:图像左侧要添加的边框宽度
right:图像右侧要添加的边框宽度
borderType:填充方式
value:当borderType为CONSTANT时候可用,对于灰度图像,它是一个标量值;对于彩色图像,它是一个包含三个值的元组(B, G, R)。
所有填充方式:
cv2.BORDER_CONSTANT: 添加一个固定颜色的边框,颜色由value参数指定
cv2.BORDER_REPLICATE: 复制图像边缘的像素值来填充边框
cv2.BORDER_REFLECT: 镜像反射图像边缘的像素值来填充边框
cv2.BORDER_WRAP: 使用图像的另一侧的像素值来填充边框
cv2.BORDER_DEFAULT: 默认的边框类型,通常与cv2.BORDER_REFLECT_101相同
cv2.BORDER_REFLECT_101: 类似于 cv2.BORDER_REFLECT,但略有不同,通常用于避免重复的边缘像素。
cv2.BORDER_ISOLATED:不处理边界,而是直接返回原始图像,即使传入了其他参数也被忽略。
'''
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
default=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_DEFAULT)
reflect=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_REFLECT)
isolated=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_ISOLATED)
wrap=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_WRAP)
replicate=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_REPLICATE)
reflect101=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_REFLECT101)
constant=cv2.copyMakeBorder(src=image,top=20,bottom=20,left=20,right=20,borderType=cv2.BORDER_CONSTANT,value=(10,250,120))#绿色边框
fig,axs=plt.subplots(3,3,figsize=(10,8))
axs[0][0].imshow(image),axs[0][0].set_title('原图')
axs[0][1].imshow(default),axs[0][1].set_title('borderType=default')
axs[0][2].imshow(reflect),axs[0][2].set_title('borderType=reflect')
axs[1][0].imshow(isolated),axs[1][0].set_title('borderType=isolated')
axs[1][1].imshow(wrap),axs[1][1].set_title('borderType=wrap')
axs[1][2].imshow(replicate),axs[1][2].set_title('borderType=replicate')
axs[2][0].imshow(reflect101),axs[2][0].set_title('borderType=reflect101')
axs[2][1].imshow(constant),axs[2][1].set_title('borderType=constant')
plt.tight_layout()
cv2.copyMakeBorder()函数参数详解
src | 原图像数据 |
top | 图像顶部要添加的边框宽度 |
bottom | 图像底部要添加的边框宽度 |
left | 图像左侧要添加的边框宽度 |
right | 图像右侧要添加的边框宽度 |
broderType | 填充方式 |
value | 当borderType为CONSTANT时候可用,对于灰度图像,它是一个标量值,对于彩色图像,它是一个包含三个值的元组(B, G, R)BGR为三个颜色通道上的值。 |
boderType常用参数
cv2.BORDER_CONSTANT | 添加一个固定颜色的边框,颜色由value参数指定 |
cv2.BORDER_REPLICATE | 复制图像边缘的像素值来填充边框 |
cv2.BORDER_REFLECT | 镜像反射图像边缘的像素值来填充边框 |
cv2.BORDER_WRAP | 使用图像的另一侧的像素值来填充边框 |
cv2.BORDER_DEFAULT | 默认的边框类型,通常与cv2.BORDER_REFLECT_101相同 |
cv2.BORDER_REFLECT_101 | 类似于cv2.BORDER_REFLECT,但略有不同,通常用于避免重复的边缘像素 |
cv2.BORDER_ISOLATED | 不处理边界,而是直接返回原始图像,即使传入了其他参数也被忽略 |
结果
cv2.threshold()全局阈值法图像二值化
全局阈值法图像二值化是通过设定一个全局阈值,将图像中的像素值分为两类:高于阈值的像素被设置为最大值(通常为白色),低于阈值的像素被设置为最小值(通常为黑色)。这种二值化处理能够极大地简化图像信息,突出目标特征,为后续的图像分析、特征提取等步骤提供便利。在OpenCV中,cv2.threshold()函数是实现这一功能的核心工具,它允许用户灵活设置阈值和处理方式,以满足不同的图像处理需求。
#cv2.threshold()实现全局阈值二值化
'''
cv2.threshold()参数:
src:原始图像通常是单通道8为灰度图
thresh:阈值
maxval:最大值,超过阈值的像素点的值都将被设为这个值
type:阈值类型,cv2.THRESH_BINARY表示二值化类型
返回值说明,cv2.threshold()函数返回两个值,第一个值是设定的thresh,第二个值是二值化后的图像数据
常用type:
cv2.THRESH_BINARY:当像素值大于阈值时,将其设置为最大值;当像素值小于等于阈值时,将其设置为0
cv2.THRESH_BINARY_INV:与cv2.THRESH_BINARY相反,当像素值小于阈值时,将其设置为最大值(通常为255);当像素值大于等于阈值时,将其设置为0
cv2.THRESH_TOZERO:当像素值大于阈值时,保持不变;当像素值小于等于阈值时,将其设置为0。
cv2.THRESH_TOZERO_INV:与cv2.THRESH_TOZERO相反,当像素值大于阈值时,将其设置为0,当像素值小于等于阈值时,保持不变。
cv2.THRESH_TRUNC:像素值大于阈值时,将其设置为阈值;当像素值小于等于阈值时,保持不变。
cv2.THRESH_TRIANGLE:自动计算阈值,基于Otsu's方法的一种变体,适用于双峰直方图(即图像有两种主要的灰度分布)
cv2.THRESH_OTSU:自动计算阈值,通过最大化类间方差来确定最佳阈值,将图像分为前景和背景两部分。
注意,以上几种type可以混合使用。
'''
import cv2
image=cv2.imread('test.jpg',cv2.IMREAD_GRAYSCALE)
_,binary_image=cv2.threshold(src=image,thresh=100,maxval=255,type=cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print(_)
cv2.imshow('binary image',binary_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果
说明
在对图像进行二值化处理时我们通常需要将其转化为灰度图像并且可能还需要对图像进行去噪、平滑等预处理操作,以提高阈值处理的效果。
cv2.threshold()函数参数详解:
src | 原始图像数据 |
thresh | 阈值 |
maxval | 最大值 |
type | 阈值类型,cv2.THRESH_BINARY表示二值化类型 |
cv2.threshold()函数常用type详解
cv2.THRESH_BINARY | 当像素值大于阈值时,将其设置为最大值;当像素值小于等于阈值时,将其设置为0 |
cv2.THRESH_BINARY_INV | 与cv2.THRESH_BINARY相反,当像素值小于阈值时,将其设置为最大值(通常为255);当像素值大于等于阈值时,将其设置为0 |
cv2.THRESH_TOZERO | 当像素值大于阈值时,保持不变;当像素值小于等于阈值时,将其设置为0。 |
cv2.THRESH_TOZERO_INV | cv2.THRESH_TOZERO相反,当像素值大于阈值时,将其设置为0,当像素值小于等于阈值时,保持不变。 |
cv2.THRESH_TRUNC | 像素值大于阈值时,将其设置为阈值;当像素值小于等于阈值时,保持不变。 |
cv2.THRESH_TRIANGLE | 自动计算阈值,基于Otsu's方法的一种变体,适用于双峰直方图(即图像有两种主要的灰度分布) |
cv2.THRESH_OTSU | 自动计算阈值,通过最大化类间方差来确定最佳阈值,将图像分为前景和背景两部分。 #注意,以上几种type可以混合使用。 |
以上几种type可以混合使用,比如示例代码中我们可以同时使用cv2.THRESH_BINARY与cv2.THRESH_OTSU方法。
特别的,当你指定的type中含有cv2.THRESH_TRIANGLE,cv2.THRESH_OTSU 这两种自动计算阈值的方法时,此时我们传入的thresh将被忽略,并且cv2.threshold()函数返回值的第一个元素是自动计算出来的thresh。
cv2.adaptiveThreshold()自适应阈值法图像二值化
#cv2.adaptivethreshold()自适应阈值二值化
'''
src:图像数据通常是单通道8为灰度图
maxValue:最大值,超过阈值的像素点的值都将被设为这个值
adaptiveNethod:指定自适应阈值方法
blockSize:整数,领域值,通常是奇数,表示计算阈值时考虑的像素领域大小
C:整数,从计算的均值或加权均值中减去的常数
thresholdType:与cv2.threshold()函数内的type取值一致
'''
import cv2
image=cv2.imread(r'test.jpg',cv2.IMREAD_GRAYSCALE)
adaptive_binary_image=cv2.adaptiveThreshold(src=image,maxValue=255,blockSize=11,C=10,adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C,thresholdType=cv2.THRESH_BINARY)
cv2.imshow('adaptive_binary_image',binary_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.adaptiveThreshold()参数详解:
src | 图像数据通常是单通道8为灰度图 |
maxValue | 最大值,超过阈值的像素点的值都将被设为这个值 |
adaptiveMethod | 指定自适应阈值方法 |
blockSize | 整数,领域值,通常是奇数,表示计算阈值时考虑的像素领域大小 |
C | 整数,从计算的均值或加权均值中减去的常数 |
thresholdType | 与Threshold()函数内的type一致 |
cv2.adaptiveThreshold()常用adaptiveMethod详解
当我们使用adaptiveThreshold()对图像二值化操作时,adaptiveThreshold主要有以下两个取值。
cv2.ADAPTIVE_THRESH_MEAN_C | 均值二值化,计算领域内所有像素的均值并减C作为该点处的像素值 |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C | 高斯加权平均二值化,计算领域内所有像素的加权平均值并减C,权重按照高斯分布分配 |
结果:
显然,同样的图像使用自适应阈值进行二值化的效果要比直接全局阈值法效果更好
图像形态学基本操作
cv2.morphologyEx()函数简介
cv2.morphologyEx 是 OpenCV 中用于执行各种形态学操作的函数。形态学操作是一种基于形状处理图像的方法,常用于图像预处理、特征提取、噪声去除和形状分析等任务。 cv2.morphologyEx 函数能够执行多种形态学操作,通过指定不同的操作类型,可以实现膨胀、腐蚀、开运算、闭运算等效果。
cv2.morphologyEX函数参数详解
src | 原图像数据通常是单通道8位灰度图 |
op | 指定形态学操作的类型 |
kernel | 一个指定领域大小的ndarray矩阵,和卷积核类似,只不过运算方法不同 |
anchor | 锚点,结构元素(卷积核)的锚点位置 |
iterations | 相应操作进行的次数,默认为1,次数越多,效果越明显 |
borderType | 边界填充方式,与cv2.copyMakeBorder()函数中的borderType一致 |
borderValue | 当borderType为cv2.BORDER_CONSTANT时,制定的填充边界的值,对于灰度图而言是一个值,对于彩色图而言是BGR三元组(B,G,R) |
cv2.morphologyEX()常见op形态学操作类型
cv2.MORPH_ERODE | 腐蚀操作 |
cv2.MORPH_DILATE | 膨胀操作 |
cv2.MORPH_OPEN | 开运算 |
cv2.MORPH_CLOSE | 闭运算 |
cv2.MORPH_TOPHAT | 顶帽操作 |
cv2.MORPH_BLACKHAT | 黑帽操作 |
cv2.MORPH_GRADIENT | 形态学梯度 |
cv2.erode()腐蚀操作
图像腐蚀(Erosion)是一种减少图像中白色物体大小的操作,它通过使用与膨胀相同的结构元素,但是规则不同,只有当结构元素完全覆盖在白色像素上时,结构元素中心位置的黑色像素会变为白色,这意味着物体的边界会向内收缩,导致图像中的亮区域边缘向内收缩,暗区域相应扩大。图像腐蚀常用于去除小的白色噪声点、分离粘连对象、细化图像结构等场景。
#cv2.erode()腐蚀操作
'''
cv2.erode()参数详解:
src:原图像数据通常是单通道8位灰度图
kernel:一个指定领域大小的ndarray矩阵,和卷积核类似,只不过运算方法不同,这里我们称他为结构元素
iterations:腐蚀操作进行的次数,默认为1,次数越多,腐蚀效果越明显
borderType:边界填充方式,与cv2.copyMakeBorder()函数中的borderType一致
borderValue:当borderType为cv2.BORDER_CONSTANT时,制定的填充边界的值,对于灰度图而言是一个值,对于彩色图而言是BGR三元组(B,G,R)
'''
import cv2
import numpy as np
image=cv2.imread('black.jpg',flags=cv2.IMREAD_GRAYSCALE)
kernel=np.ones(shape=(3,3,1))
eroded_image=cv2.erode(src=image,kernel=kernel,iterations=1,borderType=cv2.BORDER_REPLICATE)
cv2.imshow('src_mage',image)
cv2.imshow('eroded_image',eroded_image)
cv2.waitKey(0)
结果
显然,与之前的描述一致,图像中的亮区域边缘向内收缩,暗区域相应扩大
cv2.erode()函数参数详解
src | 原图像数据通常是单通道8位灰度图 |
kernel | 一个指定领域大小的ndarray矩阵,和卷积核类似,只不过运算方法不同,这里我们称他为结构元素 |
iterations | 腐蚀操作进行的次数,默认为1,次数越多,腐蚀效果越明显 |
borderType | 边界填充方式,与cv2.copyMakeBorder()函数中的borderType一致 |
borderValue | 当borderType为cv2.BORDER_CONSTANT时,制定的填充边界的值,对于灰度图而言是一个值,对于彩色图而言是BGR三元组(B,G,R) |
腐蚀原理
对图像腐蚀的原理很简单,就是结构元素(类似卷积核)沿着图像滑动(以结构元素的中心为参照点),如果与结构元素覆盖后对应的原图像的所有像素值都是1,那么中心元素保持原来的值,否则就变为0。这对于去除白噪声很有用,也可以用于断开两个连在一起的物体。
观察示例中的图片,在左侧区域内,我们无论怎么移动3x3的kernel,能够满足卷积核覆盖后对应的原图像的所有像素值都是1的位置只有上边的黄色阶梯区域,对于黑白二值图像来说1为白色,0为黑色,所以一般来说对黑白图像进行腐蚀操作后会亮区域边缘向内收缩,暗区域相应扩大。
cv2.dilate() 膨胀操作
图像膨胀(dilate)是一种主要用于增强图像中的亮区域或前景对象的图像处理技术。通过设定一个结构元素,该算法会将图像中与结构元素相匹配的部分进行扩展,使得图像中的亮区域边缘向外扩展,暗区域相应缩小。这种操作在处理二值图像或灰度图像时非常有用,特别是在需要突出图像中的特定结构或去除小孔和缝隙时。
在cv2中,cv2.dilate()函数可以用来实现上述功能
#cv2.dilate()膨胀操作
'''
cv2.dilate()参数详解:
src:原图像数据通常是单通道8位灰度图
kernel:一个指定领域大小的ndarray矩阵,和卷积核类似,只不过运算方法不同
iterations:膨胀操作进行的次数,默认为1,次数越多,膨胀效果越明显
borderType:边界填充方式,与cv2.copyMakeBorder()函数中的borderType一致
borderValue:当borderType为cv2.BORDER_CONSTANT时,制定的填充边界的值,对于灰度图而言是一个值,对于彩色图而言是BGR三元组(B,G,R)
'''
import cv2
import numpy as np
image=cv2.imread('black.jpg',flags=cv2.IMREAD_GRAYSCALE)
kernel=np.ones(shape=(3,3,1))
cv2.imshow('src_mage',image)
dilated_image=cv2.dilate(src=image,kernel=kernel,iterations=1,borderType=cv2.BORDER_REPLICATE)
cv2.imshow('dilated_image',dilated_image)
cv2.waitKey(0)
结果
显然,与之前的描述一致,图像中的亮区域边缘向外扩展,暗区域相应缩小。
cv2.dilate()函数参数详解
src | 原图像数据通常是单通道8位灰度图 |
kernel | 一个指定领域大小的ndarray矩阵,和卷积核类似,只不过运算方法不同 |
iterations | 膨胀操作进行的次数,默认为1,次数越多,膨胀效果越明显 |
borderType | 边界填充方式,与cv2.copyMakeBorder()函数中的borderType一致 |
borderValue | 当borderType为cv2.BORDER_CONSTANT时,制定的填充边界的值,对于灰度图而言是一个值,对于彩色图而言是BGR三元组(B,G,R) |
膨胀原理
膨胀的操作与腐蚀相反,这里的相反指的是运算相反,而不是值相反。膨胀操作的结构元素(类似卷积核)沿着图像滑动(以卷积核的中心为参照点),如果与结构元素覆盖后对应的原图像的像素值只要有一个是1,中心像素值就是1,否则为0。对于灰度化二值图像来说1为白色,0为黑色,所以一般来说对黑白图像进行膨胀操作后会亮区域边缘向外扩展,暗区域相应缩小。
cv2.morphologyEx()实现开闭运算
开运算原理
图像的开运算是指对图像先腐蚀后膨胀的过程。这个过程有助于去除小的对象(即前景中的小白点),在分离物体、消除小颗粒噪声或“断开”物体之间的连接时非常有用。开运算可以使得物体的轮廓变得平滑,同时减小其面积,而不会影响物体的整体形状和位置。在处理二值图像或灰度图像时,开运算是一种非常有效的形态学处理方法。
开运算原理图
闭运算原理
图像的闭运算是指对图像先膨胀后腐蚀的过程,这个过程常用于填补图像中的小孔或狭缝,以及连接相邻的物体。闭运算能够使物体的轮廓变得更为平滑,同时增加其面积,但不会显著改变物体的整体形状和位置。在二值图像或灰度图像的处理中,闭运算也是一种非常有用的形态学处理方法,特别是在需要填补图像中的小缺陷或连接相邻的分割区域时。通过闭运算,可以使得图像中的物体更加完整和连贯,为后续的图像分析或识别工作提供便利。
闭运算原理图
示例
import cv2
import numpy as np
kernel=np.ones(shape=(3,3))
image=cv2.imread('black.jpg')
opened_image=cv2.morphologyEx(src=image,op=cv2.MORPH_OPEN,kernel=kernel,anchor=(-1,1),iterations=1,borderType=cv2.BORDER_CONSTANT,borderValue=25)
closed_image=cv2.morphologyEx(src=image,op=cv2.MORPH_CLOSE,kernel=kernel,anchor=(-1,1),iterations=1,borderType=cv2.BORDER_CONSTANT,borderValue=25)
cv2.imshow('image',image)
cv2.imshow('opened_image',opened_image)
cv2.imshow('closed_image',closed_image)
cv2.waitKey(0)
使用cv2.morphologyEx()函数分别实现开运算与闭运算
结果
经过开运算的图像,小颗粒噪声(白点)被全部消除掉,经过闭运算后的图像,顶层的6个文字中的小孔或狭缝被填补起来,更加连贯。
cv2.morphologyEx()实现黑帽与顶帽
黑帽原理
图像的黑帽运算是其闭运算结果减去原始图像的一种形态学变换。这种运算可以有效地提取图像中的暗斑或比周围区域更暗的部分。通过闭运算,图像中的小孔和小的黑色区域会被填充,使得亮区域变得更大且更加连贯。然后,从闭运算后的图像中减去原始图像,就可以得到那些在原图中相对较暗且被闭运算放大的区域,这些区域通常对应于图像中的暗斑或阴影。黑帽运算在处理不均匀光照、增强图像对比度以及检测特定形状特征时非常有用。
顶帽原理
图像的顶帽是指原图像减去其开运算结果的一种形态学变换。这种运算的效果主要是突出比背景亮的区域,尤其是那些被暗色区域包围的亮色斑点或区域。通过开运算,首先去除图像中的小物体或亮斑,使其与周围的暗区域合并,然后从原图像中减去开运算的结果,得到的是那些相对亮的区域。这些区域在原始图像中由于暗背景的包围而显得更为明亮。顶帽变换在处理不均匀光照或提取特定形状特征时也非常有用,与黑帽变换形成互补,两者结合使用可以更有效地处理复杂的图像。
示例
#cv2.morphologyEx()实现顶帽与黑帽
'''
src:原图像数据通常是单通道8位灰度图
op:指定形态学操作的类型
kernel:一个指定领域大小的ndarray矩阵,和卷积核类似,只不过运算方法不同,这里我们称它为结构元素。
anchor:锚点,结构元素(卷积核)的锚点位置
iterations:相应操作进行的次数,默认为1,次数越多,效果越明显
borderType:边界填充方式,与cv2.copyMakeBorder()函数中的borderType一致
borderValue:当borderType为cv2.BORDER_CONSTANT时,制定的填充边界的值,对于灰度图而言是一个值,对于彩色图而言是BGR三元组(B,G,R)
'''
import cv2
import numpy as np
kernel=np.ones(shape=(3,3))
image=cv2.imread('black_bg.jpg')
tophat_image=cv2.morphologyEx(src=image,op=cv2.MORPH_TOPHAT,kernel=kernel,iterations=1,borderType=cv2.BORDER_CONSTANT,borderValue=25)
blackhat_image=cv2.morphologyEx(src=image,op=cv2.MORPH_BLACKHAT,kernel=kernel,iterations=1,borderType=cv2.BORDER_CONSTANT,borderValue=25)
cv2.imshow('image',image)
cv2.imshow('blackhat_image',blackhat_image)
cv2.imshow('tophat_image',tophat_image)
cv2.waitKey(0)
使用cv2.morphologyEx()函数分别实现开运算与闭运算
结果
可以发现,经过黑帽运算后,图像中相对较暗的地方被放大,经过顶帽运算后,原始图像中与暗区域相比对比度高的亮区域由于暗背景的包围而显得更为明亮。
cv2.morphologyEx()实现形态学梯度
原理
形态学梯度是指图像的膨胀与腐蚀之差,通过这一操作,可以突出团块(blob)区域,进而用于保留物体的边缘或轮廓。此外,形态学梯度还能够有效地抑制噪声,提高图像的清晰度和对比度,使得物体的边缘更加鲜明,轮廓更加清晰。这种技术在医学影像处理、工业检测、安全监控等领域都有着广泛的应用前景。
示例
import cv2
import numpy as np
kernel=np.ones(shape=(3,3))
image=cv2.imread('black_bg.jpg')
gradient_image=cv2.morphologyEx(src=image,op=cv2.MORPH_GRADIENT,kernel=kernel,iterations=1,borderType=cv2.BORDER_CONSTANT,borderValue=25)
cv2.imshow('image',image)
cv2.imshow('gradient_image',gradient_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果
可以发现,经过梯度运算后,原始图像中的主体部分被增强,边缘更加鲜明,轮廓更加清晰 。
图像直方图均衡化操作
原理
直方图均衡化的基本思想是把原始图像的直方图变换为整个灰度范围内的均匀分布的形式,这样就增加了像素灰度值的动态范围。
示例
在opencv中我们可以使用cv2.equalizeHist函数实现上述功能。
#对RGB图形每个通道进行直方图均衡化后可能会颜色失真
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
r_channel,g_channel,b_channel=cv2.split(image)
equalized_r_channel=cv2.equalizeHist(src=r_channel)
equalized_g_channel=cv2.equalizeHist(src=g_channel)
equalized_b_channel=cv2.equalizeHist(src=b_channel)
Equalized_RGB_image=cv2.merge((equalized_r_channel,equalized_g_channel,equalized_b_channel))
plt.subplot(1,2,1),plt.title('原图'),plt.imshow(image)
plt.subplot(1,2,2),plt.title('直方图均衡化'),plt.imshow(Equalized_RGB_image)
结果
图像几何变化操作
cv2.warpAffine() 仿射变换
仿射变换是指图像可以通过仿射变换矩阵运算后实现平移,旋转,放缩,倾斜,翻转等操作。
其可以看做两种简单的叠加:线性变换+平移变换。该变换能保持图像的平直性和平行性。
平直性:指图像经过仿射变换后,直线仍然是直线。
平行性:若两条线变换前平行,那么变换后仍然平行。
仿射变换矩阵
对于图像中任意一点P(x,y),设其仿射变换后的坐标为,那么
平移: 其中
与
分别是图像在x轴和y轴上的平移量。
缩放: ,其中W和H分别是图像在x轴和y轴方向上的缩放倍数。
综上,二者结合便可以得到:
然后,我们可以将其写成矩阵形式:
其中 , 称为仿射变换矩阵。
特别地,当M内各个参数取值不同时,作用在图像上将会产生不同的效果:
注意:在任何图像处理工具或框架中,图像的坐标原点都为左上角,y轴正方向向下
图像坐标系
示例:
#仿射变换矩阵图像平移,与缩放
'''
cv2.warpAffine参数详解:
src:原始图像矩阵
M:仿射变换矩阵,注意,必须使用np.float32将列表转换为矩阵
dsize:指定仿射变换后的图像外尺寸,通长是使用图像原始大小即可,(width,height)
borderMode:图像缩放后填充方式,这里的填充方式与cv2.copyMakeBorder()函数一致
borderValue:当borderMode为cv2.BORDER_CONSTANT时,使用borderValue值填充,彩色图像传入一个(B,G,R)的像素值三元组,灰度图传入一个值即可
'''
import cv2
import numpy as np
image=cv2.imread('test.jpg')
height,width=image.shape[:2]
#将图像从x轴和y轴上缩小为原来一半,那么图像缩小为原来的1/4
scale_x=0.5
scale_y=0.5
#图像在x轴和y轴的=平移量,显然图像将向右下角平移
shifted_x=20
shifted_y=100#
M_shifted=np.float32([[1,0,shifted_x],[0,1,shifted_y]])#注意,必须使用np.float32转换为矩阵,np.int8等其他类型不可以
M_scaled=np.float32([[0.5,0,0],[0,0.5,0]])
shifted_image=cv2.warpAffine(src=image,M=M_shifted,dsize=(width,height),borderMode=cv2.BORDER_CONSTANT,borderValue=(122,214,123))#使用常数填充,填充像素值是我随便写的
scaled_image=cv2.warpAffine(src=image,M=M_scaled,dsize=(width,height),borderMode=cv2.BORDER_CONSTANT,borderValue=(122,214,123))#使用常数填充,填充像素值是我随便写的
cv2.imshow('image',image)
cv2.imshow('shifted_image',shifted_image)
cv2.imshow('scaled_image',scaled_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.warpAffine()参数详解:
src | 原始图像矩阵 |
M | 仿射变换矩阵,注意,必须使用np.float32将列表转换为矩阵 |
dsize | 指定仿射变换后的图像外尺寸,通长是使用图像原始大小即可,(width,height) |
borderMode | 图像缩放后填充方式,这里的填充方式与cv2.copyMakeBorder()函数一致 |
borderValue | 当borderMode为cv2.BORDER_CONSTANT时,使用borderValue值填充,彩色图像传入一个(B,G,R)的像素值三元组,灰度图传入一个值即可 |
结果 :
这里我使用的填充方式是CONSTANT,传入的(B,G,R)值为绿色
图像旋转
我们知道,在仿射变换中需要对图像进行旋转时,需要自定设定仿射变换矩阵,这在图像沿着原点旋转时比较方便,此时仿射变换矩阵:
但是,当我们需要绕着其他点旋转时,这个反射变换矩阵便失效了,当然也可以通过数学推导得出此时的仿射变换矩阵应为:
其中,center为旋转中心坐标,center.x,center.y分别表示中心点横坐标与纵坐标,Scale为放大或缩小比例(旋转可能选出图像外,适当缩小可以避免该现象),angle为旋转角度,按照上述格式将M矩阵传入给cv2.warpAffine()便可实现图像绕着指定点旋转。
但是这样比较麻烦,那么我们便可以使用cv2.getRotationMatrix2D()函数来自动按照上式计算仿射变换矩阵M。
当然,cv2.getRotationMatrix2D()函数内部本质上就是计算这个矩阵,这里我们可以自己实现:
#cv2. getRotationMatrix2D()本质:
from math import cos,sin,radians
import numpy as np
def getRotationMatrix2D(center:tuple,angle:float,scale:float,counterclockwise:bool=False):
if not counterclockwise:#不逆时针顺时针的话
angle=radians(-1*angle)#顺时针转角度前需要加个负号.标准三角函数是按照逆时针转的
alpha=scale*cos(angle)
beta=scale*sin(angle)
tx,ty=center[0],center[1]
M=np.float32([[alpha,beta,(1-alpha)*tx-beta*ty],[-beta,alpha,beta*tx+(1-alpha)*ty]])
return M
cv2.getRotationMatrix2D()参数详解:
center | 旋转中心坐标 |
angle | 旋转角度 |
scale | 放大比例(也可以缩小) |
示例:
#使用cv2.getRotationMatrix2D()获得旋转仿射变化矩阵
'''
cv2.getRotationMatrix2D()参数详解:
center:旋转中心坐标,即图像绕着指定点旋转的坐标
angle:旋转角度
scale:图像缩放比例,旋转时角度过大可能超出边界,这时适当缩放一下可以避免这种情况
'''
import cv2
import numpy as np
image=cv2.imread('test.jpg')
height,width=image.shape[:2]
center_point=(width/2,height/2)
M=cv2.getRotationMatrix2D(center=center_point,angle=90,scale=0.8)
rotated_image=cv2.warpAffine(src=image,M=M,dsize=(width,height),borderMode=cv2.BORDER_CONSTANT,borderValue=(142,20,136))
cv2.imshow('rotated_image',rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果 :
这里我使用的填充方式是CONSTANT,传入的(B,G,R)值为紫色
图像翻转
使用仿射变换对图像翻转
前边我们提到过,对图像进行翻转可以使用仿射变换的方式,此时只需要将指定的仿射变换矩阵传入到cv2.warpAffine()函数即可实现一系列操作,在翻转这里,仿射矩阵M应为:
设width代表图像的宽度,height代表图像的宽度,水平翻转变换的矩阵为:
垂直翻转变换的矩阵为:
同时进行水平垂直翻转变换的矩阵为:
示例 :
#使用仿射变换矩阵实现翻转
import cv2
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
height,width=image.shape[:2]
horizontal_M=np.float32([[-1,0,height],[0,1,0]])
vertical_M=np.float32([[1,0,0],[0,-1,width]])
horizontal_and_vertical_M=np.float32([[-1,0,height],[0,-1,width]])
flipped_horizontally_image=cv2.warpAffine(src=image,M=horizontal_M,dsize=(width,height))
flipped_vertically_image=cv2.warpAffine(src=image,M=vertical_M,dsize=(width,height))
flipped_both_image=cv2.warpAffine(src=image,M=horizontal_and_vertical_M,dsize=(width,height))
plt.subplot(2,2,1),plt.imshow(image),plt.title('原图')
plt.subplot(2,2,2),plt.imshow(flipped_horizontally_image),plt.title('水平翻转')
plt.subplot(2,2,3),plt.imshow(flipped_vertically_image),plt.title('垂直翻转')
plt.subplot(2,2,4),plt.imshow(flipped_both_image),plt.title('水平+垂直翻转')
plt.tight_layout()
结果 :
使用cv2.flip()实现图像翻转
示例:
#使用cv2.flip()实现图像翻转
'''
cv2.flip()参数详解:
src:图像矩阵
flipcode:翻转方式,1表示水平翻转,0表示垂直翻转,-1表示水平+垂直翻转
'''
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
flipped_horizontally_image=cv2.flip(src=image,flipCode=1)
flipped_vertically_image=cv2.flip(src=image,flipCode=0)
flipped_both_image=cv2.flip(src=image,flipCode=-1)
plt.subplot(2,2,1),plt.imshow(image),plt.title('原图')
plt.subplot(2,2,2),plt.imshow(flipped_horizontally_image),plt.title('水平翻转')
plt.subplot(2,2,3),plt.imshow(flipped_vertically_image),plt.title('垂直翻转')
plt.subplot(2,2,4),plt.imshow(flipped_both_image),plt.title('水平+垂直翻转')
plt.tight_layout()
结果:
使用cv2.rotate()实现图像翻转()
我们知道,对图像进行垂直,或水平+垂直翻转实际上就是将图像分别按照逆时针旋转180°,以及直接顺时针旋转180°,因此我们还可以使用cv2.rotate()函数来实现.
示例.
#使用cv2.rotate实现图像翻转
'''
cv2.rotate()参数详解:
src:图像矩阵
rotatecode:旋转方式
cv2.ROTATE_90_CLOCKWISE示顺时针旋转90°,0表示垂直翻转,-1表示水平+垂直翻转
'''
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
rotated_90_clockwise_image=cv2.rotate(src=image,rotateCode=cv2.ROTATE_90_CLOCKWISE)
flipped_horizontally_image=cv2.rotate(src=rotated_90_clockwise_image,rotateCode=cv2.ROTATE_90_CLOCKWISE)#先顺时针旋转90°图像然后再旋转90°就可以实现水平+垂直翻转
rotated_90_counterclockwise_image=cv2.rotate(src=image,rotateCode=cv2.ROTATE_90_COUNTERCLOCKWISE)
flipped_vertically_image=cv2.rotate(src=rotated_90_counterclockwise_image,rotateCode=cv2.ROTATE_90_COUNTERCLOCKWISE)#先逆时针旋转90°再逆时针旋转90°就可以实现垂直翻转
rotated_180_image=cv2.rotate(src=image,rotateCode=cv2.ROTATE_180)#直接顺时针旋转180°
plt.subplot(2,2,1),plt.imshow(image),plt.title('原图')
plt.subplot(2,2,2),plt.imshow(flipped_horizontally_image),plt.title('顺时针旋转180度(水平+垂直翻转)')
plt.subplot(2,2,3),plt.imshow(flipped_vertically_image),plt.title('逆时针旋转180度(垂直翻转)')
plt.subplot(2,2,4),plt.imshow(rotated_180_image),plt.title('顺时针旋转180度(水平+垂直)')
plt.tight_layout()
结果:
除水平翻转外与使用cv2.flip()函数得到的结果一致
cv2.warpPerspective()透视变换
透视变换,也称为投影变换,是一种将图像从一个视角转换到另一个视角的几何变换。它能够改变图像的视角,使得原本在图像中由于透视效果而呈现倾斜或变形的对象,在变换后看起来更加符合人眼的视觉习惯。例如,我们可以将倾斜拍摄的矩形物体(如书本、建筑物的侧面)变换为正面视角的正方形图像。同样,我们也可以将正面的物体变换为倾斜视角的平行四边形图像。
cv2.warpPerspective()函数参数详解
src | 原始图像矩阵 |
M | 透视变换矩阵,使用cv2.getPerspectiveTransform()h函数传入两组坐标值后生成的矩阵 |
dsize | 指定透视变换后的图像外尺寸,通长是使用图像原始大小即可,(width,height) |
flags | 通常用来指定透视变换时的插值方式,与cv2.resize()函数的interplotations参数一致 |
borderMode | 图像缩放后填充方式,这里的填充方式与cv2.copyMakeBorder()函数一致 |
borderValue | 当borderMode为cv2.BORDER_CONSTANT时,使用borderValue值填充,彩色图像传入一个(B,G,R)的像素值三元组,灰度图传入一个值即可 |
cv2.getPerspectiveTransform()函数
cv2.getPerspectiveTransform() 是 OpenCV 中用于计算透视变换矩阵的函数。它接收两组四个对应点(源点和目标点),通过这些点计算出一个 3x3 的矩阵,该矩阵可用于将图像从一个视角变换到另一个视角。
示例
import cv2
import numpy as np
image = cv2.imread('test.jpg')
height, width = image.shape[:2]
'''
注意:图像坐标轴是以左上角为坐标原点!
*--------------->
| 在使用矩阵定义图像四个角点的坐标时,cv2默认顺序:
|
| 左上—>右上
| | |
| 左下<—右下
| 是逆时针的顺序
|
↓
'''
#定义源图像中的四个角点(矩形的四个角)
#坐标格式为[x,y]
src_points = np.float32([
[0, 0],#左上角
[width-1, 0],#右上角
[width-1, height-1],#右下角
[0, height-1]#左下角
])
#定义目标平行四边形的四个顶点
dst_points = np.float32([
[50, 0], #左上角向右偏移一些
[width-50, 0], #右上角向左偏移一些
[width-100, height-1],#右下角比右上角多向左偏移
[0, height-1] #左下角保持不变
])
# 计算透视变换矩阵
M=cv2.getPerspectiveTransform(src_points, dst_points)
warped_image=cv2.warpPerspective(src=image, M=M, dsize=(width, height))
#显示原始图像和变换后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Warped Image', warped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
图像缩放:图像金字塔
图像金字塔原理
图像金字塔
金字塔底部最大,然后逐层缩小,直至塔尖。如上图所示。将图像金字塔底部看成图像,最上方的图像尺寸最小,每操作一次,长宽各缩小一半,成为金字塔的上一层,如此循坏,直至缩小到用户期望的大小。
常用的金字塔包括以下两种:
高斯金字塔:用于向下采样,将图像缩小。
拉普拉斯金字塔:用于从上层采样重建下层图像,图像缩小或放大。
注意!这两种金字塔的上下采样方向与示例图片中的金字塔方向相反,你可以将示例图片中的金字塔倒置后理解上下采样,下采样是对图片进行缩放操作,上采样对图像进行放大操作。
高斯金字塔与拉普拉斯金字塔
1.高斯金字塔:
设定底层为低层级,顶层为高层级,按从下到上的次序编号,i+1层图像尺寸小于i层图像
。图像
下采样计算方法如下:
(1)将与高斯核卷积,高斯卷积核示例如下:
(2)高斯卷积运算就是对整幅图像进行加权平均的过程,每个像素点的值都由其本身领域内的其他像素值(权重不同)经过加权平均后得到。高斯核卷积哦临近中心的像素有更高的重要度,对周围像素计算加权平均值。
(3)去除卷积后图形的所有偶数行和列,得到。
显然,图像尺寸只有原图像的1/4.通过对输入图像不断迭代以上步骤,就可以得到期望大小的图像。
opencv内置函数cv2.pyrDown()可以实现这一过程。
2.拉普拉斯金字塔
图像向上取样是由小图像不断放大图像的过程,每次放大4倍,具体操作过程如下:
(1)将原始图像在每个方向扩大为原来的两倍,新增的行和列以0填充。
(2)前述高斯卷积核乘以4后与放大后的图像卷积,获得''新增像素''的近似值如下:
可以认为拉普拉斯金字塔是残差金字塔,用于存储下采样后图像与原始图像的差异。因为下采样过程中丢失的信息时不能通过上采样完全恢复,也就是说下采样是不可逆的。
拉普拉斯金字塔第i层数学定义:
其中,
分别表示第i,i+1层图像,UP()为操作扩充尺寸,
是5x5高斯卷积核。
在opencv中,cv2.pyrUp()函数实现的是操作,因此我们可以使用
来计算拉普拉斯金字塔
cv2.pyrUP()与cv2.pyrDown()实现图像金字塔上下采样
cv2.pyrUp()与cv2.pyrDown()函数参数详解:
src | 原图像数据 |
dstsize | 指定尺寸元祖或列表,必须是int型,宽和高必须是在原始图像尺寸上的(1/2^n)或2^n 不指定dsize默认缩小至1/4以及放大至4倍。 |
borderType | 边界填充方式,与cv2.copyMakeBorder()函数中的borderType类型一致 |
示例
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
'''
cv2.pyrUp()和cv2.pyrDown()函数参数:
src:原图像数据
dstsize:指定尺寸元祖或列表,必须是int型,宽和高必须是在原始图像尺寸上的(1/2^n)或2^n
不指定dstsize默认缩小至1/4以及放大至4倍。
borderType:边界填充方式,与cv2.copyMakeBorder()函数类型一致
'''
image=cv2.imread('test.jpg',cv2.IMREAD_COLOR_RGB)
width,height=image.shape[:2]#500x500
downsampled=cv2.pyrDown(image,dstsize=(width//2,height//2))#缩放至250x250
upsampled=cv2.pyrUp(image)#dstsize默认为(width*2,height*2)
plt.subplot(1,3,1),plt.title('原始图像(500x500)'),plt.imshow(image)
plt.subplot(1,3,2),plt.title('下采样(250x250)'),plt.imshow(downsampled)
plt.subplot(1,3,3),plt.title('上采样(1000x1000)'),plt.imshow(upsampled)
结果:
总结
以上便是opencv-python在图像预处理方面的所有常见操作,经过本文学习,读者将能够利用cv2解决实际的图像处理问题,为日后的计算机视觉项目打下坚实基础。