Title
Fork me on GitHub

openCV_Note

图像基本操作

数据读取-图像

cv2.imread_color:彩色图像

cv2.imread_grayscale:灰度图像

import cv2     # openCV读取格式为BGR
import matplotlib.pyplot as pl
import numpy as np

img=cv2.imread('./img/dog.jpg')

 # 图像的显示,也可以创建多的窗口
 cv2.imshow('image',img)
 # 等待的时间,毫秒级。0表示任意键即可终止,1000表示1000ms后关闭终止
 cv2.waitKey(0)
 # cv2.waitKey(1000)
 cv2.destroyAllWindows()
import cv2 #openCV读取格式为BGR

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

img=cv2.imread('asset/img/dog.jpg', cv2.IMREAD_GRAYSCALE)     #灰度图

# cv_show('image',img)

# 图像保存
# cv2.imwrite('mydog.png',img)




print(type(img))
#<class 'numpy.ndarray'>

print(img.size)
#5992704

print(img.dtype)
#uint8


数据读取-视频

cv2.videoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1

如果时视频文件,直接指定好路径即可

import cv2
from pandas.core import frame

# 检查是否打开正确
vc=cv2.VideoCapture('./asset/video/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(500) & 0xFF ==27:      #前者表示视频的展示速度;'0xFF==27'表示退出键
            break
vc.release()
cv2.destroyAllWindows()

截取部分数据(ROI)

(ROI:感兴趣的区域)

# ROI
img=cv2.imread('asset/img/dog.jpg')
dog=img[500:800,800:1500]      #截取部分
cv_show('dog',dog)

边界填充

1、BORDER_REPLICATE:复制法,也就是复制最边缘的像素

2、BORDER_REFLECT:反射法,对感兴趣图像中的像素在两边进行复制,例:fedcba|abcdefgh|hgfedcb

3、BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,

gfedcb|abcdefgh|gfedcba

4、BORDER_WRAP:外包装法,cdefgh|abcdefgh|abcdefg

5、BORDER_CONSTANT:常量法,以数值填充

import cv2

img=cv2.imread("asset/img/dog.jpg")

top_side,bottom_side,left_side,right_side=(50,50,50,50)

replicate=cv2.copyMakeBorder(img,top_side,bottom_side,left_side,right_side,borderType=cv2.BORDER_REPLICATE)
reflect=cv2.copyMakeBorder(img,top_side,bottom_side,left_side,right_side,cv2.BORDER_REFLECT)
reflect101=cv2.copyMakeBorder(img,top_side,bottom_side,left_side,right_side,cv2.BORDER_REFLECT_101)
wrap=cv2.copyMakeBorder(img,top_side,bottom_side,left_side,right_side,cv2.BORDER_WRAP)
constant=cv2.copyMakeBorder(img,top_side,bottom_side,left_side,right_side,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('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()

数值计算

import cv2

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


print(img_cat[:5,:,0])
img_cat2=img_cat+100    # 在每个像素点上都加10
print(img_cat2[:5,:,0])

# cv2.imshow("result",img_cat2)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

result=(img_cat+img_cat2)[:5,:,0]       # 如果两值相加大于255(0-255),结果=相加值%256
print(result)

result2=cv2.add(img_cat,img_cat2)[:5,:,0]       #如果两者大于255,结果是255
print(result2)

注意:

1、一般情况下,如果两者相加值大于255,则结果返回:相加值%256

2、使用add()下,如果大于255,则结果取255

图像融合(FUsion)

import cv2
import matplotlib.pyplot as plt

img_cat=cv2.imread("asset/img/cat.jpg")
img_dog=cv2.imread("asset/img/dog.jpg")

# img_cat+img_dog
# print(img_cat.shape)

img_dog=cv2.resize(img_dog,(1280,853)) #resize改变原有的,给一个w,h
# print(img_dog.shape)

res0=cv2.addWeighted(img_cat,0.4,img_dog,0.6,0)     #R=alpha X1,bata X2 +b
plt.imshow(res0)


res=cv2.resize(img_cat,(0,0),fx=3,fy=1)
# res1=cv2.resize(img_cat,(0,0),fx=1,fy=3)
# plt.imshow(res)
# plt.imshow(res1)

plt.show()

注意:

1、

图像处理

图像阈值

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_INVTHRESH_BINARY的反转
cv2.THRESH_TRUNC大于阈值部分设为阈值,否则不变
cv2.THRESH_TOZERO大于阈值部分不改变,否则设为0
cv2.THRESH_TOZERO_INVTHRESH_TOZERO的反转

图像平滑处理

1、均值滤波:简单的平均卷积操作

2、方框滤波:基本和均值一样,可以选择归一化

import cv2

#原始图片(有杂质)
import numpy as np
img=cv2.imread('asset/img/lenaNoise.png')

#均值滤波
blur=cv2.blur(img,(3,3))

#方框滤波
box=cv2.boxFilter(img,-1,(3,3),normalize=True)
#-1表示一致,normalize=True和均值效果一样\normalize=False 容易越界

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

3、高斯滤波: 高斯模糊的卷积核里的数值满足高斯分布,相当于更重视中间的

# 高斯滤波
gaussianblur=cv2.GaussianBlur(img,(5,5),1)

4、中值滤波:#相当于用中值代替

# 中值滤波
median=cv2.medianBlur(img,5)

腐蚀操作

erode()

import cv2
import numpy as np

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

kernel=np.ones((5,6),np.uint8)	#kernel表示核

erosion=cv2.erode(img,kernel,iterations=1) #iterations表示迭代次数



pie=cv2.imread('asset/img/pie.png')

kernel2=np.ones((30,30),np.uint8)
erosion1=cv2.erode(pie,kernel2,iterations=1)#第一次腐蚀
erosion2=cv2.erode(pie,kernel2,iterations=2)#第二次腐蚀
erosion3=cv2.erode(pie,kernel2,iterations=3)#第三次腐蚀
res=np.hstack((erosion1,erosion2,erosion3))

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

膨胀操作

dilate()

import cv2
import numpy as np

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

#膨胀
dilation=cv2.dilate(img,kernel,iterations=1)



pie=cv2.imread('asset/img/pie.png')
kernel2=np.ones((30,30),np.uint8)
dilation1=cv2.dilate(pie,kernel2,iterations=1)#第一次膨胀
dilation2=cv2.dilate(pie,kernel2,iterations=2)#第二次膨胀
dilation3=cv2.dilate(pie,kernel2,iterations=3)#第三次膨胀
res=np.hstack((dilation1,dilation2,dilation3))

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

开运算与闭运算(形态学)

需要morphologyEX(形态学)导入

开运算(MORPH_OPEN):先腐蚀,再膨胀

闭运算(MORPH_CLOSE):先膨胀,再腐蚀

import cv2
import numpy as np

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

# 开运算:先腐蚀,再膨胀
kernel=np.ones((3,3),np.uint8)
opening=cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)

# 开运算:先膨胀,再腐蚀
kernel=np.ones((3,3),np.uint8)
closing=cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)

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

梯度运算

需要morphologyEX(形态学)导入

梯度(MORPH_GRADIENT)=膨胀-腐蚀

import cv2
import numpy as np

pie = cv2.imread('asset/img/pie.png')

kernel = np.ones((7,7),np.uint8)
#梯度=膨胀-腐蚀
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)

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

