Python+Opencv实现多种形状的检测
目录
一、Hough变换是什么?
Hough变换是由 P.V.C.Hough提出的一种算法,这种算法可以快速、准确的检测出图片中的直线、圆和椭圆等多种形状。在计算机视觉领域中得到了广泛的使用。
二、Hough变换原理简介
Hough变换是一种特征提取算法,它已经被广泛的应用在机器视觉、图像分析和影像处理等多个方面。Hough变换的主要用途包括寻找图像中直线、圆或椭圆等在图像中的具体位置,其主要的原理是将图像转换到Hough变换空间中,将直角坐标系中的点转换到极坐标系中,通过数学关系的推导,我们可以得到直角坐标系中的直线对应到极坐标系中就是多条曲线的交点,即图像空间中的直线检测问题转化到Hough空间就成了检测曲线的汇集点点的问题。
Hough变换的基本思想是基于点和线的对偶关系,图像空间XY中,所有通过点(x,y)的点所表示的方程为:;其中P表示的是直线的斜率,q表示直线的截距,也可以表示为;如下图所示,图像空间中过点的直线方程可以表示成;也可以在参数空间中表示为。
通过上图我们可以得出图像空间*线的点和参数空间中的相交的线具有一一对应的关系;参数空间中相交于同一点的所有直线和图像空间中贡献的点相互对应,这就是所谓的点和线之间的对偶关系。Hough变换就是按照这种关系来将图像空间中的检测问题转变成参数空间中寻找交叉点,然后在参数空间中执行简单的累计统计来完成直线的检测任务。当直线接近于竖直方向时,此时我们用极坐标方程来表示该直线。具体的方程可以表示为;根据上式,我们可以得出对于极坐标系中的任意一组点都对应这一条直线,即这里将点和线的对偶关系转变成了点和正弦曲线的对偶关系。如下图所示,图a表示的是图像空间中的5个坐标点,它们和图b中的5条曲线相互对应,,其中N表示图像的宽度。即图像空间中的的点和参数空间中的曲线相互对应,图b中的曲线1、3、5这3条曲线都经过K点,对应到图像空间中表示1、3、5处于同一条直线上面;图a中的2、3、4这3个点处于同一条直线上,对应于参数空间中表示它们所对应的曲线2、3、4都通过同一点L。
三、Hough变换实现步骤
- 步骤1-首先建立一个二维数组,其对应的参数空间为,这个数组其实就相当于一个累加器;
- 步骤2-然后使用顺序搜索的方式在图像中的所有目标像素中进行快速的搜索,针对图像中的每一个像素而言,在参数空间中根据式计算出一个对应的位置,在累加器对应的位置上执行一次加1操作;
- 步骤3-然后求出累加器中的最大值,也就是参数空间中的最大值,其对应的位置为;
- 步骤4-最后将获得的参数空间位置输入到式中,从而找到该空间所对应的图像空间中的直线参数,即我们需要检测的结果。
四、Hough变换直线检测代码实现及效果展示
# coding=utf-8
# 导入python库
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('test3.jpg')
# 彩色图片灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 执行边缘检测
edges = cv2.Canny(gray,100,200)
# 显示原始结果
cv2.imwrite('edges.png',edges)
cv2.imshow('edge', edges)
# plt.subplot(121)
# plt.imshow(edges)
# 执行Hough直线检测
lines = cv2.HoughLines(edges,1,np.pi/180,160)
lines1 = lines[:,0,:]
for rho,theta in lines1:
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))
cv2.line(img,(x1,y1),(x2,y2),(255,0,0),1)
cv2.imwrite('line.png',img)
cv2.imshow('line', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# plt.subplot(122)
# plt.imshow(img)
上图展示了Hough变换直线检测的结果。第1列表示的是原始的输入图片;第2列表示的是边缘检测的结果;第3列表示Hough直线检测结果。我们可以发现Hough变换准确的检测到图片中的所有直线。需要主要的是用户需要根据自己的需要对边缘检测的参数进行调节。
五、Hough变换圆形检测代码实现及效果展示
# coding=utf-8
# 导入python包
import numpy as np
import argparse
import cv2
# 构建并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
# 读取彩色图片
image = cv2.imread(args["image"])
output = image.copy()
# 将其转换为灰度图片
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 应用hough变换进行圆检测
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
# 确保至少发现一个圆
if circles is not None:
# 进行取整操作
circles = np.round(circles[0, :]).astype("int")
# 循环遍历所有的坐标和半径
for (x, y, r) in circles:
# 绘制结果
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# 显示结果
cv2.imshow("output", np.hstack([image, output]))
cv2.waitKey(0)
上图展示了Hough变换圆形检测的结果。每一行表示一个测试图片,第1列表示的是原始的输入图片,第2列表示的是Hough检测的结果。通过上图我们可以获得一些信息,即Hough变换不仅能够处理简单的情况,也能很好的处理复杂的情况。需要注意的是用户需要根据自己的输入图片去调节cv2.HoughCircles函数中的一些关键参数。
六、基于Hough的椭圆检测代码实现及效果展示
# coding=utf-8
import matplotlib.pyplot as plt
from skimage import data,draw,color,transform,feature
#加载图片,转换成灰度图并检测边缘
image_rgb = data.coffee()[0:220, 160:420] #裁剪原图像,不然速度非常慢
image_gray = color.rgb2gray(image_rgb)
edges = feature.canny(image_gray, sigma=2.0, low_threshold=0.55, high_threshold=0.8)
#执行椭圆变换
result =transform.hough_ellipse(edges, accuracy=20, threshold=250,min_size=100, max_size=120)
result.sort(order='accumulator') #根据累加器排序
#估计椭圆参数
best = list(result[-1]) #排完序后取最后一个
yc, xc, a, b = [int(round(x)) for x in best[1:5]]
orientation = best[5]
#在原图上画出椭圆
cy, cx =draw.ellipse_perimeter(yc, xc, a, b, orientation)
image_rgb[cy, cx] = (0, 0, 255) #在原图中用蓝色表示检测出的椭圆
# #分别用白色表示canny边缘,用红色表示检测出的椭圆,进行对比
# edges = color.gray2rgb(edges)
# edges[cy, cx] = (250, 0, 0)
fig2, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(8, 4))
ax1.set_title('Original picture')
ax1.imshow(image_rgb)
ax2.set_title('Detect result')
ax2.imshow(edges)
plt.show()
上图展示了Hough变换椭圆检测的结果。第1列表示的是原始的输入图片,图中的蓝线表示检测的结果;第2列表示检测的结果,并将其绘制在一张存在的图片中。图中可以看出Hough变换可以很好的检测出图片中的椭圆。
七、轮廓检测不同形状代码实现及效果展示
# coding=utf-8
# 导入python包
import cv2
# 读取彩色图片
img = cv2.imread('rect1.png')
# 转换为灰度图片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 进行二值化处理
ret,binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找轮廓
_,contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制不同的轮廓
draw_img0 = cv2.drawContours(img.copy(),contours,0,(0,255,255),3)
draw_img1 = cv2.drawContours(img.copy(),contours,1,(255,0,255),3)
draw_img2 = cv2.drawContours(img.copy(),contours,2,(255,255,0),3)
draw_img3 = cv2.drawContours(img.copy(), contours, -1, (0, 0, 255), 3)
# 打印结果
print ("contours:类型:",type(contours))
print ("第0 个contours:",type(contours[0]))
print ("contours 数量:",len(contours))
print ("contours[0]点的个数:",len(contours[0]))
print ("contours[1]点的个数:",len(contours[1]))
# 显示并保存结果
cv2.imshow("img", img)
cv2.imshow("draw_img0", draw_img0)
cv2.imshow("draw_img1", draw_img1)
cv2.imshow("draw_img2", draw_img2)
cv2.imwrite("rect_result.png", draw_img3)
cv2.imshow("draw_img3", draw_img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
上图展示了利用轮廓检测不同形状的结果。每一行展示了一个测试图片,第1列展示的是输入图片,第2类展示的是输出结果。通过上图我们可以看到轮廓检测算法可以准确的检测到图中的所有轮廓并准确的将他们绘制出来。这在现实场景中具有很广泛的应用价值。
八、获取图像中的黑色形状块
# coding=utf-8
# 导入python包
import numpy as np
import argparse
import imutils
import cv2
# 构建并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help = "path to the image file")
args = vars(ap.parse_args())
# 读取图片
image = cv2.imread(args["image"])
# 寻找到图片中的黑色形状块
lower = np.array([0, 0, 0])
upper = np.array([15, 15, 15])
shapeMask = cv2.inRange(image, lower, upper)
# 在mask中寻找轮廓
cnts = cv2.findContours(shapeMask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
print("I found {} black shapes".format(len(cnts)))
cv2.imwrite("Mask.png", shapeMask)
cv2.imshow("Mask", shapeMask)
# 循环遍历所有的轮廓
for c in cnts:
# draw the contour and show it
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.imwrite("shape1.png", image)
cv2.imshow("Image", image)
cv2.waitKey(0)
上图展示了使用python+opencv自动检测到图像中的黑色形状块,第1列表示的是原始的输入图片,和代码中的image对应;第2列表示的是获取到的掩模,对应于代码中的shapeMask;第3列表示的是检测的结果,对应于代码中的image。通过上图我们可以发现该算法能够准确的检测出这些不同的形状块。
九、移除图中的圆和椭圆
<pre><code># coding=utf-8
# 导入python包
import numpy as np
import imutils
import cv2
def is_contour_bad(c):
# 近似轮廓
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# 判断当前的轮廓是不是矩形
return not len(approx) == 4
# 首先读取图片;然后进行颜色转换;最后进行边缘检测
image = cv2.imread("remove.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.Canny(gray, 50, 100)
cv2.imshow("Original", image)
# 寻找图中的轮廓并设置mask
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
mask = np.ones(image.shape[:2], dtype="uint8") * 255
# 循环遍历所有的轮廓
for c in cnts:
# 检测该轮廓的类型,在新的mask中绘制结果
if is_contour_bad(c):
cv2.drawContours(mask, [c], -1, 0, -1)
# 移除不满足条件的轮廓并显示结果
image = cv2.bitwise_and(image, image, mask=mask)
cv2.imwrite("Mask.png", mask)
cv2.imshow("Mask", mask)
cv2.imwrite("result.png", image)
cv2.imshow("After", image)
cv2.waitKey(0)
</code></pre>
十、思维扩展
在现实场景中,我们经常会遇到检测图片中具有不同形状的目标的任务。本文主要关注的是直线、圆、椭圆、三角形等,对于那些不规则的形状而言,其实Opencv中已经内嵌了其它的函数,聪明的你一定可以找到这个函数完成一些更加复杂的形状检测任务。
参考资料
[1] 参考链接
注意事项
[1] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:aaa@qq.com),我会在第一时间回复大家,谢谢大家的关注.
[2] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[3] 如果您在阅读本博客时遇到不理解的地方,希望您可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[4] 本文测试的图片可以通过该链接进行下载。网盘链接- 提取码:oluh 。
上一篇: C# Excel插入图形,添加文本,填充颜色和图片
下一篇: 简单文本挖掘(一)、词云(自定义形状)