一、同时显示多幅图像

在一张图片上显示多幅图片
参考博客:matplotlib中的plt.figure()、plt.subplot()、plt.subplots()、add_subplots以及add_axes的使用

import matplotlib.pyplot as plt
import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\ss\8.JPG')
img2=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\ss\9.JPG')
img3=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\ss\16.JPG')
img4=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\ss\20.JPG')
plt.figure()#定义画板相关参数
plt.subplot(2,2,1)#两行两列的第一行第一列
plt.imshow(img1)
plt.title('1')
plt.axis('off')
plt.subplot(2,2,2)#两行两列的第一行第二列
plt.imshow(img2)
plt.title('2')
plt.axis('off')
plt.subplot(2,2,3)#两行两列的第二行第一列
plt.imshow(img3)
plt.title('3')
plt.axis('off')
plt.subplot(2,2,4)#两行两列的第二行第二列
plt.imshow(img4)
plt.title('4')
plt.axis('off')
plt.show()

运行结果:

二、相关函数的学习

参考博客:OpenCV 例程 200 篇

1.图像的读取(cv2.imread)

cv.imread(filename,flags)
参数说明:
filename:读取图像的文件路径和文件名
flags:读取图片的方式,可选项
flag=1:始终将图像转换为 3 通道BGR彩色图像,如果未定义,默认flag=1
flag=0:始终将图像转换为单通道灰度图像
flag=-1:按原样返回加载的图像(使用Alpha通道)
flag=8:在输入具有相应深度时返回16位/ 32位图像,否则将其转换为8位
flag=4:以任何可能的颜色格式读取图像
返回值:读取的 OpenCV 图像,nparray 多维数组

2.图像的保存(cv2.imwrite)

函数 cv2.imwrite() 用于将图像保存到指定的文件夹。
参数:
filename:要保存的文件的路径和名称,包括文件扩展名
img:要保存的 OpenCV 图像,nparray 多维数组。
注意事项:
(1).cv2.imwrite() 保存的是 OpenCV 图像(多维数组),不是 cv2.imread() 读取的图像文件,所保存的文件格式是由 filename 的扩展名决定的,与读取的图像文件的格式无关。
(2).对 4 通道 BGRA 图像,可以使用 Alpha 通道保存为 PNG 图像。
(3).cv2.imwrite() 指定图片的存储路径和文件名,在 python3 中不支持中文和空格(但并不会报错)。必须使用中文时,可以使用 cv2.imencode() 处理。

3.图像的显示(cv2.imshow)

imshow(winname, img)
参数
winname:字符串,显示窗口的名称。
img:所显示的 OpenCV 图像,nparray 多维数组。
显示图像的缩放取决于图像深度:
对 8 位无符号图像,按原样显示;
对 16 位无符号或 32 位整数图像,将像素值范围 [0,255 * 256] 映射到 [0,255] 显示;
对 32 位浮点图像,将像素值范围 [0,1] 映射到 [0,255] 显示;
如果指定窗口尚未创建,则创建一个自适应图像大小的窗口;
如果要显示大于屏幕分辨率的图像,需要先调用 namedWindow(“”,WINDOW_NORMAL)。
注意事项:
(1).函数 cv2.imshow() 之后要用 waitKey() 函数设定图像窗口的显示时长,否则不会显示图像窗口。
(2).图像窗口将在 waitKey() 函数所设定的时长(毫秒)后自动关闭,waitKey(0) 表示窗口显示时长为无限。
(3).可以创建多个不同的显示窗口,每个窗口必须命名不同的 filename。
(4).可以用 destroyWindow() 函数关闭指定的显示窗口,也可以用 destroyAllWindows() 函数关闭所有的显示窗口。

#imread/imwrite/imshowimport cv2
import numpy as np
img1_path=r'C:\Users\lenovo\Desktop\imagstudy\1.jfif'
img2_path=r'C:\Users\lenovo\Desktop\imagstudy\2.jfif'a1=cv2.imread(img1_path,flags=0)
a2=cv2.imread(img1_path,flags=1)
b1=cv2.imread(img2_path,flags=0)
b2=cv2.imread(img2_path,flags=1)
cv2.imshow('image1-0',a1)
cv2.imshow('image1-1',a2)
cv2.imshow('image2-0',b1)
cv2.imshow('image2-1',b2)
key = cv2.waitKey(0)#函数 cv2.imshow()之后要用waitKey()函数设定图像窗口的显示时长,否则不会显示图像窗口。waitKey(0) 表示窗口显示时长为无限。

4.plt.imshow()用 matplotlib 显示图像

补充:BGR与RGB

cv2.imread()接口读图像,读进来直接是BGR 格式数据格式在 0~255
cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。
cv2.COLOR_BGR2RGB 将BGR格式转换成RGB格式
cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片
plt.imshow() 可以直接显示 OpenCV 灰度图像,不需要格式转换,但需要使用 cmap=‘gray’ 进行参数设置。
参考博客:cv2.imread()和cv2.cvtColor() 的使用

import matplotlib.pyplot as plt
import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\imgstudy\1.jfif')plt.subplot(2,2,1)
plt.imshow(img1)
plt.title('BGR')
plt.axis('off')img2=cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
plt.subplot(2,2,2)
plt.imshow(img2)
plt.title('RGB')
plt.axis('off')img3=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
plt.subplot(2,2,3)
plt.imshow(img3)
plt.title('GRAY')#未设置 cmap=‘gray’,灰度图像的颜色显示异常
plt.axis('off')plt.subplot(2,2,4)
plt.imshow(img3,cmap='gray')
plt.title('cmap=gray')
plt.axis('off')
plt.show()

运行结果
(如果用cv2.imshow()显示图片,不用做BGR与RGB的转换,如果用plt.imshow()显示图片,需要使用cv2.cvtColor原图片的色彩)

5.图像的属性

img.ndim:查看图像的维数,彩色图像的维数为 3,灰度图像的维数为 2。
img.shape:查看图像的形状,即图像栅格的行数(高度)、列数(宽度)、通道数。
img.size:查看图像数组元素总数,灰度图像的数组元素总数为像素数量,彩色图像的数组元素总数为像素数量与通道数的乘积
img.dtype:元素类型

#图像属性import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\imgstudy\1.jfif',flags=1)#彩色图像
img2=cv2.imread(r'C:\Users\lenovo\Desktop\imgstudy\1.jfif',flags=0)#灰度图像print('img1的维度是:{},img2的维度是:{}'.format(img1.ndim,img2.ndim))
print('img1的形状是:{},img2的形状是:{}'.format(img1.shape,img2.shape))
print('img1的像素数量是:{},img2的像素数量是:{}'.format(img1.size,img2.size))
print('img1的元素类型是:{},img2的元素类型是:{}'.format(img1.dtype,img2.dtype))

运行结果:

img1的维度是:3,img2的维度是:2
img1的形状是:(799, 1200, 3),img2的形状是:(799, 1200)
img1的像素数量是:2876400,img2的像素数量是:958800
img1的元素类型是:uint8,img2的元素类型是:uint8

6.像素的编辑(img.itemset)

(1)img[a,b]

通过访问数组元素,直接获取像素值,通过img[a,b]操作可以直接定位到对应的pixel(像素)

import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\crop.jpg',flags=1)
px=img1[10,10]
print('img1[10,10]的像素值是:{}'.format(px))运行结果:
img1[10,10]的像素值是:[168  38  61]

也可通过for循环遍历B G R三个通道

import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\crop.jpg',flags=1)x,y=10,10
#通过for循环遍历数组
print('img1[{},{}]的像素值是'.format(x,y))
for i in range(3):print(img1[x,y,i],end=' ')# i=0,1,2 对应 B,G,R 通道运行结果:
img1[10,10]的像素值是
168 38 61

从结果可看出此两种方法等同。

(2)img.item()

img.item()也是可以直接定位到对应的pixel(像素)

import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\crop.jpg',flags=1)x,y=10,10
#print(img1.item(x,y))
#通过for循环遍历数组
print('img1[{},{}]的像素值是'.format(x,y))
for i in range(3):print(img1.item(x,y,i),end=' ')# i=0,1,2 对应 B,G,R 通道

但是需注意:
直接print(img1.item(x,y))会报错!!!参考博客img.item()跟img[x,y]

(3)修改像素img.itemset()

img.itemset语法:img.itemset((x,y,channel),newvalue)
将 [x,y,channel] 的值修改为 newValue,channel可为0,1,2

