首先感谢大佬的开源代码
GitHub - fb029ed/yolov5_cpp_openvino: 用c++实现了yolov5使用openvino的部署
以及他写的原版yolov5部署代码
C++实现yolov5的OpenVINO部署_Tom Hardy的博客-CSDN博客
接下来我要在他的代码的基础上,部署yoloface四点模型代码
在大佬原版的模型中,输出为1*3*20*20*85(其中一个detect的输出,剩下两个的20分别变为40和80。)这里85=conf(1)+xywh(4)+cls(80)
而我训练的模型,输出为1*3*20*20*30,30=conf(1)+xywh(4)+point(2*4)+cls(17)。共4个关键点,17个类。
由于我的训练模型对长宽比有着较高的要求,所以对图片预处理时,不能直接resize,而要进行灰度填充。
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True): #yoloface # Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232 shape = img.shape[:2] # current shape [height, width] if isinstance(new_shape, int): new_shape = (new_shape, new_shape) # Scale ratio (new / old) r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) if not scaleup: # only scale down, do not scale up (for better test mAP) r = min(r, 1.0) # Compute padding ratio = r, r # width, height ratios new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding if auto: # minimum rectangle dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding elif scaleFill: # stretch dw, dh = 0.0, 0.0 new_unpad = (new_shape[1], new_shape[0]) ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios dw /= 2 # divide padding into 2 sides dh /= 2 if shape[::-1] != new_unpad: # resize img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border return img, ratio, (dw, dh)
我要将改写的C++代码写到了类Detector里。这个代码传入的参数有Mat类型。因此,我在头文件中进行如下声明
void letterbox(Mat& inframe,Mat& outframe,int x_size,int y_size);
在.cpp文件中如下实现
void Detector::letterbox(Mat& inframe,Mat& outframe,int x_size,int y_size) //定义一个用于将图片填充成正方形的函数 参数:(输入图像,输出图像){ int x_ori = inframe.cols; //得到图片宽度 int y_ori = inframe.rows; //得到图片高度 cout<<"宽:"<= r_y) // 在上下方向填充 那么y的理论高度应该是x_ori/x_size*y_size { int y0 = y_size * x_ori / x_size; int y_pad = (y0 - y_ori)/2; cv::copyMakeBorder(inframe,inframe,y_pad,y_pad,0,0, cv::BORDER_CONSTANT,20); } else // 在上下方向填充 那么y的理论高度应该是x_ori/x_size*y_size { int x0 = x_size * y_ori / y_size; int x_pad = (x0 - x_ori)/2; cv::copyMakeBorder(inframe,inframe,0,0,x_pad,x_pad, cv::BORDER_CONSTANT,20); }}
在函数中如下调用
letterbox(inframe,inframe,640,640);
Mat osrc ; detector->letterbox(src,osrc,640,640);
原因:之前声明和定义时没有用&,不用这个就不会传递参数!
int s[3] = {40,20,80};
这一层detect探头的顺序是40,20,80,不是这个顺序的话,输出的结果不但是错误的,而且有2-3种不同的错误结果,每次运行不一样,不知道为什么错误结果还不同。
一定要分清vector和他存储的元素。头文件没有单独定义vector,但是在声明函数的时候变量处要写vector,而且要加&,如下
bool parse_yolov5(const Blob::Ptr &blob,int net_grid,float cof_threshold, vector& o_rect,vector& o_rect_cof,vector& point_list);
框没有问题,但是点非常的狂放不羁。
原因:
four_Point.cls = cls_id; four_Point.x1 = x1 * anchors[n * 2] + j * 640.0f / net_grid; four_Point.y1 = y1 * anchors[n * 2 + 1] + i * 640.0f / net_grid; four_Point.x2 = x2 * anchors[n * 2] + j * 640.0f / net_grid; four_Point.y2 = y2 * anchors[n * 2 + 1] + i * 640.0f / net_grid; four_Point.x3 = x3 * anchors[n * 2] + j * 640.0f / net_grid; four_Point.y3 = y3 * anchors[n * 2 + 1] + i * 640.0f / net_grid; four_Point.x4 = x4 * anchors[n * 2] + j * 640.0f / net_grid; four_Point.y4 = y4 * anchors[n * 2 + 1] + i * 640.0f / net_grid;
上面这段代码我无意中多加了个括号,导致差距被放大了16倍。