Python+OpenCV图像处理(一篇全)

 

参考:1、网易云课堂 https://study.163.com/course/courseMain.htm?courseId=1005317018&share=1&shareId=1015101551

2、【在水一方xym的博客】业精于勤荒于嬉,行成于思毁于随 - CSDN博客 https://blog.csdn.net/zaishuiyifangxym

3、  模块cv2的用法  https://www.cnblogs.com/shizhengwen/p/8719062.html 

目录

一、图像处理基础知识

1、读取、显示、保存图像

2、获取图像属性

2.1形状- img.shape

2.2像素数目- img.size

 2.3图像类型- img.dtype

3、图像的通道拆分与合并

3.1 通道拆分- split()

3.2 通道合并- merge()

 4、图像加法与融合运算 和 图像类型转换

4.1图像加法运算

(1)使用Numpy

(2)使用OpenCV---add()

对比试验:

4.2图像融合运算- addWeighted()

4.3图像类型转换- cvtColor()

5、图像缩放、图像旋转、图像翻转 和 图像平移

5.1图像缩放- resize()

5.2 图像旋转- getRotationMatrix2D(), warpAffine()

5.3 图像翻转- flip()

5.4 图像平移- warpAffine()

二、图像处理进阶 

1、图像阈值化-threshold()

2、图像平滑(均值滤波、中值滤波、高斯滤波)---空域滤波

(1)均值滤波--cv2.blur(原始图像,核大小)

(2)中值滤波--cv2.medianBlur(src, ksize)

(3)高斯滤波--cv2.GaussianBlur(src, ksize, sigmaX)

 

 3、形态学处理--腐蚀、膨胀、开运算、闭运算、顶帽运算、黑帽运算

(1)腐蚀

(2)膨胀

(3)开运算--先腐蚀再膨胀

(4)闭运算--先膨胀再腐蚀

(5)顶帽运算--原始减去开运算

(6)黑帽运算--闭运算减去原始

总结

 

4、灰度直方图

4.1 绘制灰度直方图

4.2直方图均衡化-equalizeHist()

5、频域滤波---高通滤波、低通滤波

5.1 傅里叶变换

5.2 傅里叶变换的实现 

6、图像梯度

6.1 sobel算子

6.2 scharr算子 --比sobel算子效果更好

6.3 laplacian算子

7、边缘检测

8、图像金字塔

8.1 向下采样

8.2 向上采样

 8.3 取样可逆性研究

8.4 拉普拉斯金字塔

9、图像轮廓 


 

 

一、图像处理基础知识

1、读取、显示、保存图像

img = cv2.imread(文件名,[,参数])

其中参数:

参数(1) cv2.IMREAD_UNCHANGED (图像不可变)

参数(2) cv2.IMREAD_GRAYSCALE (灰度图像)

参数(3) cv2.IMREAD_COLOR (读入彩色图像),这是默认参数,不填的话默认为这个

参数(4) cv2.COLOR_BGR2RGB (图像通道BGR转成RGB)

综合实例:

import cv2
 
#读取图片
img = cv2.imread("d:\\image.jpg") #读取的的时候,路径用到了两个斜杠,第一个斜杠表示转义字符

#显示图像
cv2.imshow("Demo", img) #窗口名,文件名
 
#等待显示, 如果没有这句的话,图片就会显示一下立马消失
cv2.waitKey(0) #0表示无限制的等待(按任意键就退出),大于0(如5)表示等待5秒自动关闭窗口,小于零表 
               示敲击键盘就会关闭
                

cv2.destroyAllWindows() #从内存将窗口清除
 
#保存图像
cv2.imwrite("D:\\lalala.jpg", img) #文件地址, 文件名








#############如果关闭不了图片窗口,可以试试下面的方法######################

# 方法一:按任意键直接销毁所有打开的HighGUI窗口
cv2.waitKey(0)  # 等待用户输入,按任意键即可
cv2.destroyAllWindows()
# 方法二:按Esc键退出
k = cv2.waitKey(0) # waitkey代表读取键盘的输入,括号里的数字代表等待多长时间,单位ms。 0代表一直等待
if k == 27:     # 键盘上Esc键的ASCII值为27
   cv2.destroyAllWindows()

注意:不要点击显示图像窗口右上角的X关闭,那样会导致图片关闭了,程序还在运行。

           直接按任意键就可

2、获取图像属性

2.1形状- img.shape

通过shape关键字获取图像的形状,返回包含行数、列数、通道数的元祖。其中:

(1)灰度图像返回行数和列数;

(2)彩色图像返回行数、列数 和 通道数。

2.2像素数目- img.size

通过size关键字获取图像的像素数目,其中

(1)灰度图像返回 [行数 \times 列数]  ;

(2)彩色图像返回 [行数 \times 列数 \times 通道数] 。

 2.3图像类型- img.dtype

通过 dtype 关键字获取图像的数据类型,通常返回uint8。

 

通过像素矩阵可以直接获取ROI区域,如img[200:400, 200:400]。

3、图像的通道拆分与合并

3.1 通道拆分- split()

OpenCV读取的彩色图像由B、G、R三原色组成,可以通过下面代码获取不同的通道。

方法一:

b = img[:, :, 0]

g = img[:, :, 1]

r  = img[:, :, 2]

方法二: 

# -*- coding:utf-8 -*-
import cv2
import numpy as np
 
# 读取图片
img = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 拆分通道
b, g, r = cv2.split(img)
 
# 显示原始图像
cv2.imshow("B", b)
cv2.imshow("G", g)
cv2.imshow("R", r)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

使用split返回的是三个值,如果需要单独使用每个通道,可以

当然也可以获取不同的通道,例如:

b = cv2.split(img)[0]

g = cv2.split(img)[1]

r = cv2.split(img)[2]

代码如下:

# -*- coding:utf-8 -*-
import cv2
import numpy as np
 
# 读取图片
img = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 拆分通道
b = cv2.split(img)[0]
 
# 显示原始图像
cv2.imshow("B", b)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

3.2 通道合并- merge()

图像通道合并主要使用 merge() 函数实现。例如:

m = cv2.merge([b, g, r])

# -*- coding:utf-8 -*-
import cv2
import numpy as np
 
# 读取图片
img = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 拆分通道
b, g, r = cv2.split(img)
 
# 合并通道
m = cv2.merge([b, g, r])
cv2.imshow("Merge", m)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

 4、图像加法与融合运算 和 图像类型转换

4.1图像加法运算

(1)使用Numpy

Python+OpenCV图像处理(一篇全)_第1张图片

Numpy库加法的运算方法为:

目标图像 = 图像1 + 图像2,运算结果进行取模运算。有以下两种情况:

(1)当像素值 <= 255时,结果为“图像1+图像2”,例如:120+48=168

(2)当像素值 > 255时,结果为对255取模的结果,例如:(255+64) % 255=64
 

(2)使用OpenCV---add()

Python+OpenCV图像处理(一篇全)_第2张图片

OpenCV库实现图像加法运算,方法如下:

目标图像 = cv2.add(图像1, 图像2)  。 此时结果是饱和运算,有以下两种情况:

(1)当像素值 <= 255时,结果为“图像1+图像2”,例如:120+48=168