import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\crop.jpg',flags=1)x,y=10,10
#print(img1.item(x,y))
#通过for循环遍历数组
print('img1[{},{}]的像素值是'.format(x,y))
for i in range(3):print(img1.item(x,y,i),end=' ')# i=0,1,2 对应 B,G,R 通道img1.itemset((10,10,2),234)#img.itemset语法:img.itemset((x,y,channel),newvalue)
#将 [x,y,channel] 的值修改为 newValue
print('\n修改后的像素值是:{}'.format(img1[x,y]))运行结果:
img1[10,10]的像素值是
168 38 61
修改后的像素值是:[168  38 234]

7.图像的拷贝(np.copy)

使用 Numpy 的 np.copy() 函数可以进行图像的复制(不能通过直接赋值进行图像的复制,这样会改变原图像!!!
(1)Python 中的 “复制” 有无拷贝、浅拷贝和深拷贝之分,无拷贝相当于引用,浅拷贝只是对原变量内存地址的拷贝,深拷贝是对原变量(ndarray数组)的所有数据的拷贝。
(2)Numpy 直接赋值是无拷贝,np.copy() 方法是深拷贝,切片操作是特殊的浅拷贝。
(3)直接赋值得到的新图像相当于引用,改变新图像的值时原图像的值也发生改变;np.copy() 方法复制图像(ndarray数组)得到的新图像才是深拷贝,改变复制图像的形状或数值,原来图像并不会发生改变。

#图像拷贝
import cv2
import matplotlib.pyplot as pltimgg = cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=1)  # flags=1 读取彩色图像(BGR)
img1=cv2.cvtColor(imgg,cv2.COLOR_BGR2RGB)
img2 = img1.copy()
print("img2=img1.copy(), img2 is img1?", img2 is img1)
for col in range(200):for row in range(200): img2[col, row, :] = 255 #将此处的像素修改为255imggg = cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=1)  # flags=1 读取彩色图像(BGR)
img3=cv2.cvtColor(imggg,cv2.COLOR_BGR2RGB)
img4 = img3
print("img4=img3, img4 is img3?", img4 is img3)
for col in range(300):for row in range(300):img4[col, row, :] = 0  #将此处的像素修改为0plt.subplot(2,2,1)
plt.imshow(img1)
plt.title('img1')
plt.axis('off')plt.subplot(2,2,2)
plt.imshow(img2)
plt.title('img2')
plt.axis('off')plt.subplot(2,2,3)
plt.imshow(img3)
plt.title('img3')
plt.axis('off')plt.subplot(2,2,4)
plt.imshow(img4)
plt.title('img4')
plt.axis('off')
plt.show()
key = cv2.waitKey(0)  # 等待按键命令

运行结果:

img2=img1.copy(), img2 is img1? False
img4=img3, img4 is img3? True

图片结果:
从图片结果可以看出,使用copy函数,原图不会改变,而使用直接赋值,原图也会改变。

8. 图像的裁剪(cv2.selectROI)

(1)Numpy的切片方法

用 Numpy 的切片方法可以进行图像的裁剪,操作简单方便。

 img[y:y+h, x:x+w].copy()img:图像数据,nparray 多维数组
x, y:整数,像素值,裁剪矩形区域左上角的坐标值
w, h:整数,像素值,裁剪矩形区域的宽度、高度
返回值 :裁剪后获得的 OpenCV 图像,nparray 多维数组

原图:

把左边柱子部分裁掉,代码如下:

import cv2img1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)# flags=1 读取彩色图像(BGR)ymin,xmin,h,w=0,84,576,768 # 矩形裁剪区域 (ymin:ymin+h, xmin:xmin+w) 的位置参数
img2=img1[ymin:ymin+h,xmin:xmin+w].copy()# 切片获得裁剪后保留的图像区域
cv2.imshow("img2",img2)
print(img2.shape) #打印图像的高度,宽度,通道数
cv2.waitKey(0)结果如下:
(576, 684, 3)

(2)cv2.selectROI()鼠标选择

函数 cv2.selectROI() 可以通过鼠标选择感兴趣的矩形区域(ROI)参考博客:cv2.selectROI用法、参数 、返回值的解读
cv2.selectROI(windowName, img, showCrosshair=None, fromCenter=None):
参数windowName:选择的区域被显示在的窗口的名字
参数img:要在什么图片上选择ROI
参数showCrosshair:是否在矩形框里画十字线
参数fromCenter:是否是从矩形框的中心开始画
代码如下:

import cv2
img3=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
roi = cv2.selectROI(img3, showCrosshair=True, fromCenter=False)
print(roi)
xmin, ymin, w, h = roi  # 矩形裁剪区域(ymin:ymin+h, xmin:xmin+w) 的位置参数
imgROI = img1[ymin:ymin+h, xmin:xmin+w].copy()  # 切片获得裁剪后保留的图像区域
cv2.imshow("DemoRIO", imgROI)
cv2.imwrite(r'C:\Users\lenovo\Desktop\ROI.jpg',imgROI)
cv2.waitKey(0)运行结果:
(97, 123, 482, 426)

鼠标选定要裁剪的区域,按enter

裁剪结果如下:

9.图像的拼接(np.hstack)

(1)np.hstack() 按水平方向(列顺序)拼接 2个或多个图像,图像的高度(数组的行)必须相同。
(2)np.vstack() 按垂直方向(行顺序)拼接 2个或多个图像,图像的宽度(数组的列)必须相同。
(3)np.hstack() 和 np.vstack() 只是简单地将几张图像直接堆叠而连成一张图像,并未对图像进行特征提取和边缘处理,因而并不能实现图像的全景拼接。
水平拼接代码如下:

import cv2
import numpy as npimg1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
img2=cv2.imread(r'C:\Users\lenovo\Desktop\imgstudy\1.jfif',flags=1)
img1_resize=cv2.resize(img1,(400,400))
img2_resize=cv2.resize(img2,(400,400))img_hstack=np.hstack((img1_resize,img2_resize))
cv2.imshow('hstack',img_hstack)
cv2.waitKey(0)

运行结果如图:

10.图像通道的拆分(cv2.split)

函数 cv2.split() 将 3 通道 BGR 彩色图像分离为 B、G、R 单通道图像

cv2.split(img[, mv]) -> retval # 图像拆分为 BGR 通道
img:图像数据,nparray 多维数组
mv:指定的分拆通道(可选)

(1)对于 openCV 使用的 BGR 格式图像,返回的分拆通道的次序为 B、G、R 通道。
(2)BGR 彩色图像的数据形状为 (width, height, channels=3),返回的 B/G/R 通道的数据形状为 (width, height),不能按照 BGR 彩色图像直接显示。
(3)如果直接用 imshow 显示返回的单通道对象,将被视为 (width, height) 形状的灰度图像显示。如果要正确显示某一颜色分量,需要增加另外两个通道值(置 0)转换为 BGR 三通道格式,再用 imshow 才能显示为拆分通道的颜色。
(4)cv2.split() 操作复杂耗时,可以直接使用 NumPy 切片得到分离通道。


import cv2
import numpy as npimg1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
B_img,G_img,R_img=cv2.split(img1)
cv2.imshow('BGR',img1)
cv2.imshow('B_img',B_img)
img_zeros=np.zeros_like(img1)#np.zeros_like() 等方法创建与已有图像大小、类型相同的新图像。
img_zeros[:,:,1]=G_img #img_zeros[:,:,1]:0,1,2分别对应B,G,R三个通道
cv2.imshow('img_zeros',img_zeros)
cv2.waitKey(0)

显示BGR彩色图像:

imshow 显示返回的单通道对象,将被视为 (width, height) 形状的灰度图像显示:

如果要正确显示某一颜色分量,需要增加另外两个通道值(置 0)转换为 BGR 三通道格式,再用 imshow 才能显示为拆分通道的颜色。下图是G通道G_img:

补充:NumPy 切片分离通道

使用 NumPy 切片得到分离通道更为简便,而且运行速度比 cv2.split 更快。

#numpy切片分离三通道import cv2
import matplotlib.pyplot as plt
import numpy as npimg1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)B_img=img1.copy()
B_img[:,:,1]=0
B_img[:,:,2]=0G_img=img1.copy()
G_img[:,:,0]=0
G_img[:,:,2]=0R_img=img1.copy()
R_img[:,:,0]=0
R_img[:,:,1]=0#消除B通道:
GR_img=img1.copy()
GR_img[:,:,0]=0plt.subplot(2,2,1)
plt.imshow(B_img)
plt.title('1. B channel')
plt.axis('off')plt.subplot(2,2,2)
plt.imshow(G_img)
plt.title('2. G channel')
plt.axis('off')plt.subplot(2,2,3)
plt.imshow(R_img)
plt.title('3. R channel')
plt.axis('off')plt.subplot(2,2,4)
plt.imshow(GR_img)
plt.title('4. GR channel')
plt.axis('off')
plt.show()

