OpenCV从入门到实战

OpenCV-Python实战

  • 图像基本操作
    • 数据读取-图像
    • 数据读取-视频
    • ROI区域
    • 边界填充
    • 数值计算
    • 图像融合
    • 图像阈值
    • 图像平滑
      • 均值滤波
      • 方框滤波
      • 高斯滤波
      • 中值滤波
  • 图像形态学操作
    • 腐蚀
    • 膨胀操作
    • 开运算与闭运算
    • 梯度运算
    • 礼帽与黑帽
      • 礼帽
      • 黑帽
  • 图像梯度
    • 图像梯度-Sobel算子
    • 图像梯度-Scharr算子
    • 图像梯度-laplacian算子
  • Canny边缘检测
    • 高斯滤波器
    • 梯度和方向
    • 非极大值抑制
    • 双阈值监测
  • 图像金字塔与轮廓检测
    • 图像金字塔定义
    • 高斯金字塔
    • 拉普拉斯金字塔
    • 图像轮廓
    • 绘制轮廓
    • 轮廓特征
    • 轮廓近似
    • 边界矩形
    • 外接圆
  • 傅里叶变换
    • 傅里叶变换的作用
    • 滤波
    • 频域变换结果
    • 低通与高通滤波

记录本人学习OpenCV的全部内容

图像基本操作

由像素点(0-255)构成图像。
OpenCV从入门到实战_第1张图片

数据读取-图像

  • 1.cv2.IMREAD_COLOR:彩色图像
  • 2.cv2.IMREAD_GRAYSCALE:灰度图像
import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
img=cv2.imread('cat.jpg')
#图像的显示,也可以创建多个窗口
cv2.imshow('image',img)
#等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()

按下任意键图像既会
OpenCV从入门到实战_第2张图片

def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
img.shape

OpenCV从入门到实战_第3张图片

img=cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE)
img

OpenCV从入门到实战_第4张图片

img.shape

OpenCV从入门到实战_第5张图片

#图像的显示,也可以创建多个窗口
cv2.imshow('image',img)
#等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows() 
#保存
cv2.imwrite('mycat.png',img)

数据读取-视频

  • cv2.VideoCapture可以捕获摄像头,用来数字控制不同的设备,例如0,1 如果是视频文件,直接指定路径即可
vc=cv2.VideoCapture('test.mp4')
#检查是否打开正确
if vc.isOpened():
    open,frame = vc.read()
else:
    open=False
while open:
    ret,frame = vc.read()
    if frame is None:
        break
    if ret == True:
        gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        cv2.imshow('result',gray)
        if cv2.waitKey(10)&0xff==27:
            break
vc.release()
cv2.destroyAllwindows()

OpenCV从入门到实战_第6张图片

ROI区域

img=cv2.imread('cat.jpg')
cat=img[0:200,0:200]
cv_show('cat',cat)

OpenCV从入门到实战_第7张图片

边界填充

设置边界框方法cv2.copyMakeBorder(src, top, bottom, left, right, borderType, value)

  • src: 输入的图片
  • top, bottom, left, right: 相应方向上的边框宽度
  • borderType: 定义添加边框的方法
  • value:如果borderType为 cv2.BORDER_CONSTANT 时需要填充的常数

borderType方法如下:

  • cv2.BORDER_REPLICATE:复制法,复制最边缘像素
  • cv2.BORDER_REFLECT:反射法,在图像中指定像素的两边进行反射复制,如: gfedcba|abcdefgh|hgfedcb
  • cv2.BORDER_REFLECT_101:反射法,以最边缘像素为轴,如:dcba|abcd|dcba
  • cv2.BORDER_WRAP:外包装法,如:cdefgh|abcdefgh|abcdefg
  • cv2.BORDER_CONSTANT:常量数值填充
top_size,bottom_size,left_size,right_size = (50,50,50,50)

replicate=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE)
reflect=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REFLECT)
reflect101=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REFLECT101)
wrap=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_WRAP)
constant=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_CONSTANT,value=0)
import matplotlib.pyplot as plt
plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()

OpenCV从入门到实战_第8张图片

数值计算

两张图片直接做加法,需要两张图像的size一样大,对应的像素点相加,由于每个像素点值的大小在0-255之间,因此相加后大于255的像素值自动减去255(相当于%256)。如果一张图像整体加上一个常量,即每个元素的增加一个常量值,亮度提升。

