1. cv::Mat
与 float
数组互转
(1) cv::Mat
转 float
数组
// 假设 mat 是 CV_32FC3 类型(3通道浮点矩阵)
cv::Mat mat = cv::Mat::ones(100, 100, CV_32FC3);
// 检查矩阵是否连续存储
if (!mat.isContinuous()) {
mat = mat.clone(); // 转为连续内存
}
// 获取数据指针
float* float_array = (float*)mat.data;
// 验证数据(示例:访问第一个像素的通道值)
float b = float_array[0];
float g = float_array[1];
float r = float_array[2];
(2) float
数组转 cv::Mat
// 假设数组是 100x100x3 的浮点数据
float* float_data = new float[100 * 100 * 3];
// ... 填充数据 ...
// 创建 cv::Mat(深拷贝)
cv::Mat mat_from_array(100, 100, CV_32FC3, float_data); // 浅拷贝(注意内存生命周期)
// 或者深拷贝:
cv::Mat mat_deep;
mat_deep.create(100, 100, CV_32FC3);
memcpy(mat_deep.data, float_data, 100 * 100 * 3 * sizeof(float));
2. 实现 argmax
获取像素分类
(1) 场景说明
假设输入矩阵为多通道 cv::Mat
(例如 语义分割的输出,每个通道表示类别概率),需找到每个像素的 最大概率对应的通道索引。
(2) 核心代码
/**
* @brief 对多通道浮点矩阵执行 argmax,生成单通道分类结果
* @param src 输入矩阵(CV_32FC3、CV_32FC4 等多通道浮点类型)
* @return 单通道分类索引矩阵(CV_8UC1 或 CV_32SC1)
*/
cv::Mat argmax(const cv::Mat& src) {
// 检查输入类型
CV_Assert(src.type() == CV_32FC3 || src.type() == CV_32FC(4));
const int channels = src.channels();
cv::Mat dst(src.rows, src.cols, CV_8UC1); // 输出单通道索引
for (int r = 0; r < src.rows; r++) {
const float* src_ptr = src.ptr<float>(r);
uchar* dst_ptr = dst.ptr<uchar>(r);
for (int c = 0; c < src.cols; c++) {
float max_val = src_ptr[c * channels];
uchar max_idx = 0;
// 遍历每个通道,寻找最大值索引
for (int ch = 1; ch < channels; ch++) {
if (src_ptr[c * channels + ch] > max_val) {
max_val = src_ptr[c * channels + ch];
max_idx = ch;
}
}
dst_ptr[c] = max_idx; // 记录分类结果
}
}
return dst;
}
(3) 使用示例
// 模拟语义分割输出(3通道概率图)
cv::Mat prob_map(480, 640, CV_32FC3);
cv::randu(prob_map, 0.0, 1.0); // 随机生成概率
// 执行 argmax
cv::Mat class_idx = argmax(prob_map);
// 可视化分类结果(不同类别用不同颜色)
cv::Mat color_map;
cv::applyColorMap(class_idx * 80, color_map, cv::COLORMAP_JET); // 伪彩色映射
cv::imshow("Classification", color_map);
cv::waitKey(0);
3. 关键注意事项
-
数据类型匹配:
- 输入矩阵必须是
CV_32FC1
、CV_32FC3
等浮点类型。 - 输出分类索引矩阵根据类别数量选择类型(如类别数 < 256 用
CV_8UC1
,否则用CV_32SC1
)。
- 输入矩阵必须是
-
内存连续性:
- 使用
mat.isContinuous()
检查内存布局,必要时用mat.clone()
保证连续。
- 使用
-
性能优化:
- 多通道遍历时,优先使用指针操作而非
at<T>()
方法。 - 可并行化处理(如 OpenMP 加速)。
- 多通道遍历时,优先使用指针操作而非
-
分类结果后处理:
- 对分类结果进行形态学滤波(如
cv::medianBlur
)可消除噪声。 - 结合阈值过滤低置信度区域。
- 对分类结果进行形态学滤波(如
通过以上方法,可以实现高效的像素级分类,适用于语义分割、多类别目标检测等场景。
参考: