OpenCV10-图像直方图:直方图绘制、直方图归一化、直方图比较、直方图均衡化、直方图规定化、直方图反射投影
OpenCV10-图像直方图:直方图绘制、直方图归一化、直方图比较、直方图均衡化、直方图规定化、直方图反射投影
- 1.直方图的绘制
- 2.直方图归一化
- 3.直方图比较
- 4.直方图均衡化
- 5.直方图规定化(直方图匹配)
- 6.直方图反向投影
1.直方图的绘制
图像直方图就是统计图像中每个灰度值的个数,之后将灰度值作为横轴,以灰度值个数或者灰度值所占比率作为纵轴的统计图。通过直方图,可以看出图像中哪些灰度值数目较多,哪些较少,可以通过一定的方法将灰度值较为集中的区域映射到较为稀疏的区域,从而使图像在像素灰度值上的分布更加符合期望状态。在通常情况下,像素灰度值代表亮暗程度,因此通过直方图,可以分析图像亮暗对比度,并调整图像的亮暗程度。
在OpenCV中,只提供了图像直方图的统计函数calcHist(),该函数能够统计出图像中每个灰度值的个数,但是,对于直方图的绘制,需要自行进行:
void calcHist(const Mat* images, // 待统计直方图的图像数组,数据类型只能是CV_8U、CV_16U、CV_32F,不同图像的通道数可以不同int nimages, // 输入图像数量const int* channels, // 需要统计的通道索引数组,第一个图像的通道索引号从0到images[0].channels()-1,第二个图像的通道索引从image[0].channels()到image[0].channels()+image[1].channels()-1,以此类推InputArray mask, // 可选的操作掩码,如果是空矩阵表示图像中所有位置的像素都计入直方图中OutputArray hist, // 输出的统计直方图结果,时dims维度的数组int dims, // 需要计算直方图的维度,必须是整数,不能大于CV_MAX_DIMSconst int* histSize, // 存放每个维度直方图的数组的尺寸const float** ranges, // 每个图相同道中灰度值的取值范围bool uniform = true, // 直方图是否均匀的标志bool accumulate = false // 是否累积统计直方图的标志,如果累积,那么在统计新图像时,之前的统计结果不会被清除,该参数主要用于统计多个图像整体的直方图。
);
由于该函数具有较多参数,并且每个参数都较为复杂,因此在使用该函数时只统计单通道图像的灰度值分布,对于多个通道图像,可以将图像每个通道分离后再进行统计。
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat img = imread("apple.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);//设置提取直方图的相关变量Mat hist; //用于存放直方图计算结果const int channels[1] = { 0 }; //通道索引float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges }; //像素灰度值范围const int bins[1] = { 256 }; //直方图的维度,其实就是像素灰度值的最大值calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges); //计算图像直方图cout << "hist.channels(): " << hist.channels() << endl; // 1double maxval = 0.0;minMaxLoc(hist, 0, &maxval);cout << "hist maxval: " << maxval << endl; // 最多的一个灰度值数量有3896//准备绘制直方图int hist_w = 512; // 256个灰度值,每个灰度值画成矩形,矩形宽度为2int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width * (i - 1), hist_h - 1),// cvRound四舍五入取整,因为灰度值数量太多,所以除以了15Point(width * i - 1, hist_h - cvRound(hist.at<float>(i - 1) / 15)),Scalar(255, 255, 255), -1);}namedWindow("histImage", WINDOW_AUTOSIZE);imshow("histImage", histImage);imshow("gray", gray);int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}
由于图像中灰度值像素数目较多,因此将每个灰度值数目缩小为原来的 1 15 \frac{1}{15} 151 后再进行绘制。
2.直方图归一化
前面完成了对一幅图像像素灰度值的统计,并成功绘制了图像的直方图。直方图可以用来表示图像的明亮程度,从理论上讲,通过对同一个图像缩放后得到的两幅尺寸不一样的图像将具有大致相似的直方图分布特性,因此用灰度值的数目作为统计结果具有一定的局限性。
OpenCV提供了normalize函数实现多种形式的归一化功能:
void normalize(InputArray src, InputOutputArray dst, double alpha = 1, // 在范围归一化的情况下,归一化到下限边界的标准值double beta = 0, // 在范围归一化时的上限范围,他不用于标准规范化 int norm_type = NORM_L2, // 归一化过程中数据范数种类标志,常用的可选参数如下所示int dtype = -1, // 输出数据类型,默认即可 InputArray mask = noArray() // 掩码矩阵
);
/*
该函数通过alpha设置将数据缩放到最大范围,通过norm_type参数选择计算范数的种类,之后将输入矩阵中的每个数据分别除以求取得范数数值,最后得到缩放的结果。norm_type:计算不同的范数,最后的结果也不相同。
NORM_INF: x/max
NORM_L1: x/sum(ary)
NORM_L2: x/sqrt( sum(pow(i,2)) )
NORM_MINMAX: (x-min)/(max-min)
*/
下面通过不同方式归一化数组 [2.0, 8.0, 10.0]
,并且分别用灰度值所占比例除以数据最大值的方式对图像进行归一化操作。为了更加直观展示直方图归一化后的结果,将每个灰度值所占比例放大了30倍,并将直方图的图像高度作为1进行绘制:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);//system("color F0"); //更改输出界面颜色vector<double> positiveData = { 2.0, 8.0, 10.0 };vector<double> normalized_L1, normalized_L2, normalized_Inf, normalized_L2SQR;//测试不同归一化方法normalize(positiveData, normalized_L1, 1.0, 0.0, NORM_L1); //绝对值求和归一化cout << "normalized_L1=[" << normalized_L1[0] << ", "<< normalized_L1[1] << ", " << normalized_L1[2] << "]" << endl;normalize(positiveData, normalized_L2, 1.0, 0.0, NORM_L2); //模长归一化cout << "normalized_L2=[" << normalized_L2[0] << ", "<< normalized_L2[1] << ", " << normalized_L2[2] << "]" << endl;normalize(positiveData, normalized_Inf, 1.0, 0.0, NORM_INF); //最大值归一化cout << "normalized_Inf=[" << normalized_Inf[0] << ", "<< normalized_Inf[1] << ", " << normalized_Inf[2] << "]" << endl;normalize(positiveData, normalized_L2SQR, 1.0, 0.0, NORM_MINMAX); //偏移归一化cout << "normalized_MINMAX=[" << normalized_L2SQR[0] << ", "<< normalized_L2SQR[1] << ", " << normalized_L2SQR[2] << "]" << endl;//将图像直方图归一化Mat img = imread("apple.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, hist;cvtColor(img, gray, COLOR_BGR2GRAY);const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage_L1 = Mat::zeros(hist_h, hist_w, CV_8UC3);Mat histImage_Inf = Mat::zeros(hist_h, hist_w, CV_8UC3);Mat hist_L1, hist_Inf;normalize(hist, hist_L1, 1, 0, NORM_L1, -1, Mat());for (int i = 1; i <= hist_L1.rows; i++){rectangle(histImage_L1, Point(width * (i - 1), hist_h - 1),Point(width * i - 1, hist_h - cvRound(30 * hist_h * hist_L1.at<float>(i - 1)) - 1),Scalar(255, 255, 255), -1);}normalize(hist, hist_Inf, 1, 0, NORM_INF, -1, Mat());for (int i = 1; i <= hist_Inf.rows; i++){rectangle(histImage_Inf, Point(width * (i - 1), hist_h - 1),Point(width * i - 1, hist_h - cvRound(hist_h * hist_Inf.at<float>(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow("histImage_L1", histImage_L1);imshow("histImage_Inf", histImage_Inf);int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}
normalized_L1=[0.1, 0.4, 0.5]
normalized_L2=[0.154303, 0.617213, 0.771517]
normalized_Inf=[0.2, 0.8, 1]
normalized_MINMAX=[0, 0.75, 1]
结果显示,无论是否进行归一化,或者采用那种归一化方法,直方图的分布特性都不会改变。
3.直方图比较
通过两幅图像的直方图特性比较两幅图像的相似程度。从一定程度上来讲,虽然两幅图像的直方图分布相似不代表两幅图像相似,但是两幅图像相似则两幅图像的直方图分布一定相似。例如,在通过差值对图像进行缩放后,虽然图形的直方图不会与之前完全一致,但是两者之间一定具有很高的相似性,因而可以通过比较两幅图像的直方图分布相似性对图像进行初步的筛选与识别。
OpenCV中提供了compareHist函数用于比较两个图像直方图相似性:
double compareHist(InputArray H1, InputArray H2, int method
);// method
enum HistCompMethods {HISTCMP_CORREL = 0, // 相关法HISTCMP_CHISQR = 1, // 卡方法HISTCMP_INTERSECT = 2, // 直方图相交法HISTCMP_BHATTACHARYYA = 3, // 巴氏距离法HISTCMP_HELLINGER = HISTCMP_BHATTACHARYYA, // 3HISTCMP_CHISQR_ALT = 4, // 替代卡方法HISTCMP_KL_DIV = 5 // 相对熵法
};
示例程序:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;void drawHist(Mat& hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width * (i - 1), hist_h - 1),Point(width * i - 1, hist_h - cvRound(hist_h * hist.at<float>(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat img = imread("apple.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, hist, gray2, hist2, gray3, hist3;cvtColor(img, gray, COLOR_BGR2GRAY);resize(gray, gray2, Size(), 0.5, 0.5);gray3 = imread("lena.png", IMREAD_GRAYSCALE);const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };// gray:苹果 gray2:苹果缩小 gray3:lenacalcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);calcHist(&gray2, 1, channels, Mat(), hist2, 1, bins, ranges);calcHist(&gray3, 1, channels, Mat(), hist3, 1, bins, ranges);drawHist(hist, NORM_INF, "hist");drawHist(hist2, NORM_INF, "hist2");drawHist(hist3, NORM_INF, "hist3");//原图直方图与原图直方图的相关系数double hist_hist = compareHist(hist, hist, HISTCMP_CORREL);cout << "apple_apple=" << hist_hist << endl;//原图直方图与缩小原图直方图的相关系数double hist_hist2 = compareHist(hist, hist2, HISTCMP_CORREL);cout << "apple_apple256=" << hist_hist2 << endl;//两张不同图像直方图相关系数double hist_hist3 = compareHist(hist, hist3, HISTCMP_CORREL);cout << "apple_lena=" << hist_hist3 << endl;/*apple_apple=1apple_apple256=0.998067apple_lena=0.285314*/int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}
4.直方图均衡化
如果一个图像的直方图都集中在一个区域那么整体图像的对比度比较小,不便于图像中纹理识别。如果通过映射关系,将图像中灰度值的范围扩大,增加原来两个灰度值之间的差值,就可以提高图像的对比度,进而将图像中的纹理凸显出来,这个过程称为图像直方图均衡化。
OpenCV中提供了equalizeHist函数用于将图像的直方图均衡化:
void equalizeHist(InputArray src, // CV_8UC1OutputArray dst
);
该函数只能对单通道的灰度图进行直方图均衡化。
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;void drawHist(Mat& hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width * (i - 1), hist_h - 1),Point(width * i - 1, hist_h - cvRound(hist_h * hist.at<float>(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat img = imread("gearwheel.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, hist, hist2;cvtColor(img, gray, COLOR_BGR2GRAY);Mat equalImg;equalizeHist(gray, equalImg); //将图像直方图均衡化const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);calcHist(&equalImg, 1, channels, Mat(), hist2, 1, bins, ranges);drawHist(hist, NORM_INF, "hist");drawHist(hist2, NORM_INF, "hist2");imshow("原图", gray);imshow("均衡化后的图像", equalImg);int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}
5.直方图规定化(直方图匹配)
直方图均衡化函数可以自动地改变图像直方图的分布形式,这种方式极大地简化了直方图均衡化过程中需要的操作步骤,但是该函数不能指定均衡化后的直方图分布形式。在某些特定条件下,需要将直方图映射成指定的分布形式,这种将直方图映射成指定分布形式的算法成为直方图规定化或直方图匹配。
直方图规定化与直方图均衡化相似,都是对图像的直方图分布形式进行改变,只是直方图均衡化后的图像是均匀分布的,而直方图规定化后的直方图可以任意指定,即在执行直方图规定化操作时,首先要知道变换后的灰度直方图分布形式,进而确定变换函数,直方图规定化有目的地增强某个灰度区间。
不同图像间像素数目不同,为了使两个图像直方图能够匹配,需要使用概率形式表示每个灰度值在图像像素中所占的比例。在理想状态下,经过图像直方图匹配操作后,图像直方图分布形式应于目标分布一致,因此两者间的累积概率(小于等于某一灰度值的像素数目占所有像素的比例)分布也一致。
用 V s V_s Vs 表示原图像直方图的各个灰度级的累积概率,用 V z V_z Vz 表示匹配后直方图的各个灰度级累积概率,由原图像中灰度值 n n n 映射成 r r r 的条件:
n , r = a r g min n , r ∣ V s ( n ) − V z ( n ) ∣ n,r = arg \min_{n,r} |V_s(n) - V_z(n)| n,r=argn,rmin∣Vs(n)−Vz(n)∣
下面说明直方图匹配过程:
运算 | 步骤和结果 | |||||||
---|---|---|---|---|---|---|---|---|
原图像灰度级 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
原直方图概率 | 0.19 | 0.24 | 0.2 | 0.17 | 0.09 | 0.05 | 0.03 | 0.02 |
原直方图累积概率 | 0.19 | 0.43 | 0.63 | 0.8 | 0.89 | 0.94 | 0.97 | 0.99 |
目标直方图概率 | 0 | 0 | 0 | 0.16 | 0.19 | 0.29 | 0.2 | 0.16 |
目标直方图累积概率 | 0 | 0 | 0 | 0.16 | 0.35 | 0.64 | 0.84 | 1 |
匹配的灰度值 | 3 | 4 | 5 | 6 | 6 | 7 | 7 | 7 |
映射关系 | 0->3 | 1->4 | 2->5 | 3->6 | 4->6 | 5->7 | 6->7 | 7->7 |
1.计算原直方图概率
2.计算原直方图累积概率
3.计算目标直方图概率
4.计算目标直方图累积概率
5.确定映射:
原直方图灰度值0,累积概率 V s V_s Vs 为0.19,目标直方图累积概率 V z V_z Vz 可以为0.16,0.35…,但 V s V_s Vs 为0.19(灰度级为0)距离 V z V_z Vz 为0.16(灰度级为3)最小,因此有映射关系:0->3
这个寻找灰度值匹配的过程是直方图规定化的关键,在代码实现中可以通过构建元直方图累积概率与目标直方图累积概率之间的差值表,寻找原直方图中灰度值 n n n 的累积概率与目标直方图中所有灰度值累积概率差值的最小值,这个最小值对应的灰度值 r r r 就是 n n n 匹配后的灰度值。
在OpenCV中并没有提供直方图匹配的函数,需要自己根据算法实现图像直方图匹配。下面代码给出了实现直方图匹配的实例,该程序中待匹配的原图是一个整体偏暗的图像,目标直方图分配形式来自于一幅较为明亮的图像,经过图像直方图匹配操作之后,提高了图像的整体亮度,图像直方图的分布更加均匀:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;void drawHist(Mat& hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width * (i - 1), hist_h - 1),Point(width * i - 1, hist_h - cvRound(hist_h * hist.at<float>(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat img1 = imread("histMatch.png");Mat img2 = imread("equalLena.png");if (img1.empty() || img2.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat hist1, hist2;//计算两张图像直方图const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&img1, 1, channels, Mat(), hist1, 1, bins, ranges);calcHist(&img2, 1, channels, Mat(), hist2, 1, bins, ranges);//归一化两张图像的直方图drawHist(hist1, NORM_L1, "hist1");drawHist(hist2, NORM_L1, "hist2");//计算两张图像直方图的累积概率float hist1_cdf[256] = { hist1.at<float>(0) };float hist2_cdf[256] = { hist2.at<float>(0) };for (int i = 1; i < 256; i++){hist1_cdf[i] = hist1_cdf[i - 1] + hist1.at<float>(i);hist2_cdf[i] = hist2_cdf[i - 1] + hist2.at<float>(i);}//构建累积概率误差矩阵float diff_cdf[256][256];for (int i = 0; i < 256; i++){for (int j = 0; j < 256; j++){diff_cdf[i][j] = fabs(hist1_cdf[i] - hist2_cdf[j]);}}//生成LUT映射表Mat lut(1, 256, CV_8U);for (int i = 0; i < 256; i++){// 查找源灰度级为i的映射灰度// 和i的累积概率差值最小的规定化灰度float min = diff_cdf[i][0];int index = 0;//寻找累积概率误差矩阵中每一行中的最小值for (int j = 1; j < 256; j++){if (min > diff_cdf[i][j]){min = diff_cdf[i][j];index = j;}}lut.at<uchar>(i) = (uchar)index;}Mat result, hist3;LUT(img1, lut, result);imshow("待匹配图像", img1);imshow("匹配的模板图像", img2);imshow("直方图匹配结果", result);calcHist(&result, 1, channels, Mat(), hist3, 1, bins, ranges);drawHist(hist3, NORM_L1, "hist3"); //绘制匹配后的图像直方图int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}
6.直方图反向投影
如果一幅图像的某个区域中显示的是一种结构纹理或者一个独特的形状,那么这个区域的直方图就可以看作是这个结构或者形状的概率函数,在图像中寻找这种概率分布就是在图像中寻找该结构纹理或者独特形状。
反向投影(back projection)就是记录给定图像中的像素点如何适应直方图模型像素分布方式的一种方法。反射投影就是首先计算某一特征的直方图模型,然后使用该模型去寻找是否存在该特征。
void calcBackProject(const Mat* images, int nimages,const int* channels, // 前三个参数同calcHistInputArray hist, // 模版图像直方图OutputArray backProject, // 输出结果const float** ranges,// 同calcHistdouble scale = 1, // 输出反向投影矩阵的比例因子bool uniform = true // 同calcHist
);
/*
该函数用于在输入图像中寻找与指定图像最匹配的点或区域,即对图像直方图反向投影,输入参数和calcHist相似。
关注输入图像和模版图像的直方图,返回一幅图像。
*/
下面代码中,首先加载待反向投影图像和模版图像,模版图像从待反向投影图像中截取。之后将两幅图像由RGB颜色空间转成HSV空间,统计H-S通道的直方图,将直方图归一化后绘制H-S通道的二维直方图。最后将待反向投影图像和模版图像输入calcBackProject函数,得到对图像直方图反向投影结果。
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;void drawHist(Mat& hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width * (i - 1), hist_h - 1),Point(width * i - 1, hist_h - cvRound(hist_h * hist.at<float>(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat img = imread("apple.jpg");Mat sub_img = imread("sub_apple.jpg");Mat img_HSV, sub_HSV, hist, hist2;if (img.empty() || sub_img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("img", img);imshow("sub_img", sub_img);//转成HSV空间,提取S、V两个通道cvtColor(img, img_HSV, COLOR_BGR2HSV);cvtColor(sub_img, sub_HSV, COLOR_BGR2HSV);int h_bins = 32; int s_bins = 32;int histSize[] = { h_bins, s_bins };//H通道值的范围由0到179float h_ranges[] = { 0, 180 };//S通道值的范围由0到255float s_ranges[] = { 0, 256 };const float* ranges[] = { h_ranges, s_ranges }; //每个通道的范围int channels[] = { 0, 1 }; //统计的通道索引//绘制H-S二维直方图calcHist(&sub_HSV, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);drawHist(hist, NORM_INF, "hist"); //直方图归一化并绘制直方图Mat backproj;calcBackProject(&img_HSV, 1, channels, hist, backproj, ranges, 1.0); //直方图反向投影imshow("反向投影后结果", backproj);cout << "backproj.size(): " << backproj.size() << endl;cv::Mat imageWithMask(img.size(), img.type());img.copyTo(imageWithMask, backproj);imshow("使用反向投影掩码后结果", imageWithMask);int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}
反向投影图像给出了输入图像中每个像素与目标模型的相似程度。具体来说,反向投影图像的每个像素值表示该像素在目标模型中的可能性或置信度。像素值越高,表示该像素更可能属于目标模型。上面backproj图形是0-1二值图,直接显示看到的将是黑色,当做掩膜可以看到效果。
相关文章:

OpenCV10-图像直方图:直方图绘制、直方图归一化、直方图比较、直方图均衡化、直方图规定化、直方图反射投影
OpenCV10-图像直方图:直方图绘制、直方图归一化、直方图比较、直方图均衡化、直方图规定化、直方图反射投影 1.直方图的绘制2.直方图归一化3.直方图比较4.直方图均衡化5.直方图规定化(直方图匹配)6.直方图反向投影 1.直方图的绘制 图像直方图…...

线性回归模型进行特征重要性分析
目的 线性回归是很常用的模型;在局部可解释性上也经常用到。 数据归一化 归一化通常是为了确保不同特征之间的数值范围差异不会对线性模型的训练产生过大的影响。在某些情况下,特征归一化可以提高模型的性能,但并不是所有情况下都需要进行归一…...

hadoop -hive 安装
1.下载hive http://archive.apache.org/dist/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz2.解压/usr/app 目录 tar -zxvf apache-hive-3.1.3-bin.tar.gz -C /usr/app3.设置软连接 ln -s /usr/app/apache-hive-3.1.3-bin /usr/app/hive4.修改/usr/app/hive/conf/hive-env.…...

小迈物联网网关对接串口服务器[Modbus RTU]
很多工控现场,方案中会使用串口服务器采集Modbus RTU的设备,这种情况下一般会在PC机上装上串口服务器厂家的软件来进行数据采集。如果现场不需要PC机,而是通过网关将数据传输到软件平台,如何实现呢? 本文简要介绍小迈网…...

Java版本+企业电子招投标系统源代码+支持二开+招投标系统+中小型企业采购供应商招投标平台
功能模块: 待办消息,招标公告,中标公告,信息发布 描述: 全过程数字化采购管理,打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力,为外部供…...

Vue3中reactive, onMounted, ref,toRaw,conmpted 使用方法
import { reactive, onMounted, ref,toRaw,conmpted } from vue; vue3中 reactive ,ref , toRaw,watch,conmpted 用法 toRaw 返回原响应式对象 用法: const rowList toRaw(row) reactive:ref: ref和reactive都是V…...

有哪些免费的PPT模板网站,推荐这6个PPT模板免费下载网站!
混迹职场的打工人,或是还在校园的学生党,在日常的工作汇报或课程作业中,必然少不了PPT的影子,而每当提到做PPT,许多人首先会想到:有哪些免费的PPT模板下载网站? 本着辛苦自己,造福所…...

剧院建筑三维可视化综合管控平台提高安全管理效率
随着数字孪生技术的高速发展,智慧楼宇也被提上日程,以往楼宇管理存在着设备故障排查困难、能源浪费与管理不足、安全性和风险高等问题,而智慧楼宇数字孪生可视化中控平台,打造智慧楼宇管理一张图,实现了智慧建筑和楼宇…...

“过度炒作”的大模型巨亏,Copilot每月收10刀,倒赔20刀
大模型无论是训练还是使用,都比较“烧钱”,只是其背后的成本究竟高到何处?已经推出大模型商用产品的公司到底有没有赚到钱?事实上,即使微软、亚马逊、Adobe 这些大厂,距离盈利之路还有很远!同时…...

顺序表经典的OJ题
题目一 移除元素: 题目要求: 给你一个数组 nums 和一个值 val。你需要 原地 除所有数值等于 val 的素,并返回移除后数组的新长度.不要使用额外的数组空间。你必须仅使用 0(1) 额外空间并 原地 修改输入数组元素的顺序可以改变。你不需要考虑数…...

video_topic
使用qt5,ffmpeg6.0,opencv,os2来实现。qt并非必要,只是用惯了。 步骤是: 1.读取rtsp码流,转换成mat图像 2.发送ros::mat图像 项目结构如下: videoplayer.h #ifndef VIDEOPLAYER_H #define VIDEOPLAYER_H#include …...

uniapp获取公钥、MD5,‘keytool‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
获取MD5、SHA1、SHA256指纹信息 通过命令的形式获取 winr调出黑窗口cd到证书所在目录输入keytool -list -v -keystore test.keystore,其中 test.keystore为你的证书名称加文件后缀按照提示输入你的证书密码,就可以查看证书的信息 通过uniapp云端查看(证书是在DClou…...

Jetson Orin NX 开发指南(5): 安装 OpenCV 4.6.0 并配置 CUDA 以支持 GPU 加速
一、前言 Jetson 系列的开发板 CPU 性能不是很强,往往需要采用 GPU 加速的方式处理图像数据,因此本文主要介绍如何安装带有 GPU 加速的 OpenCV,其中 GPU 加速通过 CUDA 来实现。 参考博客 Ubuntu 20.04 配置 VINS-Fusion-gpu OpenCV 4.6.…...

Spring Security 6.x 系列【67】认证篇之安装 ApacheDS
有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.1.0 本系列Spring Security 版本 6.1.0 源码地址:https://gitee.com/pearl-organization/study-spring-security-demo 文章目录 1. 概述2. 安装3. 连接工具1. 概述 官方文档 在前两篇文档中,我们简单了…...

理解线程池源码 【C++】面试高频考点
理解线程池 C 文章目录 理解线程池 C程序源码知识点emplace_back 和 push_back有什么区别?互斥锁 mutexcondition_variablestd::move()函数bind()函数join 函数 线程池的原理就是管理一个任务队列和一个工作线程队列。 工作线程不断的从任务队列取任务,然…...

BP神经网络应用案例
目录 背景介绍 【神经网络符号说明】 【建立网络拓扑结构】 【神经网络学习步骤】 步骤1 准备输入和输出样本 步骤2 确定网络学习参数 步骤3 初始化网络权值W和阀值B 步骤4 计算网络第一层的输入和输出 步骤5 计算中间层(隐含层输入和输出) 步骤…...

日常学习记录随笔-大数据之日志(hadoop)收集实战
数据收集(nginx)--->数据分析---> 数据清洗--->数据聚合计算---数据展示 可能涉及到zabix 做任务调度我们的项目 电商日志分析 比如说我们现在有一个系统,我们的数仓建立也要有一个主题 我这个项目是什么我要干什么定义方向 对用户进行分析,用户信息 要懂整个数据的流…...

【云计算】相关解决方案介绍
文章目录 1.1 云服务环境 Eucalyptus1.1.1 介绍1.1.2 开源协议及语言1.1.3 官方网站 1.2 开源云计算平台 abiCloud1.2.1 开源协议及语言1.2.2 官方网站 1.3 分布式文件系统 Hadoop1.3.1 开源协议及语言1.3.2 官方网站 1.4 JBoss云计算项目集 StormGrind1.4.1 开源协议及语言1.4…...

攻防世界题目练习——Crypto密码新手+引导模式(二)(持续更新)
题目目录 1. 转轮机加密2. easychallenge 上一篇:攻防世界题目练习——Crypto密码新手引导模式(一)(持续更新) 1. 转轮机加密 首先了解一下轮转机加密吧。 传统密码学(三)——转轮密码机 题目内容如下: …...

LeetCode【1】两数之和
题目: 代码: public int[] twoSum(int[] nums, int target) {int[] result new int[2];Map<Integer, Integer> map new HashMap<>();// for (int i 0; i < nums.length; i) { // 这么写不能防重复啊!注意这里不…...

【运维笔记】VMWare 另一个程序已锁定文件的一部分,进程无法访问
情景再现 这里使用的是VMware 17 解决办法 进入设置 点击选项,全选复制里面内容 进入文件夹,删除所有包含.lck后缀的文件和文件夹 再启动虚拟机即可...

[Springboot]统一响应和异常处理配置
背景 前后端分离情况下,后端接口通常只需要返回JSON数据。 但有时候因为某些原因可能会导致得不到正确的结果。 比如 因为登录密码错误,你不能直接返回错误信息和null,这样前端很难处理。 又比如 因为后端接口爆出了异常,也不能直…...

Redis第四五六章 持久化事务主从复制
Redis ⽀持 RDB 和 AOF 两种持久化机制,持久化功能有效地避免因进程退出造成数据丢失问题, 当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。 目录 第四章 持久化 4.1 RDB 4.1.1 触发机制 4.1.2 流程说明 4.1.3 RDB ⽂件的处理 4.1.4 RDB 的优…...

【强烈推荐】免费的PDF工具,包括PDF拆分/分割、转WORD等功能的免费在线软件工具,救了大命,找了半天什么pdf365、福xipdf、还有哔果pdf全是打着免费名义收费,烦死了
PDF拆分 - 图文工具箱 - imgtool.net,嘎嘎好用,主要是免费 除此之外,还有其他的功能,需要的可以去看看...

SpringMVC源码分析(二)启动过程之RequestMappingHandlerMapping分析
a、http请求中的url是如何与对应Handler的即Controller对应method映射的? 在上篇中提到在SpringMVC中,初始化9大内置组件的时候其中有一个组件就是HandlerMapping,在初始化HandlerMapping的时候会加载代码中所有标注了Controller和RequestMap…...

KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(7)
接前一篇文章:KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(6) 上一回讲到了drm_internal_framebuffer_create函数中的framebuffer_check函数。讲解了该函数的参数检查部分中的第二部分,本回对于该函数余下部分进行解析。 为了便于理解,再次贴出framebuffer_ch…...

2023 年 Arm A-Profile 架构发展
随着人工智能 (AI) 的兴起和安全威胁的加剧,计算需求不断发展,作为世界设备核心的基础计算架构也必须不断发展。这就是为什么我们的工程团队向普遍存在的 Arm 架构添加新功能和技术,然后软件团队确保软件尽可能无缝地适应这些未来的功能和技术。 Arm架构是如何开发的 Arm …...

2023年09月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试
C/C++编程(1~8级)全部真题・点这里 Python编程(1~6级)全部真题・点这里 第1题:红与黑 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。 时间限…...

CentOS系统/root根目录扩容(扩展逻辑卷)
具体操作步骤 1、查看本机磁盘环境挂载情况 2、添加磁盘分区 3、开始扩容 4、同步到文件系统 1、查看本机磁盘环境挂载情况 [rooticon ~]# df -lh 可以看到/dev/mapper/centos-root 路径下容量为50G,我们要给这个路径下的容量扩容:[rooticon ~]# lsblk…...

苍穹外卖(三) 员工分页及技术实现细节
2. 员工分页查询 2.1 需求分析和设计 2.1.1 产品原型 2.1.2 接口设计 2.2 代码开发 2.2.1 设计DTO类 根据请求参数进行封装 2.2.2 封装PageResult 后面所有的分页查询,统一都封装为PageResult对象。 员工信息分页查询后端返回的对象类型为: Result 2.…...