img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')

OpenCV从入门到实战_第9张图片

img_cat2=img_cat+10
#每个像素点加10
img_cat[:5,:,0]

OpenCV从入门到实战_第10张图片

img_cat2=img_cat+10
img_cat[:5,:,0]

OpenCV从入门到实战_第11张图片

#相当于%256
(img_cat+img_cat2)[:5,:,0]

OpenCV从入门到实战_第12张图片
cv2.add(src1, src2)
要求两张图象的size和通道数相同。对应像素值相加,相加后,超过255的数值就让它等于255

cv2.add(img_cat,img_cat2)[:5,:,0]

OpenCV从入门到实战_第13张图片

图像融合

img_cat+img_dog
res=cv2.addWeighted(img_cat,0.4,img_dog,0.6,0)
plt.imshow(res)

OpenCV从入门到实战_第14张图片

图像阈值

  • 灰度图
import cv2 #opencv读取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline 

img=cv2.imread('cat.jpg')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img_gray.shape
cv2.imshow("img_gray", img_gray)
cv2.waitKey(0)    
cv2.destroyAllWindows() 

OpenCV从入门到实战_第15张图片
H-色调(主波长)。
S-饱和度(纯度/颜色的阴影)
V-(强度)

hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

cv2.imshow("hsv", hsv)
cv2.waitKey(0)    
cv2.destroyAllWindows()

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

  • src: 输入图,只能输入单通道图像,通常来说为灰度图

  • dst: 输出图

  • thresh: 阈值

  • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值

  • type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV

  • cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0

  • cv2.THRESH_BINARY_INV THRESH_BINARY的反转

  • cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变

  • cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0

  • cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转

ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)

titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, 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()

OpenCV从入门到实战_第16张图片

图像平滑

在处理和传输数字图像的过程中可能会受到不同的噪声的干扰,图像噪声会引起图像质量降低、图像变得模糊,使图像的特征被淹没、而通过除去噪声来达到图像增强的目的的操作被称为图像平滑处理

  • 1.均值滤波
    假设被噪声污染的数字图像空间含有 N*N 个像素点,而这幅数字图像中每一个像素点值可以通过领域内(可以理解为使用矩形括起来)几个像素点的平均值计算出来,采用平滑图像处理方法就可以得到一幅新的图像,这个过程就被称为均值滤波

  • 2.方框滤波
    前面提到的均值滤波是对整个领域内的像素点求平均,再将平均值赋给目标像素点,但是方框滤波可以选择是否对像素值进行归一化,即可以将得到的像素点的总值赋给一个像素点

  • 3.高斯滤波
    均值滤波中我们是将方框中的每一个像素点的值都加起来求平均然后赋给原来的像素点,这样每一个像素点的权值都是1,但是在高斯滤波中,每一个像素点的权值以和该像素点的距离成高斯分布;即距离越远的像素点的权值越小

  • 4.中值滤波
    中值滤波将一个滑动窗口内所有像素点灰度值进行排序, 将滑动窗口中中心像素点的灰度值用中值来替代; 比如一个窗口内的像素点值为[90,89,76,89,94,93,23,54,87],那么将会使用中值89来代替原先的中点94

img = cv2.imread('lenaNoise.png')

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第17张图片

均值滤波

# 均值滤波
# 简单的平均卷积操作
blur = cv2.blur(img, (3, 3))