礼帽和黑帽

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

黑帽(MORPH_BLACKHAT)=闭运算-原始输入

import cv2
import numpy as np

img = cv2.imread('asset/img/dige.png')
kernel = np.ones((7,7),np.uint8)

#礼帽
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

#黑帽
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)

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

图像梯度-Soble算子

dst=cv2.Soble(src,ddepth,dx,dy,ksize)

·ddepth:图像的深度

·dx和dy分别表示水平和竖直方向

·ksize是Soble算子的大小

#水平的梯度
sobleX=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
#白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值
sobleX=cv2.convertScaleAbs(sobleX)#取绝对值


#垂直的梯度
sobleY=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobleY=cv2.convertScaleAbs(sobleY)

#分别计算x和y,再求和
sobleXY=cv2.addWeighted(sobleX,0.5,sobleY,0.5,0)

scharr算子

#scharr算子
scharr_x=cv2.Scharr(img,cv2.CV_64F,1,0)
scharr_y=cv2.Scharr(img,cv2.CV_64F,0,1)
scharr_x=cv2.convertScaleAbs(scharr_x)
scharr_y=cv2.convertScaleAbs(scharr_y)
scharr_xy=cv2.addWeighted(scharr_x,0.5,scharr_y,0.5,0)

scharr算子相比于soble算子,描述的更详细,能够捕捉到跟丰富的梯度信息

