基于python的Opencv项目实战(第二章)

https://www.bilibili.com/video/BV1oJ411D71z?p=2
课程代码及课件链接:https://pan.baidu.com/s/1HmFVa5aiccBJKQT_1yjHdQ
提取码:ehen

一、环境配置

  • Anaconda3-2018.12-Windows-x86_64.exehttps://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/
  • OpenCV-Python3.4.2.16:打开anaconda prompt,进入d:/Anaconda3/Scripts,输入pip install opencv-python==3.4.2.16
  • OpenCV-contrib-python 3.4.2.16:接着输入pip install opencv-contrib-python==3.4.2.16

二、图像基本操作

1.数据读取-图像

import cv2 #opencv读取的格式是B,G,R,对应序号0,1,2,
import matplotlib.pyplot as plt
import numpy as np 
%matplotlib inline 

img=cv2.imread('cat.jpg')
#第一个参数为图片路径,第二个参数为读取图片的形式
#cv2.imread在不加第二个参数的情况下默认将图片转换成了一个三维数组,[行数,列数,通道数]
#默认第二个参数为cv2.IMREAD_COLOR,加载彩色图片,也可直接写1,
#cv2.IMREAD_GRAYSCALE:以灰度模式加载图片,可以直接写0。

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

img.shape #查看图像的高度、宽度、通道数(y,x,通道数)

cv2.imwrite('mycat.png',img) #('保存图像的名字',图像对象) 保存
#为了下面记录方便,这里写了一个cv_show函数
def cv_show(name,img):
    cv2.imshow(name,img) 
    cv2.waitKey(0) 
    cv2.destroyAllWindows()

2.数据读取-视频

vc = cv2.VideoCapture('test.mp4') #(视频路径)建立视频对象
vc = cv2.VideoCapture(1) #捕获摄像头。0为笔记本内置摄像头,1为外接摄像头

# 检查是否打开正确
if vc.isOpened(): 
    oepn, frame = vc.read() 
    open = False
    
while open: #(依次读取每一帧图像)
    ret,frame=vc.read()#.read()返回字典类型,ret为True,frame为读入的图像
else:
    if frame is None:
        break
    if ret==True:
        gray = cv2.cvtColor(frame,  cv2.COLOR_BGR2GRAY) #(需要转换的图片,是转换成何种格式)
#颜色空间转换函数
#cv2.COLOR_BGR2RGB 将BGR格式转换成RGB格式
#cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片
        cv2.imshow('result', gray)
        if cv2.waitKey(10) & 0xFF == 27:  
#cv2.waitKey(delay)返回值:1、等待期间有按键:返回按键的ASCII码(比如:Esc的ASCII
#码为27);2、等待期间没有按键:返回 -1
            break
vc.release()  #释放对象、关闭摄像头
cv2.destroyAllWindows() #删除窗口

3.截取部分图像数据

img=cv2.imread('cat.jpg')
cat=img[0:50,0:200] 
cv_show('cat',cat)
#把图像截成高为50,宽为200的图像(三维数组切片)

关于三维数组切片

4.颜色通道提取

b,g,r=cv2.split(img) #把图像分为B、G、R三个通道,切为三个二维数组

#split浪费资源,所以尽量写成以下形式
b=img[:,:,0]
g=img[:,:,1]
r=img[:,:,2]

# 图像只保留R
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,1] = 0
cv_show('R',cur_img)

# 只保留G
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,2] = 0
cv_show('G',cur_img)

# 只保留B
cur_img = img.copy()
cur_img[:,:,1] = 0
cur_img[:,:,2] = 0
cv_show('B',cur_img)

5.边界填充

#设定边界的长度
top_size,bottom_size,left_size,right_size = (50,50,50,50)

#5种不同的边界填充模式
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0) #value=0表示用0进行补全操作

import matplotlib.pyplot as plt
plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL') #plt.subplot(2,2,1) 将窗口分为两行两列四个子图,则可显示四幅图片
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.subplot(231)表示把显示界面分为2*3的网格,1表示第一张图片

plt.show()
BORDER_REPLICATE:复制法,也就是复制最边缘像素。
BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
BORDER_CONSTANT:常量法,常数值填充。

6.数值计算

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

img_cat2= img_cat +10 
img_cat[:5,:,0]#第一维度取前五个二维数组,第二维度取全部行,第三维度取第一列
img_cat2[:5,:,0]
#观察得每个元素都增加10

#相当于% 256
(img_cat + img_cat2)[:5,:,0] #数组大小一样可以相加

