天天看點

基于語義分割的矸石充填搗實機構防碰撞系統語義分割識别子產品輸入/輸出程式加密程式封裝

對研一時候做的一個項目進行簡短的總結~

背景:為某煤礦公司智能研究中心做一個智能檢測:矸(gān)石充填防碰撞的檢測和預警。矸石充填就是搗實機不斷把傳動帶送過來的細碎矸石給往後搗實。以往都是礦工手動操控搗實機,由于礦工經常誤操作,會把搗實機擡得過高,撞斷傳送帶。現在使用計算機視覺的方法,借助海康威視的ip攝像頭,實作一個智能的防碰撞系統,降低人力成本和撞擊的機率。

總共包括:讀取視訊流,語義分割及圖像處理,設計I/O接口,程式加密,多線程設計,程式封裝

Demo:視訊

基于語義分割的矸石充填搗實機構防碰撞系統語義分割識别子產品輸入/輸出程式加密程式封裝
基于語義分割的矸石充填搗實機構防碰撞系統語義分割識别子產品輸入/輸出程式加密程式封裝

語義分割識别子產品

在讨論設計方案的時候,提出兩種方案:

1.目标檢測(Object Detection):如YOLO,Faster R-CNN…

2.語義分割(Semantic Segmentation):如AlexNet,Bisenet…

差別在于前者是物體的分類及定位,後者是像素級别的分類。根據實際需求,最終是需要計算搗實機構和傳送帶之間的視覺距離。目标檢測的優點:計算距離的時候比較友善,隻要預測是準确的,距離的計算即是上下兩個bounding_box的坐标相減,但是目标檢測一旦沒有檢測出來,一個檢測物體就會”消失‘了,整個系統就失效了,即是一個非0即1的過程。語義分割的優點:像素級分割,會比目标檢測的識别更加準确且細緻。測距的實作,可以通過算法來進行彌補。是以最終選擇的語義分割的方法。

1.神經網絡的選擇

基于工程有一定的實時性要求,是以網絡需要盡可能的快速輕量。

通過我的實測和論文作者提供的FPS、iou等資料,bisenet是相對滿足本項目的一個網絡。是以最終選擇該語義分割網絡。實驗環境:NVIDIA 2080TI+CUDA10.1

reference:https://github.com/CoinCheung/BiSeNet

paper: https://arxiv.org/abs/1808.00897

網絡的運作和測試可以參考原作者的github

2.讀取視訊流

使用python-opencv拉流,讀取實時視訊通過ip位址+端口 以及使用者名密碼等。

import cv2
# user: admin
# pwd: 12345
# main: 主碼流
# ip: 192.168.1.64
# Channels: 實時資料
# 1: 通道
cap = cv2.VideoCapture("rtsp://admin:[email protected]/main/Channels/1")
print (cap.isOpened())
while cap.isOpened():
	 success,frame = cap.read()
	 cv2.imshow("frame",frame)
	 cv2.waitKey(1)
           

相當于視訊的輸入直接從ip攝像頭中讀取并直接輸入神經網絡。

3.畫輪廓+去噪

輪廓的目的:①為了後續能夠鎖定搗實機和傳送帶,為後面測距做準備②不受到其他因素産生的誤檢所幹擾。

利用cv2.findContours()函數來查找檢測物體的輪廓,需要注意的是cv2.findContours()函數接受的參數為二值圖,即黑白的(不是灰階圖),是以讀取的圖像要先轉成灰階的,再轉成二值圖,具體操作為下面的代碼(注釋)。

out_vis_image = helpers.colour_code_segmentation(output_image, label_values)
img = cv2.resize(cv2.cvtColor(np.uint8(out_vis_image), cv2.COLOR_RGB2BGR), (1024, 1024),interpolation=cv2.INTER_AREA)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 轉換顔色空間
ret, thresh = cv2.threshold(gray, 5, 100, 0) #檢測輪廓
image, contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, i, (0, 0, 0), cv2.FILLED)#繪制輪廓
           

需要注意的是:opencv2傳回兩個值:contours:hierarchy。opencv3會傳回三個值,分别是img, countours, hierarchy,我這裡使用opencv3是以會傳回三個參數。contours傳回的是所有輪廓的像素點坐标(x,y)。

這裡還涉及到一個opencv傳回輪廓的坑:一個python的np.ndarray(n維數組),假設輪廓有50個點,OpenCV傳回的ndarray的維數是(50, 1, 2),而不是我們認為的(100, 2)。是以需要特殊對資料處理一下才能利用輪廓點的坐标。在numpy的數組中,用逗号分隔的是軸的索引。舉個例子,假設有如下的數組:

這裡的shape是 (5, 1, 2),想要取出輪廓坐标需要 a[:,0]。

由于礦下環境複雜,搗實的過程環境很差,有煤渣覆寫和水汽等,一開始的預測效果有點差強人意,後期通過擴充資料集訓練更拟合損失函數,精度有較大的提升。除此之外,借助opencv來對誤檢的噪點進行去除,隻選中中間搗實機構的有效區域。

去噪

有了輪廓點的坐标就可以利用opencv的庫函數contourArea求出輪廓的面積,,剔除小面積誤檢的輪廓,鎖定中間區域的倒是機構和傳送帶,并使用高斯去噪操作,去噪之後的效果可以基本鎖定倒是機構和傳送帶。

4.測算距離

