#include #include #include #include #include #include #include #include #include namespace migraphxSamples { Classifier::Classifier() {} Classifier::~Classifier() { configurationFile.release(); } cv::Mat Classifier::Preprocess(const std::vector& srcImages) { // 数据预处理 std::vector image; for(int i = 0; i < srcImages.size(); ++i) { // BGR转换为RGB cv::Mat imgRGB; cv::cvtColor(srcImages[i], imgRGB, cv::COLOR_BGR2RGB); // 调整大小,使短边为256,保持长宽比 cv::Mat shrink; float ratio = (float)256 / min(imgRGB.cols, imgRGB.rows); if(imgRGB.rows > imgRGB.cols) { cv::resize(imgRGB, shrink, cv::Size(256, int(ratio * imgRGB.rows)), 0, 0); } else { cv::resize(imgRGB, shrink, cv::Size(int(ratio * imgRGB.cols), 256), 0, 0); } // 裁剪中心窗口为224*224 int start_x = shrink.cols / 2 - 224 / 2; int start_y = shrink.rows / 2 - 224 / 2; cv::Rect rect(start_x, start_y, 224, 224); cv::Mat images = shrink(rect); image.push_back(images); } // normalize并转换为NCHW cv::Mat inputBlob; Image2BlobParams image2BlobParams; image2BlobParams.scalefactor = cv::Scalar(1 / 58.395, 1 / 57.12, 1 / 57.375); image2BlobParams.mean = cv::Scalar(123.675, 116.28, 103.53); image2BlobParams.swapRB = false; blobFromImagesWithParams(image, inputBlob, image2BlobParams); return inputBlob; } ErrorCode Classifier::Initialize(InitializationParameterOfClassifier initializationParameterOfClassifier) { // 读取配置文件 std::string configFilePath = initializationParameterOfClassifier.configFilePath; if(!Exists(configFilePath)) { LOG_ERROR(stdout, "no configuration file!\n"); return CONFIG_FILE_NOT_EXIST; } if(!configurationFile.open(configFilePath, cv::FileStorage::READ)) { LOG_ERROR(stdout, "fail to open configuration file\n"); return FAIL_TO_OPEN_CONFIG_FILE; } LOG_INFO(stdout, "succeed to open configuration file\n"); // 获取配置文件参数 cv::FileNode netNode = configurationFile["Classifier"]; std::string modelPath = (std::string)netNode["ModelPath"]; useInt8 = (bool)(int)netNode["UseInt8"]; useFP16 = (bool)(int)netNode["UseFP16"]; useoffloadcopy = (bool)(int)netNode["Useoffloadcopy"]; // 加载模型 if(!Exists(modelPath)) { LOG_ERROR(stdout, "%s not exist!\n", modelPath.c_str()); return MODEL_NOT_EXIST; } net = migraphx::parse_onnx(modelPath); LOG_INFO(stdout, "succeed to load model: %s\n", GetFileName(modelPath).c_str()); // 获取模型输入/输出节点信息 std::unordered_map inputs = net.get_inputs(); std::unordered_map outputs = net.get_outputs(); inputName = inputs.begin()->first; inputShape = inputs.begin()->second; outputName = outputs.begin()->first; outputShape = outputs.begin()->second; int N = inputShape.lens()[0]; int C = inputShape.lens()[1]; int H = inputShape.lens()[2]; int W = inputShape.lens()[3]; inputSize = cv::Size(W, H); // 设置模型为GPU模式 migraphx::target gpuTarget = migraphx::gpu::target{}; // 量化 if(useInt8) { // 创建量化校准数据,建议使用测试集中的多张典型图像 cv::Mat srcImage = cv::imread("../Resource/Images/ImageNet_test.jpg", 1); std::vector srcImages; for(int i = 0; i < inputShape.lens()[0]; ++i) { srcImages.push_back(srcImage); } // 数据预处理 cv::Mat inputBlob = Preprocess(srcImages); std::unordered_map inputData; inputData[inputName] = migraphx::argument{inputShape, (float*)inputBlob.data}; std::vector> calibrationData = { inputData}; // INT8量化 migraphx::quantize_int8(net, gpuTarget, calibrationData); } else if(useFP16) { migraphx::quantize_fp16(net); } // 编译模型 migraphx::compile_options options; options.device_id = 0; // 设置GPU设备,默认为0号设备 if(useoffloadcopy) { options.offload_copy = true; } else { options.offload_copy = false; } net.compile(gpuTarget, options); LOG_INFO(stdout, "succeed to compile model: %s\n", GetFileName(modelPath).c_str()); // offloadcopy为false的时候,分配输入和输出内存 if(!useoffloadcopy) { // 分配device输入内存 inputBuffer_Device = nullptr; hipMalloc(&inputBuffer_Device, inputShape.bytes()); programParameters[inputName] = migraphx::argument{inputShape, inputBuffer_Device}; // 分配device和host输出内存 outputBuffer_Device = nullptr; hipMalloc(&outputBuffer_Device, outputShape.bytes()); programParameters[outputName] = migraphx::argument{outputShape, outputBuffer_Device}; outputBuffer_Host = nullptr; // host内存 outputBuffer_Host = malloc(outputShape.bytes()); } // warm up if(useoffloadcopy) { std::unordered_map inputData; inputData[inputName] = migraphx::argument{inputShape}; net.eval(inputData); } else { migraphx::argument inputData = migraphx::argument{inputShape}; hipMemcpy(inputBuffer_Device, inputData.data(), inputShape.bytes(), hipMemcpyHostToDevice); net.eval(programParameters); } // log LOG_INFO(stdout, "InputSize:%dx%d\n", inputSize.width, inputSize.height); LOG_INFO(stdout, "InputName:%s\n", inputName.c_str()); LOG_INFO(stdout, "UseInt8:%d\n", (int)useInt8); LOG_INFO(stdout, "UseFP16:%d\n", (int)useFP16); LOG_INFO(stdout, "Useoffloadcopy:%d\n", (int)useoffloadcopy); return SUCCESS; } ErrorCode Classifier::Classify(const std::vector& srcImages, std::vector>& predictions) { if(srcImages.size() == 0 || srcImages[0].empty() || srcImages[0].depth() != CV_8U) { LOG_ERROR(stdout, "image error!\n"); return IMAGE_ERROR; } // 数据预处理 cv::Mat inputBlob = Preprocess(srcImages); // 当offload为true时,不需要内存拷贝 if(useoffloadcopy) { std::unordered_map inputData; inputData[inputName] = migraphx::argument{inputShape, (float*)inputBlob.data}; // 推理 std::vector results = net.eval(inputData); // 获取输出节点的属性 migraphx::argument result = results[0]; // 获取第一个输出节点的数据 migraphx::shape outputShapes = result.get_shape(); // 输出节点的shape std::vector outputSize = outputShapes.lens(); // 每一维大小,维度顺序为(N,C,H,W) int numberOfOutput = outputShapes.elements(); // 输出节点元素的个数 float* logits = (float*)result.data(); // 输出节点数据指针 // 获取每张图像的预测结果 int numberOfClasses = numberOfOutput / srcImages.size(); for(int i = 0; i < srcImages.size(); ++i) { int startIndex = numberOfClasses * i; std::vector logit; for(int j = 0; j < numberOfClasses; ++j) { logit.push_back(logits[startIndex + j]); } std::vector resultOfPredictions; for(int j = 0; j < numberOfClasses; ++j) { ResultOfPrediction prediction; prediction.label = j; prediction.confidence = logit[j]; resultOfPredictions.push_back(prediction); } predictions.push_back(resultOfPredictions); } } else // 当offload为false时,需要内存拷贝 { migraphx::argument inputData = migraphx::argument{inputShape, (float*)inputBlob.data}; // 拷贝到device输入内存 hipMemcpy(inputBuffer_Device, inputData.data(), inputShape.bytes(), hipMemcpyHostToDevice); // 推理 std::vector results = net.eval(programParameters); // 获取输出节点的属性 migraphx::argument result = results[0]; // 获取第一个输出节点的数据 migraphx::shape outputShapes = result.get_shape(); // 输出节点的shape std::vector outputSize = outputShapes.lens(); // 每一维大小,维度顺序为(N,C,H,W) int numberOfOutput = outputShapes.elements(); // 输出节点元素的个数 // 将device输出数据拷贝到分配好的host输出内存 hipMemcpy(outputBuffer_Host, outputBuffer_Device, outputShapes.bytes(), hipMemcpyDeviceToHost); // 直接使用事先分配好的输出内存拷贝 // 获取每张图像的预测结果 int numberOfClasses = numberOfOutput / srcImages.size(); std::vector logit; for(int i = 0; i < srcImages.size(); ++i) { int startIndex = numberOfClasses * i; for(int j = 0; j < numberOfClasses; ++j) { logit.push_back(((float*)outputBuffer_Host)[startIndex + j]); } std::vector resultOfPredictions; for(int j = 0; j < numberOfClasses; ++j) { ResultOfPrediction prediction; prediction.label = j; prediction.confidence = logit[j]; resultOfPredictions.push_back(prediction); } predictions.push_back(resultOfPredictions); } // 释放 hipFree(inputBuffer_Device); hipFree(outputBuffer_Device); free(outputBuffer_Host); } return SUCCESS; } } // namespace migraphxSamples