#加至255不再增加
cv2.add(img_cat,img_cat2)[:5,:,0]

7.图像融合

img_cat + img_dog
#报错,不同大小的图像不能相加

img_dog = cv2.resize(img_dog, (500, 414))(宽像素,高像素)
#调整两张图像大小一致

#两张图像叠加
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0) # R=X1*α+X2*β+b  α、β为权重,b为在亮度集上的微调
plt.imshow(res)

#把图片的宽高成比例放缩
res = cv2.resize(img, (0, 0), fx=20, fy=20)
plt.imshow(res)

三、图像处理

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_INV THRESH_BINARY的反转       ## _inv为反转
cv2.THRESH_TRUNC 大于阈值部分设为最大值,否则不变
cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
img=cv2.imread('cat.jpg')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img_gray.shape

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()

2.图像平滑

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

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

# 均值滤波
# 简单的平均卷积操作
blur = cv2.blur(img, (3, 3))  #取(3,3)d的卷积核
cv2.imshow('blur', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的。像素点近的权重大,远的权重小
aussian = cv2.GaussianBlur(img, (5, 5), 1)  #标准差为1
cv2.imshow('aussian', aussian)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 中值滤波
# 相当于用中值代替
median = cv2.medianBlur(img, 5)  # 第二个参数为方框大小[1,3,5,7]
cv2.imshow('median', median)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 展示所有的
res = np.hstack((blur,aussian,median)) # horizontal横向排列   vstack   vertical纵向排列
#print (res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.形态学-腐蚀操作

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

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

kernel = np.ones((3,3),np.uint8)  #确定卷积核大小
erosion = cv2.erode(img,kernel,iterations = 1) #参数3为指定迭代次数

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

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()

4.形态学-膨胀操作

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()

5.进行各类形态学的变化

img = cv2.morphologyEx(src, op, kernel)

op:
·cv2.MORPH_OPEN:开运算
·cv2.MORPH_CLOSE:闭运算
·cv2.MORPH_GRADIENT:梯度运算
·cv2.MORPH_TOPHAT:礼帽运算
·cv2.MORPH_BLACKHAT:黑帽运算

开运算与闭运算

# 开:先腐蚀,再膨胀
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()


# 闭:先膨胀,再腐蚀
img = cv2.imread('dige.png')

kernel = np.ones((5,5),np.uint8) 
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)  #morphology形态学  morphologyEx高级形态变换

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

梯度运算

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

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

礼帽与黑帽

·礼帽 = 原始输入-开运算结果
·黑帽 = 闭运算-原始输入
#礼帽
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()

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

6.图像梯度-Sobel算子

这个内积是矩阵卷积(对应元素相乘再相加)
所以从右到左,从下到上

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

· ddepth:图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度
· dx和dy:表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
· ksize:Sobel算子的大小[1,3,5,7]
img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)  #白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要用CV_64F保留负数
cv_show('sobelx',sobelx)

#分别计算x和y
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)
#不建议直接计算xy,即参数3,4 =[1,1]

#再求和
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) #参数5为增量
cv_show('sobelxy',sobelxy)

#kk丽娜
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show('img',img)

img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show('sobelxy',sobelxy)

7.图像梯度-Scharr算子

在这里插入图片描述

8.图像梯度-laplacian算子

在这里插入图片描述

#不同算子的差异
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)

img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show('img',img)
Scharr算子比Sobel算子要细致,laplacian算子为二次导对噪点更为敏感,所以不建议单独使用

9.Canny边缘检测

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

1)高斯滤波器

在这里插入图片描述

2)梯度和方向

在这里插入图片描述

3)非极大值抑制

理解一:
在这里插入图片描述理解二:如果比周围的大,则保留,反之舍弃
在这里插入图片描述

4)双阈值检测

在这里插入图片描述
maxVal,minVal越大,则能保留的值越少,图像边界越简洁,

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

v1=cv2.Canny(img,80,150) #[minVal,maxVal]
v2=cv2.Canny(img,50,100)

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

10.图像金字塔

·高斯金字塔
·拉普拉斯金字塔

在这里插入图片描述

1)高斯金字塔

1.1 高斯金字塔:向下采样方法(缩小)

在这里插入图片描述

1.2高斯金字塔:向上采样方法(放大)

在这里插入图片描述

img=cv2.imread("AM.png")
cv_show(img,'img')
print (img.shape)

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')
需要注意的是,PryUp和PryDown不是互逆的,即PryUp不是降采样的逆操作。这种情况下,图像首先在每个维度上扩大为原来的两倍,
新增的行(偶数行)以0填充。然后给指定的滤波器进行卷积(实际上是一个在每个维度都扩大为原来两倍的过滤器)去估计“丢失”像素
的近似值。

