OpenCV 直方图和应用

OpenCV 直方图

什么是图像直方图(Histogram)?

  • 它是图像的强度分布的图形表示。
  • 它量化所考虑的每个强度值的像素数。
  • 直方图的横坐标代表像素值,纵坐标代表像素的个数,如下:
    OpenCV 直方图和应用_第1张图片

直方图可以做什么?

图像直方图由于其计算代价较小,且具有图像平移、旋转、缩放不变性等众多优点,广泛地应用于图像处理的各个领域,特别是灰度图像的阈值分割、基于颜色的图像检索以及图像分类
图像分割
图像分割是图像识别的基础,对图像进行图像分割,将目标从背景区域中分离出,可以避免图像识别时在图像上进行盲目的搜索,大大提高图像识别的效率以及识别准确率。基于灰度直方图的阈值分割计算简单,适用于目标与背景分布于不同灰度范围的灰度图像,特别是遥感图像。
图像检索
图像检索是指快速有效地从大规模图像数据库中检索出所需的图像,是目前一个非常重要又富有的挑战性的研究课题。颜色特征由于其直观性、计算代价较小等优点,在图像检索中扮演着重要角色,早期的图像检索算法也主要利用颜色特征,特别是颜色直方图。
图像分类
图像分类任务主要是对一组图进行一系列自在这里插入图片描述
动处理,最终确定图形所属的类别。图像分类具有广泛的应用前景,是计算机视觉的难点问题。针对图像分类的算法众多,其中以基于bag-words模型的方法最为经典有效。该方法首先利用提取的颜色、形状等特征构建视觉词典,然后在图像上统计视觉词的直方图,最后利用视觉词直方图作为特征运用分类器进行分类决策。

获取直方图数据

opencv 可以使用calcHist函数来获取直方图

void calcHist( const Mat* images, int nimages,
                          const int* channels, InputArray mask,
                          OutputArray hist, int dims, const int* histSize,
                          const float** ranges, bool uniform=true, bool accumulate=false );

参数解释:

  • images: 输入的图像的指针(数组);
  • nimages: 输入图像个数;
  • channels:需要统计直方图的第几通道;
  • mask: 掩模,mask必须是一个8位(CV_8U)的数组并且和images的数组大小相同;
  • hist: 直方图计算的输出值;
  • dims: 输出直方图的维度(由channels指定);
  • histSize:直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);
  • ranges: 统计像素值的区间;
  • uniform=true:是否对得到的直方图数组进行归一化处理;
  • accumulate=false:在多个图像时,是否累积计算像素值的个数;

OpenCV 绘制直方图

绘制灰度直方图

/*
 * 1. 读取原图像并转化为灰度图
 */
cv::Mat srcImg, grayImg;
srcImg = cv::imread("./img/dog.jpg");
if (!srcImg.data){
/*文件读取错误*/
	return -1;
}
cv::cvtColor(srcImg, grayImg, cv::CV_BGR2GRAY);//转换为灰度图像

/*
 * 2. calcHist 来获取颜色分布强度数据
 */
const int channels[] = { 0 };
cv::Mat hist;
int dims = 1;//设置直方图维度,灰度直方图为1通道
const int histSize[] = { 256 };//直方图每一个维度划分的柱条的数目
//每一个维度取值范围
float pranges[] = { 0, 255 };//取值区间
const float* ranges[] = { pranges };
cv::calcHist(&grayImg, 1, channels, cv::Mat(), hist, dims, histSize, ranges, true, false);//计算直方图

/*
 * 3. 通过calcHist的数据来绘制灰度直方图
 */
int scale = 2;
int hist_height = 256;
cv::Mat hist_img = cv::Mat::zeros(hist_height, 256 * scale, CV_8UC3); //创建一个黑底的8位的3通道图像,高256,宽256*2
double minval,maxval;
cv::minMaxLoc(hist, &minval, &maxval);//定义矩阵中最小值,最大值的位置
int scale = 2;
int hist_height = 256;
cv::Mat hist_ing = cv::Mat::zeros(hist_height, scale * bins, CV_8UC3);
for (int i = 0; i < bins; i++)
{
    float bin_val = hist.at(i);//图像的灰度频率表
    int inten = cvRound(bin_val * hist_height / maxval);//绘制高度
    rectangle(hist_ing, cv::Point(scale * i, hist_height - 1), cv::Point((i + 1) * scale - 1, hist_height - inten), CV_RGB(255, 255, 255));
}
    
cv::imshow("gray hist", hist_ing);
cv::waitKey(0);

OpenCV 绘制BGR各个通道直方图

绘制各个通道的直方图方法和绘制灰度直方图方法一样,通过cv::split 分离出各个通道数据,再分别绘制

int histSize = 256;
float range[] = { 0,255 };
const float* Ranges = { range };

cv::Mat src = cv::imread(m_strImgPath);
std::vectorbgr_planes;
cv::split(src, bgr_planes);

cv::Mat b_hist, g_hist, r_hist;
cv::calcHist(&bgr_planes[0], 1, 0, cv::Mat(), b_hist, 1, &histSize, &Ranges, true, false);
cv::calcHist(&bgr_planes[1], 1, 0, cv::Mat(), g_hist, 1, &histSize, &Ranges, true, false);
cv::calcHist(&bgr_planes[2], 1, 0, cv::Mat(), r_hist, 1, &histSize, &Ranges, true, false);

//归一化
int hist_w = 500;//直方图的图像的宽
int hist_h = 300; //直方图的图像的高
int nHistSize = 256;
int bin_w = cvRound((double)hist_w / nHistSize);	//区间
cv::Mat histImage(hist_h, hist_w, CV_8UC3, cv::Scalar(0, 0, 0));//绘制直方图显示的图像
cv::normalize(b_hist, b_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());//归一化
cv::normalize(g_hist, g_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
cv::normalize(r_hist, r_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());

for (int i = 1; i < nHistSize; i++)
{
    //绘制蓝色分量直方图
    cv::line(histImage, cv::Point((i - 1) * bin_w, hist_h - cvRound(b_hist.at(i - 1))),
        cv::Point((i)*bin_w, hist_h - cvRound(b_hist.at(i))), cv::Scalar(255, 0, 0), 2);
    //绘制绿色分量直方图
    cv::line(histImage, cv::Point((i - 1) * bin_w, hist_h - cvRound(g_hist.at(i - 1))),
        cv::Point((i)*bin_w, hist_h - cvRound(g_hist.at(i))), cv::Scalar(0, 255, 0), 2);
    //绘制红色分量直方图
    cv::line(histImage, cv::Point((i - 1) * bin_w, hist_h - cvRound(r_hist.at(i - 1))),
        cv::Point((i)*bin_w, hist_h - cvRound(r_hist.at(i))), cv::Scalar(0, 0, 255), 2);
}

同理可以绘制HSV等颜色空间的各个通道直方图

颜色直方图实际应用

图像分割,物体分拣

图像检索

图像分类,bag-words模型

参考文档

你可能感兴趣的:(opencv,计算机视觉,opencv,人工智能)