基于python的ncnn案例代码
#!/usr/bin/env python # encoding: utf-8 """ @version: v1.0 @author: Jory.d @contact: 707564875@qq.com @site: @software: PyCharm @file: ncnn_basenet.py @time: 2022/10/28 下午3:28 @desc: 用于ncnn推理的基础类 """ from abc import ABCMeta, abstractmethod import os, os.path as osp import numpy as np import ncnn import cv2 import pickle class NCNNBaseNet(metaclass=ABCMeta): CLASSES = ('object1', '__backgound__',) MODEL_ROOT = './ncnn_models/repvgg' PARAM_PATH = f'{MODEL_ROOT}/epoch_635.deploy.pth.sim-opt-fp16.param' BIN_PATH = f'{MODEL_ROOT}/epoch_635.deploy.pth.sim-opt-fp16.bin' bbox_means = [0., 0., 0., 0.] bbox_stds = [0.1, 0.1, 0.2, 0.2] INPUT_W = 112 INPUT_H = 112 INPUT_C = 3 MEAN = [128., ] * INPUT_C STD = [1 / 128., ] * INPUT_C def __init__(self): self.mean_vals = self.MEAN self.norm_vals = self.STD self.class_num = len(self.CLASSES) self.use_gpu = False self.num_threads = 4 self.init_net() def init_net(self): assert osp.exists(self.PARAM_PATH) assert osp.exists(self.BIN_PATH) self.net = ncnn.Net() self.net.opt.use_vulkan_compute = self.use_gpu self.net.load_param(self.PARAM_PATH) self.net.load_model(self.BIN_PATH) print('self.net.load_param() is done.') print('self.net.load_model() is done.') input_names = self.net.input_names() output_names = self.net.output_names() assert len(input_names) > 0 and len(output_names) > 0 print(f'input_names: {input_names}') print(f'output_names: {output_names}') self.input_names = input_names self.output_names = output_names self.class_num = len(self.CLASSES) def __del__(self): self.net = None self.anchors_per_level = None def sigmoid(self, x): return 1 / (1 + np.exp(-x)) def softmax(self, x, dim=-1): x = np.exp(x) sum_value = np.sum(x, axis=dim) sum_value = np.expand_dims(sum_value, axis=dim) return x / sum_value def preprocess(self, img): """ :param img: opencv_mat :return: ncnn:mat """ img_h, img_w = img.shape[:2] img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) x = img_rgb if self.INPUT_C == 1: x = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) mat_in = ncnn.Mat.from_pixels_resize( x, ncnn.Mat.PixelType.PIXEL_GRAY if self.INPUT_C == 1 else ncnn.Mat.PixelType.PIXEL_RGB, img_w, img_h, self.INPUT_W, self.INPUT_H, ) mat_in.substract_mean_normalize(self.MEAN, self.STD) return mat_in @abstractmethod def detect(self, img, thres): raise NotImplementedError('must be overwrite.') pass # 目标检测基础类 class NCNNDetectNet(NCNNBaseNet): ANCHOR_PATH = f'./epoch_7000_anchor_per_level.pkl' OUTPUT_NODES = [ ['61', '62'], # stride 32 -- (conf, loc) ] def __init__(self): super(NCNNDetectNet, self).__init__() self.load_anchor_data() def load_anchor_data(self): assert osp.exists(self.ANCHOR_PATH) f = open(self.ANCHOR_PATH, 'rb') # [level1, level2, ...] """ [ [ [x1,y1,x2,y2], ... ], [ [x1,y1,x2,y2], ... ], ... ] """ self.anchors_per_level = pickle.load(f) f.close() def ssd_decode(self, priors, bbox_pred): """ SSD DeltaXYWHBBoxCoder 方式的box解码, 参考自 mmdetection / DeltaXYWHBBoxCoder """ deltas = bbox_pred rois_ = np.asarray(priors).reshape(-1, 4) assert rois_.shape == bbox_pred.shape, f'{rois_.shape} != {bbox_pred.shape}.' means, stds = self.bbox_means, self.bbox_stds means, stds = np.array([means]), np.array([stds]) denorm_deltas = deltas * stds + means dxy = denorm_deltas[:, :2] dwh = denorm_deltas[:, 2:] pxy = ((rois_[:, :2] + rois_[:, 2:]) * 0.5) pwh = (rois_[:, 2:] - rois_[:, :2]) dxy_wh = pwh * dxy gxy = pxy + dxy_wh gwh = pwh * np.exp(dwh) x1y1 = gxy - (gwh * 0.5) x2y2 = gxy + (gwh * 0.5) bboxes = np.concatenate([x1y1, x2y2], axis=-1) min_xy = 0 maxXY = (self.INPUT_W, self.INPUT_H) max_xy = np.array([[*maxXY] * 2], np.float32) bboxes = np.where(bboxes < min_xy, min_xy, bboxes) bboxes = np.where(bboxes > max_xy, max_xy, bboxes) return bboxes def nms_bboxes(self, boxes, scores, nms_thres=0.6): """Suppress non-maximal boxes. # Arguments boxes: ndarray, boxes of objects. scores: ndarray, scores of objects. # Returns keep: ndarray, index of effective boxes. """ x = boxes[:, 0] y = boxes[:, 1] w = boxes[:, 2] - boxes[:, 0] h = boxes[:, 3] - boxes[:, 1] areas = w * h order = scores.argsort()[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) xx1 = np.maximum(x[i], x[order[1:]]) yy1 = np.maximum(y[i], y[order[1:]]) xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]]) yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]]) w1 = np.maximum(0.0, xx2 - xx1) h1 = np.maximum(0.0, yy2 - yy1) inter = w1 * h1 ovr = inter / (areas[i] + areas[order[1:]] - inter + 1e-8) inds = np.where(ovr <= nms_thres)[0] order = order[inds + 1] keep = np.array(keep) return keep def post_process(self, outs, thres=0.5): mlvl_bboxes, mlvl_scores, mlvl_labels = outs nboxes, nclasses, nscores = [], [], [] for c in range(self.class_num): inds = np.where(mlvl_labels == c) b = mlvl_bboxes[inds] c = mlvl_labels[inds] s = mlvl_scores[inds] inds = np.where(s >= thres)[0] b = b[inds] c = c[inds] s = s[inds] if b.shape[0] == 0: continue keep = self.nms_bboxes(b, s) nboxes.append(b[keep]) nclasses.append(c[keep]) nscores.append(s[keep]) if len(nboxes) > 0: bboxes = np.concatenate(nboxes) # [N,4] classes = np.concatenate(nclasses) # [N,] scores = np.concatenate(nscores) # [N,] return bboxes, classes, scores else: return [], [], [] def detect(self, img, thres=0.7): """ img: opencv_mat, channel order: RGB """ img_h, img_w = img.shape[:2] mat_in = self.preprocess(img) ex = self.net.create_extractor() ex.set_num_threads(self.num_threads) ex.input(self.input_names[0], mat_in) cls_score_list = [] bbox_pred_list = [] for out_names in self.OUTPUT_NODES: # stride 从小到大 assert len(out_names) == 2, '2个featmap, 代表score和location.' ret, out_conf_stride_n = ex.extract(out_names[0]) ret, out_loc_stride_n = ex.extract(out_names[1]) out_conf_stride_n = np.array(out_conf_stride_n) # [c,h,w] out_loc_stride_n = np.array(out_loc_stride_n) # print(out_conf_stride_n.shape) # print(out_loc_stride_n.shape) # print(out_conf_stride_n[0][0][:10]) # print(out_loc_stride_n[0][0][:10]) cls_score_list.append(out_conf_stride_n) bbox_pred_list.append(out_loc_stride_n) mat_in.release() ########################################################################### mlvl_priors = self.anchors_per_level mlvl_bboxes, mlvl_scores, mlvl_labels = [], [], [] for level_idx, (cls_score, bbox_pred, priors) in \ enumerate(zip(cls_score_list, bbox_pred_list, mlvl_priors)): cls_score = np.transpose(cls_score, axes=[1, 2, 0]).reshape(-1, self.class_num) bboxes = np.transpose(bbox_pred, axes=[1, 2, 0]).reshape(-1, 4) # if self.class_num == 1: cls_score = self.sigmoid(cls_score) else: cls_score = self.softmax(cls_score, -1) bboxes = self.ssd_decode(priors, bboxes) valid_mask = np.where(cls_score > thres) # (行,列) labels = valid_mask[1] cls_score = cls_score[valid_mask] bboxes = bboxes[valid_mask[0]] mlvl_bboxes.append(bboxes) mlvl_scores.append(cls_score) mlvl_labels.append(labels) mlvl_bboxes = np.concatenate(mlvl_bboxes, axis=0) mlvl_scores = np.concatenate(mlvl_scores, axis=0) mlvl_labels = np.concatenate(mlvl_labels, axis=0) # print(mlvl_scores, mlvl_bboxes) # scale detect size to src scale_factor = np.array([[img_w / self.INPUT_W, img_h / self.INPUT_H, ] * 2]) mlvl_bboxes *= scale_factor result = self.post_process((mlvl_bboxes, mlvl_scores, mlvl_labels), thres) return result # NCNN分割基础类 class NCNNSegNet(NCNNBaseNet): OUTPUT_NODES = [ ['61', '62'] # heatmap ] def detect(self, img, thres=0.7): mat_in = self.preprocess(img) ex = self.net.create_extractor() ex.set_num_threads(self.num_threads) ex.input(self.input_names[0], mat_in) outs = [] for node in self.OUTPUT_NODES: ret, out_holes_seg = ex.extract(node) # [n,k,k] out_holes_seg = np.array(out_holes_seg) outs.append(out_holes_seg) mat_in.release() return outs @abstractmethod def post_process(self, outs, thres): raise NotImplementedError('must be overwrite.') pass