CLAHE的实现和研究
CLAHE的实现和研究
{
//读入灰度的手部图像
Mat src = imread("arm.jpg",0);
Mat dst = src.clone();
Mat HT_OpenCV;
Mat HT_GO;
Mat AHE_GO;
Mat CLHE_GO;
Mat CLAHE_Without_Interpolation;
Mat CLAHE_OpenCV;
Mat CLAHE_GO;
Mat matInter;
OpenCV HT 方法
cv::equalizeHist(src,HT_OpenCV);
GO HT方法
HT_GO = eaualizeHist_GO(src);
GO AHE方法
AHE_GO = aheGO(src);
GO CLHE方法
CLHE_GO = clheGO(src);
clahe不计算差值
CLAHE_Without_Interpolation = claheGoWithoutInterpolation(src);
OpenCV CLAHE 方法
Ptr<cv::CLAHE> clahe = createCLAHE();//默认参数
clahe->apply(src, CLAHE_OpenCV);
GO CLAHE方法
CLAHE_GO = claheGO(src);
结果显示
imshow("原始图像",src);
imshow("OpencvHT",HT_OpenCV);
imshow("GOHT",HT_GO);
imshow("GOAHE",AHE_GO);
imshow("GOCLHE",CLHE_GO);
imshow("GOCLAHE",CLAHE_GO);
imshow("CLAHE_Without_Interpolation",CLAHE_Without_Interpolation);
imshow("OpencvCLAHE",CLAHE_OpenCV);
waitKey();
return 0;
}
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
int width = src.cols;
int height= src.rows;
Mat HT_GO = src.clone();
int tmp[256] ={0};
float C[256] = {0.0};
int total = width*height;
for (int i=0 ;i<src.rows;i++)
{
for (int j=0;j<src.cols;j++)
{
int index = src.at<uchar>(i,j);
tmp[index] ++;
}
}
//计算累积函数
for(int i = 0;i < 256 ; i++){
if(i == 0)
C[i] = 1.0f * tmp[i] / total;
else
C[i] = C[i-1] + 1.0f * tmp[i] / total;
}
//这里的累积函数分配的方法非常直观高效
for(int i = 0;i < src.rows;i++){
for(int j = 0;j < src.cols;j++){
int index = src.at<uchar>(i,j);
HT_GO.at<uchar>(i,j) = C[index] * 255 ;
}
}
return HT_GO;
}
![](/assets/blank.gif)
![](/assets/blank.gif)
{
Mat AHE_GO = src.clone();
int block = _step;
int width = src.cols;
int height = src.rows;
int width_block = width/block; //每个小格子的长和宽
int height_block = height/block;
//存储各个直方图
int tmp2[8*8][256] ={0};
float C2[8*8][256] = {0.0};
//分块
int total = width_block * height_block;
for (int i=0;i<block;i++)
{
for (int j=0;j<block;j++)
{
int start_x = i*width_block;
int end_x = start_x + width_block;
int start_y = j*height_block;
int end_y = start_y + height_block;
int num = i+block*j;
//遍历小块,计算直方图
for(int ii = start_x ; ii < end_x ; ii++)
{
for(int jj = start_y ; jj < end_y ; jj++)
{
int index =src.at<uchar>(jj,ii);
tmp2[num][index]++;
}
}
//计算累积分布直方图
for(int k = 0 ; k < 256 ; k++)
{
if( k == 0)
C2[num][k] = 1.0f * tmp2[num][k] / total;
else
C2[num][k] = C2[num][k-1] + 1.0f * tmp2[num][k] / total;
}
}
}
//将统计结果写入
for (int i=0;i<block;i++)
{
for (int j=0;j<block;j++)
{
int start_x = i*width_block;
int end_x = start_x + width_block;
int start_y = j*height_block;
int end_y = start_y + height_block;
int num = i+block*j;
//遍历小块,计算直方图
for(int ii = start_x ; ii < end_x ; ii++)
{
for(int jj = start_y ; jj < end_y ; jj++)
{
int index =src.at<uchar>(jj,ii);
//结果直接写入AHE_GO中去
AHE_GO.at<uchar>(jj,ii) = C2[num][index] * 255 ;
}
}
}
}
return AHE_GO;
}
![](/assets/blank.gif)
Mat clheGO(Mat src,int _step = 8)
{
int width = src.cols;
int height= src.rows;
Mat CLHE_GO = src.clone();
int tmp[256] ={0};
float C[256] = {0.0};
int total = width*height;
for (int i=0 ;i<src.rows;i++)
{
for (int j=0;j<src.cols;j++)
{
int index = src.at<uchar>(i,j);
tmp[index] ++;
}
}
/限制对比度计算部分,注意这个地方average的计算不一定科学
int average = width * height / 255/64;
int LIMIT = 4 * average;
int steal = 0;
for(int k = 0 ; k < 256 ; k++)
{
if(tmp[k] > LIMIT){
steal += tmp[k] - LIMIT;
tmp[k] = LIMIT;
}
}
int bonus = steal/256;
//hand out the steals averagely
for(int k = 0 ; k < 256 ; k++)
{
tmp[k] += bonus;
}
///
//计算累积函数
for(int i = 0;i < 256 ; i++){
if(i == 0)
C[i] = 1.0f * tmp[i] / total;
else
C[i] = C[i-1] + 1.0f * tmp[i] / total;
}
//这里的累积函数分配的方法非常直观高效
for(int i = 0;i < src.rows;i++){
for(int j = 0;j < src.cols;j++){
int index = src.at<uchar>(i,j);
CLHE_GO.at<uchar>(i,j) = C[index] * 255 ;
}
}
return CLHE_GO;
}
![](/assets/blank.gif)
{
Mat CLAHE_GO = src.clone();
int block = _step;//pblock
int width = src.cols;
int height= src.rows;
int width_block = width/block; //每个小格子的长和宽
int height_block = height/block;
//存储各个直方图
int tmp2[8*8][256] ={0};
float C2[8*8][256] = {0.0};
//分块
int total = width_block * height_block;
for (int i=0;i<block;i++)
{
for (int j=0;j<block;j++)
{
int start_x = i*width_block;
int end_x = start_x + width_block;
int start_y = j*height_block;
int end_y = start_y + height_block;
int num = i+block*j;
//遍历小块,计算直方图
for(int ii = start_x ; ii < end_x ; ii++)
{
for(int jj = start_y ; jj < end_y ; jj++)
{
int index =src.at<uchar>(jj,ii);
tmp2[num][index]++;
}
}
//裁剪和增加操作,也就是clahe中的cl部分
//这里的参数 对应《Gem》上面 fCliplimit = 4 , uiNrBins = 255
int average = width_block * height_block / 255;
int LIMIT = 4 * average;
int steal = 0;
for(int k = 0 ; k < 256 ; k++)
{
if(tmp2[num][k] > LIMIT){
steal += tmp2[num][k] - LIMIT;
tmp2[num][k] = LIMIT;
}
}
int bonus = steal/256;
//hand out the steals averagely
for(int k = 0 ; k < 256 ; k++)
{
tmp2[num][k] += bonus;
}
//计算累积分布直方图
for(int k = 0 ; k < 256 ; k++)
{
if( k == 0)
C2[num][k] = 1.0f * tmp2[num][k] / total;
else
C2[num][k] = C2[num][k-1] + 1.0f * tmp2[num][k] / total;
}
}
}
//计算变换后的像素值
//将统计结果写入
for (int i=0;i<block;i++)
{
for (int j=0;j<block;j++)
{
int start_x = i*width_block;
int end_x = start_x + width_block;
int start_y = j*height_block;
int end_y = start_y + height_block;
int num = i+block*j;
//遍历小块,计算直方图
for(int ii = start_x ; ii < end_x ; ii++)
{
for(int jj = start_y ; jj < end_y ; jj++)
{
int index =src.at<uchar>(jj,ii);
//结果直接写入AHE_GO中去
CLAHE_GO.at<uchar>(jj,ii) = C2[num][index] * 255 ;
}
}
}
}
return CLAHE_GO;
}
![](/assets/blank.gif)
{
Mat CLAHE_GO = src.clone();
int block = _step;//pblock
int width = src.cols;
int height= src.rows;
int width_block = width/block; //每个小格子的长和宽
int height_block = height/block;
//存储各个直方图
int tmp2[8*8][256] ={0};
float C2[8*8][256] = {0.0};
//分块
int total = width_block * height_block;
for (int i=0;i<block;i++)
{
for (int j=0;j<block;j++)
{
int start_x = i*width_block;
int end_x = start_x + width_block;
int start_y = j*height_block;
int end_y = start_y + height_block;
int num = i+block*j;
//遍历小块,计算直方图
for(int ii = start_x ; ii < end_x ; ii++)
{
for(int jj = start_y ; jj < end_y ; jj++)
{
int index =src.at<uchar>(jj,ii);
tmp2[num][index]++;
}
}
//裁剪和增加操作,也就是clahe中的cl部分
//这里的参数 对应《Gem》上面 fCliplimit = 4 , uiNrBins = 255
int average = width_block * height_block / 255;
//关于参数如何选择,需要进行讨论。不同的结果进行讨论
//关于全局的时候,这里的这个cl如何算,需要进行讨论
int LIMIT = 40 * average;
int steal = 0;
for(int k = 0 ; k < 256 ; k++)
{
if(tmp2[num][k] > LIMIT){
steal += tmp2[num][k] - LIMIT;
tmp2[num][k] = LIMIT;
}
}
int bonus = steal/256;
//hand out the steals averagely
for(int k = 0 ; k < 256 ; k++)
{
tmp2[num][k] += bonus;
}
//计算累积分布直方图
for(int k = 0 ; k < 256 ; k++)
{
if( k == 0)
C2[num][k] = 1.0f * tmp2[num][k] / total;
else
C2[num][k] = C2[num][k-1] + 1.0f * tmp2[num][k] / total;
}
}
}
//计算变换后的像素值
//根据像素点的位置,选择不同的计算方法
for(int i = 0 ; i < width; i++)
{
for(int j = 0 ; j < height; j++)
{
//four coners
if(i <= width_block/2 && j <= height_block/2)
{
int num = 0;
CLAHE_GO.at<uchar>(j,i) = (int)(C2[num][CLAHE_GO.at<uchar>(j,i)] * 255);
}else if(i <= width_block/2 && j >= ((block-1)*height_block + height_block/2)){
int num = block*(block-1);
CLAHE_GO.at<uchar>(j,i) = (int)(C2[num][CLAHE_GO.at<uchar>(j,i)] * 255);
}else if(i >= ((block-1)*width_block+width_block/2) && j <= height_block/2){
int num = block-1;
CLAHE_GO.at<uchar>(j,i) = (int)(C2[num][CLAHE_GO.at<uchar>(j,i)] * 255);
}else if(i >= ((block-1)*width_block+width_block/2) && j >= ((block-1)*height_block + height_block/2)){
int num = block*block-1;
CLAHE_GO.at<uchar>(j,i) = (int)(C2[num][CLAHE_GO.at<uchar>(j,i)] * 255);
}
//four edges except coners
else if( i <= width_block/2 )
{
//线性插值
int num_i = 0;
int num_j = (j - height_block/2)/height_block;
int num1 = num_j*block + num_i;
int num2 = num1 + block;
float p = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
float q = 1-p;
CLAHE_GO.at<uchar>(j,i) = (int)((q*C2[num1][CLAHE_GO.at<uchar>(j,i)]+ p*C2[num2][CLAHE_GO.at<uchar>(j,i)])* 255);
}else if( i >= ((block-1)*width_block+width_block/2)){
//线性插值
int num_i = block-1;
int num_j = (j - height_block/2)/height_block;
int num1 = num_j*block + num_i;
int num2 = num1 + block;
float p = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
float q = 1-p;
CLAHE_GO.at<uchar>(j,i) = (int)((q*C2[num1][CLAHE_GO.at<uchar>(j,i)]+ p*C2[num2][CLAHE_GO.at<uchar>(j,i)])* 255);
}else if( j <= height_block/2 ){
//线性插值
int num_i = (i - width_block/2)/width_block;
int num_j = 0;
int num1 = num_j*block + num_i;
int num2 = num1 + 1;
float p = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
float q = 1-p;
CLAHE_GO.at<uchar>(j,i) = (int)((q*C2[num1][CLAHE_GO.at<uchar>(j,i)]+ p*C2[num2][CLAHE_GO.at<uchar>(j,i)])* 255);
}else if( j >= ((block-1)*height_block + height_block/2) ){
//线性插值
int num_i = (i - width_block/2)/width_block;
int num_j = block-1;
int num1 = num_j*block + num_i;
int num2 = num1 + 1;
float p = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
float q = 1-p;
CLAHE_GO.at<uchar>(j,i) = (int)((q*C2[num1][CLAHE_GO.at<uchar>(j,i)]+ p*C2[num2][CLAHE_GO.at<uchar>(j,i)])* 255);
}
//双线性插值
else{
int num_i = (i - width_block/2)/width_block;
int num_j = (j - height_block/2)/height_block;
int num1 = num_j*block + num_i;
int num2 = num1 + 1;
int num3 = num1 + block;
int num4 = num2 + block;
float u = (i - (num_i*width_block+width_block/2))/(1.0f*width_block);
float v = (j - (num_j*height_block+height_block/2))/(1.0f*height_block);
CLAHE_GO.at<uchar>(j,i) = (int)((u*v*C2[num4][CLAHE_GO.at<uchar>(j,i)] +
(1-v)*(1-u)*C2[num1][CLAHE_GO.at<uchar>(j,i)] +
u*(1-v)*C2[num2][CLAHE_GO.at<uchar>(j,i)] +
v*(1-u)*C2[num3][CLAHE_GO.at<uchar>(j,i)]) * 255);
}
//最后这步,类似高斯平滑
CLAHE_GO.at<uchar>(j,i) = CLAHE_GO.at<uchar>(j,i) + (CLAHE_GO.at<uchar>(j,i) << 8) + (CLAHE_GO.at<uchar>(j,i) << 16);
}
}
return CLAHE_GO;
}
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
int average = width_block * height_block / 255;
//关于参数如何选择,需要进行讨论。不同的结果进行讨论
//关于全局的时候,这里的这个cl如何算,需要进行讨论
int LIMIT = 40 * average;
int steal = 0;
![](/assets/blank.gif)
![](/assets/blank.gif)
CLAHE的实现和研究相关推荐
- 循序渐进之(五)空间域图像增强之自适应直方图均衡化(AHE)
循序渐进之(五)空间域图像增强之自适应直方图均衡化(AHE) 文字摘自:对比度受限的自适应直方图均衡化(CLAHE) 直方图均衡化(HE)是一种很常用的直方图类方法,基本思想是通过图像的灰度分布直方图 ...
- 结合实例与代码谈数字图像处理都研究什么?
图像处理(以及机器视觉)在学校里是一个很大的研究方向,很多研究生.博士生都在导师的带领下从事着这方面的研究.另外,就工作而言,也确实有很多这方面的岗位和机会虚位以待.而且这种情势也越来越凸显.那么图像 ...
- 基于直方图的图像增强算法(HE、CLAHE、Retinex)
直方图是图像色彩统计特征的抽象表述.基于直方图可以实现很多有趣的算法.例如,图像增强中利用直方图来调整图像的对比度.有人利用直方图来进行大规模无损数据隐藏.还有人利用梯度直方图HOG来构建图像特征进而 ...
- 综述:视频和图像去雾算法以及相关的图像恢复和增强研究
综述:视频和图像去雾算法以及相关的图像恢复和增强研究 翻译自IEEE的一篇文章<Review of Video and Image Defogging Algorithms and Relate ...
- matlab视网膜血管分割,视网膜血管增强与分割算法研究
摘要: 视网膜血管图像的研究在图像处理领域占据重要的地位,在临床医学领域更具有重大的实际意义.它不仅能及时地反映出高血压.糖尿病和血液病等全身性疾病,还可以很好地帮助医生掌握病人的情况,因此对眼底视网 ...
- 关于 CLAHE 的理解及实现
CLAHE CLAHE 是一种非常有效的直方图均衡算法, 目前网上已经有很多文章进行了说明, 这里说一下自己的理解. CLAHE是怎么来的 直方图均衡是一种简单快速的图像增强方法, 其原理和实现过程以 ...
- OpenCV-Python自适应直方图均衡类CLAHE及方法详解
一.引言 对比度受限的自适应直方图均衡在OpenCV中是通过类CLAHE来提供实现的,老猿没研究过C++中的应用,但OpenCV-Python中应用时与普通的Python类构建对象的机制有所不同,老猿 ...
- OpenCV自适应直方图均衡CLAHE图像和分块大小不能整除的处理
一.引言 最近一个月来都在研究OpenCV 中CLAHE算法的一些问题,如: 图像横向和纵向分块大小与图像的宽和高不能整除怎么处理? CLIP的剪裁是怎么实施的? 解决棋盘效应的具体插值处理过程怎样? ...
- 限制对比度自适应直方图均衡(Contrast Limited Adaptive histgram equalization/CLAHE)
转自:http://www.cnblogs.com/Imageshop/archive/2013/04/07/3006334.html 一.自适应直方图均衡化(Adaptive histgram eq ...
最新文章
- mysql创建表选择字段的时候下尽量小
- MySQL修改字段名、字段类型
- html鼠标点击切换图片,js鼠标点击图片切换效果代码分享
- 微信小程序想要最短服务路径
- regardless what you do
- Mysql错误:服务名无效。 请键入 NET HELPMSG 2185 以获得更多的帮助。
- DataTable to byte[]、DataTable to XML(string)
- Xshell分屏显示
- 玩转NumPy——NumPy数组的切片和索引
- Linux|麒麟操作系统实现多路RTMP|RTSP播放
- Spring的数据库编程浅入浅出——不吹牛逼不装逼
- 快手视频如何一键批量下载
- DSP入门:GPIO
- Solidworks常用插件介绍
- 北海市卫生学校计算机教室,北海市卫生学校:借力智慧校园,开启德育管理新篇章...
- python爬取固定酒店评论_爬取携程上酒店评论数据
- windows注册表恢复方法
- 4.蒙特卡洛(Monte-Carlo, MC)+时序差分(Temporal Difference, TD)
- C语言中的细节知识点(五)
- 常见的数据结构和数据库的设计方法
热门文章
- NUS CS5477 assignment1
- SQL Server学习笔记3——WHERE子句
- java-php-python-ssm面相高校学生的图书共享平台计算机毕业设计
- tfidf+java+权重,使用scikit-learn tfidf计算词语权重
- 2020车载凯立德懒人包下载_华为HarmonyOS App开发工具DevEco Studio下载安装及第一个HarmonyOS App实战教程...
- python创建学生字典_用python创建简单字典
- RxBus的简单使用(易懂)
- 关于xp64位系统须注意的四个问题
- 音频设备的接线图了解
- Servlet过滤器与SpringMVC拦截器