|
|
@@ -11,7 +11,7 @@
|
|
|
#include "../tool/debuglog.h"
|
|
|
|
|
|
YoloFeatureExtractor::YoloFeatureExtractor(const std::string& modelPath, const std::string& classesPath)
|
|
|
- : inputWidth(448), inputHeight(448)
|
|
|
+ : inputWidth(800), inputHeight(800)
|
|
|
{
|
|
|
net = cv::dnn::readNetFromONNX(modelPath);
|
|
|
loadClassNames(classesPath);
|
|
|
@@ -85,8 +85,15 @@ std::vector<float> YoloFeatureExtractor::extractFeatures(const std::string& imag
|
|
|
|
|
|
cv::resize(image, image, cv::Size(inputWidth, inputHeight));
|
|
|
|
|
|
+ // 2. 转换为blob(归一化+通道转换)
|
|
|
+ //均值和标准差使用ImageNet标准值,与YOLO预训练分类模型一致
|
|
|
+ cv::Scalar mean = cv::Scalar(0.485 * 255, 0.456 * 255, 0.406 * 255);
|
|
|
+ cv::Scalar std = cv::Scalar(0.229 * 255, 0.224 * 255, 0.225 * 255);
|
|
|
+
|
|
|
cv::Mat blob;
|
|
|
- cv::dnn::blobFromImage(image, blob, 1.0 / 255.0, cv::Size(inputWidth, inputHeight), cv::Scalar(0, 0, 0), true, false);
|
|
|
+ cv::dnn::blobFromImage(image, blob, 1.0, cv::Size(inputWidth, inputHeight), mean, true, false);
|
|
|
+ blob /= std; // 应用标准差归一化
|
|
|
+
|
|
|
net.setInput(blob);
|
|
|
|
|
|
DEBUG_LOG::debug_printf("blob 形状:%d×%d×%d×%d\n", blob.size[0], blob.size[1], blob.size[2], blob.size[3]);
|
|
|
@@ -94,44 +101,61 @@ std::vector<float> YoloFeatureExtractor::extractFeatures(const std::string& imag
|
|
|
auto time_2 = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
std::vector<cv::String> layerNames = net.getLayerNames();
|
|
|
- std::vector<cv::String> outputNames;
|
|
|
|
|
|
std::vector<float> features;
|
|
|
|
|
|
-
|
|
|
- // 选择GAP层(对于yolo2026,通常是倒数第6层)的输出作为特征向量
|
|
|
- outputNames.push_back(layerNames[layerNames.size() - 6]);
|
|
|
-
|
|
|
- std::vector<cv::Mat> outputs;
|
|
|
- net.forward(outputs, outputNames);
|
|
|
-
|
|
|
+ cv::Mat featureMat = net.forward("onnx_node!/model.10/pool/GlobalAveragePool");
|
|
|
// 检查输出是否有效
|
|
|
- if (outputs.empty() || outputs[0].empty())
|
|
|
+ if (featureMat.empty())
|
|
|
{
|
|
|
throw std::runtime_error("模型前向传播未产生有效输出");
|
|
|
}
|
|
|
|
|
|
// 获取GAP层输出并转换为特征向量
|
|
|
- cv::Mat featuresMat = outputs[0];
|
|
|
- DEBUG_LOG::debug_printf("原始特征形状:%d×%d,类型:%d(CV_32F=5)\n", featuresMat.cols, featuresMat.rows, featuresMat.type());
|
|
|
+ //DEBUG_LOG::debug_printf("原始特征形状:%d×%d,类型:%d(CV_32F=5)\n", featureMat.cols, featureMat.rows, featureMat.type());
|
|
|
|
|
|
- cv::Mat featuresMatVec = featuresMat.reshape(1, 1);
|
|
|
- DEBUG_LOG::debug_printf("重塑后特征形状:%d×%d,类型:%d\n", featuresMatVec.cols, featuresMatVec.rows, featuresMatVec.type());
|
|
|
+ featureMat = featureMat.reshape(1, 1);
|
|
|
+ //DEBUG_LOG::debug_printf("重塑后特征形状:%d×%d,类型:%d\n", featureMat.cols, featureMat.rows, featureMat.type());
|
|
|
|
|
|
- float norm_before = cv::norm(featuresMatVec, cv::NORM_L2);
|
|
|
- DEBUG_LOG::debug_printf("归一化前 norm:%.6f\n", norm_before);
|
|
|
+ //float norm_before = cv::norm(featureMat, cv::NORM_L2);
|
|
|
+ //DEBUG_LOG::debug_printf("归一化前 norm:%.6f\n", norm_before);
|
|
|
|
|
|
- //normalizeL2(featuresMatVec);
|
|
|
- cv::normalize(featuresMat, featuresMatVec, 1.0, 0.0, cv::NORM_L2);
|
|
|
+ //normalizeL2(featureMat);
|
|
|
+ //cv::normalize(featureMat, featureMat, 1.0, 0.0, cv::NORM_L2);
|
|
|
|
|
|
- float norm_after = cv::norm(featuresMatVec, cv::NORM_L2);
|
|
|
- DEBUG_LOG::debug_printf("归一化后 norm:%.6f\n", norm_after);
|
|
|
+ //float norm_after = cv::norm(featureMat, cv::NORM_L2);
|
|
|
+ //DEBUG_LOG::debug_printf("归一化后 norm:%.6f\n", norm_after);
|
|
|
|
|
|
- features.reserve(features.size() + featuresMatVec.total());
|
|
|
- for (size_t j = 0; j < featuresMatVec.total(); ++j)
|
|
|
+ // ===================== 5. 特征向量处理 =====================
|
|
|
+ // 将Mat格式的特征转换为vector<float>(方便后续计算/存储)
|
|
|
+ std::vector<float> feature_vector;
|
|
|
+ feature_vector.assign((float *)featureMat.data, (float *)featureMat.data + featureMat.total());
|
|
|
+
|
|
|
+ // -------------------- 新增:L2归一化 -------------------
|
|
|
+ // 1. 计算L2范数
|
|
|
+ /*
|
|
|
+ float l2_norm = 0.0f;
|
|
|
+ for (float val : feature_vector)
|
|
|
{
|
|
|
- features.push_back(featuresMatVec.at<float>(j));
|
|
|
+ l2_norm += val * val;
|
|
|
}
|
|
|
+ l2_norm = sqrt(l2_norm);
|
|
|
+
|
|
|
+ // 2. 避免除以0(极端情况,特征全为0)
|
|
|
+ if (fabs(l2_norm) < 1e-8)
|
|
|
+ {
|
|
|
+ l2_norm = 1e-8;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 归一化:每个元素除以L2范数
|
|
|
+ std::vector<float> normalized_feature;
|
|
|
+ for (float val : feature_vector)
|
|
|
+ {
|
|
|
+ normalized_feature.push_back(val / l2_norm);
|
|
|
+ }
|
|
|
+ // -------------------- L2归一化结束 --------------------
|
|
|
+ */
|
|
|
+
|
|
|
|
|
|
// 转换为std::vector<float>
|
|
|
//features = std::vector<float>(featuresMatVec.begin<float>(), featuresMatVec.end<float>());
|
|
|
@@ -150,7 +174,7 @@ std::vector<float> YoloFeatureExtractor::extractFeatures(const std::string& imag
|
|
|
std::wstring msg4 = L"总耗时: " + std::to_wstring(totalDuration.count()) + L" 毫秒";
|
|
|
DEBUG_LOG(msg4.c_str());
|
|
|
|
|
|
- return features;
|
|
|
+ return feature_vector;
|
|
|
}
|
|
|
catch (const std::exception& e)
|
|
|
{
|