OpenCV之直方图拉伸
来源:互联网 发布:分形设计软件 编辑:程序博客网 时间:2024/05/18 06:23
- 本文代码使用OpenCV版本:2.4.13
- 本文代码在Win10+Visual Studio 2013 Update 3下测试通过
上一个博客《OpenCV之图像直方图计算》讲述了图像直方图的计算。计算出图像的直方图后,我们不仅可以据此观察图像的像素分布情况,还可以利用它对图像进行增强,例如直方图拉伸(Histogram Stretching)。
比如,我们有一个灰度图像的直方图如下所示:
可以看到,该直方图的左右两侧都有一部分区域,其高度近乎于0。这意味着,在该图像中,对应灰度级的像素出现的次数近乎于0。换句话说,图像所使用的灰度级集中在中部区域,从而使得图像具有较低的对比度。
如果我们忽略掉左右两侧高度近乎于0的区域,然后将直方图向左右两侧拉伸,则就可以使用到所有的灰度级,从而提高图像的对比度,这就是直方图拉伸的理念。拉伸后的直方图如下所示:
直方图拉伸的数学公式
假设决定忽略左右两侧高度小于等于minValue
的区域,那么首先应该从最左侧向右寻找第一个高度大于minValue
的灰度值,设为grayMin
;然后从最右侧向左寻找第一个高度大于minValue
的灰度值,设置为grayMax
,从而得到将要进行拉伸的区域[grayMin, grayMax]
。而要拉伸的目标是[0, 255]
。
所以对于原区域[grayMin, grayMax]
中的任意灰度g
,其拉伸后的结果应为:
另外,不要忘了被忽略的灰度级。对于左侧被忽略的灰度级,其对应的新的灰度应该为0;对于右侧被忽略的灰度级,其对应的新的灰度应该为255。
总结如下:
下列代码展示了此计算过程:
////////////////////////////////////////////// 灰度直方图拉伸示例#include <iostream>#include "opencv2/core/core.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"// 使用Rect绘制直方图void drawHist_Rect(const cv::Mat& hist, cv::Mat& canvas, const cv::Scalar& color){ CV_Assert(!hist.empty() && hist.cols == 1); CV_Assert(hist.depth() == CV_32F && hist.channels() == 1); CV_Assert(!canvas.empty() && canvas.cols >= hist.rows); const int width = canvas.cols; const int height = canvas.rows; // 获取最大值 double dMax = 0.0; cv::minMaxLoc(hist, nullptr, &dMax); // 计算直线的宽度 float thickness = float(width) / float(hist.rows); // 绘制直方图 for (int i = 1; i < hist.rows; ++i) { double h = hist.at<float>(i, 0) / dMax * 0.9 * height; // 最高显示为画布的90% cv::rectangle(canvas, cv::Point(static_cast<int>((i - 1) * thickness), height), cv::Point(static_cast<int>(i * thickness), static_cast<int>(height - h)), color, static_cast<int>(thickness)); }}// 直方图拉伸// grayImage - 要拉伸的单通道灰度图像// hist - grayImage的直方图// minValue - 忽略像数个数小于此值的灰度级void histStretch(cv::Mat& grayImage, const cv::Mat& hist, int minValue){ CV_Assert(!grayImage.empty() && grayImage.channels() == 1 && grayImage.depth() == CV_8U); CV_Assert(!hist.empty() && hist.rows == 256 && hist.cols == 1 && hist.depth() == CV_32F); CV_Assert(minValue >= 0); // 求左边界 uchar grayMin = 0; for (int i = 0; i < hist.rows; ++i) { if (hist.at<float>(i, 0) > minValue) { grayMin = static_cast<uchar>(i); break; } } // 求右边界 uchar grayMax = 0; for (int i = hist.rows - 1; i >= 0; --i) { if (hist.at<float>(i, 0) > minValue) { grayMax = static_cast<uchar>(i); break; } } if (grayMin >= grayMax) { return; } const int w = grayImage.cols; const int h = grayImage.rows; for (int y = 0; y < h; ++y) { uchar* imageData = grayImage.ptr<uchar>(y); for (int x = 0; x < w; ++x) { if (imageData[x] < grayMin) { imageData[x] = 0; } else if (imageData[x] > grayMax) { imageData[x] = 255; } else { imageData[x] = static_cast<uchar>(std::round((imageData[x] - grayMin) * 255.0 / (grayMax - grayMin))); } } }}int main(){ // 读入图像,此时是3通道的RGB图像 cv::Mat image = cv::imread("G:/dataset/lena512.bmp"); if (image.empty()) { return -1; } // 转换为单通道的灰度图 cv::Mat grayImage; cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); // 计算直方图并绘制 cv::Mat hist; cv::Mat histCanvas(400, 512, CV_8UC3, cv::Scalar(255, 255, 255)); int channels[1] = { 0 }; int histSize = 256; float range[2] = { 0, 256 }; const float* ranges[1] = { range }; cv::calcHist(&grayImage, 1, channels, cv::Mat(), hist, 1, &histSize, ranges); drawHist_Rect(hist, histCanvas, cv::Scalar(255, 0, 0)); // 显示原始灰度图像及其直方图 cv::imshow("Gray image", grayImage); cv::imshow("Gray image's histogram", histCanvas); // 直方图拉伸 cv::Mat grayImageStretched = grayImage.clone(); histStretch(grayImageStretched, hist, 20); // 计算直方图并绘制 cv::Mat histStretched; cv::Mat histCanvasStretched(400, 512, CV_8UC3, cv::Scalar(255, 255, 255)); cv::calcHist(&grayImageStretched, 1, channels, cv::Mat(), histStretched, 1, &histSize, ranges); drawHist_Rect(histStretched, histCanvasStretched, cv::Scalar(255, 0, 0)); // 显示拉伸后的灰度图像及其直方图 cv::imshow("Stretched image", grayImageStretched); cv::imshow("Stretched image's histogram", histCanvasStretched); cv::waitKey(); return 0;}
原始图像及拉伸后图像的直方图,上面已经展示过了。下面是原始图像及经过拉伸后的图像,可以看到,对比度有了明显的提升(头发更黑了):
彩色直方图拉伸
彩色图像也可以进行直方图拉伸,方法是分别对R通道、G通道和B通道进行直方图拉伸。此处不再赘述,仅展示一下原图及拉伸效果(minValue
设置为100;同样头发变得更黑了):
参考链接
- 《OpenCV 2 视觉编程手册》4.3节
- 图像处理(一)全等级直方图灰度拉伸
(转载请保留作者信息)
- OpenCV之直方图拉伸
- OpenCV之查找表与直方图拉伸
- opencv直方图拉伸
- OpenCV实现灰度直方图和直方图拉伸
- OpenCV图像增强:直方图拉伸和直方图均衡化
- 直方图拉伸
- opencv之直方图
- opencv 学习之 直方图
- OpenCV之直方图操作
- opencv之图像直方图
- 我的OpenCV学习笔记(13):计算直方图,利用查找表拉伸直方图,直方图均衡
- OpenCV 图像增强—直方图均衡化和灰度拉伸
- Opencv图像识别从零到精通(10)-----直方图均衡化与直方图拉伸
- opencv之直方图caluHist函数
- Opencv之直方图计算calcHist
- 【opencv】之直方图的应用
- opencv学习之直方图绘制
- opencv 学习之直方图统计
- js select实现项目的左移和右移
- 幼儿教育纪录片
- linux内存管理之vmalloc
- python常用 标准库介绍
- FreeScale-SD-5舵机中值
- OpenCV之直方图拉伸
- 证件识别结合人脸识别的人证合一技术
- Python:dataframe转html
- 【UVA1543】Telescope
- 实验11 编写子程序
- 最近遇到的curl问题和libcurl问题(Protocol https not supported or disabled in libcurl)
- Python中Pyc文件的作用
- HDU1232 Prim
- 【codevs 3287】货车运输