使用OpenCV處理YOLOv4即時影像辨識
文章推薦指數: 80 %
机器人; 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
延伸文章資訊
- 1使用OpenCV處理YOLOv4即時影像辨識
机器人; NVIDAI Jetson Nano深度學習應用-使用OpenCV處理YOLOv4即時影像辨識. NVIDAI Jetson Nano深度學習應用- ...
- 2成為AI 科學家|動手玩OpenCV ,邁入影像辨識新視界 - TibaMe
... 接著逐漸帶到OpenCV 的基本操作、繪圖、色彩、濾波器、直方圖等概念,並深入影像處理目的與實作,讓你探索出OpenCV 的多種玩法,學會影像辨識、偵測、融合等技能。
- 3Python影像辨識筆記(三):Open CV操作筆記
#2021/04/13更新OpenCV的DNN模組使用,在使用Intel CPU的情況下,可直接載入透過darknet, PyTorch, TensorFlow, ONNX......等fram...
- 4影像辨識
影像辨識. --以Python為例. 1. 106.3.1 古佳怡. Page 2. 2. 流程. 影像輸入. 前置處理. 切割. 辨識 ... OpenCV資料夾所附的digits.png. ...
- 5Raspberry3使用WebCam+OpenCV進行人臉辨識 - Google Sites
Raspberry 3+Python3 + opencv-contrib-python 3.4.3.18 ... detectMultiScale(gray, scaleFactor=1.3, ...