尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenCV 直方图均衡化完全指南:equalizeHist 原理、源码剖析与 C++/Python 实战

OpenCV 直方图均衡化完全指南:equalizeHist 原理、源码剖析与 C++/Python 实战 OpenCV 直方图均衡化完全指南equalizeHist 原理、源码剖析与 C/Python 实战【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv本指南围绕 OpenCV 官方直方图均衡化教程 histogram_equalization.markdown 展开系统讲解图像直方图的基本概念、直方图均衡化的数学原理CDF 重映射并深入cv::equalizeHist的函数实现与配套示例代码。读完本文你将理解什么是图像直方图、均衡化为何能提升对比度、equalizeHist的算法与边界约束并能在 C 与 Python 项目中直接复现这一经典图像增强流程。本教程为 OpenCV 官方教程Original author: Ana Huamán兼容 OpenCV 3.0核心函数为cv::equalizeHist直方图均衡化。学习目标通过本篇教程你将掌握什么是图像直方图以及它为何有用什么是直方图均衡化它如何拉伸像素强度分布如何使用 OpenCV 的cv::equalizeHist函数对图像做直方图均衡化均衡化前后图像及其直方图的变化规律作为效果验证手段。什么是图像直方图图像直方图是对图像灰度强度分布的图形化表示。它统计图像中每一个强度值bin所对应的像素个数横轴是强度值Intensity纵轴是该强度下的像素数量Pixels。对一幅 8 位灰度图而言强度范围是 0~255因此直方图通常包含 256 个统计项。下图是教程中的示例左侧为灰度原图右侧红色部分即其直方图——可以明显看到像素强度大多聚集在较低强度的区域直方图的价值在于它能用一张二维图直观暴露图像的整体明暗分布与对比度状况。例如低对比度图像通常表现为像素集中在较窄的强度区间内而高对比度图像的直方图则会更均匀地铺满整个强度范围。直方图的绘制方法参见仓库中的 直方图计算教程。什么是直方图均衡化直方图均衡化是一种提升图像对比度的方法其目的就是把聚集在狭窄区间内的像素强度向整个可用强度范围拉伸stretch out。如下图所示左侧原始直方图中像素强度大量聚集在中低强度区域中间是均衡化后的直方图分布明显更宽、更均匀右侧为均衡化后增强出的图像亮度与细节都得到改善。图中绿色圆圈标出的即是均衡化前像素稀疏underpopulated的强度区间均衡化之后这些区间也被合理填充这种方法的本质是将图像原有的直方图这一种概率分布重映射mapping为另一种更宽、更均匀的强度分布使图像明暗层次铺满整个取值范围从而增强视觉对比度。均衡化的数学原理为什么用累积分布函数均衡化实质上是一个强度重映射过程。要让像素在新分布中尽可能均匀地铺开需要使用原直方图的累积分布函数Cumulative Distribution Function, CDF作为映射函数。设原直方图为 \(H(i)\)其中 \(i\) 为强度值其累积分布 \(H(i)\) 定义为H(i) Σ_{0 ≤ j i} H(j)即强度小于i的所有像素总数。为了把它直接用作重映射查找表LUT还需要对 \(H(i)\) 做归一化使其最大值等于图像允许的最大强度值对 8 位图即 255。归一化后的累积分布函数曲线如下图所示最后只需按下面的映射式逐像素重映射即可得到均衡化后的图像equalized(x, y) H( src(x, y) )也就是说对原图每个像素用其灰度值在归一化 CDF 中查表把查到的累积概率放大到 0~255 后作为新灰度写入输出图。由于 CDF 是单调递增的原图中较暗的区域会被分配到较低的灰度、较亮区域被分配到较高灰度累积概率差较大的区间被拉开于是整体对比度增强。在 OpenCV 的官方头文件注释中该算法被描述为四个步骤见 imgproc.hpp计算输入src的直方图 \(H\)归一化直方图使各 bin 之和为 255计算直方图的积分 \(Hi \sum{0 \le j i} H(j)\)将 \(H\) 作为查找表对图像逐像素变换\(\texttt{dst}(x,y) H(\texttt{src}(x,y))\)。头文件同时给出了函数语义与约束该算法会归一化图像亮度并提升对比度src必须是8 位单通道灰度图像dst与src同尺寸、同类型。实战示例程序做什么仓库中随教程配套了完整可运行的示例程序流程为加载一张输入图像将原图转换为灰度图调用cv::equalizeHist对灰度图做直方图均衡化在同一窗口区域显示原图与均衡化后的图像。C 版本示例源码位于 samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp完整代码#include opencv2/imgcodecs.hpp #include opencv2/highgui.hpp #include opencv2/imgproc.hpp #include iostream using namespace cv; using namespace std; int main( int argc, char** argv ) { // [Load image] CommandLineParser parser( argc, argv, {input | lena.jpg | input image} ); Mat src imread( samples::findFile( parser.getString( input ) ), IMREAD_COLOR ); if( src.empty() ) { cout Could not open or find the image!\n endl; cout Usage: argv[0] Input image endl; return -1; } // [Load image] // [Convert to grayscale] cvtColor( src, src, COLOR_BGR2GRAY ); // [Convert to grayscale] // [Apply Histogram Equalization] Mat dst; equalizeHist( src, dst ); // [Apply Histogram Equalization] // [Display results] imshow( Source image, src ); imshow( Equalized Image, dst ); // [Display results] // [Wait until user exits the program] waitKey(); // [Wait until user exits the program] return 0; }编译时链接opencv_imgcodecs、opencv_highgui、opencv_imgproc模块即可。运行时默认输入图片为lena.jpg可通过第一个命令行参数指定任意图片例如./EqualizeHist_Demo path/to/your/image。Python 版本示例源码位于 samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py完整代码from __future__ import print_function import cv2 as cv import argparse # [Load image] parser argparse.ArgumentParser(descriptionCode for Histogram Equalization tutorial.) parser.add_argument(--input, helpPath to input image., defaultlena.jpg) args parser.parse_args() src cv.imread(cv.samples.findFile(args.input)) if src is None: print(Could not open or find the image:, args.input) exit(0) # [Load image] # [Convert to grayscale] src cv.cvtColor(src, cv.COLOR_BGR2GRAY) # [Convert to grayscale] # [Apply Histogram Equalization] dst cv.equalizeHist(src) # [Apply Histogram Equalization] # [Display results] cv.imshow(Source image, src) cv.imshow(Equalized Image, dst) # [Display results] # [Wait until user exits the program] cv.waitKey() # [Wait until user exits the program]运行方式python EqualizeHist_Demo.py --input /path/to/your/image.jpg代码逐步讲解下面把示例拆成几个关键步骤逐一说明。1. 加载源图像C 侧使用CommandLineParser解析参数默认图片名lena.jpg再调用imread以彩色模式读入并通过src.empty()判空兜底Python 侧使用argparse接收--input同样用cv.imreadNone判断读图失败。Mat src imread( samples::findFile( parser.getString( input ) ), IMREAD_COLOR ); if( src.empty() ) { /* 报错并返回 -1 */ }2. 转换为灰度图由于equalizeHist只接受单通道 8 位图像需先用cvtColor把 BGR 三通道图转成灰度cvtColor( src, src, COLOR_BGR2GRAY );对应 Pythonsrc cv.cvtColor(src, cv.COLOR_BGR2GRAY)。注意若对三通道彩色图直接调用equalizeHist会触发源码中的CV_Assert( _src.type() CV_8UC1 )断言失败详见下文源码剖析。如需增强彩色图应先将图像转换到YCrCb/HSV/Lab等亮度与色度分离的色彩空间仅对亮度/明度通道做均衡化后再合并回来或对每个通道单独均衡化。这些属于基于函数约束的工程处理方式官方示例保持灰度图演示。3. 调用 equalizeHist 做直方图均衡化Mat dst; equalizeHist( src, dst );对应 Pythondst cv.equalizeHist(src)。从调用方式可见该函数唯一的两个参数就是输入图与输出图src为源 8 位单通道图dst为与src同尺寸、同类型的输出图。4. 显示与退出用imshow分别展示 Source image 与 Equalized Image再调用waitKey()等待用户按键退出程序。源码级剖析equalizeHist 究竟怎么算cv::equalizeHist的实现位于 modules/imgproc/src/histogram.cpp与其顶层 SIMD 分发相关的内核在 equalize_hist.dispatch.cpp 与 equalize_hist.simd.hpp。结合源码可以看出完整执行链路如下① 入参校验与运行分支CV_Assert( _src.type() CV_8UC1 )强约束输入必须是8 位单通道彩色图/多通道图在此直接报断言错误_src.empty()时空输入直接返回CV_OCL_RUN(... ocl_equalizeHist(...))当输入为二维UMat且启用了 OpenCL 时走 OpenCL 加速路径CALL_HAL(equalizeHist, ...)优先尝试调用可插拔的 HAL 硬件加速实现。② 并行计算灰度直方图代码使用EqualizeHistCalcHist_Invoker计算直方图其内部枚举HIST_SZ 256对应 8 位灰度的全部取值见 histogram.cpp。该类派生自cv::ParallelLoopBody每个线程先在栈上维护localHistogram[256]处理完若干行后加总到全局直方图当EqualizeHistCalcHist_Invoker::isWorthParallel(src)判定并行收益足够时通过parallel_for_按行切分并行执行。③ 计算归一化累积查找表LUT首先跳过直方图中首个非零 bin记为索引iint i 0; while (!hist[i]) i;特殊情形若图像所有像素都落在同一个 binhist[i] total即整幅图是单一灰度的平坦图无法也不需要进行均衡化直接dst.setTo(i)返回原值计算缩放因子并构建 LUTfloat scale (hist_sz - 1.f)/(total - hist[i]); int sum 0; for (lut[i] 0; i hist_sz; i) { sum hist[i]; lut[i] saturate_castuchar(sum * scale); }可见其核心正是文档中描述的对直方图做累积积分后按 256/总像素数归一化lut从首个有效 bin 之后逐项累加直方图值sum再乘以scale并用saturate_castuchar把结果安全截断到 0~255。于是lut就是一条单调递增、最大输出接近 255 的归一化累积分布查找表。④ 用 LUT 重映射整幅图像最后创建EqualizeHistLut_Invoker把lut_u8查找表应用到每一个像素dst(x,y) lut[src(x,y)]同样地当图像足够大时通过parallel_for_多线程并行完成。LUT 的实际读写在内核层由equalizeHistLut_系列实现并支持通过 dispatch 机制针对不同 CPU 指令集选用 SIMD 优化版本。综上所述equalizeHist的内存/时间成本为 O(N)N 为像素总数只额外使用 256 长度的直方图与 LUT 数组非常轻量。仓库内对应的回归测试见 modules/imgproc/test/test_histograms.cppOpenCL 路径与 UMat 路径分别在 ocl/test_imgproc.cpp 与 test_imgproc_umat.cpp 中覆盖。运行结果与效果验证教程推荐使用一张本身对比度较低的图像来体会均衡化的改善。例如这张原始图像低对比度注意其画面整体偏灰、层次不清观察它的直方图Histogram_Equalization_Original_Histogram.jpg可以看到像素明显聚集在直方图的中心/中低区域两端基本空缺这正是对比度不足的直接证据。经过equalizeHist处理后得到如下结果图像的整体对比度显著提升明暗层次变得丰富再看它新生成的直方图Histogram_Equalization_Equalized_Histogram.jpg像素已从原来聚集的窄区间铺展到几乎整个 0~255 强度范围分布明显更均匀。前后直方图的这种从集中到铺开的变化就是均衡化生效的量化验证。想复现上图中直方图的绘制代码可参考仓库内的直方图计算教程。使用建议与注意事项equalizeHist只接受 8 位单通道CV_8UC1灰度图这是源码CV_Assert强制的约束输入其他类型会直接抛出断言异常输入与输出尺寸、类型一致调用前无需手动创建输出矩阵平坦图全部像素同灰度会被特殊处理函数直接输出原灰度不产生无意义的增强均衡化是全局操作它依据整幅图的累积直方图映射不感知局部细节。对含噪图像或存在大面积过曝/欠曝区域的场景全局均衡化可能放大噪声或造成局部过增强此时可考虑 CLAHE对比度受限自适应直方图均衡化等局部方法作为替代若确实要增强彩色图应基于其亮度通道如YCrCb的Y或HSV的V进行均衡化避免直接对 BGR 三通道分别处理导致色偏。延伸阅读直方图均衡化是直方图系列教程之一本教程的下一篇是直方图计算教程其中演示了如何用 OpenCV 绘制上文所示的各类直方图直方图系列还包括直方图对比、反向投影与模板匹配等教程本文相关源码与样例汇总教程文档histogram_equalization.markdownC 示例EqualizeHist_Demo.cppPython 示例EqualizeHist_Demo.py核心实现histogram.cpp、equalize_hist.dispatch.cpp、equalize_hist.simd.hppAPI 声明imgproc.hpp单元测试test_histograms.cpp【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表