运行结果:

11.图像通道的合并(cv2.merge)

(1)进行合并的 B、G、R 单通道图像分量,数据形状必须为 (width, height),而不是形状为 (width, height, channels=3) 的蓝色/绿色/红色图像。
(2)单通道图像分量的图像大小 (width, height) 必须相同才能进行合并。
(3)颜色通道要按照 B、G、R 通道次序合并,才能得到 BGR 格式的合并结果。
(4)cv2.merge() 操作复杂耗时,推荐使用 NumPy 数组合并函数 np.stack() 生成合成图像。

#图像通道的合并import cv2
import numpy as npimg1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
B_img,G_img,R_img=cv2.split(img1)# cv2.merge 实现图像通道的合并
img_merge=cv2.merge([B_img,G_img,R_img])
cv2.imshow('merge',img_merge)#np.stack()实现通道的合并
img_stack=np.stack([B_img,G_img,R_img],axis=2)#不太理解参数axis
cv2.imshow('stack',img_stack)print(img_merge.shape,img_stack.shape)
cv2.waitKey(0)运行结果:
(576, 768, 3) (576, 768, 3)

merge:

np.stack:

Merge 与 Stack 不仅形状相同,而且每个位置的元素相等,表明 cv2.merge() 与 np.stack() 方法合并图像通道的结果是相同的。

12.图像的加法运算(cv2.add)

语法:
cv2.add(img1, img2)
img1为图片1,img2为图片2
(1)OpenCV 加法和 numpy 加法之间有区别:cv2.add() 是饱和运算(相加后如大于 255 则结果为 255),而 Numpy 加法是模运算。
(2)使用 cv2.add() 函数对两张图片相加时,图片的大小和类型(通道数)必须相同

#图像的加法:import cv2
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
imgg=cv2.imread(r'C:\Users\lenovo\Desktop\bridge.jpg',flags=1)
img1=cv2.resize(img,(400,400))
img2=cv2.resize(imgg,(400,400))
img_cvadd=cv2.add(img1,img2)
img_npadd=img1+img2plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(img_cvadd,cv2.COLOR_BGR2RGB))
plt.title('1. img_cvadd')
plt.axis('off')plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(img_npadd,cv2.COLOR_BGR2RGB))
plt.title('2,img_npadd')
plt.axis('off')
plt.show()

运行结果:

饱和加法cv2.add()以 255 为上限,所有像素只会变的更白(大于原值);取模加法numpy以 255 为模,会导致部分像素变黑 (小于原值)。
因此,一般情况下应使用 cv2.add 进行饱和加法操作,不宜使用 numpy 取模加法。

13.图像与标量相加(cv2.add)

参考博客:【OpenCV 例程200篇】14. 图像与标量相加(cv2.add)

14.图像的加权加法(cv2.addWeight)

语法:cv2.addWeighted(src1, alpha, src2, beta, gamma)
scr1, scr2:ndarray 多维数组,表示一个灰度或彩色图像
alpha:第一张图像 scr1 的权重,通常取为 0~1 之间的浮点数
beta:第二张图像 scr2 的权重,通常取为 0~1 之间的浮点数
gamma: 灰度系数,图像校正的偏移量,用于调节亮度

#图像的加权加法:import cv2
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
imgg=cv2.imread(r'C:\Users\lenovo\Desktop\bridge.jpg',flags=1)
img1=cv2.resize(img,(400,400))
img2=cv2.resize(imgg,(400,400))
img_cvadd1=cv2.addWeighted(img1,0.8,img2,0.2,0)
img_cvadd2=cv2.addWeighted(img1,0.6,img2,0.4,0)
img_cvadd3=cv2.addWeighted(img1,0.4,img2,0.6,0)
plt.subplot(2,2,1)
plt.imshow(cv2.cvtColor(img_cvadd1,cv2.COLOR_BGR2RGB))
plt.title('1. img_cvadd1')
plt.axis('off')plt.subplot(2,2,2)
plt.imshow(cv2.cvtColor(img_cvadd2,cv2.COLOR_BGR2RGB))
plt.title('2. img_cvadd2')
plt.axis('off')plt.subplot(2,2,3)
plt.imshow(cv2.cvtColor(img_cvadd3,cv2.COLOR_BGR2RGB))
plt.title('3. img_cvadd3')
plt.axis('off')
plt.show()

运行结果:

15.不同尺寸的图像加法

参考博客不同尺寸的图像加法

16.两张图像的渐变切换

#图像的渐变切换
import cv2
import numpy as np
img=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
imgg=cv2.imread(r'C:\Users\lenovo\Desktop\bridge.jpg',flags=1)
img1=cv2.resize(img,(400,400))
img2=cv2.resize(imgg,(400,400))wList = np.arange(0.0, 1.0, 0.05)  # start, end, step
for w in wList:imgAddW = cv2.addWeighted(img1, w, img2, (1 - w), 0)cv2.imshow("imgAddWeight", imgAddW)cv2.waitKey(1000)

上述代码可以观察到img1到img2的渐变过程(img1的权重从0开始,每次以0.05叠加,直至img1的权重为1)

17. 图像的掩模加法/图像的圆形遮罩/图像的按位运算/图像的叠加/图像添加非中文文字/图像添加中文

此部分暂时用不着,略过不学
具体参考博客:OpenCV 例程 200 篇

18.图像的仿射变换

仿射变换,指一个向量空间进行线性变换+平移变成另外一个向量空间,它需要一个变换矩阵,而由于仿射变换较为复杂,一般很难找出这个矩阵,于是opencv提供了cv2.getAffineTransform()
cv2.getAffineTransForm()通过找原图像中三个点的坐标和变换图像的相应三个点坐标,创建一个2X3的矩阵。最后这个矩阵会被传给函数cv2.warpAffine()
(1)cv2.getAffineTransform( pts1 , pts2)
函数作用:构建变换矩阵
pts1 原图像三个点的坐标
pts2 原图像三个点在变换后相应的坐标
(2)cv2.warpAffine()
cv2.warpAffine(img, MA, (cols, rows))
img:原图像
MA:变换矩阵
(cols,rows):此图像的长、宽。

#图像的仿射变换
import cv2
import matplotlib.pyplot as plt
import numpy as npimg1=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
rows, cols, ch = img1.shape#rows:行数, cols:列数,ch:通道数pts1 = np.float32([[50, 50], [200, 50], [50, 200]])  # 初始位置
pts2 = np.float32([[50, 100], [200, 50], [150, 250]])  # 终止位置
MA = cv2.getAffineTransform(pts1, pts2)  # 计算 2x3 变换矩阵 MA
dst = cv2.warpAffine(img1, MA, (cols, rows))  # 实现仿射变换plt.figure(figsize=(9,6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)), plt.title("Original")
plt.subplot(122), plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)), plt.title("warpAffine")
plt.show()

运行结果:

19.图像的平移变换

原理参考博客:图像的平移
(1)、变换矩阵 MA 反映平移或旋转的关系,是 np.float32 类型 ndarray 二维数组(2行*3列)。
(2)、平移变换矩阵 M = [(1,0,dx), (0,1,dy)],Tx 表示向右(负值向左)移动像素点数,Ty 表示向下(负值向上)移动像素点数。
(3)、输出图像的大小 dsize 的格式为元组 (width,height)。

import matplotlib.pyplot as plt
import numpy as np
import cv2img=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
rows,cols,channel=img.shapedx,dy=100,20
MA=np.float32([[1,0,dx],[0,1,dy]])#构造平移矩阵
dst=cv2.warpAffine(img,MA,[cols,rows],borderValue=(128,128,128))#borderValue:128即采用灰色填充plt.figure(figsize=(9,6))
plt.subplot(121),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)),plt.title('Before translation')
plt.subplot(122),plt.imshow(cv2.cvtColor(dst,cv2.COLOR_BGR2RGB)),plt.title('After translation')
plt.show()

运行结果如下:

20.图像的旋转(以原点为中心)

此节可以略过,直接跳到第21节(以任意点为中心的旋转),原理参考博客图像的旋转(以原点为中心)

  • 求出旋转变换矩阵 MA,由函数 cv2.warpAffine 可以实现任意角度和任意中心的旋转效果。
  • 以图像中心作为旋转中心时,可以用 img.shape 获得图像的宽度和高度值,除以 2 就是图像中心点坐标。
  • 旋转角度为 90,180,270 度时,可以用 cv2.rotate(src, rotateCode)
    函数实现,该方法实际上是通过矩阵转置实现的,因此速度很快。
