OpenCV十字激光中心与端点识别【转载】

转自https://www.jianshu.com/p/53982e81d781

出品:1Z实验室 (1ZLAB: Make Things Easy)

概要

下面的这张图片是十字激光的图像, 当前我们的任务就是要识别十字的四个端点还有十字中心。

这个缺口就是螺丝的孔位,所以你获得的图像可能是片段的,不一定连续。

binary.png

我们用不同的颜色标示不同的端点, 空心圆代表交点。

cross-laser-demo.png

算法流程讲解

1-图像二值化

binary.png

二值化排除其他干扰。

binary = cv2.inRange(gray, 200, 255)

2-拟合线段与直线

HoughLineP 进行线段拟合。

line_segs = cv2.HoughLinesP(binary, rho=2,theta=0.1, threshold=100)
len(line_segs)

HoughLineP返回的是数组,每个元素是Tuple类型的数据。还是要打印一下这个数据结构。

import math

for lseg in line_segs:
#
x1,y1,x2,y2 = lseg[0]
# 计算权重
weight = math.sqrt(math.pow(x1-x2, 2) + math.pow(y1-y2, 2))
print(‘x1: {}, y1: {}, x2: {}, y2: {}, weight: {}’.format(x1, y1, x2, y2, weight))

打印结果

线段的标示方法是记录线段的两个端点, 从(x1, y1) 点到(x2, y2) 点。

数组的个数,取决于你的调参,从十几个到几百个不等。

x1: 817, y1: 408, x2: 1068, y2: 164, weight: 350.0528531522061
x1: 525, y1: 93, x2: 868, y2: 468, weight: 508.20665088131227
x1: 630, y1: 202, x2: 818, y2: 408, weight: 278.89065957826557
x1: 488, y1: 56, x2: 814, y2: 412, weight: 482.7131653477042
x1: 816, y1: 407, x2: 960, y2: 267, weight: 200.83824337013107
x1: 714, y1: 520, x2: 714, y2: 520, weight: 0.0
x1: 845, y1: 391, x2: 1113, y2: 131, weight: 373.39523296367884
x1: 698, y1: 275, x2: 817, y2: 404, weight: 175.50498568416796
....

把线段的长度 作为这个线段的权重 Weight

 weight = math.sqrt(math.pow(x1-x2, 2) + math.pow(y1-y2, 2))

每个线段都可以求解出这个线段所在直线的k还有b.

y = k*x + b

求解kb的算法比较简单,需要借助初中所学的知识。

def calculate_line(x1, y1, x2, y2):
    '''
    计算直线
    如果直线水平或者垂直,统一向一个方向倾斜特定角度。
    TODO 这里面没有考虑水平或者垂直的情况
    '''
    if x1 > x2:
        x1,y1,x2,y2 = x2,y2,x1,y1
    if x1 == x2 or y1 == y2:
        # 有时候会出现单个像素点 x1 = x2 而且 y1 = y2
        print('x1:{} y1:{} x2:{} y2:{}'.format(x1, y1, x2, y2))
    k = (y1 - y2) / (x1 - x2)
    b = (y2 * x1 - y1*x2) / (x1 - x2)
<span class="hljs-keyword">return</span> k,b

3-合并线段

我们需要把这几百个线段拟合成两条直线, 这个2 属于我们的先验知识。

遍历所有的线段,求解其kb 还有对应的weight

然后我们通过字典数据结构来存放合并过后的直线。 数据结构细节如下:

参数备注
cur_k当前合并的直线的K值
cur_b当期合并的直线的b值
k_sumK值的带权累加和
b_sumb值的带权累加和
weight_sum权重和
x1合并后大线段的左侧端点的x坐标
y1合并后大线段的左侧端点的y坐标
x2合并后大线段的右侧端点的x坐标
y2合并后大线段的右侧端点的y坐标

所有合并后的直线/线段,存放在lines里面, 每个线段在合并的时候,遍历lines里面合并后的线段,通过k的差值(max_k_distance)判断是否属于同一个直线,如果里面都没有的话就另外添加一个。


lines = []
# 最小权值
min_weight = 20
# 相同k之间最大的差距
max_k_distance = 0.3

for lseg in line_segs:
# 获取线段端点值
x1,y1,x2,y2 = lseg[0]
if x1 > x2:
x1, y1, x2, y2 = x2, y2, x1, y1

