OpenCV图像处理(Python版)



OpenCV(一)——图像基础知识

Excerpt

课程一览:目录1.数字图像1.1 数字图像概念1.2 数字图像起源1.3 常见成像方式1.4 数字图像的应用1.5 OpenCV介绍2.图像属性2.1 图像格式2.2 图像尺寸2.3 图像分辨率和图像通道2.3.1灰度转化2.3.2 RGB 与BGR2.3.3 通道分离2.3.4 通道合并2.4 图像直方图2.4.1 直方图绘制2.4.2 三通道直方图绘制2.5图像色彩空间学习目标:…


课程一览:

目录

1.数字图像

1.1 数字图像概念

1.2 数字图像起源

1.3 常见成像方式

1.4 数字图像的应用

1.5 OpenCV介绍

2.图像属性

2.1 图像格式

2.2 图像尺寸

2.3 图像分辨率和图像通道

2.3.1 灰度转化

2.3.2 RGB 与BGR

2.3.3 通道分离

2.3.4 通道合并

2.4 图像直方图

2.4.1 直方图绘制

2.4.2 三通道直方图绘制

2.5图像色彩空间

完整代码


学习目标:

1.数字图像

1.1 数字图像概念

  • 数字图像
    • 数字图像又称数码图像或数位图像,是二维图像用有限数字数值像素的表示,由数组或矩阵表示。
    • 数字图像可以理解为一个二维函数f(x,y),其中x和y是空间(平面)坐标,而在任意坐标处的幅值f称为图像在该点处的强度或灰度。
  • 图像处理目的
    • 改善图示的信息以便人们解释
    • 为存储、传输和表示而对图像进行的处理

1.2 数字图像起源

1.3 常见成像方式

 

1.4数字图像的应用

图像处理、机器视觉、人工智能关系

  • 图像处理主要研究二维图像,处理一个图像或一组图像之间的相互转换的过程,包括图像滤波,图像识别,图像分割等问题。
  • 计算机视觉主要研究映射到单幅或多幅图像上的三维场景,从图像中提取抽象的语义信息,实现图像理解是计算机视觉的终极目标。
  • 人工智能在计算机视觉上的目标就是解决像素值和语义之间关系,主要的问题有图片检测,图片识别,图片分割和图片检索。

1.5 OpenCV介绍

2.图像属性

2.1 图像格式

  • BMP格式:Windows系统下的标准位图格式,未经压缩,一般图像文件会比较大。在很多软件中被广泛应用。
  • JPEG格式:也是应用最广泛的图片格式之一,它采用一种特殊的有损压缩算法,达到较大的压缩比(可达到2:1甚至40:1),互联网上最广泛使用的格式。
  • GIF:不仅可以是一张静止的图片,也可以是动画,并且支持透明背景图像,适用于多种操作系统,“体型”很小,网上很多小动画都是GIF格式。但是其色域不太广,只支持256中颜色。
  • PNG格式:与JPG格式类似,压缩比高于GIF,支持图像透明,支持Alpha通道调节图像的透明度。
  • TIFF格式:它的特点是图像格式复杂、存储信息多,在Mac中广泛使用,非常有利于原稿的复制。很多地方将TIFF格式用于印刷。

2.2 图像尺寸

  • 图像尺寸的长度与宽度是以像素为单位的。
  • 像素(pixel)
    • 像素是数码影像最基本的单位,每个像素就是一个小点,而不同颜色的点聚集起来就变成一幅动人的照片。
    • 灰度像素点数值范围在0-255之间,0表示黑,255表示白,其他值表示处于黑白之间。
    • 彩色图用红绿蓝三通道的二维矩阵来表示。每个数值也是在0-255之间,0表示相应的基色,而255则代表相应的基色在该像素中取得最大值。

函数:cv2.imread()

参数说明:

  • 第一参数为待读路径
  • 第二个参数为读取方式,常见读取方式有三种

#导入opencv的python版本依赖库cv2
import cv2
 
#使用opencv中imread函数读取图片,
#0代表灰度图形式打开,1代表彩色形式打开
img = cv2.imread('split.jpg',1)
print(img.shape)
#print(img)

函数:cv2.imshow()

参数说明:

  • 参数一,窗口的名字
  • 参数二,图像数据名

#导入opencv依赖库
import cv2
 
#读取图像,读取方式为彩色读取
img = cv2.imread('split.jpg',1)
#
cv2.imshow('photo',img)
#等待时间,若为0,则键入后退出
k = cv2.waitKey(0)
if k == 27:   # 输入ESC键退出
    cv2.destroyAllWindows()
elif k == ord('s'): # 输入S键保存图片并退出
    cv2.imwrite('split_.jpg',img)
#销毁窗口
cv2.destroyAllWindows()

函数:cv2.imwrite()

参数说明:

  • 参数一,图像名(包括格式)
  • 参数二,待写入的图像数据变量名

2.3 图像分辨率和图像通道

  • 分辨率:单位长度中所表达或截取的像素数目。每英寸图像内的像素点数,单位是像素每英寸(PPI)。图像分辨率越高,像素的点密度越高,图像越清晰。
  • 通道数:图像的位深度,是指描述图像中每个pixel数值所占的二进制位数。位深度越大则图像能表示的颜色数就越多,色彩越丰富逼真。
    • 8位:单通道图像,也就是灰度图,灰度值范围2^8=256
    • 24为:三通道3*8=24
    • 32位:三通道加透明度Alpha通道

2.3.1灰度转化

  • 目的:将三通道图像(彩色图)转化为单通道图像(灰度图)。
  • 公式:
    • 3–>1:GRAY = B * 0.114 + G * 0.587 + R * 0.299
    • 1–>3:R = G = B = GRAY; Alpha = 0
  • 函数:cv2.cvtColor(img,flag)
  • 参数说明
    • 参数一:待转化图像
    • 参数二:flag就是转换模式,
      • cv2.COLOR_BGR2GRAY:彩色转灰度
      • cv2.COLOR_GRAY2BGR:单通道转三通道

#导入opencv
import cv2 
#读入原始图像,使用cv2.IMREAD_UNCHANGED
img = cv2.imread("girl.jpg",cv2.IMREAD_UNCHANGED)
#查看打印图像的shape
shape = img.shape
print(shape)
#判断通道数是否为3通道或4通道
if shape[2] == 3 or shape[2] == 4 :
    #将彩色图转化为单通道图
    img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    cv2.imshow("gray_image",img_gray)
cv2.imshow("image", img)
cv2.waitKey(1000)
cv2.destroyAllWindows()

2.3.2 RGB 与BGR

用opencv读取的是BGR的格式,如果用plt展示图片,需要将图片从BGR转为RGB格式。

import cv2 
import matplotlib.pyplot as plt 
img = cv2.imread("test2.png", cv2.IMREAD_COLOR)
cv2.imshow("Opencv_win", img)
# 用opencv自带的方法转
img_cv_method = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 用numpy转,img[:,:,::-1]列左右翻转
img_numpy_method = img[:,:,::-1] # 本来是BGR 现在逆序,变成RGB
# 用matplot画图
plt.subplot(1,3,1)
plt.imshow(img_cv_method)
plt.subplot(1,3,2)
plt.imshow(img_numpy_method)
plt.subplot(1,3,3)
plt.imshow(img)
plt.savefig("./plt.png")
plt.show()
#保存图片
cv2.imwrite("opencv.png", img)
cv2.waitKey(0)
cv2.destroyAllWindows()


2.3.3 通道分离

  • 目的:将彩色图像,分成B、G、R三个单色通道图像。方便我们对BGR三个通道分别进行操作。
  • 函数:cv2.split(img)
  • 参数说明
    • 参数一:待分离通道的图像
#加载opencv
import cv2
src=cv2.imread('split.jpg')
cv2.imshow('before',src)
#调用通道分离
b,g,r=cv2.split(src)
#三通道分别显示
cv2.imshow('blue',b)
cv2.imshow('green',g)
cv2.imshow('red',r)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出全为三个灰度图

#导入opencv模块
import numpy as np
import cv2            
 
image=cv2.imread("split.jpg")#读取要处理的图片
cv2.imshow("src",image)
cv2.waitKey(0)
B,G,R = cv2.split(image)#分离出图片的B,R,G颜色通道
 
zeros = np.zeros(image.shape[:2],dtype="uint8")#创建与image相同大小的零矩阵
cv2.imshow("BLUE",cv2.merge([B,zeros,zeros]))#显示 (B,0,0)图像
cv2.imshow("GREEN",cv2.merge([zeros,G,zeros]))#显示(0,G,0)图像
cv2.imshow("RED",cv2.merge([zeros,zeros,R]))#显示(0,0,R)图像
cv2.waitKey(0)
cv2.destroyAllWindows()

2.3.4 通道合并

  • 目的:通道分离为B、G、R后,对单独通道进行修改,最后将修改后的三通道合并为彩色图像。
  • 函数:cv2.merge(List)
  • 参数说明
    • 参数一:待合并的通道数,以list的形式输入
#加载opencv
import cv2
src=cv2.imread('split.jpg')
cv2.imshow('before',src)
#调用通道分离
b,g,r=cv2.split(src)
#将Blue通道数值修改为0
g[:] = 0
#合并修改后的通道
img_merge=cv2.merge([b,g,r])
cv2.imshow('merge',img_merge)
cv2.waitKey(0)
cv2.destroyAllWindows()


2.4 图像直方图

  • 图像直方图:图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。
  • 图像直方图的意义:
    • 直方图是图像中像素强度分布的图形表达方式
    • 它统计了每一个强度值所具有的像素个数
    • CV领域常借助图像直方图来实现图像的二值化

2.4.1 直方图绘制

  • 目的:直方图是对图像像素的统计分布,它统计了每个像素(0-255)的数量
  • 函数:cv2.calcHist(images,channels,mask,histSize,ranges)
  • 参数说明:
    • 参数1:待统计图像,需用中括号括起来
    • 参数2:待计算的通道
    • 参数3:Mask,这里没有使用,所以用None
    • 参数4:histSize,表示直方图分成多少份
    • 参数5:是表示直方图中各个像素的值,[0.0,256.0]表示直方图能表示像素值从0.0到256的像素。直方图是对图像像素的统计分布,它统计了每个像素(0到255)的数量。

from matplotlib import pyplot as plt
import cv2
import numpy as np
img = cv2.imread('girl.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
plt.imshow(img_gray, cmap=plt.cm.gray)
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
 
 
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.plot(hist)
plt.xlim([0, 256])
plt.show()

2.4.2 三通道直方图绘制

from matplotlib import pyplot as plt
import cv2
girl = cv2.imread("girl.jpg")
cv2.imshow("girl", girl)
color = ("b", "g", "r")
#使用for循环遍历color列表,enumerate枚举返回索引和值
for i, color in enumerate(color):
    hist = cv2.calcHist([girl], [i], None, [256], [0, 256])
    plt.title("girl")
    plt.xlabel("Bins")
    plt.ylabel("num of perlex")
    plt.plot(hist, color = color)
    plt.xlim([0, 260])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()

 

2.5图像色彩空间

  • 概念
    • 颜色空间也称为彩色模型(又称彩色空间或彩色系统),它的用途是在某些标准下用通常可接受的方式对彩色加以说明。
  • 常见的颜色空间:RGB、HSV、HSI、CMYK

(1)RGB颜色

  • RGB颜色空间概念
    • 主要用于计算机图形学中,依据人眼识别的颜色创建,图像中每一个像素都具有R、G、B三个颜色分量组成,这三个分量大小均为[0,255]。通常表示某个颜色的时候,写成一个三维向量的形式(110,150,130)。
  • 颜色模型
    • 原点对应的颜色为黑点,它的三个分量值都为0
    • 距离原点最远的顶点对应的颜色为白色,三个分量值都为1
    • 从黑色到白色的灰度值分布在这两个点的连线上,该虚线称为灰度线
    • 立方体的其余各点对应不同的颜色,即三原色红、绿、蓝及其混合色黄、品红、青色。

(2)HSV颜色

  • HSV颜色空间概念
    • HSV(Hue,Saturation,Value)是根据颜色的直观特性由A.R.Smith在1978年创建的一种颜色空间,这个模型中颜色的参数分别是:色调(H)、饱和度(S)、明度(V)。
  • 颜色模型
    • H通道:Hue,色调/色彩,这个通道代表颜色
    • S通道:Saturation,饱和度,取值范围0%~100%,值越大,颜色越饱和
    • V通道:Value,明暗,数值越高,越明亮,0%(黑)到100%(白)

RGB转HSV

import cv2
#色彩空间转换函数
def color_space_demo(image):
    gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    cv2.imshow('gray',gray)
    hsv=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)
    #print(hsv)
    cv2.imshow('hsv',hsv)
#读入一张彩色图
src=cv2.imread('girl.jpg')
cv2.imshow('before',src)
#调用color_space_demo函数进行色彩空间转化
color_space_demo(src)
cv2.waitKey(0)
cv2.destroyAllWindows()

(3)HSI

  • HSI颜色空间概念
    • HSI模型是美国色彩学家孟塞尔与1915年提出的,它反映了人的视觉系统感知彩色的方式,以色调、饱和度和强度三种基本特征量来感知颜色。
  • 模型的优点
    • 在处理彩色图像时,可仅对I分量进行处理,结果不改变原图像中的彩色种类
    • HSI模型完全反映了人感知颜色的基本属性,与人感知颜色的结果一一对应

(4)CMYK颜色

  • CMYK颜色空间概念
    • CMYK颜色空间应用于印刷工业,印刷业通过青(C)、品(M)、黄(Y)三原色油墨的不同网点面积率的叠印来表现丰富多彩的颜色和阶调,这便是三原色的CMY颜色空间。

完整代码

import cv2
from matplotlib import pyplot as plt
import numpy as np
 
#=============================cv2.imread打开图片===========================#
flag = 0
# flag = 1
if flag == 1:
    # 使用opencv中imread函数读取图片,
    # 0代表灰度图形式打开,1代表彩色形式打开
    img = cv2.imread('girl.jpg',1)
    print(img.shape)
 
#=============================cv2.imshow显示图片==========================#
#=============================cv2.imwrite保存图片==========================#
flag = 0
# flag = 1
if flag == 1:
    #读取彩色图片
    img = cv2.imread('girl.jpg', 1)
    #窗口名和图片参数
    cv2.imshow('girl',img)
 
    # 等待时间,若为0,则键入任意键后退出
    key = cv2.waitKey(0)
    #根据指示键入,操作完后退出
    if key == 27:    # 输入ESC键退出
        #imshow会在桌面创建一个临时地址,使用完后需手动销毁窗口
        cv2.destroyAllWindows()
    elif key == ord('s'):   # 输入S键保存图片并退出
        #保存图片:图片地址及图片名,图片参数
        #注意,图片名要加扩展名,否则报错
        #文件保存位置与该文件同级
        cv2.imwrite('save_girl.jpg',img)
    cv2.destroyAllWindows()
 
#=========cv2.cvtColor实现三通道图与单通道灰度图的相互转化================#
 
