OpenCV10-图像直方图:直方图绘制、直方图归一化、直方图比较、直方图均衡化、直方图规定化、直方图反射投影

本文主要是介绍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,rminVs(n)Vz(n)
下面说明直方图匹配过程:

运算步骤和结果
原图像灰度级01234567
原直方图概率0.190.240.20.170.090.050.030.02
原直方图累积概率0.190.430.630.80.890.940.970.99
目标直方图概率0000.160.190.290.20.16
目标直方图累积概率0000.160.350.640.841
匹配的灰度值34566777
映射关系0->31->42->53->64->65->76->77->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-图像直方图:直方图绘制、直方图归一化、直方图比较、直方图均衡化、直方图规定化、直方图反射投影的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/205552

相关文章

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

关键字synchronized、volatile的比较

关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字的执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。多线程访问volatile不会发生阻塞,而synchronize

Verybot之OpenCV应用一:安装与图像采集测试

在Verybot上安装OpenCV是很简单的,只需要执行:         sudo apt-get update         sudo apt-get install libopencv-dev         sudo apt-get install python-opencv         下面就对安装好的OpenCV进行一下测试,编写一个通过USB摄像头采

【反射知识点详解】

Java中的反射(Reflection)是一个非常强大的机制,它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect包中的类和接口来实现。 通过反射,Java程序可以动态地创建对象、调用方法、访问字段,以及获取类的各种信息(如构造器、方法、字段等)。 反射的用途 反射主要用于以下几种情况: 动态创建对象:通过类的Class对象动态地创建其实例。访问类的字段

Go 在orm中使用反射

作为静态语言,golang 稍显笨拙,还好 go 的标准包reflect(反射)包弥补了这点不足,它提供了一系列强大的 API,能够根据执行过程中对象的类型来改变程序控制流。本文将通过设计并实现一个简易的 mysql orm 来学习它,要求读者了解mysql基本知识,并且跟我一样至少已经接触 golang 两到三个月。 orm 这个概念相信同学们都非常熟悉,尤其是写过rails的同学,对acti

类型信息:反射-Class

在说反射前提一个概念:RTTI(在运行时,识别一个对象的类型) public class Shapes {public static void main(String[] args) {List<Shape> shapes = Arrays.asList(new Circle(), new Square(), new Triangle());for (Shape shape : shapes