OpenCV4入门系列教程18:像素归一化

索引地址:系列教程索引地址

上一篇:OpenCV4入门系列教程17:像素逻辑操作

像素归一化

像素值归一化就是要把图片像素值数据经过某种算法限制在需要的一定范围内。归一化可以使没有可比性的数据变得具有可比性,同时保持相比较的数据之间的相对关系。OpenCV提供了四种图片像素归一化的方法:

  • L1归一化 : NORM_L1
  • L2归一化 : NORM_L2
  • INF归一化 : NORM_INF
  • MINMAX归一化 : NORM_MINMAX(最常用)

使用的函数是normalize(),函数说明为:

1
2
3
4
5
6
7
8
void normalize( InputArray src, //输入图片
InputOutputArray dst, //输出图片
double alpha = 1, //norm_type = NORM_MINMAX时下限
double beta = 0,//norm_type = NORM_MINMAX时上限,norm_type = NORM_INF时最大值
int norm_type = NORM_L2,
int dtype = -1, //输出type与输入type相同
InputArray mask = noArray()
);

下面以20,80,100三个像素值为例解释OpenCV中四种归一化的计算方法,由于归一化后为小数,因此将20,80,100转换成浮点型20.0,80.0,100.0以减小精度丢失。

L1归一化:NORM_L1

L1归一化依据所有像素值之和,用原始像素值除以所有像素值之和即为原始像素值归一化后的值。

例如对于20.0,80.0,100.0三个像素值,它们的和为20.0+80.0+100.0=200.0

20.0/200.0=0.120.0/200.0=0.1
80.0/200.0=0.480.0/200.0=0.4
100.0/200.0=0.5

所以20.0,80.0,100.0经过L1归一化后的值分别为0.1,0.4,0.5。

L2归一化:NORM_L2

L2归一化依据于原始像素值组成的单位向量,用原始像素值除以所有原始像素值平方值之和的平方根即为原始像素值归一化后的值。

例如对于20.0,80.0,100.0三个像素值,它们的平方和为:20.0∗20.0+80.0∗80.0+100.0∗100.0=16800.0,开平方得129.61。

20.0/129.61=0.15420.0/129.61=0.154
80.0/129.61=0.61780.0/129.61=0.617
100.0/129.61=0.772

所以20.0,80.0,100.0经过L2归一化后的值分别为0.154,0.617,0.772。

INF归一化:NORM_INF

INF归一化依据于最大值,用原始像素值除以所有原始像素值中的最大值即为原始像素值归一化后的值。

例如对于20.0,80.0,100.0三个像素值,它们的最大值为100.0。

20.0/100.0=0.2
80.0/100.0=0.880.0/100.0=0.8
100.0/100.0=1.0

所以20.0,80.0,100.0经过INF归一化后的值分别为0.2,0.8,1.0。

MINMAX归一化:NORM_MINMAX

INF归一化依据于最大值与最小值的差值(记为delta),用原始像素值除以所有原始像素值中的最大值即为原始像素值归一化后的值。

例如对于20.0,80.0,100.0三个像素值,它们的最小值min为20.0,最大值maxmax为100.0,则delta=max−min=100.0−20.0=80.0

所以

(20.0−min)/delta=(20.0−20.0)/80.0=0.0
(80.0−min)/delta=(80.0−20.0)/80.0=0.75(80.0−min)/delta=(80.0−20.0)/80.0=0.75
(100.0−min)/delta=(100.0−20.0)/80.0=1.0(100.0−min)/delta=(100.0−20.0)/80.0=1.0

所以20.0,80.0,100.0经过MINMAX归一化后的值分别为0.0,0.75,1.0。

测试代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<opencv2/opencv.hpp>
#include<iostream>
#include<Windows.h>

using namespace cv;
using namespace std;

int main(int argc, char**argv)
{
vector<double>a = {20,80,100};
vector<double>a1(3);
vector<double>a2(3);
vector<double>a3(3);
vector<double>a4(3);
cv::normalize(a, a1, 1, 0, NORM_L1);
cv::normalize(a, a2, 1, 0, NORM_L2);
cv::normalize(a, a3, 1, 0, NORM_INF);
cv::normalize(a, a4, 1, 0, NORM_MINMAX);

cout << "L1 normal:" << endl;
for (int i = 0; i < a.size(); i++){
cout << a1[i]<<" ";
}
cout << endl;

cout << "L2 normal:" << endl;
for (int i = 0; i < a.size(); i++){
cout << a2[i] << " ";
}
cout << endl;

cout << "INF normal:" << endl;
for (int i = 0; i < a.size(); i++){
cout << a3[i] << " ";
}
cout << endl;

cout << "MINMAX normal:" << endl;
for (int i = 0; i < a.size(); i++){
cout << a4[i] << " ";
}
cout << endl;

return 0;
}

输出结果为:

1
2
3
4
5
6
7
8
9
$ ./normalize
L1 normal:
0.1 0.4 0.5
L2 normal:
0.154303 0.617213 0.771517
INF normal:
0.2 0.8 1
MINMAX normal:
1.38778e-17 0.75 1

测试2

我们处理一下图片,可以看出归一化的对象要求每个像素只能有一个值,那么最好的就是灰度图或者黑白二值图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;
using namespace std;

int main(int argc, char**argv)
{
Mat gray_input_image = imread("1.jpg", 0);//读取图片并进行灰度处理
if (gray_input_image.empty()){//如果没有值就表示图片读取错误,退出
cout << "read input error!" << endl;
return -1;
}
imshow("gary_input", gray_input_image);//显示读取的灰度图片

//转为32位浮点型
gray_input_image.convertTo(gray_input_image, CV_32F);
//创建一个全是零的与原图像同大小的32位浮点型一通道图片
Mat result = Mat::zeros(gray_input_image.size(), CV_32FC1);
normalize(gray_input_image, result, 1.0, 0.0, NORM_MINMAX);//归一化
result = result * 255;//归一化的结果是小于1大于0的,将其像素值放大
result.convertTo(result, CV_8UC1);//转换为8位UC一通道
imshow("result", result);//显示处理结果

waitKey(0);

return 0;
}

处理结果:

normalize

说明

上面显示的就是我们使用OpenCV的正常流程,读取-处理-结果,如果需要调试,可以在中间加上显示来确保或者查看中间处理结果。

下一篇:OpenCV4入门系列教程测试2:像素插值实现缩放