├── FastestDet.onnx ├── README.md ├── coco.names ├── data ├── 0.jpg ├── 1.jpg ├── 2.jpg ├── 3.jpg └── 4.jpg ├── main.cpp └── main.py /FastestDet.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/FastestDet-opencv-dnn/d91f1273d1e6d0728ec7807bac7d8dc3627da206/FastestDet.onnx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastestDet-opencv-dnn 2 | 使用OpenCV部署FastestDet,包含C++和Python两种版本的程序 3 | 4 | 在知乎上看到一篇在文章《FastestDet: 比yolo-fastest更快!更强!更简单!全新设计的超实时Anchor-free目标检测算法》, 5 | 于是我就导出onnx文件,编写了使用OpenCV部署FastestDet,依然是包含C++和Python两种版本的程序。 6 | .onnx文件很小,只有960kb,不超过1M的。适合应用到对实时性要求高的场景里。 7 | -------------------------------------------------------------------------------- /coco.names: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorcycle 5 | airplane 6 | bus 7 | train 8 | truck 9 | boat 10 | traffic light 11 | fire hydrant 12 | stop sign 13 | parking meter 14 | bench 15 | bird 16 | cat 17 | dog 18 | horse 19 | sheep 20 | cow 21 | elephant 22 | bear 23 | zebra 24 | giraffe 25 | backpack 26 | umbrella 27 | handbag 28 | tie 29 | suitcase 30 | frisbee 31 | skis 32 | snowboard 33 | sports ball 34 | kite 35 | baseball bat 36 | baseball glove 37 | skateboard 38 | surfboard 39 | tennis racket 40 | bottle 41 | wine glass 42 | cup 43 | fork 44 | knife 45 | spoon 46 | bowl 47 | banana 48 | apple 49 | sandwich 50 | orange 51 | broccoli 52 | carrot 53 | hot dog 54 | pizza 55 | donut 56 | cake 57 | chair 58 | couch 59 | potted plant 60 | bed 61 | dining table 62 | toilet 63 | tv 64 | laptop 65 | mouse 66 | remote 67 | keyboard 68 | cell phone 69 | microwave 70 | oven 71 | toaster 72 | sink 73 | refrigerator 74 | book 75 | clock 76 | vase 77 | scissors 78 | teddy bear 79 | hair drier 80 | toothbrush 81 | -------------------------------------------------------------------------------- /data/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/FastestDet-opencv-dnn/d91f1273d1e6d0728ec7807bac7d8dc3627da206/data/0.jpg -------------------------------------------------------------------------------- /data/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/FastestDet-opencv-dnn/d91f1273d1e6d0728ec7807bac7d8dc3627da206/data/1.jpg -------------------------------------------------------------------------------- /data/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/FastestDet-opencv-dnn/d91f1273d1e6d0728ec7807bac7d8dc3627da206/data/2.jpg -------------------------------------------------------------------------------- /data/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/FastestDet-opencv-dnn/d91f1273d1e6d0728ec7807bac7d8dc3627da206/data/3.jpg -------------------------------------------------------------------------------- /data/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/FastestDet-opencv-dnn/d91f1273d1e6d0728ec7807bac7d8dc3627da206/data/4.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace cv; 9 | using namespace dnn; 10 | using namespace std; 11 | 12 | class FastestDet 13 | { 14 | public: 15 | FastestDet(float confThreshold, float nmsThreshold); 16 | void detect(Mat& frame); 17 | private: 18 | const int inpWidth = 512; 19 | const int inpHeight = 512; 20 | vector class_names; 21 | int num_class; 22 | 23 | float confThreshold; 24 | float nmsThreshold; 25 | Net net; 26 | void drawPred(float conf, int left, int top, int right, int bottom, Mat& frame, int classid); 27 | }; 28 | 29 | FastestDet::FastestDet(float confThreshold, float nmsThreshold) 30 | { 31 | this->confThreshold = confThreshold; 32 | this->nmsThreshold = nmsThreshold; 33 | 34 | this->net = readNet("FastestDet.onnx"); 35 | ifstream ifs("coco.names"); 36 | string line; 37 | while (getline(ifs, line)) this->class_names.push_back(line); 38 | this->num_class = class_names.size(); 39 | } 40 | 41 | void FastestDet::drawPred(float conf, int left, int top, int right, int bottom, Mat& frame, int classid) // Draw the predicted bounding box 42 | { 43 | //Draw a rectangle displaying the bounding box 44 | rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 0, 255), 2); 45 | 46 | //Get the label for the class name and its confidence 47 | string label = format("%.2f", conf); 48 | label = this->class_names[classid] + ":" + label; 49 | 50 | //Display the label at the top of the bounding box 51 | int baseLine; 52 | Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); 53 | top = max(top, labelSize.height); 54 | //rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED); 55 | putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 1); 56 | } 57 | 58 | inline float sigmoid(float x) 59 | { 60 | return 1.0 / (1 + expf(-x)); 61 | } 62 | 63 | void FastestDet::detect(Mat& frame) 64 | { 65 | Mat blob = blobFromImage(frame, 1 / 255.0, Size(this->inpWidth, this->inpHeight), Scalar(0, 0, 0), false, false); 66 | this->net.setInput(blob); 67 | vector outs; 68 | this->net.forward(outs, this->net.getUnconnectedOutLayersNames()); 69 | 70 | int num_proposal = outs[0].size[0]; 71 | int nout = outs[0].size[1]; 72 | /////generate proposals 73 | vector confidences; 74 | vector boxes; 75 | vector classIds; 76 | 77 | int i = 0, j = 0, row_ind = 0; ///box_score, xmin,ymin,xamx,ymax,class_score 78 | const int num_grid_x = 32; 79 | const int num_grid_y = 32; 80 | float* pdata = (float*)outs[0].data; 81 | 82 | for (i = 0; i < num_grid_y; i++) 83 | { 84 | for (j = 0; j < num_grid_x; j++) 85 | { 86 | Mat scores = outs[0].row(row_ind).colRange(5, nout); 87 | Point classIdPoint; 88 | double max_class_socre; 89 | // Get the value and location of the maximum score 90 | minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint); 91 | max_class_socre *= pdata[0]; 92 | if (max_class_socre > this->confThreshold) 93 | { 94 | const int class_idx = classIdPoint.x; 95 | float cx = (tanh(pdata[1]) + j) / (float)num_grid_x; ///cx 96 | float cy = (tanh(pdata[2]) + i) / (float)num_grid_y; ///cy 97 | float w = sigmoid(pdata[3]); ///w 98 | float h = sigmoid(pdata[4]); ///h 99 | 100 | cx *= float(frame.cols); 101 | cy *= float(frame.rows); 102 | w *= float(frame.cols); 103 | h *= float(frame.rows); 104 | 105 | int left = int(cx - 0.5 * w); 106 | int top = int(cy - 0.5 * h); 107 | 108 | confidences.push_back((float)max_class_socre); 109 | boxes.push_back(Rect(left, top, int(w), int(h))); 110 | classIds.push_back(class_idx); 111 | } 112 | row_ind++; 113 | pdata += nout; 114 | } 115 | } 116 | 117 | // Perform non maximum suppression to eliminate redundant overlapping boxes with 118 | // lower confidences 119 | vector indices; 120 | dnn::NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices); 121 | for (size_t i = 0; i < indices.size(); ++i) 122 | { 123 | int idx = indices[i]; 124 | Rect box = boxes[idx]; 125 | this->drawPred(confidences[idx], box.x, box.y, 126 | box.x + box.width, box.y + box.height, frame, classIds[idx]); 127 | } 128 | } 129 | 130 | int main() 131 | { 132 | FastestDet FastestDet_model(0.8, 0.35); 133 | string imgpath = "data/3.jpg"; 134 | Mat srcimg = imread(imgpath); 135 | FastestDet_model.detect(srcimg); 136 | 137 | static const string kWinName = "Deep learning object detection in OpenCV"; 138 | namedWindow(kWinName, WINDOW_NORMAL); 139 | imshow(kWinName, srcimg); 140 | waitKey(0); 141 | destroyAllWindows(); 142 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import argparse 4 | 5 | class FastestDet(): 6 | def __init__(self, confThreshold=0.3, nmsThreshold=0.4): 7 | self.classes = list(map(lambda x: x.strip(), open('coco.names', 8 | 'r').readlines())) ###这个是在coco数据集上训练的模型做opencv部署的,如果你在自己的数据集上训练出的模型做opencv部署,那么需要修改self.classes 9 | self.inpWidth = 512 10 | self.inpHeight = 512 11 | self.net = cv2.dnn.readNet('FastestDet.onnx') 12 | self.confThreshold = confThreshold 13 | self.nmsThreshold = nmsThreshold 14 | self.H, self.W = 32, 32 15 | self.grid = self._make_grid(self.W, self.H) 16 | 17 | def _make_grid(self, nx=20, ny=20): 18 | xv, yv = np.meshgrid(np.arange(ny), np.arange(nx)) 19 | return np.stack((xv, yv), 2).reshape((-1, 2)).astype(np.float32) 20 | 21 | def postprocess(self, frame, outs): 22 | frameHeight = frame.shape[0] 23 | frameWidth = frame.shape[1] 24 | # Scan through all the bounding boxes output from the network and keep only the 25 | # ones with high confidence scores. Assign the box's class label as the class with the highest score. 26 | classIds = [] 27 | confidences = [] 28 | boxes = [] 29 | for detection in outs: 30 | scores = detection[5:] 31 | classId = np.argmax(scores) 32 | confidence = scores[classId] * detection[0] 33 | if confidence > self.confThreshold: 34 | center_x = int(detection[1] * frameWidth) 35 | center_y = int(detection[2] * frameHeight) 36 | width = int(detection[3] * frameWidth) 37 | height = int(detection[4] * frameHeight) 38 | left = int(center_x - width / 2) 39 | top = int(center_y - height / 2) 40 | classIds.append(classId) 41 | # confidences.append(float(confidence)) 42 | confidences.append(float(confidence)) 43 | boxes.append([left, top, width, height]) 44 | 45 | # Perform non maximum suppression to eliminate redundant overlapping boxes with 46 | # lower confidences. 47 | indices = cv2.dnn.NMSBoxes(boxes, confidences, self.confThreshold, self.nmsThreshold).flatten() 48 | for i in indices: 49 | box = boxes[i] 50 | left = box[0] 51 | top = box[1] 52 | width = box[2] 53 | height = box[3] 54 | frame = self.drawPred(frame, classIds[i], confidences[i], left, top, left + width, top + height) 55 | return frame 56 | 57 | def drawPred(self, frame, classId, conf, left, top, right, bottom): 58 | # Draw a bounding box. 59 | cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), thickness=2) 60 | 61 | label = '%.2f' % conf 62 | label = '%s:%s' % (self.classes[classId], label) 63 | 64 | # Display the label at the top of the bounding box 65 | labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) 66 | top = max(top, labelSize[1]) 67 | # cv.rectangle(frame, (left, top - round(1.5 * labelSize[1])), (left + round(1.5 * labelSize[0]), top + baseLine), (255,255,255), cv.FILLED) 68 | cv2.putText(frame, label, (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), thickness=1) 69 | return frame 70 | 71 | def sigmoid(self, x): 72 | return 1 / (1 + np.exp(-x)) 73 | 74 | def detect(self, srcimg): 75 | blob = cv2.dnn.blobFromImage(srcimg, 1 / 255.0, (self.inpWidth, self.inpHeight)) 76 | self.net.setInput(blob) 77 | pred = self.net.forward(self.net.getUnconnectedOutLayersNames())[0] 78 | pred[:, 3:5] = self.sigmoid(pred[:, 3:5]) ###w,h 79 | pred[:, 1:3] = (np.tanh(pred[:, 1:3]) + self.grid) / np.tile(np.array([self.W,self.H]), (pred.shape[0], 1)) ###cx,cy 80 | srcimg = self.postprocess(srcimg, pred) 81 | return srcimg 82 | 83 | if __name__ == '__main__': 84 | parser = argparse.ArgumentParser() 85 | parser.add_argument('--imgpath', type=str, default='data/3.jpg', help="image path") 86 | parser.add_argument('--confThreshold', default=0.8, type=float, help='class confidence') 87 | parser.add_argument('--nmsThreshold', default=0.35, type=float, help='nms iou thresh') 88 | args = parser.parse_args() 89 | 90 | srcimg = cv2.imread(args.imgpath) 91 | model = FastestDet(confThreshold=args.confThreshold, nmsThreshold=args.nmsThreshold) 92 | srcimg = model.detect(srcimg) 93 | 94 | winName = 'Deep learning object detection in OpenCV' 95 | cv2.namedWindow(winName, 0) 96 | cv2.imshow(winName, srcimg) 97 | cv2.waitKey(0) 98 | cv2.destroyAllWindows() --------------------------------------------------------------------------------