PryDown( )是一个会丢失信息的函数。为了恢复原来更高的分辨率的图像,我们要获得由降采样操作丢失的信息,这些数据就和拉普拉斯
金字塔有关系了。

2)拉普拉斯金字塔

在这里插入图片描述具体关于pyrUp(),pyrDown(),resize()函数的区别请看这篇博文
https://www.cnblogs.com/wyuzl/p/6294275.html

11.图像轮廓

binary, contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])

返回的参数:
·参数一:传入的二值化的图像
·参数二:图像的轮廓
·参数三:每条轮廓对应的属性

传入的参数:
·参数一image:寻找轮廓的图像
·参数二mode:轮廓的检索模式

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

·参数三method:轮廓逼近方法

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

注意:在新版opencv中,该函数不知返回后两个参数,所以使用新版opencv时,因为不会改变原图,所以下面代码中不需要使用copy

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')

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

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

轮廓特征

# 进行轮廓特征操作记得要选中某一个轮廓
cnt = contours[0]
#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)

轮廓近似

在这里插入图片描述

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')

边界矩形

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')

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

外接圆

#获取外接圆的圆心和半径
(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')

四、直方图与模板匹配

直方图

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

·images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]
·channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。
·mask: 掩模图像。统整幅图像的直方图就把它为 None。但是如 果你想统图像某一分的直方图的你就制作一个掩模图像并 使用它。
·histSize:BIN 的数目。也应用中括号括来
·ranges: 像素值范围常为 [0~256]
img = cv2.imread('cat.jpg',0) #0表示灰度图
hist = cv2.calcHist([img],[0],None,[256],[0,256])

plt.hist(img.ravel(),256);  # img.ravel()把多维数组转化为一维数组
#plt.hist()  绘制直方图
plt.show()

img = cv2.imread('cat.jpg') 
color = ('b','g','r')
for i,col in enumerate(color):  #枚举类型,i=[0,1,2],col=['b','g','r']
    histr = cv2.calcHist([img],[i],None,[256],[0,256]) 
    plt.plot(histr,color = col) #绘制曲线图
    plt.xlim([0,256]) #设置x轴的数值显示范围

mask操作

# 创建mast
mask = np.zeros(img.shape[:2], np.uint8) [:2]取前两个值
mask[100:300, 100:400] = 255  # 要保存什么就把什么值设为255
cv_show(mask,'mask')

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

masked_img = cv2.bitwise_and(img, img, mask=mask)#与操作。黑的仍为黑,白的被图片对应代替
cv_show(masked_img,'masked_img')

hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])

#可以看掩模后的曲线图
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()

直方图均衡化(增加图像对比度)

img = cv2.imread('clahe.jpg',0) #0表示灰度图 #clahe
plt.hist(img.ravel(),256); 
plt.show()

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

自适应直方图均衡化

把图像切成一小块一小块分别做均衡化,可以更多的保留细节,不过在噪点多的时候影响会更大,需要做出取舍

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) #参数一为阈值

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

模板匹配

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

# 模板匹配
img = cv2.imread('lena.jpg', 0)
template = cv2.imread('face.jpg', 0)  # 读取模板的灰度图
h, w = template.shape  #获取模板的高宽
·TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
·TM_CCORR:计算相关性,计算出来的值越大,越相关
·TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
·TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
·TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
·TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关

六种模式的相关公式

methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
           'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']

res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF_NORMED) #返回匹配矩阵(A-a+1)x(B-b+1)

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) #返回最小值,最大值,最小值的坐标,最大值的坐标

在opencv中,坐标原点为左上角,x轴从左向右,y轴从上向下。

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

    # 匹配方法的真值
    method = eval(meth)  #将字符串转换为可执行语句
    print (method)
    res = cv2.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

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

    # 画矩形
    cv2.rectangle(img2, top_left, bottom_right, 255, 2)

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

匹配多个对象

原理:设置匹配阈值,超过阈值的坐标都标记

img_bgr = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
h, w = template.shape[:2]

res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
# 取匹配程度大于%80的坐标
loc = np.where(res >= threshold)  此时的loc为(y,x)的集合

for pt in zip(*loc[::-1]):  # *号表示可选参数  loc[::-1]可以反转获取到的坐标点顺序;即(x,y)
    bottom_right = (pt[0] + w, pt[1] + h)
    cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
    print(pt)

cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值