当前位置: 首页 > article >正文

实战OpenCV之直方图

基础入门

        直方图是对数据分布情况的图形表示,特别适用于图像处理领域。在图像处理中,直方图通常用于表示图像中像素值的分布情况。直方图由一系列矩形条(也被称为bin)组成,每个矩形条的高度表示某个像素值(或像素值范围)在图像中出现的频次。直方图的横轴(X轴)表示像素值的范围,通常对于8位灰度图像,其范围为0-255。直方图的纵轴(Y轴)表示在图像中每个像素值(或像素值范围)出现的频次。

        在OpenCV中,计算直方图使用cv::calcHist函数,其函数原型如下。

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

        各个参数的含义如下。

        images:输入图像或图像集合的指针数组。images指向一个图像矩阵数组,nimages指定数组的长度,即要处理的图像数量。通常,当处理单个图像时,会传递一个包含单个图像地址的指针。

        nimages:输入图像的数量,表示images数组中图像的个数。

        channels:一个整数数组,指定了要从每个图像中使用的通道的索引。比如:如果处理的是一个三通道的BGR图像,并且只想计算蓝色通道的直方图,则应传递{0}。如果想计算所有通道的联合直方图,则可以传递{0, 1, 2}。

        mask: 可选的掩码图像,它定义了一个感兴趣的区域,只有这个区域内的像素才会被用于计算直方图。如果不需要掩码,可以传递一个空的cv::Mat()。

        hist:输出的直方图,它是一个多维数据的数组,其维度取决于dims和histSize参数。这个输出直方图需要事先创建好,或使用智能指自动管理内存。

        dims:直方图的维度。对于单通道图像,通常是1;对于多通道联合直方图,这个值会更高。

        histSize:指定每一维直方图的bin(桶)数量的数组。对于单通道8位图像,通常使用{256}来表示从0到255的每个灰度级都有一个bin。

        ranges:指定每个维度的值范围的数组的指针。通常情况下,对于单通道8位图像,每个维度的范围是{0, 256},意味着从0到255的像素值范围。数组中的每个元素都是一个包含两个元素(起始值和结束值)的数组。

        accumulate:如果设置为true,则当前计算的直方图会被累积到hist中已有的值上。默认值为false,意味着每次调用都会重新计算直方图,而不保留之前的结果。

实战解析

        在下面的实战代码中,我们首先读取一张图像,并转换为灰度图像img。然后,计算该图像的灰度直方图hist。其中histSize设为256,意味着将灰度级分为256个区间。接下来,我们使用cv::normalize函数对其进行归一化处理,以确保直方图的高度总和为1,便于可视化展示。

        最后,我们创建一个新的图像histImage,用于绘制直方图。通过循环遍历每个bin,根据每个bin的高度在histImage上绘制相应的矩形条,形成可视化的直方图。直方图的背景色为白色,矩形条的颜色为蓝色。

#include <opencv2/opencv.hpp>
using namespace cv;

#include <iostream>
using namespace std;

int main()
{
    Mat img = imread("OpenCV.png", IMREAD_GRAYSCALE);
    if(img.empty())
    {
        cout << "Can not open or find the image" << endl;
        return -1;
    }

    // 计算灰度直方图
    int channels[] = {0};
    int histSize = 256;
    float range[] = {0, 256};
    const float* histRange = {range};
    bool uniform = true;
    bool accumulate = false;
    Mat hist;
    calcHist(&img, 1, channels, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);

    // 对直方图进行归一化
    normalize(hist, hist, 0, 1, NORM_MINMAX, -1, Mat());

    // 绘制直方图
    int hist_w = 512;
    int hist_h = 400;
    int bin_w = cvRound((double)hist_w / histSize);
    Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(255, 255, 255));
    for(int i = 1; i < histSize; i++)
    {
        rectangle(histImage, Point(bin_w*(i-1), hist_h - cvRound(hist.at<float>(i-1)*hist_h)),
            Point(bin_w*(i), hist_h), Scalar(255, 0, 0), -1);
    }

    // 显示原图和直方图
    imshow("Original Image", img);
    imshow("Histogram", histImage);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

        执行上面的代码,运行效果可参考下图。

直方图比较

        比较两个直方图的相似度是通过cv::compareHist函数实现的,常用于图像检索、对象识别等场景,该函数的原型如下。