#图像的旋转(以原点为中心旋转), (以原点 (0,0) 为中心顺时针旋转)
import cv2
import matplotlib.pyplot as plt
import numpy as npimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
rows,cols,ch=img.shapetheta=np.pi/30
costheta=np.cos(theta)
sintheta=np.sin(theta)MA=np.float32([[costheta,-sintheta,0],[sintheta,costheta,0]])#构造绕原点顺时针旋转的矩阵
dst=cv2.warpAffine(img,MA,(cols,rows),borderValue=(128,128,128))plt.figure(figsize=(9,6))
plt.subplot(121),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)),plt.title('Before ROtation')
plt.subplot(122),plt.imshow(cv2.cvtColor(dst,cv2.COLOR_BGR2RGB)),plt.title('After ROtation')
plt.show()

运行结果:

21.图像的旋转(以任意点为中心)

原理参考博客:图像的旋转(以任意点为中心)
OpenCV 提供了 cv2.getRotationMatrix2D 函数, 根据旋转角度和位移计算旋转变换矩阵 MA。
cv2.getRotationMatrix2D(center, angle, scale) → MA
语法:

  • center:旋转中心坐标,二元元组 (x0, y0)
  • angle:旋转角度,单位为角度,逆时针为正数,顺时针为负数
  • scale:缩放因子 返回值:
  • MA: 旋转变换矩阵,2行3列
import cv2
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
height,width,channel=img.shape#cv2.getRotationMatrix2D(center, angle, scale) → M
theta1,theta2=30,90 #定义旋转角度
x0,y0=width//2,height//2 #定义旋转中心
MA1=cv2.getRotationMatrix2D((x0,y0),theta1,1)
MA2=cv2.getRotationMatrix2D((x0,y0),theta2,1)
dst1=cv2.warpAffine(img,MA1,(width,height),borderValue=(255,255,255))
dst2=cv2.warpAffine(img,MA2,(width,height),borderValue=(255,255,255))plt.figure(figsize=(9,6))
plt.subplot(131),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)),plt.title('original')
plt.subplot(132),plt.imshow(cv2.cvtColor(dst1,cv2.COLOR_BGR2RGB)),plt.title('Rotation 30')
plt.subplot(133),plt.imshow(cv2.cvtColor(dst2,cv2.COLOR_BGR2RGB)),plt.title('Rotation 90')
plt.show()

运行结果:

22.图像的翻转(cv2.flip)

cv2.flip(src, flipCode)
语法:

  • src:原图像
  • flipcode:控制参数,整型(int),flipCode>0 水平翻转,flipCode=0
    垂直翻转,flipCode<0 水平和垂直翻转
#图像的翻转:
import cv2
import matplotlib.pyplot as plt#img=cv2.imread(r'C:\Users\lenovo\Desktop\ExImage_beam_data_183925_00001.jpg',flags=1)
img=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
#flipCode:控制参数,整型(int),flipCode>0 水平翻转,flipCode=0 垂直翻转,flipCode<0 水平和垂直翻转
imgflip1=cv2.flip(img,flipCode=-1)
imgflip2=cv2.flip(img,flipCode=0)
imgflip3=cv2.flip(img,flipCode=1)plt.figure(figsize=(30,20))
plt.subplot(221),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)),plt.title('Before Flip'),plt.axis('off')
plt.subplot(222),plt.imshow(cv2.cvtColor(imgflip1,cv2.COLOR_BGR2RGB)),plt.title(' Flip1'),plt.axis('off')
plt.subplot(223),plt.imshow(cv2.cvtColor(imgflip2,cv2.COLOR_BGR2RGB)),plt.title(' Flip2'),plt.axis('off')
plt.subplot(224),plt.imshow(cv2.cvtColor(imgflip3,cv2.COLOR_BGR2RGB)),plt.title(' Flip3'),plt.axis('off')
plt.show()

运行结果:

23.图像的缩放(cv2.resize)

cv2.resize(src, dst, dsize, fx, fy, interpolation)
语法:

  • scr:变换操作的输入图像
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • fx, fy:x 轴、y 轴上的缩放比例,实型
  • interpolation:插值方法,整型 cv2.INTER_LINEAR:双线性插值(默认方法)
    cv2.INTER_AREA:使用像素区域关系重采样,缩小图像时可以避免波纹出现
    cv2.INTER_NEAREST:最近邻插值 cv2.INTER_CUBIC:4x4 像素邻域的双三次插值
    cv2.INTER_LANCZOS4:8x8 像素邻域的Lanczos插值
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组
#图像的缩放import cv2
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
img_resize1=cv2.resize(img,(416,416))
img_resize2=cv2.resize(img,None,fx=0.3,fy=0.4)
img_resize3=cv2.resize(img,(500,200),interpolation=cv2.INTER_NEAREST)plt.figure(figsize=(30,10))
plt.subplot(221),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)),plt.title('Before Resize'),plt.axis('off')
plt.subplot(222),plt.imshow(cv2.cvtColor(img_resize1,cv2.COLOR_BGR2RGB)),plt.title('resize1'),plt.axis('off')
plt.subplot(223),plt.imshow(cv2.cvtColor(img_resize2,cv2.COLOR_BGR2RGB)),plt.title('resize2'),plt.axis('off')
plt.subplot(224),plt.imshow(cv2.cvtColor(img_resize3,cv2.COLOR_BGR2RGB)),plt.title('resize3'),plt.axis('off')
plt.show()

运行结果:

24. 图像金字塔(cv2.pyrDown)

参考博客:图像金字塔(cv2.pyrDown)

25. 直角坐标与极坐标的转换

参考博客:https://blog.csdn.net/youcans/article/details/121416883

26.图像的灰度化处理和二值化处理

按照颜色对图像进行分类,可以分为二值图像、灰度图像和彩色图像。

  • 二值图像:只有黑色和白色两种颜色的图像。每个像素点可以用 0/1 表示,0 表示黑色,1 表示白色。
  • 灰度图像:只有灰度的图像。每个像素点用 8bit 数字 [0,255] 表示灰度,如:0 表示纯黑,255 表示纯白。
  • 彩色图像:彩色图像通常采用红色(R)、绿色(G)和蓝色(B)三个色彩通道的组合表示。
    OpenCV 中彩色图像使用 BGR 格式。彩色图像进行灰度化处理,可以在读取图像文件时直接读取为灰度图像,也可以通过颜色空间转换函数 cv2.cvtColor 将彩色图像转换为灰度图像。
import cv2img_original=cv2.imread(r'C:\Users\lenovo\Desktop\ExImage_beam_data_183925_00001.jpg',flags=1)
img_flags=cv2.imread(r'C:\Users\lenovo\Desktop\ExImage_beam_data_183925_00001.jpg',flags=0)#读取图像文件时flags=0直接读取为灰度图像
img_Gray=cv2.cvtColor(img_original,cv2.COLOR_BGR2GRAY)
img_RGB=cv2.cvtColor(img_original,cv2.COLOR_BGR2RGB)
cv2.imshow('flags=0',img_flags)
cv2.imshow('oringinal',img_original)
cv2.imshow('RGB',img_RGB)
cv2.imshow('Gray',img_Gray)
cv2.waitKey(0)

cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
语法:

  • scr:变换操作的输入图像,nparray 二维数组,必须是单通道灰度图像!
  • thresh:阈值,取值范围 0~255
  • maxval:填充色,取值范围 0~255,一般取 255。
  • type:变换类型
    cv2.THRESH_BINARY:大于阈值时置 255,否则置 0
    cv2.THRESH_BINARY_INV:大于阈值时置 0,否则置 255
    cv2.THRESH_TRUNC:大于阈值时置为阈值 thresh,否则不变(保持原色)
    cv2.THRESH_TOZERO:大于阈值时不变(保持原色),否则置 0
    cv2.THRESH_TOZERO_INV:大于阈值时置
    0,否则不变(保持原色)
    cv2.THRESH_OTSU:使用 OTSU 算法选择阈值 返回值 retval:返回二值化的阈值 返回值
    dst:返回阈值变换的输出图像
    注意:
    (1)函数 cv2.threshold 进行固定阈值的二值化处理;函数 cv2.adaptiveThreshold 为自适应阈值的二值化处理函数,可以通过比较像素点与周围像素点的关系动态调整阈值。
    (2)确切地说,只有 type 为 cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV 时输出为二值图像,其它变换类型时进行阈值处理但并不是二值处理。
#图像的二值化处理
import cv2
import matplotlib.pyplot as pltimg_Gray=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=0)ret1,img1=cv2.threshold(img_Gray,75,255,cv2.THRESH_BINARY)#大于63时置为255,否则置为0
ret2,img2=cv2.threshold(img_Gray,75,255,cv2.THRESH_OTSU)#使用 OTSU 算法选择阈值
ret3,img3=cv2.threshold(img_Gray,75,255,cv2.THRESH_BINARY_INV)#大于160时置为0,否则置为255
ret4,img4=cv2.threshold(img_Gray,80,255,cv2.THRESH_TRUNC)#大于阈值时置为阈值 thresh,否则不变(保持原色)
ret5,img5=cv2.threshold(img_Gray,75,255,cv2.THRESH_TOZERO)#大于阈值时不变,保持原色,否则置零plt.figure(figsize=(50,6))
titlelist=['1.threshold=75','2.threshold=75(OTSU)','3.threshold=75(BINARY_INV)','4.threshold=75(TRUNC)','5.threshold=75(TOZERO)','6.original']
imglist=[img1,img2,img3,img4,img5,img_Gray]
for i in range(6):plt.subplot(2,3,i+1)plt.imshow(imglist[i],'gray')plt.title(titlelist[i])plt.axis('off')
plt.show()

运行结果:

函数 threshold() 可以将灰度图像转换为二值图像,图像完全由像素 0 和 255 构成,呈现出只有黑白两色的视觉效果。
灰度阈值化通过选取的灰度阈值 thresh,将每个像素的灰度值与阈值进行比较,将灰度大于阈值的像素点置为最大灰度,小于阈值的像素点置为最小灰度,得到二值图像,可以突出图像轮廓,把目标从背景中分割出来。

27. 图像的反色变换(图像反转)

图像的反色变换,即图像反转,将黑色像素点变白色,白色像素点变黑色。广义的反色变换也可以应用于彩色图像,即对所有像素点取补。
图像的反转处理可以增强暗色区域中的白色或灰色细节。
注意图像反转(Invert)与图像翻转(Flip)的区别:图像翻转是沿对称轴的几何变换,像素值不变;图像反转是像素颜色的逆转,像素位置不变。

#图像的反转:即图像像素取补
import cv2
import matplotlib.pyplot as plt
import numpy as npimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
img_gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
height,width,channel=img.shapeimginv=np.empty((height,width),np.uint8)#创建空白数组
for i in range(height):for j in range(width):imginv[i][j]=255-img_gray[i][j] #图像取补# plt.figure(figsize=(9,6))
# titlelist=['1.BGR','2.GRAY','3,inv']
# imglist=[img,img_gray,imginv]
# for i in range(3):
#     plt.subplot(1,3,i+1),plt.imshow(imglist[i]),plt.title(titlelist[i]),plt.axis('off')
# plt.show()cv2.imshow('original',img)
cv2.imshow('gray',img_gray)
cv2.imshow('inv',imginv)
cv2.waitKey(0)

运行结果:
原图:

像素取补:

28.图像灰度的线性变换

线性灰度变换将原始图像灰度值的动态范围按线性关系扩展到指定范围或整个动态范围。
线性灰度变化对图像的每一个像素作线性拉伸,可以凸显图像的细节,提高图像的对比度。
线性灰度变换可以由以下公式描述 :

式中,D 为原始图像的灰度值,Dt 为线性灰度变换后的图像灰度值。

  • 当 α=1,β=0 时,保持原始图像不变
  • 当α=1,β>0 时,图像的灰度值上移,灰度图像颜色发白(彩色图像颜色发亮)
  • 当α=1,β<0 时,图像的灰度值下移,灰度图像颜色发黑(彩色图像颜色发暗)
  • 当α>1 时,图像的对比度增强
  • 当 0<α<1 时,图像的对比度减小
  • 当α<0,β=255 时,图像暗区域变亮,亮区域变暗,图像求补
  • 当 α=−1,β=255 时,图像的灰度值反转
    直方图正规化是根据图像的最小灰度级和最大灰度级,将其拉伸到灰度级全域 [0,255] 的线性变换。
#图像的线性灰度变换
import cv2
import matplotlib.pyplot as plt
import numpy as npimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=1)
img_gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
h,w,ch=img.shapeimg1=np.empty((h,w),np.uint8)
img2=np.empty((h,w),np.uint8)
img3=np.empty((h,w),np.uint8)
img4=np.empty((h,w),np.uint8)
img5=np.empty((h,w),np.uint8)
img6=np.empty((h,w),np.uint8)
img7=np.empty((h,w),np.uint8)# Dt[i,j] = a*D[i,j] + b
a1,b1=1,0 # 保持原始图像不变
a2,b2=1,80 #图像的灰度值上移,灰度图像发白,彩色图像颜色发亮
a3,b3=1,-80#图像的灰度值下移,灰度图像发黑,彩色图像颜色变暗
a4=2 #图像对比度增强
a5=0.6 #图像对比度减弱
a6,b6=-0.5,255 #图像暗区域变亮,亮区域变暗,图像求补
a7,b7=-1,255 #图像反转for i in range(h):for j in range(w):img1[i][j] = min(255,max(img_gray[i][j]+b1,0))img2[i][j] = min(255, max(img_gray[i][j] + b2, 0))img3[i][j] = min(255, max(img_gray[i][j] + b3, 0))img4[i][j] = min(255, max(a4*img_gray[i][j], 0))img5[i][j] = min(255, max(a5*img_gray[i][j], 0))img6[i][j] = a6*img_gray[i][j] + b6img7[i][j] = min(255, max(a7*img_gray[i][j] + b7, 0))plt.figure(figsize=(9,6))
titlelist=['1.img_GRAY','2.a=1,b=0','3.a=1,b=80','4.a=1,b=-80','5.a=2','6.a=0.6','7.a=-0.5,b=255','8.a=-1,b=255']
imglist=[img_gray,img1,img2,img3,img4,img5,img6,img7]
for i in range(8):plt.subplot(3,3,i+1),plt.imshow(imglist[i],cmap='gray'),plt.title(titlelist[i]),plt.axis('off')
plt.show()

运行结果:

29.图像分段线性灰度变换

分段线性变换函数可以增强图像各部分的反差,增强感兴趣的灰度区间、抑制不感兴趣的灰度级。
分段线性函数的优点是可以根据需要拉伸特征物的灰度细节,一些重要的变换只能用分段函数来描述和实现,缺点则是参数较多不容易确定。
分段线性函数通用公式如下:

式中,D 为原始图像的灰度值,Dt 为线性灰度变换后的图像灰度值。

#图像的分段线性变换:对比度拉伸
import cv2
import numpy as np
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\ExImage_beam_data_183925_00001.jpg',flags=0)#直接读取为灰度图像
h,w=img.shaper1,s1=65,30
r2,s2=100,225k1=s1/r1
k2=(s2-s1)/(r2-r1)
k3=(255-s2)/(255-r2)
imgStretch=np.empty((h,w),np.uint8)for i in range(h):for j in range(w):if img[i][j]<r1:imgStretch[i][j]=k1*img[i][j]elif r1 <=img[i][j] <=r2:imgStretch[i][j]=k2*(img[i][j]-r1)+s1elif img[i][j]>r2:imgStretch[i][j]=k3*(img[i][j]-r2)+s2
plt.subplots_adjust(left=0.2, bottom=0.2, right=0.9, top=0.8, wspace=0.1, hspace=0.1)
plt.subplot(131), plt.title("s=T(r)")
x = [0, 65, 100, 255]
y = [0, 30, 225, 255]
plt.plot(x, y)
plt.axis([0,256,0,256])
plt.text(65, 30, "(r1,s1)", fontsize=8)
plt.text(100, 215, "(r2,s2)", fontsize=8)
plt.xlabel("r, Input value")
plt.ylabel("s, Output value")
plt.subplot(132), plt.imshow(img, cmap='gray', vmin=0, vmax=255), plt.title("Original"), plt.axis('off')
plt.subplot(133), plt.imshow(imgStretch, cmap='gray', vmin=0, vmax=255), plt.title("Stretch"), plt.axis('off')
plt.show()

运行结果如下:

30.图像的灰度变换(灰度级分层)

灰度级分层可以突出图像中特定的灰度级区间,可以对灰度级进行分层处理。

灰度级分层有两种常用方案:一种方案是二值处理,将感兴趣的灰度级区间设为较大的灰度值,其它区间设为较小的灰度值;另一种方案是窗口处理,将感兴趣的灰度级区间设为较大的灰度值,其它区间不变。

两种灰度级分层方案的分段变换公式分别为:

式中,D 为原始图像的灰度值,Dt1、Dt2 为灰度变换后的图像灰度值。