(2)当像素值 > 255时,结果为255,例如:(255+64) = 255   注意与numpy加法运算的区别!!!

对比试验:

(1) 两个灰度图像相加

代码如下所示:

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena1.tiff", cv2.IMREAD_UNCHANGED)
test = img
 
# 方法一:Numpy加法运算
result1 = img + test
 
# 方法二:OpenCV加法运算
result2 = cv2.add(img, test)
 
# 显示图像
cv2.imshow("original", img)
cv2.imshow("result1_Numpy", result1)
cv2.imshow("result2_OpenCv", result2)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下所示:

注:参与运算的图像大小和类型必须一致

下面是对彩色图像进行加法运算的结果。

(2)两个彩色图像相加

代码如下:

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
test = img
 
# 方法一:Numpy加法运算
result1 = img + test
 
# 方法二:OpenCV加法运算
result2 = cv2.add(img, test)
 
# 显示图像
cv2.imshow("original", img)
cv2.imshow("result1_Numpy", result1)
cv2.imshow("result2_OpenCv", result2)

运行结果如下:

Python+OpenCV图像处理(一篇全)_第3张图片

4.2图像融合运算- addWeighted()

图像融合通常是指将2张或2张以上的图像信息融合到1张图像上,融合的图像含有更多的信息,能够更方便人们观察或计算机处理。如下图所示,将两张不清晰的图像融合得到更清晰的图。

Python+OpenCV图像处理(一篇全)_第4张图片

 

Python+OpenCV图像处理(一篇全)_第5张图片

如上图所示,图像融合是在图像加法的基础上增加了系数和亮度调节量。

(1)图像加法:目标图像 = 图像1 + 图像2

(2)图像融合:目标图像 = 图像1  系数1 + 图像2  系数2 + 亮度调节量

主要调用的函数是 addWeighted() 函数,方法如下:

dst = cv2.addWeighter(scr1, alpha, src2, beta, gamma)

dst = src1  alpha + src2  beta + gamma

其中,参数gamma不能省略。

注:两张融合的图像像素大小需要一致

代码如下所示:

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
src1 = cv2.imread('lena.tiff')
src2 = cv2.imread('peppers.tiff')
 
# 图像融合
result = cv2.addWeighted(src1, 1, src2, 1, 0)
 
# 显示图像
cv2.imshow("src1", src1)
cv2.imshow("src2", src2)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下:

Python+OpenCV图像处理(一篇全)_第6张图片

 

1)设置参数1

result = cv2.addWeighted(src1, 0.8, src2, 0.2, 10)

运行结果如下:

Python+OpenCV图像处理(一篇全)_第7张图片

(2)设置参数2

result = cv2.addWeighted(src1, 0.2, src2, 0.8, 10)

运行结果如下:

Python+OpenCV图像处理(一篇全)_第8张图片

4.3图像类型转换- cvtColor()

        图像类型转换是指将一种类型转换为另一种类型,比如彩色图像转换为灰度图像、BGR图像转换为RGB图像。OPenCV提供了200多种不同类型之间的转换,其中最常用的包括3类,如下:

 result = cv2.cvtColor(图像, 参数) 

其中,参数有以下三种:

      1) cv2.COLOR_BGR2GRAY #彩色转灰度 类似于Matlab 中的 rgb2gray()

      2) cv2.COLOR_BGR2RGB

      3) cv2.COLOR_GRAY2BGR

 

(1)彩色图像转灰度图像

代码如下:

# encoding:utf-8
import cv2
 
# 读取图片
src = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 图像类型转换
result = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下:

Python+OpenCV图像处理(一篇全)_第9张图片

(2)BRG通道转RGB通道

代码如下:

# encoding:utf-8
import cv2
 
# 读取图片
src = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 图像类型转换
result = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下:

Python+OpenCV图像处理(一篇全)_第10张图片

一般在图像处理中,彩色图像转灰度图像最常用。

5、图像缩放、图像旋转、图像翻转 和 图像平移

5.1图像缩放- resize()

图像缩放主要调用 resize() 函数实现,具体如下:

            result = cv2.resize(src, dsize[, result[. fx[, fy[, interpolation]]]])

其中,参数

src 表示原始图像;

dsize 表示缩放大小;

fx和fy 也可以表示缩放大小倍数,他们两个(dsize或fx/fy)设置一个即可实现图像缩放。例如:

(1)result = cv2.resize(src, (160,160))

(2)result = cv2.resize(src, None, fx=0.5, fy=0.5)
 

图像缩放:设({x_0},{y_0})是缩放后的坐标,(x,y)是缩放前的坐标,{s_x} 和 {s_y} 为缩放因子,则公式如下:

                                                               [{x_0}\text{ }{\text{ }}{y_0}\text{ }{\text{ }}1] = [x\text{ }{\text{ }}y\text{ }{\text{ }}1]{\text{ }}\left[ \begin{gathered} {s_x}\text{ }{\text{ }}0\text{ }\text{ }{\text{ 0}} \hfill \\ 0{\text{ }}\text{ }\text{ }{s_y}\text{ }{\text{ 0}} \hfill \\ {\text{0 }}\text{ }\text{ }0\text{ }\text{ }{\text{ 1}} \hfill \\ \end{gathered} \right]

(1) cv2.resize(src, (200,100)) 设置的dsize是列数为200,行数为100

result = cv2.resize(src, (200,100))

代码如下:

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 图像缩放
result = cv2.resize(src, (200,100))
print (result.shape)
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

 运行结果如下图所示:

 

(2)可以获取 原始图像像素\times乘以缩放系数 进行图像变换;

result = cv2.resize(src, (int(cols * 0.6), int(rows * 1.2)))

代码如下所示:

 

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
rows, cols = src.shape[:2]
print
rows, cols
 
# 图像缩放 dsize(列,行)
result = cv2.resize(src, (int(cols * 0.6), int(rows * 1.2)))
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下图所示:

Python+OpenCV图像处理(一篇全)_第11张图片

 

3)(fx,fy) 缩放倍数的方法对图像进行放大或缩小。

result = cv2.resize(src, None, fx=0.3, fy=0.3)

代码如下所示:

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src =cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
rows, cols = src.shape[:2]
print
rows, cols
 
# 图像缩放
result = cv2.resize(src, None, fx=0.3, fy=0.3)
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下图所示:(按例比 0.3 \times 0.3 缩小)

Python+OpenCV图像处理(一篇全)_第12张图片

5.2 图像旋转- getRotationMatrix2D(), warpAffine()

          图像旋转:设 ({x_0},{y_0}) 是旋转后的坐标,({x},{y}) 是旋转前的坐标,({ m},{n}) 是旋转中心,a 是旋转的角度,({ left},{top}) 是旋转后图像的左上角坐标,则公式如下:

                                           [{x_0}\text{ }{\text{ }}{y_0}\text{ }{\text{ }}1] = [x\text{ }{\text{ }}y\text{ }{\text{ }}1]{\text{ }}\left[ \begin{gathered} \text{ }\text{ } 1\text{ }\text{ }\text{ }{\text{ }}0\text{ }\text{ }{\text{ 0}} \hfill \\ \text{ }\text{ }0{\text{ }} - 1\text{ }{\text{ 0}} \hfill \\ {\text{ - }}m\text{ }{\text{ }}n\text{ }{\text{ 1}} \hfill \\ \end{gathered} \right]{\text{ }}\left[ \begin{gathered} \cos a{\text{ }} - \sin a{\text{ }}0 \hfill \\ \sin a\text{ }\text{ }{\text{ }}\cos a\text{ }{\text{ }}0 \hfill \\ \text{ }\text{ }{\text{ }}0{\text{ }}\text{ }\text{ }\text{ }\text{ }\text{ }\text{ }0\text{ }\text{ }\text{ }{\text{ }}1 \hfill \\ \end{gathered} \right]{\text{ }}\left[ \begin{gathered} \text{ }\text{ }1\text{ }\text{ }\text{ }\text{ }{\text{ }}0\text{ }\text{ }{\text{ }}0 \hfill \\ \text{ }\text{ }0{\text{ }} \text{ }- 1\text{ }{\text{ }}0 \hfill \\ left\text{ }{\text{ }}top{\text{ }}\text{ }1 \hfill \\ \end{gathered} \right]

图像旋转主要调用getRotationMatrix2D() 函数和 warpAffine() 函数实现,绕图像的中心旋转,具体如下:

M = cv2.getRotationMatrix2D((cols/2, rows/2), 30, 1)

其中,参数分别为:旋转中心、旋转度数、scale缩放比例

rotated = cv2.warpAffine(src, M, (cols, rows))

其中,参数分别为:原始图像、旋转参数 和 原始图像宽高

(1)旋转30度

代码如下:

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src =  cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 原图的高、宽 以及通道数
rows, cols, channel = src.shape
 
# 绕图像的中心旋转
# 参数:旋转中心 旋转度数 scale
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 30, 1)
# 参数:原始图像 旋转参数 元素图像宽高
rotated = cv2.warpAffine(src, M, (cols, rows))
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("rotated", rotated)

运行结果如下图所示:

Python+OpenCV图像处理(一篇全)_第13张图片

 

(2)旋转90度

代码如下:

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src =  cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 原图的高、宽 以及通道数
rows, cols, channel = src.shape
 
# 绕图像的中心旋转
# 参数:旋转中心 旋转度数 scale
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1)
# 参数:原始图像 旋转参数 元素图像宽高
rotated = cv2.warpAffine(src, M, (cols, rows))
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("rotated", rotated)

运行结果如下图所示:

Python+OpenCV图像处理(一篇全)_第14张图片

(3)旋转180度

代码如下:

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src =  cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
 
# 原图的高、宽 以及通道数
rows, cols, channel = src.shape
 
# 绕图像的中心旋转
# 参数:旋转中心 旋转度数 scale
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 180, 1)
# 参数:原始图像 旋转参数 元素图像宽高
rotated = cv2.warpAffine(src, M, (cols, rows))
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("rotated", rotated)

运行结果如下图所示:

 

Python+OpenCV图像处理(一篇全)_第15张图片

5.3 图像翻转- flip()

          图像翻转在OpenCV中调用函数 flip() 实现,函数用法如下:

dst = cv2.flip(src, flipCode)

其中,参数:

src 表示原始图像;

flipCode 表示翻转方向,如果flipCode为0,则以X轴为对称轴翻转,如果fliipCode>0则以Y轴为对称轴翻转,如果flipCode<0则在X轴、Y轴方向同时翻转。
 

代码如下:(注意一个窗口多张图像的用法

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
src = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
# 图像翻转
# 0以X轴为对称轴翻转 >0以Y轴为对称轴翻转 <0X轴Y轴翻转
img1 = cv2.flip(src, 0)
img2 = cv2.flip(src, 1)
img3 = cv2.flip(src, -1)
 
# 显示图形 (注意一个窗口多张图像的用法)
titles = ['Source', 'Image1', 'Image2', 'Image3']
images = [src, img1, img2, img3]
for i in range(4):
    plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下图所示:(注意一个窗口多张图像的用法

Python+OpenCV图像处理(一篇全)_第16张图片

5.4 图像平移- warpAffine()

图像平移:设 ({x_0},{y_0}) 是缩放后的坐标,({x},{y}) 是缩放前的坐标,{d_x},{d_y} 为偏移量,则公式如下:

                                                     [{x_0}\text{ }{\text{ }}{y_0}\text{ }{\text{ }}1] = [x\text{ }{\text{ }}\text{ }y{\text{ }}\text{ }1]{\text{ }}\left[ \begin{gathered} \text{ }1{\text{ }}\text{ }\text{ }0\text{ }\text{ }{\text{ }}0 \hfill \\ \text{ }0\text{ }\text{ }{\text{ }}1\text{ }\text{ }{\text{ }}0 \hfill \\ {d_x}{\text{ }}\text{ }{d_y}{\text{ }}\text{ }1 \hfill \\ \end{gathered} \right]

       图像平移首先定义平移矩阵M,再调用 warpAffine() 函数实现平移,函数用法如下:

M = np.float32([[1, 0, x], [0, 1, y]])

shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))

代码如下:

 

 

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena.tiff", cv2.IMREAD_UNCHANGED)
image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
# 图像平移 下、上、右、左平移
M = np.float32([[1, 0, 0], [0, 1, 100]])
img1 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
M = np.float32([[1, 0, 0], [0, 1, -100]])
img2 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
M = np.float32([[1, 0, 100], [0, 1, 0]])
img3 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
M = np.float32([[1, 0, -100], [0, 1, 0]])
img4 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
# 显示图形
titles = ['Image1', 'Image2', 'Image3', 'Image4']
images = [img1, img2, img3, img4]
for i in range(4):
    plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

 运行结果如下图所示:

Python+OpenCV图像处理(一篇全)_第17张图片

 

二、图像处理进阶 

