如何用C++结合ONNX与OpenCV实现图像超分辨率?(Python转C++出错求助)
解决AnimeSR ONNX模型C++版本预处理/后处理问题
参考Python实现(可正常运行)
session = onnxruntime.InferenceSession('animesr.onnx') img = cv2.imread('imgs/naruto.jpg') ori_h, ori_w, _ = img.shape img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (512,512)) img = (np.array(img) / 255.0).astype(np.float32) img = np.transpose(img, (2, 0, 1)) img = np.expand_dims(img, 0) input_name = session.get_inputs()[0].name output_name = session.get_outputs()[0].name input_feed = {input_name: img} output = session.run([output_name], input_feed) output = output[0].clip(0, 1) * 255 output = output.astype(np.uint8) output = np.squeeze(output) output = np.transpose(output, (1, 2, 0)) output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) output = cv2.resize(output, (ori_w*4, ori_h*4)) cv2.imwrite('naruto_animesr.jpg', output)
正确的C++实现代码
Ort::Session session(env, ORT_TSTR(modelPath), sessionOptions); cv::Mat inputImage = cv::imread(imagePath, cv::IMREAD_COLOR); if (inputImage.empty()) { // 处理图像读取失败逻辑 return; } int ori_h = inputImage.rows; int ori_w = inputImage.cols; // ---------------------- 预处理环节 ---------------------- cv::Mat resizedImage; cv::resize(inputImage, resizedImage, cv::Size(512, 512)); // BGR转RGB cv::Mat rgbImage; cv::cvtColor(resizedImage, rgbImage, cv::COLOR_BGR2RGB); // 转换为float并归一化 cv::Mat floatImage; rgbImage.convertTo(floatImage, CV_32FC3, 1.0 / 255.0); // 将HWC布局转为CHW布局 std::vector<float> inputData(3 * 512 * 512); int idx = 0; for (int c = 0; c < 3; ++c) { for (int h = 0; h < 512; ++h) { for (int w = 0; w < 512; ++w) { inputData[idx++] = floatImage.at<cv::Vec3f>(h, w)[c]; } } } // 创建ONNX输入张量 Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); std::vector<int64_t> inputShape = {1, 3, 512, 512}; Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, inputData.data(), inputData.size(), inputShape.data(), inputShape.size()); // 动态获取输入输出名称(避免硬编码) Ort::AllocatorWithDefaultOptions allocator; const char* inputName = session.GetInputName(0, allocator); const char* outputName = session.GetOutputName(0, allocator); std::vector<const char*> inputNames = {inputName}; std::vector<const char*> outputNames = {outputName}; // 运行模型 std::vector<Ort::Value> outputTensor = session.Run(Ort::RunOptions{}, inputNames.data(), &inputTensor, 1, outputNames.data(), 1); // ---------------------- 后处理环节 ---------------------- Ort::TensorTypeAndShapeInfo outputInfo = outputTensor[0].GetTensorTypeAndShapeInfo(); std::vector<int64_t> outputShape = outputInfo.GetShape(); int channels = outputShape[1]; // 3 int height = outputShape[2]; // 2048 int width = outputShape[3]; // 2048 const float* outputData = outputTensor[0].GetTensorData<float>(); // 将CHW布局转为HWC的RGB图像,同时完成clip(0,1)操作 cv::Mat outputImage(height, width, CV_32FC3); idx = 0; for (int c = 0; c < 3; ++c) { for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { float val = std::max(0.0f, std::min(1.0f, outputData[idx++])); outputImage.at<cv::Vec3f>(h, w)[c] = val * 255.0f; } } } // 转换为uint8格式,RGB转BGR cv::Mat uint8Image; outputImage.convertTo(uint8Image, CV_8UC3); cv::Mat bgrOutput; cv::cvtColor(uint8Image, bgrOutput, cv::COLOR_RGB2BGR); // 缩放回原图的4倍大小 cv::Mat finalOutput; cv::resize(bgrOutput, finalOutput, cv::Size(ori_w * 4, ori_h * 4)); // 保存结果 cv::imwrite(outputPath, finalOutput);
关键修正点
- 输入布局转换:OpenCV的
Mat默认是HWC(高、宽、通道)的BGR顺序,而模型需要CHW(通道、高、宽)的RGB顺序,必须手动遍历转置通道,不能直接用floatImage.data作为张量输入,否则会导致通道顺序混乱。 - 输出clip操作:补全Python代码中
clip(0,1)的逻辑,避免像素值溢出导致的异常色彩。 - 输出布局转换:模型输出是CHW布局,需要转置回HWC后再转换为BGR格式,最后缩放至原图4倍尺寸,和Python逻辑完全对齐。
- 动态名称获取:通过
session.GetInputName动态获取输入输出名称,避免硬编码名称导致的模型兼容性问题。
内容的提问来源于stack exchange,提问作者Rizeky
相关产品推荐
相关产品推荐