cv2.imshow('blur', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()

方框滤波

# 方框滤波
# 基本和均值一样,可以选择归一化
box = cv2.boxFilter(img,-1,(3,3), normalize=True)  
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 方框滤波
# 基本和均值一样,可以选择归一化,容易越界
box = cv2.boxFilter(img,-1,(3,3), normalize=False)  

cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()

高斯滤波

# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img, (5, 5), 1)  

cv2.imshow('aussian', aussian)
cv2.waitKey(0)
cv2.destroyAllWindows()

中值滤波

# 中值滤波
# 相当于用中值代替
median = cv2.medianBlur(img, 5)  # 中值滤波

cv2.imshow('median', median)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 展示所有的
res = np.hstack((blur,aussian,median))
#print (res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像形态学操作

形态学操作是根据图像形状进行的简单操作。一般情况下对二值化图像/灰度图像进行操作。它需要输入两个操作,一个是原始图像,另一个被称为结构化元素或核,它是用来决定操作的性质的。两个基本的形态学操作是腐蚀和膨胀,它们的变体构成了开运算、闭运算和梯度等。简言之,形态学操作其实就是改变物体的形状,比如腐蚀就是"变瘦",膨胀就是"变胖",注意腐蚀和膨胀是针对图片中的白色部分。效果展示如下图:
在这里插入图片描述

腐蚀

img = cv2.imread('dige.png')

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第18张图片

kernel = np.ones((3,3),np.uint8) 
erosion = cv2.erode(img,kernel,iterations = 1)

cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

pie = cv2.imread('pie.png')

cv2.imshow('pie', pie)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((30,30),np.uint8) 
erosion_1 = cv2.erode(pie,kernel,iterations = 1)
erosion_2 = cv2.erode(pie,kernel,iterations = 2)
erosion_3 = cv2.erode(pie,kernel,iterations = 3)
res = np.hstack((erosion_1,erosion_2,erosion_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第19张图片

膨胀操作

img = cv2.imread('dige.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((3,3),np.uint8) 
dige_erosion = cv2.erode(img,kernel,iterations = 1)

cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((3,3),np.uint8) 
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations = 1)

cv2.imshow('dilate', dige_dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()
pie = cv2.imread('pie.png')

kernel = np.ones((30,30),np.uint8) 
dilate_1 = cv2.dilate(pie,kernel,iterations = 1)
dilate_2 = cv2.dilate(pie,kernel,iterations = 2)
dilate_3 = cv2.dilate(pie,kernel,iterations = 3)
res = np.hstack((dilate_1,dilate_2,dilate_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第20张图片

开运算与闭运算

1.开运算 = 先腐蚀运算,再膨胀运算(看上去把细微连在一起的两块目标分开了)
开运算的效果图如下图所示:OpenCV从入门到实战_第21张图片

  • 开运算总结:
    (1)开运算能够除去孤立的小点,毛刺和小桥,而总的位置和形状不便。
    (2)开运算是一个基于几何运算的滤波器。
    (3)结构元素大小的不同将导致滤波效果的不同。
    (4)不同的结构元素的选择导致了不同的分割,即提取出不同的特征。
# 开:先腐蚀,再膨胀
img = cv2.imread('dige.png')

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

cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第22张图片

2.闭运算 = 先膨胀运算,再腐蚀运算(看上去将两个细微连接的图块封闭在一起)
闭运算的效果图如下图所示:
OpenCV从入门到实战_第23张图片

  • 闭运算总结:
    (1)闭运算能够填平小湖(即小孔),弥合小裂缝,而总的位置和形状不变。
    (2)闭运算是通过填充图像的凹角来滤波图像的。
    (3)结构元素大小的不同将导致滤波效果的不同。
    (4)不同结构元素的选择导致了不同的分割。
# 闭:先膨胀,再腐蚀
img = cv2.imread('dige.png')

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

cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第24张图片

梯度运算

一张图片分别做膨胀与腐蚀操作,最后用膨胀后的图片减去腐蚀的图片,得到的新图片便是该图片的梯度运算。

即:梯度运算 (img) = 膨胀 (img) - 腐蚀 (img)

通过梯度运算可以得到前景物体的轮廓。

# 梯度=膨胀-腐蚀
pie = cv2.imread('pie.png')
kernel = np.ones((7,7),np.uint8) 
dilate = cv2.dilate(pie,kernel,iterations = 5)
erosion = cv2.erode(pie,kernel,iterations = 5)

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

cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第25张图片

gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)

cv2.imshow('gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第26张图片

礼帽与黑帽

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

礼帽

#礼帽
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第27张图片

黑帽

#黑帽
img = cv2.imread('dige.png')
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV从入门到实战_第28张图片

图像梯度

图像梯度-Sobel算子

OpenCV从入门到实战_第29张图片
梯度可以按照x方向或者y方向求梯度,其实就是在看像素点的差异变化情况,比如黑白物体的交界,其像素值变化差异是非常大的。
求梯度计算使用的函数就叫做Sobel算子,可以分为水平梯度与竖直梯度。
简单点说,Sobel算子是一种特殊的卷积核,可以用于图像的边缘检测。

img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

OpenCV从入门到实战_第30张图片
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)

  • ddepth:图像的深度
  • dx和dy分别表示水平和竖直方向
  • ksize是Sobel算子的大小
def cv_show(img,name):
    cv2.imshow(name,img)
    cv2.waitKey()
    cv2.destroyAllWindows()
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)

cv_show(sobelx,'sobelx')

OpenCV从入门到实战_第31张图片
白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值

sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx,'sobelx')

在这里插入图片描述

sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)  
cv_show(sobely,'sobely')

OpenCV从入门到实战_第32张图片

图像梯度-Scharr算子

OpenCV从入门到实战_第33张图片

img = cv2.imread('pie.png', cv2.IMREAD_GRAYSCALE)
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)

图像梯度-laplacian算子

OpenCV从入门到实战_第34张图片

  • 不同算子的差异
#不同算子的差异
img=cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.convertScaleAbs(sobely)
sobelxy=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)

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

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