1、图像阈值化-threshold()

     灰度转换处理后的图像中,每个像素都只有一个灰度值,其大小表示明暗程度。二值化处理可以将图像中的像素划分为两类颜色,常用的二值化算法如下所示:

                                                         \left\{ {\begin{array}{*{20}{c}} {Y = 0}&, \\ {Y = 255}&, \end{array}\begin{array}{*{20}{c}} {gray < T} \\ {gray > = T} \end{array}} \right.

其中,当灰度Gray小于阈值T时,其像素设置为0,表示黑色;

           当灰度Gray大于或等于阈值T时,其Y值为255,表示白色。

Python OpenCV中提供了阈值函数 threshold() 实现二值化处理,其函数形式及参数如下图所示:

retval, dst = cv2.threshold(src, thresh, maxval, type)

其中,参数:

               retval:阈值        

               dst: 处理结果

               src,:原图像

               thresh:阈值

               maxval:最大值

               type:类

                               Python+OpenCV图像处理(一篇全)_第18张图片

 cv2.threshold 阈值灰度 - aoru45 - 博客园 https://www.cnblogs.com/aoru45/p/9763995.html

 

          常用的方法如下表所示,其中函数中的参数Gray表示灰度图,参数127表示对像素值进行分类的阈值,参数255表示像素值高于阈值时应该被赋予的新像素值,最后一个参数对应不同的阈值处理方法。

                                              

             对应OpenCV提供的五张图如下所示,第一张为原图,后面依次为:

二进制阈值化反二进制阈值化截断阈值化反阈值化为0 和 阈值化为0 

                                                  

              二值化处理广泛应用于各行各业,比如生物学中的细胞图分割、交通领域的车牌设别等。在文化应用领域中,通过二值化处理将所需民族文物图像转换为黑白两色图,从而为后面的图像识别提供更好的支撑作用。下图表示图像经过各种二值化处理算法后的结果,其中“BINARY”是最常见的黑白两色处理。

指定一个阈值thresh

(1)二进制阈值化--cv2.THRESH_BINARY

像素灰度值>thresh,设为最大灰度值(如:8位灰度值最大为255,以下都以8位灰度图为例)

像素灰度值

(2)反二进制阈值化--cv2.THRESH_BINARY_INV

与二进制阈值化相反,>thresh,设为0

                                       

(3)截断阈值化--cv2.THRESH_TRUNC

像素灰度值>thresh,设为thresh

像素灰度值

(4)阈值化为0--cv2.THRESH_TOZERO

>thresh,不变

(5)反阈值化为0--cv2.THRESH_TOZERO_INV 

与阈值化为0正相反, >thresh,设为0

                                       

 总代码:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
#读取图像
img = cv2.imread("zxp.jpg", cv2.IMREAD_UNCHANGED)
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
GrayImage=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
 
#阈值化处理
ret,thresh1=cv2.threshold(GrayImage,127,255,cv2.THRESH_BINARY)
ret,thresh2=cv2.threshold(GrayImage,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3=cv2.threshold(GrayImage,127,255,cv2.THRESH_TRUNC)
ret,thresh4=cv2.threshold(GrayImage,127,255,cv2.THRESH_TOZERO)
ret,thresh5=cv2.threshold(GrayImage,127,255,cv2.THRESH_TOZERO_INV)
 
#显示结果
titles = ['Gray Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [GrayImage, 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()

运行结果如下所示:(注意一个窗口多张图像的用法

                    

2、图像平滑(均值滤波、中值滤波、高斯滤波)---空域滤波

       图像增强是对图像进行处理,使其比原始图像更适合于特定的应用,它需要与实际应用相结合。对于图像的某些特征如边缘、轮廓、对比度等,图像增强是进行强调或锐化,以便于显示、观察或进一步分析与处理。图像增强主要是一个主观过程,而图像复原大部分是一个客观过程。图像增强的方法是因应用不同而不同的,研究内容包括:
 

                  

      图像平滑是一种区域增强的算法,平滑算法有邻域平均法、中指滤波、边界保持类滤波等。在图像产生、传输和复制过程中,常常会因为多方面原因而被噪声干扰或出现数据丢失,降低了图像的质量(某一像素,如果它与周围像素点相比有明显的不同,则该点被噪声所感染)。这就需要对图像进行一定的增强处理以减小这些缺陷带来的影响。
 

(1)均值滤波--cv2.blur(原始图像,核大小)

指任意一点的像素值,都是周围 N \times M 个像素值的均值

注:

1)随着核大小逐渐变大,会让图像变得更加模糊;

2)如果设置为核大小为(1,1),则结果就是原始图像。

(2)中值滤波--cv2.medianBlur(src, ksize)

这里的核大小ksize必须是奇数

将该点周围的像素点包括本身,按次序排列,取中位数作为点的像素值

注:

1)随着核大小逐渐变大,会让图像变得更加模糊;

2)核必须是大于1的奇数,如3、5、7等;

3)在代码 dst = cv2.medianBlur(src, ksize) 中 填写核大小时,只需填写一个数即可,如3、5、7等,对比均值滤波函数用法。

(3)高斯滤波--cv2.GaussianBlur(src, ksize, sigmaX)

           为了克服简单局部平均法的弊端(图像模糊),目前已提出许多保持边缘、细节的局部平滑算法。它们的出发点都集中在如何选择邻域的大小、形状和方向、参数加平均及邻域各店的权重系数等。

            图像高斯平滑也是邻域平均的思想对图像进行平滑的一种方法,在图像高斯平滑中,对图像进行平均时,不同位置的像素被赋予了不同的权重。高斯平滑与简单平滑不同,它在对邻域内像素进行平均时,给予不同位置的像素不同的权值,下图的所示的 3x3 和 5x5 邻域的高斯模板。
(1)核大小为3x3

                                                            \frac{1}{16}\times \left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \\ \end{matrix} \right]=\left[ \begin{matrix} {}^{1}\!\!\diagup\!\!{}_{16}\; & {}^{1}\!\!\diagup\!\!{}_{8}\; & {}^{1}\!\!\diagup\!\!{}_{16}\; \\ {}^{1}\!\!\diagup\!\!{}_{8}\; & {}^{1}\!\!\diagup\!\!{}_{4}\; & {}^{1}\!\!\diagup\!\!{}_{8}\; \\ {}^{1}\!\!\diagup\!\!{}_{16}\; & {}^{1}\!\!\diagup\!\!{}_{8}\; & {}^{1}\!\!\diagup\!\!{}_{16}\; \\ \end{matrix} \right]

(2)核大小为5x5

                                                                    \frac{1}{273}\times \left[ \begin{matrix} 1 & 4 & 7 & 4 & 1 \\ 4 & 16 & 26 & 16 & 4 \\ 7 & 26 & 41 & 26 & 7 \\ 4 & 16 & 26 & 16 & 4 \\ 1 & 4 & 7 & 4 & 1 \\ \end{matrix} \right]

        高斯滤波让临近的像素具有更高的重要度,对周围像素计算加权平均值,较近的像素具有较大的权重值。如下图所示,中心位置权重最高为0.4。 

                        

                                          

 

Python中OpenCV主要调用 GaussianBlur() 函数,如下:

 

dst = cv2.GaussianBlur(src, ksize, sigmaX)

其中,参数:

src 表示原始图像;

ksize 表示核大小;

sigmaX 表示X方向方差。

注:核大小(N, N)必须是奇数,X方向方差主要控制权重。

1)核大小为 3x3

                                      K=\left[ \begin{matrix} 0.05 & 0.1 & 0.05 \\ 0.1 & 0.4 & 0.1 \\ 0.05 & 0.1 & 0.05 \\ \end{matrix} \right]

                                                                    

2)核大小为 5x5

                                        K=\left[ \begin{matrix} 1 & 1 & 2 & 1 & 1 \\ 1 & 3 & 4 & 3 & 1 \\ 2 & 4 & 8 & 4 & 2 \\ 1 & 3 & 4 & 3 & 1 \\ 1 & 1 & 2 & 1 & 1 \\ \end{matrix} \right]

 

(1)核大小为 3\times3

              代码如下所示:

 
# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread('zxp_noise.jpg')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
# 高斯滤波
result = cv2.GaussianBlur(source, (3, 3), 0) #可以更改核大小
 
# 显示图形
titles = ['Source Image', 'GaussianBlur Image (3, 3)']
images = [source, result]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

          运行结果如下图所示:

                                                   

 (2)核大小为 5\times5

                 代码如下所示:

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread('zxp_noise.jpg')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
# 高斯滤波
result = cv2.GaussianBlur(source, (5, 5), 0) #可以更改核大小
 
# 显示图形
titles = ['Source Image', 'GaussianBlur Image (5, 5)']
images = [source, result]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