#灰度级分层
import cv2
import matplotlib.pyplot as pltimg_Gray=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg',flags=0)
h,w=img_Gray.shapea,b=15,150
img1=img_Gray.copy()
img1[(img1[:,:]<a ) | (img1[:,:]>b)]=0
img1[(img1[:, :] >= a ) & (img1[:, :] <=b)]=128img2=img_Gray.copy()
img2[(img2[:,:]>=a) & (img2[:,:]<=b)]=225plt.figure(figsize=(9,6))
plt.subplot(131),plt.imshow(img_Gray,cmap='gray'),plt.title('Gray'),plt.axis('off')
plt.subplot(132),plt.imshow(img1,cmap='gray'),plt.title('Binary layered'),plt.axis('off')
plt.subplot(133),plt.imshow(img2,cmap='gray'),plt.title('Grayscale layered'),plt.axis('off')
plt.show()

运行结果:

31.图像的灰度变换(比特平面分层)

略过不学

32.图像的灰度变换(对数变换)

对数变换可以由以下公式描述:
对数曲线在像素值较低的区域斜率大,在像素值较高的区域斜率小。对数变换将输入中范围较窄的低灰度值映射为范围较宽的灰度级,输入中的高灰度值则被映射为范围较窄的灰度级。对数变换后,较暗区域的对比度提升,可以增强图像的暗部细节。

对数变换实现了扩展低灰度值而压缩高灰度值的效果,广泛应用于频谱图像的显示中。对数变换的典型应用是傅立叶频谱的动态范围很宽,直接显示时受显示设备动态范围的限制而丢失大量的暗部细节;使用对数变换将图像的动态范围进行非线性压缩后,就可以清晰地显示。

#灰度变换,对数变换
import mathimport cv2
import matplotlib.pyplot as plt
import numpy as npimg_gray=cv2.imread(r'C:\Users\lenovo\Desktop\ExImage_beam_data_183925_00001.jpg',flags=0)
h,w=img_gray.shapec=5
img_new=np.empty((h,w),np.uint8)
for i in range(h):for j in range(w):img_new[i][j]=c*(math.log(1+img_gray[i][j]))#经过log变换后0-255变成了0-5.5,需要在8比特的显示器中进行显示,则需要将其归一化规范化到(0,255)中,即使用normalize来实现
img_new = cv2.normalize(img_new,img_new,0,255,cv2.NORM_MINMAX)plt.figure(figsize=(9,6))
plt.subplot(121),plt.imshow(img_gray,cmap='gray'),plt.title('GRAY'),plt.axis('off')
plt.subplot(122),plt.imshow(img_new,cmap='gray'),plt.title('LOG'),plt.axis('off')
plt.show()

运行结果:

33.图像的灰度变换(伽马变换)

幂律变换也称伽马变换,可以提升暗部细节,对发白(曝光过度)或过暗(曝光不足)的图片进行矫正。幂律变换也称伽马变换,可以提升暗部细节,对发白(曝光过度)或过暗(曝光不足)的图片进行矫正。

伽马变换本质上是对图像矩阵中的每个值进行幂运算。0< gamma <1 时,拉伸图像中灰度级较低的区域,压缩灰度级较高的部分,增加图像的对比度;gamma >1时,拉伸图像中灰度级较高的区域,压缩灰度级较低的部分,降低图像的对比度。
伽马变换通过非线性变换对人类视觉特性进行补偿,最大化地利用有效的灰度级带宽。很多拍摄、显示、打印设备的亮度曲线都符合幂律曲线,因此伽马变换广泛应用于各种设备显示效果的调校,称为伽马校正。

#图像的灰度变换-伽马变换
import cv2
import matplotlib.pyplot as plt
import numpy as np
img=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=0)gammalist=[0.125, 0.25, 0.5, 1.0, 2.0, 4.0]plt.figure(figsize=(9,6))
for i in range(len(gammalist)):img_gamma = np.power(img, gammalist[i]) #power(x, y) 函数,计算 x 的 y 次方img_gamma=cv2.normalize(img_gamma,img_gamma,0,255,cv2.NORM_MINMAX)#cv2.normalize()按归一化到[0,255]img_gamma = cv2.convertScaleAbs(img_gamma,img_gamma) #cv2.convertScaleAbs()函数 (通过线性变换将数据转换成8位[uint8])plt.subplot(2,3,i+1),plt.imshow(img_gamma, cmap='gray'),plt.axis('off'),plt.title(f"$\gamma={gammalist[i]}$")
plt.show()

34.图像的灰度直方图

图像直方图是反映图像像素分布的统计表,横坐标代表像素值的取值区间,纵坐标代表每一像素值在图像中的像素总数或者所占的百分比。 灰度直方图是图像灰度级的函数,用来描述每个灰度级在图像矩阵中的像素个数。
灰度直方图反映了图像中的灰度分布规律,直观地表现了图像中各灰度级的占比,很好地体现出图像的亮度和对比度信息:灰度图分布居中说明亮度正常,偏左说明亮度较暗,偏右表明亮度较高;狭窄陡峭表明对比度降低,宽泛平缓表明对比度较高。
根据直方图的形态可以判断图像的质量,通过调控直方图的形态可以改善图像的质量。OpenCV 提供了函数 cv2.calcHist 可以计算直方图,Numpy 中的函数 np.bincount 也可以实现同样的功能。

cv2.calcHist(images, channels, mask, histSize, ranges[, hist[,> accumulate ]]) → hist
函数 cv2.calcHist 可以计算一维直方图或二维直方图,函数的参数 images, channels, histSize, ranges 在计算一维直方图时也要带 [] 号。
images:输入图像,用 [] 括号表示
channels: 直方图计算的通道,用 [] 括号表示
mask:掩模图像,一般置为 None
histSize:直方柱的数量,一般取 256
ranges:像素值的取值范围,一般为 [0,256]
返回值 hist:返回每一像素值在图像中的像素总数,形状为 (histSize,1)

注意:

  • 参数 images, channels, histSize, ranges 都要带 [] 号。
  • mask 是与 images 大小相同的掩模图像,掩模为 0 的区域不作处理。不使用掩模时设为 None。
  • channels 设置对彩色图像的指定通道计算直方图,灰度图像时设为 0。
  • Numpy 中的函数 np.bincount 也可以实现同样的功能,但该函数返回值的形状为 (histSize,)
#图像的灰度直方图  画出原图及经过伽马变换后的灰度直方图,gamma=0.6/对数变换 c=4import cv2
import matplotlib.pyplot as plt
import numpy as npimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=0)hist=cv2.calcHist([img],[0],None,[256],[0,256])plt.figure(figsize=(9,6))
plt.subplot(121),plt.axis('off'),plt.title('Origin'),plt.imshow(img,cmap='gray')
plt.subplot(122),plt.axis([0,255,0,np.max(hist)]),plt.bar(range(256),hist[:,0]),plt.title('Gray Hist')
plt.show()

运行结果:

35.直方图均衡化

原理部分不多阐述,OpenCV有一个函数可以这样做,cv.equalizeHist(),它封装好了计算cdf和cdf重映射以及根据cdf表生成直方图均衡图像的过程。它的输入只是灰度图像,输出是我们的直方图均衡图像。
语法:
cv2.qualizeHist(src[, dst]) → dst

  • src:输入图像 返回值
  • dst:输出图像,直方图均衡化
#直方图均衡import cv2
import matplotlib.pyplot as plt
import numpy as npimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=0)
img_equal=cv2.equalizeHist(img)
calhist1=cv2.calcHist([img],[0],None,[256],[0,256])
calhist2=cv2.calcHist([img_equal],[0],None,[256],[0,256])plt.figure(figsize=(30,20))
plt.subplot(221),plt.imshow(img,cmap='gray'),plt.title('Origin_img'),plt.axis('off')
plt.subplot(222,xticks=[], yticks=[]),plt.bar(range(256),calhist1[:,0]),plt.axis([0,255,0,np.max(calhist1)]),plt.title('The original Hist')
plt.subplot(223),plt.imshow(img_equal,cmap='gray'),plt.title('equalize_img'),plt.axis('off')
plt.subplot(224),plt.bar(range(256),calhist2[:,0]),plt.axis([0,255,0,np.max(calhist2)]),plt.title('The equalize Hist')
plt.show()

运行结果:

此法似乎不太适用于声呐图像

抬走吧!!!!

36.图像增强—直方图匹配

37.图像增强—彩色直方图匹配

38. 图像增强—局部直方图处理

39. 图像增强—直方图统计量图像增强

40. 图像增强—直方图反向追踪

41. 图像的相关与卷积运算