<span class="hljs-comment"># 计算权重</span>
weight = math.sqrt(math.pow(x1 - x2, <span class="hljs-number">2</span>) + math.pow(y1 - y2, <span class="hljs-number">2</span>))

<span class="hljs-keyword">if</span> weight != <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> weight &gt; min_weight:
    <span class="hljs-comment"># 计算K与b</span>
    k, b = calculate_line(x1, y1, x2, y2)
    <span class="hljs-comment"># print('k: {:.2f}, b: {:.2f}, weight: {:.2f}'.format(k, b, weight))</span>
    
    <span class="hljs-keyword">if</span> len(lines) == <span class="hljs-number">0</span>:
        <span class="hljs-comment"># 初次填充line</span>
        line = {}
        line[<span class="hljs-string">'cur_k'</span>] = k
        line[<span class="hljs-string">'cur_b'</span>] = b
        line[<span class="hljs-string">'k_sum'</span>] = k * weight
        line[<span class="hljs-string">'b_sum'</span>] = b * weight
        line[<span class="hljs-string">'weight_sum'</span>] = weight
        line[<span class="hljs-string">'x1'</span>] = x1
        line[<span class="hljs-string">'y1'</span>] = y1
        line[<span class="hljs-string">'x2'</span>] = x2
        line[<span class="hljs-string">'y2'</span>] = y2
        lines.append(line)
        <span class="hljs-keyword">continue</span>
    
    <span class="hljs-comment"># 根据k的差异做加权</span>
    <span class="hljs-comment"># 首先获取lines数组里面k举例最近的那个</span>
    
    neighbor_line = min(lines, key=<span class="hljs-keyword">lambda</span> line:abs(line[<span class="hljs-string">'cur_k'</span>] - k))
    
    <span class="hljs-keyword">if</span>  abs(neighbor_line[<span class="hljs-string">'cur_k'</span>] - k) &lt; max_k_distance:
        <span class="hljs-comment"># 小于最大k差值,认为是同一条线</span>
        
        neighbor_line[<span class="hljs-string">'weight_sum'</span>] += weight
        neighbor_line[<span class="hljs-string">'k_sum'</span>] += k * weight
        neighbor_line[<span class="hljs-string">'b_sum'</span>] += b * weight
        neighbor_line[<span class="hljs-string">'cur_k'</span>] = neighbor_line[<span class="hljs-string">'k_sum'</span>] / neighbor_line[<span class="hljs-string">'weight_sum'</span>]
        neighbor_line[<span class="hljs-string">'cur_b'</span>] = neighbor_line[<span class="hljs-string">'b_sum'</span>] / neighbor_line[<span class="hljs-string">'weight_sum'</span>]
        
        <span class="hljs-keyword">if</span> neighbor_line[<span class="hljs-string">'x1'</span>] &gt; x1:
            neighbor_line[<span class="hljs-string">'x1'</span>] = x1
            neighbor_line[<span class="hljs-string">'y1'</span>] = y1
            
        <span class="hljs-keyword">if</span> neighbor_line[<span class="hljs-string">'x2'</span>] &lt; x2:
            neighbor_line[<span class="hljs-string">'x2'</span>] = x2
            neighbor_line[<span class="hljs-string">'y2'</span>] = y2
        
    <span class="hljs-keyword">else</span>:
        <span class="hljs-comment"># 添加另外一条线</span>
        <span class="hljs-comment"># 初次填充line</span>
        line = {}
        line[<span class="hljs-string">'cur_k'</span>] = k
        line[<span class="hljs-string">'cur_b'</span>] = b
        line[<span class="hljs-string">'k_sum'</span>] = k * weight
        line[<span class="hljs-string">'b_sum'</span>] = b * weight
        line[<span class="hljs-string">'weight_sum'</span>] = weight
        line[<span class="hljs-string">'x1'</span>] = x1
        line[<span class="hljs-string">'y1'</span>] = y1
        line[<span class="hljs-string">'x2'</span>] = x2
        line[<span class="hljs-string">'y2'</span>] = y2
        lines.append(line)

做完上述的操作之后,我们获取的lines的长度可能不是2, 可能会大于2. 所以我们可以根据weight_sumlines进行重新排序, 然后截取前两个,作为激光十字的两条直线。