运行结果如下图所示:

                                      

 

注:

1)随着核大小逐渐变大,会让图像变得更加模糊;

2)核大小(N, N)必须是大于1的奇数,如3、5、7等;

 

 3、形态学处理--腐蚀、膨胀、开运算、闭运算、顶帽运算、黑帽运算

形态学运算主要针对的二值图像

(1)腐蚀

       卷积核中心逐个遍历图像像素,当卷积核范围内全是原图像时,不改变原像素值,当卷积核范围内有除了原图像以外的区域时,将此范围内的原图像像素值置为0,即腐蚀这个像素点。

                              

(2)膨胀

         卷积核中心逐个遍历图像像素,当卷积核范围内有一个原图像的像素点时,将卷积核范围内设为1,即将该像素点膨胀为卷积核大小的区域

                           

(3)开运算--先腐蚀再膨胀

开运算一般会平滑物体的轮廓、断开较窄的狭颈并消除细的突出物。

 

(4)闭运算--先膨胀再腐蚀

闭运算同样也会平滑轮廓的一部分。但与开操作相反,它通常会弥合较窄的间断和细长的沟壑,消除小的孔洞,填补轮廓线中的断裂。

(5)顶帽运算--原始减去开运算

                

(6)黑帽运算--闭运算减去原始

               

总结

腐蚀cv2.erode(src, kernel, iterations)     #iterations表示腐蚀几次

膨胀:cv2.dilate(src, kernel, iterations)

开运算:cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)

闭运算:cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)

代码示例

#encoding:utf-8
import cv2
import numpy as np
 
#读取图片
src = cv2.imread('test2.bmp', cv2.IMREAD_UNCHANGED)
 
#设置卷积核
kernel = np.ones((3,3), np.uint8)


'''形态学6种操作'''

#腐蚀
fushi = cv2.erode(src, kernel) 
#膨胀
pengzhang = cv2.dilate(src, kernel)
#开运算
kai = cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)
#闭运算
bi = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)
#顶帽运算
tophat = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)
#黑帽运算
blackhat = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)
 
#显示图像
cv2.imshow("src", src)
cv2.imshow("result", erosion)
 
#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

卷积核的设置

(1)用numpy生成

np.ones((3,3), np.uint8)             #一般用这个也够了

np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])

(2)用opencv自带函数--getStructuringElement

element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))   #十字型

element = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))  #矩形

element = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))  #椭圆形

 

4、灰度直方图

4.1 绘制灰度直方图

(1)matplotlib---hist()函数

     使用matplotlib的子库pyplot实现,它提供了类似于Matlab的绘图框架,matplotlib是非常强大基础的一个Python绘图包。其中绘制直方图主要调用 hist() 函数实现,它根据数据源和像素级绘制直方图。

hist()函数形式如下:

hist(数据源, 像素级)

其中,参数:

数据源:必须是一维数组,通常需要通过函数 ravel() 拉直图像

像素级:一般是256,表示[0, 255]

函数 ravel() 将多维数组降为一维数组,其格式为:

一维数组 = 多维数组.ravel()

Python+OpenCV图像处理(一篇全)_第19张图片

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
src = cv2.imread('zxp.jpg')
cv2.imshow("src", src)
 
plt.hist(src.ravel(), 256)
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

 运行结果:

                             Python+OpenCV图像处理(一篇全)_第20张图片

(2) opencv----calcHist()函数

            hist = cv2.calcHist(images, channels, mask, histSize, ranges, accumulate)

其中,参数:

             hist 表示直方图,返回的是一个二维数组;

             images 表示原始图像;

             channels 表示指定通道,通道编号需要用中括号括起,输入图像是灰度图像时,它的值为[0],彩色图像则为[0]、[1]、[2],分别表示B、G、R;

             mask 表示掩码图像,统计整副图像的直方图,设为None,统计图像的某一部分直方图时,需要掩码图像;

             histSize 表示BINS的数量,参数子集的数目,如下图当bins=3表示三个灰度级;

                                                     Python+OpenCV图像处理(一篇全)_第21张图片

             ranges 表示像素值范围,例如[0, 255];

             accumulate 表示累计叠加标识,默认为false,如果被设置为true,则直方图在开始分配时不会被清零,该参数允许从多个   对象中计算单个直方图,或者用于实时更新直方图;多个直方图的累积结果用于对一组图像的直方图计算。
 

补充:

(1)matplotlib库 绘制图像

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
#绘制sin函数曲线
x1 = np.arange(0, 6, 0.1)
y1 = np.sin(x1)
plt.plot(x1, y1)
 
#绘制坐标点折线
x2 = [0, 1, 2, 3, 4, 5, 6]
y2 = [0.3, 0.4, 2.5, 3.4, 4, 5.8, 7.2]
plt.plot(x2, y2)
 
#省略有规则递增的x2参数
y3 = [0, 0.5, 1.5, 2.4, 4.6, 8]
plt.plot(y3, color="r")
 
plt.show()

                                    

(2)使用OpenCV库 中的 calcHist() 函数 计算B、G、R通道的灰度级并绘制图形

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
src = cv2.imread('zxp.jpg')

#生成掩摸
mask = np.zeros(src.shape,np.uint8)
mask[200:400, 200:400]=255

 
histb = cv2.calcHist([src], [0], mask, [256], [0,255]) #掩摸处理,求的是掩摸区域的直方图
histg = cv2.calcHist([src], [1], None, [256], [0,255]) #不做掩膜处理,直接求整幅图(G分量灰
                                                        度图)的直方图
histr = cv2.calcHist([src], [2], None, [256], [0,255])
 
cv2.imshow("src", src)
 
plt.plot(histb, color='b')
plt.plot(histg, color='g')
plt.plot(histr, color='r')
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

                          Python+OpenCV图像处理(一篇全)_第22张图片

 补充:掩摸原理

Python+OpenCV图像处理(一篇全)_第23张图片

             Python+OpenCV图像处理(一篇全)_第24张图片 

             Python+OpenCV图像处理(一篇全)_第25张图片 

                     Python+OpenCV图像处理(一篇全)_第26张图片 

                     Python+OpenCV图像处理(一篇全)_第27张图片                                  

                  Python+OpenCV图像处理(一篇全)_第28张图片

 

4.2直方图均衡化-equalizeHist()

(1) 直方图均衡化概念

直方图均衡化 (Histogram Equalization) 就是把一个已知灰度概率密度分布的图像经过一种变换,使之演变为一幅具有均匀灰度概率密度分布的新图像。

如下图所示,过暗过亮的图像 经过直方图均衡化,使得图像变得清晰。                 

                        

           直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。

(2)直方图均衡化的理论基础

前提:如果一幅图像占有全部可能的灰度级,并且均匀分布。

结论:该图像具有高对比度和多变的灰色色调。

外观:图像细节丰富,质量更高。

(3)直方图均衡化的步骤

      a)计算累计直方图;

      b)将累计直方图进行区间转换;

      c)在累计直方图中,概率相近的原始值,会被处理为相同的值。

具体的例子如下:

如下图所示,已知一幅图像的像素分布为 7\times7,根据像素值,则可以计算出统计直方图

