一、知識點
1、OpenCV支持多種數據類型,每種類型都對應著不同的取值范圍。?
? (1)、CV_8U取值范圍[0, 255]。
? (2)、CV_16U取值范圍[0, 65535]。
? (3)、CV_32F取值范圍[0, 1]。
??
2、OpenCV提供convertTo()函數來轉換數據類型,提供normalize()函數來改變取值范圍。
3、void Mat::convertTo(OutputArray dst, int rtype, double alpha, double beta) const
? (1)、Mat成員函數,將Mat對象轉換到另一種數據類型。
? (2)、參數說明:
? ? ? dst: 輸出數組。
?? ? ?rtype: 輸出數組的期望類型,如CV_8UC1、CV_8UC3、CV_32FC3等。
?? ? ?alpha: 縮放因子,默認值為1,用于乘以輸入矩陣的每個元素。
?? ? ?beta: 偏移量,默認值為0,用于將縮放后的值加上偏移量。
4、void normalize( InputArray src,?
? ? ? ? ? ? ? ? ? InputOutputArray dst,?
?? ??? ??? ??? ? ?double alpha = 1,?
?? ??? ??? ??? ? ?double beta = 0,
? ? ? ? ? ? ? ? ? int norm_type = NORM_L2,?
?? ??? ??? ??? ? ?int dtype = -1,?
?? ??? ??? ??? ? ?InputArray mask = noArray());
? (1)、將數組歸一化到指定的取值范圍。
? (2)、參數說明:
? ? ? src: 輸入數組。
?? ? ?dst: 輸出數組,其類型和輸入數組相同。
?? ? ?alpha: 歸一化后的最小值或縮放系數,具體取決于norm_type。 若為NORM_MINMAX則表示歸一化后的最小值; 若為NORM_INF、NORM_L1、NORM_L2則表示縮放系數。
?? ? ?beta: 歸一化后的最大值,僅當norm_type為NORM_MINMAX時有效。
?? ? ?norm_type: 歸一化的方式,cv::NormTypes枚舉值,詳見5。
?? ? ?dtype: 為負數時,dst的類型和src相同; 否則,dst的通道數和src相同,但深度為dtype。
?? ? ?mask: 可選掩碼,若不為空,則在指定數組中(非零掩碼對應)歸一化; 若為空,則在整個數組歸一化。
? (3)、注意,norm_type為NORM_MINMAX時,雖然alpha表示最小值,beta表示最大值,但是兩值調換也不影響。
?? ? ?
5、enum NormTypes {
? ? NORM_INF = 1,
? ? NORM_L1 = 2,
? ? NORM_L2 = 4,
? ? NORM_L2SQR = 5,
? ? NORM_HAMMING = 6,
? ? NORM_HAMMING2 = 7,
? ? NORM_TYPE_MASK = 7,?
? ? NORM_RELATIVE = 8,?
? ? NORM_MINMAX = 32?
};
? (1)、常用NORM_INF、NORM_L1、NORM_L2、NORM_MINMAX這四種歸一化方式。
? (2)、NORM_L1,所有像素所有通道的值和為1。
? ? ? cv::normalize(src, dst, 1.0, 0.0, cv::NORM_L1);
? ? ? sum = 2 + 8 + 10 = 20
? ? ? 2 ?---> ?0.1 ? (2.0 / 20.0)
?? ? ?8 ?---> ?0.4 ? (8.0 / 20.0)
?? ? ?10 ---> ?0.5 ? (10.0 / 20.0)
? (3)、NORM_L2,所有像素所有通道的值求單位向量。 (默認)
? ? ? cv::normalize(src, dst, 1.0, 0.0, cv::NORM_L2);
? ? ? |v| = 開根號(2 * 2 + 8 * 8 + 10 * 10) = 開根號(168) = 12.96
?? ? ?2 ?---> ?0.15 ? (2.0 / 12.96)
?? ? ?8 ?---> ?0.62 ? (8.0 / 12.96)
?? ? ?10 ---> ?0.77 ? (10.0 / 12.96)
? (4)、NORM_INF,每個值除以所有像素所有通道的最大值。
? ? ? cv::normalize(src, dst, 1.0, 0.0, cv::NORM_INF);
?? ? ?Max = 10
?? ? ?2 ?---> ?0.2 ? (2.0 / 10.0)
?? ? ?8 ?---> ?0.8 ? (8.0 / 10.0)
?? ? ?10 ---> ?1.0 ? (10.0 / 10.0)
? (5)、NORM_MINMAX (常用)
? ? ? cv::normalize(src, dst, 0.0, 1.0, cv::NORM_MINMAX);
?? ? ?alpha = 0.0, beta = 1.0, 歸一化范圍[0.0, 1.0],簡記為[a, b]
?? ? ?找到樣本數據最小值Min = 2, 最大值Max = 10
?? ? ?計算系數k = (b - a) / (Max - Min) = 1.0 / 8.0 = 0.125
?? ? ?歸一化值 = a + k * (當前值 - Min)
?? ? ?2 ?---> ?0.0 ? (0.0 + 0.125 * (2 - 2))
?? ? ?8 ?---> ?0.75 ?(0.0 + 0.125 * (8 - 2))
?? ? ?10 ---> ?1.0 ? (0.0 + 0.125 * (10 - 2))
?? ? ?
6、注意:
? (1)、將CV_8UC3轉換為CV_32FC3,8U取值范圍[0, 255],32F取值范圍[0.0, 1.0],如果不歸一化,imshow會顯示幾乎全白圖像。
? (2)、將CV_8UC3圖像不轉換類型,直接歸一化,imshow會顯示全黑圖片。
??
??
??
二、示例代碼
#include <iostream>
#include <opencv2/opencv.hpp>int main()
{//自定義src1,CV_8UC3類型的src1轉為CV_32FC3類型的dst1,類型值改變但是元素值不變cv::Mat src1 = cv::Mat::zeros(3, 3, CV_8UC3);src1 = cv::Scalar(45, 60, 80);std::cout << "src1 type = " << src1.type() << std::endl << src1 << std::endl;cv::Mat dst1;src1.convertTo(dst1, CV_32FC3, 1.0, 0.0);std::cout << "dst1 type = " << dst1.type() << std::endl << dst1 << std::endl;//NORM_L1, 所有像素所有通道的值求和為1cv::Mat dst2;cv::normalize(dst1, dst2, 1.0, 0.0, cv::NORM_L1);std::cout << "dst2 type = " << dst2.type() << std::endl << dst2 << std::endl;//NORM_L2, 所有像素所有通道的值求單位向量cv::Mat dst3;cv::normalize(dst1, dst3, 1.0, 0.0, cv::NORM_L2);std::cout << "dst3 type = " << dst3.type() << std::endl << dst3 << std::endl;//NORM_INF, 每個值除以所有像素所有通道的最大值cv::Mat dst4;cv::normalize(dst1, dst4, 1.0, 0.0, cv::NORM_INF);std::cout << "dst4 type = " << dst4.type() << std::endl << dst4 << std::endl;//NORM_MINMAXcv::Mat dst5;cv::normalize(dst1, dst5, 0.0, 1.0, cv::NORM_MINMAX);std::cout << "dst5 type = " << dst5.type() << std::endl << dst5 << std::endl;//src2是讀取的圖像cv::Mat src2 = cv::imread("../images/6.png");if (src2.empty()){std::cout << "load src2 image error..." << std::endl;return -1;}cv::imshow("原始圖像", src2);//CV_8UC3轉為32FC3,值沒有變化,但是顯示會非常白的圖片src2.convertTo(src2, CV_32FC3);cv::imshow("類型轉換", src2);//NORM_L1,結果值太小,圖像顯示黑色cv::Mat dst6;cv::normalize(src2, dst6, 1.0, 0.0, cv::NORM_L1);cv::imshow("NORM_L1", dst6);//NORM_L2,結果值太小,放大100倍,圖像能顯示出來cv::Mat dst7;cv::normalize(src2, dst7, 100.0, 0.0, cv::NORM_L2);cv::imshow("NORM_L2", dst7);//NORM_INFcv::Mat dst8;cv::normalize(src2, dst8, 1.0, 0.0, cv::NORM_INF);cv::imshow("NORM_INF", dst8);//NORM_MINMAXcv::Mat dst9;cv::normalize(src2, dst9, 1.0, 0.0, cv::NORM_MINMAX);cv::imshow("NORM_MINMAX", dst9);//src3和src2讀取一樣的原始圖片, 但如果不轉換類型,直接歸一化,會顯示全黑的圖片cv::Mat src3 = cv::imread("../images/6.png");if (src3.empty()){std::cout << "load src3 image error..." << std::endl;return -1;}cv::Mat dst10;cv::normalize(src3, dst10, 1.0, 0.0, cv::NORM_MINMAX);cv::imshow("直接歸一化", dst10);cv::waitKey(0);return 0;
}
? 輸出結果:
src1 type = 16
[ 45, 60, 80, 45, 60, 80, 45, 60, 80;45, 60, 80, 45, 60, 80, 45, 60, 80;45, 60, 80, 45, 60, 80, 45, 60, 80]
dst1 type = 21
[45, 60, 80, 45, 60, 80, 45, 60, 80;45, 60, 80, 45, 60, 80, 45, 60, 80;45, 60, 80, 45, 60, 80, 45, 60, 80]
dst2 type = 21
[0.027027028, 0.036036037, 0.048048049, 0.027027028, 0.036036037, 0.048048049, 0.027027028, 0.036036037, 0.048048049;0.027027028, 0.036036037, 0.048048049, 0.027027028, 0.036036037, 0.048048049, 0.027027028, 0.036036037, 0.048048049;0.027027028, 0.036036037, 0.048048049, 0.027027028, 0.036036037, 0.048048049, 0.027027028, 0.036036037, 0.048048049]
dst3 type = 21
[0.13678823, 0.1823843, 0.24317907, 0.13678823, 0.1823843, 0.24317907, 0.13678823, 0.1823843, 0.24317907;0.13678823, 0.1823843, 0.24317907, 0.13678823, 0.1823843, 0.24317907, 0.13678823, 0.1823843, 0.24317907;0.13678823, 0.1823843, 0.24317907, 0.13678823, 0.1823843, 0.24317907, 0.13678823, 0.1823843, 0.24317907]
dst4 type = 21
[0.5625, 0.75, 1, 0.5625, 0.75, 1, 0.5625, 0.75, 1;0.5625, 0.75, 1, 0.5625, 0.75, 1, 0.5625, 0.75, 1;0.5625, 0.75, 1, 0.5625, 0.75, 1, 0.5625, 0.75, 1]
dst5 type = 21
[2.4214387e-08, 0.42857146, 1, 2.4214387e-08, 0.42857146, 1, 2.4214387e-08, 0.42857146, 1;2.4214387e-08, 0.42857146, 1, 2.4214387e-08, 0.42857146, 1, 2.4214387e-08, 0.42857146, 1;2.4214387e-08, 0.42857146, 1, 2.4214387e-08, 0.42857146, 1, 2.4214387e-08, 0.42857146, 1]