res=np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')

OpenCV从入门到实战_第35张图片

img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show(img,'img')

OpenCV从入门到实战_第36张图片

Canny边缘检测

  • 1 使用高斯滤波器,以平滑图像,滤除噪声。

  • 2 计算图像中每个像素点的梯度强度和方向。

  • 3 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。

  • 4 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。

  • 5 通过抑制孤立的弱边缘最终完成边缘检测。

高斯滤波器

本质就是模糊图像,玩过PS的应该很好了解,本来图像上有很多噪点,用涂抹工具抹一下,噪点没了,图片变模糊了,噪点消失了.
这一阶段比较类似于卷积,拿一个33或55的带权重的滤波器给图像乘一下.
这里以size=3的高斯内核为例:这里做了归一化处理(元素和为 1)
OpenCV从入门到实战_第37张图片
滤波的主要目的是降噪,一般的图像处理算法都需要先进行降噪。而高斯滤波主要使图像变得平滑(模糊),同时也有可能增大了边缘的宽度。

梯度和方向

对于平滑后的图像,首先在水平和垂直方向采用 Sobel kernel 计算得到水平方向Gx和垂直方向Gy

然后计算每个像素的边缘梯度和梯度方向:
OpenCV从入门到实战_第38张图片

非极大值抑制

计算得到梯度值和梯度方向后,对图片进行全面的扫描,以去除不构成边缘的无关像素点.

对于每个像素,检查其是否是在梯度方向中其临近像素点中的局部最大值. 如图:
OpenCV从入门到实战_第39张图片OpenCV从入门到实战_第40张图片
点 A 位于图像边缘(垂直方向). 梯度方向(Gradient Direction) 垂直于边缘. 点 B 和点 C 位于梯度方向.
因此,检查点 A 和点 B,点 C,确定点A 是否是局部最大值. 如果点 A 是局部最大值,则继续下一个阶段;如果点 A 不是局部最大值,则其被抑制(设为 0).

简单来说,NMS 得到的结果是一个 薄边缘(thin edges) 的二值图片.可以理解为边缘点全部被标为1,非边缘点被标为0 .

双阈值监测

这一阶段主要判断上面NMS后的哪些是真正的边缘,哪些是假边缘
该阶段需要设定两个阈值,minVal 和 maxVal,任何边缘强度大于maxVal的确定为边缘,而小于minVal的确定为非边缘,进行丢弃.
位于maxVal与minVal之间的为待确定边缘,进行连续性判断,如果其连着确定边缘则认为是真正边缘的一部分,否则,进行丢弃.如下图所示.
OpenCV从入门到实战_第41张图片
边缘 A 大于 maxVal,因此为“确定边缘(sure-edge)”.
虽然边缘 C 小于 maxVal,但其连接着边缘 A,因此也认为是有效边缘,以得到完整的边缘曲线.
但,边缘 B 虽然大于 minVal,并与边缘 C 位于相同的区域,但其没有与任何“确定边缘”相连接,因此,丢弃该边缘 B.
由上可见,minVal 和 maxVal 值的选择对于边缘检测的结果非常重要.

此外,该阶段的处理还移除小的像素噪声,因为假设边缘是长曲线.
最终,即可得到图片的有效边缘.

img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)

v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100)

res = np.hstack((v1,v2))
cv_show(res,'res')

OpenCV从入门到实战_第42张图片

img=cv2.imread("car.png",cv2.IMREAD_GRAYSCALE)

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

res = np.hstack((v1,v2))
cv_show(res,'res')