根据统计直方图,可以算出归一化直方图(即纵坐标转换为概率)和累计直方图,如下图所示:

 

将累计直方图进行区间转换,如下图所示:

 

由上图的结果可知,原先8个灰度级转变成6个灰度级,那么原始直方图和均衡直方图为:

 

上面的灰度级是8,那灰度级转变成256,计算方法类似,如下图所示:

 

 同样的,原始直方图和均衡直方图为:

由上图可以看出,虽然二者相似,但右侧均衡化后的直方图分布更均匀,相邻像素级概率和与高概率近似相等。如果将两张图的灰度级放在同一区间,可以看出差别更大,如下图所示:

 

(4)直方图均衡化应用场景

             (a)医学图像处理

             (b)车牌照识别

             (c)人脸识别

 (5)函数实现-equalizeHist()

OpenCV库下,直方图均衡化使用  equalizeHist() 函数,函数用法如下所示:

dst=cv2.equalizeHist(src)

其中,参数:

         dst 表示处理结果

         src 表示原始图像

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('zxp.jpg', cv2.IMREAD_GRAYSCALE)
equ = cv2.equalizeHist(img)
 
 
cv2.imshow("src", img)
cv2.imshow("result", equ)
 
 
plt.hist(img.ravel(), 256)
plt.figure()
plt.hist(equ.ravel(), 256)
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

  运行结果如下图所示:(左图为原始灰度图像,右图为均衡化后的图像)

 

matplotlib.pyplot.subplot() 函数

matplotlib.pyplot.subplot() 函数可以将多张图像放在一个窗口内,Pyhton下需要导入 matplotlib.pyplot  绘图包,其用法和Matlab中的subplot()函数用法类似。matplotlib 是一个强大的绘图包。subplot() 函数用法如下所示:

 

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('zxp.jpg', cv2.IMREAD_GRAYSCALE)
equ = cv2.equalizeHist(img)
 
plt.subplot(221),plt.imshow(img, 'gray'),plt.title('img'), plt.xticks([]),plt.yticks([])
plt.subplot(222),plt.imshow(equ, 'gray'),plt.title('equ'), plt.xticks([]),plt.yticks([])
plt.subplot(223),plt.hist(img.ravel(),256),plt.title('img_hist')
plt.subplot(224),plt.hist(equ.ravel(),256),plt.title('equ_hist')
 
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

matplotlib.pyplot.imshow() 函数

imshow() 函数用法如下所示,同样的,Pyhton下需要导入 matplotlib.pyplot 绘图包。

imshow(X, cmap=None)

其中,参数:

X 表示要绘制的图像;

cmap 表示colormap,颜色图谱,默认为RGB(A)颜色空间:

    1)对于灰度图像,使用参数 “ cmap=plt.cm.gray ”;

    2)对于彩色图像,如果使用opencv读入的图像,默认空间为BRG,需要调整色彩空间为RGB。

下面是分别使用函数读取灰度图像彩色图像例子。