lapkacian算子

#laplacian算子
laplacian=cv2.Laplacian(img,cv2.CV_64F)
laplacian=cv2.convertScaleAbs(laplacian)

不建议单独使用该算子

Canny边缘检测

步骤:

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

1、高斯滤波

2、梯度和方向

3、非极大值抑制

方法一:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-roPecZdU-1650809092131)(C:\Users\yujunyu\AppData\Roaming\Typora\typora-user-images\image-20220402171136467.png)]

线性插值法:设g1的梯度幅值M(g1),g2的梯度幅值M(g2),则dtmp1就可以得到M(dtmp1)=w*M(g2)+(1-w) * M(g1)

其中w=distance(dtmp1,g2)/distance(g1,g2)

distance(g1,g2)表示两点之间的距离

方法二:

为了简化计算,由于一个像素周围有八个像素,把一个像素的梯度方向离散为八个方向,这样就只需要计算见后即可,不用再插值了

4、双阈值检测

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5QLPmX5P-1650809092132)(C:\Users\yujunyu\AppData\Roaming\Typora\typora-user-images\image-20220402171655535.png)]

img_lena=cv2.imread("../asset/img/lena.jpg",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img_lena,80,150)
v2=cv2.Canny(img_lena,50,100)
res=np.hstack((v1,v2))

img_car=cv2.imread("../asset/img/car.png",cv2.IMREAD_GRAYSCALE)
v3=cv2.Canny(img_car,120,250)	#阈值越大,检测的边界越模糊
v4=cv2.Canny(img_car,50,100)	#阈值越小,检测的边界越仔细
res1=np.hstack((v3,v4))

图像金字塔

高斯金字塔

1、向下采样方法(缩小)

1)将Gi与高斯内核卷积

2)将所有偶数行和列去除

PyrDown

2、向上采样方法(放大)

1)将图像再每个方向扩大为原来的两倍,新增的行和列以00填充

2)使用先前同样的内核(乘以四)与放大后的图像卷积,获得近似值

PyrUP

#上采样(放大)
up=cv2.pyrUp(img)

#下采样(缩小)
down=cv2.pyrDown(img)

如果先进行上采样,再进行下采样,结果图像和原始图像会有区别,进行完Up、Down的图像会有损失,会有模糊

拉普拉斯金字塔

Li=Gi-PyrUP(PyrDown(Gi))

简单来说,就是先Down,然后Up,再原始数据-down_up

图像轮廓

轮廓检测函数:cv2.findContours(img,mode,method)

mode:轮廓检索模式

·RETR_EXTERNAL:只检索最外面的轮廓

·RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中

·RETR_CCOMP:检索所有的轮廓,并将它们组织为两层:顶部是各部分的外部边界,第二层是空洞的边界

·(常用)RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次

method:轮廓逼近方法

·cv2.CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶的序列)

·cv2.CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留它们的终点部分

做法:

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