flag = 0
# flag = 1
if flag == 1:
    #-1表示不对图片做改变
    img = cv2.imread('girl.jpg',-1)
    #获取图片的shape
    shape = img.shape
    ##判断通道数是否为3通道或4通道
    if shape[2] == 3 or shape[2] == 4:
        # 将彩色图转化为单通道图
        img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
        cv2.imshow('girl_gray',img_gray)
    cv2.imshow('girl',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
 
 
flag = 0
# flag = 1
if flag == 1:
    img_gray = cv2.imread('girl_gray.jpg',-1)
    shape = img_gray.shape
    #单通道灰度图转为三通道
    img = cv2.cvtColor(img_gray,cv2.COLOR_GRAY2BGR)
    #虽然转为了三通道,单输出的结果仍是灰度图
    cv2.imshow('img',img)
    cv2.imshow('img_gray',img_gray)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
 
#=========================BGR与RGB之间的转换==========================#
 
flag = 0
# flag = 1
if flag == 1:
    #用cv读取BGR图像
    img = cv2.imread('test_image.png', 0)
    #用matplotlib展示RGB图片,gray为灰度图
    plt.imshow(img)
 
    # 隐藏X、Y轴上的刻度
    plt.xticks([])
    plt.yticks([])
    plt.show()
 
 
flag = 0
# flag = 1
if flag == 1:
    '''
        由于cv读入的是BGR图像,matplotlib输出的RGB图像,则输出的图像色彩与原图不符
        所以需要在plt.imshow之前对图片的通道进行变换:BGR-->RGB
    '''
    img_BGR = cv2.imread('girl.jpg',-1)
    #通过opencv自带的方法转换
    img_cv_RGB = cv2.cvtColor(img_BGR,cv2.COLOR_BGR2RGB)
    #用numpy转,将img[:,:,::-1]列左右翻转
    img_numpy_RGB = img_BGR[:, :, ::-1]
 
    #用matplotlib画图
    #定义行列,(1,3)表示一行三列的窗口,最后一个1表示第一个图像
    plt.subplot(1,3,1)
    plt.imshow(img_BGR)
    plt.subplot(1,3,2)
    plt.imshow(img_cv_RGB)
    plt.subplot(1,3,3)
    plt.imshow(img_numpy_RGB)
    #用plt显示图像不用手动销毁窗口
    plt.show()
 
#=======================使用split对BGR通道分离=========================#
flag = 0
# flag = 1
if flag == 1:
    '''
    可以看到,输出结果为三张不同深度的灰度图,分离后每张图片都是单通道的,仅有一个值来表示,而彩图需要是三个以上个通道
    '''
    img = cv2.imread('girl.jpg',-1)
    #分离通道
    b,g,r = cv2.split(img)
    #显示分离后的各通道
    cv2.imshow('blue',b)
    cv2.imshow('green',g)
    cv2.imshow('red',r)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
 
#=============================手动通道分离==========================#
flag = 0
# flag = 1
if flag == 1:
    img = cv2.imread('girl.jpg', -1)
    # 分离通道
    b, g, r = cv2.split(img)
    #创建与img相同的二维零矩阵
    zero = np.zeros(img.shape[:2],dtype='uint8')
    #cv2.merge用于合并通道
    cv2.imshow('blue',cv2.merge([b,zero,zero]))     #显示 (B,0,0)图像
    cv2.imshow('green', cv2.merge([zero, g , zero]))    #显示(0,G,0)图像
    cv2.imshow('red', cv2.merge([zero, zero, r]))   #显示(0,0,R)图像
    cv2.waitKey(0)
    cv2.destroyAllWindows()
 
#=============================绘制灰度直方图==========================#
flag = 0
# flag = 1
if flag == 1:
    img = cv2.imread('girl.jpg',-1)
    img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #绘制直方图,参数分别为img、通道、mask、份数、灰度值范围
    #方括号必须要加上哦
    hist = cv2.calcHist([img],[0],None,[256],[0,256])
 
    #创建画布,可以自定义大小,figsize = ?
    plt.figure('gray_hist')
    #画布名称
    plt.title('gray_hist')
    plt.xlabel('grayhist')
    plt.ylabel('pixel')
    plt.plot(hist)
    plt.xlim([0,256])
    plt.show()
 
 
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('girl.jpg',-1)
    # girl.ravel()函数是将图像的三位数组降到一维上去,
    # 256为bins的数目,[0, 256]为范围
    plt.hist(img.ravel(),256,[0,256])
    plt.show()
 
#============================绘制三通道直方图==========================#
flag = 0
# flag = 1
if flag == 1:
    img = cv2.imread('girl.jpg',-1)
    color = ('b','g','r')
    # 使用for循环遍历color列表,enumerate枚举返回索引和值
    for i , color in enumerate(color):
        hist = cv2.calcHist([img],[i],None,[256],[0,256])
        plt.title('gray_hist')
        plt.xlabel('grayhist')
        plt.ylabel('pixel')
        plt.plot(hist,color = color)
        plt.xlim([0, 256])
 
    plt.show()


OpenCV(二)——图像基本处理_Billie使劲学的博客-CSDN博客_opencv图像处理

Excerpt

课程一览:目录1.opencv中的绘图函数1.1 线段绘制1.2 矩形绘制1.3 圆、椭圆绘制1.4 多边形绘制1.5 添加文字2.图像的几何变换2.1 图像平移2.2 图像缩放2.3 图像旋转2.4 图像镜像2.5 仿射变换2.6 透视变换3.图像滤波与增强3.1线性滤波3.2 非线性滤波3.3 图像锐化3.4 直方图均衡化3.5 Gamma变化4.图像形态学操作4.1 图像腐蚀4.2 图像膨胀4.3 开运


课程一览:

目录

1.opencv中的绘图函数

1.1 线段绘制

1.2 矩形绘制

1.3 圆、椭圆绘制

1.4 多边形绘制

1.5 添加文字

2.图像的几何变换

2.1 图像平移

2.2 图像缩放

2.3 图像旋转

2.4 图像镜像

2.5 仿射变换

2.6 透视变换

3.图像滤波与增强

3.1线性滤波

3.2 非线性滤波

3.3 图像锐化

3.4 直方图均衡化

3.5 Gamma变化

4.图像形态学操作

4.1 图像腐蚀

4.2 图像膨胀

4.3 开运算

4.4 闭运算

4.5 形态学梯度

4.6 顶帽和黑帽


OpenCV图像处理(Python版)_第1张图片

1.opencv中的绘图函数

1.1 线段绘制

函数:cv2.line(img,pts,color,thickness,linetype)

参数说明:

  • img:待绘制的图像
  • color:形状的颜色,元组如(255,0,0)
  • pts:起点和终点
  • thickness:线条的粗细。-1位填充,默认值是1。
  • linetype:线条的类型,8型或cv2.LINE_AA,默认值为8型。(AA比8型更快,8型比AA型更好看)

OpenCV图像处理(Python版)_第2张图片

#=============================绘制线段============================#
# flag = 0
flag = 1
if flag == 1:
    # 创建一张黑色的背景图
    img=np.zeros((512,512,3), np.uint8)
    cv2.imshow("black", img)
    cv2.waitKey(0)
    cv2.destroyWindow("black")
    # 绘制一条线宽为5的线段
    cv2.line(img,(0,0),(200,500),(0,0,255),5)
    #定义一个变量
    winname = 'example'
    #将这个名字作为窗口名
    cv2.namedWindow(winname)
    cv2.imshow(winname, img)
    cv2.waitKey(0)
    cv2.destroyWindow(winname)

输出结果:

OpenCV图像处理(Python版)_第3张图片OpenCV图像处理(Python版)_第4张图片

1.2 矩形绘制

函数:cv2.rectangle(img,prets,color,thickness,linetype)

参数说明:

  • img:待绘制图像
  • pts:左上角和右下角坐标点
  • color:形状的颜色,元组如(255,0,0)
  • thickness:线条的粗细。-1位填充,默认值是1。
  • linetype:线条的类型,使用默认值即可

#=============================矩形线段============================#
# flag = 0
flag = 1
if flag == 1:
    img = np.zeros((512,512,3),np.uint8)
    #绘制矩形
    #新图像可以单独命名,也可以直接原位操作
    img_2 = cv2.rectangle(img,(23,23),(234,134),(19,0,240),-1)
    # cv2.rectangle(img, (23, 23), (234, 134), (19, 0, 240), -1)
    
    cv2.imshow("rectangle",img_2)
    cv2.waitKey(0)
    cv2.destroyWindow()

OpenCV图像处理(Python版)_第5张图片

OpenCV图像处理(Python版)_第6张图片

1.3 圆、椭圆绘制

绘制圆

函数:cv2.circle(img,pts,radius,color,thickness,linetype)

参数说明:

  • img:待绘制图像
  • pts:左圆心
  • radius:半径
  • color:形状的颜色
  • thickness:线条的粗细。-1位填充,默认值是1。
  • linetype:线条的类型,使用默认值即可

OpenCV图像处理(Python版)_第7张图片

#=============================圆绘制============================#
# flag = 0
flag = 1
if flag == 1:
    img = np.zeros((512,512,3),np.uint8)
    #绘制圆形
    #新图像可以单独命名,也可以直接原位操作
    #最后一个参数为1则表示不填充
    img_2 = cv2.circle(img,(100,100),78,(19,0,240),-1)
    # cv2.rectangle(img, (23, 23), (234, 134), (19, 0, 240), -1)
 
    cv2.imshow("rectangle",img_2)
    cv2.waitKey(0)
    cv2.destroyWindow()

OpenCV图像处理(Python版)_第8张图片

绘制椭圆

函数:cv2.ellipse()

画椭圆需要的参数比较多,请对照后面的代码理解这几个参数。

参数说明:

  • 参数1:图像
  • 参数2:椭圆中心(x,y)
  • 参数3:x/y轴的长度
  • 参数4:angle——椭圆的旋转角度
  • 参数5:startAngle——椭圆的起始角度
  • 参数6:endAngle——椭圆的结束角度

OpenCV图像处理(Python版)_第9张图片

#=============================椭圆绘制============================#
# flag = 0
flag = 1
if flag == 1:
    img = np.zeros((512,512,3),np.uint8)
    #绘制椭圆
    #新图像可以单独命名,也可以直接原位操作
    #最后一个参数为1则表示不填充
    cv2.ellipse(img,(200,100),(78,45),0,30,270,(19,0,240),-1)
    cv2.ellipse(img, (200, 250), (78, 45), 0, 0, 360, (19, 0, 240), -1)
    cv2.ellipse(img, (200, 400), (78, 45), 30, 180, 360, (19, 0, 240), -1)
    # cv2.rectangle(img, (23, 23), (234, 134), (19, 0, 240), -1)
 
    cv2.imshow("picture",img)
    cv2.waitKey(0)
    cv2.destroyWindow()

OpenCV图像处理(Python版)_第10张图片

1.4 多边形绘制

函数:cv2.polylines(img,pts,isClosed,color,thickness,lineType)

参数说明:

  • 参数1:img图像
  • 参数2:pts,表示多个坐标点
  • 参数3:isClosed,布尔型,True表示的是线段闭合,False表示的是仅保留线段
  • 参数4:color,线段颜色,格式是(R,G,B)
  • 参数5:thickness,数值型,线宽度,默认值为1,-1则会填充整个图形
  • 参数6:lineType,线型

OpenCV图像处理(Python版)_第11张图片

#=============================多边形绘制============================#
# flag = 0
flag = 1
if flag == 1:
    img = np.zeros((512, 512, 3), np.uint8)
    # 定义四个顶点坐标
    pts = np.array([[200, 200],  [250, 210], [100, 280], [230, 100]])
    print(pts)
    # 顶点个数:4,矩阵变成4*1*2维
    pts = pts.reshape((-1, 1, 2))
    print(pts)
    #绘制多边形
    cv2.polylines(img, [pts], False, (19, 0, 240))
    cv2.polylines(img, [pts], True, (19, 0, 240))
    cv2.imshow("picture", img)
    cv2.waitKey(0)
    cv2.destroyWindow()

OpenCV图像处理(Python版)_第12张图片

1.5 添加文字

函数:cv2.putText()

参数说明:

  • 参数1:img,图像–画板
  • 参数2:要添加的文字
  • 参数3:文字的起始坐标(左下角为起点)
  • 参数4:字体
  • 参数5:文字大小(缩放比例)
  • 参数6:颜色
  • 参数7:线条宽度
  • 参数8:线条形状

OpenCV图像处理(Python版)_第13张图片

#=============================添加文字============================#
# flag = 0
flag = 1
if flag == 1:
    img = np.zeros((512, 512, 3), np.uint8)
 
    #添加文字
    font=cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(img,'OpenCV',(50,200), font, 3,(0,255,255),5)
 
    cv2.imshow("picture", img)
    cv2.waitKey(0)
    cv2.destroyWindow()

OpenCV图像处理(Python版)_第14张图片

2.图像的几何变换

2.1 图像平移

图像平移:
将图像中所有的点按照指定的平移量水平或垂直移动。

变换公式:

设(x0,y0)为原图像上的一点,图像水平平移量为Tx,垂直平移量为Ty,则平移后的点坐标(x1,y1)变为:

x1 = x0 + Tx;  y1 = y0 + Ty

用仿射变换来封装函数

仿射变换函数:cv2.warpAffine(src,M,dsize,flags,borderMode,borderValue)

参数说明:

  • src:输入图像
  • M:变换矩阵
    • M作为仿射变换矩阵,一般反映平移或旋转的关系,为InputArray类型的2×3的变换矩阵
  • dsize:输出图像的大小
  • flags:插值方法的组合(int类型!)
    • 默认为flag = cv2.INTER_LINEAR,表示线性插值
    • cv2.INTER_NEAREST(最近邻插值)
    • cv2.INTER_AREA(区域插值)
    • cv2.INTER_CUBIC(三次样条插值)
    • cv2.INTER_LANCZOS4(Lanczos插值)
  • borderMode:边界像素模式(int类型!)
  • borderValue:(重点!)边界填充值;默认情况下为0。

OpenCV图像处理(Python版)_第15张图片

#=============================图像平移============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('img2.png')
    # 构造移动矩阵H
    # 在x轴方向移动多少距离,在y轴方向移动多少距离
    #[1,0,50]表示在x轴上移动50单位;[0,1,25]表示在y轴上平移25单位
    H = np.float32([[1, 0, 50], [0, 1, 25]])
    #获取图像的行、列
    rows, cols = img.shape[:2]
    print(img.shape)
    print(rows, cols)
 
    # 注意这里rows和cols需要反置,即先列后行
    res = cv2.warpAffine(img, H, (2*cols, 2*rows))
    cv2.imshow('origin_picture', img)
    cv2.imshow('new_picture', res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第16张图片OpenCV图像处理(Python版)_第17张图片

2.2 图像缩放

下采样:缩小图像称为下采样或降采样

上采样:方法图像称为上采样,主要目的是得到更高的分辨率

OpenCV图像处理(Python版)_第18张图片

图像缩放:图像缩放是指图像大小按照指定比例进行方法或缩小

函数:cv2.resize(src,dsize=None,fx,fy,interpolation)

参数说明:

  • src:原图
  • dsize:输出图像尺寸,与比例因子二选一
  • fx:沿水平轴的比例因子
  • fy:沿垂直轴的比例因子
  • interpolation:插值方法
    • 默认为flag = cv2.INTER_NEAREST(最近邻插值)
    • cv2.INTER_LINEAR,表示线性插值
    • cv2.INTER_AREA(区域插值)
    • cv2.INTER_CUBIC(三次样条插值)
    • cv2.INTER_LANCZOS4(Lanczos插值)

(1)最近邻插值:最简单的一种插值方法,不需要计算,在待求像素的四邻像素中,将距离待求像素最近的邻像素灰度赋给待求像素。(缺点:输出的图像会有锯齿状)

设i+u,j+v(i,j为正整数,u,v为大于零小于1的小数,下同)为待求像素坐标,则待求像素灰度的值f(i+u,j+v)

公式如下:src为原来的坐标,dst为待求的坐标

如图:图像由原来的3×3变为4×4大小的图像

srcWidth为3;dstWidth为4

以待求图的第一个点(0,0)为例:

srcX = (0*(3/4),0*(3/4))=(0,0)= 234

得出的(0,0)就为原图坐标,则待求点的值就用求出的相对应原图位置的值来代替

以待求图的(3,0)点为例:

srcX = ((3*(3/4),(0*(3/4))=(3*0.75,0)=(2.25)=(2,0)=89

采用四舍五入的方法求最近坐标

(2)线性插值

单线性插值:

双线性插值:

双线性插值又叫一阶插值法,它要经过三次插值才能获得最终结果,是对最近邻插值法的一种改进,先对两水平方向进行一阶线性插值,然后再在垂直方向进行一阶线性插值。

OpenCV图像处理(Python版)_第19张图片

#=============================图像缩放============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('img2.png')
    # 方法一:通过设置缩放比例,来对图像进行放大或缩小
    #缩放因子设置为2,放大两倍
    res1 = cv2.resize(img, None, fx=2, fy=2,
                      interpolation=cv2.INTER_CUBIC)
    height, width = img.shape[:2]
    # 方法二:直接设置图像的大小,不需要缩放因子
    #cv2.INTER_NEAREST(最近邻插值) cv2.INTER_AREA (区域插值) cv2.INTER_CUBIC(三次样条插值) cv2.INTER_LANCZOS4(Lanczos插值)
    
    res2 = cv2.resize(img, (int(0.8*width), int(0.8*height)),interpolation=cv2.INTER_LANCZOS4)
    cv2.imshow('origin_picture', img)
    #|cv2.imshow('res1', res1)
    cv2.imshow('res2', res2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第20张图片

2.3 图像旋转

图像旋转:以图像的中心为原点,旋转一定的角度,也就是将图像上的所有像素都旋转一个相同的角度。旋转都图像的大小一般会改变,即可以把转出显示区域的图像裁取,或扩大图像范围来显示所有的图像。图像的旋转变换也可以用矩阵变换来表示。

设点p_{0}(x_{0},y_{0})逆时针旋转\theta角后的对应点为p_{}(x_{},y_{})

那么,旋转后点p_{}(x_{},y_{})的坐标是:

利用上述方法进行图像旋转是需要注意以下两点:

(1)图像旋转之前,为了避免信息的丢失,一定要有坐标平移。

(2)图像旋转之后,会出现许多空洞点。对这些空洞点必须进行填充处理,否则画面效果不好,一般也称这种操作为插值处理。

图像的旋转使用仿射变换函数封装:

变换矩阵函数:cv2.getRotationMatrix2D(center,angle,scale)

参数说明:

  • center:图片的旋转中心
  • angle:旋转角度
  • scale:缩放比例,0.5表示缩小一半
    • scale的正负表示旋转方向:正为逆时针,负为顺时针。

OpenCV图像处理(Python版)_第21张图片

#=============================图像旋转============================#
# flag = 0
flag = 1
if flag == 1:
    img=cv2.imread('img2.png',1)
    rows,cols=img.shape[:2]
    #参数1:旋转中心,参数2:旋转角度,参数3:缩放因子
    #参数3正为逆时针,负值为正时针
    #创建一个仿射矩阵
    M=cv2.getRotationMatrix2D((cols/2,rows/2),45,1,)
    print(M)
    #第三个参数是输出图像的尺寸中心
    dst=cv2.warpAffine(img,M,(cols,rows))
    #dst=cv2.warpAffine(img,M,(cols,rows),borderValue=(255,255,255))
    while(1):
        cv2.imshow('img', img)
        cv2.imshow('img1',dst)
        #0xFF==27  ESC
        if cv2.waitKey(1)&0xFF==27:
            break
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第22张图片

2.4 图像镜像

2.5 仿射变换

仿射变换的作用:通过仿射变换对图片进行旋转、平移、缩放等操作以达到数据增强的效果。

线性变换从几何直观上来看有两个要点:变换之前是直线、变换之后依然是直线;直线的比例保持不变。

以下变换分别为:平移、旋转、同比例缩放、不同比例缩放、镜像、剪切

OpenCV图像处理(Python版)_第23张图片

仿射变换:平移、旋转、缩放、剪切、反射

函数:

仿射变换的函数原型如下:

M = cv2.getAffineTransform(pos1,pos2)

pos1表示变换前的位置,三个点

pos2表示变换后的位置,三个点

OpenCV图像处理(Python版)_第24张图片

#=============================仿射变换============================#
# flag = 0
flag = 1
if flag == 1:
    src = cv2.imread('bird.png')
    # 获取图像大小
    rows, cols = src.shape[:2]
    # 设置图像仿射变换矩阵
    pos1 = np.float32([[50, 50], [200, 50], [50, 200]])
    pos2 = np.float32([[10, 100], [200, 50], [100, 250]])
    M = cv2.getAffineTransform(pos1, pos2)
    print(M)
    # 图像仿射变换
    result = cv2.warpAffine(src, M, (2 * cols, 2 * rows))
    # 显示图像
    cv2.imshow("original", src)
 
    cv2.imshow("result", result)
    # 等待显示
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第25张图片OpenCV图像处理(Python版)_第26张图片

2.6 透视变换

透视变换:本质是将图像投影到一个新的视屏面

函数:

M = cv2.getPerspectiveTransform(pos1,pos2)

参数说明:

  • pos1表示透视变换前的四个点对应位置
  • pos2表示透视变换后的四个点对应位置

cv2.warpPerspective(src,M,(cols,rows))

参数说明:

  • src表示原始图像
  • M表示透视变换矩阵
  • (rows,cols)表示变换后的图像大小,rows表示行数,cols表示列数

OpenCV图像处理(Python版)_第27张图片

# =============================透视变换============================#
# flag = 0
flag = 1
if flag == 1:
    #读取图片
    src = cv2.imread('bird.png')
    #获取图像大小
    rows, cols = src.shape[:2]
    #设置图像透视变换矩阵
    pos1 = np.float32([[114, 82], [287, 156],
                       [8, 100], [143, 177]])
    pos2 = np.float32([[0, 0], [188, 0],
                       [0, 262], [188, 262]])
    M = cv2.getPerspectiveTransform(pos1, pos2)
    #图像透视变换
    result = cv2.warpPerspective(src, M, (2*cols,2*rows))
    #显示图像
    cv2.imshow("original", src)
    cv2.imshow("result", result)
    #等待显示
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第28张图片

OpenCV图像处理(Python版)_第29张图片

2.7 小结

例1:利用图像的变换对文档进行矫正

OpenCV图像处理(Python版)_第30张图片

# =============================图像矫正============================#
# flag = 0
flag = 1
if flag == 1:
    #读取图片
    src = cv2.imread('paper.png')
 
    #获取图像大小
    rows, cols = src.shape[:2]
 
    #将源图像高斯模糊,去除噪声
    img = cv2.GaussianBlur(src, (3,3), 0)
    #进行灰度化处理
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
 
    #边缘检测(检测出图像的边缘信息)
    edges = cv2.Canny(gray,50,250,apertureSize = 3)
    cv2.imwrite("canny.jpg", edges)
    cv2.imshow("canny", edges)
    #通过霍夫变换得到A4纸边缘,这是可以在二值图像中检测直线的方法
    lines = cv2.HoughLinesP(edges,1,np.pi/180,50,minLineLength=90,maxLineGap=10)
    print(lines)
    #下面输出的四个点分别为四个顶点
    for x1,y1,x2,y2 in lines[0]:
        print(x1,y1)
        print(x2,y2)
    for x3,y3,x4,y4 in lines[1]:
        print(x3,y3)
        print(x4,y4)
 
    #绘制边缘
    for x1,y1,x2,y2 in lines[0]:
        cv2.line(gray, (x1,y1), (x2,y2), (0,0,255), 1)
 
    #根据四个顶点设置图像透视变换矩阵
    pos1 = np.float32([[114, 82], [287, 156], [8, 322], [216, 333]])
    pos2 = np.float32([[0, 0], [188, 0], [0, 262], [188, 262]])
    M = cv2.getPerspectiveTransform(pos1, pos2)
    # pos1 = np.float32([[114, 82], [287, 156], [8, 322]])
    # pos2 = np.float32([[0, 0], [188, 0], [0, 262]])
    # M = cv2.getAffineTransform(pos1,pos2)
    print(M)
    #图像仿射变换
    #result = cv2.warpAffine(src, M, (2*cols, 2*rows))
 
 
    #图像透视变换
    result = cv2.warpPerspective(src, M, (190, 272))
 
    #显示图像
    cv2.imshow("original", src)
    cv2.imshow("result", result)
 
    cv2.imshow("gray", gray)
    #等待显示
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第31张图片

例2:利用几何变换,对图像进行扩增

# =============================图数扩增============================#
# flag = 0
flag = 1
if flag == 1:
    # 读取图片
    img = cv2.imread('test2.png')
    image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
    # 图像平移矩阵
    M = np.float32([[1, 0, 80], [0, 1, 30]])
    rows, cols = image.shape[:2]
    img1 = cv2.warpAffine(image, M, (cols, rows))
 
    # 图像缩小
    img2 = cv2.resize(image, (200, 100))
 
    # 图像放大
    img3 = cv2.resize(image, None, fx=1.1, fy=1.1)
 
    # 绕图像的中心旋转
    # 源图像的高、宽 以及通道数
    rows, cols, channel = image.shape
    # 函数参数:旋转中心 旋转度数 scale
    M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 30, 1)
    # 函数参数:原始图像 旋转参数 元素图像宽高
    img4 = cv2.warpAffine(image, M, (cols, rows))
 
    # 图像翻转
    img5 = cv2.flip(image, 0)  # 参数=0以X轴为对称轴翻转
    img6 = cv2.flip(image, 1)  # 参数>0以Y轴为对称轴翻转
 
    # 图像的仿射
    pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
    pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
    M = cv2.getAffineTransform(pts1, pts2)
    img7 = cv2.warpAffine(image, M, (rows, cols))
 
    # 图像的透射
    pts1 = np.float32([[56, 65], [238, 52], [28, 237], [239, 240]])
    pts2 = np.float32([[0, 0], [200, 0], [0, 200], [200, 200]])
    M = cv2.getPerspectiveTransform(pts1, pts2)
    img8 = cv2.warpPerspective(image, M, (200, 200))
 
    # 循环显示图形
    titles = ['source', 'shift', 'reduction', 'enlarge', 'rotation', 'flipX', 'flipY', 'affine', 'transmission']
    images = [image, img1, img2, img3, img4, img5, img6, img7, img8]
    for i in range(9):
        plt.subplot(3, 3, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()


OpenCV图像处理(Python版)_第32张图片

3.图像滤波与增强

  • 滤波实际上是信号处理的一个概念,图像可以看成一个二维信号,其中像素点的灰度值代表信号的强弱。
  • 高频:图像上变化剧烈的部分
  • 低频:图像灰度值变化缓慢,平坦的地方
  • 根据图像高低频,设置高通和低通滤波器。高通滤波器可以检测变化尖锐,明显的地方,低通可以让图像变得平滑,消除噪声
  • 滤波作用:高通滤波用于边缘检测,低通滤波用于图像平滑去噪
  • 线性滤波:方框滤波/均值滤波/高斯滤波
  • 非线性滤波:中值滤波/双边滤波

3.1线性滤波

**邻域算子:**利用给定像素周围的像素值决定此像素的最终输出值得一种算子。

**线性滤波:**一种常用的邻域算子,像素输出取决于输入像素的加权和。(卷积操作)

OpenCV图像处理(Python版)_第33张图片

(1)方框滤波

方框滤波被封装在一个名为boxFilter的函数中,即boxFilter函数的作用是使用方框滤波器来模糊一张图片,从src输入,从dst输出。

方框滤波核:

normalize = True 与均值滤波相同,归一化

normalize = False 很容易发生溢出

函数:cv2.boxFilter(src,depth,ksize,normalize)

参数说明:

  • 参数1:输入图像
  • 参数2:目标图像深度,一般采用默认值
  • 参数3:核大小,默认是3×3
  • 参数4:normalize属性,1或 0

OpenCV图像处理(Python版)_第34张图片

# =============================方框滤波============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('girl2.png',cv2.IMREAD_UNCHANGED)
    r = cv2.boxFilter(img, -1 , (7,7) , normalize = 1)
    d = cv2.boxFilter(img, -1 , (3,3) , normalize = 0)
    cv2.namedWindow('img',cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow('r',cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow('d',cv2.WINDOW_AUTOSIZE)
    cv2.imshow('img',img)
    cv2.imshow('r',r)
    cv2.imshow('d',d)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第35张图片

(2)均值滤波

均值滤波是一种最简单的滤波处理,它取的是卷积核区域内元素的均值,用cv2.blur()实现,如3×3的卷积核:

函数:cv2.blur(src,ksize)

参数说明:

  • 参数1:输入原图
  • 参数2:kernel的大小,一般为奇数

OpenCV图像处理(Python版)_第36张图片

# =============================均值滤波============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('image/opencv.png')
    cv2.imshow('img',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    blur = cv2.blur(img,(7,7 ))
    plt.subplot(121),plt.imshow(img),plt.title('Original')
    plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
    #不显示坐标
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第37张图片

(3)高斯滤波

高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛用于图像处理的减噪过程。高斯滤波的卷积核权重并不相同,中间像素点权重最高,越远离中心的像素权重越小。(其原理是一个二维高斯函数)

高斯滤波相比均值滤波效率要慢,但可以有效消除高斯噪声,能保留更多的图像细节,所以经常被称为最有用的滤波器。

OpenCV图像处理(Python版)_第38张图片OpenCV图像处理(Python版)_第39张图片

**函数:**cv2.Guassianblur(src,ksize,std)    ,表示进行高斯滤波

参数说明:

  • 参数1:输入原图
  • 参数2:高斯核大小
  • 参数3:标准差σ,平滑时,调整σ实际是在调整周围像素对当前像素的影响程度,调大σ即提高了远处像素对中心像素的影响程度,滤波结果也就越平滑。

OpenCV图像处理(Python版)_第40张图片

# =============================高斯滤波============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('image/median.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    blur = cv2.GaussianBlur(img, (7, 7), 7)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
    plt.xticks([]), plt.yticks([])
    plt.show()

可以看到噪声消除了一些

OpenCV图像处理(Python版)_第41张图片

3.2 非线性滤波

(1)中值滤波

中值滤波是一种非线性滤波,是用像素点邻域灰度值的中值代替该点的灰度值,中值滤波可以去除椒盐噪声和斑点噪声

函数:cv2.medianBlur(img,ksize)

参数说明

  • 参数1:输入图像
  • 参数2:核大小

OpenCV图像处理(Python版)_第42张图片

# =============================中值滤波============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('image/median.png')
    median = cv2.medianBlur(img, 3)
 
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(median), plt.title('median')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第43张图片

(2)双边滤波

双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空间与信息和灰度相似性,达到保边去噪的目的,具有简单、非迭代、局部处理的特点。

函数:cv2.bilateralFilter(src = image,d,sigmaColor,sigmaSpace)

参数说明:

  • 参数1:输入原图
  • 参数2:像素的邻域直径
  • 参数3:灰度值相似性高斯函数标准差
  • 参数4:空间高斯函数标准差

OpenCV图像处理(Python版)_第44张图片

# =============================双边滤波============================#
# flag = 0
flag = 1
if flag == 1:
    '''
    关于2个sigma参数:
    简单起见,可以令2个sigma的值相等; 
    如果他们很小(小于10),那么滤波器几乎没有什么效果; 
    如果他们很大(大于150),那么滤波器的效果会很强,使图像显得非常卡通化; 
    
    关于参数d:
    过大的滤波器(d>5)执行效率低。 对于实时应用,建议取d=5; 
    对于需要过滤严重噪声的离线应用,可取d=9; 
    d>0时,由d指定邻域直径; 
    d<=0时,d会自动由sigmaSpace的值确定,且d与sigmaSpace成正比;
    '''
    img = cv2.imread('image/bilateral.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    blur = cv2.bilateralFilter(img, -1, 15, 10)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
    plt.xticks([]), plt.yticks([])
    plt.show()

加大参数3和参数4的值可以更好的去除噪声

OpenCV图像处理(Python版)_第45张图片

3.3 图像锐化

# =============================图像锐化============================#
# flag = 0
flag = 1
if flag == 1:
    def custom_blur_demo(image):
        kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) #锐化
        dst = cv2.filter2D(image, -1, kernel=kernel)
        cv2.imshow("custom_blur_demo", dst)
    src = cv2.imread("./image/sharpen.png")
    cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE)
    cv2.imshow("input image", src)
    custom_blur_demo(src)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第46张图片

3.4 直方图均衡化

(1)灰度图直方图均衡化

目的:直方图均衡化是将原图通过某种变换,得到一幅灰度直方图为均匀分布的新图像的方法

直方图均衡化方法的基本思想是对在图像中像素个数多的灰度级进行展宽,而对像素个数少的灰度级进行缩减。从而达到清晰图像的目的。

函数:cv2.equalizeHist(img)

参数说明:

  • 参数1:待均衡化图像

步骤:

  • 统计直方图中每个灰度级出现的次数
  • 计算累计归一化直方图
  • 重新计算像素点的像素值。

OpenCV图像处理(Python版)_第47张图片

OpenCV图像处理(Python版)_第48张图片

OpenCV图像处理(Python版)_第49张图片

# =============================直方图均衡化============================#
# flag = 0
flag = 1
if flag == 1:
    #直接读为灰度图像
    img = cv2.imread('./image/dark.png',0)
    cv2.imshow("dark",img)
    cv2.waitKey(0)
    #调用cv2.equalizeHist函数进行直方图均衡化
    img_equal = cv2.equalizeHist(img)
 
    cv2.imshow("img_equal",img_equal)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第50张图片 OpenCV图像处理(Python版)_第51张图片

(2)局部直方图均衡化

# flag = 0
flag = 1
if flag == 1:
    #局部直方图均衡化
    #直接读为灰度图像
    img = cv2.imread('./image/dark.png',0)
    cv2.imshow("dark",img)
    cv2.waitKey(0)
    #调用cv2.createCLAHE函数进行局部直方图均衡化
    clahe = cv2.createCLAHE(clipLimit=2,tileGridSize=(30,30))
    cl1 = clahe.apply(img)
 
    cv2.imshow("img_equal",cl1)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

OpenCV图像处理(Python版)_第52张图片OpenCV图像处理(Python版)_第53张图片

(3)彩色图直方图均衡化

OpenCV图像处理(Python版)_第54张图片

# flag = 0
flag = 1
if flag == 1:
    #彩图直方图均衡化
    img = cv2.imread("./image/dark1.jpg")
    cv2.imshow("src", img)
    # 彩色图像均衡化,需要分解通道 对每一个通道均衡化
    (b, g, r) = cv2.split(img)
    bH = cv2.equalizeHist(b)
    gH = cv2.equalizeHist(g)
    rH = cv2.equalizeHist(r)
    # 合并每一个通道
    result = cv2.merge((bH, gH, rH))
    cv2.imshow("dst", result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

亮度提升,但提升效果夸张

3.5 Gamma变化

Gamma变换是对输入图像灰度值进行的非线性操作,使输出图像灰度值与输入图像灰度值呈指数关系。

目的:Gamma变换就是用来图像增强,其提升了暗部细节,通过非线性变换,让图像从曝光强度的线性响应变得更接近人眼感受的响应,即将漂白(相机曝光)或过暗(曝光不足)的图片,进行矫正。

Gamma > 1,图像变亮

Gamma < 1,图形变暗

OpenCV图像处理(Python版)_第55张图片

# =============================Gamma变换============================#
# flag = 0
flag = 1
if flag == 1:
    img=cv2.imread('./image/dark1.jpg')
    def adjust_gamma(image, gamma=1.0):
        invGamma = 1.0/gamma
        table = []
        for i in range(256):
            table.append(((i / 255.0) ** invGamma) * 255)
        table = np.array(table).astype("uint8")
        print(table)
        return cv2.LUT(image, table)
 
    img_gamma = adjust_gamma(img, 0.8)
    #print(img_gamma)
    cv2.imshow("img",img)
    cv2.imshow("img_gamma",img_gamma)
 
    cv2.waitKey(0)
    cv2.destroyAllWindows()

4.图像形态学操作

形态学是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质的形状特征如边界和连通区域等。

结构元素:

设有两幅图像B,X。

若X是被处理的对象,而B是用来处理X的,则称B为结构元素,又被形象的称作刷子

结构原色通常都是一些比较小的图像。

腐蚀和膨胀

图像的膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,其中膨胀类似于“领域扩张”,将图像中的白色部分(领域)进行扩张,其运行结果图比原图的白色区域更大;腐蚀类似于“领域被蚕食”,将图像中白色部分进行缩减细化,其运行结果图比原图的白色区域更小。

4.1 图像腐蚀

(1)灰度图像的腐蚀操作

腐蚀运算符“—”,其定义如下:

该公式表示图像A用卷积模板B来进行腐蚀处理,通过模板B与图像A进行卷积计算,得出B覆盖区域的像素点最小值,并用这个最小值来代替参考点的像素值。

OpenCV图像处理(Python版)_第56张图片

(2)二值图像的腐蚀操作

把结构元素B平移a后得到Ba,若Ba包含于X,我们记下这个a点,所有满足上述条件的a点组成的集合称作X被B腐蚀的结果。如图所示。

OpenCV图像处理(Python版)_第57张图片

其中X是被处理的对象,B是结构元素。对于任意一个在阴影部分的点a,Ba包含于X,所以X被B腐蚀的结果就是那个阴影部分。阴影部分在X的范围之内,且比X小,就像X被剥掉一层似的。

OpenCV图像处理(Python版)_第58张图片

函数:cv2.erode(src,element,anchor,iterations)

参数说明:

  • 参数1:src,原图像
  • 参数2:element,腐蚀操作的内核,默认为一个简单的3×3矩阵
  • 参数3:anchor,默认为point(-1,1),内核中心点
  • 参数4:iterations,腐蚀次数,默认值为1
# =============================图像腐蚀============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/morphology.png')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    kernel = np.ones((3,3),np.uint8)
    erosion = cv2.erode(img,kernel,iterations = 1)
    plt.subplot(121),plt.imshow(img),plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(erosion),plt.title('erosion')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第59张图片

4.2 图像膨胀

膨胀(dilation)可以看做是腐蚀的对偶运算。

其定义是:把结构元素B平移a后得到Ba,若Ba击中(即B的任何一个点碰到X)X,我们记下这个a点。所有满足上述条件的a点组成的集合称作X被B膨胀的结果。如图所示。

OpenCV图像处理(Python版)_第60张图片

其中X是被处理的对象,B是结构元素,对于任意一个在阴影部分的点a,Ba击中X,所以X被B膨胀的结果就是那个阴影部分。阴影部分包括X的所有范围,就像X膨胀了一圈似的。

OpenCV图像处理(Python版)_第61张图片

# =============================图像膨胀============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/morphology.png')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    #kernel = np.ones((3,),np.uint8)
 
    # kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
    # kernel = np.ones((5,5),np.uint8)
    # kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (7,7))
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
 
    dilation = cv2.dilate(img,kernel,iterations = 1)
 
    plt.subplot(121),plt.imshow(img),plt.title('opening')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(dilation),plt.title('dilation')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第62张图片

4.3 开运算

开运算 = 先腐蚀运算,再膨胀运算(看上去把细微连在一起的两块目标分开了),开运算的效果图如下所示:

OpenCV图像处理(Python版)_第63张图片

开运算总结:

  • 开运算能够去除孤立的小点,毛刺和小桥,而总的位置和形状不变。
  • 开运算是一个基于几何运算的滤波器。
  • 结构元素大小的不同将导致滤波效果的不同。
  • 不同的结构元素的选择导致了不同的分割,即提取出不同的特征。
# =============================开运算(去除噪音)============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/open.png')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    #kernel = np.ones((5,5),np.uint8)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
    opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
    plt.subplot(121),plt.imshow(img),plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(opening),plt.title('opening')
    plt.xticks([]), plt.yticks([])
    plt.show()

可以看到,噪声被去掉了

OpenCV图像处理(Python版)_第64张图片

4.4 闭运算

闭运算 = 先膨胀运算,再腐蚀运算(看上去将两个细微连接的图块封闭在一起),闭运算的效果图如图所示:

OpenCV图像处理(Python版)_第65张图片

# =============================闭运算(去除噪音)============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/close.png')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    #kernel = np.ones((5,5),np.uint8)
    kernel = np.ones((7,7),np.uint8)
    closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
    plt.subplot(121),plt.imshow(img),plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(closing),plt.title('closing')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第66张图片

闭运算总结:

  • 闭运算能够填平小孔,弥合小裂缝,而总的位置和形状不变。
  • 闭运算是通过填充图像的凹角来滤波图像的。
  • 结构元素大小的不同将导致滤波效果的不同。
  • 不同结构元素的选择导致了不同的分割。

4.5 形态学梯度

形态学梯度:

  • 基础梯度:基础梯度时用膨胀后的图像减去腐蚀后的图像得到差值图像,也是opencv中支持的计算形态学梯度的方法,而此方法得到梯度有称为基本梯度。
  • 内部梯度:是用原图像减去腐蚀后的图像得到差值图像,称为图像的内部梯度。
  • 外部梯度:图像膨胀后再减去原来的图像得到的差值图像,称为图像的外部梯度。
# =============================形态学梯度============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/morphology.png')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    kernel = np.ones((3,3),np.uint8)
    gradient = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel)
    plt.subplot(121),plt.imshow(img),plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(gradient),plt.title('gradient')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第67张图片

4.6 顶帽和黑帽

顶帽(Top Hat):原图像与开运算图的差值,突出原图像中比周围亮的区域。

黑帽(Black Hat):闭操作图像与原图像的差值,突出原图像比周围暗的区域。

顶帽代码:

# =============================顶帽============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/morphology.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    kernel = np.ones((9, 9), np.uint8)
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(tophat), plt.title('tophat')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第68张图片

黑帽代码:

# =============================黑帽============================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/morphology.png')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    kernel = np.ones((9,9),np.uint8)
    tophat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
    plt.subplot(121),plt.imshow(img),plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(tophat),plt.title('tophat')
    plt.xticks([]), plt.yticks([])
    plt.show()

OpenCV图像处理(Python版)_第69张图片

OpenCV(三)——图像分割

Excerpt

目录1.图像分割2.固定阈值法——直方图双峰法3.自动阈值法3.1 自适应阈值法3.2 迭代阈值分割3.3 Otsu大津法4.边缘检测4.1 图像梯度的概念4.2 模板卷积和梯度图的概念4.3 梯度算子4.4Canny边缘检测算法5.连通区域分析5.1 连通区域概要5.2 Two-Pass算法6.区域生长算法6.1 区域生长概要6.2 区域生长原理7.分水岭算法7.1 分水岭算法概要7.2 分水岭算法1…


目录

1.图像分割

2.固定阈值法——直方图双峰法

3.自动阈值法

3.1 自适应阈值法

3.2 迭代阈值分割

3.3 Otsu大津法

4.边缘检测

4.1 图像梯度的概念

4.2 模板卷积和梯度图的概念

4.3 梯度算子

4.4 Canny边缘检测算法

5.连通区域分析

5.1 连通区域概要

5.2 Two-Pass算法

6.区域生长算法

6.1 区域生长概要

6.2 区域生长原理

7.分水岭算法

7.1 分水岭算法概要

7.2 分水岭算法


1.图像分割

  • 图像分割是指将图像分成若干具有相似性质的区域的过程,主要有基于阈值、基于区域、基于边缘、基于聚类、基于图论和基于深度学习的图像分割方法等。
  • 图像分割分为语义分割和实例分割。

  • 分割的原则就是使划分后的子图在内部保持相似度最大,而子图之间的相似度保持最小
  • 将G = (V,E)分成两个子集A,B,使得:

2.固定阈值法——直方图双峰法

**双峰法:**是典型的全局单阈值分割方法。

基本思想:假设图像中有明显的目标和背景,则其灰度直方图呈双峰分布,当灰度级直方图具有双峰特性时,选取两峰之间的对应的灰度级作为阈值

函数:cv2.threshold(src,thresh,maxval,type)

参数说明:

  • 参数1:原图像
  • 参数2:对像素值进行分类的阈值
  • 参数3:当像素值高于(低于)阈值时,应该被赋予的新的像素值
  • 参数4:第四个参数是阈值方法
  • 返回值:函数有两个返回值,一个为retVal(阈值),一个阈值化处理之后的图像。

#=================================固定阈值======================================#
# flag = 0
flag = 1
if flag == 1:
    # 灰度图读入
    img = cv2.imread('./image/thresh.png', 0)
    threshold = 127
    # 阈值分割
    ret, th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)
    print(ret)
 
    cv2.imshow('thresh', th)
    cv2.imshow('img',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# flag = 0
flag = 1
if flag == 1:
    #opencv读取图像
    img = cv2.imread('./image/person.png',0)
    #5种阈值法图像分割
    ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
    ret, thresh3 = cv2.threshold(img, 127, 255,cv2.THRESH_TRUNC)
    ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
    ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
 
    images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
    #使用for循环进行遍历,matplotlib进行显示
    for i in range(6):
        plt.subplot(2,3, i+1)
        plt.imshow(images[i],cmap='gray')
        plt.xticks([])
        plt.yticks([])
    plt.suptitle('fixed threshold')
    plt.show()

3.自动阈值法

3.1 自适应阈值法

对于简单的图像固定阈值法可以很好的分割,但是对于元素多,明暗变化不均匀的图像,固定阈值法就无法很好的进行分割。

函数:cv2.adaptiveThreshold()

参数说明

  • 参数1:要处理的原图
  • 参数2:最大阈值,一般为255
  • 参数3:小区域阈值的计算方式
    • ADAPTIVE_THRESH_MEAN_C:小区域内取均值
    • ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
  • 参数4:阈值方式(5种,如上节介绍)
  • 参数5:小区域的面积,如11就是11×11的小块
  • 参数6:最终阈值等于小区域计算出的阈值再减去此值

特定:自适应阈值会每次取图片的一小部分计算阈值,这样图片不同区域的阈值就不尽相同,适用于明暗分布不均的图片。

#=================================自适应阈值======================================#
# flag = 0
flag = 1
if flag == 1:
    img = cv2.imread('./image/paper2.png', 0)
 
    # 固定阈值
    ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    # 自适应阈值
    th2 = cv2.adaptiveThreshold(
        img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,11, 4)
    th3 = cv2.adaptiveThreshold(
        img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 4)
    #全局阈值,均值自适应,高斯加权自适应对比
    titles = ['Original', 'Global(v = 127)', 'Adaptive Mean', 'Adaptive Gaussian']
    images = [img, th1, th2, th3]
    for i in range(4):
        plt.subplot(2, 2, i + 1)
        plt.imshow(images[i], cmap='gray')
        plt.title(titles[i], fontsize=8)
        plt.xticks([])
        plt.yticks([])
    plt.show()

3.2 迭代阈值分割

步骤:

  • 求出图像的最大灰度值最小灰度值,分别记为ZMAX和ZMIN,令初始阈值tT0=(ZMAX+ZMIN)/2
  • 根据阈值TK将图像分割为前景和背景,分别求出两者的平均灰度值ZO和ZB
  • 求出新阈值TK+1=(ZO+ZB)/2
  • 若TK==TK+1,则所得即为阈值;否则转2,迭代计算
  • 使用计算后的阈值进行固定阈值分割

`#=自适应阈值——迭代法======#

flag = 0

flag = 1
if flag == 1:
def best_thresh(img):
img_array = np.array(img).astype(np.float32)#转化成数组
I=img_array
zmax=np.max(I)
zmin=np.min(I)
tk=(zmax+zmin)/2#设置初始阈值
#根据阈值将图像进行分割为前景和背景,分别求出两者的平均灰度zo和zb
b=1
m,n=I.shape;
while b0:
ifg=0
ibg=0
fnum=0
bnum=0
for i in range(1,m):
for j in range(1,n):
tmp=I(i,j)
if tmp>=tk:
ifg=ifg+1
fnum=fnum+int(tmp)#前景像素的个数以及像素值的总和
else:
ibg=ibg+1
bnum=bnum+int(tmp)#背景像素的个数以及像素值的总和
#计算前景和背景的平均值
zo=int(fnum/ifg)
zb=int(bnum/ibg)
if tk
int((zo+zb)/2):
b=0
else:
tk=int((zo+zb)/2)
return tk

img = cv2.imread("./image/bird.png")
img_1 = cv2.imread("./image/bird.png")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
img = cv2.resize(gray,(200,200))#大小
yvzhi=best_thresh(img)
ret1, th1 = cv2.threshold(img, yvzhi, 255, cv2.THRESH_BINARY)
print(ret1)
plt.imshow(img_1,cmap=cm.gray)
plt.show()
plt.imshow(th1,cmap=cm.gray)
plt.show()

![](https://img-blog.csdnimg.cn/676ee268cdbb4b14af9f41a83ea83384.png)

 ![](https://img-blog.csdnimg.cn/7a87295147b04d36afcf04f773c9f8ba.png)

## 3.3 Otsu大津法

**大津法:**

-   **最大类间方差**是一种基于**全局阈值**的自适应方法。
-   灰度特性:图像分为前景和背景。当取最佳阈值时,两部分之间的差别应该是最大的,衡量差别的标准为最大类间方差。
-   直方图有两个峰值的图像,大津法求得的T近似等于两个峰值之间的低谷。

![](https://img-blog.csdnimg.cn/fb8188d33283412c899fd03435579a5e.png)

**符号说明:**

-   T:图像(x,y)前景和背景的分割**阈值**。
-   w1:属于**前景的像素点数占整幅图像的比例**,其平均灰度μ1。
-   w2:**背景像素点数占整幅图像的比例**,其平均灰度μ2。
-   μ:图像的**总平均灰度**。
-   g:**类间方差**。
-   N1:设图像的大小为M×N,图像中像素的灰度值**小于阈值T的像素个数**。
-   N2:像素灰度**大于阈值T的像素个数**。

![](https://img-blog.csdnimg.cn/a5a657383a3d4b1690e8cc425f6b4be3.png)

 ![](https://img-blog.csdnimg.cn/af22db915ad44877a52c15b31dfb18ed.png)

#=自适应阈值——大津阈值法======#

flag = 0

flag = 1
if flag == 1:
img = cv2.imread(‘./image/noisy.png’, 0)
# 固定阈值法
ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)
# Otsu阈值法
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 先进行高斯滤波,再使用Otsu阈值法
blur = cv2.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
images = [img, 0, th1, img, 0, th2, blur, 0, th3]
titles = ['Original', 'Histogram', 'Global(v=100)',
          'Original', 'Histogram', "Otsu's",
          'Gaussian filtered Image', 'Histogram', "Otsu's"]

for i in range(3):
    # 绘制原图
    plt.subplot(3, 3, i * 3 + 1)
    plt.imshow(images[i * 3], 'gray')
    plt.title(titles[i * 3], fontsize=8)
    plt.xticks([]), plt.yticks([])

    # 绘制直方图plt.hist, ravel函数将数组降成一维
    plt.subplot(3, 3, i * 3 + 2)
    plt.hist(images[i * 3].ravel(), 256)
    plt.title(titles[i * 3 + 1], fontsize=8)
    plt.xticks([]), plt.yticks([])

    # 绘制阈值图
    plt.subplot(3, 3, i * 3 + 3)
    plt.imshow(images[i * 3 + 2], 'gray')
    plt.title(titles[i * 3 + 2], fontsize=8)
    plt.xticks([]), plt.yticks([])
plt.show()

![](https://img-blog.csdnimg.cn/840a1269d72c420991d381f3412d5f2e.png)

#Otsu源码
import numpy as np

def OTSU_enhance(img_gray, th_begin=0, th_end=256, th_step=1):
#“must input a gary_img”
assert img_gray.ndim == 2

max_g = 0
suitable_th = 0
for threshold in range(th_begin, th_end, th_step):
    bin_img = img_gray > threshold
    bin_img_inv = img_gray <= threshold
    fore_pix = np.sum(bin_img)
    back_pix = np.sum(bin_img_inv)
    if 0 == fore_pix:
        break
    if 0 == back_pix:
        continue

    w0 = float(fore_pix) / img_gray.size
    u0 = float(np.sum(img_gray * bin_img)) / fore_pix
    w1 = float(back_pix) / img_gray.size
    u1 = float(np.sum(img_gray * bin_img_inv)) / back_pix
    # intra-class variance
    g = w0 * w1 * (u0 - u1) * (u0 - u1)
    if g > max_g:
        max_g = g
        suitable_th = threshold
return suitable_th

img = cv2.imread(‘noisy.png’, 0)
thresh = OTSU_enhance(img)
ret1, th1 = cv2.threshold(img, thresh, 255, cv2.THRESH_BINARY)
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
a = plt.imshow(th1,cmap=cm.gray)
plt.show(a)
b = plt.imshow(th2,cmap=cm.gray)
plt.show(b)


## 4.[边缘检测](https://so.csdn.net/so/search?q=%E8%BE%B9%E7%BC%98%E6%A3%80%E6%B5%8B&spm=1001.2101.3001.7020)

## 4.1 图像梯度的概念

**图像梯度**:梯度是一个向量,梯度方向指向**函数变化最快的方向,大小就是它的模**,也是最大的变化率,对于二元函数z=f(x,y),它在点(x,y)的梯度就是grad(x,y)或者![](https://img-blog.csdnimg.cn/d100628f61114321a144d87323b10e24.png)

 ![](https://img-blog.csdnimg.cn/1de3367b96a8479cb5ab2a04df6b5969.png)

这个梯度向量的幅度和方向角为: 

![](https://img-blog.csdnimg.cn/f266291af19641118ea79e139560adb3.png)

图像梯度即**图像中灰度变化的度量**,求图像梯度的过程是**二维离散函数求导过程**。**边缘**其实就是图像上**灰度级变化很快的点的集合**。

下图展示了一个灰度图的数学化表达,像素点(x,y)的灰度值是f(x,y),它有八个邻域

![](https://img-blog.csdnimg.cn/8a9d6a7c2d934fff9fda2ba656b69740.png)

图像在点(x,y)的梯度为:

![](https://img-blog.csdnimg.cn/4d968a97ea3947fb9f9083c5c74427d4.png)

![](https://img-blog.csdnimg.cn/163ef5f5036249369d89270b78df5826.png)即gx对应图像的水平方向,![](https://img-blog.csdnimg.cn/38d9b7d8c2dd4991a751620d3762e094.png)即gx对应图像的竖直方向。

##  4.2 模板卷积和梯度图的概念

**(1)模板卷积:**

要理解梯度图的生成,就要先了解模板卷积的过程,模板卷积是模板运算的一种方式,其步骤如下:

-   将模板在输入图像中漫游,并将模板中心与图像中某个像素位置重合。
-   将模板上各个系数与模板下各对应像素的灰度相乘。
-   将所有乘积相加(为保持灰度范围,常将结果再除以模板系数之和,后面梯度算子模板和为0的话就不需要除了)。
-   将上述运算结果(模板的响应输出)赋给输出图像中对应模板中心位置的像素。

 ![](https://img-blog.csdnimg.cn/931f995de6bc4d939da438a4ccc87e75.png)

**(2)梯度图:** 梯度图的生成和模板卷积相同,不同的是要生成梯度图,还需要在模板卷积完成后计算在点(x,y)梯度的幅值,将幅值作为像素值,这样才算完。

注意:梯度图上每个像素点的灰度值就是梯度向量的幅度。

生成梯度图需要模板,右图为水平和竖直方向最简单的模板。

**水平方向**:![](https://img-blog.csdnimg.cn/467bf8862d1d40669f54b8a6104529f9.png)

**竖直方向**:![](https://img-blog.csdnimg.cn/61d49c95e0634d89b1caf1370b12be99.png)

 ![](https://img-blog.csdnimg.cn/ce114357d1164b55b479a4539c82499d.png)

## 4.3 梯度[算子](https://so.csdn.net/so/search?q=%E7%AE%97%E5%AD%90&spm=1001.2101.3001.7020)

**梯度算子**: 梯度算子是一阶导数算子,是水平G(x)和竖直G(y)方向对应模板的组合,也有对角线方向。

**常见的一阶算子**:Roberts交叉算子,Prewitt算子,Sobel算子

**(1)Roberts交叉算子**

![](https://img-blog.csdnimg.cn/beaa4b26fb1a48f3951b92101f3d694c.png)

Roberts交叉算子其本质是**一个对角线方向的梯度算子**,对应的水平方向和竖直方向的梯度分别为: 

![](https://img-blog.csdnimg.cn/cc3c9a20e9b64457b9f27001c4c07342.png)

**优点**:**边缘定位校准,适用于边缘明显且噪声较少的图像**

**缺点**:

-   **没有描述水平和竖直方向的灰度变化**,只关注了对角线方向,容易造成**遗漏**。
-   **鲁棒性差**。由于点本身参加了梯度计算,**不能有效的抑制噪声的干扰**。

**(2)Prewitt算子**

![](https://img-blog.csdnimg.cn/87804170a8724668aa982f91a9027637.png)

Prewitt算子是典型的3×3模板,其模板中心对应要求梯度的原图像坐标(x,y),(x,y)对应的8领域的像素灰度值如下表所示:

![](https://img-blog.csdnimg.cn/51ffc758a25b4e869e2848cd62e34ad8.png)

通过Prewitt算子的水平模板M(x)卷积后,对应的水平方向梯度为:

![](https://img-blog.csdnimg.cn/32ed640e537e4d1a9984bb096560013c.png)

 通过Prewitt算子的竖直模板M(y)卷积,对应的竖直方向梯度为:  
![](https://img-blog.csdnimg.cn/df38029363f44b1d89204f6287ebaf30.png)

输出梯度图在(x,y)的灰度值为:

![](https://img-blog.csdnimg.cn/f116373794f6448fafbea05f2aa07f6d.png)

Prewitt算子引入了类似**局部平均的运算**,对噪声具有平滑作用,**较Roberts算子更能抑制噪声**。 

**(3)Sobel算子**

![](https://img-blog.csdnimg.cn/f1811aa60f1d45e8ada250b18b36f0fc.png)

Sobel算子其实就是**增加了权重系数的Prewitt算子**,其模板中心对应要求梯度的原图像坐标,对应的8领域的像素灰度值如下表所示:

![](https://img-blog.csdnimg.cn/90e604845be24ffcb09a5bcfc00f46a2.png)

 过Sobel算子的水平模板M\_{x}卷积后,对应的水平方向梯度为:

![](https://img-blog.csdnimg.cn/f6a56f7fb5d9494989cfa20154f030a6.png)

通过Sobel算子的竖直模板M(y)卷积后,对应的竖直方向梯度为:

![](https://img-blog.csdnimg.cn/148fc56df8254dde9f309c0f00d5b98f.png)

输出梯度图在(x,y)的灰度值为:

![](https://img-blog.csdnimg.cn/e7b12dcd226140a7801fd398bd47efca.png)

Sobel算子引入了类似局部加权平均的运算,对边缘的定位比要比Prewitt算子好 

**Sobel算子**

**函数**:dst = cv2.Sobel(src,ddepth,dx,dy,ksize)

参数说明**:**

-   参数1:需要处理的图像
-   参数2:图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度
-   参数3,4:dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0,1,2
-   参数5:ksize是Sobel算子的大小,必须为1,3,5,7。

![](https://img-blog.csdnimg.cn/b6db02a00838444f9949018af05339df.png)

#=Sobel======#

flag = 0

flag = 1
if flag == 1:
img = cv2.imread(‘image/girl2.png’,0)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)

plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

![](https://img-blog.csdnimg.cn/7f8e9ce4f145400da234d5c398947fcb.png)

##  4.4 **Canny边缘检测算法**

Canny算子是**先平滑后求导数**的方法。John Canny研究了最优边缘检测方法所需的特性,给出了评价边缘检测性能优劣的三个指标:

-   **好的信噪比**,即将非边缘点判定为边缘点的概率要低,将边缘点判为非边缘点的概率要低
-   **高的定位性能**,即检测出的边缘点要尽可能在实际边缘的中心
-   **对单一边缘仅有唯一响应**,即单个边缘产生多个响应的概率要低,并且虚假响应边缘应该得到最大抑制

**函数**:cv2.Canny(image,th1,th2,Size)

**参数说明**:

-   image:原图像
-   th1:阈值1
-   th2:阈值2
-   Size:可选参数,Sobel算子的大小

**步骤**:

-   彩色图像转换为灰度图像(以灰度图单通道图读入)
-   对图像进行高斯模糊(去噪)
-   计算图像梯度,根据梯度计算图像边缘幅值与角度
-   沿梯度方向进行非极大值抑制(边缘细化)
-   双阈值边缘连接处理
-   二值图像输出结果

![](https://img-blog.csdnimg.cn/35fff76bf24b42e196aaf88d0f5056a1.png)

#=canny======#

flag = 0

flag = 1
if flag == 1:
#以灰度图形式读入图像
img = cv2.imread(‘image/canny.png’)
v1 = cv2.Canny(img, 80, 150,(3,3))
v2 = cv2.Canny(img, 50, 100,(5,5))

#np.vstack():在竖直方向上堆叠
#np.hstack():在水平方向上平铺堆叠
ret = np.hstack((v1, v2))
cv2.imshow('img', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()

![](https://img-blog.csdnimg.cn/12cebc2903ff4269b1211fec664b688a.png)

## 5.连通区域分析

## **5.1 连通区域概要**

连通区域一般是指图像中**具有相同像素值且位置相邻的前景像素点组成的图像区域**,**连通区域分析是指将图像中的各个连通区域找出并标记。**连通区域分析是一种在CV和图像分析处理的众多应用领域中较为常用和基本的方法。

例如:OCR是被中字符分割提取(车牌识别、文本识别、字幕识别等),视觉跟踪中的运动前景目标分割与提取(行人入侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等),医学图像处理(感兴趣目标区域提取)等。

在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析方法,通常连通区域分析处理的对象是一张**二值化后**的图像。

在图像中,最小的单位是像素,每个像素周围有邻接像素,常见的邻接关系有2种:**4邻接和8邻接。**

![](https://img-blog.csdnimg.cn/33ad71b3a1a64ae2a64876cac55a3821.png)![](https://img-blog.csdnimg.cn/fbaecc81dcc64de681ee85961e2823fd.png)

如果A与B连通,B与C连通,则A与C连通,在视觉上看来,彼此连通的点形成了一个区域,而不连通的点形成了不同的区域。这样的一个所有的点彼此连通点构成的集合,我们称为一个连通区域。

4邻接,则有3个连通区域

8邻接,则有2个连通区域

 ![](https://img-blog.csdnimg.cn/9aefd95c85444dc685af0d8b00f7d240.png)

## **5.2 Two-Pass算法**

两遍扫描法(Two-Pass),正如其名,指的就是通过扫描两遍图像,将图像中**存在的所有连通区域找出并标记。**

**第一次扫描**:

-   从左上角开始遍历像素点,找到第一个像素为255的点,label = 1
-   当该像素的左邻像素和上邻像素为无效值时,给该像素置一个新的label值,label++
-   当该像素的左邻像素或者上邻像素有一个有效值时,将有效值像素的label赋给该像素的label值
-   当该像素的左邻像素和上邻像素都为有效值时,选取其中较小的label值赋给该像素的label值

![](https://img-blog.csdnimg.cn/8da7dfdd1cb9402e8110f023d1af6636.png)![](https://img-blog.csdnimg.cn/bb60cc0a72a24f0c8a786faebcfd5342.png)

 **第二次扫描**:

-   对每个点的label进行更新,更新为其对于其集合中最小的label

 ![](https://img-blog.csdnimg.cn/f7c9a1592c8b4e92b1c5b3e568bc682e.png)

import cv2
import numpy as np

4邻域的连通域和 8邻域的连通域

[row, col]

NEIGHBOR_HOODS_4 = True
OFFSETS_4 = [[0, -1], [-1, 0], [0, 0], [1, 0], [0, 1]]

NEIGHBOR_HOODS_8 = False
OFFSETS_8 = [[-1, -1], [0, -1], [1, -1],
[-1, 0], [0, 0], [1, 0],
[-1, 1], [0, 1], [1, 1]]
#第二遍扫描
def reorganize(binary_img: np.array):
index_map = []
points = []
index = -1
rows, cols = binary_img.shape
for row in range(rows):
for col in range(cols):
var = binary_img[row][col]
if var < 0.5:
continue
if var in index_map:
index = index_map.index(var)
num = index + 1
else:
index = len(index_map)
num = index + 1
index_map.append(var)
points.append([])
binary_img[row][col] = num
points[index].append([row, col])
#print(binary_img)
#print(points)
return binary_img, points

#四领域或八领域判断
def neighbor_value(binary_img: np.array, offsets, reverse=False):
rows, cols = binary_img.shape
label_idx = 0
rows_ = [0, rows, 1] if reverse == False else [rows-1, -1, -1]
cols_ = [0, cols, 1] if reverse == False else [cols-1, -1, -1]
for row in range(rows_[0], rows_[1], rows_[2]):
for col in range(cols_[0], cols_[1], cols_[2]):
label = 256
if binary_img[row][col] < 0.5:
continue
for offset in offsets:
neighbor_row = min(max(0, row+offset[0]), rows-1)
neighbor_col = min(max(0, col+offset[1]), cols-1)
neighbor_val = binary_img[neighbor_row, neighbor_col]
if neighbor_val < 0.5:
continue
label = neighbor_val if neighbor_val < label else label
if label == 255:
label_idx += 1
label = label_idx
binary_img[row][col] = label
print(‘第一遍扫描:’,binary_img)
print(‘开始第二遍…’)
return binary_img

binary_img: bg-0, object-255; int

#第一遍扫描
def Two_Pass(binary_img: np.array, neighbor_hoods):
if neighbor_hoods == NEIGHBOR_HOODS_4:
offsets = OFFSETS_4
elif neighbor_hoods == NEIGHBOR_HOODS_8:
offsets = OFFSETS_8
else:
raise ValueError

binary_img = neighbor_value(binary_img, offsets, False)

return binary_img

if name == “main”:
#创建四行七列的矩阵
binary_img = np.zeros((4, 7), dtype=np.int16)
#指定点设置为255
index = [[0, 2], [0, 5],
[1, 0], [1, 1], [1, 2], [1, 4], [1, 5], [1, 6],
[2, 2], [2, 5],
[3, 1], [3, 2], [3, 4],[3,5], [3, 6]]
for i in index:
binary_img[i[0], i[1]] = np.int16(255)

print("原始二值图像")
print(binary_img)

#print("Two_Pass")
#调用Two Pass算法,计算第一遍扫面的结果
binary_img = Two_Pass(binary_img, NEIGHBOR_HOODS_4)
#print(binary_img)
#计算第一遍扫面的结果
binary_img, points = reorganize(binary_img)
print(binary_img)
#print(points)

## 6.区域生长算法

## **6.1 区域生长概要**

区域生长是一种**串行区域分割**的图像分割方法。区域生长是指从某个像素出发,按照一定的准则,逐步加入邻近像素,当满足一定的条件时,区域生长终止。

区域生长的好坏决定于

-   初始点(种子点)的选取
-   生长准则
-   终止条件

区域生长是从某个或者某些像素点出发,最后得到整个区域,进而实现目标的提取。

## **6.2 区域生长原理**

**基本思想**:将具有相似性质的像素集合起来构成区域

**步骤**:

-   (1)对图像顺序扫描,找到第一个还没有归属的像素,设该像素为(x0,y0)。
-   (2)以(x0,y0)为中心,考虑(x0,y0)的4邻域像素(x,y)如果(x0,y0)满足生长准则,将(x,y)与(x0,y0)合并(在同一区域内),同时将(x,y)压入堆栈。
-   (3)从堆栈中取出一个像素,把它当做(x0,y0)返回到步骤2。
-   (4)当堆栈为空时,返回到步骤1。
-   (5)重复步骤1-4直到图像中的每个点都有归属时。
-   (6)生长结束。

-- coding:utf-8 --

import cv2
import numpy as np

####################################################################################

#######################################################################################
class Point(object):
def init(self , x , y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
connects = [ Point(-1, -1), Point(0, -1), Point(1, -1), Point(1, 0),
Point(1, 1), Point(0, 1), Point(-1, 1), Point(-1, 0)]
#####################################################################################
#计算两个点间的欧式距离
def get_dist(seed_location1,seed_location2):
l1 = im[seed_location1.x , seed_location1.y]
l2 = im[seed_location2.x , seed_location2.y]
count = np.sqrt(np.sum(np.square(l1-l2)))
return count
#import Image
im = cv2.imread(‘image/222.jpg’)
cv2.imshow(‘src’ , im)
cv2.waitKey(0)
cv2.destroyAllWindows()
im_shape = im.shape
height = im_shape[0]
width = im_shape[1]
print( ‘the shape of image :’, im_shape)
#标记,判断种子是否已经生长
img_mark = np.zeros([height , width])
cv2.imshow(‘img_mark’ , img_mark)
cv2.waitKey(0)
cv2.destroyAllWindows()

建立空的图像数组,作为一类

img_re = im.copy()
for i in range(height):
for j in range(width):
img_re[i, j][0] = 0
img_re[i, j][1] = 0
img_re[i, j][2] = 0
cv2.imshow(‘img_re’ , img_re)
cv2.waitKey(0)
cv2.destroyAllWindows()
#取一点作为种子点
seed_list = []
seed_list.append(Point(15, 15))
T = 7#阈值
class_k = 1#类别
#生长一个类
while (len(seed_list) > 0):
seed_tmp = seed_list[0]
#将以生长的点从一个类的种子点列表中删除
seed_list.pop(0)
img_mark[seed_tmp.x, seed_tmp.y] = class_k
# 遍历8邻域
for i in range(8):
tmpX = seed_tmp.x + connects[i].x
tmpY = seed_tmp.y + connects[i].y
if (tmpX < 0 or tmpY < 0 or tmpX >= height or tmpY >= width):
continue
dist = get_dist(seed_tmp, Point(tmpX, tmpY))
#在种子集合中满足条件的点进行生长
if (dist < T and img_mark[tmpX, tmpY] == 0):
img_re[tmpX, tmpY][0] = im[tmpX, tmpY][0]
img_re[tmpX, tmpY][1] = im[tmpX, tmpY][1]
img_re[tmpX, tmpY][2] = im[tmpX, tmpY][2]
img_mark[tmpX, tmpY] = class_k
seed_list.append(Point(tmpX, tmpY))
########################################################################################

#输出图像
cv2.imshow(‘OUTIMAGE’ , img_re)
cv2.waitKey(0)
cv2.destroyAllWindows()


## 7.[分水岭算法](https://so.csdn.net/so/search?q=%E5%88%86%E6%B0%B4%E5%B2%AD%E7%AE%97%E6%B3%95&spm=1001.2101.3001.7020)

## **7.1 分水岭算法概要**

任意的灰度图像可以被看做是地质学表面,高亮度的地方是山峰,低亮度的地方是山谷。

![](https://img-blog.csdnimg.cn/2c312bf187594675a9afaf836825b958.png)

给每个孤立的山谷(局部最小值)不同颜色的水(标签),当水涨起来,根据周围的山峰(梯度),不同的山谷也就是不同的颜色会开始合并,要避免山谷合并,需要在水要合并的地方建立分水岭,直到所有山峰都被淹没,所创建的分水岭就是分割边界线,这个就是分水岭的原理。 

## **7.2 分水岭算法**

**步骤**:

-   加载原始图像
-   阈值分割,将图像分割为黑白两个部分
-   对图像进行开运算,即先腐蚀再膨胀
-   对开运算的结果再进行膨胀,得到大部分是背景的区域
-   通过距离变换Distance Transform获得前景区域
-   背景区域sure\_bg和前景区域sure\_fg相减,得到即有前景又有背景的重合区域
-   连通区域处理
-   最后使用分水岭算法

import cv2

“”"
完成分水岭算法步骤:
1、加载原始图像
2、阈值分割,将图像分割为黑白两个部分
3、对图像进行开运算,即先腐蚀在膨胀
4、对开运算的结果再进行 膨胀,得到大部分是背景的区域
5、通过距离变换 Distance Transform 获取前景区域
6、背景区域sure_bg 和前景区域sure_fg相减,得到即有前景又有背景的重合区域
7、连通区域处理
8、最后使用分水岭算法
“”"

import cv2
import numpy as np

Step1. 加载图像

img = cv2.imread(‘image/yezi.jpg’)
cv2.imshow(“img”, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

Step2.阈值分割,将图像分为黑白两部分

ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

cv2.imshow(“thresh”, thresh)

Step3. 对图像进行“开运算”,先腐蚀再膨胀

kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

cv2.imshow(“opening”, opening)

Step4. 对“开运算”的结果进行膨胀,得到大部分都是背景的区域

sure_bg = cv2.dilate(opening, kernel, iterations=3)
cv2.imshow(“sure_bg”, sure_bg)
cv2.waitKey(0)
cv2.destroyAllWindows()

Step5.通过distanceTransform获取前景区域

dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) # DIST_L1 DIST_C只能 对应掩膜为3 DIST_L2 可以为3或者5
cv2.imshow(“dist_transform”, dist_transform)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(dist_transform.max())
ret, sure_fg = cv2.threshold(dist_transform, 0.1 * dist_transform.max(), 255, 0)

Step6. sure_bg与sure_fg相减,得到既有前景又有背景的重合区域 #此区域和轮廓区域的关系未知

sure_fg = np.uint8(sure_fg)
unknow = cv2.subtract(sure_bg, sure_fg)
cv2.imshow(“unknow”, unknow)
cv2.waitKey(0)
cv2.destroyAllWindows()

Step7. 连通区域处理

ret, markers = cv2.connectedComponents(sure_fg,connectivity=8) #对连通区域进行标号 序号为 0 - N-1
#print(markers)
print(ret)
markers = markers + 1 #OpenCV 分水岭算法对物体做的标注必须都 大于1 ,背景为标号 为0 因此对所有markers 加1 变成了 1 - N
#去掉属于背景区域的部分(即让其变为0,成为背景)

此语句的Python语法 类似于if ,“unknow==255” 返回的是图像矩阵的真值表。

markers[unknow==255] = 0

Step8.分水岭算法

markers = cv2.watershed(img, markers) #分水岭算法后,所有轮廓的像素点被标注为 -1
#print(markers)

img[markers == -1] = [0, 0, 255] # 标注为-1 的像素点标 红
cv2.imshow(“dst”, img)
cv2.waitKey(0)
cv2.destroyAllWindows()



# OpenCV(四)——图像特征与目标检测

> ## Excerpt
> 课程一览:目录1.图像特征的理解2.形状特征描述2.1 HOG原理2.2 Harris2.3 SIFT3.纹理特征4.模板匹配5.人脸检测1.图像特征的理解图像特征是图像中独特的,易于跟踪和比较的特定模板或特定结构。图像特征提取与匹配是计算机视觉中的一个关键问题,在目标检测、物体识别、三维重建、图像配准、图像理解等具体应用中发挥着重要作用。图像特征主要有图像的颜色特征、纹理特征、形状特征和空间关系特征。(1)颜色特征颜色特征.

---
课程一览:

![](https://img-blog.csdnimg.cn/e0526a9259a84b60aefbd320667c2d7a.png)

**目录**

[1.图像特征的理解](https://blog.csdn.net/m0_45447650/article/details/124408553#1.%E5%9B%BE%E5%83%8F%E7%89%B9%E5%BE%81%E7%9A%84%E7%90%86%E8%A7%A3)

[2.形状特征描述](https://blog.csdn.net/m0_45447650/article/details/124408553#2.%E5%BD%A2%E7%8A%B6%E7%89%B9%E5%BE%81%E6%8F%8F%E8%BF%B0)

[2.1 HOG原理](https://blog.csdn.net/m0_45447650/article/details/124408553#2.1%20HOG%E5%8E%9F%E7%90%86)

[2.2 Harris](https://blog.csdn.net/m0_45447650/article/details/124408553#2.2%20Harris)

[2.3 SIFT](https://blog.csdn.net/m0_45447650/article/details/124408553#2.3%20SIFT)

[3.纹理特征](https://blog.csdn.net/m0_45447650/article/details/124408553#3.%E7%BA%B9%E7%90%86%E7%89%B9%E5%BE%81)

[4.模板匹配](https://blog.csdn.net/m0_45447650/article/details/124408553#4.%E6%A8%A1%E6%9D%BF%E5%8C%B9%E9%85%8D)

[5.人脸检测](https://blog.csdn.net/m0_45447650/article/details/124408553#5.%E4%BA%BA%E8%84%B8%E6%A3%80%E6%B5%8B)

___

## 1.图像特征的理解

图像特征是图像中独特的,易于跟踪和比较的特定模板或特定结构。

-   **图像特征提取与匹配**是计算机视觉中的一个关键问题,在目标检测、物体识别、三维重建、图像配准、图像理解等具体应用中发挥着重要作用。
-   图像特征主要有图像的**颜色特征**、**纹理特征**、**形状特征**和**空间关系特征**。

![](https://img-blog.csdnimg.cn/d751cff82cf9421c872d15952993f890.png)

**(1)颜色特征**

-   颜色特征是一种全局特征,描述了图像或图像区域所对应的景物的表面性质
-   颜色特征描述方法
    -   颜色直方图
    -   颜色空间
    -   颜色分布

**(2)纹理特征**

-   纹理特征也是一种全局特征,它也描述了图像或图像区域所对应景物的表面性质。但由于纹理只是一种物体表面的特性,并不能完全反映出物体的本质属性,所以仅仅利用纹理特征是无法获得高层次图像内容的。

![](https://img-blog.csdnimg.cn/b7852e8cd38c40aaa60289f45deb162b.png)

 **(3)形状特征**

-   形状特征有两类表示方法,一类是**轮廓特征**,另一类是**区域特征**。图像的轮廓特征主要针对物体的外边界,而图像的区域特征则描述了是图像中的局部形状特征。

![](https://img-blog.csdnimg.cn/b80a39735e3948c690c8a87cb66e69b2.png)

 **(4)空间关系特征**

-   空间关系特征,是指图像中分割出来的**多个目标之间的相互的空间位置或相对方向关系**
-   这些关系也可分为连接/邻接关系,交叠/重叠关系和包含/独立关系等。

## 2.形状特征描述

## 2.1 HOG原理

**HOG特征提取**

-   方向梯度直方图(HOG)特征是一种在计算机视觉和图像处理中用来进行**物体检测**的特征描述子。(主要用来提取**形状特征**)
-   它通过计算和统计图像**局部区域**的**梯度直方图**来构成特征。
-   Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。(用HOG提取特征,用SVM进行分类)
-   主要思想:在一幅图像中,目标的形状能够被梯度或边缘的方向密度分布很好的描述。

**HOG实现过程**

-   **灰度化**(将图像看做一个x,y,z(灰度)的三维图像)
-   采用Gamma校正法对输入图像进行颜色空间的**标准化**(归一化)
-   计算图像每个像素的梯度(包括大小和方向)
-   将图像划分成小cells
-   统计每个cell的梯度直方图(不同梯度的个数),得到cell的描述子
-   将每几个cell组成一个block,得到block的描述子
-   将图像image内的所有block的HOG特征descriptor串联起来就可以得到HOG特征,该特征向量就是用来目标检测或分类的特征

#===HOG 特征=#

flag = 0

flag = 1
if flag == 1:
# 判断矩形i是否完全包含在矩形o中
def is_inside(o, i):
ox, oy, ow, oh = o
ix, iy, iw, ih = i
return ox > ix and oy > iy and ox + ow < ix + iw and oy + oh < iy + ih
# 对人体绘制颜色框
def draw_person(image, person):
x, y, w, h = person
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 255), 2)
img = cv2.imread(“people.jpg”)
hog = cv2.HOGDescriptor() # 启动检测器对象
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector()) # 指定检测器类型为人体
found, w = hog.detectMultiScale(img,0.1,(2,2)) # 加载并检测图像
print(found)
print(w)

# 丢弃某些完全被其它矩形包含在内的矩形
found_filtered = []
for ri, r in enumerate(found):
    for qi, q in enumerate(found):
        if ri != qi and is_inside(r, q):
            break
    else:
        found_filtered.append(r)
        print(found_filtered)
# 对不包含在内的有效矩形进行颜色框定
for person in found_filtered:
    draw_person(img, person)
cv2.imshow("people detection", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

![](https://img-blog.csdnimg.cn/b68e3114e1ff4c039152e6aacf0a030a.png)

## 2.2 Harris

**(1)Harris角点检测**

-   角点:在现实世界中,角点对应于物体的拐角,道路的十字路口、丁字路口等。从图像分析的角度来定义角点可以有以下两种定义:
    -   角点可以是两个边缘的交点
    -   角点是邻域内具有两个主方向的特征点
-   角点计算方法:
    -   前者通过图像边缘计算,计算量大,图像局部变化会对结果产生较大的影响
    -   后者基于图像灰度的方法通过计算点的曲率及梯度来检测角点

![](https://img-blog.csdnimg.cn/ebcea5eff7c34a2f891e40aa8c5da383.png)

-   角点所具有的特征:
    -   轮廓之间的交点
    -   对于同一场景,即使视角发生变化,通常具备稳定性质的特征
    -   该点附近区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化
-   性能较好的角点:
    -   检测出图像中“真实”的角点
    -   准确的定位性能
    -   很高的重复检测率
    -   噪声的鲁棒性
    -   较高的计算效率

**(2)Harris实现过程**

**(3)Harris代码应用**

Open中的函数cv2.cornerHarris()可以用来进行角点检测。

参数如下:

-   img:数据类型为float32的输入图像
-   blockSize:角点检测中要考虑的领域大小
-   ksize:Sobel求导中使用的窗口大小
-   k:Harris角点检测方程中的自由参数,取值参数为\[0.04,0.06\]

![](https://img-blog.csdnimg.cn/16be49662fa74911b21ef91e3ede0260.png)

#===Harris 角点=#

flag = 0

flag = 1
if flag == 1:
img = cv2.imread(‘harris2.png’)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst_block9_ksize19 = cv2.cornerHarris(gray, 9, 19, 0.04)
img1 = np.copy(img)
img1[dst_block9_ksize19 > 0.01 * dst_block9_ksize19.max()] = [0, 0, 255]

dst_block5_ksize19 = cv2.cornerHarris(gray, 5, 19, 0.04)
img2 = np.copy(img)
img2[dst_block5_ksize19 > 0.01 * dst_block5_ksize19.max()] = [0, 0, 255]

dst_block9_ksize5 = cv2.cornerHarris(gray, 9, 5, 0.04)
img3 = np.copy(img)
img3[dst_block9_ksize5 > 0.01 * dst_block9_ksize5.max()] = [0, 0, 255]

dst_block9_ksize31 = cv2.cornerHarris(gray, 9, 31, 0.04)
img4 = np.copy(img)
img4[dst_block9_ksize31 > 0.01 * dst_block9_ksize31.max()] = [0, 0, 255]

dst_block9_ksize19_k6 = cv2.cornerHarris(gray, 9, 19, 0.06)
img5 = np.copy(img)
img5[dst_block9_ksize19_k6 > 0.01 * dst_block9_ksize19_k6.max()] = [0, 0, 255]

dst_block9_ksize19_k6_1e_5 = cv2.cornerHarris(gray, 9, 19, 0.06)
img6 = np.copy(img)
img6[dst_block9_ksize19_k6_1e_5 > 0.00001 * dst_block9_ksize19_k6_1e_5.max()] = [0, 0, 255]

titles = ["Original", "block9_ksize19", "dst_block5_ksize19", "dst_block9_ksize5", "dst_block9_ksize31",
          "dst_block9_ksize19_k6", "dst_block9_ksize19_k6_1e_5"]
imgs = [img, img1, img2, img3, img4, img5, img6]
for i in range(len(titles)):
    plt.subplot(3, 3, i + 1), plt.imshow(imgs[i]), plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()
# cv2.imshow('src',img)
# cv2.imshow('dst',img5)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

![](https://img-blog.csdnimg.cn/1e1656de0c904aba91a2c855fead5725.png)

## 2.3 SIFT

**(1)SIFT算法**

-   SIFT,即**尺度不变特征变换算法**,是用于图像处理领域的一种算法。SIFT具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子。
-   其应用范围包含物体辨识、机器人地图感知与导航、影响缝合、3D模型建立、手势辨识、影响追踪和动作比对。

**(2)SIFT特性**

-   独特性:也就是特征点可分变性高,类似指纹,适合在海量数据汇总匹配
-   多量性:提供的特征多
-   高速性:就是速度快
-   可扩展:能与其他特征向量联合使用

**(3)SIFT特点**

-   旋转、缩放、平移不变性
-   解决图像仿射变换,投影变换的关键的匹配
-   光照影响小
-   目标遮挡影响较小
-   噪声景物影响小

**(4)SIFT算法步骤**

-   尺度空间极值检测点检测
-   关键点定位:去除一些不好的特征点,保存下来的特征点能够满足稳定性等条件
-   关键点方向参数:获取关键点所在尺度空间的邻域,然后计算该区域的梯度和方向,根据计算得到的结果创建方向直方图,直方图的峰值为主方向的参数
-   关键点描述符:每个关键点用一组向量(位置、尺度、方向)将这个关键点描述出来,使其不随着光照、视角等等影响而改变
-   关键点匹配:分别对模板图和实时图建立关键点描述符集合,通过对比关键点描述符来判断两个关键点是否相同

#===SIFT=#

flag = 0

flag = 1
if flag == 1:
img = cv2.imread(‘harris2.png’)
gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)#找到关键点
img=cv2.drawKeypoints(gray,kp,img)#绘制关键点
cv2.imshow(‘sp’,img)
cv2.waitKey(0)
cv2.destroyAllWindows()


## 3.纹理特征

**(1)LBP介绍**

-   LBP(局部二值模式),是一种用来描述图像**局部纹理特征**的算子;它具有**旋转不变性和灰度不变性**等显著的优点

**(2)LBP原理**

-   LBP算子定义在一个3×3的窗口内,以窗口中心像素为阈值,与相邻的8个像素的灰度值比较,若周围的像素值大于中心像素值,则该位置被标记为1;否则标记为0。如此可以得到一个8位二进制数(通常还要转换为10进制,即LBP码,共256种),将这个值作为窗口中心像素点的LBP值,以此来反应这个3×3区域的纹理信息.

![](https://img-blog.csdnimg.cn/3f38ed7997cf4ba089766bd2ea6c8464.png)

#===LBP=#

flag = 0

flag = 1
if flag == 1:
def LBP(src):
‘’’
:param src:灰度图像
:return:
‘’’
height = src.shape[0]
width = src.shape[1]
dst = src.copy()
lbp_value = np.zeros((1, 8), dtype=np.uint8)
# print(lbp_value)
neighbours = np.zeros((1, 8), dtype=np.uint8)
# print(neighbours)
for x in range(1, width - 1):
for y in range(1, height - 1):
neighbours[0, 0] = src[y - 1, x - 1]
neighbours[0, 1] = src[y - 1, x]
neighbours[0, 2] = src[y - 1, x + 1]
neighbours[0, 3] = src[y, x - 1]
neighbours[0, 4] = src[y, x + 1]
neighbours[0, 5] = src[y + 1, x - 1]
neighbours[0, 6] = src[y + 1, x]
neighbours[0, 7] = src[y + 1, x + 1]
center = src[y, x]
for i in range(8):
if neighbours[0, i] > center:
lbp_value[0, i] = 1
else:
lbp_value[0, i] = 0

            lbp = lbp_value[0, 0] * 1 + lbp_value[0, 1] * 2 + lbp_value[0, 2] * 4 + lbp_value[0, 3] * 8 \
                  + lbp_value[0, 4] * 16 + lbp_value[0, 5] * 32 + lbp_value[0, 6] * 64 + lbp_value[0, 7] * 128

            # print(lbp)
            dst[y, x] = lbp

    return dst

img = cv2.imread('people.jpg', 0)
print(img.shape)
cv2.imshow('src', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
new_img = LBP(img)

cv2.imshow('dst', new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

![](https://img-blog.csdnimg.cn/f6280d688c0947e4aea05f3739bfbd0e.png)

 ![](https://img-blog.csdnimg.cn/eab9c603b93447b8b3799f7b0f1b32ad.png)

## 4.模板匹配

**(1)模板匹配介绍**

-   模板匹配是一种最原始、最基本的模式识别方法,研究某一特定目标的图像位于图像的什么地方,进而对图像进行定位。
-   在待检测图像上,从左到右,从上向下计算模板图像与重叠子图像的匹配度,匹配程度越大,两者相同的可能性越大。

![](https://img-blog.csdnimg.cn/00a177a7807b492ebbbaa9c23432e2db.png)

 **(2)函数**:result = cv2.matchTemplate(image,templ,method)

-   image参数表示待搜索图像
-   templ参数表示模板图像,必须不大于原图像并具有相同的数据类型
-   method参数表示计算匹配程度的方法

**(3)匹配方法**

-   TM\_SQDIFF\_NORMED是标准平方差匹配,通过计算两图之间平方差来进行匹配,最好匹配为0,匹配越差,匹配值越大。
-   TM\_CCORR\_NORMED是标准相关性匹配,采用模板和图像间的乘法操作,数越大表示匹配程度较高,0表示最坏的匹配效果,这种方法除了亮度线性变化对相似度计算的影响。
-   TM\_CCOEFF\_NORMED是标准相关性系数匹配,两图减去了各自的平均值之外,还要各自除以各自的方差。将模板对其均值的相对值对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性(随机序列)。

**函数**:minVal,maxVal,minLoc,maxLoc = cv2.minMaxLoc()

-   minVal参数表示返回的最小值
-   maxVal参数表示返回的最大值
-   minLoc参数表示返回的最小位置
-   maxLoc参数表示返回的最大位置

===模板检测=#

flag = 0

flag = 1
if flag == 1:

def template_demo(tpl, target):

    methods = [cv2.TM_SQDIFF_NORMED, cv2.TM_CCORR_NORMED, cv2.TM_CCOEFF_NORMED]  # 3种模板匹配方法
    th, tw = tpl.shape[:2]
    for md in methods:
        # print(md)
        result = cv2.matchTemplate(target, tpl, md)
        # print(result.shape)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        print(min_val, max_val, min_loc, max_loc)
        if md == cv2.TM_SQDIFF_NORMED:
            tl = min_loc
        else:
            tl = max_loc
        br = (tl[0] + tw, tl[1] + th)  # br是矩形右下角的点的坐标
        cv2.rectangle(target, tl, br, (0, 0, 255), 2)
        cv2.namedWindow("match-" + np.str(md), cv2.WINDOW_NORMAL)
        cv2.imshow("match-" + np.str(md), target)


tpl = cv2.imread("sample2.jpg")
print(tpl.shape)
target = cv2.imread("target1.jpg")
print(target.shape)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.namedWindow('template image', cv2.WINDOW_NORMAL)
cv2.imshow("template image", tpl)
cv2.namedWindow('target image', cv2.WINDOW_NORMAL)
cv2.imshow("target image", target)
template_demo(tpl, target)
cv2.waitKey(0)
cv2.destroyAllWindows()

## 5.人脸检测

-   一般而言,一个完整的人脸识别系统包含四个主要组成部分,即人脸检测、人脸对齐、人类特征提取以及人脸识别。
-   四部分流水线操作:
    -   人脸检测在图像中找到人脸的位置
    -   人脸配准在人脸上找到眼镜、鼻子、嘴巴等面部器官的位置
    -   通过人脸特征提取将人脸图像信息抽象为字符串信息
    -   人脸识别将目标人脸图像与既有人脸比对计算相似度,确认人脸对应的身份

![](https://img-blog.csdnimg.cn/7d72ae9e7b9b4b09a9396b9e40ff0e34.png)

 ![](https://img-blog.csdnimg.cn/635b922364bd4272ad53fbbb9c845db3.png)

===人脸检测=#

flag = 0

flag = 1
if flag == 1:
# 读入图像
img = cv2.imread(“3.png”)

# 加载人脸特征,该文件在 python安装目录\Lib\site-packages\cv2\data 下
face_cascade = cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml')
# 将读取的图像转为COLOR_BGR2GRAY,减少计算强度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 检测出的人脸个数
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.15, minNeighbors=4, minSize=(5, 5))

print("Face : {0}".format(len(faces)))
print(faces)
# 用矩形圈出人脸的位置
for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.namedWindow("Faces")
cv2.imshow("Faces", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

![](https://img-blog.csdnimg.cn/51576a24a2864384b3788696b46d7e2f.png)

====人脸检测=#

flag = 0

flag = 1

if flag == 1:
predictor_model = ‘shape_predictor_68_face_landmarks/shape_predictor_68_face_landmarks.dat’
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(predictor_model)

# cv2读取图像
test_film_path = "3.png"
img = cv2.imread(test_film_path)
# 取灰度
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

# 人脸数rects
rects = detector(img_gray, 0)
print(rects[0])
for i in range(len(rects)):
    landmarks = np.matrix([[p.x, p.y] for p in predictor(img, rects[i]).parts()])
    print(landmarks, type(landmarks))
    for idx, point in enumerate(landmarks):
        # 68点的坐标
        pos = (point[0, 0], point[0, 1])
        # print(idx+1, pos)

        # 利用cv2.circle给每个特征点画一个圈,共68个
        cv2.circle(img, pos, 3, color=(0, 255, 0))
        # 利用cv2.putText输出1-68
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(img, str(idx + 1), pos, font, 0.5, (0, 0, 25 5), 1, cv2.LINE_AA)

# cv2.imwrite("result.png", img)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()


# OpenCV(五)——运动目标识别

> ## Excerpt
> 课程一览:目录1.摄像头调用2.视频的读取与保存3.帧差法4.光流法5.背景减除法1.摄像头调用开启摄像头函数1 :cv2VideoCapture()参数说明:0,1代表电脑摄像头,或视频文件路径函数2:ret,frame = cap.read()说明:cap.read()Ret:返回布尔值Ture/False,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为FalseFrame:每一帧的图像,是个三维矩阵

---
课程一览:

![](https://img-blog.csdnimg.cn/e0526a9259a84b60aefbd320667c2d7a.png)

**目录**

[1.摄像头调用](https://blog.csdn.net/m0_45447650/article/details/124412679#1.%E6%91%84%E5%83%8F%E5%A4%B4%E8%B0%83%E7%94%A8)

[2.视频的读取与保存](https://blog.csdn.net/m0_45447650/article/details/124412679#2.%E8%A7%86%E9%A2%91%E7%9A%84%E8%AF%BB%E5%8F%96%E4%B8%8E%E4%BF%9D%E5%AD%98)

[3.帧差法](https://blog.csdn.net/m0_45447650/article/details/124412679#3.%E5%B8%A7%E5%B7%AE%E6%B3%95)

[4.光流法](https://blog.csdn.net/m0_45447650/article/details/124412679#4.%E5%85%89%E6%B5%81%E6%B3%95)

[5.背景减除法](https://blog.csdn.net/m0_45447650/article/details/124412679#5.%E8%83%8C%E6%99%AF%E5%87%8F%E9%99%A4%E6%B3%95)

___

## 1.摄像头调用

**开启摄像头**

-   函数1 :cv2VideoCapture()
    -   参数说明:0,1代表电脑摄像头,或视频文件路径
-   函数2:ret,frame = cap.read()
-   说明:cap.read()
    -   Ret:返回布尔值Ture/False,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False
    -   Frame:每一帧的图像,是个三维矩阵
-   下面的程序将使用opencv调用摄像头,并实时播放摄像头中画面,按下“q”键结束播放

![](https://img-blog.csdnimg.cn/d9947bdaa95b45c8a58a29ca94ae0736.png)

#=调用摄像头====#

flag = 0

flag = 1
if flag == 1:
cap = cv2.VideoCapture(0)
while (True):
# 获取一帧帧图像
ret, frame = cap.read()
# 转化为灰度图
# gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow(‘frame’, frame)
# 按下“q”键停止
if cv2.waitKey(1) & 0xFF == ord(‘q’):
break
cap.release()
cv2.destroyAllWindows()


## 2.视频的读取与保存

-   指定写入视频帧编码格式
-   函数fourcc = cv2.VideoWriter\_fourcc("M","J","P","G")

![](https://img-blog.csdnimg.cn/d69a7b64506c4ed8b1b3dec42c256109.png)

-    创建VideoWriter对象
    -   函数 out = cv2.VideoWriter(“output.avi”,fourcc,20.0,(640,480))
-   参数说明:
    -   参数1:保存视频路径+名字
    -   参数2:Fourcc为4字节码,确定视频的编码格式
    -   参数3:播放帧率
    -   参数4:大小
    -   参数5:默认为True,彩色图

![](https://img-blog.csdnimg.cn/7f578cee94c44a999be4696bd14f8c51.png)

#=保存视频====#

flag = 0

flag = 1
if flag == 1:
# 调用摄像头函数cv2.VideoCapture,参数0:系统摄像头
cap = cv2.VideoCapture(0)
# 创建编码方式
# mp4:‘X’,‘V’,‘I’,'D’avi:‘M’,‘J’,‘P’,‘G’或’P’,‘I’,‘M’,‘1’ flv:‘F’,‘L’,‘V’,‘1’
fourcc = cv2.VideoWriter_fourcc(‘F’, ‘L’, ‘V’, ‘1’)

# 创建VideoWriter对象
out = cv2.VideoWriter('output_1.flv', fourcc, 20.0, (640, 480))
# 创建循环结构进行连续读写
while (cap.isOpened()):
    ret, frame = cap.read()
    if ret == True:
        out.write(frame)
        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break
cap.release()
out.release()
cv2.destroyAllWindows()

#=修改视频格式====#

flag = 0

flag = 1
if flag == 1:
cap = cv2.VideoCapture(‘output_1.flv’)
fourcc = cv2.VideoWriter_fourcc(‘M’, ‘J’, ‘P’, ‘G’)
fps = cap.get(cv2.CAP_PROP_FPS)
print(fps)

# 视频图像的宽度
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# 视频图像的长度
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(frame_width)
print(frame_height)
# 创建VideoWriter对象
out = cv2.VideoWriter('output_1_new.mp4', fourcc, fps, (frame_width, frame_height))
while (True):
    ret, frame = cap.read()
    if ret == True:
        # 水平翻转
        frame = cv2.flip(frame, 1)
        out.write(frame)
        cv2.imshow('frame', frame)
        if cv2.waitKey(25) & 0xff == ord('q'):
            break
    else:
        break
out.release()
cap.release()
cv2.destroyAllWindows()

## 3.帧差法

-   帧间差分法是通过对视频中相邻两帧图像做差分运算来标记运动物体的方法
-   当视频中存在移动物体的时候,相邻帧(或相邻三帧)之间在灰度上会有差别,求取两帧图像灰度差的绝对值,则静止的物体在插值图像上表现出来全是0,而移动物体特别是移动物体的轮廓处由于存在灰度变化为非0。
-   优点:
    -   算法实现简单,程序设计复杂度低
    -   对光线等场景变化不太敏感,能够适应各种动态环境,稳定性较好
-   缺点:
    -   不能提取出对象的完整区域,对象内部有“空洞”
    -   只能提取出边界,边界轮廓比较粗,往往比实际物体要大
    -   对快速运动的物体,容易出现糊影的现象,甚至会被检测为两个不同的运动物体
    -   对慢速运动的物体,当物体在前后两帧中几乎完全重叠时,则检测不到物体

#=帧差法====#

flag = 0

flag = 1
if flag == 1:
# 视频文件输入初始化
filename = “move_detect.flv”
camera = cv2.VideoCapture(filename)

# 视频文件输出参数设置
out_fps = 12.0  # 输出文件的帧率
fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', '2')
out1 = cv2.VideoWriter('E:/video/v1.avi', fourcc, out_fps, (500, 400))
out2 = cv2.VideoWriter('E:/video/v2.avi', fourcc, out_fps, (500, 400))

# 初始化当前帧的前帧
lastFrame = None

# 遍历视频的每一帧
while camera.isOpened():

    # 读取下一帧
    (ret, frame) = camera.read()

    # 如果不能抓取到一帧,说明我们到了视频的结尾
    if not ret:
        break

        # 调整该帧的大小
    frame = cv2.resize(frame, (500, 400), interpolation=cv2.INTER_CUBIC)

    # 如果第一帧是None,对其进行初始化
    if lastFrame is None:
        lastFrame = frame
        continue

        # 计算当前帧和前帧的不同
    frameDelta = cv2.absdiff(lastFrame, frame)

    # 当前帧设置为下一帧的前帧
    lastFrame = frame.copy()

    # 结果转为灰度图
    thresh = cv2.cvtColor(frameDelta, cv2.COLOR_BGR2GRAY)

    # 图像二值化
    thresh = cv2.threshold(thresh, 25, 255, cv2.THRESH_BINARY)[1]

    ''' 
    #去除图像噪声,先腐蚀再膨胀(形态学开运算) 
    thresh=cv2.erode(thresh,None,iterations=1) 
    thresh = cv2.dilate(thresh, None, iterations=2) 
    '''

    # 阀值图像上的轮廓位置
    binary, cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 遍历轮廓
    for c in cnts:
        # 忽略小轮廓,排除误差
        if cv2.contourArea(c) < 300:
            continue

            # 计算轮廓的边界框,在当前帧中画出该框
        (x, y, w, h) = cv2.boundingRect(c)
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

        # 显示当前帧
    cv2.imshow("frame", frame)
    cv2.imshow("frameDelta", frameDelta)
    cv2.imshow("thresh", thresh)

    # 保存视频
    out1.write(frame)
    out2.write(frameDelta)

    # 如果q键被按下,跳出循环
    if cv2.waitKey(20) & 0xFF == ord('q'):
        break
        # 清理资源并关闭打开的窗口
out1.release()
out2.release()
camera.release()
cv2.destroyAllWindows()

## 4.[光流](https://so.csdn.net/so/search?q=%E5%85%89%E6%B5%81&spm=1001.2101.3001.7020)法

-   光流法利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性,根据上一帧与当前帧之间的对应关系,计算得到相邻帧之间物体的运动信息
-   大多数的光流计算方法计算量巨大,结构复杂,且易受光照、物体遮挡或图像噪声的影响,鲁棒性差,故一般不对精度和实时性要求比较高的监控系统所采用。
-   光流是基于以下假设的:
    -   在连续的两帧图像之间(目标对象的)像素的灰度值不改变
    -   相邻的像素具有相同的运动

#=光流法====#

flag = 0

flag = 1
if flag == 1:
import cv2 as cv
es = cv.getStructuringElement(cv.MORPH_ELLIPSE, (10, 10))
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
cap = cv.VideoCapture(“move_detect.flv”)
frame1 = cap.read()[1]
prvs = cv.cvtColor(frame1, cv.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[…, 1] = 255

# 视频文件输出参数设置
out_fps = 12.0  # 输出文件的帧率
fourcc = cv.VideoWriter_fourcc('M', 'P', '4', '2')
sizes = (int(cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT)))
out1 = cv.VideoWriter('E:/video/v6.avi', fourcc, out_fps, sizes)
out2 = cv.VideoWriter('E:/video/v8.avi', fourcc, out_fps, sizes)

while True:
    (ret, frame2) = cap.read()
    next = cv.cvtColor(frame2, cv.COLOR_BGR2GRAY)
    flow = cv.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    mag, ang = cv.cartToPolar(flow[..., 0], flow[..., 1])
    hsv[..., 0] = ang * 180 / np.pi / 2
    hsv[..., 2] = cv.normalize(mag, None, 0, 255, cv.NORM_MINMAX)

    bgr = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)

    draw = cv.cvtColor(bgr, cv.COLOR_BGR2GRAY)
    draw = cv.morphologyEx(draw, cv.MORPH_OPEN, kernel)
    draw = cv.threshold(draw, 25, 255, cv.THRESH_BINARY)[1]

    image, contours, hierarchy = cv.findContours(draw.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

    for c in contours:
        if cv.contourArea(c) < 500:
            continue
        (x, y, w, h) = cv.boundingRect(c)
        cv.rectangle(frame2, (x, y), (x + w, y + h), (255, 255, 0), 2)

    cv.imshow('frame2', bgr)

    cv.imshow('draw', draw)
    cv.imshow('frame1', frame2)
    out1.write(bgr)
    out2.write(frame2)

    k = cv.waitKey(20) & 0xff
    if k == 27 or k == ord('q'):
        break
    elif k == ord('s'):
        cv.imwrite('opticalfb.png', frame2)
        cv.imwrite('opticalhsv.png', bgr)
    prvs = next

out1.release()
out2.release()
cap.release()
cv.destroyAllWindows()

## 5.背景减除法

-   背景消除
    -   OpenCV中常用的两种背景消除方法,一种是基于高斯混合模型GMM实现的背景提取,另一种是基于最近邻KNN实现的。
-   GMM模型
    -   MOG2算法,高斯混合模型分离算法,它为每个像素选择适当数量的高斯分布,它可以更好的适应不同场景的照明变化等。
    -   函数:cv2.createBackgroundSubtractorMOG2(int history = 500,double varThreshold = 16,booldetectShadows = true)
-   KNN模型
    -   cv2.createBackgroundSubtractorKNN()
-   方法
    -   主要通过视频中的背景进行建模,实现背景消除,生成mask图像,通过对mask二值图像分析实现对前景活动对象的区域的提取,整个步骤如下:
        -   初始化背景建模对象GMM
        -   读取视频一帧
        -   使用背景建模消除生成mask
        -   对mask进行轮廓分析提取ROI
        -   绘制ROI对象

#=光流法====#

flag = 0

flag = 1
if flag == 1:
import numpy as np
import cv2

# read the video
cap = cv2.VideoCapture('move_detect.flv')

# create the subtractor
fgbg = cv2.createBackgroundSubtractorMOG2(
    history=500, varThreshold=100, detectShadows=False)


def getPerson(image, opt=1):

    # get the front mask
    mask = fgbg.apply(frame)

    # eliminate the noise
    line = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 5), (-1, -1))
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, line)
    cv2.imshow("mask", mask)

    # find the max area contours
    out, contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in range(len(contours)):
        area = cv2.contourArea(contours[c])
        if area < 150:
            continue
        rect = cv2.minAreaRect(contours[c])
        cv2.ellipse(image, rect, (0, 255, 0), 2, 8)
        cv2.circle(image, (np.int32(rect[0][0]), np.int32(rect[0][1])), 2, (255, 0, 0), 2, 8, 0)
    return image, mask


while True:
    ret, frame = cap.read()
    cv2.imwrite("input.png", frame)
    cv2.imshow('input', frame)
    result, m_ = getPerson(frame)
    cv2.imshow('result', result)
    k = cv2.waitKey(20) & 0xff
    if k == 27:
        cv2.imwrite("result.png", result)
        cv2.imwrite("mask.png", m_)

        break
cap.release()
cv2.destroyAllWindows()

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