#include #include #include #include #include #include #include #include #include "NvInfer.h" #include "cuda_runtime_api.h" #include "logging.h" #include "include/utils.hpp" #include "preprocess.h" #define MAX_IMAGE_INPUT_SIZE_THRESH 5000 * 5000 struct bbox { float x1,x2,y1,y2; float landmarks[8]; float score; }; bool my_func(bbox a,bbox b) { return a.score>b.score; } float get_IOU(bbox a,bbox b) { float x1 = std::max(a.x1,b.x1); float x2 = std::min(a.x2,b.x2); float y1 = std::max(a.y1,b.y1); float y2 = std::min(a.y2,b.y2); float w = std::max(0.0f,x2-x1); float h = std::max(0.0f,y2-y1); float inter_area = w*h; float union_area = (a.x2-a.x1)*(a.y2-a.y1)+(b.x2-b.x1)*(b.y2-b.y1)-inter_area; float IOU = 1.0*inter_area/ union_area; return IOU; } #define CHECK(status) \ do\ {\ auto ret = (status);\ if (ret != 0)\ {\ std::cerr << "Cuda failure: " << ret << std::endl;\ abort();\ }\ } while (0) #define DEVICE 0 // GPU id #define NMS_THRESH 0.45 #define BBOX_CONF_THRESH 0.3 using namespace nvinfer1; // stuff we know about the network and the input/output blobs const std::vector plate_string={"#","京","沪","津","渝","冀","晋","蒙","辽","吉","黑","苏","浙","皖", \ "闽","赣","鲁","豫","鄂","湘","粤","桂","琼","川","贵","云","藏","陕","甘","青","宁","新","学","警","港","澳","挂","使","领","民","航","深", \ "0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z"}; const std::vector plate_string_yinwen={"#","","","","","","","","","","","","","", \ "","","","","","","","","","","","","","","","",\ "","","","","","","","","","","","", \ "0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z"}; static const int INPUT_W = 640; static const int INPUT_H = 640; static const int NUM_CLASSES = 2; //单层车牌,双层车牌两类 const char* INPUT_BLOB_NAME = "images"; //onnx 输入 名字 const char* OUTPUT_BLOB_NAME = "output"; //onnx 输出 名字 static Logger gLogger; cv::Mat static_resize(cv::Mat& img,int &top,int &left) //对应yolov5中的letter_box { float r = std::min(INPUT_W / (img.cols*1.0), INPUT_H / (img.rows*1.0)); // r = std::min(r, 1.0f); int unpad_w = r * img.cols; int unpad_h = r * img.rows; left = (INPUT_W-unpad_w)/2; top = (INPUT_H-unpad_h)/2; int right = INPUT_W-unpad_w-left; int bottom = INPUT_H-unpad_h-top; cv::Mat re(unpad_h, unpad_w, CV_8UC3); cv::resize(img, re, re.size()); cv::Mat out; cv::copyMakeBorder(re,out,top,bottom,left,right,cv::BORDER_CONSTANT,cv::Scalar(114,114,114)); return out; } struct Object { cv::Rect_ rect; // float landmarks[8]; //4个关键点 int label; float prob; }; static inline float intersection_area(const Object& a, const Object& b) { cv::Rect_ inter = a.rect & b.rect; return inter.area(); } static void qsort_descent_inplace(std::vector& faceobjects, int left, int right) { int i = left; int j = right; float p = faceobjects[(left + right) / 2].prob; while (i <= j) { while (faceobjects[i].prob > p) i++; while (faceobjects[j].prob < p) j--; if (i <= j) { // swap std::swap(faceobjects[i], faceobjects[j]); i++; j--; } } #pragma omp parallel sections { #pragma omp section { if (left < j) qsort_descent_inplace(faceobjects, left, j); } #pragma omp section { if (i < right) qsort_descent_inplace(faceobjects, i, right); } } } static void qsort_descent_inplace(std::vector& objects) { if (objects.empty()) return; qsort_descent_inplace(objects, 0, objects.size() - 1); } static void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& picked, float nms_threshold) { picked.clear(); const int n = faceobjects.size(); std::vector areas(n); for (int i = 0; i < n; i++) { areas[i] = faceobjects[i].rect.area(); } for (int i = 0; i < n; i++) { const Object& a = faceobjects[i]; int keep = 1; for (int j = 0; j < (int)picked.size(); j++) { const Object& b = faceobjects[picked[j]]; // intersection over union float inter_area = intersection_area(a, b); float union_area = areas[i] + areas[picked[j]] - inter_area; // float IoU = inter_area / union_area if (inter_area / union_area > nms_threshold) keep = 0; } if (keep) picked.push_back(i); } } std::vector my_nms(std::vector &bboxes, float nms_threshold) { std:: vector choice; for(int i = 0; inms_threshold) keep = 0; } if (keep) choice.push_back(i); } return choice; } int find_max(float *prob,int num) //找到类别 { int max= 0; for(int i=1; i &objects,int OUTPUT_CANDIDATES) { const int num_class = 2; const int ckpt=12 ; //yolov7 是12,yolov5是8 const int num_anchors = OUTPUT_CANDIDATES; for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++) { // const int basic_pos = anchor_idx * (num_class + 5 + 1); // float box_objectness = feat_blob[basic_pos + 4]; // int cls_id = feat_blob[basic_pos + 5]; // float score = feat_blob[basic_pos + 5 + 1 + cls_id]; // score *= box_objectness; const int basic_pos = anchor_idx * (num_class + 5 + ckpt); //5代表 x,y,w,h,object_score 8代表4个关键点 float box_objectness = feat_blob[basic_pos + 4]; // int cls_id = find_max(&feat_blob[basic_pos +5+ckpt],num_class); //找到类别v5 int cls_id = find_max(&feat_blob[basic_pos +5],num_class); //v7 // float score = feat_blob[basic_pos + 5 +8 + cls_id]; //v5 float score = feat_blob[basic_pos + 5 + cls_id]; //v7 score *= box_objectness; if (score > prob_threshold) { // yolox/models/yolo_head.py decode logic float x_center = feat_blob[basic_pos + 0]; float y_center = feat_blob[basic_pos + 1]; float w = feat_blob[basic_pos + 2]; float h = feat_blob[basic_pos + 3]; float x0 = x_center - w * 0.5f; float y0 = y_center - h * 0.5f; // float *landmarks=&feat_blob[basic_pos +5]; //v5 float *landmarks=&feat_blob[basic_pos +5+num_class]; Object obj; obj.rect.x = x0; obj.rect.y = y0; obj.rect.width = w; obj.rect.height = h; obj.label = cls_id; obj.prob = score; int k = 0; // for (int i = 0; i &bboxes,int OUTPUT_CANDIDATES) { const int num_class = 3; const int num_anchors = OUTPUT_CANDIDATES; for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++) { // const int basic_pos = anchor_idx * (num_class + 5 + 1); // float box_objectness = feat_blob[basic_pos + 4]; // int cls_id = feat_blob[basic_pos + 5]; // float score = feat_blob[basic_pos + 5 + 1 + cls_id]; // score *= box_objectness; const int basic_pos = anchor_idx * (num_class + 5 + 8); //5代表 x,y,w,h,object_score 8代表4个关键点 float box_objectness = feat_blob[basic_pos + 4]; int cls_id = find_max(&feat_blob[basic_pos +5+8],num_class); //找到类别 float score = feat_blob[basic_pos + 5 +8 + cls_id]; score *= box_objectness; if (score > prob_threshold) { // yolox/models/yolo_head.py decode logic float x_center = feat_blob[basic_pos + 0]; float y_center = feat_blob[basic_pos + 1]; float w = feat_blob[basic_pos + 2]; float h = feat_blob[basic_pos + 3]; float x0 = x_center - w * 0.5f; float y0 = y_center - h * 0.5f; float *landmarks=&feat_blob[basic_pos +5]; bbox obj; obj.x1=x0; obj.y1=y0; obj.x2=x0+w; obj.y2=y0+h; obj.score = score; for (int i = 0; i<8; i++) { obj.landmarks[i]=landmarks[i]; } bboxes.push_back(obj); } } } float* blobFromImage(cv::Mat& img){ float* blob = new float[img.total()*3]; int channels = 3; int img_h = img.rows; int img_w = img.cols; int k = 0; for (size_t c = 0; c < channels; c++) { for (size_t h = 0; h < img_h; h++) { for (size_t w = 0; w < img_w; w++) { // blob[c * img_w * img_h + h * img_w + w] = // (float)img.at(h, w)[c]; blob[k++] = (float)img.at(h, w)[2-c]/255.0; } } } return blob; } void blobFromImage_plate(cv::Mat& img,float mean_value,float std_value,float *blob) { // float* blob = new float[img.total()*3]; // int channels = NUM_CLASSES; int img_h = img.rows; int img_w = img.cols; int k = 0; for (size_t c = 0; c <3; c++) { for (size_t h = 0; h < img_h; h++) { for (size_t w = 0; w < img_w; w++) { blob[k++] = ((float)img.at(h, w)[c]/255.0-mean_value)/std_value; } } } // return blob; } static void decode_outputs(float* prob, std::vector& objects, float scale, const int img_w, const int img_h,int OUTPUT_CANDIDATES,int top,int left) { std::vector proposals; std::vector bboxes; generate_yolox_proposals(prob, BBOX_CONF_THRESH, proposals,OUTPUT_CANDIDATES); // generate_proposals(prob, BBOX_CONF_THRESH, bboxes,OUTPUT_CANDIDATES); // std::cout << "num of boxes before nms: " << proposals.size() << std::endl; qsort_descent_inplace(proposals); // std::sort(bboxes.begin(),bboxes.end(),my_func); std::vector picked; nms_sorted_bboxes(proposals, picked, NMS_THRESH); // auto choice =my_nms(bboxes, NMS_THRESH); int count = picked.size(); // std::cout << "num of boxes: " << count << std::endl; objects.resize(count); for (int i = 0; i < count; i++) { objects[i] = proposals[picked[i]]; // adjust offset to original unpadded float x0 = (objects[i].rect.x-left) / scale; float y0 = (objects[i].rect.y-top) / scale; float x1 = (objects[i].rect.x + objects[i].rect.width-left) / scale; float y1 = (objects[i].rect.y + objects[i].rect.height-top) / scale; float *landmarks = objects[i].landmarks; for(int i= 0; i<8; i++) { if(i%2==0) landmarks[i]=(landmarks[i]-left)/scale; else landmarks[i]=(landmarks[i]-top)/scale; } // clip x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f); y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f); x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f); y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f); objects[i].rect.x = x0; objects[i].rect.y = y0; objects[i].rect.width = x1 - x0; objects[i].rect.height = y1 - y0; } } const float color_list[4][3] = { {255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {0, 255, 255}, }; static void draw_objects(const cv::Mat& bgr, const std::vector& objects, std::string f) { static const char* class_names[] = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" }; cv::Mat image = bgr.clone(); for (size_t i = 0; i < objects.size(); i++) { const Object& obj = objects[i]; // fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob, // obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height); cv::Scalar color = cv::Scalar(color_list[obj.label][0], color_list[obj.label][1], color_list[obj.label][2]); float c_mean = cv::mean(color)[0]; cv::Scalar txt_color; if (c_mean > 0.5){ txt_color = cv::Scalar(0, 0, 0); }else{ txt_color = cv::Scalar(255, 255, 255); } cv::rectangle(image, obj.rect, color * 255, 2); char text[256]; sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100); int baseLine = 0; cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.4, 1, &baseLine); cv::Scalar txt_bk_color = color * 0.7 * 255; int x = obj.rect.x; int y = obj.rect.y + 1; //int y = obj.rect.y - label_size.height - baseLine; if (y > image.rows) y = image.rows; //if (x + label_size.width > image.cols) //x = image.cols - label_size.width; cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)), txt_bk_color, -1); cv::putText(image, text, cv::Point(x, y + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.4, txt_color, 1); } int pos = f.find_last_of("/"); auto substr = f.substr(pos+1); std::string savePath = "/mnt/Gpan/Mydata/pytorchPorject/yoloxNew/newYoloxCpp/result_pic/"+substr; cv::imwrite(savePath, image); // fprintf(stderr, "save vis file\n"); // cv::imshow("image", image); // cv::waitKey(0); } void doInference(IExecutionContext& context, float* input, float* output, const int output_size, cv::Size input_shape,const char *INPUT_BLOB_NAME,const char *OUTPUT_BLOB_NAME) { const ICudaEngine& engine = context.getEngine(); // Pointers to input and output device buffers to pass to engine. // Engine requires exactly IEngine::getNbBindings() number of buffers. assert(engine.getNbBindings() == 2); void* buffers[2]; // In order to bind the buffers, we need to know the names of the input and output tensors. // Note that indices are guaranteed to be less than IEngine::getNbBindings() const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME); assert(engine.getBindingDataType(inputIndex) == nvinfer1::DataType::kFLOAT); const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME); assert(engine.getBindingDataType(outputIndex) == nvinfer1::DataType::kFLOAT); int mBatchSize = engine.getMaxBatchSize(); // Create GPU buffers on device CHECK(cudaMalloc(&buffers[inputIndex], 3 * input_shape.height * input_shape.width * sizeof(float))); CHECK(cudaMalloc(&buffers[outputIndex], output_size*sizeof(float))); // Create stream cudaStream_t stream; CHECK(cudaStreamCreate(&stream)); // DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host CHECK(cudaMemcpyAsync(buffers[inputIndex], input, 3 * input_shape.height * input_shape.width * sizeof(float), cudaMemcpyHostToDevice, stream)); context.enqueue(1, buffers, stream, nullptr); // context.enqueueV2( buffers, stream, nullptr); CHECK(cudaMemcpyAsync(output, buffers[outputIndex], output_size * sizeof(float), cudaMemcpyDeviceToHost, stream)); cudaStreamSynchronize(stream); // Release stream and buffers cudaStreamDestroy(stream); CHECK(cudaFree(buffers[inputIndex])); CHECK(cudaFree(buffers[outputIndex])); } float getNorm2(float x,float y) { return sqrt(x*x+y*y); } cv::Mat getTransForm(cv::Mat &src_img, cv::Point2f order_rect[4]) //透视变换 { cv::Point2f w1=order_rect[0]-order_rect[1]; cv::Point2f w2=order_rect[2]-order_rect[3]; auto width1 = getNorm2(w1.x,w1.y); auto width2 = getNorm2(w2.x,w2.y); auto maxWidth = std::max(width1,width2); cv::Point2f h1=order_rect[0]-order_rect[3]; cv::Point2f h2=order_rect[1]-order_rect[2]; auto height1 = getNorm2(h1.x,h1.y); auto height2 = getNorm2(h2.x,h2.y); auto maxHeight = std::max(height1,height2); // 透视变换 std::vector pts_ori(4); std::vector pts_std(4); pts_ori[0]=order_rect[0]; pts_ori[1]=order_rect[1]; pts_ori[2]=order_rect[2]; pts_ori[3]=order_rect[3]; pts_std[0]=cv::Point2f(0,0); pts_std[1]=cv::Point2f(maxWidth,0); pts_std[2]=cv::Point2f(maxWidth,maxHeight); pts_std[3]=cv::Point2f(0,maxHeight); cv::Mat M = cv::getPerspectiveTransform(pts_ori,pts_std); cv:: Mat dstimg; cv::warpPerspective(src_img,dstimg,M,cv::Size(maxWidth,maxHeight)); return dstimg; } cv::Mat get_split_merge(cv::Mat &img) //双层车牌 分割 拼接 { cv::Rect upper_rect_area = cv::Rect(0,0,img.cols,int(5.0/12*img.rows)); cv::Rect lower_rect_area = cv::Rect(0,int(1.0/3*img.rows),img.cols,img.rows-int(1.0/3*img.rows)); cv::Mat img_upper = img(upper_rect_area); cv::Mat img_lower =img(lower_rect_area); cv::resize(img_upper,img_upper,img_lower.size()); cv::Mat out(img_lower.rows,img_lower.cols+img_upper.cols, CV_8UC3, cv::Scalar(114, 114, 114)); img_upper.copyTo(out(cv::Rect(0,0,img_upper.cols,img_upper.rows))); img_lower.copyTo(out(cv::Rect(img_upper.cols,0,img_lower.cols,img_lower.rows))); return out; } std::string decode_outputs(float *prob,int output_size) { std::string plate =""; std::string pre_str ="#"; for (int i = 0; ideserializeCudaEngine(trtModelStreamDet, size); assert(engine_det != nullptr); IExecutionContext* context_det = engine_det->createExecutionContext(); assert(context_det != nullptr); delete[] trtModelStreamDet; //rec模型trt初始化 IRuntime* runtime_rec = createInferRuntime(gLogger); assert(runtime_rec!= nullptr); ICudaEngine* engine_rec = runtime_rec->deserializeCudaEngine(trtModelStreamRec, size_rec); assert(engine_rec != nullptr); IExecutionContext* context_rec = engine_rec->createExecutionContext(); assert(context_rec != nullptr); delete[] trtModelStreamRec; float *buffers[2]; const int inputIndex = engine_det->getBindingIndex(INPUT_BLOB_NAME); const int outputIndex = engine_det->getBindingIndex(OUTPUT_BLOB_NAME); assert(inputIndex == 0); assert(outputIndex == 1); // Create GPU buffers on device auto out_dims = engine_det->getBindingDimensions(1); auto output_size = 1; int OUTPUT_CANDIDATES = out_dims.d[1]; for(int j=0;jgetBindingDimensions(1); auto output_size_rec = 1; int OUTPUT_CANDIDATES_REC = out_dims_rec.d[1]; for(int j=0;j imagList; std::vectorfileType{"jpg","png"}; readFileList(const_cast(imgPath.c_str()),imagList,fileType); double sumTime = 0; int index = 0; for (auto &input_image_path:imagList) { cv::Mat img = cv::imread(input_image_path); double begin_time = cv::getTickCount(); float *buffer_idx = (float*)buffers[inputIndex]; size_t size_image = img.cols * img.rows * 3; size_t size_image_dst = INPUT_H * INPUT_W * 3; memcpy(img_host, img.data, size_image); CHECK(cudaMemcpyAsync(img_device, img_host, size_image, cudaMemcpyHostToDevice, stream)); preprocess_kernel_img(img_device, img.cols, img.rows, buffer_idx, INPUT_W, INPUT_H, stream); double time_pre = cv::getTickCount(); double time_pre_=(time_pre-begin_time)/cv::getTickFrequency()*1000; // std::cout<<"preprocessing time is "< objects; decode_outputs(prob, objects, scale, img_w, img_h,OUTPUT_CANDIDATES,top,left); // std::cout << std::chrono::duration_cast(end - start).count() << "ms" << std::endl; // std::cout << std::chrono::duration_cast(end - start).count() << "ms" << std::endl; std::cout<destroy(); engine_det->destroy(); runtime_det->destroy(); context_rec->destroy(); engine_rec->destroy(); runtime_rec->destroy(); delete [] blob_rec; cudaStreamDestroy(stream); CHECK(cudaFree(img_device)); CHECK(cudaFreeHost(img_host)); CHECK(cudaFree(buffers[inputIndex])); CHECK(cudaFree(buffers[outputIndex])); return 0; }