img=cv2.imread('../asset/img/contours.png')    #读取图像
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   #转灰度图
ret,thresh=cv2.threshold(gray,127,255,cv2.THRESH_BINARY)     #二值

contours,hierarchy=cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)

2、绘制轮廓

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

轮廓特征

面积:countArea(img)

周长:arcLength(img,True) #True表示闭合的

轮廓近似

生成逼近曲线:approxPolyDP()

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

cnt=contours[0]#拿到其中一个轮廓

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



#近似
epsilon=0.01*cv2.arcLength(cnt,True)    #0.15这个值,越小与原来一比没什么变化
approx=cv2.approxPolyDP(cnt,epsilon,True)

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


在图像轮廓中其他的操作:

边界矩形

外接圆

模板匹配

模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)

TM_SQDIFF:计算平方不同,计算出来的值越小,越相关

TM_CCORR:计算相关性,计算出来的值越大,越相关

TM_CCOEFF:计算相关系数,计算出来的值越大,越相关

TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关

TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关

TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关

1、读取两张图像;一张是原始图像,一张是模板图像

2、模板匹配;cv2.matchTemplate(img,template,method)

3、定位;cv2.minMaxLoc(img)

直方图

calcHist()函数来计算图像直方图

cv2.calcHist(images,channels,mask,histSize,ranges)

- images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]

- channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。

- mask: 掩模图像。统整幅图像的直方图就把它为 None。但是如 果你想统图像某一分的直方图的你就制作一个掩模图像并 使用它。

- histSize:BIN 的数目。也应用中括号括来。通常为256

- ranges: 像素值范围常为 [0-256]

img=cv2.imread('../asset/img/lena.jpg',0)   #0表示灰度图
#直方图
hist=cv2.calcHist([img],[0],None,[256],[0,256])
#展示
plt.hist(img.ravel(),256)
plt.show()

mask操作

1、创建mask

#1-创建一个mask
mask=np.zeros(img2.shape[:2],np.uint8)      #使用np.zeros()来定义;参数二:图像大小,
# print(mask.shape)
mask[100:700,100:1100]=255      #保存为白色
# cv_show('mask',mask)

2、与操作

new_img=cv2.bitwise_and(img2,img2,mask=mask)    

直方图均衡化

equalizeHist(img)均衡化函数

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

#均衡化
equ=cv2.equalizeHist(img)
# plt.hist(equ.ravel(),256)
# plt.show()

res=np.hstack((img,equ))
cv_show('equ',res)

自适应直方图均衡化

简单来说就是将图像分小块进行均衡化

cv2.createCLAHE(clipLimit,tileGridSize) #参数一:对比度限制,默认值是40,参数二:分块的大小,默认是8*8

img1=cv2.imread('../asset/img/clahe.jpg',0)
#自适应均衡化
clahe=cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
#将自适应应用到图像上
res_clahe=clahe.apply(img1)

傅里叶变换

在频域中一切都是静止的

傅里叶变换的作用:

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

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

滤波:

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

- opencv中主要就是cv2.dft()和cv2.idft(),输入图像需要先转换成np.float32 格式。dft就是执行傅里叶变换,idft就是逆变换

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

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

\1. cv2.dft(img, cv2.DFT_COMPLEX_OUTPUT) 进行傅里叶变化

参数说明: img表示输入的图片, cv2.DFT_COMPLEX_OUTPUT表示进行傅里叶变化的方法

\2. np.fft.fftshift(img) 将图像中的低频部分移动到图像的中心

参数说明:img表示输入的图片

\3. cv2.magnitude(x, y) 将sqrt(x^2 + y^2) 计算矩阵维度的平方根

参数说明:需要进行x和y平方的数

4.np.fft.ifftshift(img) # 进图像的低频和高频部分移动到图像原来的位置

参数说明:img表示输入的图片

5.cv2.idft(img) # 进行傅里叶的逆变化

