2020-06-21-image-process-canny

Record

Posted by Jocelyn on June 21, 2020

2020-06-21-recording.

图像金字塔

# -*- coding: UTF-8 -*-
import cv2
import numpy as np
img = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg')
img = img[300:500,300:500]
def cv_show(image,name):
    cv2.imshow(name,image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
# cv_show(img,'ori')

up = cv2.pyrUp(img)#上采样
down = cv2.pyrDown(img)#下采样
#经过上采样 再进行下采样 会损失两次信息,因为是用0填充的

#拉普拉斯变换第一层结果
down = cv2.pyrDown(img)
down_up = cv2.pyrUp(down);
l_1 = img-down_up
# cv_show(l_1,'ttest')

图像轮廓

#图像轮廓
#1. 为了更高的准确率,使用二值图像
img = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,155,cv2.THRESH_BINARY)#小于127的设置为0,大于=127的设置为255
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
#cv2.findContours(img,mode,method)
#mode:轮廓检索模式
#这个建议使用 RETR_TREE,会检索所有轮廓,并且所有轮廓有个嵌套层次
#method:轮廓逼近方法
#CHAIN_APPROX_NONE:会输出整个轮廓,比如正方形就是四条边
#CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,正方形可能最后只留下四个点

#绘制轮廓
#cv2.drawContours(要绘制的图像,轮廓,轮廓索引,颜色模式,线条厚度)
#draw = img
draw = img.copy()#这里需要注意的是,drawContours会修改原图,所以需要复制一份,但是如果仅使用上面一句话是不足的,draw和img还是相同的
res = cv2.drawContours(draw,contours,-1,(0,0,255),2)# -1表示所有轮廓,(0,0,255)BGR
# cv_show(res,'img')

#轮廓特征
cnt = contours[0]
#面积
cv2.contourArea(cnt)
#周长
cv2.arcLength(cnt,True)#True表示是闭合的

#轮廓近似
imgs = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg')
imgs = imgs[100:600,300:800]

gray = cv2.cvtColor(imgs,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)
res = cv2.drawContours(imgs,contours,-1,(0,0,255),2)
# cv_show(res,'ori')
# cnt = contours[0]
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(contours[2],epsilon,True)#近似函数

res = cv2.drawContours(thresh,[approx],-1,(0,0,255),2)
# cv_show(res,'approx')#因为图片太复制,所以轮廓较多,所以单独画出来一个不太明显

#边界矩形 外接正方形 内接正方形
cnt = contours[2]#某个轮廓
#如何把这个外接矩形画出来
x,y,w,h=cv2.boundingRect(cnt)
img1 = cv2.rectangle(imgs,(x,y),(x+w,y+h),(0,0,255),2)
#这个还是将矩形画在了原来的图片上面

area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area

#外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img2 = cv2.circle(imgs,center,radius,(0,0,255),2)

模板匹配

#模板匹配
#模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与被覆盖图像的差别程度,这个差别程度的计算方法在opencv中有6种,输出是作为一个矩阵来输出的
#假如原图是AxB的大小,模板是axb的大小,那么输出结果的矩阵就是(A-a+1)x(B-b+1)
#模板匹配

#这里用的都是灰度图
image = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg',0)
#print(image.shape)#778x514
image = image[100:500,100:500]
template = image.copy()
template = template[100:300,100:300]
temp = image.copy()
temp = temp[100:300,300:500]#这里的temp的shape会变成200,100,因为temp现在是个新图片
#它的长宽为400 400,所以没有500的索引,但是如果是[100:300,100:300]shape还是会是200 200
# print(temp.shape)
h,w = template.shape[:2]
# print(template.shape)

res = cv2.matchTemplate(image,template,cv2.TM_SQDIFF_NORMED)
#TM_SQDIFF:
#TM_CCORR:
#TM_CCOEFF:主要注意下面进行了归一化的版本
#TM_SQDIFF_NORMED:计算归一化平方,计算出来的值越接近0,越相关
#TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
#TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res)
#会返回最小值 最大值,最小值坐标位置,最大值坐标位置
#通过合适的位置,画出宽高就可以得到识别框

#如果想要匹配多个对象
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(image,pt,bottom_right,(0,0,255),2)

直方图及均衡化

#直方图
#cv2.calcHist(images,channels,mask,histSize,ranges)
#images:原图像图像格式为uint8或者float32,当传入函数时 需要使用[]
#channels,需要用[]来告诉函数接口,[0]表示是灰度图,如果是彩色图像可以传入[0][1][2]分别对应着BGR
#mask:掩模图像,如果想要统计整个图片,输入为None,如果想要对图片中的部分进行操作,需要使用掩模
#histSize:bin的数目,应该用[]括起来
#ranges:像素值范围,通常为[0-256]
img3 = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg',0)
hist = cv2.calcHist([img3],[0],None,[256],[0,256])
import matplotlib.pyplot as plt
# plt.hist(img3.ravel(),256)
# plt.show()

img4 = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg')
color = ('b','g','r')
# for i,col in enumerate(color):
#     histr = cv2.calcHist([img4],[i],None,[256],[0,256])
#     plt.plot(histr,color= col)
#     plt.xlim([0,256])
# plt.show()

#mask操作
#创建mask
mask = np.zeros(img4.shape[:2],np.uint8)
mask[100:300,100:300]=255
masked_img = cv2.bitwise_and(img4,img4,mask = mask)#与操作
# cv_show(masked_img,'masked_img')

#直方图均衡化
img5 = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg',0)
euq = cv2.equalizeHist(img5)
# plt.hist(euq.ravel(),256)
# plt.shapeow()
#直方图均衡化 会丢失一些信息,所以可以考虑分区域进行直方图均衡化
#自适应直方图均衡化,分为小格子进行直方图均衡化,但是边缘接口也会进行处理的
clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
res_clahe = clahe.apply(img5)
# cv_show(res_clahe,'res')

傅里叶变换

#傅里叶变换
#以时间为参考,时域
#在频域中一切都是静止的

#傅里叶变换的作用
#高频:变化剧烈,比如边界
#低频:变化缓慢,比如一片大海

#滤波:
#低通滤波器:只保留低频,图片变得模糊
#高通滤波器:只保留高频,图片细节增强

#opencv主要是cv2.dft()和cv2.idft()输入的图片需要先转换为np.float32格式
#得到的结果中 频率为0的部分会在左上角,通常需要转换到中心位置,可以通过shift变化来实现
#cv2.dft()返回的结果是双通道的(实部 虚部),通常需要转换成图片格式才能展示(0 255)

img6 = cv2.imread('/Users/syy/learn/repositories/blogPost/JocelynHub.github.io/img/post-sample-image.jpg',0)
img_float32 = np.float32(img6)
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.imshow(magnitude_spectrum,cmap='gray')
# plt.show()
#低频在中间,可以用低通滤波器,利用类似掩模,进行低通滤波

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

#低通滤波器
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30,ccol-30:ccol+30]=1
#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.imshow(img_back,cmap='gray')
# plt.show()
#转换到频域 去得到 低频和高频 比较简单