作为 深入浅出retinaFace的第一篇文章 ,小编带大家看一下 retinaFace学到的特征。
先上代码,代码修改自 insightFace官方出品的,github上可以直接搜索即可,star最高的就是,本文使用pycaffe作为推理框架,喜欢动手的朋友可以实践一下哦,画图软件使用的是matplotlib,windows下的同学需要安装tkinter框架才能使用matplotlib
代码如下:
# coding=utf-8 import sys import numpy as np import cv2 import math from skimage import transform as tf import caffe _ratio = (1.,) _feat_stride_fpn = [32, 16, 8] anchor_stride = 32 anchor_cfg = { '32': {'SCALES': (32, 16), 'BASE_SIZE': 16, 'RATIOS': _ratio, 'ALLOWED_BORDER': 9999}, '16': {'SCALES': (8, 4), 'BASE_SIZE': 16, 'RATIOS': _ratio, 'ALLOWED_BORDER': 9999}, '8': {'SCALES': (2, 1), 'BASE_SIZE': 16, 'RATIOS': _ratio, 'ALLOWED_BORDER': 9999}, } preset_anchors = [] import matplotlib matplotlib.use('agg') import matplotlib.pyplot as plt weight_file = "model/mnetv2_0_25.caffemodel" model_file = "model/mnetv2_0_25.prototxt" net = caffe.Net(model_file, weight_file, caffe.TEST) img = cv2.imread("s2.jpg") rows, cols, ch = img.shape scale = 1 w, h = int(rows * scale), int(cols * scale) scale_img = tf.resize(img, (w, h)) net.blobs['data'].reshape(1, 3, w, h) transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) transformer.set_transpose('data', (2, 0, 1)) transformer.set_channel_swap('data', (2, 1, 0)) transformer.set_raw_scale('data', 255.0) out = net.forward_all(data=np.asarray([transformer.preprocess('data', scale_img)])) featMaps = [] # 给定一个 anchor(x1, y1, x2, y2),返回 anchor的中心(x,y)和 宽度及高度 def _whctrs(anchor): """ Return width, height, x center, and y center for an anchor (window). """ w = anchor[2] - anchor[0] + 1 h = anchor[3] - anchor[1] + 1 x_ctr = anchor[0] + 0.5 * (w - 1) y_ctr = anchor[1] + 0.5 * (h - 1) return w, h, x_ctr, y_ctr # 给定一组 宽度 和高度以及中心点 返回一个anchor集合 def _mkanchors(ws, hs, x_ctr, y_ctr): """ Given a vector of widths (ws) and heights (hs) around a center (x_ctr, y_ctr), output a set of anchors (windows). """ ws = ws[:, np.newaxis] hs = hs[:, np.newaxis] anchors = np.hstack((x_ctr - 0.5 * (ws - 1), y_ctr - 0.5 * (hs - 1), x_ctr + 0.5 * (ws - 1), y_ctr + 0.5 * (hs - 1))) return anchors # 根据 缩放比例 ratios 返回一个anchor的集合 def _ratio_enum(anchor, ratios): """ Enumerate a set of anchors for each aspect ratio wrt an anchor. """ w, h, x_ctr, y_ctr = _whctrs(anchor) size = w * h size_ratios = size / ratios ws = np.round(np.sqrt(size_ratios)) hs = np.round(ws * ratios) anchors = _mkanchors(ws, hs, x_ctr, y_ctr) return anchors def _scale_enum(anchor, scales): """ Enumerate a set of anchors for each scale wrt an anchor. """ w, h, x_ctr, y_ctr = _whctrs(anchor) ws = w * scales hs = h * scales anchors = _mkanchors(ws, hs, x_ctr, y_ctr) return anchors def anchors_plane(height, width, stride, base_anchors): """ Parameters ---------- height: height of plane width: width of plane stride: stride ot the original image anchors_base: (A, 4) a base set of anchors Returns ------- all_anchors: (height, width, A, 4) ndarray of anchors spreading over the plane """ A = base_anchors.shape[0] all_anchors = np.zeros((height, width, A, 4), dtype=np.float32) for iw in range(width): sw = iw * stride for ih in range(height): sh = ih * stride for k in range(A): all_anchors[ih, iw, k, 0] = base_anchors[k, 0] + sw all_anchors[ih, iw, k, 1] = base_anchors[k, 1] + sh all_anchors[ih, iw, k, 2] = base_anchors[k, 2] + sw all_anchors[ih, iw, k, 3] = base_anchors[k, 3] + sh # if all_anchors[ih, iw, k, 0] < 0 or all_anchors[ih, iw, k, 1] < 0 or all_anchors[ih, iw, k, 2] < 0 or all_anchors[ih, iw, k, 2] < 0 : # print "===>>> ", all_anchors[ih, iw, k, 0], all_anchors[ih, iw, k, 1], all_anchors[ih, iw, k, 2], all_anchors[ih, iw, k, 3] return all_anchors def generate_anchors(base_size=16, ratios=[0.2,0.5, 1, 2], scales=2 ** np.arange(2, 6), stride=12): """ Generate anchor (reference) windows by enumerating aspect ratios X scales wrt a reference (0, 0, 15, 15) window. """ base_anchor = np.array([1, 1, base_size, base_size]) - 1 ratio_anchors = _ratio_enum(base_anchor, ratios) anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales) for i in range(ratio_anchors.shape[0])]) return anchors def generate_anchors_fpn(cfg): """ Generate anchor (reference) windows by enumerating aspect ratios X scales wrt a reference (0, 0, 15, 15) window. """ RPN_FEAT_STRIDE = [] for k in cfg: RPN_FEAT_STRIDE.append(int(k)) RPN_FEAT_STRIDE = sorted(RPN_FEAT_STRIDE, reverse=True) anchors = [] for k in RPN_FEAT_STRIDE: v = cfg[str(k)] bs = v['BASE_SIZE'] __ratios = np.array(v['RATIOS']) __scales = np.array(v['SCALES']) stride = int(k) # print('anchors_fpn', bs, __ratios, __scales, file=sys.stderr) r = generate_anchors(bs, __ratios, __scales, stride) #print r # print('anchors_fpn', r.shape, file=sys.stderr) anchors.append(r) return anchors def nms(dets): dets = np.asarray(dets) thresh = 0.4 x1 = dets[:, 0] y1 = dets[:, 1] x2 = dets[:, 2] y2 = dets[:, 3] scores = dets[:, 4] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.argsort()[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) xx1 = np.maximum(x1[i], x1[order[1:]]) yy1 = np.maximum(y1[i], y1[order[1:]]) xx2 = np.minimum(x2[i], x2[order[1:]]) yy2 = np.minimum(y2[i], y2[order[1:]]) w = np.maximum(0.0, xx2 - xx1 + 1) h = np.maximum(0.0, yy2 - yy1 + 1) inter = w * h ovr = inter / (areas[i] + areas[order[1:]] - inter) inds = np.where(ovr <= thresh)[0] order = order[inds + 1] return keep def bbox_pred(boxes, box_deltas): """ Transform the set of class-agnostic boxes into class-specific boxes by applying the predicted offsets (box_deltas) :param boxes: !important [N 4] :param box_deltas: [N, 4 * num_classes] :return: [N 4 * num_classes] """ if boxes.shape[0] == 0: return np.zeros((0, box_deltas.shape[1])) boxes = boxes.astype(np.float, copy=False) widths = boxes[:, 2] - boxes[:, 0] + 1.0 heights = boxes[:, 3] - boxes[:, 1] + 1.0 ctr_x = boxes[:, 0] + 0.5 * (widths - 1.0) ctr_y = boxes[:, 1] + 0.5 * (heights - 1.0) dx = box_deltas[:, 0:1] dy = box_deltas[:, 1:2] dw = box_deltas[:, 2:3] dh = box_deltas[:, 3:4] pred_ctr_x = dx * widths[:, np.newaxis] + ctr_x[:, np.newaxis] pred_ctr_y = dy * heights[:, np.newaxis] + ctr_y[:, np.newaxis] pred_w = np.exp(dw) * widths[:, np.newaxis] pred_h = np.exp(dh) * heights[:, np.newaxis] pred_boxes = np.zeros(box_deltas.shape) # x1 pred_boxes[:, 0:1] = pred_ctr_x - 0.5 * (pred_w - 1.0) # y1 pred_boxes[:, 1:2] = pred_ctr_y - 0.5 * (pred_h - 1.0) # x2 pred_boxes[:, 2:3] = pred_ctr_x + 0.5 * (pred_w - 1.0) # y2 pred_boxes[:, 3:4] = pred_ctr_y + 0.5 * (pred_h - 1.0) if box_deltas.shape[1] > 4: pred_boxes[:, 4:] = box_deltas[:, 4:] return pred_boxes def bbox_reg(anchor, regress): # 0, 1, 2, 3 # x1,y1,x2,y2 width = anchor[2] - anchor[0] + 1 height = anchor[3] - anchor[1] + 1 ctr_x = anchor[0] + 0.5 * (width - 1.0) ctr_y = anchor[1] + 0.5 * (height - 1.0) pred_ctr_x = regress[0] * width + ctr_x pred_ctr_y = regress[1] * height + ctr_y pred_w = math.exp(regress[2]) * width pred_h = math.exp(regress[3]) * height rect = [pred_ctr_x - 0.5 * (pred_w - 1.0), pred_ctr_y - 0.5 * (pred_h - 1.0), pred_ctr_x + 0.5 * (pred_w - 1.0), pred_ctr_y + 0.5 * (pred_h - 1.0)] return rect # c裁剪 box def clip_pad(tensor, pad_shape): """ Clip boxes of the pad area. :param tensor: [n, c, H, W] :param pad_shape: [h, w] :return: [n, c, h, w] """ H, W = tensor.shape[2:] h, w = pad_shape if h < H or w < W: tensor = tensor[:, :, :h, :w].copy() return tensor def detect(): fpn_keys = [] for s in _feat_stride_fpn: fpn_keys.append('stride%s' % s) generated_anchors_fpns = generate_anchors_fpn(anchor_cfg) _anchors_fpn = dict(zip(fpn_keys, generated_anchors_fpns)) _num_anchors = dict(zip(fpn_keys, [anchors.shape[0] for anchors in _anchors_fpn.values()])) for k in _anchors_fpn: v = _anchors_fpn[k].astype(np.float32) _anchors_fpn[k] = v final_box = [] proposals_list = [] scores_list = [] for stride in _feat_stride_fpn: _key = 'stride%s' % stride cls_name = "face_rpn_cls_prob_reshape_stride%s" % stride reg_name = "face_rpn_bbox_pred_stride%s" % stride cls_blob = net.blobs[cls_name].data reg_blob = net.blobs[reg_name].data height, width = reg_blob.shape[2], reg_blob.shape[3] A = _num_anchors[_key] print("A====>>>", A) K = height * width anchors_fpn = _anchors_fpn[_key] anchors = anchors_plane(height, width, stride, anchors_fpn) anchors = anchors.reshape((K * A, 4)) for i in range(cls_blob.shape[1]): featMaps.append((cls_blob[0, i], stride)) # 从 scores(tensor) 中 获取 (height, width) 尺寸的数据 cls_blob = cls_blob[:, _num_anchors[_key]:, :, :] scores = clip_pad(cls_blob, (height, width)) scores = scores.transpose((0, 2, 3, 1)).reshape((-1, 1)) bbox_deltas = clip_pad(reg_blob, (height, width)) bbox_deltas = bbox_deltas.transpose((0, 2, 3, 1)) bbox_pred_len = bbox_deltas.shape[3] // A bbox_deltas = bbox_deltas.reshape((-1, bbox_pred_len)) proposals = bbox_pred(anchors, bbox_deltas) scores_ravel = scores.ravel() order = np.where(scores_ravel >= 0.5)[0] proposals = proposals[order, :] scores = scores[order] proposals[:, 0:4] /= scale proposals_list.append(proposals) scores_list.append(scores) ### for 循环结束 proposals = np.vstack(proposals_list) if proposals.shape[0] == 0: return np.zeros((0, 5)) # 垂直方向添加 scores_list scores = np.vstack(scores_list) # ravel 二维变为一维 scores_ravel = scores.ravel() order = scores_ravel.argsort()[::-1] proposals = proposals[order, :] scores = scores[order] pre_det = np.hstack((proposals[:, 0:4], scores)).astype(np.float32, copy=False) keep = nms(pre_det) #print keep det = np.hstack((pre_det, proposals[:, 4:])) det = det[keep, :] for b in det: #print b cv2.rectangle(img, (int(b[0]), int(b[1])), (int(b[2]), int(b[3])), (0, 0, 255), 1) # im_copy = cv2.resize(im_copy, (1024,768)) cv2.imshow("./tmp.jpg", img) cv2.imwrite("./tmp_1.jpg", img) cv2.waitKey(0) if __name__ == "__main__": detect() fig = plt.figure() # plt.subplot(221, facecolor='r').set_title('%.3f') # facecolor指定背景颜色 在之前的Python版本使用的是axisbg 现在已经改成了facecolor # plt.subplot(222, facecolor='b') # plt.subplot(223, facecolor='g') # plt.subplot(224, facecolor='y') # plt.show() num_scale = len(featMaps) norm = matplotlib.colors.Normalize(vmin=0, vmax=1) for idx, heatmap in enumerate(featMaps): plt.subplot(3, 4, idx + 1).set_title(heatmap[1]) #plt.imshow(heatmap[0], cmap='hot', origin='low') plt.imshow(heatmap[0]) plt.axis('off') func = lambda x, pos: "{:g}".format(x * 1) fmt = matplotlib.ticker.FuncFormatter(func) position = fig.add_axes([0.15, 0.05, 0.7, 0.03]) # 位置[左,下,右,上] plt.colorbar(cax=position, orientation='horizontal') #plt.colorbar() # 显示色度条 plt.show()
然后放几张学习到的特征,(前方高能,多图预警...)