# 根据权重对lines数组进行排序, 取前两个(lines的长度有可能大于2)
sorted_lines = sorted(lines, key=lambda line: line['weight_sum'])[::-1]
line1 = sorted_lines[0]
line2 = sorted_lines[1]
[{'b_sum': -3304027.8377846032,
  'cur_b': -482.1075439824276,
  'cur_k': 1.0900334200603314,
  'k_sum': 7470.32650483927,
  'weight_sum': 6853.300428555484,
  'x1': 478,
  'x2': 1001,
  'y1': 54,
  'y2': 597},
 {'b_sum': 8948293.312710544,
  'cur_b': 1209.7121822845368,
  'cur_k': -0.9799324921216083,
  'k_sum': -7248.603010345705,
  'weight_sum': 7397.043233715087,
  'x1': 599,
  'x2': 1113,
  'y1': 607,
  'y2': 129}]

4-计算交点

def calculate_intersection(line1, line2):
    a1 = line1['y2'] - line1['y1']
    b1 = line1['x1'] - line1['x2']
    c1 = line1['x2'] * line1['y1'] - line1['x1'] * line1['y2']
a2 = line2[<span class="hljs-string">'y2'</span>] - line2[<span class="hljs-string">'y1'</span>]
b2 = line2[<span class="hljs-string">'x1'</span>] - line2[<span class="hljs-string">'x2'</span>]
c2 = line2[<span class="hljs-string">'x2'</span>] * line2[<span class="hljs-string">'y1'</span>] - line2[<span class="hljs-string">'x1'</span>] * line2[<span class="hljs-string">'y2'</span>]

<span class="hljs-keyword">if</span> (a1 * b2 - a2 * b1) != <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> (a2 * b1 - a1 * b2) != <span class="hljs-number">0</span>:
    cross_x = int((b1*c2-b2*c1)/(a1*b2-a2*b1))
    cross_y = int((c1*a2-c2*a1)/(a1*b2-a2*b1))
    <span class="hljs-keyword">return</span> (cross_x, cross_y)
<span class="hljs-keyword">return</span> <span class="hljs-keyword">None</span>

计算交点:

(cx, cy) = calculate_intersection(line1, line2)
print('cx: {} cy: {}'.format(cx, cy))
cx: 816 cy: 405

5-信息可视化

在画面上绘制四个端点与中心交点:

canvas = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)

# 绘制第一条线
pt_radius = 20
cv2.circle(canvas, (line1[‘x1’], line1[‘y1’]),pt_radius, (255, 0, 0), thickness=-1)
cv2.circle(canvas, (line1[‘x2’], line1[‘y2’]),pt_radius, (0, 255, 0), thickness=-1)
cv2.circle(canvas, (line2[‘x1’], line2[‘y1’]),pt_radius, (0, 255, 255), thickness=-1)
cv2.circle(canvas, (line2[‘x2’], line2[‘y2’]),pt_radius, (0, 0, 255), thickness=-1)

cv2.circle(canvas, (cx, cy), 40, (255, 0, 255), thickness=20)

plt.imshow(cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB))
plt.show()

cross-laser-demo.png
引用\[1\]和\[2\]提供了一种使用树莓派和OpenCV识别激光笔的方法。首先,树莓派从摄像头获取图像,并通过鼠标点击来确定需要识别的位置。然后,根据点击位置获取该点的HSV色域值,并根据色域值调整识别的颜色范围。接下来,通过骨架化和拟合等操作,可以得到激光线的轮廓。最后,使用Hough变换来检测直线,并将检测到的直线在图像上显示出来。 引用\[3\]提供了另一种确定色域值的方法。通过从摄像头获取图像,并将图像转换为HSV色域,可以通过鼠标点击来获取点击位置的HSV值。这样可以确定激光笔的颜色范围。 综上所述,使用OpenCV可以通过树莓派和摄像头来识别激光笔。具体的实现方法可以根据引用\[1\]和\[2\]中的代码进行调整和实验。 #### 引用[.reference_title] - *1* *3* [2021电赛D题:基于互联网的摄像测量系统 思路](https://blog.csdn.net/weixin_50569944/article/details/122407999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [opencv 裂隙检测用红色激光线识别裂隙+opencv骨架化](https://blog.csdn.net/Andrwin/article/details/105797364)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值