# YOLOv3-透過darknet.py進行影像串流 ###### tags: `YOLO` >YOLO有darknet.py,但只能讀取單張照片。因此紀錄如何使用修改darknet.py、image.c、image.h、Makefile,將YOLO的及時影像串流的辨識結果進行讀取,並做其他應用。 >本環境為Python3.6,於Ubuntu1804 (**64-bit**才行)執行YOLOv3 1. 下載pjreddie的YOLO,並進行編譯 (make後產生libdarknet.so) ```bash= git clone https://github.com/pjreddie/darknet.git cd darknet make ``` 下載權重,此處以yolov3為例 ```bash= wget https://pjreddie.com/media/files/yolov3.weights ``` 2. darknet.py是依賴libdarknet.so這份文件,所以要**將darknet.py(位於python下)移動到與libdarknet.so相同的位置**(darknet下),並將darknet.py裡的CDLL路徑更改成libdarknet.so新路徑(可能需要用絕對路徑) ```bash= lib = CDLL("/home/jim/Desktop/darknet_pjreddie/libdarknet.so", RTLD_GLOBAL) ``` 3. 處理darknet的錯誤程式碼 - 由於以下程式碼會將這些位置參數傳至libdarknet.so,而libdarknet.so是用C/C++撰寫,因此會出現錯誤。 解決方法: 開啟darknet.py後,將有路徑的code,前面加上b,並將修改檔案對應路徑,如下。 ``` if __name__ == "__main__": net = load_net(b"cfg/tiny-yolo.cfg", b"tiny-yolo.weights", 0) meta = load_meta(b"cfg/coco.data") r = detect(net, meta, b"data/dog.jpg") print r ``` 4. 將darknet.py內自定義一個函數,透過此函數將影像傳至image.c,並讀取image.c的回傳辨識結果 ```bash= def nparray_to_image(img): data = img.ctypes.data_as(POINTER(c_ubyte)) image = ndarray_image(data, img.ctypes.shape, img.ctypes.strides) return image ``` 5. 修改darknet.py中detect函數的前幾行,前幾行更改後為: ```python= def detect(net, meta, im, thresh=.5, hier_thresh=.5, nms=.45): #im = load_image(image, 0, 0) num = c_int(0) pnum = pointer(num) ... ``` 此處將**image修改成im**。image是字串型態,即路徑,並調用load_meta函數後得到im(一個darknet自定義的image類型),再進行處理。而此處打算直接讀入影像進行辨識,因此省去load_meta函數。 6. 將darknet.py內加入以下程式碼,調用image.c的涵式 ```bash= ndarray_image = lib.ndarray_to_image ndarray_image.argtypes = [POINTER(c_ubyte), POINTER(c_long), POINTER(c_long)] ndarray_image.restype = IMAGE ``` 7. 在darknet/src/image.c中,定義以下涵式 ```bash= #ifdef NUMPY image ndarray_to_image(unsigned char* src, long* shape, long* strides) { int h = shape[0]; int w = shape[1]; int c = shape[2]; int step_h = strides[0]; int step_w = strides[1]; int step_c = strides[2]; image im = make_image(w, h, c); int i, j, k; int index1, index2 = 0; for(i = 0; i < h; ++i){ for(k= 0; k < c; ++k){ for(j = 0; j < w; ++j){ index1 = k*w*h + i*w + j; index2 = step_h*i + step_w*j + step_c*k; //fprintf(stderr, "w=%d h=%d c=%d step_w=%d step_h=%d step_c=%d \n", w, h, c, step_w, step_h, step_c); //fprintf(stderr, "im.data[%d]=%u data[%d]=%f \n", index1, src[index2], index2, src[index2]/255.); im.data[index1] = src[index2]/255.; } } } rgbgr_image(im); return im; } #endif ``` 8. 在darknet/src/image.h內宣告涵式 ```bash= #ifdef NUMPY image ndarray_to_image(unsigned char* src, long* shape, long* strides); #endif ``` 9. 在darknet/Makefile內加入以下程式碼 (不確定是否要將python2.7改成自己環境的版本) ``` ifeq ($(NUMPY), 1) COMMON+= -DNUMPY -I/usr/include/python2.7/ -I/usr/lib/python2.7/dist-packages/numpy/core/include/numpy/ CFLAGS+= -DNUMPY endif ``` 並在上方加入NUMPY = 1 ,讓image.c中的 #ifdef NUMPY 成立 加入後會像這樣: ``` GPU=1 CUDNN=1 OPENCV=1 OPENMP=0 NUMPY=1 DEBUG=0 ``` 9. 重新編譯 ```bash= make clean make ``` 10. 在darknet.py增加讀取影像以及辨識功能 回傳的r有**類別**以及**bounding box** ```python= if __name__ == "__main__": net = load_net(b"cfg/yolov3.cfg", b"yolov3.weights", 0) meta = load_meta(b"cfg/coco.data") vid = cv2.VideoCapture(0) while True: return_value,arr=vid.read() im=nparray_to_image(arr) r = detect(net, meta, im) print(r) ``` --- # 番外 :bulb:可使用我的Sample.py,進行影像串流至網頁,檔案說明可參考 ➜[這篇](https://hackmd.io/4iTpehQkRq6iE3I5q3gEZg?view) ```bash= git clone https://github.com/jim93073/yolo_socket.git ``` 並將檔案移動至darknet底下 --- Reference ➜ [YOLO實戰](http://www.jeepxie.net/article/319977.html) Reference ➜[github issue](https://github.com/pjreddie/darknet/issues/289#issuecomment-342448358)