1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 07:13:02 +04:00

Merge pull request #28014 from harunresit:fix-28002-clahe

Added BitShift option to CLAHE #28014

Briefly, this PR is the fix of https://github.com/opencv/opencv/issues/28002

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
harunresit
2025-11-16 12:45:50 +01:00
committed by GitHub
parent 45f37fe9c8
commit f26d7e1bf4
2 changed files with 69 additions and 32 deletions
@@ -1067,6 +1067,18 @@ public:
//!@brief Returns Size defines the number of tiles in row and column.
CV_WRAP virtual Size getTilesGridSize() const = 0;
/** @brief Sets bit shift parameter for histogram bins.
@param bitShift bit shift value (default is 0).
*/
CV_WRAP virtual void setBitShift(int bitShift) = 0;
/** @brief Returns the bit shift parameter for histogram bins.
@return current bit shift value.
*/
CV_WRAP virtual int getBitShift() const = 0;
CV_WRAP virtual void collectGarbage() = 0;
};
+57 -32
View File
@@ -118,12 +118,12 @@ namespace clahe
namespace
{
template <class T, int histSize, int shift>
template <class T>
class CLAHE_CalcLut_Body : public cv::ParallelLoopBody
{
public:
CLAHE_CalcLut_Body(const cv::Mat& src, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& clipLimit, const float& lutScale) :
src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale)
CLAHE_CalcLut_Body(const cv::Mat& src, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& clipLimit, const float& lutScale, const int& histSize, const int& shift) :
src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale), histSize_(histSize), shift_(shift)
{
}
@@ -137,10 +137,12 @@ namespace
int tilesX_;
int clipLimit_;
float lutScale_;
int histSize_;
int shift_;
};
template <class T, int histSize, int shift>
void CLAHE_CalcLut_Body<T,histSize,shift>::operator ()(const cv::Range& range) const
template <class T>
void CLAHE_CalcLut_Body<T>::operator ()(const cv::Range& range) const
{
T* tileLut = lut_.ptr<T>(range.start);
const size_t lut_step = lut_.step / sizeof(T);
@@ -162,9 +164,9 @@ namespace
// calc histogram
cv::AutoBuffer<int> _tileHist(histSize);
cv::AutoBuffer<int> _tileHist(histSize_);
int* tileHist = _tileHist.data();
std::fill(tileHist, tileHist + histSize, 0);
std::fill(tileHist, tileHist + histSize_, 0);
int height = tileROI.height;
const size_t sstep = src_.step / sizeof(T);
@@ -174,13 +176,13 @@ namespace
for (; x <= tileROI.width - 4; x += 4)
{
int t0 = ptr[x], t1 = ptr[x+1];
tileHist[t0 >> shift]++; tileHist[t1 >> shift]++;
tileHist[t0 >> shift_]++; tileHist[t1 >> shift_]++;
t0 = ptr[x+2]; t1 = ptr[x+3];
tileHist[t0 >> shift]++; tileHist[t1 >> shift]++;
tileHist[t0 >> shift_]++; tileHist[t1 >> shift_]++;
}
for (; x < tileROI.width; ++x)
tileHist[ptr[x] >> shift]++;
tileHist[ptr[x] >> shift_]++;
}
// clip histogram
@@ -189,7 +191,7 @@ namespace
{
// how many pixels were clipped
int clipped = 0;
for (int i = 0; i < histSize; ++i)
for (int i = 0; i < histSize_; ++i)
{
if (tileHist[i] > clipLimit_)
{
@@ -199,16 +201,16 @@ namespace
}
// redistribute clipped pixels
int redistBatch = clipped / histSize;
int residual = clipped - redistBatch * histSize;
int redistBatch = clipped / histSize_;
int residual = clipped - redistBatch * histSize_;
for (int i = 0; i < histSize; ++i)
for (int i = 0; i < histSize_; ++i)
tileHist[i] += redistBatch;
if (residual != 0)
{
int residualStep = MAX(histSize / residual, 1);
for (int i = 0; i < histSize && residual > 0; i += residualStep, residual--)
int residualStep = MAX(histSize_ / residual, 1);
for (int i = 0; i < histSize_ && residual > 0; i += residualStep, residual--)
tileHist[i]++;
}
}
@@ -216,7 +218,7 @@ namespace
// calc Lut
int sum = 0;
for (int i = 0; i < histSize; ++i)
for (int i = 0; i < histSize_; ++i)
{
sum += tileHist[i];
tileLut[i] = cv::saturate_cast<T>(sum * lutScale_);
@@ -224,12 +226,12 @@ namespace
}
}
template <class T, int shift>
template <class T>
class CLAHE_Interpolation_Body : public cv::ParallelLoopBody
{
public:
CLAHE_Interpolation_Body(const cv::Mat& src, const cv::Mat& dst, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& tilesY) :
src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY)
CLAHE_Interpolation_Body(const cv::Mat& src, const cv::Mat& dst, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& tilesY, const int& shift) :
src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY), shift_(shift)
{
buf.allocate(src.cols << 2);
ind1_p = buf.data();
@@ -268,14 +270,15 @@ namespace
cv::Size tileSize_;
int tilesX_;
int tilesY_;
int shift_;
cv::AutoBuffer<int> buf;
int * ind1_p, * ind2_p;
float * xa_p, * xa1_p;
};
template <class T, int shift>
void CLAHE_Interpolation_Body<T, shift>::operator ()(const cv::Range& range) const
template <class T>
void CLAHE_Interpolation_Body<T>::operator ()(const cv::Range& range) const
{
float inv_th = 1.0f / tileSize_.height;
@@ -299,7 +302,7 @@ namespace
for (int x = 0; x < src_.cols; ++x)
{
int srcVal = srcRow[x] >> shift;
int srcVal = srcRow[x] >> shift_;
int ind1 = ind1_p[x] + srcVal;
int ind2 = ind2_p[x] + srcVal;
@@ -307,7 +310,7 @@ namespace
float res = (lutPlane1[ind1] * xa1_p[x] + lutPlane1[ind2] * xa_p[x]) * ya1 +
(lutPlane2[ind1] * xa1_p[x] + lutPlane2[ind2] * xa_p[x]) * ya;
dstRow[x] = cv::saturate_cast<T>(res) << shift;
dstRow[x] = cv::saturate_cast<T>(res) << shift_;
}
}
}
@@ -315,7 +318,7 @@ namespace
class CLAHE_Impl CV_FINAL : public cv::CLAHE
{
public:
CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8);
CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8, int bitShift = 0);
void apply(cv::InputArray src, cv::OutputArray dst) CV_OVERRIDE;
@@ -325,12 +328,16 @@ namespace
void setTilesGridSize(cv::Size tileGridSize) CV_OVERRIDE;
cv::Size getTilesGridSize() const CV_OVERRIDE;
void setBitShift(int bitShift) CV_OVERRIDE;
int getBitShift() const CV_OVERRIDE;
void collectGarbage() CV_OVERRIDE;
private:
double clipLimit_;
int tilesX_;
int tilesY_;
int bitShift_;
cv::Mat srcExt_;
cv::Mat lut_;
@@ -341,8 +348,8 @@ namespace
#endif
};
CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY) :
clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY)
CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY, int bitShift) :
clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY), bitShift_(bitShift)
{
}
@@ -356,7 +363,7 @@ namespace
bool useOpenCL = cv::ocl::isOpenCLActivated() && _src.isUMat() && _src.dims()<=2 && _src.type() == CV_8UC1;
#endif
int histSize = _src.type() == CV_8UC1 ? 256 : 65536;
int histSize = _src.type() == CV_8UC1 ? (256 >> bitShift_) : (65536 >> bitShift_);
cv::Size tileSize;
cv::_InputArray _srcForLut;
@@ -411,9 +418,13 @@ namespace
cv::Ptr<cv::ParallelLoopBody> calcLutBody;
if (_src.type() == CV_8UC1)
calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<uchar, 256, 0> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale);
{
calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<uchar> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale, histSize, bitShift_);
}
else if (_src.type() == CV_16UC1)
calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<ushort, 65536, 0> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale);
{
calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<ushort> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale, histSize, bitShift_);
}
else
CV_Error( cv::Error::StsBadArg, "Unsupported type" );
@@ -421,9 +432,13 @@ namespace
cv::Ptr<cv::ParallelLoopBody> interpolationBody;
if (_src.type() == CV_8UC1)
interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<uchar, 0> >(src, dst, lut_, tileSize, tilesX_, tilesY_);
{
interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<uchar> >(src, dst, lut_, tileSize, tilesX_, tilesY_, bitShift_);
}
else if (_src.type() == CV_16UC1)
interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<ushort, 0> >(src, dst, lut_, tileSize, tilesX_, tilesY_);
{
interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<ushort> >(src, dst, lut_, tileSize, tilesX_, tilesY_, bitShift_);
}
cv::parallel_for_(cv::Range(0, src.rows), *interpolationBody);
}
@@ -449,6 +464,16 @@ namespace
return cv::Size(tilesX_, tilesY_);
}
void CLAHE_Impl::setBitShift(int bitShift)
{
bitShift_ = bitShift;
}
int CLAHE_Impl::getBitShift() const
{
return bitShift_;
}
void CLAHE_Impl::collectGarbage()
{
srcExt_.release();