在上一篇关于傅里叶变换的博客中,知道了imshow的一个小trick:对normalize得到的0~1之间的浮点数构成的矩阵会进行放大255的操作,得到可视化的灰度图。即便是在python中也是如此操作的,只不过python中的函数封装得更加严密,我们看不到填充、归一化等操作,当把显示出的图像保存到本地时再打开查看,图像的像素依然在0~1之间,不利于我们之后对于频谱图的处理。所以这里就着重研究一下如何将imshow出的图像原样地保存下来。

链接1提到该函数分为范围归一化与数据值归一化。(Normalizes the norm or value range of an array.),由第三个参数alpha决定,1表示函数用来规范值,2表示规范范围,并且和第四个参数分别规定了范围的下限和上限。由此我们可以看到数值归一化和范围归一化的区别:

数值归一化是利用现有的数据通过某种占比方式重新计算每一个数值的占比作为新的值。

           

离线傅里叶计算如何使用python实现 python傅里叶级数可视化_浮点数

                                                                    

根据范数类型norm_type的不同,分母可以是数值之和,对应L1范数;可以是最大值,对应INF范数;可以是数据的平方之和再开根号,对应L2范数。所以数据值归一化也叫范数归一化。

范围的归一化也叫线性变换(线性拉伸或压缩),因为范围变换引入了范围的上限和下限,根据几何知识可以进行计算。

                                                                 

离线傅里叶计算如何使用python实现 python傅里叶级数可视化_归一化_02

特别地,当待归一化的数组最小值是0,且范围上下限分别是0、1时,范围归一化的结果和范数INF归一化的结果相同。可以按照如下代码验证:

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;

int main()
{
	vector<double>a = { 10, 11, 234, 45, 65, 456};
	cv::normalize(a, a, 2, 300, cv::NORM_MINMAX);
	for (int i = 0; i < a.size(); i++)
	{
		cout << a[i] << endl;
	}
	getchar();
	return 0;
}

为了保证频谱图像本身是可视化图像(灰度值分布在0~255),使用normalize函数:

normalize(mag, mag, 0, 255, CV_MINMAX);//范围归一化在0~255,但是显示出来的是全白图像。

于是决定手动对数组乘255.实现方式是用OpenCV的函数:

void multiply(InputArray src1, InputArraysrc2,OutputArray dst, doublescale=1, int dtype=-1);

//Mat gainMat(mag.rows, mag.cols, CV_32F, Scalar::all(255));
	//multiply(gainMat, mag, mag);//multiply要求两个矩阵类型一样

但结果是显示出的频谱图依然是全白图像。注意到浮点数才想起来imshow对浮点数还会进行一次255的乘法操作,所以关键其实在把数组由float类型转换为可视图像的uchar(8为无符号类型)。同样利用OpenCV函数:convertTo函数(src.convertTo(dst, type, scale, shift)

mag.convertTo(mag, CV_8U, 1);

其实convertTo函数也可以实现数据的缩放。如果我们用normalize函数范围变换到0~255,就不必经过数组乘法,在convertTo函数中选择数据格式为CV_8U,缩放尺度为1,就可以得到我们想要的频谱图像。

前文中涉及的multiply函数实现的实际上是矩阵的数乘,两个参与运算的矩阵行列一致,矩阵每个元素对应相乘,得到依然是同样大小的数组。这里顺便总结一下矩阵的其他乘法。

点乘(内积、数量积)

要求参与运算的两个矩阵行列一致,但会把矩阵扩展成一个行(列)向量,结果是对应元素的相乘,再相加求和,结果是一个标量(double类型)。

Mul

Mul函数和multiply的功能一样,但需要注意的是输出的Mat AB如果没有声明类型,默认是和A、B一致的,且若AB精度不够,可能产生溢出,溢出的值被置为当前精度下的最大值。如AB中第一个元素应该为60*60=360,但AB默认的类型为CV_8UC1,即最大值只能是255

矩阵乘法

OpenCV重载了运算符“*”,实现我们最熟知的矩阵乘法,结果的元素是行列对应元素相乘求和的结果。参与点乘的两个Mat矩阵的数据类型(type)只能是 CV_32F、 CV_64FC1、 CV_32FC2、 CV_64FC2 这4种类型中的一种。若选用其他类型,比如CV_8UC1,编译器会报错。

Reference:

1.      Normalize:
2.      
3.      convertTo :
4.      点乘、dot、mul: