17.OpenCV图像直方图、二维直方图与直方图均衡化详解

发布于:2025-03-30 ⋅ 阅读:(83) ⋅ 点赞:(0)

OpenCV图像直方图、二维直方图与直方图均衡化详解

   在图像处理领域,直方图是一种常用的统计工具,用于描述图像中像素值的分布情况。直方图不仅能揭示图像的对比度、亮度等信息,还可用于图像增强、分割、匹配等应用。本文将介绍如何使用 OpenCV C++ 计算图像直方图、构建二维直方图,以及如何进行直方图均衡化,从而提升图像质量。

1. 图像直方图

1.1 直方图基本概念

图像直方图(Image Histogram)是用于描述图像中像素值分布的统计图表,通过直观展示不同像素值的出现频率,帮助分析图像的亮度、对比度和颜色特征。它是图像处理中最基础且重要的工具之一。例如,对于灰度图像,横轴表示像素强度(0~255),纵轴表示每个强度值的像素数量。直方图能够帮助我们分析图像的曝光情况、对比度以及动态范围。

  1. 横轴(X轴)
    • 表示像素的取值范围(如灰度图像的0-255,0为纯黑,255为纯白)。
    • 对于彩色图像,可以分别统计R、G、B通道的直方图。
  2. 纵轴(Y轴)
    • 表示对应像素值的出现频率(即该像素值在图像中出现的次数)。
  3. 图形形态
    • 每个柱状条(bin)代表一个像素值区间内像素的数量总和。

1.2核心作用

  1. 诊断图像问题
    • 检测曝光问题(过曝/欠曝)、对比度不足等。
    • 例如:直方图集中在中间可能表明对比度低。
  2. 指导图像增强
    • 直方图均衡化:拉伸像素分布,增强对比度。
    • 自适应直方图均衡化(CLAHE):避免局部过增强。
  3. 辅助图像分割
    • 通过直方图峰谷分析,确定阈值(如Otsu算法)。
  4. 颜色分析
    • 识别主色调,分析颜色分布是否均衡。

1.3 示例代码:灰度图直方图计算与绘制

在 OpenCV 中,计算直方图主要使用函数 cv::calcHist。该函数可以针对单通道或多通道图像进行统计。
函数原型

void calcHist(
    const Mat* images,      // 输入图像数组
    int nimages,            // 图像数量(通常为 1)
    const int* channels,    // 指定计算直方图的通道索引(如 0 表示 BGR 中的 B 通道)
    InputArray mask,        // 可选的掩码(用于指定计算某个区域的直方图)
    OutputArray hist,       // 输出直方图(Mat 类型)
    int dims,               // 直方图的维度(如 1 表示灰度直方图,2 表示 H-S 直方图)
    const int* histSize,    // 每个维度的 bin 数(如 256 表示 256 个灰度级)
    const float** ranges,   // 每个维度的取值范围(如 [0, 256])
    bool uniform = true,    // 是否均匀分布 bin(默认 true)
    bool accumulate = false // 是否累积计算(默认 false)
);

计算图片直方图

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取灰度图像
    Mat gray = imread("E:\\image\\lena.png", IMREAD_GRAYSCALE);
    if (gray.empty()) {
        cerr << "加载图像失败!" << endl;
        return -1;
    }

    // 设置直方图参数
    int histSize = 256; // 256个灰度级
    float range[] = { 0, 255 };
    const float* histRange = { range };

    // 计算直方图
    Mat hist;
    calcHist(&gray, 1, 0, Mat(), hist, 1, &histSize, &histRange);

    // 绘制直方图
    int hist_w = 512, hist_h = 400;
    int bin_w = cvRound((double)hist_w / histSize);
    Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(255,255,255));

    // 归一化直方图到 [0, histImage.rows]
    normalize(hist, hist, 0, histImage.rows, NORM_MINMAX);

    for (int i = 1; i < histSize; i++) {
        line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
            Point(bin_w * (i), hist_h - cvRound(hist.at<float>(i))),
            Scalar(0,0,255), 2);
    }

    imshow("灰度图像", gray);
    imshow("直方图", histImage);
    waitKey(0);
    return 0;
}

