使用OpenCV處理YOLOv4即時影像辨識

文章推薦指數: 80 %
投票人數:10人

机器人; NVIDAI Jetson Nano深度學習應用-使用OpenCV處理YOLOv4即時影像辨識. NVIDAI Jetson Nano深度學習應用- ... 主页 发现 技术 机器人 NVIDAIJetsonNano深度學習應用-使用OpenCV處理YOLOv4即時影像辨識 NVIDAIJetsonNano深度學習應用-使用OpenCV處理YOLOv4即時影像辨識 关注文章 CAVEEducation 27Apr2021 3 4 0 Yournextarticle       DavefromDesignSpark 你觉得这篇文章怎么样?帮助我们为您提供更好的内容。

DavefromDesignSpark Thankyou!Yourfeedbackhasbeenreceived. DavefromDesignSpark Therewasaproblemsubmittingyourfeedback,pleasetryagainlater. DavefromDesignSpark 你觉得这篇文章怎么样? 难度 太难 有点难 完美 有点简单 太简单 长度 太长 有点长 完美 有点短 太短 发送反馈 作者 張嘉鈞 難度 普通     詳解darknet.py 首先我們先來詳解一下darknet.py,由於他是由C去做封裝的所以比較難理解一些,但是他已經幫我們整理好一些Python的副函式可以使用,這邊將會一一介紹。

  取得神經網路的輸入寬高(network_width、network_height) 如同標題所示,其中lib會連結到darknetlib.so,是作者用c封裝好的函式庫,最後面會稍微介紹一下怎麼樣去查看各函式,這邊功能簡單就不多說了: defnetwork_width(net): returnlib.network_width(net) defnetwork_height(net): returnlib.network_height(net)   邊界框座標與標籤色彩(bbox2point、class_color) 由於神經網路模型輸出的是中心點的位置以及物件的寬高大小,這邊需要一個副函式來做轉換;接著通常物件辨識會變是一種以上的物件,所以通常會使用不同的顏色來做區隔,所以也提供了一個隨機色彩的副函式: defbbox2points(bbox): """ Fromboundingboxyoloformat tocornerpointscv2rectangle """ x,y,w,h=bbox xmin=int(round(x-(w/2))) xmax=int(round(x+(w/2))) ymin=int(round(y-(h/2))) ymax=int(round(y+(h/2))) returnxmin,ymin,xmax,ymax defclass_colors(names): """ CreateadictwithonerandomBGRcolorforeach classname """ return{name:( random.randint(0,255), random.randint(0,255), random.randint(0,255))fornameinnames}   載入神經網路模型(load_network) 在執行命令的時候可以發現每一次都需要給予data、cfg、weight,原因就在這個副函式上拉,在load_net_custom的部分會透過config(配置檔)、weight(權重檔)導入神經網路模型,這邊是他寫好的liberary我也不再深入探討;接著metadata存放.data檔案後就可以取得所有的標籤,這邊用Python的簡寫來完成,將所有的標籤都存放在陣列裡面(class_names),metadata的部分可以搭配下一列的coco.data內容去理解: defload_network(config_file,data_file,weights,batch_size=1): """ loadmodeldescriptionandweightsfromconfigfiles args: config_file(str):pathto.cfgmodelfile data_file(str):pathto.datamodelfile weights(str):pathtoweights returns: network:trainedmodel class_names class_colors """ network=load_net_custom( config_file.encode("ascii"), weights.encode("ascii"),0,batch_size) metadata=load_meta(data_file.encode("ascii")) class_names=[metadata.names[i].decode("ascii")foriinrange(metadata.classes)] colors=class_colors(class_names) returnnetwork,class_names,colors   這邊可以稍微帶一下各個檔案的內容,下列是coco.data,這邊就不多作介紹了,應該都可以看得懂: classes=80 train=/home/pjreddie/data/coco/trainvalno5k.txt valid=coco_testdev #valid=data/coco_val_5k.list names=data/coco.names backup=/home/pjreddie/backup/ eval=coco   將辨識結果顯示出來(print_detections) 將推論後的結果顯示在終端機上面,如果要學習怎麼提取資料可以參考這個部分,在推論後的結果(detection)中可以解析出三個內容標籤(labels)、信心指數(confidence)、邊界框(bbox),取得到之後將所有內容顯示出來,這邊提供了一個變數是coordinates讓使用者自己確定是否要顯示邊界框資訊: defprint_detections(detections,coordinates=False): print("\nObjects:") forlabel,confidence,bboxindetections: x,y,w,h=bbox ifcoordinates: print("{}:{}%(left_x:{:.0f}top_y:{:.0f}width:{:.0f}height:{:.0f})".format(label,confidence,x,y,w,h)) else: print("{}:{}%".format(label,confidence))   將邊界框繪製到圖片上(draw_boxes) 將邊界框繪製到圖片上面,針對bbox進行轉換(使用bbox2point)取得四個邊角座標,方便繪製邊界框使用(cv2.rectangle);接著要將標籤資訊給繪製上去(cv2.putText),最後將繪製完的圖片回傳: defdraw_boxes(detections,image,colors): importcv2 forlabel,confidence,bboxindetections: left,top,right,bottom=bbox2points(bbox) cv2.rectangle(image,(left,top),(right,bottom),colors[label],1) cv2.putText(image,"{}[{:.2f}]".format(label,float(confidence)), (left,top-5),cv2.FONT_HERSHEY_SIMPLEX,0.5, colors[label],2) returnimage   解析辨識結果並回傳(decode_detection) 這部分是待會加上GPIO互動最重要的環節了,他會解析detection並將各個職做好處理後回傳讓使用者去做後續的應用;這邊比較不常看到的是round(x,2)為取小數點後2位,乘以100則是轉換成百分比: defdecode_detection(detections): decoded=[] forlabel,confidence,bboxindetections: confidence=str(round(confidence*100,2)) decoded.append((str(label),confidence,bbox)) returndecoded   取得乾淨的辨識結果(remove_negatives) 這邊的目的是因為cocodataset有91種類別,但辨識出來的東西可能僅有3個,這樣就會有88個0,資料稍微肥大不好查看,所以她這邊提供一個副函式將所有有信心指數為0的給除去,留下有辨識到的物件: defremove_negatives(detections,class_names,num): """ Removeallclasseswith0%confidencewithinthedetection """ predictions=[] forjinrange(num): foridx,nameinenumerate(class_names): ifdetections[j].prob[idx]>0: bbox=detections[j].bbox bbox=(bbox.x,bbox.y,bbox.w,bbox.h) predictions.append((name,detections[j].prob[idx],(bbox))) returnpredictions   進行辨識回傳結果(detect_image) 最重要的環節來了,這邊是主要推論的地方,將模型、標籤、圖片都丟進副函式當中就可以獲得結果了,這邊比較多C函式庫的內容,如果不想深入研究的話只需要知道透過這個副函式可以獲得predict的結果,知道這個點我們就可以客製化程式了: defdetect_image(network,class_names,image,thresh=.5,hier_thresh=.5,nms=.45): """ Returnsalistwithhighestconfidenceclassandtheirbbox """ pnum=pointer(c_int(0)) predict_image(network,image) detections=get_network_boxes(network,image.w,image.h, thresh,hier_thresh,None,0,pnum,0) num=pnum[0] ifnms: do_nms_sort(detections,num,len(class_names),nms) predictions=remove_negatives(detections,class_names,num) predictions=decode_detection(predictions) free_detections(detections,num) returnsorted(predictions,key=lambdax:x[1])   進行辨識回傳結果-進階 這裡多是用C的函式庫內容,在程式的最頂端有導入了ctypes這個函式庫,這是個與C兼容的數據類型,並且可以透過這個函式庫調用DLL等用C建置好的函式庫,如果想要了解更多可以去include資料夾中尋找C的函式,並且在darknet/src當中找到對應的內容。

