在 OpenCV 图像处理的世界里,有几个函数进行一些基本数据变换:
-
cv::convertTo():类型转换与线性缩放; -
cv::normalize():归一化处理; -
cv::scaleAdd():加权叠加运算。 -
cv::addWeighted(): 与
scaleAdd相似,进行加权叠加运算;
一、cv::convertTo():线性变换 + 数据类型转换
cpp
void cv::Mat::convertTo(OutputArray dst, int rtype, double alpha = 1, double beta = 0) const;
其操作逻辑为:
cpp
dst(x, y) = saturate_cast<rtype>( src(x, y) * alpha + beta )
主要功能包括:
- 类型转换,如CV_8U->CV_32F
- 缩放变换,如alpha=1/255时实现归一化
- 图像增强,如
alpha > 1,beta ≠ 0调整对比度与亮度
二、cv::normalize():归一化到特定范围或分布
cpp
cv::normalize(InputArray src, OutputArray dst, double alpha = 1, double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());
src:输入数组(cv::Mat)
-
要被归一化的数据,可以是图像,也可以是向量或矩阵。
-
可以是任意深度(CV_8U、CV_32F、CV_64F等),任意通道数(灰度、彩色等)。
dst:输出数组(cv::Mat)
- 存储归一化后的结果,类型由
dtype决定(默认与src一致)。
alpha:归一化下限或范数值(具体含义取决于 norm_type)
-
如果
norm_type是NORM_MINMAX,alpha表示 归一化后的最小值。 -
如果是其他范数(如
NORM_L1,NORM_L2),alpha是规范化后的 目标范数。
beta:归一化上限(仅在 NORM_MINMAX 有意义)
-
只有当
norm_type = NORM_MINMAX时,beta表示 归一化后的最大值。 -
对于其他归一化类型,
beta会被忽略。
norm_type:归一化类型(cv::NormTypes 枚举)
cv::NORM_MINMAX,将数组线性映射到[alpha, beta]区间,常用于图像对比度增强。cv::NORM_L1,将向量归一化为 L1 范数(绝对值和)为alpha。cv::NORM_L2(默认),将向量归一化为 L2 范数(欧几里得范数)为alpha。cv::NORM_INF,将向量归一化为最大绝对值为alpha。
dtype:输出数据类型(默认 -1 表示与 src 一致)
-
可以指定输出图像的数据类型,如
CV_8U,CV_32F等。 -
如果
dtype = -1,表示dst类型与src保持一致。
mask:掩膜(可选)
-
用于选择对哪些像素进行归一化,非零位置参与归一化计算。
-
对于不需要掩膜的常见情况,使用默认值
cv::noArray()。
函数应用场景:
- 使用
NORM_MINMAX,将图像所有像素值映射到[0,1]区间 - 使用
NORM_L2,将图像看作向量,使得所有元素构成向量的模长为alpha
三、cv::scaleAdd():线性叠加的高效实现
cpp
void cv::scaleAdd(InputArray src1, double alpha, InputArray src2, OutputArray dst);
其操作逻辑为:
cpp
dst = src1 * alpha + src2;
用法场景:
-
梯度融合;
-
图像加权融合(曝光合成);
-
向量叠加(例如 PCA 主成分叠加)。
四、完整实战示例:图像增强 + 归一化 + 加权融合
我们将演示如下流程:
-
读取一张图像并转换为浮点格式;
-
使用
convertTo将其归一化到 [0,1]; -
使用
scaleAdd将原图与边缘图像加权融合,输出增强图像。
cpp
int main() {
cv::Mat img = cv::imread("lena.png", cv::IMREAD_GRAYSCALE);
if (img.empty()) {
std::cerr << "Cannot read image!" << std::endl;
return -1;
}
// 1. 使用 convertTo 将图像转换为 float 并归一化
cv::Mat img_f;
img.convertTo(img_f, CV_32F, 1.0 / 255.0);
// 2. 使用 Sobel 提取边缘
cv::Mat grad_x, grad_y, grad;
cv::Sobel(img_f, grad_x, CV_32F, 1, 0, 3);
cv::Sobel(img_f, grad_y, CV_32F, 0, 1, 3);
cv::magnitude(grad_x, grad_y, grad);
// 3. 对梯度图 normalize 到 [0,1]
cv::Mat grad_norm;
cv::normalize(grad, grad_norm, 0.0, 1.0, cv::NORM_MINMAX);
// 4. 使用 scaleAdd 融合边缘与原图
cv::Mat enhanced;
cv::scaleAdd(grad_norm, 0.5, img_f, enhanced); // enhanced = grad_norm * 0.5 + img_f
// 5. 将增强图转换回 uchar 显示
cv::Mat enhanced_8u;
enhanced.convertTo(enhanced_8u, CV_8U, 255.0);
// 显示结果
cv::imshow("Original", img);
cv::imshow("Enhanced", enhanced_8u);
cv::waitKey(0);
return 0;
}