图片直方图

2. 二维直方图

2.1 二维直方图概述

二维直方图用于描述图像中两个通道之间的联合分布。例如,对于彩色图像中的 Hue(色调)和 Saturation(饱和度),我们可以构建一个二维直方图来反映颜色的联合统计信息,这在颜色分割、目标检测等任务中非常有用。

2.2 OpenCV 中计算二维直方图

同样使用 cv::calcHist 函数,但需要同时传入两个通道的数据,并设置维数为 2

示例代码:计算 HSV 图像中 Hue 和 Saturation 的二维直方图

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取彩色图像并转换到 HSV 空间
    Mat img = imread("E:\\image\\lena.png");
    if (img.empty()) {
        cerr << "加载图像失败!" << endl;
        return -1;
    }
    Mat hsv;
    cvtColor(img, hsv, COLOR_BGR2HSV);

    // 设置二维直方图参数
    int hBins = 50, sBins = 60;
    int histSize[2] = { hBins, sBins };
    // H 范围:0~180,S 范围:0~256
    float hRanges[] = { 0, 180 };
    float sRanges[] = { 0, 256 };
    const float* ranges[] = { hRanges, sRanges };
    int channels[] = { 0, 1 }; // 使用 H 和 S 通道

    // 计算二维直方图
    Mat hist;
    calcHist(&hsv, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);

    // 归一化直方图到 [0, 255]
    normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());

    // 将二维直方图显示为图像
    int scale = 4;  // 调整显示图像的尺寸因子
    Mat histImg = Mat::zeros(sBins * scale, hBins * scale, CV_8UC3);

    // 注意:hist 的维度为 [hBins x sBins]
    // 这里遍历 hBins 和 sBins,绘制每个 bin 的强度
    for (int h = 0; h < hBins; h++) {
        for (int s = 0; s < sBins; s++) {
            float binVal = hist.at<float>(h, s);
            int intensity = cvRound(binVal);
            // 使用颜色填充直方图的每个小矩形区域
            rectangle(histImg,
                Point(h * scale, s * scale),
                Point((h + 1) * scale - 1, (s + 1) * scale - 1),
                Scalar(intensity, intensity, intensity),
                FILLED);
        }
    }
    //转为彩色热力图
	applyColorMap(histImg, histImg, COLORMAP_JET);
    imshow("原始图像", img);
    imshow("二维直方图 (H vs S)", histImg);
    waitKey(0);
    return 0;
}

此示例展示了如何计算 HSV 图像中 Hue 和 Saturation 的二维直方图,并将统计结果绘制为一幅热图,直观反映颜色分布情况。
二维直方图

3. 直方图均衡化

3.1 均衡化原理

直方图均衡化是一种图像增强技术,其目标是改善图像对比度。通过重新分配像素的灰度值,使得直方图变得更均匀,达到突出细节和边缘的效果。该方法常用于灰度图像,但对于彩色图像,通常先将图像转换到 YCrCb 或 HSV 空间,仅对亮度通道进行均衡化,再转换回原空间。

3.2 OpenCV 中直方图均衡化

OpenCV 提供了 cv::equalizeHist 函数,该函数针对单通道图像进行均衡化。

示例代码:灰度图像直方图均衡化

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取灰度图像
    Mat gray = imread("E:\\image\\Opencv-logo.png",IMREAD_GRAYSCALE);
    if (gray.empty()) {
        cerr << "加载图像失败!" << endl;
        return -1;
    }

    // 进行直方图均衡化
    Mat equalized;
    equalizeHist(gray, equalized);

    // 显示原图和均衡化后的图像
    imshow("原始灰度图像", gray);
    imshow("直方图均衡化后的图像", equalized);
    waitKey(0);
    return 0;
}

运行后如下图左边对比度明显增强
直方图均衡对比度增强

示例代码:彩色图像直方图均衡化