滤波通常是指对图像中特定频率的分量进行过滤或抑制。图像滤波是在尽可能保留图像细节特征的条件下对目标图像的噪声进行抑制,是常用的图像预处理操作。
数据采集都会带有一定的噪声,图像的噪声可以理解为灰度值的随机变化。对图像在空间域存在的随机噪声,可以通过平滑技术进行抑制或去除,称为空间域图像滤波。
频率域滤波是通过傅里叶变换方法实现的,而空间域滤波则是通过相关与卷积运算实现。常用的平滑处理算法有基于二维离散卷积的高斯平滑、均值平滑,基于统计方法的中值平滑,保留边缘信息的双边滤波、导向滤波等。
空间滤波器是由邻域和定义的操作构成的,滤波器规定了滤波时采用的邻域形状及该区域内像素值的处理方法。滤波器也被称为 “核”、“模板”、“窗口”、“掩模”、“算子”,一般在信号处理中称为 “滤波器”,在数学领域称为 “核”。线性滤波器就是指基于线性核的滤波,也就是卷积运算。
原理参考博客图像的相关与卷积运算

图像的边界扩充

相关和卷积运算都要对图像的边界点要进行特殊处理,就需要将边界进行适当扩充。
OpenCV 中提供了函数 cv.copyMakeBorder 进行边界扩充方式,也可以为图像设置边框。

cv.copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]]) → dst

  • src:进行边界扩充的图像 top, bottom, left, right:上侧、下侧、左侧、右侧边界扩充的的宽度(像素数)
  • value:当 borderType 为 BORDER_CONSTANT 时,以常量(value)填充扩充的边界,默认值为 (0,0,0)borderType 边界扩充的类型
  • cv2.BORDER_REPLICATE:复制,复制最边缘像素进行填充(aa | abcdefg | gg),中值滤波采用复制法
  • cv2.BORDER_REFLECT:对称法,以图像边缘为轴进行对称填充(cba| abcdefg | gfe)
  • cv2.BORDER_REFLECTT_101:倒映法,以图像最边缘像素为轴进行对称填充(dcb| abcdefg fed),函数,filter2D, blur, GaussianBlur, bilateralFilter 中默认的边界处理方法
  • cv2.BORDER_WRAP:用另一侧元素来填充这一侧的扩充边界(efg| abcdefg | ab)
  • cv2.BORDER_CONSTANT:以常数(value)作为像素值进行扩充(vv | abcdefg | vv)
#图像的相关与卷积运算import cv2
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=0)top = bottom = left = right = 50img1=cv2.copyMakeBorder(img,top,bottom,left,right,borderType=cv2.BORDER_REPLICATE) #复制,复制最边缘像素进行填充,中值滤波采用复制法
img2=cv2.copyMakeBorder(img,top,bottom,left,right,borderType=cv2.BORDER_REFLECT)#对称法,以图像边缘为轴进行对称填充
img3=cv2.copyMakeBorder(img,top,bottom,left,right,borderType=cv2.BORDER_REFLECT_101)#倒映法,以图像最边缘像素为轴进行对称填充,filter2D, blur, GaussianBlur, bilateralFilter 中默认的边界处理方法
img4=cv2.copyMakeBorder(img,top,bottom,left,right,borderType=cv2.BORDER_WRAP)#用另一侧的元素来填充这一侧的元素
img5=cv2.copyMakeBorder(img,top,bottom,left,right,borderType=cv2.BORDER_CONSTANT,value=(128,128,128))plt.figure(figsize=(9,6))
imglist=[img,img1,img2,img3,img4,img5]
titlelist=['1.Original','2.REPLICATE','3.REFLECT','4,REFLECT_101','5.WRAP','6.CONSTANT(value=128)']
for i in range(6):plt.subplot(2,3,i+1),plt.imshow(imglist[i],cmap='gray'),plt.title(titlelist[i]),plt.axis('off')
plt.show()

运行结果:

42. Scipy 实现图像二维卷积

Scipy 中提供了函数 sp.convolve2d 实现二维离散卷积的计算。

convolve2d(in1, in2, mode="full", boundary="fill", fillvalue=0) → dst
  • in1:进行卷积运算的图像,二维数组——只能处理单通道图像,如灰度图像
  • in2:卷积操作的模板(卷积核),二维数
  • mode:卷积类型,‘full’、‘valid’、‘same’,默认值为 ‘full’
  • boundary:边界扩充方式,‘fill’、‘wrap’、‘symm’,默认值为 ‘fill’
    (1) ‘fill’:以常数(fillvalue)作为像素值进行扩充(vv | abcdefg | vv)
    (2)‘symm’:对称法,以图像边缘为轴进行对称填充(cba| abcdefg | gfe)
    (3)‘wrap’:用另一侧元素来填充这一侧的扩充边界(efg| abcdefg | ab)
  • fillvalue:当 boundary=‘fill’ 时,以以常数(fillvalue)作为像素值进行扩充
#scipy.signal 实现图像的二维卷积
import cv2
import matplotlib.pyplot as plt
import numpy as np
from scipy import signalimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=0)
kernel=np.array([[-3-3j,0-10j,+3-3j],[-10+0j,0+0j,+10+0j], [-3+3j,0+10j,+3+3j]])img1=signal.convolve2d(img,kernel,mode='full',boundary='symm') # full 卷积
img2=signal.convolve2d(img,kernel,mode='valid',boundary='symm')#valid卷积
img3=signal.convolve2d(img,kernel,mode='same',boundary='symm') #same卷积
print(img.shape,img1.shape,img2.shape,img3.shape)plt.figure(figsize=(9,6))
imglist=[img,img1,img2,img3]
titlelist=['1.original','2.full_convolve2d','3.valid_convolve2d','4.same_convolve2d']
for i in range(4):plt.subplot(2,2,i+1),plt.imshow(np.abs(imglist[i]),cmap='gray',vmin=0, vmax=255),plt.title(titlelist[i]),plt.axis('off')
plt.show()

运行结果:

注意:

  1. signal.convolve2d 只能对二维矩阵进行卷积操作,因此只能处理灰度图像。如果需要处理彩色图像,可以分别对每一通道进行卷积操作来实现。
  2. signal.convolve2d 选择不同卷积类型 ‘full’、‘valid’、‘same’ 时,图像卷积效果的差别并不明显,但图像尺寸大小有区别,这与不同类型时采用不同的边界处理方式有关。

43. OpenCV 实现图像二维卷积

使用 OpenCV 中的 cv.flip 和 cv.filter2D 函数也可以实现图像的卷积运算。
函数 cv.flip 实现围绕轴线翻转二维阵列,将图像沿轴线进行轴对称变换,可以将图像沿水平方向、垂直方向、或水平/垂直方向同时进行翻转
函数 cv.filter2D 对图像与核(模板)进行相关计算,与函数 cv.flip 共同实现卷积运算。

cv.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]) → dst

  • src:卷积处理的输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:卷积处理的输出图像,大小和类型与 src 相同
  • ddepth:目标图像每个通道的深度(数据类型),ddepth=-1 表示与输入图像的数据类型相同
  • kernel:卷积操作的模板(卷积核),二维实型数组
  • anchor:卷积核的锚点位置,默认值 (-1, -1) 表示以卷积核的中心为锚点
  • delta:输出图像的偏移量,可选项,默认值为0
  • borderType:边界扩充的类型
    cv.filter2D 可以处理灰度图像,也可以直接处理彩色图像,不需要对每一色彩通道分别操作。
#cv2 实现图像的二维卷积import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=0)kernel= np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])kernel_flip=cv2.flip(kernel,flipCode=-1) # 将卷积核旋转180 度
img1=cv2.filter2D(img,ddepth=-1,kernel=kernel_flip,anchor=[-1,-1],borderType=cv2.BORDER_REPLICATE)
img2=cv2.filter2D(img,ddepth=-1,kernel=kernel_flip,anchor=[-1,-1],borderType=cv2.BORDER_REFLECT_101)plt.figure(figsize=(9,6))
imglist=[img,img1,img2]
titlelist=['1.original','2.REPLICATE_convolve2d','3.REFLECT_101_convolve2d']
for i in range(3):plt.subplot(1,3,i+1),plt.imshow(np.abs(imglist[i]),cmap='gray',vmin=0, vmax=255),plt.title(titlelist[i]),plt.axis('off')
plt.show()

运行结果:

44. 可分离卷积核

原理参考博客可分离卷积核
随着图像尺寸与卷积核尺寸的增大,用分离的卷积核依次对图像进行卷积操作,可以有效地提高运算速度。因此,在二维图像处理中,经常将一个可分离卷积核分解为一维水平核 kernalX 和一维垂直核 kernalY 的乘积。
函数 sepFilter2D 实现可分离核(模板)对图像进行线性滤波。

