直方图均衡化
yes,听名字直方图均衡化,直方图是对图片像素的统计,均衡化就是平均一下,什么意思呢,比如一张图片白的很白,黑的很黑,这样就不好,把峰值往下平摊,都得到一些值,这样可以提高对比度,对于一些高亮或者暗的环境处理好。
看代码
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\testp.png"
img=cv.imread(path,0)
rows,cols=img.shape[:2]
cv.imshow("1",img)
zhifang=cv.equalizeHist(img)
cv.imshow("2",zhifang)
cv.waitKey(0)
cv.destroyAllWindows()
上面的直方图均衡有时候将细节忽略掉,所以出来了自适应直方图均衡化
它把图片分成一个个小块,进行直方图均衡化,并且会设置上限阈值来分去比较大的像素值分给其他bins中,也就是增加对比度,但不是很夸张。
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\testp.png"
img=cv.imread(path,0)
rows,cols=img.shape[:2]
cv.imshow("1",img)
cl=cv.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
cll=cl.apply(img)
cv.imshow("2",cll)
cv.waitKey(0)
cv.destroyAllWindows()
边沿检测
分为基于搜索导数的最大值和基于二阶导数的过零点值。
sobel算子 利用导数最大值来寻找
[-1,0,+1],[-2,0,2],[-1,0,+1]是水平变化做卷积
[-1,-2,-1],[0,0,0],[1,2,1]是垂直变化做卷积
结合两个结果的平方和的开根号,统计极大值位置也就是边缘。
内核为3的时候,sobel有缺陷,用schar算子弥补,但schar只使用在内核3的卷积,ksize为1,3,5,7
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\testp.png"
img=cv.imread(path,0)
rows,cols=img.shape[:2]
cv.imshow("1",img)
x1=cv.Sobel(img,cv.CV_16S,1,0)
y1=cv.Sobel(img,cv.CV_16S,0,1)
xx1=cv.convertScaleAbs(x1)
yy1=cv.convertScaleAbs(y1)
r1=cv.addWeighted(xx1,0.5,yy1,0.5,0)
#下面是schar算子
x2=cv.Sobel(img,cv.CV_16S,1,0,ksize=-1)
y2=cv.Sobel(img,cv.CV_16S,0,1,ksize=-1)
xx2=cv.convertScaleAbs(x2)
yy2=cv.convertScaleAbs(y2)
r2=cv.addWeighted(xx2,0.5,yy2,0.5,0)
cv.imshow("2",r1)
cv.imshow("3",r2)
cv.waitKey(0)
cv.destroyAllWindows()
拉普拉斯算子(过零点二阶导数)
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\testp.png"
img=cv.imread(path,0)
rows,cols=img.shape[:2]
cv.imshow("1",img)
lab=cv.Laplacian(img,cv.CV_16S,ksize=5)
s=cv.convertScaleAbs(lab)
cv.imshow("s",s)
cv.waitKey(0)
cv.destroyAllWindows()
这里也需要用到类型转换,我还发现通过改变ksize的大小,效果也明显不同,我调大了ksize的值会让边界线更粗厚
一种牛逼的算子 --- canny算子(最优算法)
这边缘检测分为四步进行
第一步:噪声去除
用5x5高斯滤波器先滤去噪点
第二步:和sobel相近,去计算梯度和一阶导数,梯度有值和方向。
第三步:再次扫描整幅图像去除非边界点,看每个点梯度是不是周围方向最大的,如果是就保留,不是则抑制成细边
最后一步:滞后阈值,设置两个阈值,梯度高于这个才是真的边界,在中间的若和真的边界相连也保留,若不相邻则是假的边界去除。
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\testp.png"
img=cv.imread(path,0)
rows,cols=img.shape[:2]
cv.imshow("1",img)
s=cv.Canny(img,0,20)
cv.imshow("s",s)
cv.waitKey(0)
cv.destroyAllWindows()
经过实验,确实效果是最佳的!边界平滑并且美丽
模板匹配
在openmv也学了模板匹配,就是先存一个照片当模板,然后去找图片中和模板最相似的。
原理是什么呢?其实是模板这个照片在原图像的滑动,匹配每个像素点,从左上一个一个像素点滑动到右下,把相似结果保存在矩阵中,获取最大值位置后,那么该位置是最匹配的,对应的就是模板的顶点,长宽和模板大小一样的。有三种匹配方式
cv.tm.sqdiff cv.tm.ccorr cv.tm.ccoeff 第一个是越小越好,后两个是越大越好
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\chenhao.jpg"
img=cv.imread(path)
rows,cols=img.shape[:2]
cv.imshow("1",img)
path1=r"F:\yanjing.png"
yanjing=cv.imread(path1)
h,w,l=yanjing.shape
r=cv.matchTemplate(img,yanjing,cv.TM_SQDIFF)
min_val,max_val,min_lo,max_lo=cv.minMaxLoc(r)
cv.rectangle(img,min_lo,(min_lo[0]+w,min_lo[1]+h),(0,0,255),2)
cv.imshow("2",img)
cv.imshow("3",yanjing)
cv.waitKey(0)
cv.destroyAllWindows()
经过实验,不同放大找出来的位置有些不同,说明不同方法找出来的效果是很不一样的感觉,我用后两种方法找不出对应的模板,换成了第一种方法找到了模板.
cv.rectangle(img,m[2],(m[2][0]+w,m[2][1]+h),(0,0,255),2)这样也是行的通的
霍夫变换
首先是霍夫线变换
创建一个rho和theta的累加器二维数组,行数应与对角线长度相同,取一个点遍历所有theta的值的rho值,在累加器上加1,搜索累加器中最大值,对应的rho和theta就是那条直线。
在进行检测之前必须让图像变成二值化,或者先进行canny检测。
import cv2 as cv
import math
import numpy as np
import matplotlib.pyplot as plt
path=r"F:\kaibi.png"
img=cv.imread(path)
rows,cols=img.shape[:2]
cv.imshow("1",img)
edge=cv.Canny(img,80,100)
cv.imshow("2",edge)
lines=cv.HoughLines(edge,0.1,np.pi/180,80)
for line in lines:
xinxi=line[0]
a=np.cos(xinxi[1])
b=np.sin(xinxi[1])
x0=a*xinxi[0]
y0=b*xinxi[0]
x1 = int(x0 + 1000 * (-b))
x2 = int(x0 - 1000 * (-b))
y1 = int(y0 + 1000 * (-a))
y2 = int(y0 - 1000 * (-a))
cv.line(img,(x1,y1),(x2,y2),(0,0,255))
cv.imshow("3",img)
print(lines)
cv.waitKey(0)
cv.destroyAllWindows()
这里看一下lines具体是什么:
[[[322.95 1.5707964]]
[[ 25.95 1.5707964]]
[[250.95 1.5707964]]
[[526.95 0. ]]
[[ 4.9500003 0. ]]
[[ 97.950005 1.5707964]]
[[291.95 0. ]]
[[212.95 0. ]]
[[ 76.950005 0. ]]
[[382.95 0. ]]
[[177.95 1.5707964]]
[[161.95 1.5707964]]
[[160.95 1.5707964]]
[[178.95 1.5707964]]]
是检测到的rho和theta的值,xinxi[0]是rho,xinxi[1]是theta
这里为什莫xinxi等于line[0]呢?这是因为for 循环只能去一个括号,也就是line等于[[322.95 1.5707964]],这样的话line[0]才是里面的两个值。
还有0.1 np.pi/180是rho和theta的精度,80是阈值,大于这个值才会认为是直线,可以调整这个值来忽略短一点的直线。