栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

OpenCV解决两个工程问题

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

OpenCV解决两个工程问题

在工程项目中遇到的两个实际问题,因为没有现成的函数解决问题,所以想了一些方法来完成,并不是很完美的解决办法,但是可以近似的完成任务,记录一下解决思路。

问题一:

一张图像,里面打了一堆点,要求对点做一个筛选:任意一个点,以半径为threshold画圆,如果在此范围内没有其他的点存在,那么就认为这个点是孤立的点。要求将图中的点分为两类:孤立的点,不孤立的点。

解决思路:

最朴素的做法是来个双重循环,计算出任意一个点和图中所有点的距离,如果某个点和所有点的距离都大于半径,那他就是孤立的点。但是显然,这种做法复杂度为O(n^2),有没有更简便的做法呢?
我没有查找到专门进行此操作的函数,所以就自己想了个方法
既然我们想知道以这个点为中心的一定范围内有没有别的点,那并不一定要画圆,我的做法是画一个正方形,准确来说是画一个目标圆的外切正方形。我们创建一个与目标图像等高等宽的全黑色的图像,然后把目标点全部画上去,画成白色,然后呢,遍历每一个点,取以该点为圆心,threshold为半径的圆的外切正方形,(为什么这么做,因为取圆不好取,取正方形直接对图像切片即可,非常方便),将这块正方形裁剪出来,OpenCv有一个函数,cv2.countNonZero可以获得图像中不为0的像素数量,用这个函数,我们就可以轻松的知道该点周围有没有别的点,它是不是孤立的点。
此方法的复杂度差不多是O(n)

代码实现:
# vib_pts:[(x,y),(x,y),....] 是点的集合
# shape是图像大小
# threshold 是筛选范围
def distance_filter(vib_pts,shape,threshold):
    ans = [[],[]] # 距离合格的是ans[0]  不合格的是ans[1]
    # 行数  列数 通道数
    img = np.zeros((shape[0],shape[1],1),dtype=np.uint8)
    for i in vib_pts:
        img[ i[0],i[1] ] = 1
    for i in vib_pts:
        # 裁剪不能超出范围
        x1 = max(i[0] - threshold,0) # left
        x2 = min(i[0] + threshold,shape[1]) # right
        y1 = max(i[1] - threshold,0) # up
        y2 = min(i[1] + threshold,shape[0]) # down
        temp_img = img[y1:y2,x1:x2]
        cnt = cv2.countNonZero(temp_img)
        if cnt > 1: # 距离合格
            ans[0].append(i)
        else: # 是一个孤立的点 (距离不合格)
            ans[1].append(i)
    return ans
问题二

工程中对一个管状物体做了识别,识别结果如下,左边是对该物体的语义分割结果,右边是用opencv画出的轮廓。
(注意,左图是处理过的二值图,只有黑白两色)

我们的目的是想求出该物体的平均宽度(像素宽度)。

解决思路

这样一个图,使用opencv提供的寻找轮廓的函数findContours可以获得轮廓点,也可以获得面积,周长。
如何求宽度呢?
刚开始有个思路是随机的选取一个轮廓点,然后拿这个点,向左向右搜索,再向上向下搜索(这个物体不一定是竖着的,也可能横着),分别搜索出它的白色宽度,然后纵向宽度和横向宽度取个最小值,重复随机计算30次,取个平均值作为物体宽度,实验发现,这个结果不是很准,误差较大。
又想了一个方法
我把问题抽象一下,既然有周长有宽度,那么我不管这个曲曲折折的物体是什么样子,我直接计算一个同样周长和面积的长方形,(相当于把这个管状物体“抻直”,近似成一个长方形),那么问题就转化为:已知某个长方形的面积和周长,求出长和宽?
那就简单了,假设面积=S,周长=P,长和宽=x,y
x + y = P / 2
x * y = S
把 y = S / x 代入第一个式子,即可得到 x^2 - (P/2)x + S = 0
使用求根公式即可求出x,一元二次方程嘛,求出来两个值,取最小的

实现代码
# ret,binary = cv2.threshold(img1,1,255,cv2.THRESH_BINARY) # 二值化处理
# cnts, hierarchy = cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)  #轮廓搜索
# cnts是轮廓搜索函数返回的轮廓点集合(有多个轮廓)
def average_width(cnts):
	if len(cnts) == 0: return 0 
	average_width = 0
	for cnt in cnts: # 每个轮廓都计算
		# 计算轮廓所包含的面积
        S = cv2.contourArea(cnt)
        # 计算轮廓的周长
        P = cv2.arcLength(cnt, True)
        # 假设长和宽为x y   周长为P  面积为 S   有关系 x^2 - (P/2)x + S = 0       x + y = P/2
        # 根据求根公式  x = ( -b +- sqrt(b^2 - 4ac) )/(2a) 其中 a=1  b=-(P/2)   c=S
        # 解出一元二次方程的两个解
        c1 = int((P / 2 + ((P / 2) ** 2 - 4 * S) ** 0.5) / 2)
        c2 = int((P / 2 - ((P / 2) ** 2 - 4 * S) ** 0.5) / 2)
        c_x = max(c1, c2)  # 取最大的那个值 作为长或者宽
        c_y = int((P/2) - c_x) # 求出另一个边长
        average_width += min(c_x,c_y) # 取最小的那个为宽度
return int(average_width/len(cnts))  # 返回均值
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/938319.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号