图片由h * w * c个像素组成(一般分为R/G/B三个通道时,每个通道图片由h * w个像素组成),像素范围在0-255(即黑-白),故图片的numpy数组格式为uint8足以。
opencv读取的格式是BGR
【注】matplotlib inline 在jupyter展示中,图绘制完了直接进行展示,不用plt.show()显示
import cv2 # opencv读取的格式是BGR
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
img = cv2.imread('cat.jpg')
# 图像的显示,也可以创建多个窗口
cv2.imshow('image',img)
# 等待时间,毫秒级,0表示任意键终止。 如果设定1000,大概10s就销毁窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
规范一下,写个小函数,定义图像名 并 传入已读取的图像,即可显示
def cv_show(img_name,img):
cv2.imshow(img_name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
读取图片的同时转换为灰度图像
img.shape 图像的形状
img_gray = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
print(img_gray, img_gray.shape) # 灰度图是2维 (414, 500)
cv_show('cat_gray',img_gray)
imwrite需要两个参数:(路径名)图片名, 图
cv2.imwrite('cat_gray.jpg',img_gray)
感兴趣区域(ROI)的截取 [x:x+w,y:y+h]
img = cv2.imread('cat.jpg')
img_crop = img[100:200,50:200]
cv2.imshow('cat_crop',img_crop)
import cv2
b,g,r = cv2.split(img) # cv2.split(img)[0] 等同于 img[:,:,0]取第一个通道
b[100:200,50:200] = 0
img_new = cv2.merge((b,g,r)) # 处理完某个通道,再重新组合b g r3个通道
cv2.imshow('img_new',img_new)
cv2.waitKey(0)
比如将蓝色通道都置0,绿 红通道 这块区域的值没变,则组合成黄色
扩充图像边界 copyMakeBorder,需要6个参数:图+上+下+左+右(填充的像素大小)+填充方式
填充方式如下:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg')
top_size,bottom_size,left_size,right_size = (50,50,50,50)
img_replace = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REPLICATE)
img_reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT)
img_reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
img_wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
img_constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0)
# cv_show('img_wrap',img_wrap)
plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(img_replace,'gray'),plt.title('Replace')
plt.subplot(233), plt.imshow(img_reflect, 'gray'), plt.title('REFLECT')
plt.subplot(234), plt.imshow(img_reflect101, 'gray'), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(img_wrap, 'gray'), plt.title('WRAP')
plt.subplot(236), plt.imshow(img_constant, 'gray'), plt.title('CONSTANT')
plt.savefig('cat_fill.jpg')
plt.show()
import cv2
img_cat = cv2.imread('cat.jpg') # 414x500x3
img_dog = cv2.imread('dog.jpg')
# 普通resize
img_dog = cv2.resize(img_dog,(img_cat.shape[1],img_cat.shape[0]))
cat_dog = img_cat+img_dog
cv2.imshow('cat_dog',cat_dog)
import matplotlib.pyplot as plt
# 放大倍数的resize
big_cat = cv2.resize(img_cat,(0,0),fx=4,fy=4)
long_cat = cv2.resize(img_cat,(0,0),fx=3,fy=1)
plt.subplot(121),plt.imshow(big_cat),plt.title('big_cat')
plt.subplot(122),plt.imshow(long_cat),plt.title('long_cat')
plt.show()
res = cv2.addWeighted(img_cat,0.4,img_dog,0.6,0)
cv_show('res',res)
import cv2
vc = cv2.VideoCapture('test.mp4')
# 检查是否正确打开
if vc.isOpened():
opened, frame = vc.read() # 此时frame中存储的是视频的第一帧图片
cv2.imshow('frame',frame)
else:
open = False # 第一感觉是,是不是写错了,但还就是open
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(100) & 0xFF == 27:
break
vc.release() #关闭相机
cv2.destroyAllWindows() #关闭窗口
ret, dst = cv2.threshold(src, thresh, maxval, type)
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,img_bi = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY)
ret,img_bi_inv = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY_INV)
ret,img_tr = cv2.threshold(img_gray,127,255,cv2.THRESH_TRUNC)
ret,img_zero = cv2.threshold(img_gray,127,255,cv2.THRESH_TOZERO)
ret,img_zero_inv = cv2.threshold(img_gray,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original','Binary','Binary_INV','TRUNC','ZERO','ZERO_INV']
images = [img,img_bi,img_bi_inv,img_tr,img_zero,img_zero_inv]
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()
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lenaNoise.png')
blur = cv2.blur(img,(3,3))
boxFilter = cv2.boxFilter(img,-1,(3,3),normalize=False)
gussian = cv2.GaussianBlur(img,(3,3),1)
median = cv2.medianBlur(img,5)
titles = ['Original','Binary','Binary_INV','TRUNC','ZERO','ZERO_INV']
images = [img,blur,boxFilter,gussian,median]
# 显示1
# for i in range(5):
# plt.subplot(1,5,i+1),plt.imshow(images[i],'gray'),plt.title(titles[i])
# plt.xticks([]),plt.yticks([]) # 不显示坐标轴
# plt.show()
# 显示2
res = np.hstack((blur,gussian,median))
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
腐蚀与膨胀属于形态学操作,所谓的形态学,就是改变物体的形状,形象理解一些:腐蚀=变瘦 膨胀=变胖
主要是采用 cv2.erode() 和 cv2.dilate()
主要针对二值化图像的白色部分
.
腐蚀:是一种消除边界点,使边界向内部收缩的过程
- 通俗讲法:在原图的每一个小区域里取最小值,由于是二值化图像,只要有一个点为0,则都为0,来达到瘦身的目的
- 算法:用 3x3 的 kernel,扫描图像的每一个像素;用 kernel 与其覆盖的二值图像做 “与” 操作;若都为1,则图像的该像素为1;否则为0. 最终结果:使二值图像减小一圈.
膨胀:是将与物体接触的所有背景点合并到该物体中,使边界向外部扩张的过程,可以用来填补物体中的空洞.
- 算法:用 3x3 的 kernel,扫描图像的每一个像素;用 kernel 与其覆盖的二值图像做 “与” 操作;若都为0,则图像的该像素为0;否则为1. 最终结果:使二值图像扩大一圈
.先腐蚀后膨胀的过程称为 开运算。用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积.【cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)】
先膨胀后腐蚀的过程称为 闭运算。用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积.【cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)】
膨胀 - 腐蚀的过程称为 梯度运算。用来计算轮廓【cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)】
顶帽:原始输入 - 开运算结果 【cv2.morphologyEx(img,cv.MORPH_TOPHAT,kernel)】
黑帽:闭运算 - 原始输入 【cv2.morphologyEx(img,cv.MORPH_BLACKHAT,kernel)】
import cv2
import numpy as np
img = cv2.imread('dige.png')
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1) # 迭代次数越多 和 kernel越大 效果越明显
dilate = cv2.dilate(img,kernel,iterations = 1)
res = np.hstack((img,erosion,dilate))
cv_show('dige and erode and dilate',res)
pie = cv2.imread('pie.png')
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)
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_e = np.hstack((pie,erosion_1,erosion_2,erosion_3))
res_d = np.hstack((pie,dilate_1,dilate_2,dilate_3))
# cv2.imshow('res', res_e)
cv2.imwrite('pie_erode_res.jpg',res_e)
cv2.imwrite('pie_dilate_res.jpg',res_d)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
img = cv2.imread('dige.png')
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel) # 开:把刺去掉了
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel) # 闭:字和刺都胖了
result = np.hstack((img,opening,closing))
cv2.imshow('open and close',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 梯度 = 膨胀-腐蚀
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)
gradient = cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)
res = np.hstack((dilate,erosion,gradient))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread('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) # 只剩白边的轮廓
res = np.hstack((img,tophat,blackhat))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.Sobel(src, ddepth, dx, dy, ksize) 进行sobel算子计算
参数说明:src表示当前图片,ddepth表示图片深度,这里使用cv2.CV_64F使得结果可以是负值, dx表示x轴方向,dy表示y轴方向, ksize表示移动方框的大小
cv2.convertScalerAbs(src) 将像素点进行绝对值计算
参数说明: src表示当前图片
sobel算子:分为x轴方向和y轴方向上的,
x轴方向上的算子如图中的Gx,将sober算子在图中进行平移,当前位置的像素值等于sobel算子与(当前位置与周边位置8个点)进行对应位置相乘并相加操作,作为当前位置的像素点(右减左)
y轴方向的算子如Gy, 对于x轴方向上,即左右两边的比较(下减上)
计算方程为:x轴: p3 - p1 + 2 * p6 - 2 * p4 + p9 - p7, 右边的像素值减去左边的像素值
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)# 1,0: 表示只算水平的,不算竖直的
sobelxx = cv2.convertScaleAbs(sobelx)#
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelyy = cv2.convertScaleAbs(sobely)
# 分别计算x和y,再求和,融合的较好
sobelxy_1 = cv2.addWeighted(sobelxx,0.5,sobelyy,0.5,0)
# 不建议直接计算,融合的不好
sobelxy_2 = cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
# cv_show('pie',img)
cv2.imshow('sobelx',sobelx)
cv2.imshow('sobelxx',sobelxx)
cv2.imshow('sobely',sobely)
cv2.imshow('sobelyy',sobelyy)
cv2.imshow('sobelxy_1',sobelxy_1)
cv2.imshow('sobelxy_2',sobelxy_2)
cv2.waitKey(0)
cv2.destroyAllWindows()
白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要用convertScalerAbs取绝对值
注:图sobelx,右半边也有梯度,但是是从黑到白,所以为0(黑),所以看不出来。(图sobely的下半部分同理)
laplacian算子计算方程为:p2 + p4 + p6 +p8 - 4 * p5
Scharr算子和Sobel算子的用法相同,Laplacian算子就不存在x y了
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((img,sobelxy,scharrxy,laplacian))
cv_show(res,'res')
可以看到,Scharr算子比Sobel算子更敏感,捕获更多细节,更丰富
img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100) # 阈值设置的合适,就可以把细节信息展示更多,发丝和细纹理都显现出来了
res = np.hstack((img,v1,v2))
cv_show(res,'Canny_res')
img=cv2.imread("car.png",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,120,250)
v2=cv2.Canny(img,50,100) # 阈值设置的合适,就可以把细节信息展示更多,高楼的边缘也显现出来了
res = np.hstack((img,v1,v2))
cv_show(res,'res')
图像金字塔是一组图像的集合,集合中的所有图像都是通过对某一图像连续降采样得到的一组图像序列。
有两种经典的金字塔:高斯金字塔和拉普拉斯金字塔,前者采用向下采样,后者是向上采样需要的缺失的信息。
向下采样(生成高斯金字塔)的具体操作为: 从大到小
1. 对图像进行高斯卷积
2. 删除所有的偶数行和偶数列
向上采样的缺失信息(生成拉普拉斯金字塔)的具体操作为:从小到大
1. 首先将维数扩大两倍
2. 将扩大位的值置为0
3. 对新的图像进行高斯卷积
4. 用新的层次的高斯金字塔减去 3 中形成的图像
import cv2
import numpy as np
img = cv2.imread('AM.png')
up = cv2.pyrUp(img) # 上采样
down = cv2.pyrDown(img) # 下采样
cv2.imshow('img',img)
cv2.imshow('up',up)
cv2.imshow('down',down)
cv2.waitKey(0)
cv2.destroyAllWindows()
先上采样,再下采样,不能恢复原图的清晰度
up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(up_down,'up_down')
cv_show(np.hstack((img,up_down)),'up_down')
cv_show(img-up_down,'img-up_down') # 前面两图的差异
method:轮廓逼近方法
返回值
如果报错
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
ValueError: not enough values to unpack (expected 3, got 2)
.要想返回三个参数:
把OpenCV 降级成3.4.3.18 就可以了,在终端输入pip install opencv-python==3.4.3.18
OpenCV 新版返回两个参数:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
.
查看opencv版本:print(cv2.version)
我的opecv版本为: 4.2.0
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')
# 计算轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 绘制轮廓
draw_img1 = img.copy() # 需进行copy,因drawContours会在原图上绘制,改变原图
draw_img2 = img.copy()
draw_img3 = img.copy()
all_contours = cv2.drawContours(draw_img1, contours, -1, (0, 0, 255), 2) # -1是指所有的轮廓 BGR 线条宽度
contours_0 = cv2.drawContours(draw_img2,contours,0,(0,0,255),2) # 检测到的第1个轮廓
contours_1 = cv2.drawContours(draw_img3,contours,1,(0,0,255),2) # 检测到的第2个轮廓
cv_show('res',np.hstack((all_contours,contours_0,contours_1)))
cv2.drawContours
参数如下:传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
cnt = contours[0]
#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)
算法步骤 :
- 连接曲线首尾两点A、B形成一条直线AB; 计算曲线上离该直线段距离最大的点C,计算其与AB的距离d;
- 比较该距离与预先给定的阈值threshold的大小,如果小于threshold,则以该直线作为曲线的近似,该段曲线处理完毕。
- 如果距离大于阈值,则用点C将曲线分为两段AC和BC,并分别对两段曲线进行步骤[1~3]的处理。
- 当所有曲线都处理完毕后,依次连接各个分割点形成折线,作为原曲线的近似。
img = cv2.imread('contours2.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)
cnt = contours[0]
epsilon1 = 0.05*cv2.arcLength(cnt,True) # arcLength周长 阈值d一般按照轮廓周长百分比计算,值越小,轮廓与原来变化越小
epsilon2 = 0.1*cv2.arcLength(cnt,True)
epsilon3 = 0.15*cv2.arcLength(cnt,True)
approx1 = cv2.approxPolyDP(cnt,epsilon1,True)
approx2 = cv2.approxPolyDP(cnt,epsilon2,True)
approx3 = cv2.approxPolyDP(cnt,epsilon3,True)
draw_img1 = img.copy()
draw_img2 = img.copy()
draw_img3 = img.copy()
approx1_res = cv2.drawContours(draw_img1, [approx1], -1, (0, 0, 255), 2)
approx2_res = cv2.drawContours(draw_img2, [approx2], -1, (0, 0, 255), 2)
approx3_res = cv2.drawContours(draw_img3, [approx3], -1, (0, 0, 255), 2)
cv_show('res',np.hstack((approx1_res,approx2_res,approx3_res)))
# 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)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt_2 = contours[2]
x,y,w,h = cv2.boundingRect(cnt_2)# 得到 矩形边框
img_rec = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)#在原图上画出矩形边框
cv_show('img_rec',img_rec)
# 轮廓面积与边界矩形比
area = cv2.contourArea(cnt)#轮廓面积
rect_area = w * h #矩形边框面积
extent = float(area) / rect_area
# print ('轮廓面积与边界矩形比',extent)
# 外接圆
cnt_8 = contours[8]
(x,y),radius = cv2.minEnclosingCircle(cnt_8)
center = (int(x),int(y))
radius = int(radius)
img_cir = cv2.circle(img,center,radius,(0,255,255),2)
cv_show('img_cir',img_cir)
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后格每次计算的結果放入一个矩阵里,作为結果输出.假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)
简单来说,模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域。
其中method:
TM-SQDIFF:计算平方不同,计算出来的值越小,越相关
TM_CCORR:计算相关性,计算出来的值越大,越相关
TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
TM SQDIFF-NORMED: 计算归一化平方不同,计算出来的值越接近0,越相关
TM_CCORR-NORMED: 计t算归一化相关性,计算出来的值越接近1,越相关
TM-CCOEFF-NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
公式:https://docs.opencv.org/3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d
https://blog.csdn.net/a906958671/article/details/89551856ps:加归一化的模式都比较准确
import cv2
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
img = cv2.imread('touxiang.jpg',0)
img2 = img.copy()
template = cv2.imread('template.jpg',0)
w, h = template.shape[::-1]
# All the 6 methods for comparison in a list
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
for meth in methods:
img = img2.copy()
method = eval(meth)
# Apply template Matching
res = cv2.matchTemplate(img,template,method) # method这不要写成字符串的形式,别加上引号, cv2.xx就行
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
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(img,top_left, bottom_right, 255, 2)
plt.subplot(121),plt.imshow(res,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img,cmap = 'gray')
plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
最亮的位置,就是找到的 接近人脸框的位置
匹配多个对象
img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, 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)
for pt in zip(*loc[::-1]): # *号表示可选参数
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
直方图(histogram)是灰度级的函数,描述的是图像中每种灰度级像素的个数,反映图像中每种灰度出现的频率。横坐标是灰度级,纵坐标是灰度级出现的频率
import cv2 #opencv读取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
img = cv2.imread('cat.jpg',0) #0表示灰度图
hist = cv2.calcHist([img],[0],None,[256],[0,256])
# hist.shape # (256,1) 256指0-255的256个像素; 1指1维,像素x 多少多少个
plt.hist(img.ravel(),256); # 此处不显示图像,用plt展示方便,若用cv2,要进行颜色转换
plt.show()
分别显示3个颜色通道的直方图
img = cv2.imread('cat.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
img = cv2.imread('cat.jpg', 0)
# 创建mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
cv_show(mask,'mask')
masked_img = cv2.bitwise_and(img, img, mask=mask) #与操作
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('cat.jpg',0) #0表示灰度图 #clahe
equ = cv2.equalizeHist(img)
res = np.hstack((img,equ))
cv_show(res,'res')
自适应直方图均衡化
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')
我们生活在时间的世界中,早上7:00起来吃早饭,8:00去挤地铁,9:00开始上班。。。以时间为参照就是时域分析。
但是在频域中一切都是静止的!
https://zhuanlan.zhihu.com/p/19763358
傅里叶变换的作用
高频:变化剧烈的灰度分量,例如边界
低频:变化缓慢的灰度分量,例如一片大海
在原图中做低频/高频的变换较难,因此转换到频域中处理较方便
滤波
低通滤波器:只保留低频,会使得图像模糊
高通滤波器:只保留高频,会使得图像细节增强
opencv中主要就是 cv2.dft() 和 cv2.idft() ,输入图像需要先转换成np.float32 格式。
得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现。
cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255), 用逆变换cv2.idft()。
img = cv2.imread('lena.jpg',0)
# 1.转换成np.float32 格式
img_float32 = np.float32(img)
# 2.傅里叶变换 dft
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
# 3.np的fftshift得到低频在中心位置的频谱图
dft_shift = np.fft.fftshift(dft)
# 4.转换一下 得到灰度图能表示的形式
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1])) # 0 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()
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('lena.jpg',0)
# 1.转换成np.float32 格式
img_float32 = np.float32(img)
# 2.傅里叶变换 dft
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
# 3.np的fftshift得到低频在中心位置的频谱图
dft_shift = np.fft.fftshift(dft)
rows, cols = img.shape
crow, ccol = int(rows/2) , int(cols/2) # 中心位置
# 4.低通滤波
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1 # 所以计算图像长宽是为制作mask,中心(30+30)x(30x30)区域为1
# 5.IDFT
fshift = dft_shift*mask # 仅保留了中心区域
f_ishift = np.fft.ifftshift(fshift) # 把中心位置的东西放回原位 ifftshift
img_back = cv2.idft(f_ishift) # 从频谱图转换回来 idft
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()
同理,则代码不详细备注
仅mask[crow-30:crow+30, ccol-30:ccol+30] = 0与上面不同
因为保留高频
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()