对于彩色图像,我们可以先转换为 YCrCb 空间,对 Y 通道进行均衡化,再转换回 BGR 空间。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取彩色图像
    Mat img = imread("E:\\image\\lena.png");
    if (img.empty()) {
        cerr << "加载图像失败!" << endl;
        return -1;
    }

    // 转换到 YCrCb 空间
    Mat img_ycrcb;
    cvtColor(img, img_ycrcb, COLOR_BGR2YCrCb);

    // 分离通道,均衡化 Y 通道
    vector<Mat> channels;
    split(img_ycrcb, channels);
    equalizeHist(channels[0], channels[0]);
    merge(channels, img_ycrcb);

    // 转换回 BGR 空间
    Mat img_equalized;
    cvtColor(img_ycrcb, img_equalized, COLOR_YCrCb2BGR);

    imshow("原始彩色图像", img);
    imshow("彩色图直方图均衡化", img_equalized);
    waitKey(0);
    return 0;
}

在这里插入图片描述

3.3 自适应直方图均衡

自适应直方图均衡(Adaptive Histogram Equalization, AHE)是一种改进的直方图均衡化方法,它不是对整幅图像进行全局均衡,而是将图像划分为多个小区域(称为子块窗口),在每个子块上分别执行直方图均衡化,以增强局部对比度。

基本步骤:

  1. 划分子块(Tiles): 将图像分成多个小的子区域(如 8×8 或 16×16);
  2. 计算直方图均衡化: 对每个子块单独计算累积分布函数(CDF)并应用均衡化;
  3. 插值平滑(Interpolation): 由于不同子块的均衡化结果可能不连续,采用双线性插值使其过渡平滑,避免块状效应;
  4. 边界处理: 处理图像边缘区域,防止伪影出现。

对比普通直方图均衡化的优势:

  • 适用于局部光照不均的图像,增强细节;
  • 适用于对比度较低的区域,而不会过度增强对比度已高的区域;
  • 解决了全局均衡化可能导致的过度曝光或伪影问题。

改进方法:CLAHE(对比度受限自适应直方图均衡化)

CLAHE(Contrast Limited Adaptive Histogram Equalization)是在 AHE 基础上增加了对比度限制,防止某些局部区域对比度过高而产生噪声。其核心思想是对每个子块的直方图设定一个对比度限制阈值,若某个灰度级的频率超过阈值,则将超出的部分均匀分配到所有灰度级,最终进行均衡化。

OpenCV 实现:

在 OpenCV 中,自适应直方图均衡化可通过 cv::createCLAHE() 实现,例如:

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

int main() {
    Mat img = imread("image.jpg", IMREAD_GRAYSCALE);
    Ptr<CLAHE> clahe = createCLAHE(2.0, Size(8, 8));  // 限制对比度 2.0,窗口大小 8×8
    Mat dst;
    clahe->apply(img, dst);
    imshow("Original", img);
    imshow("CLAHE", dst);
    waitKey(0);
    return 0;
}

4. 小结

本文详细介绍了 OpenCV C++ 中的直方图分析技术,包括:

  • 图像直方图:利用 cv::calcHist 计算灰度图像的直方图,并绘制统计结果。
  • 二维直方图:通过统计两个通道(如 HSV 中的 H 和 S)的联合分布,构建二维直方图热图,展示颜色分布信息。
  • 直方图均衡化:使用 cv::equalizeHist 对灰度图像进行均衡化,同时介绍了彩色图像均衡化的常用方法(基于 YCrCb 色彩空间).
  • 自适应均衡化:一种改进的直方图均衡化方法,它不是对整幅图像进行全局均衡,而是将图像划分为多个小区域(称为子块窗口),在每个子块上分别执行直方图均衡化,以增强局部对比度

通过这些技术,我们不仅可以对图像的灰度或颜色分布进行详细分析,还能改善图像对比度,从而在图像预处理、特征提取和目标检测等任务中发挥重要作用.


网站公告

今日签到

点亮在社区的每一天
去签到