飛槳與PyQt的碰撞,圖形化目標檢測So Easy

還記得3月份的時候我給大家介紹了PaddleDetection的環境部署、訓練及可視化、模型導出。但那只是一個算法程序,一個完整的項目需要在算法的基礎上將可視化操作開發成型。今天我給大家帶來如何利用Py-Qt編一個顯示界面,並結合工業相機實時採集並進行目標檢測。

本文用到的軟件有PyQt5、Pycharm、Hikvision工業相機。

本文內容如下:

1、在Pycharm下搭建pyqt環境

2、介紹通過飛槳進行模型保存和模型加載

3、如何利用飛槳檢測單幀圖像

4、PyQt5效果展示

Pycharm下搭建PyQt5的環境

該過程見此鏈接https://zhuanlan.zhihu.com/p/110224202,作者將飛槳和PyQt5安裝在了同一個虛擬環境下面。


通過飛槳保存模型和加載模型

介紹模型的保存和加載,目的是更好地瞭解飛槳預測過程。本文主要介紹模型保存函數:fluid.io.save_params;fluid.io.save_inference_model和模型加載函數 ;fluid.io.save_params ;fluid.io.load_inference_model


1. 模型的保存

1)fluid.io.save_params

fluid.io.save_params:從字面意思來理解,表示保存參數。而該函數的主要目的也確實是保存訓練過程中生成的各個節點之間的參數(權重、偏置等),使用fluid.io.save_params進行模型的保存,其保存的內容如下圖所示:


2)fluid.io.save_inference_model

fluid.io.save_inference_model:從字面上理解,表示保存可進行後續預測的模型。而該函數的主要目的也確實是保存可進行預測的模型。即調用save_inference_model函數,可在save_params所保存的參數基礎上融合網絡結構生成的可進行預測的模型。這一點類似於tensorflow將訓練出來的ckpt文件固化成pb文件。從我們git中下載的PaddleDetection代碼,在PaddleDetection-release/tools/export_model中就是利用save_inference_model導出結合後的模型。

利用export_model.py保存下來的文件如下所示:生成了兩個文件,__model__和__params__文件,其中__model__表示網絡結構,__params__表示訓過程產生的參數。

2. 模型的加載


1) fluid.io.load_params

fluid.io.load_params:根據上文得知,該函數表示加載訓練過程中的參數進行預測,如果要加載參數進行預測,則需要將網絡結構也加載進來,PaddleDetection給出的官方預測示例就是利用該方式進行預測。

2) fluid.io.load_inference_model

fluid.io.load_inference_model:根據上文得知,該函數表示加載參數和網絡結構的融合體進行預測。作者在如下的內容中就是使用該方式進行預測。

3. 備註


1) 備註一

前面生成的模型文件中,__model__文件包含了NMS部分(這一點和tensorflow的pb文件不同,pb文件是沒有NMS節點的)。關於__model__文件,我們可以使用Netron進行展開。Netron使用地址https://lutzroeder.github.io/netron/。打開以後可看到網絡結構,在最下端發現有NMS節點。

2) 備註二

除了上述談到的模型加載和保存的方式,還有另外一組即fluid.io.save_persistables和fluid.io.load_persistables。

如何利用飛槳進行預測?

PaddleDetection裏面已經有了相應的預測代碼infer.py,該代碼利用paddle的reader機制進行圖像的預測,同時該代碼使用的加載模型的方式是fluid.io.load_params。作者個人爲了能配合後續在qt下運行檢測,使用fluid.io.load_inference_model作爲模型的加載方式,從paddle的AI Studio上截取一部分代碼重新構造了預測代碼。本段預測代碼加載的模型是yolov3(主幹網絡MobileNet)訓練出來的模型。

作者構造自身的預測代碼思路如下圖所示:

代碼如下:

說明:

1. 該代碼由於使用參數和網絡結構融合的模型,因此對於其他庫的依賴比較少,只需要將飛槳導入即可,

2. 該代碼由於使用的模型文件是《__model__和__params__》,因此在使用load_inference__model時候,必須在該函數的參數中指明這兩個名字。

3. 該代碼中需要着重注意一下一點:由於預測加載的模型是使用yolov3訓練出來的模型,因此在進行預測時要考慮圖像歸一化的操作,數據歸一化的操作來源訓練該模型的config文件,輸入圖像大小需要和export model時設置的圖像大小保持一致

import numpy as np
import time
import paddle.fluid as fluid
from PIL import Image
from PIL import ImageDraw

train_parameters = {
    "label_dict": {0:"apple",1:"banana",2:"orange"},
    "use_gpu": True,
    "input_size": [3, 608, 608],    # 原版的邊長大小爲608,爲了提高訓練速度和預測速度,此處壓縮爲448
}

