直方图均衡化的 C++ 实现(基于 openCV)
这是数字图像处理课的大作业,完成于 2013/06/17,需要调用 openCV 库,完整源码和报告如下:
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string> /* 灰度级结点 */
typedef struct {
int pixels; // 灰度级对应像素个数
float rate; // 像素比例
float accuRate; // 累计像素比例
int map; // 到均衡化后的灰度级的映射
} levNode; void histeqGray(IplImage* pGray, int levels, int argc);
IplImage* histImage(IplImage* pSrc, int histWidth, int histHeight, int nScale); int main(int argc, char* argv[])
{
int levels;
std::string imgName, inTmp;
if (argc == ) {
levels = atoi(argv[]);
imgName = argv[];
}
else if (argc == )
imgName = argv[];
else {
printf("usage: histeq [levels] image_name \n");
return -;
} IplImage* pSrc = cvLoadImage(imgName.c_str(), CV_LOAD_IMAGE_UNCHANGED);
int channel = pSrc->nChannels; IplImage* pChnl[] = { NULL }; for (int i = ; i < channel; ++i)
pChnl[i] = cvCreateImage(cvGetSize(pSrc), pSrc->depth, ); cvSplit(pSrc, pChnl[], pChnl[], pChnl[], pChnl[]); for (int i = ; i < channel; ++i)
histeqGray(pChnl[i], levels, argc); IplImage* pEql = cvCreateImage(cvGetSize(pSrc), pChnl[]->depth, pSrc->nChannels); cvMerge(pChnl[], pChnl[], pChnl[], pChnl[], pEql); inTmp = imgName + "_Eql.jpg";
cvSaveImage(inTmp.c_str(), pEql); //cvNamedWindow(imgName.c_str(), CV_WINDOW_AUTOSIZE);
cvShowImage(imgName.c_str(), pSrc);
//cvNamedWindow(inTmp.c_str(), CV_WINDOW_AUTOSIZE);
cvShowImage(inTmp.c_str(), pEql); IplImage* pSrcGray = cvCreateImage(cvGetSize(pSrc), IPL_DEPTH_8U, );
if (pSrc->nChannels == )
cvCvtColor(pSrc, pSrcGray, CV_BGR2GRAY);
else
cvCopyImage(pSrc, pSrcGray);
IplImage* pEqlGray = cvCreateImage(cvGetSize(pEql), IPL_DEPTH_8U, );
if (pSrc->nChannels == )
cvCvtColor(pEql, pEqlGray, CV_BGR2GRAY);
else
cvCopyImage(pEql, pEqlGray);
imgName += "_Hist.jpg";
inTmp += "_Hist.jpg";
int nScale = ;
int histWidth = /*pSrc->width * nScale*/ * nScale;
int histHeight = /*pSrc->height*/;
IplImage* pSrcGrayHist = histImage(pSrcGray, histWidth, histHeight, nScale);
IplImage* pEqlGrayHist = histImage(pEqlGray, histWidth, histHeight, nScale);
cvSaveImage(imgName.c_str(), pSrcGrayHist);
cvSaveImage(inTmp.c_str(), pEqlGrayHist);
cvShowImage(imgName.c_str(), pSrcGrayHist);
cvShowImage(inTmp.c_str(), pEqlGrayHist); cvWaitKey(); cvReleaseImage(&pEql);
cvReleaseImage(&pEqlGray);
for (int i = ; i < channel; ++i)
cvReleaseImage(&pChnl[i]);
cvReleaseImage(&pSrc);
cvReleaseImage(&pSrcGray); return ;
} /*
* 直方图均衡化函数
* pGray为输入的灰度图
* levels为均衡化的灰度级
*/
void histeqGray(IplImage* pGray, int levels, int argc)
{
int depth = pGray->depth;
printf("%d \n", depth);
int width = pGray->width;
int height = pGray->height;
int sumPixels = width * height; // 总像素数
printf("%d \n", sumPixels);
int values = static_cast<int>(pow((float), depth)); // 根据图像深度计算像素取值范围
if (argc == ) levels = values;
printf("%d \n", levels); int outDepth;
/*if (levels <= 2)
outDepth = 1;
else*/ if (levels <= )
outDepth = ;
else if (levels <= )
outDepth = ; assert(levels <= values);
int intervals = values / levels; // 根据像素取值范围和灰度级求每个灰度级的像素间隔
levNode* levNodes = (levNode*)calloc(levels, sizeof(levNode)); // 生成灰度结点
//for (int lev = 0; lev < levels; ++lev) printf("%d \n", levNodes[lev].pixels);
//char* pValues = pGray->imageData; /* 统计每个灰度级的像素个数 */
for (int y = ; y < height; ++y)
for (int x = ; x < width; ++x) {
CvScalar scal = cvGet2D(pGray, y, x);
int val = (int)scal.val[];
//printf("%d \n", val);
for (int lev = ; lev < levels; ++lev) {
if ( val >= intervals*lev && val < intervals*(lev+)) {
++levNodes[lev].pixels; break;
}
}
} int sum = ;
for (int lev = ; lev < levels; ++lev)
sum += levNodes[lev].pixels;
printf("%d \n", sum); /* 计算每个灰度级像素比例和累计比例 */
levNodes[].accuRate = levNodes[].rate = levNodes[].pixels / (float)sumPixels;
levNodes[].map = (int)(levNodes[].accuRate * (levels - ) + 0.5);
printf("%d \n", levNodes[].pixels);
for (int lev = ; lev < levels; ++lev) {
levNodes[lev].rate = levNodes[lev].pixels / (float)sumPixels;
levNodes[lev].accuRate = levNodes[lev-].accuRate + levNodes[lev].rate;
levNodes[lev].map = (int)(levNodes[lev].accuRate * (levels - ) + 0.5);
}
printf("%f \n", levNodes[levels-].accuRate); /* 生成均衡化后的图像 */
for (int y = ; y < height; ++y)
for (int x = ; x < width; ++x) {
CvScalar scal = cvGet2D(pGray, y, x);
int val = (int)scal.val[];
//printf("%d \n", val);
for (int lev = ; lev < levels; ++lev) {
if (val >= intervals*lev && val < intervals*(lev+)) {
scal.val[] = levNodes[lev].map;
//printf("%f \n", scal.val[0]);
cvSet2D(pGray, y, x, scal);
break;
}
}
}
pGray->depth = outDepth; free(levNodes);
} /*
* 绘制直方图函数
*/
IplImage* histImage(IplImage* pSrc, int histWidth, int histHeight, int nScale)
{
int histSize = static_cast<int>(pow((float), pSrc->depth));
CvHistogram* pHist = cvCreateHist(/*pSrc->nChannels*/, &histSize, CV_HIST_ARRAY);
cvCalcHist(&pSrc, pHist); IplImage* pHistImg = cvCreateImage(cvSize(histWidth, histHeight), IPL_DEPTH_8U, );
cvRectangle(pHistImg, cvPoint(,), cvPoint(pHistImg->width,pHistImg->height), CV_RGB(,,), CV_FILLED); float histMaxVal = ;
cvGetMinMaxHistValue(pHist, , &histMaxVal); for(int i = ; i < histSize; i++)
{
float histValue= cvQueryHistValue_1D(pHist, i); // 像素为i的直方块大小
int nRealHeight = cvRound((histValue / histMaxVal) * histHeight); // 要绘制的高度
cvRectangle(pHistImg,
cvPoint(i*nScale, histHeight - ),
cvPoint((i + )*nScale - , histHeight - nRealHeight),
cvScalar(i),
CV_FILLED
);
}
//cvFillConvexPoly cvReleaseHist(&pHist);
return pHistImg;
}
一、直方图均衡化概述
直方图均衡化是一种图像增强方法,其基本思想是把给定图像的直方图分布改造成均匀分布的直方图,从而增加象素灰度值的动态范围,达到增强图像整体对比度的效果。由信息学的理论来解释,具有最大熵(信息量)的图像为均衡化图像。
直方图均衡化可表示为:
,t为某个象素变换后的灰度级,s为该象素变换前的灰度级。
该灰度变换函数应满足如下两个条件:
1)f(s)在
范围内是单值递增函数;
2)对
有
条件1:保证原图各灰度级在变换后仍保持从黑到白(或从白到黑)的排列顺序;
条件2:保证变换前后灰度值动态范围的一致性。
可以证明累积分布函数(cumulative distribution function CDF)满足上述两个条件并能将s的分布转换为t的均匀分布。
事实上,s的CDF就是原始图的累积直方图,即:

其中
、
、k=0,1,…,L-1
根据这个公式,可以直接算出直方图均衡化后各象素的灰度值。
需要取整,以满足数字图象的要求。
二、算法步骤
|
步骤 |
运算 |
|
1 |
列出原始图灰度级 |
|
2 |
统计原始直方图各灰度级象素数 |
|
3 |
计算原始直方图(像素比例) |
|
4 |
计算累积直方图 |
|
5 |
取整 |
|
6 |
确定映射对应关系( |
|
7 |
计算新的直方图 |
三、算法测试
1、灰度图




2、彩色图




四、结果分析
(1)对于灰度图和彩色图,算法结果都不错,直方图显示像素分布很广、很平均。
(2)直方图均衡化的优点:自动增强整个图像的对比度。
(3)直方图均衡化的不足:具体增强效果不易控制,处理的结果总是得到全局均衡化的直方图。
直方图均衡化的 C++ 实现(基于 openCV)的更多相关文章
- 图像增强 | CLAHE 限制对比度自适应直方图均衡化
1 基本概述 CLAHE是一个比较有意思的图像增强的方法,主要用在医学图像上面.之前的比赛中,用到了这个,但是对其算法原理不甚了解.在这里做一个复盘. CLAHE起到的作用简单来说就是增强图像的对比度 ...
- 【图像增强】CLAHE 限制对比度自适应直方图均衡化
文章目录: 目录 1 基本概述 2 竞赛中的CLAHE实现 3 openCV绘制直方图 4 对比度Contrast 5 Contrast Stretching 6 Histogram Equaliza ...
- OpenCV图像增强算法实现(直方图均衡化、拉普拉斯、Log、Gamma)
http://blog.csdn.net/dcrmg/article/details/53677739 1. 基于直方图均衡化的图像增强 直方图均衡化是通过调整图像的灰阶分布,使得在0~255灰阶 ...
- opencv 彩色图像亮度、对比度调节 直方图均衡化
直接上代码: #include <Windows.h> #include <iostream>// for stand I/O #include <string> ...
- OpenCV——直方图均衡化(用于图像增强)
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespac ...
- 【图像处理】基于OpenCV底层实现的直方图匹配
image processing 系列: [图像处理]图片旋转 [图像处理]高斯滤波.中值滤波.均值滤波 直方图匹配算法.又称直方图规定化.简单说.就是依据某函数.或者另外一张图片的引导,使得原图改变 ...
- opencv图像直方图均衡化及其原理
直方图均衡化是什么有什么用 先说什么是直方图均衡化,通俗的说,以灰度图为例,原图的某一个像素为x,经过某个函数变为y.形成新的图.新的图的灰度值的分布是均匀的,这个过程就叫直方图均衡化. 图像直方图均 ...
- opencv 5 图像转换(3 重映射 仿射变换 直方图均衡化)
重映射 实现重映射(remap函数) 基础示例程序:基本重映射 //---------------------------------[头文件.命名空间包含部分]------------------- ...
- opencv —— equalizeHist 直方图均衡化实现对比度增强
直方图均匀化简介 从这张未经处理的灰度图可以看出,其灰度集中在非常小的一个范围内.这就导致了图片的强弱对比不强烈. 直方图均衡化的目的,就是把原始的直方图变换为在整个灰度范围(0~255)内均匀分布的 ...
随机推荐
- MySql 安装常见问题汇总
说明: 以下是针对 Mac 10.11 系统 以前,安装 MySql 数据库后, 设置的密码过于复杂,想更改为简单的密码, 方便数据库的使用. 1. 关闭和启动 MySql 数据库的方法: Syste ...
- 转载:阮一峰 RESTful API设计指南
阮一峰先生的文章对我理解启发很大,转载到我的博客 http://www.ruanyifeng.com/blog/2014/05/restful_api.html 网络应用程序,分为前端和后端两个部分. ...
- MongoDB学习笔记—概念解析
Mongo基本概念 下表将帮助您更容易理解Mongo中的一些概念: SQL术语/概念 MongoDB术语/概念 解释/说明 database database 数据库 table collection ...
- 20170523 BSEG替代付款条件-ZTERM 天数-ZBD1T
增强方式:替代,[替代基本用在FICO模块]BTE增强方式应用更广,需要学习总结. 程序:ZRGGBS* 步骤 1,SE16N:GB01 将 ZBD1T排除标记置为空,[注意,此更改跨client,d ...
- django内容总结
一.django请求的生命周期 1.django请求生命周期如图所示 2.django本身没有socket,客户端请求先到达wsgi然后再提交给django,而wsgi的本质就是个socket程序 注 ...
- Linux:文件系统
Linux:文件系统 分区与文件系统 对分区进行格式化是为了在分区上建立文件系统.一个分区通常只能格式化为一个文件系统,但是磁盘阵列等技术可以将一个分区格式化为多个文件系统. 组成 最主要的组成部分如 ...
- 【数学建模】MATLAB学习笔记——函数式文件
MATLAB学习笔记——函数式文件 引入函数式文件 说明: 函数式文件主要用于解决计算中的参数传递和函数调用的问题. 函数式的标志是它的第一行为function语句. 函数式文件可以有返回值,也可以没 ...
- Pacemaker详解
一.前言 云计算与集群系统密不可分,作为分布式计算和集群计算的集大成者,云计算的基础设施必须通过集群进行管理控制,而作为拥有大量资源与节点的集群,必须具备一个强大的集群资源管理器(Cluster sy ...
- paramiko 模块安装和使用
一.Centos安装Paramiko 1.安装组件 yum install openssl openssl-devel python-dev pycrypto -y yum install zlib- ...
- MySql安装成功后命令行进行必要的配置
1.1 首次用命令行登录 用zip方式安装成功mysql,并通过net start mysql 命令正常启动mysql服务后,打开dos命令行窗口,输入“mysql -uroot -p”或“mysql ...


(累计像素比例)
→
)