cv.sepFilter2D( src, ddepth, kernelX, kernelY[, dst[, anchor[, delta[, borderType]]]]) → dst # OpenCV4

  • src:卷积处理的输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:卷积处理的输出图像,大小和类型与 src 相同
  • ddepth:目标图像每个通道的深度(数据类型),ddepth=-1 表示与输入图像的数据类型相同
  • kernelX:水平卷积核向量,一维实型数组
  • kernelY:垂直卷积核向量,一维实型数组
  • anchor:卷积核的锚点位置,默认值 (-1, -1) 表示以卷积核的中心为锚点
  • delta:输出图像的偏移量,可选项,默认值为 0
  • borderType:边界扩充的类型

45. 低通盒式滤波器

46. 低通高斯滤波器

47. 非线性滤波—中值滤波

中值滤波是一种非线性滤波方法,是基于统计排序方法的滤波器 。中值滤波法将像素点的邻域内的所有像素点灰度值的中值作为该像素点的灰度值。

注意中值不是平均值,而是按大小排序的中间值。由于需要排序操作,中值滤波消耗的运算时间很长。

中值滤波处理后像素点的灰度值,可能保持不变,也可能改变为邻域内其它像素点的灰度值。

中值滤波对于消除图像中的椒盐噪声非常有效。椒盐噪声也称为脉冲噪声,是随机出现的白点或者黑点,通常是由于影像讯号受到干扰而产生,如脉冲干扰、图像扫描。

OpenCV 提供了 cv.medianBlur 函数实现中值滤波算法。

cv.medianBlur(src, ksize[, dst]) → dst

  • src:输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:输出图像,大小和类型与 src 相同
  • ksize:模糊核的线性大小,大于 1 的奇数
#中值滤波import cv2
import matplotlib.pyplot as pltimg=cv2.imread(r'C:\Users\lenovo\Desktop\pythonn\dog_bike_car.jpg', flags=1)img1=cv2.medianBlur(img,3)
img2=cv2.medianBlur(img,5)
img3=cv2.medianBlur(img,9)plt.figure(figsize=(9,6))
imglist=[img,img1,img2,img3]
titlelist=['1.Original','2.medianBlur(size=3)','3.medianBlur(size=5)','4.medianBlur(size=9)']
for i in range(4):plt.subplot(2,2,i+1),plt.imshow(cv2.cvtColor(imglist[i],cv2.COLOR_BGR2RGB)),plt.title(titlelist[i]),plt.axis('off')
plt.show()

运行结果:

48. 非线性滤波—双边滤波

双边滤波是一种非线性滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,在去除噪声的同时有效地保持边缘清晰锐利,对于人像处理具有美颜功能。
边缘的灰度变化较大,高斯滤波会明显地模糊边缘,对于高频细节的保护较弱。双边滤波器在空间中也采用高斯滤波器,但增加了一个反映像素强度差异的高斯方差 σd在边缘附近离的较远的像素对边缘上的像素值影响很小,从而保证了边缘附近的像素值,实现边缘保存(edge preserving)。双边滤波器的权值是空间临近度权值和像素值相似度权值的乘积,因此输出像素依赖于当前被卷积像素的邻域,又取决于被卷积像素的灰度值和邻域像素的灰度值的差。
双边滤波器对于低频信息的滤波效果较好,但不能干净地过滤彩色图像里的高频噪声。
OpenCV 提供了 cv. bilateralFilter 函数可以实现图像的双边滤波。

cv.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) → dst

  • src:输入图像,可以是灰度图像,也可以是多通道的彩色图像
  • dst:输出图像,大小和类型与 src 相同
  • d:滤波核的像素邻域直径。如d<=0 ,则由从 sigmaSpace 计算得到。
  • sigmaColor:滤波器核在颜色空间的方差,反映产生颜色影响的颜色强度区间的大小
  • sigmaSpace:滤波器核在坐标空间的方差,反映产生颜色影响的影响空间的大小
  • borderType:边界扩充的类型

49. 非线性滤波—联合双边滤波

50. 导向滤波(Guided filter)

python图像处理学习笔记相关推荐

  1. 基于python的数字图像处理--学习笔记(三)

    基于python的数字图像处理--学习笔记(三) 前言 一.灰度拉伸 二.幂律(伽马)变换 三.对数变换 前言 进入冈萨雷斯的第三章内容,并用python实现功能.我更改了代码源,之前找到太烂了,代码 ...

  2. python数据挖掘学习笔记】十四.Scipy调用curve_fit实现曲线拟合

    #2018-03-28 10:02:08 March Wednesday the 13 week, the 087 day SZ SSMR python数据挖掘学习笔记]十四.Scipy调用curve ...

  3. 深度学习常用python库学习笔记

    深度学习常用python库学习笔记 常用的4个库 一.Numpy库 1.数组的创建 (1)np.array() (2)np.zeros() (3)np.ones() (4)np.empty() (5) ...

  4. Machine Learning with Python Cookbook 学习笔记 第8章

    Chapter 8. Handling Images 前言 本笔记是针对人工智能典型算法的课程中Machine Learning with Python Cookbook的学习笔记 学习的实战代码都放 ...

  5. 数字图像处理学习笔记 六 彩色图像处理

    目录 (一)彩色模型介绍 1.1 RGB模型 1.2 CMY.CMYK模型 1.3 HSI彩色模型 1.4 HSV模型 1.5 YCbCr 彩色空间 (二)伪彩色图像处理 (三)全彩色图像处理及彩色变 ...

  6. python做直方图-python OpenCV学习笔记实现二维直方图

    本文介绍了python OpenCV学习笔记实现二维直方图,分享给大家,具体如下: 官方文档 – https://docs.opencv.org/3.4.0/dd/d0d/tutorial_py_2d ...

  7. python 正则学习笔记

    python 正则学习笔记 官方document #1.0 import re m=re.search('(?<=abc)def','cxabcdefgb')print(m.group(0))# ...

  8. Python数据结构学习笔记——链表:无序链表和有序链表

    目录 一.链表 二.无序链表 实现步骤分析 三.无序链表的Python实现代码 四.有序链表 实现步骤分析 五.有序链表的Python实现代码 结语 一.链表 链表中每一个元素都由为两部分构成:一是该 ...

  9. Python数据结构学习笔记——队列和双端队列

    目录 一.队列的定义 二.队列 实现步骤分析 三.队列的Python实现代码 四.队列的应用 六人传土豆游戏 五.双端队列的定义 六.双端队列 实现步骤分析 七.双端队列的Python实现代码 八.双 ...

最新文章

  1. php查询性能提升,php – Mysql查询提高性能
  2. LeetCode 207. Course Schedule--有向图找环--面试算法题--DFS递归,拓扑排序迭代--Python
  3. c语言调用tuxedo步骤,tuxedo 入门
  4. spring boot 集成 log4j 解决与logback冲突问题
  5. linux /etc/passwd文件各参数的意义
  6. html调用媒体图标,关于CSS 媒体查询(media queries)
  7. UniversalVideoView
  8. linux mysql 主从分离_MySQL主从分离基本配置
  9. OpenWrt中对USB文件系统的操作, 以及读写性能测试
  10. Find Backpacker Jobs in Australia
  11. AD----如何将立创EDA元器件封装库导入AD库
  12. 模糊局部信息c均值聚类算法(flicm)
  13. 【交易所相关】网关、席位、交易单元
  14. 数字化是实现“跨界打劫”的超级武器
  15. 使用Aspose给PDF加密,免受未经授权的访问和内容篡改
  16. 西游记中天庭与西天的关系
  17. HTML|下拉框和文本域、文件域
  18. BAT、网易面试经验收集
  19. 普罗米修斯Prometheus手记
  20. swift和swiftui_SwiftUI和UIStackflow问题

热门文章

  1. Android Q暗色模式适配踩坑—状态栏
  2. php 获取手机设备的ID,获取苹果设备的UDID
  3. android 沉浸式状态栏导致布局被遮挡,沉浸式状态栏导致华为手机虚拟按钮被遮挡的解决办法...
  4. 服务器系统 与win7系统,服务器系统win7
  5. Redis基础——数据类型详解
  6. 华为鸿蒙2.0来了,挑战谷歌安卓APP成关键?
  7. 中国自动浓咖啡机市场趋势报告、技术动态创新及市场预测
  8. 给社交软件“一星保护”:95后在想什么
  9. 每天努力再多一点,每天坚持再久一点
  10. 好佳居软装十大品牌 软装拥有与众不同的体验