Opencv -- 18图像像素类型转换与归一化

原文笔记参考(转载)链接:https://www.freesion.com/article/48551470037/.
什么是归一化 概念一:归一化是把需要处理的数据通过某种算法处理后限制在所需要的一定范围内。
概念二:归一化是指在处理数据的过程中,把数据范围相差较大的数据进行标准化处理,让所有的数据都处于同一个数量级中。
为什么要归一化 【Opencv -- 18图像像素类型转换与归一化】首先,归一化是 为了后面数据处理的方便,其次是保证程序运行时收敛加快。归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在某个区间上是统计的坐标分布。
归一化的目的,是使得没有可比性的数据变得具有可比性,同时又保持相比较的两个数据之间的相对关系,如大小关系;或是为了作图,原来很难在一张图上作出来,归一化后就可以很方便的给出图上的相对位置等。
归一化的方法 OpenCV中提供了四种归一化的方法。
Opencv -- 18图像像素类型转换与归一化
文章图片

最常用的就是 NORM_MINMAX 归一化方法。
解释下上面的归一化方法中第二种和第四种的计算过程。
NORM_L2

// NORM_L2 归一化,根据单位向量为1 ||positiveData|| = sqrt(2.0*2.0 + 8.0*8.0 + 10.0*10.0) = 12.96 2.00.15(2.0/12.96) 8.00.62(8.0/12.96) 10.0 0.77(10.0/12.96)

NORM_MINMAX
ORM_MINMAX 归一化,根据delta = max - min = 8.0 归一化到[alpha, beta],即[1.0, 0] 2.00.0((2.0 - 2.0)/8.0) 8.00.75 ((8.0 - 2.0)/8.0) 10.0 1.0((10.0 - 2.0)/8.0)

normalization 中的 norm 应该如何去理解呢?
总的来说,norm 的各种算法得出的最终产物都会是一个数,然后要归一的对象都来除以这个数,得到的最后结果才是归一之后的结果。也就是说 norm (范式)就是那个要被除以的数。
相关API函数
函数介绍: 1、归一化函数:会将变成输入图像的每个像素值变为 0—1 之间的浮点数void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray()); src-- 输入图像 dst-- 输出图像(与输入图像大小一致) alpha -- 归一化后的下界值(NORM_MINMAX时的低值) beta-- 极差归一化(NORM_MINMAX)时的上界值,不用于 NORM_L1、NORM_L2 和 NORM_INF。 norm_type-- 归一化类型,默认为 NORM_L2,有四种,NORM_L2、NORM_L1、NORM_INF、NORM_MINMAX dtype -- 当为负值时,输出数组的类型与src相同; 否则, 它具有与src相同数量的通道,并且depth =CV_MAT_DEPTH(dtype)。 默认值为 -1,即输出数组的类型与src相同; mask-- mask默认值为空,mask是一个二值图像,不为0的像素才参与计算,为0的像素则直接忽略。2、类型转化函数:功能:将数组转换为另一种可选缩放数据类型。void convertTo(OutputArray dst, int rtype, double alpha, double beta) constdst-- 目标图像 rtype -- 目标图像的类型,(目标矩阵的通道数与源矩阵一样) 如果rtype为负值,目标矩阵和源矩阵将使用同样的类型。 alpha -- 可选的比例因子,用于调整目标图像的像素值大小,默认值为1.0 beta-- 添加到缩放值的可选增量,默认值为0。 (alpha和beta可用于调整输出图像的亮度)

下面,我们来实验一下 convertTo 这个函数。
void QuickDemo::normalize_demo(Mat &image) { Mat dst; std::cout << image.type() << std::endl; //输出源图像的像素点类型 image.convertTo(dst, CV_32FC3); //将源图像的每个像素点类型转化为32位的浮点数类型 std::cout << dst.type() << std::endl; //输出目标图像的像素点类型 }

运行结果如下:
Opencv -- 18图像像素类型转换与归一化
文章图片

那为什么会是这两个数字呢?
首先,我们输入的图像像素类型是8位的无符号整数类型,在OpenCV中用宏 CV_8U 表示,因为是三通道的,所以输入的图像像素类型为
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)

Opencv -- 18图像像素类型转换与归一化
文章图片

通过上面的宏定义,来看下 CV_8UC3 的值是多少。
CV_8U 是 0,
CV_MAT_DEPTH(flags) 是 ((flags) & CV_MAT_DEPTH_MASK)
CV_MAT_DEPTH_MASK 是 (CV_DEPTH_MAX - 1) 即 5
CV_CN_SHIFT 是 3
CV_DEPTH_MAX 是 (1 << CV_CN_SHIFT) 即 6
CV_MAKETYPE(CV_8U,3) 即 CV_MAKETYPE(0,3)
CV_MAKETYPE(depth,cn) 是 (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
所以 CV_MAKETYPE(0,3) 是 CV_MAT_DEPTH(0) + 2 << 3
CV_MAT_DEPTH(0) 是 0
CV_MAKETYPE(0,3) = 16
CV_32FC3 是多少呢?
CV_MAKETYPE(CV_32F,3) 即 CV_MAKETYPE(5,3)即(CV_MAT_DEPTH(5) + (((3)-1) << 3))
即(5) & CV_MAT_DEPTH_MASK + 16
5&((1<<3)-1)+16 = 5&7 + 16 = 5+16 =21
(写的比较乱,但一步步代入推,能推出来)
下面,来将一个图像归一化。
#include "18_opencv_mat.h"void QuickDemo::normalize_demo(Mat& image) { Mat dst; std::cout << image.type() << std::endl; //输出源图像的像素点类型 image.convertTo(image, CV_32F, 1.0, 0); //将源图像的每个像素点类型转化为32位的浮点数类型,如果图像为3通道,则为CV_32FC3 std::cout << image.type() << std::endl; //输出转换后图像的像素点类型 normalize(image, dst, 1.0, 0, NORM_MINMAX); std::cout << dst.type() << std::endl; //输出归一化后图像的像素点类型 imshow("图像数据归一化",dst); }

输出结果如下:
Opencv -- 18图像像素类型转换与归一化
文章图片

如果只是将源图像的每个像素点类型转化为32位的浮点数类型,而不进行归一化,
#include "18_opencv_mat.h"void QuickDemo::normalize_demo(Mat& image) { Mat dst; std::cout << image.type() << std::endl; //输出源图像的像素点类型 image.convertTo(image, CV_32F, 1.0, 0); //将源图像的每个像素点类型转化为32位的浮点数类型,如果图像为3通道,则为CV_32FC3 std::cout << image.type() << std::endl; //输出转换后图像的像素点类型 imshow("图像数据归一化", image); }

输出结果如下:
Opencv -- 18图像像素类型转换与归一化
文章图片

这是因为浮点数的取值范围只能在0-1之间,才能被正常显示。
归一化的作用 在深度学习的训练模型中,对数据集(像素数据)进行预处理成浮点数的数据后,有时还会要求归一化到 0—1 之间。

    推荐阅读