利用輪廓處理後的坐标,由于差別于目标檢測,語義分割沒有規整的矩形框,是以需要進行坐标選點。

搗實機構和傳送帶兩者,傳送帶如果不受到撞擊是相對靜止的,而搗實機構是會動态變化的,是以确定搗實機構的點坐标更加的關鍵。

依次在輪廓上選取四個點,并且取y坐标的最小值(相對人眼視角的最大值),将最小值(x1,y1)的向上延長,使用這個點的很坐标x得到在傳動帶内輪廓的對應坐标(x2,y2),然後x1-x2就可得到像素距離(需要注意的是:這裡opencv的矩陣和我們所了解的橫縱坐标是相反的,垂直方向是x,水準方向是y,左上角是(0,0))。

這塊在後期測試的時候,遇到一個問題:在搗實機構向前推的時候,由于搗實機構上面會附帶碎的矸石,是以會遮擋住傳送帶,這個時候是無法識别傳送帶的。

解決方案:一旦搗實機構的坐标小于傳送帶的下沿坐标,把傳動帶的坐标給儲存下來,也就是上一時刻的傳送帶坐标。

輸入/輸出

因為需求的變化,需要同時對四個相機(總共有81個相機,81選4)下的搗實環境同時進行防碰撞檢測。主程式在主目錄的main.py,

為了減小程式的耦合,更好的并行運作,将輸入視訊流和圖像處理分割成了兩個子函數,分别為readframe和ca()分别有四個邏輯相同的子函數。

因為所有的處理(包括語義分割、圖像處理、視訊流讀入、黑屏檢測、程式加密)都需要在背景處理,是以前端需要留一個輸入供使用者選擇控制的四個相機,以及檢測系統需要随時輸出每台相機下搗實機構與傳送帶的距離,輸出給後面的PLC端控制。分别為in.txt,out.txt。是以主要涉及到怎麼實時的修改輸入輸出檔案。python有一個對文本的讀寫功能,可以滿足需求。

f = open('in.txt')
urlgl = f.read().strip().split(',')
           

這裡urlgl 設定的就是ip位址尾部的每個相機的差別。

def savetxt_compact(fname, x, fmt="%d", delimiter=','):
    with open(fname, 'w+') as fh:
        for row in x:
            line = delimiter.join("0" if value == 0 else fmt % value for value in row)
            fh.write(line + '\n')
           

儲存輸出的像素距離。

程式加密

使用的是tkinter設計的一個GUI,功能就是程式可以被指定運作指定時間,計時器到期後需要輸入密碼才能獲得重新啟動。相當于輸入密碼,又加了一段程式使用的時間周期。通過一個外部檔案讀取剩餘時間,程式内部設計了一個類似于密碼本的字典,外部顯示的話是一個亂碼,但是輸入正确密碼之後就會怎麼指定的時間。一個數字對應一個字母,在外部顯示的就是打亂的英文碼。過程就類似于加密傳輸中的編碼和解碼。

dict1 = {'0': 'H', '1': '*', '2': 'q', '3': 'M', '4': '&', '5': 'd', '6': 'W', '7': 'K', '8': 'x', '9': '#'}
dict2 = {'0': 'n', '1': 'B', '2': 'c', '3': 'k', '4': 'F', '5': 'j', '6': '^', '7': 'L', '8': 's', '9': '%'}
dict3 = {'0': 'R', '1': 'r', '2': 'S', '3': '$', '4': 'Y', '5': 'Z', '6': 'A', '7': 'a', '8': 'U', '9': 'i'}
           

程式封裝

因為企業的電腦是windows平台,且沒有python和深度學習的環境,并且要求需要有可移植性,是以程式要整個封裝成一個可執行檔案。

使用的是外置的Pyinstaller子產品,可以直接pip安裝。

pip install pyinstaller
           

基本文法:

PyInstaller -F -w -i  xxx.ico dev.py --hidden-import=pandas._libs.tslibs.timedeltas
           

-F 指隻生成一個exe檔案,不生成其他dll檔案

-w 不彈出互動視窗,如果你想程式運作的時候,與程式進行互動,則不加該參數

-i 設定程式圖示 ,其後面的xxx.ico檔案就是程式小圖示

dev.py 要打包的程式,如果你不是在dev.py同一級目錄下執行的打包指令,這裡得寫上dev.py的路徑位址

–hidden-import=pandas._libs.tslibs.timedeltas 隐藏相關子產品的引用

你在哪個目錄下執行的指令,預設打包完成的檔案或者檔案夾就在該目錄,需要特别注意的是如果程式是在虛拟環境下開發的,封裝也需要在虛拟環境下執行,畢竟封裝包括一些依賴庫的封裝。

py檔案運作沒問題,不代表你打包後的檔案運作就沒問題,在windows下測試時,可以使用windows下的powershell來運作,這樣就不會閃退了。封裝真的是一個很痛苦,很折騰的事情!!CUDA版本也需要比對,包括一些細小的版本差別。

後記

本項目目前已經申請專利《一種基于深度學習的矸石充填搗實機構防碰撞應用方法》

請勿轉載~

這個項目的github部分實作代碼放在了我的個人https://github.com/hm7455/Anti-collision_Semantic-segmentation_,之前一直把項目的過程記錄在本地檔案裡,一直學習别人的部落格,現在也體驗一下自己寫部落格的的感覺~

繼續閱讀