OpenCV十字激光中心与端点识别-1Z实验室

出品: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)
    
    return k,b

3-合并线段

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

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

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

参数 备注
cur_k 当前合并的直线的K值
cur_b 当期合并的直线的b值
k_sum K值的带权累加和
b_sum b值的带权累加和
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
        
    # 计算权重
    weight = math.sqrt(math.pow(x1 - x2, 2) + math.pow(y1 - y2, 2))
    
    if weight != 0 and weight > min_weight:
        # 计算K与b
        k, b = calculate_line(x1, y1, x2, y2)
        # print('k: {:.2f}, b: {:.2f}, weight: {:.2f}'.format(k, b, weight))
        
        if len(lines) == 0:
            # 初次填充line
            line = {}
            line['cur_k'] = k
            line['cur_b'] = b
            line['k_sum'] = k * weight
            line['b_sum'] = b * weight
            line['weight_sum'] = weight
            line['x1'] = x1
            line['y1'] = y1
            line['x2'] = x2
            line['y2'] = y2
            lines.append(line)
            continue
        
        # 根据k的差异做加权
        # 首先获取lines数组里面k举例最近的那个
        
        neighbor_line = min(lines, key=lambda line:abs(line['cur_k'] - k))
        
        if  abs(neighbor_line['cur_k'] - k) < max_k_distance:
            # 小于最大k差值,认为是同一条线
            
            neighbor_line['weight_sum'] += weight
            neighbor_line['k_sum'] += k * weight
            neighbor_line['b_sum'] += b * weight
            neighbor_line['cur_k'] = neighbor_line['k_sum'] / neighbor_line['weight_sum']
            neighbor_line['cur_b'] = neighbor_line['b_sum'] / neighbor_line['weight_sum']
            
            if neighbor_line['x1'] > x1:
                neighbor_line['x1'] = x1
                neighbor_line['y1'] = y1
                
            if neighbor_line['x2'] < x2:
                neighbor_line['x2'] = x2
                neighbor_line['y2'] = y2
            
        else:
            # 添加另外一条线
            # 初次填充line
            line = {}
            line['cur_k'] = k
            line['cur_b'] = b
            line['k_sum'] = k * weight
            line['b_sum'] = b * weight
            line['weight_sum'] = weight
            line['x1'] = x1
            line['y1'] = y1
            line['x2'] = x2
            line['y2'] = 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['y2'] - line2['y1']
    b2 = line2['x1'] - line2['x2']
    c2 = line2['x2'] * line2['y1'] - line2['x1'] * line2['y2']
    
    if (a1 * b2 - a2 * b1) != 0 and (a2 * b1 - a1 * b2) != 0:
        cross_x = int((b1*c2-b2*c1)/(a1*b2-a2*b1))
        cross_y = int((c1*a2-c2*a1)/(a1*b2-a2*b1))
        return (cross_x, cross_y)
    return None

计算交点:

(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

推广

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

扫码关注微信公众号1Z实验室, 回复关键词激光十字源码 获取源码。

1Z实验室 Make Things Easy . 致力于在机器人+计算机视觉+人工智能的重叠区域, 制作小白友好的教程.

tuiguang
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 基于学生学习共同体培育语文生态课堂文化的研究 近年来,随着现代教育理念的不断深入与...
    火车头123阅读 1,974评论 0 8
  • 思维导图的矢量图 访问密码 e9ed 深入讨论在输出设备上显示二维图形的问题 [TOC] 1. 二维观察流水线...
    Gaolex阅读 2,252评论 0 1
  • 日记体的文字就是写着自己没法说出口的心情…一切事情既来之则安之 <日常> 最近白天去学习,晚上去健身房,每天学习的...
    从未荼靡阅读 309评论 0 2
  • 驻足,亦真亦假 恍恍惚惚 不愿独自回家,害怕回忆失败的孤寂 除了工作就是工作,生活应该多增添点色彩 比如,那,蓝天...
    Ainiobaba阅读 233评论 0 0
  • 在出差地,是热的,很热。醉氧,真的是让人随时随地睡的很香,吃饭很香。但不出活呀,咋办? 接之前的提法,其实“育周边...
    海义的教育观阅读 592评论 0 48