target_size = train_parameters['input_size']
anchors = train_parameters['anchors']
anchor_mask = train_parameters['anchor_mask']
label_dict = train_parameters['label_dict']
print(label_dict[1])
# class_dim = train_parameters['class_dim']
# print("label_dict:{} class dim:{}".format(label_dict, class_dim))
place = fluid.CUDAPlace(0) if train_parameters['use_gpu'] else fluid.CPUPlace()
exe = fluid.Executor(place)
path="C:\\Users\\zhili\\Desktop\\2"#_mobilenet_v1
[inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model(dirname=path, executor=exe,model_filename='__model__', params_filename='__params__')

class inference():
    def __init__(self):
        print("8888888888")

    def draw_bbox_image(self,img, boxes, labels,scores, save_name):
        """
        給圖片畫上外接矩形框
        :param img:
        :param boxes:
        :param save_name:
        :param labels
        :return:
        """
        draw = ImageDraw.Draw(img)
        for box, label,score in zip(boxes, labels,scores):
            print(box, label, score)
            if(score >0.9):
                xmin, ymin, xmax, ymax = box[0], box[1], box[2], box[3]
                draw.rectangle((xmin, ymin, xmax, ymax), 3, 'red')
                draw.text((xmin, ymin), label_dict[label], (255, 255, 0))
        img.save(save_name)

    def resize_img(self,img, target_size):#將圖片resize到target_size
        """
        保持比例的縮放圖片
        :param img:
        :param target_size:
        :return:
        """
        img = img.resize(target_size[1:], Image.BILINEAR)
        return img


    def read_image(self,img_path):

        origin = Image.open(img_path)
        img = self.resize_img(origin, target_size)
        resized_img = img.copy()
        if img.mode != 'RGB':
            img = img.convert('RGB')
        img = np.array(img).astype('float32').transpose((2, 0, 1))  # HWC to CHW 讓矩陣進行方向的轉置
        img = img / 255.0

        img[0, :, :] -= 0.485
        img[1, :, :] -= 0.456
        img[2, :, :] -= 0.406

        img[0, :, :] /=0.229
        img[1, :, :] /=0.224
        img[2, :, :] /=0.225
        img = img[np.newaxis, :]
        return origin, img, resized_img

    def infer(self,image_path):
        """
        預測,將結果保存到一副新的圖片中
        :param image_path:
        :return:
        """
        origin, tensor_img, resized_img = self.read_image(image_path)
        input_w, input_h = origin.size[0], origin.size[1]
        image_shape = np.array([input_h, input_w], dtype='int32')
        t1 = time.time()
        batch_outputs = exe.run(inference_program,
                                feed={feed_target_names[0]: tensor_img,
                                      feed_target_names[1]: image_shape[np.newaxis, :]},備註:此時送入網絡進行預測的圖像大小是原圖的尺寸大小。                                fetch_list=fetch_targets,
                                return_numpy=False)

        period = time.time() - t1
        print("predict cost time:{0}".format("%2.2f sec" % period))
        bboxes = np.array(batch_outputs[0])

        if bboxes.shape[1] != 6:
            print("No object found in {}".format(image_path))
            return
        labels = bboxes[:, 0].astype('int32')
        scores = bboxes[:, 1].astype('float32')
        boxes = bboxes[:, 2:].astype('float32')

        last_dot_index = image_path.rfind('.')
        out_path = image_path[:last_dot_index]
        out_path += '-result.jpg'
        self.draw_bbox_image(origin, boxes, labels,scores, out_path)

if __name__ == '__main__':
    image_path= "C:\\Users\\zhili\\Desktop\\123\\2.jpg"
    a=inference()
    a.infer(image_path)

PyQt5檢測效果

1. 首先通過步驟1配置好的PyQt中QT Designer,創建兩個Button對象,分別爲“打開相機”、“開始檢測”,然後創建兩個Label對象,分別用於顯示相機原圖和顯示檢測後圖像。

2. 創建多線程檢測機制,分別給兩個Button設置不同的槽函數,分別用於觸發相機拍照和調用檢測函數。運行infe_video.py可得到如下結果。由於作者使用的是黑白相機拍攝電腦屏幕上百度搜索出來的橘子照片,檢測效果質量不高。

特別說明:該文章受到了高松鶴同學和百度飛槳團隊的大力支持,表示感謝。

該項目Github地址:

https://github.com/yzl19940819/Paddle_infer_python

如果您加入官方QQ羣,您將遇上大批志同道合的深度學習同學。官方QQ羣:703252161

如果您想詳細瞭解更多飛槳的相關內容,請參閱以下文檔。

官網地址:https://www.paddlepaddle.org.cn

飛槳開源框架項目地址:

GitHub: https://github.com/PaddlePaddle/Paddle

Gitee:  https://gitee.com/paddlepaddle/Paddle

END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章