1. 直线检测
Hough Line Transform:前提:边缘检测已经完成,基于霍夫变换
1.1 原理
可以通过(theta,r)唯一表示一条直线 把过三个点的全部直线以某一角度全部计算出来,如果三个点的直线有相同的,则说明有一条直线过了这三个点。
至于为啥用(theta, r)而不是斜率k和截距b来表示一条直线,是因为利用y = kx + b来表示直线时,存在斜率k无穷大的情况,无法计算。并且theta为0到2*pi, 且对于直线来说r一定小于等于b,所以计算的数值也相对较小。 参考博客
def line_detection(image):
blurred = cv.GaussianBlur(image, (3, 3), 0)
gray =cv.cvtColor(image,cv.COLOR_BGR2GRAY)
edges=cv.Canny(image,50,150,apertureSize=3)
lines =cv.HoughLines(edges,1,np.pi/180,200)
for line in lines:
rho,theta=line[0]
a=np.cos(theta)
b=np.sin(theta)
x0=a*rho
y0=b*rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)
cv.imshow("line detect image",image)
def line_detection_possible(image):
gray =cv.cvtColor(image,cv.COLOR_BGR2GRAY)
edges=cv.Canny(gray,50,150,apertureSize=3)
lines =cv.HoughLinesP(edges,1,np.pi/180,200,minLineLength=20,maxLineGap=5)
for line in lines:
x1,y1,x2,y2=line[0]
cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)
cv.imshow("line detect possible image",image)
2. 圆检测
直线检测、圆检测:基于霍夫变换 霍夫圆检测对噪声比较敏感,所以要先进行滤波(去噪) 霍夫圆检测基于梯度变换:
- 检测边缘,发现可能的圆心
- 从候选圆心开始计算最佳半径大小
def detect_CIRCLES(image):
dst=cv.pyrMeanShiftFiltering(image,10,100)
img=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)
circles=cv.HoughCircles(img,cv.HOUGH_GRADIENT,1,20,param1=50,param2=55,minRadius=0,maxRadius=0)
circles=np.uint16(np.around(circles))
for i in circles[0,:]:
cv.circle(image,(i[0],i[1]),i[2],[0,0,255],2)
cv.circle(image,(i[0],i[1]),2,[255,0,0],2)
cv.imshow("circles:",image)
cv.imshow("circles1:",dst)
3. 对象测量
- cv.findContours():获取轮廓
- cv.contourArea() :#计算轮廓面积
- cv.boundingRect() :计算轮廓外接矩形
- cv.moments() : 计算轮廓的几何矩
- approxPolyDP(curve, epsilon, closed, approxCurve=None):多边形逼近函数
参数curve表示输入的点集,直接使用轮廓点集contour 参数epsilon表示指定的精度,即原始曲线与近似曲线之间的最大距离 参数closed表示若为true,则说明近似曲线是闭合的,反之,若为false,则断开 参数approxCurve表示输出的点集,当前点集是能最小包容指定点集的,画出来即是一个多边形
def measure_object(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
dst = cv.cvtColor(binary, cv.COLOR_GRAY2BGR)
print('threshold value: %s' % ret)
cv.imshow('binary image', binary)
image, contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i, contour in enumerate(contours):
area = cv.contourArea(contour)
print('area', area)
x, y, w, h = cv.boundingRect(contour)
rate = min(w, h) / max(w, h)
print('rate', rate)
mm = cv.moments(contour)
print(type(mm))
cv.rectangle(dst, (x, y), (x + w, y + h), (0, 0, 255), 2)
approxCurve = cv.approxPolyDP(contour, 4, True)
"""
approxPolyDP(curve, epsilon, closed, approxCurve=None)
多边形逼近函数
参数curve表示输入的点集,直接使用轮廓点集contour
参数epsilon表示指定的精度,即原始曲线与近似曲线之间的最大距离
参数closed表示若为true,则说明近似曲线是闭合的,反之,若为false,则断开
参数approxCurve表示输出的点集,当前点集是能最小包容指定点集的,画出来即是一个多边形
"""
print(approxCurve.shape)
if approxCurve.shape[0] >6:
cv.drawContours(dst, contours, i, (0, 255, 0), 2)
if approxCurve.shape[0] == 4:
cv.drawContours(dst, contours, i, (0, 0, 255), 2)
if approxCurve.shape[0] == 3:
cv.drawContours(dst, contours, i, (255, 0, 0), 2)
if approxCurve.shape[0] == 5:
cv.drawContours(dst, contours, i, (255, 255, 0), 2)
cv.imshow("measure-contours", dst)
4.膨胀和腐蚀
- 膨胀:最大值替换中心像素;cv.dilate
- 腐蚀:最小值替换中心像素 cv.erode
- 膨胀和腐蚀支持任意形状的结构元素
- 3x3、5x5的结构元素或者模板
废话不多说,看效果:如下图对二值图像进行腐蚀和膨胀的效果(如字面意思) 对彩色图像的腐蚀和膨胀效果直观来看其实就是变暗和变亮? 完整代码:
import cv2 as cv
import numpy as np
data_path='C:\\Users\\22852\\Desktop\\opencv\\data\\'
result_path='C:\\Users\\22852\\Desktop\\opencv\\result\\'
def erode_demo(image):
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
cv.imshow("binary:", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
dst = cv.erode(binary,kernel)
cv.imshow("eroid:",dst)
def dilate_demo(image):
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
cv.imshow("binary:",binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
dst = cv.dilate(binary,kernel)
cv.imshow("dilate:",dst)
src = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\handwriting.jpeg')
src1 = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\input.jpg')
cv.imshow("input image:", src)
cv.imshow("input image1:", src1)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5))
dst1 =cv.erode(src1,kernel)
dst2 =cv.dilate(src1,kernel)
cv.imshow("erode image:", dst1)
cv.imshow("dilate image:", dst2)
erode_demo(src)
dilate_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
5.开闭操作
cv.morphologyEx(binary,cv.MORPH_OPEN,kernel) cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel)
- 开操作:腐蚀+膨胀,运用在灰度图、二值图像分选中,去除小的干扰块,尽量保证其他地方无变化
- 闭操作:膨胀+腐蚀,运用在灰度图、二值图像分选中,填充封闭区域,尽量保证其他地方无变化
- 还可以进行 水平、垂直线提取
开操作效果: 闭操作效果(没有很好的素材)
def open_demo(image):
dst = cv.GaussianBlur(image, (5,5), 0)
gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)
rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
cv.imshow("binary:", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
dst = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)
cv.imshow("open:",dst)
cv.imwrite(result_path+"y_open.jpg:",dst)
def close_demo(image):
dst = cv.GaussianBlur(image, (5, 5), 0)
gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)
rect,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
cv.imshow("binary:",binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(15,15))
dst = cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel)
cv.imshow("close:",dst)
src = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\y.jpg')
src1 = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\lines.jpg')
cv.imshow("input image:", src)
open_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
6.其他形态学操作
- 顶帽:原图与开操作之间的差值 得到闭合图像外的杂质?
- 黑帽:原图与闭操作之间的差值 得到原本闭合图像里没被填充的部分(杂质)?
- 形态学梯度(基本梯度):膨胀后的图像减去腐蚀后的图像得到的梯度
- 内梯度:原图-腐蚀后
- 外梯度:膨胀后-原图
顶帽:
def top_hat_demo(image):
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
rect, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
dst = cv.morphologyEx(gray,cv.MORPH_TOPHAT,kernel)
dst1 = cv.morphologyEx(binary,cv.MORPH_TOPHAT,kernel)
cimage = np.array(gray.shape, np.uint8)
cimage = 75
dst = cv.add(dst, cimage)
cv.imshow("top_hat:",dst1)
黑帽:
ef Black_hat_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
rect, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(15,15))
dst = cv.morphologyEx(gray,cv.MORPH_BLACKHAT,kernel)
dst1 = cv.morphologyEx(binary,cv.MORPH_BLACKHAT,kernel)
cimage = np.array(gray.shape, np.uint8)
cimage = 55
dst=cv.add(dst,cimage)
cv.imshow("Blackhat_hat:",dst1)
形态学梯度:
def Gradient_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
rect, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
dst = cv.morphologyEx(gray,cv.MORPH_GRADIENT,kernel)
dst1 = cv.morphologyEx(binary,cv.MORPH_GRADIENT,kernel)
cimage = np.array(gray.shape, np.uint8)
cimage = 55
dst=cv.add(dst,cimage)
cv.imshow("Gradient:",dst)
7. 分水岭算法
在该算法中,空间上相邻并且灰度值相近的像素被划分为一个区域。 分水岭算法的整个过程:
- 把梯度图像中的所有像素按照灰度值进行分类,并设定一个测地距离阈值。
- 找到灰度值最小的像素点(默认标记为灰度值最低点),让threshold从最小值开始增长,这些点为起始点。
- 水平面在增长的过程中,会碰到周围的邻域像素,测量这些像素到起始点(灰度值最低点)的测地距离,如果小于设定阈值,则将这些像素淹没,否则在这些像素上设置大坝,这样就对这些邻域像素进行了分类。
- 随着水平面越来越高,会设置更多更高的大坝,直到灰度值的最大值,所有区域都在分水岭线上相遇,这些大坝就对整个图像像素的进行了分区。
在OpenCV中,我们需要给不同区域贴上不同的标签。用大于1的整数表示我们确定为前景或对象的区域,用1表示我们确定为背景或非对象的区域,最后用0表示我们无法确定的区域。然后应用分水岭算法,我们的标记图像将被更新,更新后的标记图像的边界像素值为-1。
分水岭算法步骤:
- 模糊去噪
- 灰度、二值化图像
- morphology operation:开闭操作得到确定的背景区域
- distance transform:通过距离转换得到确定的前景区域,剩余部分为不确定区域
- 对确定的前景图像进行连接组件处理,得到标记图像
- watershed transform:cv.watershed()根据标记图像对原图像应用分水岭算法,更新标记图像
def watershed_demo(src):
blur = cv.pyrMeanShiftFiltering(src, 5, 100)
gray = cv.cvtColor(blur, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
cv.imshow("binary", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
mb = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel, iterations=2)
sure_bg = cv.morphologyEx(mb, cv.MORPH_CLOSE, kernel, iterations=3)
cv.imshow("morphology operation", sure_bg)
dist = cv.distanceTransform(mb, cv.DIST_L2, 3)
dist_output = cv.normalize(dist, 0, 1, cv.NORM_MINMAX)
cv.imshow("distance_t", dist_output*50)
ret, surface = cv.threshold(dist, dist.max()*0.6, 255, cv.THRESH_BINARY)
cv.imshow("surface", surface)
surface_fg = np.uint8(surface)
unknown = cv.subtract(sure_bg, surface_fg)
ret, markers = cv.connectedComponents(surface_fg)
print("ret =", ret)
markers = markers +1
markers[unknown == 255] = 0
markers = cv.watershed(src, markers = markers)
src[markers == -1] = [0, 0, 255]
cv.imshow("result", src)
8. 案例:数字验证码识别
import cv2 as cv
import numpy as np
from PIL import Image
def recognize_txt(image):
gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY)
rect,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
cv.imshow("binary:", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(2,1))
binary1 = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)
cv.imshow("binary1:",binary1)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 2))
binary2 = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel)
cv.imshow("binary2:", binary2)
cv.bitwise_not(binary2,binary2)
txt_image=Image.fromarray(binary2)
text = tess.image_to_string(txt_image)
print("识别结果:%s"%text)
src = cv.imread("data/yzm.jpg")
cv.imshow("input image:", src)
recognize_txt(src)
cv.waitKey(0)
cv.destroyAllWindows()
9.案例:人脸检测
需要调用opencv官方的库,比如说”haarcascade_frontalface_alt_tree.xml“ 把官方库下载到本地用cv.CascadeClassifier加载
import cv2 as cv
import numpy as np
def face_detect(image):
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
face_detector= cv.CascadeClassifier("C:\\Users\\22852\\Desktop\\opencv\\opencv-3.3.1\\data\\haarcascades\\haarcascade_frontalface_alt_tree.xml")
faces=face_detector.detectMultiScale(gray,1.02,5)
for x,y,w,h in faces:
cv.rectangle(image,(x,y),(x+w,y+h),(0,255,255),2)
cv.imshow("result:",image)
src = cv.imread('C:\\Users\\22852\\Desktop\\opencv\\data\\face.jpg')
cv.namedWindow("input image:",cv.WINDOW_AUTOSIZE)
cv.imshow("input image:", src)
face_detect(src)
也可以直接调摄像头进行视频检测~
import cv2 as cv
import numpy as np
def face_detect(image):
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
face_detector= cv.CascadeClassifier("C:\\Users\\22852\\Desktop\\opencv\\opencv-3.3.1\\data\\haarcascades\\haarcascade_frontalface_alt_tree.xml")
faces=face_detector.detectMultiScale(gray,1.02,5)
for x,y,w,h in faces:
cv.rectangle(image,(x,y),(x+w,y+h),(0,255,255),2)
cv.imshow("result:",image)
capture = cv.VideoCapture(0)
while(True):
ret,frame = capture.read()
frame=cv.flip(frame,1)
face_detect(frame)
c=cv.waitKey(10)
if c==27:
break
|