自适应直方图均衡(CLAHE)代码及详细注释【OpenCV】

更新时间:2023-07-20 19:14:54 阅读: 评论:0

⾃适应直⽅图均衡(CLAHE)代码及详细注释【OpenCV】效果图
标题ps画框
CLAHE简介
HE 直⽅图增强,⼤家都不陌⽣,是⼀种⽐较古⽼的对⽐度增强算法,它有两种变体:AHE 和 CLAHE;两者都是⾃适应的增强算法,功能差不多,但是前者有⼀个很⼤的缺陷,就是有时候会过度放⼤图像中相同区域的噪声,为了解决这⼀问题,出现了 HE 的另⼀种改进算法,就是 CLAHE;CLAHE 是另外⼀种直⽅图均衡算法,CLAHE 和 AHE 的区别在于前者对区域对⽐度实⾏了限制,并且利⽤插值来加快计算。它能有效的增强或改善图像(局部)对⽐度,从⽽获取更多图像相关边缘信息有利于分割。还能够有效改善 AHE 中放⼤噪声的问题。另外,CLAHE 的有⼀个⽤途是被⽤来对图像去雾。
详细理论请参考
OpenCV源码的本地路径: %OPENCV%\opencv\sources\modules\imgproc\src\clahe.cpp
clahe.cpp
// ----------------------------------------------------------------------
// CLAHE
namespace
{
class CLAHE_CalcLut_Body : public cv::ParallelLoopBody
{
public:
CLAHE_CalcLut_Body(const cv::Mat& src, cv::Mat& lut, cv::Size tileSize, int tilesX, int clipLimit, float lutScale) :
src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale)
{
}
void operator ()(const cv::Range& range) const;
private:
cv::Mat src_;
mutable cv::Mat lut_;
cv::Size tileSize_;
int tilesX_;
int clipLimit_;
float lutScale_;
};
// 计算直⽅图查找表
void CLAHE_CalcLut_Body::operator ()(const cv::Range& range) const
{
const int histSize = 256;
uchar* tileLut = lut_.ptr(range.start);
const size_t lut_step = lut_.step; // size = tilesX_*tilesY_ * lut_step
// Range(0, tilesX_ * tilesY_),全图被分为tilesX_*tiles_Y个块
for (int k = range.start; k < d; ++k, tileLut += lut_step)
{
// (tx, ty)表⽰当前所在是哪⼀块
// (0, 0) (1, 0)...(tilesX_-1, 0)
// (0, 1) (1, 1)...(tilesX_-1, 1)
// ...
// (0, tilesY_-1)... (tilesX_-1, tilesY_-1)
const int ty = k / tilesX_;
const int tx = k % tilesX_;
// retrieve tile submatrix
// 注意:tileSize.width表⽰分块的宽度,tileSize.height表⽰分块⾼度
cv::Rect tileROI;
tileROI.x = tx * tileSize_.width; // 换算为全局坐标
tileROI.y = ty * tileSize_.height;
tileROI.width = tileSize_.width;
tileROI.height = tileSize_.height;
const cv::Mat tile = src_(tileROI);
// calc histogram
int tileHist[histSize] = { 0, };
/
空间关闭
/ 统计 ROI 的直⽅图
int height = tileROI.height;
const size_t sstep = tile.step;
for (const uchar* ptr = tile.ptr<uchar>(0); height--; ptr += sstep)
{
int x = 0;
for (; x <= tileROI.width - 4; x += 4)
{
int t0 = ptr[x], t1 = ptr[x + 1];
tileHist[t0]++; tileHist[t1]++;
t0 = ptr[x + 2]; t1 = ptr[x + 3];
tileHist[t0]++; tileHist[t1]++;
桃花灼灼}
for (; x < tileROI.width; ++x)
tileHist[ptr[x]]++;
}
// clip histogram
if (clipLimit_ > 0)
{
// how many pixels were clipped
int clipped = 0;
for (int i = 0; i < histSize; ++i)
{
// 超过裁剪阈值
if (tileHist[i] > clipLimit_)演绎推理
{
clipped += tileHist[i] - clipLimit_;
tileHist[i] = clipLimit_;
}
}
// redistribute clipped pixels
int redistBatch = clipped / histSize;
int residual = clipped - redistBatch * histSize;
/
/ 平均分配裁剪的差值到所有直⽅图
for (int i = 0; i < histSize; ++i)
tileHist[i] += redistBatch;
// 处理差值的余数
for (int i = 0; i < residual; ++i)
tileHist[i]++;
}
// calc Lut
int sum = 0;
for (int i = 0; i < histSize; ++i)
{
/
/ 累加直⽅图
sum += tileHist[i];
tileLut[i] = cv::saturate_cast<uchar>(sum * lutScale_); // static_cast<float>(histSize - 1) / tileSizeTotal
}
}
}
class CLAHE_Interpolation_Body : public cv::ParallelLoopBody
{
public:
CLAHE_Interpolation_Body(const cv::Mat& src, cv::Mat& dst, const cv::Mat& lut, cv::Size tileSize, int tilesX, int tilesY) :
src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY)  {
}
void operator ()(const cv::Range& range) const;
private:
cv::Mat src_;
mutable cv::Mat dst_;
cv::Mat lut_;
cv::Size tileSize_;
int tilesX_;
int tilesY_;
};
// 根据相邻4块的直⽅图插值
void CLAHE_Interpolation_Body::operator ()(const cv::Range& range) const  {
const size_t lut_step = lut_.step;
// Range(0, ws)
for (int y = range.start; y < d; ++y)
{
const uchar* srcRow = src_.ptr<uchar>(y);
uchar* dstRow = dst_.ptr<uchar>(y);
const float tyf = (static_cast<float>(y) / tileSize_.height) - 0.5f;
怎样卸美甲int ty1 = cvFloor(tyf);
int ty2 = ty1 + 1;
// 差值作为插值的⽐例
const float ya = tyf - ty1;
ty1 = std::max(ty1, 0);
郁抑症ty2 = std::min(ty2, tilesY_ - 1);
const uchar* lutPlane1 = lut_.ptr(ty1 * tilesX_); // 当前块的直⽅图
const uchar* lutPlane2 = lut_.ptr(ty2 * tilesX_); // 向下⼀块的直⽅图
for (int x = 0; x < src_.cols; ++x)
{
const float txf = (static_cast<float>(x) / tileSize_.width) - 0.5f;
int tx1 = cvFloor(txf);
int tx2 = tx1 + 1;
// 差值作为插值的⽐例
const float xa = txf - tx1;
tx1 = std::max(tx1, 0);
tx2 = std::min(tx2, tilesX_ - 1);
// src_.ptr<uchar>(y)[x]
const int srcVal = srcRow[x];
// 索引 LUT
const size_t ind1 = tx1 * lut_step + srcVal;
const size_t ind2 = tx2 * lut_step + srcVal;  // 向右⼀块的直⽅图
float res = 0;
// 根据直⽅图的值进⾏插值
// lut_.ptr(ty1 * tilesX_)[tx1 * lut_step + srcVa] => lut_[ty1][tx1][srcVal]
res += lutPlane1[ind1] * ((1.0f - xa) * (1.0f - ya));
res += lutPlane1[ind2] * ((xa) * (1.0f - ya));
res += lutPlane2[ind1] * ((1.0f - xa) * (ya));
res += lutPlane2[ind2] * ((xa) * (ya));
dstRow[x] = cv::saturate_cast<uchar>(res);
}
}
}
class CLAHE_Impl : public cv::CLAHE
{
public:石斛怎么吃最好
CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8);
cv::AlgorithmInfo* info() const; // Algorithm类⼯⼚⽅法封装相关
线稿动漫人物图
void apply(cv::InputArray src, cv::OutputArray dst);
void tClipLimit(double clipLimit);
double getClipLimit() const;
void tTilesGridSize(cv::Size tileGridSize);
cv::Size getTilesGridSize() const;
void collectGarbage();
private:
double clipLimit_;
int tilesX_;
int tilesY_;
cv::Mat srcExt_;
cv::Mat lut_;
};
CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY) :  clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY)
{
}
// Algorithm类⼯⼚⽅法封装相关
//CV_INIT_ALGORITHM(CLAHE_Impl, "CLAHE",
// obj.info()->addParam(obj, "clipLimit", obj.clipLimit_);
//obj.info()->addParam(obj, "tilesX", obj.tilesX_);
//obj.info()->addParam(obj, "tilesY", obj.tilesY_))
void CLAHE_Impl::apply(cv::InputArray _src, cv::OutputArray _dst) {
cv::Mat src = _Mat();
CV_pe() == CV_8UC1);
_ate(src.size(), pe());
cv::Mat dst = _Mat();
const int histSize = 256;
// 准备 LUT,tilesX_*tilesY_个块,每个块都有256个柱⼦的直⽅图  lut_.create(tilesX_ * tilesY_, histSize, CV_8UC1);
cv::Size tileSize;
cv::Mat srcForLut;
// 如果分块刚好(整除)
if (ls % tilesX_ == 0 && ws % tilesY_ == 0)
{
tileSize = cv::ls / tilesX_, ws / tilesY_);
srcForLut = src;

本文发布于:2023-07-20 19:14:54,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1089495.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:图像   插值   差值   相关   增强   算法   注释   分块
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图