参数说明:img表示经过傅里叶变化后的图片

第一步:载入图片

第二步:使用np.float32进行格式转换

第三步:使用cv2.dft进行傅里叶变化

第四步:使用np.fft.shiftfft将低频转移到中间位置

第五步:使用cv2.magnitude将实部和虚部投影到空间域

第六步:进行作图操作

import numpy as np
import cv2
from fontTools.merge import cmap
from matplotlib import pyplot as plt

#读入图像,转换为灰度图
img = cv2.imread('../asset/img/lena.jpg',0)

#转换为np.float32格式
img_float32 = np.float32(img)

#使用cv2.dft进行傅里叶变换
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)

#使用np.fft.shiftfft()将转换后的图像的低频转移到中心位置
dft_shift = np.fft.fftshift(dft)

# 得到灰度图能表示的形式;
# 使用cv2.magnitude将实部和虚部转换为实部,乘以20是为了使得结果更大
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))

plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.subplot(122)
plt.imshow(magnitude_spectrum,cmap='gray')
plt.show()
# 使用掩模只保留低通

# 第一步读入图片
img = cv2.imread('lena.jpg', 0)
# 第二步:进行数据类型转换
img_float = np.float32(img)
# 第三步:使用cv2.dft进行傅里叶变化
dft = cv2.dft(img_float, flags=cv2.DFT_COMPLEX_OUTPUT)
# 第四步:使用np.fft.fftshift将低频转移到图像中心
dft_center = np.fft.fftshift(dft)
# 第五步:定义掩模:生成的掩模中间为1周围为0
crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2) # 求得图像的中心点位置
mask = np.zeros((img.shape[0], img.shape[1], 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1

# 第六步:将掩模与傅里叶变化后图像相乘,保留中间部分
mask_img = dft_center * mask

# 第七步:使用np.fft.ifftshift(将低频移动到原来的位置
img_idf = np.fft.ifftshift(mask_img)

# 第八步:使用cv2.idft进行傅里叶的反变化
img_idf = cv2.idft(img_idf)

# 第九步:使用cv2.magnitude转化为空间域内
img_idf = cv2.magnitude(img_idf[:, :, 0], img_idf[:, :, 1])

# 第十步:进行绘图操作
plt.subplot(121)
plt.imshow(img, cmap='gray')
plt.subplot(122)
plt.imshow(img_idf, cmap='gray')
plt.show()

实战:银行卡号识别

基本思路:

1、读取模板

​ 2、灰度处理

​ 3、二值处理

​ 4、检测轮廓

​ 5、算外接矩形

6、读取检测图像

​ 7、灰度转换

​ 8、二值

​ 9、礼帽操作

​ 10、闭操作

​ 11、轮廓检测,对每检测到的一组切分成小区域,(5412–>5 4 1 2)

12、模板匹配

一、模板处理:

1)模板预处理

1、读取模板

2、转灰度图

3、二值图

2)模板轮廓检测

4、计算轮廓

5、遍历轮廓

二、输入图像处理:

1)预处理

2)去除不需要的内容

3)轮廓检测

实战:文档OCR

图像特征

harris

基本原理:

代码实现:

cv2.cornerHarris(img,blockSize,ksize,K)

img:数据类型为float32的输入图像

blockSize:角点检测中指定的区域的大小

ksize:Sobel求导中使用的窗口大小

k:取值参数为【0.04,0.06】

SIFT

SIFT(Scale Invariant Feature Transfrom)

图像尺度空间

尺度空间的获取通常使用高斯模糊来实现

不同α的高斯函数决定了对图像的平滑程度,越大的α值对应的图像越模糊

多分辨率金字塔

高斯差分金字塔(DOG)

代码实现:

img=cv2.imread('../asset/img/test_1.jpg')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# 得到特征点
sift=cv2.SIFT_create()
kp=sift.detect(gray,None)

img=cv2.drawKeypoints(gray,kp,img)

show.cv_show("img",img)

# 计算特征
kp,des=sift.compute(gray,kp)
print(np.array(kp).shape)

print(des.shape)

特征匹配

img1=cv2.imread('../asset/img/box.png',0)
img2=cv2.imread('../asset/img/box_in_scene.png',0)


sift=cv2.SIFT_create()

kp1,des1=sift.detectAndCompute(img1,None)
kp2,des2=sift.detectAndCompute(img2,None)

# crossCheck表示两个特征点要互相匹,例如A中的第i个特征点与B中的第j个特征点最近的,并且B中的第j个特征点到A中的第i个特征点也是
# NORM_L2: 归一化数组的(欧几里德距离),如果其他特征计算方法需要考虑不同的匹配计算方式
bf=cv2.BFMatcher(crossCheck=True)

# 1对1的匹配
matches=bf.match(des1,des2)
matches=sorted(matches,key=lambda x:x.distance)
img3=cv2.drawMatches(img1,kp1,img2,kp2,matches[:5],None,flags=None)


# k对最佳匹配
bf=cv2.BFMatcher()
matches=bf.knnMatch(des1,des2,k=2)

#过滤操作,
good=[]
for m,n in matches:
    if m.distance<0.75*n.distance:
        good.append([m])

img4=cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)

show.cv_show('res',img4)

RANSAC算法

随机抽样一致算法(random sample consens)

实战:全景图像拼接

实战:停车场车位识别

实战:答题卡识别

背景建模

帧差法(了解)

由于场景中的目标在运动,目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值,当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。

帧差法非常简单,但是会引入噪音和空洞问题

混合高斯模型

可以实现行人跟踪

光流估计

光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪

实习光流满足的三个前提:

亮度恒定:同一点随着时间的变化,其亮度不会发生改变

小运动:随着时间的变化不会引起位置的剧烈变化

空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。例一辆车的左车灯和右车灯

Lucas-Kanade算法

约束方程:

cv2.calcOpticalFlowPyrLK()

参数:

  • prevImage 前一帧图像

  • nextImage 当前帧图像

  • prevPts 待跟踪的特征点向量

  • winSize 搜索窗口的大小

  • maxLevel 最大的金字塔层数

返回:

  • nextPts 输出跟踪特征点向量

  • status 特征点是否找到,找到的状态为1,未找到的状态为0

实现代码

import numpy as np
import cv2

cap = cv2.VideoCapture('test.avi')

# 角点检测所需参数
feature_params = dict( maxCorners = 100,
                       qualityLevel = 0.3,
                       minDistance = 7)

# lucas kanade参数
lk_params = dict( winSize  = (15,15),
                  maxLevel = 2)

# 随机颜色条
color = np.random.randint(0,255,(100,3))

# 拿到第一帧图像
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 返回所有检测特征点,需要输入图像,角点最大数量(效率),品质因子(特征值越大的越好,来筛选)
# 距离相当于这区间有比这个角点强的,就不要这个弱的了
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)

# 创建一个mask
mask = np.zeros_like(old_frame)

while(True):
    ret,frame = cap.read()
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 需要传入前一帧和当前图像以及前一帧检测到的角点
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

    # st=1表示
    good_new = p1[st==1]
    good_old = p0[st==1]

    # 绘制轨迹
    for i,(new,old) in enumerate(zip(good_new,good_old)):
        a,b = new.ravel()
        c,d = old.ravel()
        mask = cv2.line(mask, (a,b),(c,d), color[i].tolist(), 2)
        frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1)
    img = cv2.add(frame,mask)

    cv2.imshow('frame',img)
    k = cv2.waitKey(150) & 0xff
    if k == 27:
        break

    # 更新
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()
cap.release()

cv2.dnn.readNetCaffe()

posted @ 2022-04-24 22:09  新西兰蟹老板  阅读(25)  评论(0编辑  收藏  举报  来源