#  **前言**

如题所示，记录了本人学习opencv时的具体代码与注释，包括一些常见问题，但可能不会太详细，因为是回顾，所以问题大多解决，后期欢迎评论区提问。[桀桀桀！基于pycharm的opencv入门笔记-CSDN博客](https://blog.csdn.net/2301_79946146/article/details/144953143?spm=1001.2014.3001.5501)

如果你用下面的代码报错了，可能是一些库没添加，你可以根据报错提示来下载对应的库。、

# 显示图片及属性（点灯！）

首先，选取一个自己喜欢的图片，放入当前项目的文件地址。以下是我选取的图片，以及它的文件名luna.jpg。

![img](./ywj_img/1b78cc45929548fe979d96c7db5a727f.png)

```python
import cv2  
import matplotlib.pyplot as plt
import numpy as np
```

简单说，下面两行代码是为了方便后续的作图，引用的相关库。

```python
plt.switch_backend('TkAgg')       #matplotlib的其中一个backend的选项
```

## 显示图片

为了方便后续绘制的图片或图像方便缩放或者移动，使用上述代码。

```python
import cv2 #opencv读取的格式是BGR
import matplotlib.pyplot as plt
import numpy as np
plt.switch_backend('TkAgg')#matplotlib的其中一个backend的选项

img=cv2.imread('luna.jpg')#在当前路径下读取图片
print(img) #打印像素数组

def cv_showimg(name,img):
    #图像显示，也可以创建多个窗口
    cv2.imshow(name,img)
    #等待时间，毫秒级，0表示任意键终止
    cv2.waitKey(0)
    cv2.destroyAllWindows()

cv_showimg('image',img)
```

运行得到一个名为img的窗口，显示图片。（下文的cv_showimg皆为上述代码中的定义函数）

<img src="./ywj_img/941b73fd2c7e406ca7eca475ba6939b2.png" alt="img" style="zoom: 33%;" /><img src="./ywj_img/1c44c1d8079242dbab269e73d27b9d76.png" alt="img" style="zoom:33%;" />

## 属性

打印出图片的像素点，即像素色块的方阵序列。

```python
print(img.shape) #读取图片的属性
```

结果为![img](./ywj_img/65173b6b19b543c989a5fcc61412dd86.png)

打印图片的像素高度，宽度以及颜色通道，图像的高度为 959像素，宽度为 959像素，并且有 3 个颜色通道（红、绿、蓝）。

```python
#cv2.IMREAD_COLOR：彩色图像
#cv2.IMREAD_GRAYSCALE:灰度图像
```

图像可以由彩色，也能由灰度的形式表现。

```python
img=cv2.imread('luna.jpg',cv2.IMREAD_GRAYSCALE)#将图片灰度化
cv_showimg('image',img)
cv2.imwrite('myluna.jpg',img)#保存图片
```

结果<img src="./ywj_img/d4b2445e39a14f708300cfa60145c5a0.png" alt="img" style="zoom:33%;" />

# 视频的读取与处理

与显示图片之前一样，先将视频boat.mp4（我的）添加至文件地址。

```python
vc=cv2.VideoCapture('boat.mp4')#在当前路径下读取视频
if vc.isOpened():#检查是否打开正确
    open, frame = vc.read()#frame是图像数组，即一帧图像
else:
    open = False
while open:#遍历视频中的每一帧
    ret, frame = vc.read()
    if frame is None:
        break
    if ret == True:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)#转换为灰度图
        cv2.imshow('result',gray)
        if cv2.waitKey(10) & 0xFF == 27:#27为退出键
            break
vc.release()
cv2.destroyAllWindows
```

结果是，将视频的每一帧提取出来，进行灰度处理，再播放由处理后的灰度帧组成的视频，演示视频不方便放，可以自己试试。

代码  if cv2.waitKey(10) & 0xFF == 27:#27为退出键  即视频播放过程中按下ESC键，视频退出播放，可以将27改为其他数字，则对应的退出按钮也会改变。

# 截取部分图像数据(ROI)

相当于裁剪图片大小，根据上文图片属性可知luna.jpg的高度为 959像素，宽度为 959像素。

```python
img=cv2.imread('luna.jpg')
luna=img[300:800,100:500]
cv_showimg('luna',luna)
```

截取高度为300-800，宽度为100-500，结果为

<img src="./ywj_img/a713062cbde4474890ce2f87263d7640.png" alt="img" style="zoom:33%;" />

窗口名称为luna

# 颜色通道提取

```python
img=cv2.imread('luna.jpg')#在当前路径下读取图片

b,g,r=cv2.split(img)#分离颜色通道，blue，green，red
img=cv2.merge((b,g,r))#重组

#只保留R,   B:0,G:1,R:2
cur_img=img.copy()#创建原始图像的副本
cur_img[:,:,0] = 0#把B设置成0
cur_img[:,:,1] = 0#把G设置成0
cv_showimg('R',cur_img)#最后只能保留2通道red
```

举个例子，先分离颜色通道，再重新组装成图像（实际没变）。复制原先的图像，并命名为cur_img，通过操作将副本的blue，green通道关闭（B:0,G:1,R:2），保留了red。

结果

<img src="./ywj_img/3754daa95a9845bdabd4bc10db1aba40.png" alt="img" style="zoom:33%;" />

# 边界填充

边界填充就是给图片的边界续上一段，补上一段的感觉。常见的边界填充分为五种，

1.复制，在边界补上和原先一样的边界

2.反射，在两边进行复制

3.反射，以最边缘为轴，作对称，补上和原先一样的边界

4.外包装，上下边界一样复制，将原图包装或者镶嵌进去

5.常量，使用常数值填充，value=0表示边框填充为黑色，RGB值为 (0, 0, 0)，也可以写作value=[0, 0, 0]

```python
top_size, bottom_size, left_size, right_size = (50,50,50,50)#上下左右
#Type：复制，反射（在两边进行复制），反射（以最边缘位轴，对称），外包装法，常量法（常数值填充）
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_CONSTANT,value=0)

import matplotlib.pyplot as plt
plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(replicate,'gray'),plt.title('REFLECT')
plt.subplot(235),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()
```

![img](./ywj_img/e0ac98aae881495f8db01eaac7d3e728.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

#  数值计算

```python
img_luna=cv2.imread('luna.jpg')
img_luna2=img_luna +10
img_luna[:5,:,0]
img_luna2[:5,:,0]
print((img_luna+img_luna2)[:5,:,0])#超过255，用这个数取余255
print(cv2.add(img_luna,img_luna2)[:5,:,0])#超过255，就是255
```

先解释下img_luna[:5,:,0]，在 OpenCV 中，通常它的形状是行数，列数，还有通道数，:5 表示前五行，: 则是全部列，0是第0个通道，在 BGR 图像中，0是用来选择 BGR 中的蓝色通道。

将img_luna中的每个像素加10，cv2.add函数会进行饱和相加，如果某个像素值超过 255，它会被设置为 255，数值计算后面会有用。

结果<img src="./ywj_img/0f912317c32248a1a6af1804f9407c97.png" alt="img" style="zoom: 50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 图像融合

```python
import matplotlib.pyplot as plt
import numpy as np
plt.switch_backend('TkAgg')#matplotlib的其中一个backend的选项
img_luna=cv2.imread('luna.jpg')
img_lovegood=cv2.imread('lovegood.jpg')
print(img_luna.shape)
img_lovegood=cv2.resize(img_lovegood,(959, 959))
print(img_lovegood.shape)
res = cv2.addWeighted(img_luna, 0.4, img_lovegood, 0.6, 10)
plt.imshow(res)
plt.show()#使用matplotlib时最后一定要加
```

将两张图片进行融合，每张图片的尺寸维度和混合权重可以设置。

结果<img src="./ywj_img/3cf316db73b7435f80f1df88c9e2d978.png" alt="img" style="zoom: 80%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 图片阈值

阈值可以理解为图片的像素值，比如说亮度。凡事先将图片灰度化[捂脸]，通常有5个方法。

第一个，超过阈值（比如127）了，那就是255，反之则为0（亮为白点，暗为黑点）

第二个，未超过阈值（比如127）了，那就是255，反之则为0（亮为黑点，暗为白点）

第三个，截断，超过阈值即为阈值，未超过阈值就是本身

第四个，大于阈值为本身，小于或者等于阈值即为0

第五个，小于阈值为本身，其余为0

```python
import matplotlib.pyplot as plt
import numpy as np
plt.switch_backend('TkAgg')#matplotlib的其中一个backend的选项
img_lovegood=cv2.imread('lovegood.jpg')
img_lovegood_gray=cv2.imread('lovegood.jpg',cv2.IMREAD_GRAYSCALE)#将图片灰度化
ret, thresh1 = cv2.threshold(img_lovegood_gray, 127, 255, cv2.THRESH_BINARY)#超过阈值（127）即为255，反之则为0（亮为白点，暗为黑点）
ret, thresh2 = cv2.threshold(img_lovegood_gray, 127, 255, cv2.THRESH_BINARY_INV)#上面反转，超过阈值（127）为0，反之则为255(亮为黑点，暗为白点)
ret, thresh3 = cv2.threshold(img_lovegood_gray, 127, 255, cv2.THRESH_TRUNC)#截断值，大于阈值即为阈值（大于127=127），小于阈值不变为本身
ret, thresh4 = cv2.threshold(img_lovegood_gray, 127, 255, cv2.THRESH_TOZERO)#大于阈值不变为本身，其余为0
ret, thresh5 = cv2.threshold(img_lovegood_gray, 127, 255, cv2.THRESH_TOZERO_INV)#上面反转，小于阈值不变为本身，其余为0

titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img_lovegood, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range(6):
    plt.subplot(2,3, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()
```

结果![img](./ywj_img/799362b6615145648dfeeaa14a16fc01.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 滤波（图像平滑处理）

原图<img src="./ywj_img/308a4d8d89964eddb68573d96d101f0d.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

简单来说，滤波就相当于磨皮。

## 均值滤波

```python
img_charming_girl=cv2.imread('table luna.jpg')
cv_showimg('img', img_charming_girl)
smaller = cv2.resize(img_charming_girl, (0,0), fx=0.6, fy=0.6)#把图片缩小

blur = cv2.blur(smaller, (3,3))
cv_showimg('blur', blur)
```

因为原图较大，先将原图缩小，然后使用均值滤波。

## 方框滤波

```python
box = cv2.boxFilter(smaller, -1, (3,3), normalize=True)#or False
```

## 高斯和中值滤波

```python
#高斯滤波
aussian = cv2.GaussianBlur(smaller, (5,5), 1)
#中值滤波(good)
median = cv2.medianBlur(smaller, 5)
```

高斯滤波与卷积核的计算相关，较小的值会保留更多细节，大一些会模糊化，原理不深入探讨。个人感觉中值滤波更好，下面附上所有滤波的对比。

![img](./ywj_img/5d642f95e1b1419487429c0846ce0079.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

第一张是原图，后面依次。

# 形态学操作

## 腐蚀（去毛刺）

通过缩小图像中的物体（通常是白色区域）去掉不需要的细节，比如小的突出部分。

![img](./ywj_img/56d156f1646c4d95a88d8975df1402e2.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

最典型的一张图片，经过三次腐蚀得到![img](./ywj_img/18e2b0a542bb49a4a9bdfbf90b60b53a.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

每次腐蚀的多少也是根据所选卷积核的大小得到的，这里不过多阐述。

```python
img_fushi = cv2.imread('fushi3.png')
smaller = cv2.resize(img_fushi, (0,0), fx=0.4, fy=0.4)#把图片缩小
# cv_showimg('img', res)
kernel = np.ones((5,5), np.uint8)#选择核的矩阵
erosion_1 = cv2.erode(smaller, kernel, iterations = 1)
erosion_2 = cv2.erode(smaller, kernel, iterations = 2)
erosion_3 = cv2.erode(smaller, kernel, iterations = 3)
res_erosion = np.hstack((smaller,erosion_1, erosion_2, erosion_3))###np.vstack也行
cv_showimg('res_erosion', res_erosion)
```

而将（5，5）改为（8，8）后，差距显而易见。![img](./ywj_img/f9e6d9b92ccb431283b76883fb874442.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

## 膨胀

腐蚀后可进行膨胀操作，将以（5，5）卷积核腐蚀后的结果以（3，3）膨胀三次。

```python
kernel = np.ones((3,3), np.uint8)
dilate_1 = cv2.dilate(erosion_1, kernel, iterations = 1)#把腐蚀后的结果传入进行膨胀
dilate_2 = cv2.dilate(erosion_1, kernel, iterations = 2)
dilate_3 = cv2.dilate(erosion_1, kernel, iterations = 3)
res_dilate = np.hstack((dilate_1, dilate_2, dilate_3))###np.vstack也行
cv_showimg('res_dilate', res_dilate)
```

<img src="./ywj_img/2f60d55c17a14468b3b511ddf2020f1a.png" alt="img" style="zoom:80%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

## 开运算

开运算就是先腐蚀再膨胀，下图为例。

<img src="./ywj_img/57a0dcc3af08443fb15a8569887500cd.png" alt="img" style="zoom:50%;" />  

```python
img = cv2.imread('fushi2.png')
smaller = cv2.resize(img, (0,0), fx=0.4, fy=0.4)#把图片缩小

kernel = np.ones((5,5), np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

cv_showimg('opening', opening)
```

结果<img src="./ywj_img/c364a01260264db3be4d15a2dd24241a.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

效果还不错。

## 闭运算

```python
img = cv2.imread('fushi2.png')
smaller = cv2.resize(img, (0,0), fx=0.4, fy=0.4)#把图片缩小

kernel = np.ones((5,5), np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

cv_showimg('closing', closing)
```

结果<img src="./ywj_img/911c8f673343482998577c8ac7b6bea5.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

似乎没什么效果，没关系，我们试一下（8，8）

结果<img src="./ywj_img/ac0a066cee0d4e8d9d7a659397e53f89.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

[捂脸][捂脸][捂脸]

## 梯度

梯度=膨胀-腐蚀

```python
img = cv2.imread('fushi2.png')
kernel = np.ones((5,5), np.uint8)#选择5*5矩阵的核
dilate = cv2.dilate(img, kernel, iterations = 1)#膨胀1次
erosion = cv2.erode(img, kernel, iterations = 1)#腐蚀1次

res = np.hstack((dilate, erosion))
cv_showimg('res', res)

gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)#进行梯度运算，膨胀-腐蚀
cv_showimg('gradient', gradient)
```

先展示膨胀和腐蚀的结果

<img src="./ywj_img/0e55999b4dc544d39b2293f77c263749.png" alt="img" style="zoom: 67%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

进行梯度运算后

<img src="./ywj_img/3156dba349944cbe9c59be250c9112a6.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

## 礼帽与黑帽

礼帽=原始输入-开运算结果

```python
#礼帽
img = cv2.imread('fushi2.png')
kernel = np.ones((5,5), np.uint8)#选择5*5矩阵的核
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)#礼帽
cv_showimg('tophat', tophat)
```

结果<img src="./ywj_img/75b30521857c499baec3300494180f8c.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

黑帽=闭运算-原始输入

```python
##黑帽
img = cv2.imread('fushi2.png')
kernel = np.ones((5,5), np.uint8)#选择5*5矩阵的核
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)#黑帽
cv_showimg('blackhat', blackhat)
```

结果<img src="./ywj_img/2cd3a20e09f34a6fa0a0a8d53cb62a2d.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

可以发现结果就是两次运算与原图的相减。

# 轮廓

## 图像轮廓

mode:轮廓检索模式
 cv2.findContours(img, mode, method)
 RETR_EXTERNAL:只检索最外面的轮廓
 RETR_LIST:检索所有的轮廓，并将其保存到一条链表当中
 RETR_CCOMP:检索所有的轮廓，并将他们组织为两层：顶层是各部分的外界边界，第二层是空洞的边界
 RETR_TREE:检索所有的轮廓，并重构嵌套轮廓的整个层次(最常用的)

method:轮廓逼近方法
 CHAIN_APPROX_NONE: 以Freeman链码的方式输出轮廓，所有其他方法输出多边形(顶点的序列)(长方形四边)
 CHAIN_APPROX_SIMPLE:压缩水平的，垂直的和斜的部分，也就是，函数只保留他们的终点部分(长方形四顶点)

```python
####为更高的准确度，使用二值法
gray = cv2.imread('fushi2.png',cv2.IMREAD_GRAYSCALE)#将图片灰度化
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# cv_showimg('thresh', thresh)

##contours是一堆轮廓点(轮廓信息),hierarchy是层级
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)##由于版本问题，必须删除一个参数
############绘制轮廓
#传入绘制图像，轮廓，轮廓索引，颜色模式，线条厚度
#需要copy,不然原图会变
img = cv2.imread('fushi2.png')
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)##(0,0,255)是BGR，-1是整体轮廓,2是线条的宽度
cv_showimg('res', res)
```

<img src="./ywj_img/3ab841239eb042b689173b9547a83b3c.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

## 轮廓特征与近似

轮廓特征包括面积和周长，可以采用

```python
cnt = contours[0]
#面积
contourArea = cv2.contourArea(cnt)
print(contourArea)
#周长，True表示闭合的
arcLength = cv2.arcLength(cnt, True)
print(arcLength)
```

得到轮廓特征。

以下图为例，

<img src="./ywj_img/1eb24863cc4645ddbe137f5cc0d6c6af.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

```python
img = cv2.imread('jinsi.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#将图片灰度化，在得到轮廓特征，更精确
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)#二值法
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)##由于版本问题，必须删除一个参数
cnt = contours[0]##如果图中只有一个轮廓，直接用第一个元素

draw_img = img.copy()
res = cv2.drawContours(draw_img, cnt, -1, (0, 0, 255), 2)##(0,0,255)是BGR，-1是整体轮廓,2是线条的宽度
cv_showimg('res', res)
```

先使用二值法进行图像处理，如果图中只有一个轮廓，就直接用数组的第一个元素，用红色描出轮廓，设置宽度。

<img src="./ywj_img/f50ba6c983504c049ac10be9c569d9ec.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

### 轮廓近似法

```python
espilon = 0.01 * cv2.arcLength(cnt, True)##0.1倍周长
approx = cv2.approxPolyDP(cnt, espilon, True)##轮廓近似的数组

draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)##(0,0,255)是BGR，-1是整体轮廓,2是线条的宽度
cv_showimg('res', res)
```

![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)

<img src="./ywj_img/c6ac7c2d3e1b4e7f8051b25bcf8bd3f7.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

### 边界矩形

```python
img = cv2.imread('jinsi.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#灰度化
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)#二值法
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]

x,y,w,h = cv2.boundingRect(cnt)#得到边界矩形
img = cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)#做出矩形框，(0,255,0)绿色
cv_showimg('img', img)

area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)#边界矩形
```

<img src="./ywj_img/a279be0413a6414b8fab5a9624f8726b.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

由此还可以作外接圆

```python
(x,y), radius = cv2.minEnclosingCircle(cnt)#得到边界外接圆
center = (int(x), int(y))
radius = int(radius)
img = cv2.circle(img, center, radius, (0,255,0),2)
cv_showimg('img', img)
```

# <img src="./ywj_img/9effa56cc0cd4d049c524a621ac519a2.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 图像梯度

## Sobel算子

### 例一

<img src="./ywj_img/8b4fd736ce58444bbb26d966ac1c3dae.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

以上图为例。

先介绍下相关函数

```python
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
#ddepth: 图像的深度(通常为-1，表示输出和输入深度是一样的),(cv2.CV_64F表示能使用负数)
#dx和dy分别表示水平和竖直方向
#ksize是Sobel算子的大小(几乘几的矩阵大小)
```

先使dx=1，dy=0(算水平不算竖直)(没取绝对值，只有白到黑那一半)，

```python
img = cv2.imread('pi.png')
# cv_showimg('img', img)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3)#dx=1，dy=0(算水平不算竖直)(没取绝对值，只有白到黑那一半)
cv_showimg('sobelx', sobelx)
```

结果<img src="./ywj_img/5327ff492a084ec484a3fa6b97121fbb.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

上述代码中未加绝对值，白到黑是正数，黑到白是负数，所有负数会被截断成0，所以要取绝对值。

```python
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3)#dx=1，dy=0(算水平不算竖直)（先把正负数都算出来）
sobelx = cv2.convertScaleAbs(sobelx)#Abs是算负数的绝对值
```

结果<img src="./ywj_img/1873e6957e4c4e6596a04179a8649915.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

再将x，y相掉，

```python
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize = 3)#dx=0，dy=1(算竖直不算水平)
sobely = cv2.convertScaleAbs(sobely)#Abs是算负数的绝对值
cv_showimg('sobely', sobely)
```

得到<img src="./ywj_img/dd18fb818ee14789bd24527960b8dfce.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

最后将x，y相结合，得到xy梯度

```python
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
cv_showimg('sobelxy', sobelxy)
```

得到<img src="./ywj_img/5057ceb9db884711b02496a3fd7d006c.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

然后就有人要问了，为什么不直接计算xy梯度，而要分别计算出x梯度和y梯度然后相结合才得出xy梯度呢？我们看下直接计算xy梯度的效果。

```python
#直接计算xy梯度(不建议)
sobelxy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize = 3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_showimg('sobelxy', sobelxy)
```

结果<img src="./ywj_img/258dd3ea2d9c428fb0500392cd50dce1.png" alt="img" style="zoom:50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  ，得到的差异还是挺大的，四周边框消失，白点更多，更密集。

### 例二

```python
img = cv2.imread('luna.jpg',cv2.IMREAD_GRAYSCALE)
# cv_showimg('img', img)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3)#dx=1，dy=0(算水平不算竖直)（先把正负数都算出来）
sobelx = cv2.convertScaleAbs(sobelx)#Abs是算负数的绝对值
# cv_showimg('sobelx', sobelx)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize = 3)#dx=0，dy=1(算竖直不算水平)
sobely = cv2.convertScaleAbs(sobely)#Abs是算负数的绝对值
# cv_showimg('sobely', sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
cv_showimg('sobelxy', sobelxy)
###整体进行计算(不建议)
sobelxy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize = 3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_showimg('sobelxy', sobelxy)
```

x梯度结果<img src="./ywj_img/e17c2e01b71d45fe86f941852e028cc7.png" alt="img" style="zoom: 33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

y梯度结果<img src="./ywj_img/f30e6596ac8d4a13953efee4ce38395c.png" alt="img" style="zoom: 33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

xy梯度（x梯度和y梯度相结合）

<img src="./ywj_img/4ac5d628056f41bf97ef314a86222e0b.png" alt="img" style="zoom: 33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

直接计算xy梯度

<img src="./ywj_img/f6f6dcb27ba14fd785fe4fb256071c94.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

对比就出来了。

## 几种算子的对比

除了Sobel算子外，还有Scharr算子，laplacian算子，不在一一阐述。

```python
img = cv2.imread('loveluna.jpg',cv2.IMREAD_GRAYSCALE)
smaller = cv2.resize(img, (0,0), fx=0.25, fy=0.25)#把图片缩小

sobelx = cv2.Sobel(smaller, cv2.CV_64F, 1, 0, ksize = 3)#dx=1，dy=0(算水平不算竖直)（先把正负数都算出来）
sobelx = cv2.convertScaleAbs(sobelx)#Abs是算负数的绝对值
sobely = cv2.Sobel(smaller, cv2.CV_64F, 0, 1, ksize = 3)#dx=0，dy=1(算竖直不算水平)
sobely = cv2.convertScaleAbs(sobely)#Abs是算负数的绝对值
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)

scharrx = cv2.Scharr(smaller, cv2.CV_64F, 1, 0)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.Scharr(smaller, cv2.CV_64F, 0, 1)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)

laplacian = cv2.Laplacian(smaller, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

res = np.hstack((smaller, sobelxy, scharrxy, laplacian))
cv_showimg('res', res)
```

对比结果![img](./ywj_img/9de86b797e034c7d905c82ea9bb3a3ca.png)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 边缘检测

这里使用Canny边缘检测，

1）使用高斯滤波器，以平滑图像，滤除噪声
 2）计算图像中每个像素点的梯度强度和方向
 3）应用非极大值（Non-Maximum Suppression）抑制，以消除边缘检测带来的杂散相应
 4）应用双阈值（Double-Threshold）检测来确定真实的和潜在的边缘
 5）通过抑制孤立的弱边缘最终完成边缘检测

以一张小猫图片为例，上来先缩图[捂脸][捂脸][捂脸]

<img src="./ywj_img/8f30ae3f86ca4277861a9457c8db039c.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

```python
img = cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE)
smaller = cv2.resize(img, (0,0), fx=0.3, fy=0.3)#把图片缩小

v1 = cv2.Canny(smaller, 120, 250)
v2 = cv2.Canny(smaller, 50, 100)

res= np.hstack((smaller, v1, v2))
cv_showimg('res', res)
```

------

结果<img src="./ywj_img/56e443f2b8ca4df1842285bd2f796514.png" alt="img" style="zoom: 67%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 金字塔

## 图像金字塔

对图像，先上采样，再下采样，即先放大再缩小，最终得到的图片是较模糊的。

```python
####################图像金字塔，金字塔制作方法
img = cv2.imread('loveluna.jpg')
smaller = cv2.resize(img, (0,0), fx=0.3, fy=0.3)#把图片缩小
# cv_showimg('img', img)
print(smaller.shape)

up = cv2.pyrUp(smaller)##上采样（放大）
# cv_showimg('up', up)
print(up.shape)

down = cv2.pyrDown(smaller)##下采样（缩小）
# cv_showimg('down', down)
print(down.shape)

up_down = cv2.pyrDown(up)##先向上采样后向下采样，得到的图像比原来模糊
# cv_showimg('up_down', up_down)
print(up_down.shape)

res = np.hstack((smaller, up_down))
cv_showimg('res', res)
```

结果<img src="./ywj_img/4d95ff54161e4ed8aadf8dbea7fb314f.png" alt="img" style="zoom:67%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

<img src="./ywj_img/a86377c6d7bb41ffbebc1c732f713a3d.png" alt="img" style="zoom:67%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

## 拉普拉斯金字塔

拉普拉斯金字塔
 先低通滤波，然后缩小尺寸，放大尺寸，图像相减。。

```python
img = cv2.imread('loveluna.jpg')
smaller = cv2.resize(img, (0,0), fx=0.5, fy=0.5)#把图片缩小
# cv_showimg('img', img)
down = cv2.pyrDown(smaller)
down_up = cv2.pyrUp(down)
i_1 = smaller - down_up
cv_showimg('i_1', i_1)
```

结果<img src="./ywj_img/ef458cb8f96140eb8f34b3455c5442ce.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

有点感人。

# 模板匹配

简单来说，就是使用对比检测的方法判断某局部图像在整体图像中的位置。

TM_SQDIFF:计算平方不同，计算出的值越小，越相关
 TM_CCORR:计算相关性，计算出的值越大，越相关
 TM_CCOEFF:计算相关系数，计算出的值越大，越相关
 TM_SQDIFF_NORMED:计算归一化平方不同，计算出的值越接近0，越相关
 TM_CCORR_NORMED:计算归一化相关性，计算出来的值越接近1，越相关
 TM_CCOEFF_NORMED:计算归一化相关系数，计算出来的值越接近1，越相关

接下来一一使用

<img src="./ywj_img/5a88093382924a7cb88e412214dd0f79.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  为整体图片

<img src="./ywj_img/db303241dc174c7c8c0c0b8e0cc16bad.png" alt="img" style="zoom:25%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  为局部图片

```python
img = cv2.imread('charming girl.jpg', 0)
template = cv2.imread('girl face.png', 0)
h, w = template.shape[:2]
print("img.shape is",img.shape)
print("template.shape is",template.shape)

methods = ['cv2.TM_CCOEFF','cv2.TM_CCORR_NORMED','cv2.TM_CCORR',
           'cv2.TM_CCORR_NORMED','cv2.TM_SQDIFF','cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
print("res.shape is",res.shape)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
print("min_val is",min_val)
print("max_val is",max_val)
print("min_loc is",min_loc)##左上角的点
print("max_loc is",max_loc)

for meth in methods:
    img2 = img.copy()

    #匹配方法的真值
    method = eval(meth)
    print(method)
    res = cv2.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    #如果是平方差匹配TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    #画矩形
    cv2.rectangle(img2, top_left, bottom_right, 255, 2)

    plt.subplot(121), plt.imshow(res, cmap = 'gray')
#     plt.xticks([]), plt.yticks([]) #隐藏坐标轴
    plt.subplot(122),plt.imshow(img2, cmap = 'gray')
#     plt.xticks([]), plt.yticks([]) #隐藏坐标轴
    plt.suptitle(meth)
    plt.show()
```

 <img src="./ywj_img/4e3571c911c44dc1af60b23ee5017570.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  <img src="./ywj_img/4375cb13d8fa414da49ac3eb172dc770.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  <img src="./ywj_img/9b920e339515415bb58f6c7dfdd34884.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  <img src="./ywj_img/fbb2ed52c02f414499e27fcb57714278.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  <img src="./ywj_img/1b0660870756443f885c6fa2300fc0e8.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  <img src="./ywj_img/c766071bfb3f4d5eaac4bccb8f8f48fd.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

# 直方图

## 绘制

cv2.calcHist(images, channels, mask, histSize, ranges)
 images:原图像格式为unit8或float32,传入函数时用[]先把正负数都算出来
 channels:用[]，如果入图像是灰度图，它的值就是[0],如果是彩色图像的传入的参数可以是[0][1][2]它们对应BGR
 mask:掩模图像，统整幅图像的直方图就把它为None,但是如果想统图像某一分的直方图，就制作一个掩模图像并使用它
 histSize: BIN的数目。也应用[]，直方图每个范围大小
 range: 像素值范围常为[0, 256]

```python
img = cv2.imread('lovegood.jpg')
hist = cv2.calcHist([img], [0], None, [256], [0,256])
# print("hist.shape is",hist.shape)
plt.hist(img.ravel(), 256)
plt.show()
#####遍历三种颜色的直方图
img = cv2.imread('many cats.jfif')
color = ('b', 'g', 'r')
for i, col in enumerate(color):
    histr = cv2.calcHist([img], [i], None, [256], [0,256])
    plt.plot(histr, color = col)
    plt.xlim([0,256])
plt.show()
```

 遍历三种颜色的直方图

<img src="./ywj_img/7d06d912b3f74bdca6871808d7836bfe.png" alt="img" style="zoom: 50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

## 掩码（mask）

掩码操作是为了截取图像的一部分，先创建掩码，掩码的像素数组必须小于待截取图像的大小，接着截取图像



<img src="./ywj_img/7347fb2256db47d1a9338c304b66da9f.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  <img src="./ywj_img/3253afda52ba4e80969b2c82ae94513d.png" alt="img" style="zoom:33%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

最后列出进行对比，并绘制该区域直方图，

<img src="./ywj_img/dc165983a26b45959c5a24a38f24fd5e.png" alt="img" style="zoom: 67%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

```python
#mask操作（掩码）##截取图像操作
#创建mask
img = cv2.imread('lovegood.jpg')
print(img.shape)
mask = np.zeros(img.shape[:2], np.uint8)#无符号整形
mask[200:450, 200:450] = 255
cv_showimg('mask', mask)

img = cv2.imread('lovegood.jpg', 0)
# cv_showimg('img', img)
masked_img = cv2.bitwise_and(img, img, mask=mask)#与操作
cv_showimg('masked_img', masked_img)
#
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])

plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full),plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()
```

##  直方图均衡化

不多说，效果很好

```python
img = cv2.imread('lovegood.jpg', 0)
smaller = cv2.resize(img, (0,0), fx=0.6, fy=0.6)#把图片缩小
plt.hist(smaller.ravel(), 256)
# plt.show()

equ = cv2.equalizeHist(smaller)#直方图均衡化
plt.hist(equ.ravel(), 256)
# plt.show()

res = np.hstack((smaller, equ))
cv_showimg("res", res)
#自适应均衡化
clahe = cv2.createCLAHE(clipLimit = 2.0, tileGridSize = (8, 8))
res_clahe = clahe.apply(smaller)
res = np.hstack((smaller, equ, res_clahe))
cv_showimg('res', res)
```

 <img src="./ywj_img/2a9ec347b99e49ebb931447662afea79.png" alt="img" style="zoom:67%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)  

#  傅里叶变换

这节了解下就行。

高频:变化剧烈的灰度分量

低频:变化缓慢的灰度分量

滤波:低通：只保留低频，使图像模糊

高通:只保留高频，使图像细节增强

```python
img = cv2.imread('charming girl.jpg', 0)
img_float32 = np.float32(img)
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)#执行傅里叶变换
dft_shift = np.fft.fftshift(dft)
#得到灰度图能表现的形式
magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))##将实部和虚部进行处理
#低频在中间的表达
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'),plt.xticks([]),plt.yticks([])
plt.show()
####低通滤波
img = cv2.imread('charming girl.jpg', 0)
img_float32 = np.float32(img)
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)#执行傅里叶变换
dft_shift = np.fft.fftshift(dft)#将左上角放到中间
#得到灰度图能表现的形式
rows, cols = img.shape
crow, ccol = int(rows/2), int(cols/2) #中心位置
###低通滤波
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1##左30，右30，上下30分别置为1（除了中间是1，其余都是0）
#IDFT##傅里叶变化向原始图像中逆变换的过程
fshift = dft_shift * mask#将掩码与图像结合
f_ishift = np.fft.ifftshift(fshift)#将中间还回去
img_back = cv2.idft(f_ishift)#将中间还回去
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])##将实部和虚部进行处理

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Result'),plt.xticks([]),plt.yticks([])
plt.show()

####################高通滤波
img = cv2.imread('charming girl.jpg', 0)
img_float32 = np.float32(img)
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)#执行傅里叶变换
dft_shift = np.fft.fftshift(dft)#将左上角放到中间
#得到灰度图能表现的形式
rows, cols = img.shape
crow, ccol = int(rows/2), int(cols/2) #中心位置
###低通滤波
mask = np.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0##左30，右30，上下30分别置为1（除了中间是0，其余都是1）
#IDFT##傅里叶变化向原始图像中逆变换的过程
fshift = dft_shift * mask#将掩码与图像结合
f_ishift = np.fft.ifftshift(fshift)#将中间还回去
img_back = cv2.idft(f_ishift)#将中间还回去
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])##将实部和虚部进行处理

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Result'),plt.xticks([]),plt.yticks([])
plt.show()
```

 低通，高通滤波结果

<img src="./ywj_img/86f7e6f39eb2407f91cc8a2ea6f8c5e7.png" alt="img" style="zoom: 50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)<img src="./ywj_img/5895f9b0c87d4e58b46fb3486f20aa78.png" alt="img" style="zoom: 50%;" />![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)

# 最后

加油各位，that is all，thank you！