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閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容

  • 基于學生學習共同體培育語文生態課堂文化的研究 近年來,隨著現代教育理念的不斷深入與...
    火車頭123閱讀 2,011評論 0 8
  • 思維導圖的矢量圖 訪問密碼 e9ed 深入討論在輸出設備上顯示二維圖形的問題 [TOC] 1. 二維觀察流水線...
    Gaolex閱讀 2,299評論 0 1
  • 日記體的文字就是寫著自己沒法說出口的心情…一切事情既來之則安之 <日常> 最近白天去學習,晚上去健身房,每天學習的...
    從未荼靡閱讀 315評論 0 2
  • 駐足,亦真亦假 恍恍惚惚 不愿獨自回家,害怕回憶失敗的孤寂 除了工作就是工作,生活應該多增添點色彩 比如,那,藍天...
    Ainiobaba閱讀 238評論 0 0
  • 在出差地,是熱的,很熱。醉氧,真的是讓人隨時隨地睡的很香,吃飯很香。但不出活呀,咋辦? 接之前的提法,其實“育周邊...
    海義的教育觀閱讀 605評論 0 48