double cv::compareHist(InputArray H1, InputArray H2, int method);

        各个参数的含义如下。

        H1:第一个直方图。

        H2:第二个直方图,需要与H1有相同的尺寸和类型。

        method:指定用于比较直方图的方法,被定义为枚举类型cv::HistCompMethods,常用的比较方法如下。

          (1)cv::HISTCMP_CORREL:相关性方法,计算两个直方图之间的皮尔逊相关系数。此时,函数返回值表示两个直方图之间的皮尔逊相关系数,范围从[-1, 1]。值接近1表示两个直方图非常相似(高度相关),值接近-1表示两者几乎完全负相关,而接近0则表示没有线性关系。

          (2)cv::HISTCMP_INTERSECT:交集方法,计算两直方图的重叠部分。此时,函数返回值的范围是[0, 1]。值越接近1表示直方图越相似,值越接近0表示差异越大。

          (3)cv::HISTCMP_BHATTACHARYYA:巴氏距离,计算两直方图的巴塔查里亚距离。此时,函数返回值用于衡量两个直方图的相似度。这是一种基于概率密度函数的测度,值越小表示直方图越相似。最大值为1,表示完全不相似。

        在下面的实战代码中,我们首先读取两张灰度图像img1和img2。然后,分别计算每张图像的灰度直方图,并使用cv::normalize函数对直方图进行了归一化处理。接下来,我们使用cv::compareHist函数比较这两个直方图。最后,我们输出了比较结果,展示了两幅图像直方图的相似度。

#include <opencv2/opencv.hpp>
using namespace cv;

#include <iostream>
using namespace std;

int main()
{
    Mat img1 = imread("OpenCV.png", IMREAD_GRAYSCALE);
    Mat img2 = imread("C++.png", IMREAD_GRAYSCALE);
    if(img1.empty() || img2.empty())
    {
        cout << "Can not open or find the image" << endl;
        return -1;
    }

    // 计算两幅图像的直方图
    int histSize = 256;
    float range[] = {0, 256};
    const float* histRange = {range};
    bool uniform = true;
    bool accumulate = false;
    Mat hist1;
    Mat hist2;
    calcHist(&img1, 1, 0, Mat(), hist1, 1, &histSize, &histRange, uniform, accumulate);
    calcHist(&img2, 1, 0, Mat(), hist2, 1, &histSize, &histRange, uniform, accumulate);

    // 归一化直方图
    normalize(hist1, hist1, 0, 1, NORM_MINMAX, -1, Mat());
    normalize(hist2, hist2, 0, 1, NORM_MINMAX, -1, Mat());

    // 比较直方图
    double result = compareHist(hist1, hist2, HISTCMP_CORREL);
    // 输出:0.953461
    cout << result << endl;

    waitKey(0);
    destroyAllWindows();
    return 0;
}


http://www.kler.cn/a/315985.html

相关文章:

  • 轮转数组
  • 整理iPhone空间:iphone怎么删除相簿
  • 【CSS】什么是BFC?
  • java的JJWT 0.91在jdk21中报错的解决方法
  • 百度搜索AI探索版多线程批量生成TXT原创文章软件-可生成3种类型文章
  • arcgis做buffer
  • ESP32-WROOM-32 [ESP连接路由器+TCP Client 透传 + TCP Server数据发送]
  • 网络安全:构建数字世界的坚实防线
  • ps学习。
  • 经典大语言模型解读(3):参数量更大、泛化性能更强的生成式模型GPT-2
  • 低代码开发平台系统架构概述
  • js进阶——函数作用域和块作用域
  • 卷积神经网络(CNN):深度学习中的视觉奇迹
  • 【论文阅读】Benchmarking Retrieval-Augmented Generation for Medicine
  • Redis 持久化数据
  • 【详细解答】指出下面指令的错误:IN AL,300H
  • MySQL高阶1939-主动请求确认消息的用户
  • 占用消防通道监测摄像机
  • MyBatis-Plus 插件扩展
  • linux强制关闭再启动后zookeeper无法启动
  • 使用Python免费将pdf转为docx
  • JVM频繁Full GC问题的排查与解决方案
  • 展锐平台的手机camera 系统开发过程
  • 2024年最新前端工程师 TypeScript 基础知识点详细教程(更新中)
  • Java启动Tomcat: Can‘t load IA 32-bit .dll on a AMD 64-bit platform报错问题解决
  • 【小沐学GIS】blender导入OpenStreetMap城市建筑(blender-osm、blosm)