OpenCV从入门到实战_第43张图片

图像金字塔与轮廓检测

图像金字塔定义

图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

常见两类图像金字塔

  • 高斯金字塔 ( Gaussian pyramid): 用来向下/降采样,主要的图像金字塔
  • 拉普拉斯金字塔(Laplacian pyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。
    OpenCV从入门到实战_第44张图片

高斯金字塔

  • 高斯金字塔:向下采样方法(缩小)
    OpenCV从入门到实战_第45张图片
  • 高斯金字塔:向上采样方法(放大)
    OpenCV从入门到实战_第46张图片
img=cv2.imread("AM.png")
cv_show(img,'img')
print (img.shape)

OpenCV从入门到实战_第47张图片

up=cv2.pyrUp(img)
cv_show(up,'up')
print (up.shape)
down=cv2.pyrDown(img)
cv_show(down,'down')
print (down.shape)
up2=cv2.pyrUp(up)
cv_show(up2,'up2')
print (up2.shape)
up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(up_down,'up_down')
cv_show(np.hstack((img,up_down)),'up_down')

OpenCV从入门到实战_第48张图片

up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(img-up_down,'img-up_down')

OpenCV从入门到实战_第49张图片

拉普拉斯金字塔

OpenCV从入门到实战_第50张图片

down=cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
l_1=img-down_up
cv_show(l_1,'l_1')

OpenCV从入门到实战_第51张图片

图像轮廓

cv2.findContours(img,mode,method)
mode:轮廓检索模式

  • RETR_EXTERNAL :只检索最外面的轮廓;
  • RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
  • RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
  • RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;

method:轮廓逼近方法

  • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
  • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
    OpenCV从入门到实战_第52张图片

为了更高的准确率,使用二值图像

img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv_show(thresh,'thresh')

OpenCV从入门到实战_第53张图片binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

绘制轮廓

#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# 注意需要copy,要不原图会变。。。
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
cv_show(res,'res')

OpenCV从入门到实战_第54张图片

draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2)
cv_show(res,'res')

OpenCV从入门到实战_第55张图片

轮廓特征

cnt = contours[0]

#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)

OpenCV从入门到实战_第56张图片

轮廓近似

OpenCV从入门到实战_第57张图片

img = cv2.imread('contours2.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]

draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')

OpenCV从入门到实战_第58张图片

epsilon = 0.15*cv2.arcLength(cnt,True) 
approx = cv2.approxPolyDP(cnt,epsilon,True)

draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')

OpenCV从入门到实战_第59张图片

边界矩形

img = cv2.imread('contours.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]

x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')

OpenCV从入门到实战_第60张图片

area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('轮廓面积与边界矩形比',extent)

轮廓面积与边界矩形比 0.5154317244724715

外接圆

(x,y),radius = cv2.minEnclosingCircle(cnt) 
center = (int(x),int(y)) 
radius = int(radius) 
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')

OpenCV从入门到实战_第61张图片

傅里叶变换

傅里叶变换的作用

  • 高频:变化剧烈的灰度分量,例如边界

  • 低频:变化缓慢的灰度分量,例如一片大海

滤波

  • 低通滤波器:只保留低频,会使得图像模糊

  • 高通滤波器:只保留高频,会使得图像细节增强

  • opencv中主要就是cv2.dft()和cv2.idft(),输入图像需要先转换成np.float32 格式。

  • 得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现。

  • cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255)。

频域变换结果

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('lena.jpg',0)

img_float32 = np.float32(img)

dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
# 得到灰度图能表示的形式
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))

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

OpenCV从入门到实战_第62张图片

低通与高通滤波

img = cv2.imread('lena.jpg',0)

img_float32 = np.float32(img)

dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

rows, cols = img.shape
crow, ccol = int(rows/2) , int(cols/2)     # 中心位置

# 高通滤波
mask = np.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0

# IDFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])

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

plt.show() 

OpenCV从入门到实战_第63张图片

img = cv2.imread('lena.jpg',0)

img_float32 = np.float32(img)

dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

rows, cols = img.shape
crow, ccol = int(rows/2) , int(cols/2)     # 中心位置

# 高通滤波
mask = np.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0

# IDFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])

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

plt.show()    

OpenCV从入门到实战_第64张图片

你可能感兴趣的:(笔记,opencv,计算机视觉,python)