(1)灰度图像

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('zxp.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
plt.subplot(221),plt.imshow(img),plt.title('img'), plt.axis('off') #坐标轴关闭
plt.subplot(222),plt.imshow(img, cmap=plt.cm.gray),plt.title('img_cmap'), plt.axis('off')
 
plt.subplot(223),plt.imshow(img_gray),plt.title('img_gray'), plt.axis('off')
plt.subplot(224),plt.imshow(img_gray, cmap=plt.cm.gray),plt.title('img_gray_cmap'),plt.axis('off')#正确用法
 
 
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

 运行结果如下图所示:

(2)彩色图像

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('zxp.jpg')
b,g,r=cv2.split(img) #通道分割
img_RGB=cv2.merge([r,g,b])#通道组合
 
plt.subplot(121),plt.imshow(img),plt.title('img_BGR'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(img_RGB),plt.title('img_RGB'), plt.axis('off')

 运行结果如下图所示:

 

5、频域滤波---高通滤波、低通滤波

         频率域图像增强首先通过傅里叶变换将图像从空间域转换为频率域,然后在频率域对图像进行处理,最后通过傅里叶反变换转换到空间域。频率域内的图像增强包括低通滤波、高通滤波、同态滤波等等。

5.1 傅里叶变换

Python+OpenCV图像处理(一篇全)_第29张图片

Python+OpenCV图像处理(一篇全)_第30张图片

Python+OpenCV图像处理(一篇全)_第31张图片

Python+OpenCV图像处理(一篇全)_第32张图片

Python+OpenCV图像处理(一篇全)_第33张图片

5.2 傅里叶变换的实现 

(1)numpy实现

f = numpy.fft.fft2(img) :实现傅里叶变换,返回一个复数数组(complex ndarray)

fshift = numpy.fft.fftshift(f) : 将低频分量移到频谱中心(傅里叶变换后的低频是在频谱左上角,为了观看方便,移到中心)

Python+OpenCV图像处理(一篇全)_第34张图片

以上得到的是复数,没法显示为图像,要显示为图像,需要将复数转换为灰度值[0-255]范围,就可以观察频谱图像了

                                                    result =  20*np.log(np.abs(fshift)) 

                                                Python+OpenCV图像处理(一篇全)_第35张图片

代码实现:

import cv2 
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('image\\lean.bmp', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
result = 20*np.log(np.abs(fshift))
plt.subplot(121)
plt.imshow(img, cmap = 'gray')
plt.title('original')
plt.axis('off')
plt.subplot(122)
plt.imshow(result, cmap = 'gray')
plt.title('result')
plt.axis('off')
plt.show()

 运行结果:

Python+OpenCV图像处理(一篇全)_第36张图片

注意:

(1)傅里叶得到低频、高频信息,针对低频、高频能够实现不同的目的

(2)傅里叶过程是可逆的,图像经过傅里叶变换、逆傅里叶变换,能够恢复到原始图像

(3)在频域对图像进行处理,在频域的处理会反应在逆变换图像上

 

逆傅里叶变换的实现--从频谱图像恢复到原始图像

numpy.fft.ifft2(): 实现逆傅里叶变换,返回一个复数数组

numpy.fft.ifftshift() :fftshift的逆函数,将低频信息由中心移到左上角

iimg = np.abs(逆傅里叶变换的结果) : 将逆傅里叶变换的结果不能直接显示为图像,需要变为灰度值,显示为图像

 

高通滤波:

低频和高频:低频对应图像内变化缓慢的灰度分量,高频相反。例如一幅大草原图像,低频对应着颜色大体一致的绿色草原,                      草原上的狮子边缘对应着高频。

滤波:让低频通过的滤波器称为低通滤波器,反之为高通滤波器。

            低通滤波将模糊一幅图像(丢失边缘),高通滤波将增强尖锐的细节,图像的边缘会被保留,细节会丢失掉。但是会               导致图像的对比度降低。

频域滤波:修改傅里叶变换以达到特殊目的,然后计算逆傅里叶变换(IDFT)返回到图像域(空域)。可以实现图像增强、图                 像去燥、边缘检测、特征提取、压缩、加密等等。

Python+OpenCV图像处理(一篇全)_第37张图片

将低频的地方置为0

Python+OpenCV图像处理(一篇全)_第38张图片

代码:

import cv2
import numpy as np 
import matplotlib.pyplot as plt
img = cv2.imread('image\\boat.bmp', 0)
f = np.fft.fft2(img)    #傅里叶变换
fshift = np.fft.fftshift(f)   #将低频移到中心
rows,cols = int(rows/2), int(cols/2)  #获取图像中心点
fshift[crow-30:crow+30, col-30:col+30] = 0  #将中心点周围30区域置为0,即将低频信息去掉,实现高通
                                            #滤波
ishift = np.fft.ifftshift(fshift)   #将低频移还原到左上角
iimg = np.fft.ifft2(ishift)   #逆傅里叶变换
iimg = np.abs(iimg)    #将逆傅里叶变换的结果转换为灰度值,显示为图像
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('original'), plt.axis('off')
plt.subplot(122), plt.imshow(iimg, cmap='gray')
plt.title('iimg'),plt.axis('off')
plt.show()

 

(2)opencv实现--cv2.dft()

result = cv2.dft(原始图像, 转换标识)

注意:

(1)返回结果是双通道的,第1个通道是结果的实数部分,第2个通道是结果的虚数部分

(2)输入的图像首先要转化成np.float32格式, 语法:np.float32(img)

(3)转换标识:flags = cv2.DFT_COMPLEX_OUTPUT, 输出是一个复数阵列

numpy.fft.fftshift():将低频分量转移到频谱中心

 返回值 = cv2.magnitude(参数1, 参数2) : 将傅里叶变换结果转换为灰度值,进行显示

     参数1:浮点型X坐标值,也就是实部

     参数2:浮点型Y坐标值,也就是虚部

代码示例:

 

import numpy as np 
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('image\\lena.bmp', 0)
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT)        #傅里叶变换
dftShift = np.fft.fftshift(dft)                                       #低频移到中心位置
result = 20*np.log(cv2.magnitude(dftShift[:,:,0], dftShift[:,:,1]))   #转换为灰度值,显示
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('original'), plt.axis('off')
plt.subplot(122),plt.imshow(result, cmap='gray')
plt.title('result'), plt.axis('off')
plt.show()

运行结果: 

Python+OpenCV图像处理(一篇全)_第39张图片

 opencv进行逆傅里叶变换

 将经过傅里叶变换并处理的频域图像,进行逆傅里叶变换为原图

返回结果=cv2.idft(原始数据)

      返回结果:取决于原始数据的类型和大小

      原始数据:实数或者复数均可

import cv2 
import numpy as np
import matplotlib.pyplot as plt
o = cv2.imread('image\\lena.bmp', 0)
dft = cv2.dft(np.float32(o), flags=cv2.DFT_COMPLEX_OUTPUT)  #傅里叶变换,flags不能省
dshift = np.fft.fftshift(dft)                         #低频从左上角移到中心
ishift = np.fft.ifftshift(dshift)                     #从中心移到左上角,还原
io = cv2.idft(ishift)                                 #逆傅里叶变换
io = cv2.magnitude(io[:,:,0], io[:,:,1])              #转换为灰度值, [0-255]

plt.subplot(121)
plt.imshow(o, cmap='gray')
plt.axis('off')
plt.title('original')
plt.subplot(122)
plt.imshow(io, cmap='gray')
plt.axis('off')
plt.title('inverse')
plt.show()                         

 

低通滤波

Python+OpenCV图像处理(一篇全)_第40张图片

 

Python+OpenCV图像处理(一篇全)_第41张图片

 Python+OpenCV图像处理(一篇全)_第42张图片

 构造低通滤波器

Python+OpenCV图像处理(一篇全)_第43张图片

 

import cv2
import numpt as np
import matplotlib.pyplot as plt 

o = cv2.dft(np.float32(o), flags=cv2.DFT_COMPLEX_OUTPUT)  #傅里叶变换
dshift = np.fft.fftshift(dft)                             #将低频移到中间
rs,cs = o.shape                                           #获取图像大小
cr,cc = int(rs/2), int(cs/2)                              #图像中心
mask=np.zeros((rs,cs,2),np.int8)                          #构造掩摸图像,两个通道
mask[cr-30:cr+30, cc-30:cc+30] = 1                        
md = dshift*mask                                          #消除高频,留着中间部分的低频部分
imd = np.fft.ifftshift(md)                                #将低频还原到左上角
io = cv2.idft(imd)                                        #逆傅里叶变换
io = cv2.magnitude(io[:,:,0], io[:,:,1])                  #转换为灰度值,才能显示为图像

plt.subplot(121)
plt.imshow(o, cmap='gray')
plt.axis('off')
plt.title('original')
plt.subplot(122)
plt.imshow(io, cmap='gray')
plt.axis('off')
plt.title('result')

plt.show()

运行结果

Python+OpenCV图像处理(一篇全)_第44张图片

 

6、图像梯度

6.1 sobel算子

Python+OpenCV图像处理(一篇全)_第45张图片

Python+OpenCV图像处理(一篇全)_第46张图片

说明: p5x表示在水平方向的梯度,因为这条垂直边界是由水平方向的像素点差值得来的,所以叫水平方向的梯度

            p4、p6离 p5近,给一个比较大的权重

            如果p5左右两列差距很大,那么p5的值将会很大,此时p5所在的这一列就是一个边界

            类似,p5y表示垂直方向上的梯度

Python+OpenCV图像处理(一篇全)_第47张图片

 计算图像的总梯度(两个方向上)

Python+OpenCV图像处理(一篇全)_第48张图片

Python+OpenCV图像处理(一篇全)_第49张图片

 

Python+OpenCV图像处理(一篇全)_第50张图片

 

函数实现

dst = cv2.Sobel(src, ddepth, dx, dy,  [ksize])

           dst:计算结果

           src:原始图像

           ddepth :处理结果图像深度

           dx:x轴方向

           dy:y轴方向

           ksize:核大小

ddepth参数说明:

          一般,通常直接设置为-1,表示让处理结果与原始图像保持一致

         但是此处需要注意,不能直接置为-1

举例说明

Python+OpenCV图像处理(一篇全)_第51张图片

在256色位图中,白色点像素值为255,黑色点像素值为0

A:右边减去左边为0-255=-255,是负数,会直接截断,处理为0,所以这个边界显示不出来,

B:右边为255-0=255可以显示出来

说明直接计算,只能得到一个边界

故,需要取绝对值才行

因此,在写ddepth参数的时候,不能让它为-1,需要设置成cv2.CV_64F

 表示如果出现负数,先保留负数的值,而不是直接将它截断置为0,那样就找不到边界了

现在怎么处理这个负数呢?

   dst = cv2.converScaleAbs(src, [, alpha[, beta]])          后两个参数可以省略

   作用:把负数的值取绝对值,无论边界是正负,都能获取

 

dx dy参数说明

计算x方向梯度: dx=1,dy=0

计算y方向梯度:dx=0,dy=1

 

Python+OpenCV图像处理(一篇全)_第52张图片

 一般用方式2(方式1不太准),为了确保相加不溢出,dx和dy都乘以2一个系数

opencv提供了一个计算两幅图像的权重和

       dst = cv2.addWeighted(src1, alpha, src2, beta, gamma)

              五个参数分别表示:原图像1,原图像1的系数,原图像2,原图像2的系数,修正值

                                       Python+OpenCV图像处理(一篇全)_第53张图片

示例:

(1)深度设置为-1

可以看到只检测出了右边界

Python+OpenCV图像处理(一篇全)_第54张图片                  Python+OpenCV图像处理(一篇全)_第55张图片

(2)深度设置为cv2.CV_64F

没取绝对值,还是一样

Python+OpenCV图像处理(一篇全)_第56张图片Python+OpenCV图像处理(一篇全)_第57张图片

(3)水平方向,深度设置为cv2.CV_64F,且取绝对值

Python+OpenCV图像处理(一篇全)_第58张图片Python+OpenCV图像处理(一篇全)_第59张图片

(4)竖直方向,深度设置为cv2.CV_64F

 Python+OpenCV图像处理(一篇全)_第60张图片Python+OpenCV图像处理(一篇全)_第61张图片

(5)方法二,水平和竖直连个方向分别计算再合并

Python+OpenCV图像处理(一篇全)_第62张图片 Python+OpenCV图像处理(一篇全)_第63张图片

(6)方法一,将水平和竖直两个方向参数直接设置为1

表明用方法2好,方法一检测不出来边界,就算检测出来,效果也没方法二好

Python+OpenCV图像处理(一篇全)_第64张图片 Python+OpenCV图像处理(一篇全)_第65张图片

Python+OpenCV图像处理(一篇全)_第66张图片

6.2 scharr算子 --比sobel算子效果更好

使用3*3的sobel算子时,可能不太清楚吗,使用scharr算子,效果更好。

Python+OpenCV图像处理(一篇全)_第67张图片

 函数实现:

   dst = Scharr(src, ddpeth, dx, dy)

             src:原始图像

             ddepth:处理结果图像深度

             dx:x轴方向

             dy:y轴方向

 注意:

ddepth参数:一般在别的图像处理函数中,直接置为-1,表示处理成与原图像一样深度的图像,但是由于此函数处理过程中会出现负数,故与sobel算子一样,将它赋值为cv2.CV_64F

然后再转为unit8正数

dst = Scharr(src, cv2.CV_64F, dx, dy)

dst = cv2.convertScaleAbs(dst)

 Python+OpenCV图像处理(一篇全)_第68张图片Python+OpenCV图像处理(一篇全)_第69张图片 

 

等价关系:

dst=Scharr(src,ddepth, dx,dy)

等价于

dst = Sobel(src,ddepth,dx,dy,-1)

Python+OpenCV图像处理(一篇全)_第70张图片                  Python+OpenCV图像处理(一篇全)_第71张图片

 

 

 

sobel和 scharr算子的比较

(1)大小是一样的,说明计算起来的工作量是一样的

但是scharr精确度更高,给了临近像素更高的权重

 Python+OpenCV图像处理(一篇全)_第72张图片

代码:

Python+OpenCV图像处理(一篇全)_第73张图片

 运行结果:

从结果可以看出,scharr算子效果更好

Python+OpenCV图像处理(一篇全)_第74张图片

6.3 laplacian算子

  拉普拉斯算子类似于二阶sobel导数。实际上,在OpenCV中通过调用sobel算子来计算拉普拉斯算子。

公式:

 

使用的卷积核为:

Python+OpenCV图像处理(一篇全)_第75张图片

例如: 

 Python+OpenCV图像处理(一篇全)_第76张图片

 

Python+OpenCV图像处理(一篇全)_第77张图片

Python+OpenCV图像处理(一篇全)_第78张图片

函数实现:

dst = cv2.Laplacian(src, ddepth)

 注意:通常情况下,可以将图像深度ddepth的值设置为-1,让处理结果与原始图像保持一致

但是该函数在计算过程中也会出现负数,故

Python+OpenCV图像处理(一篇全)_第79张图片

代码:

import cv2
import numpy as np
o = cv2.imread('image\\laplacian.bmp', cv2.IMREAD_GRAYSCALE)

laplacian = cv2.Laplacian(o, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)  #转回uint8

cv2.imshow('original', o)
cv2.imshow('laplacian', laplacian)
cv2.waitKey()
cv2.destoryAllWindows()

 运行结果:

Python+OpenCV图像处理(一篇全)_第80张图片

Python+OpenCV图像处理(一篇全)_第81张图片

7、边缘检测

canny边缘检测

函数实现:

edges =  cv2.Canny(image, threshold1, threshold2)

     image: 原始图像

     threshold1: 阈值1 minVal

     threshold2: 阈值2  maxVal

 

 Python+OpenCV图像处理(一篇全)_第82张图片

 threshold值越小,细节越丰富

 

Python+OpenCV图像处理(一篇全)_第83张图片Python+OpenCV图像处理(一篇全)_第84张图片

 

Python+OpenCV图像处理(一篇全)_第85张图片

下面是不同阈值处理结果,可以看出阈值越小细节越丰富

 Python+OpenCV图像处理(一篇全)_第86张图片

Python+OpenCV图像处理(一篇全)_第87张图片

 

8、图像金字塔

 Python+OpenCV图像处理(一篇全)_第88张图片

8.1 向下采样

import cv2
import numpy as np
o = cv2.imread('image\\man.bmp')
r1 = cv2.pyrDown(o)
r2 = cv2.pyrDown(r1)
r3 = cv2.pyrDown(r2)

cv2.imshow('original', o)
cv2.imshow('PyrDown1', r1)
cv2.imshow('PyrDown2', r2)
cv2.imshow('PyrDown3', r3)

cv2.waitKey()
cv2.destoryAllWindows()

 Python+OpenCV图像处理(一篇全)_第89张图片

依次变为原来的四分之一

 

8.2 向上采样

import cv2
import numpy as np
o = cv2.imread('image\\man.bmp')
r1 = cv2.pyrUp(o)
r2 = cv2.pyrUp(r1)
r3 = cv2.pyrUp(r2)

cv2.imshow('original', o)
cv2.imshow('PyrUp1', r1)
cv2.imshow('PyrUp2', r2)
cv2.imshow('PyrUp3', r3)

cv2.waitKey()
cv2.destoryAllWindows()

Python+OpenCV图像处理(一篇全)_第90张图片

 8.3 取样可逆性研究

Q1: 经过一次向下取样和向上取样,能否恢复到原始图像?

Python+OpenCV图像处理(一篇全)_第91张图片

A1: 不能,清晰度降低了,图片大小一样

 

Q2: 经过一次向上取样和向下取样,能否恢复到原始图像?

A2:也不能,清晰度也降低了

下面例子说明了两幅图像的差异

Python+OpenCV图像处理(一篇全)_第92张图片                   Python+OpenCV图像处理(一篇全)_第93张图片

 

 

 

8.4 拉普拉斯金字塔

前面讲的是高斯金字塔,使用的是高斯卷积核不断对图像进行变换

拉普拉斯金字塔是在高斯金字塔的基础上构造的新型金字塔

 

 Python+OpenCV图像处理(一篇全)_第94张图片

 

 

 Python+OpenCV图像处理(一篇全)_第95张图片

 

 

Python+OpenCV图像处理(一篇全)_第96张图片Python+OpenCV图像处理(一篇全)_第97张图片

 

9、图像轮廓 

你可能感兴趣的:(opencv,opencv)