舉例來說,我現在想了解get_network_boxes,先開啟darknet/include/darknet.h的標頭檔進行搜尋:   接著可以看到一系列的副函式上方有//network.h的字樣,代表要去/darknet/src/network.c中找到這個副函式的內容,注意程式內容是放在.c哦:   自己撰寫一個最易理解的yolov4即時影像辨識 礙於原本github提供的程式碼對於一些新手來說還是不太好理解,因為新手也比較少用到Threading跟Queue,除此之外原本的darknet_video.py我在JetsonNano上執行非常的卡頓(原因待查證),所以我決定來帶大家撰寫一個較好理解的版本,使用OpenCV就可以搞定。

這個程式需要放在darknet的資料夾當中,並且確保已經有build過了(是否有libdarknet.so),詳細的使用方法可以參考github或我之前的yolov4文章。

  正式開始 最陽春的版本就是直接導入darknet.py之後開始撰寫,因為我直接寫即時影像辨識,所以還需要導入opencv: importcv2 importdarknet importtime   一些基本的參數可以先宣告,像是導入神經網路模型的配置、權重、資料集等: #Parameters win_title='YOLOv4CUSTOMDETECTOR' cfg_file='cfg/yolov4-tiny.cfg' data_file='cfg/coco.data' weight_file='yolov4-tiny.weights' thre=0.25 show_coordinates=True   接著我們可以先宣告神經網路模型並且取得輸入的維度大小,注意我們是以darknet.py進行客製,所以如果不知道load_network的功用可以往回去了解,: #LoadNetwork network,class_names,class_colors=darknet.load_network( cfg_file, data_file, weight_file, batch_size=1 ) #GetNetsInputdimentions width=darknet.network_width(network) height=darknet.network_height(network)   有了模型、輸入維度之後就可以開始取得輸入圖像,第一個版本中我們使用OpenCV進行即時影像辨識,所以需要先取得到Webcam的物件並使用While來完成: #VideoStream whilecap.isOpened(): #Getcurrentframe,quitifnoframe ret,frame=cap.read() ifnotret:break t_prev=time.time()   接著需要對圖像進行前處理,在主要是OpenCV格式是BGR需要轉換成RGB,除此之外就是輸入的大小需要跟神經網路模型相同: #Fiximageformat frame_rgb=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) frame_resized=cv2.resize(frame_rgb,(width,height))   接著轉換成darknet的格式,透過make_image事先宣告好輸入的圖片,再透過copy_image_from_bytes將位元組的形式複製到剛剛宣告好的圖片當中: #converttodarknetformat,saveto"darknet_image" darknet_image=darknet.make_image(width,height,3) darknet.copy_image_from_bytes(darknet_image,frame_resized.tobytes())   再來就是Inference的部分,直接調用detect_image即可獲得結果,可以使用print_detections將資訊顯示在終端機上,最後要記得用free_image將圖片給清除: #inference detections=darknet.detect_image(network,class_names,darknet_image,thresh=thre) darknet.print_detections(detections,show_coordinates) darknet.free_image(darknet_image)   最後就是將boundingbox繪製到圖片上並顯示,這邊使用的是frame_resized而不是剛剛的darknet_image,那個變數的內容會專門用來辨識所使用,況且剛剛free_image已經將其清除了;用OpenCV顯示圖片前記得要轉換回BGR格式哦: #drawboundingbox image=darknet.draw_boxes(detections,frame_resized,class_colors) image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)   顯示之前計算一下FPS並顯示在左上角: #ShowImageandFPS fps=int(1/(time.time()-t_prev)) cv2.rectangle(image,(5,5),(75,25),(0,0,0),-1) cv2.putText(image,f'FPS{fps}',(10,20),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),2) cv2.imshow(win_title,image)   程式的最後是按下小寫q離開迴圈並且刪除所有視窗、釋放Webcam的物件: ifcv2.waitKey(1)==ord('q'): break cv2.destroyAllWindows() cap.release()   運行結果   結論 其實搞清楚darknet.py之後再進行客製化的修改已經就不難了,已經使用常見的OpenCV來取得圖像,如果想要改成視窗程式也很簡單,使用Tkinter、PyQt5等都可以更快結合yolov4;順道提一下,如果想要追求更高的效能可以使用Gstreamer搭配多線程或是轉換到TensorRT引擎上加速,可以參考之前的yolov4基礎介紹,後半段有提供TensorRT的Github。

  相關文章 https://github.com/AlexeyAB/darknet           下载 本篇文章完整範例程式 ZIP,6.0kB 分享这个帖子 赞好 关注文章 CAVEEducation 关注 CAVEDUEducationisdevotedintoroboticseducationandmakermovementsince2008,andisintensivelyactiveinteachingfundamentalknowledgeandskills.Wehadpublishedmanybooksforreadersinallages,topicsincludingDeepLearning,edgecomputing,AppInventor,IoTandrobotics. PleasecheckCAVEDU'swebsiteformoreinformation:http://www.cavedu.com,http://www.appinventor.tw Yournextarticle       RecommendedArticles 评论 立即登录发表评论 Tao1 August16,202102:07 您好,想請問該如何更改顯示的畫面大小呢,謝謝 Upvote 0 Votes Tao1 August16,202102:07 您好,想請問說如果想要更改影像串流的視窗大小是如何更改呢?我有直接試過更改cap的大小,但沒有效果,請問是要在darknet.py直接做更改嗎? Upvote 0 Votes LXXII July13,202106:01 你好我已經接上usb鏡頭 但執行及時景象辨識時 NameError:name'cap'isnotdefind 說在whilecap.isOpened():這一行有錯誤 請問我需要添加什麼嗎 謝謝 Upvote 0 Votes CAVEEducation July14,202123:28 @LXXII在這行之前whilecap.isOpened():。

加一行程式碼cpa=cv2.VideoCapture(0)。

cap=cv2.VideoCapture(0) whilecap.isOpened(): #Getcurrentframe,quitifnoframe ret,frame=cap.read() ifnotret:break t_prev=time.time() DesignSparkElectricalLogolinkedin



請為這篇文章評分?