Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/clahe.cpp
16354 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2013, NVIDIA Corporation, all rights reserved.
14
// Copyright (C) 2014, Itseez Inc., all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the copyright holders or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
#include "precomp.hpp"
44
#include "opencl_kernels_imgproc.hpp"
45
46
// ----------------------------------------------------------------------
47
// CLAHE
48
49
#ifdef HAVE_OPENCL
50
51
namespace clahe
52
{
53
static bool calcLut(cv::InputArray _src, cv::OutputArray _dst,
54
const int tilesX, const int tilesY, const cv::Size tileSize,
55
const int clipLimit, const float lutScale)
56
{
57
cv::ocl::Kernel k("calcLut", cv::ocl::imgproc::clahe_oclsrc);
58
if(k.empty())
59
return false;
60
61
cv::UMat src = _src.getUMat();
62
_dst.create(tilesX * tilesY, 256, CV_8UC1);
63
cv::UMat dst = _dst.getUMat();
64
65
int tile_size[2];
66
tile_size[0] = tileSize.width;
67
tile_size[1] = tileSize.height;
68
69
size_t localThreads[3] = { 32, 8, 1 };
70
size_t globalThreads[3] = { tilesX * localThreads[0], tilesY * localThreads[1], 1 };
71
72
int idx = 0;
73
idx = k.set(idx, cv::ocl::KernelArg::ReadOnlyNoSize(src));
74
idx = k.set(idx, cv::ocl::KernelArg::WriteOnlyNoSize(dst));
75
idx = k.set(idx, tile_size);
76
idx = k.set(idx, tilesX);
77
idx = k.set(idx, clipLimit);
78
k.set(idx, lutScale);
79
80
return k.run(2, globalThreads, localThreads, false);
81
}
82
83
static bool transform(cv::InputArray _src, cv::OutputArray _dst, cv::InputArray _lut,
84
const int tilesX, const int tilesY, const cv::Size & tileSize)
85
{
86
87
cv::ocl::Kernel k("transform", cv::ocl::imgproc::clahe_oclsrc);
88
if(k.empty())
89
return false;
90
91
int tile_size[2];
92
tile_size[0] = tileSize.width;
93
tile_size[1] = tileSize.height;
94
95
cv::UMat src = _src.getUMat();
96
_dst.create(src.size(), src.type());
97
cv::UMat dst = _dst.getUMat();
98
cv::UMat lut = _lut.getUMat();
99
100
size_t localThreads[3] = { 32, 8, 1 };
101
size_t globalThreads[3] = { (size_t)src.cols, (size_t)src.rows, 1 };
102
103
int idx = 0;
104
idx = k.set(idx, cv::ocl::KernelArg::ReadOnlyNoSize(src));
105
idx = k.set(idx, cv::ocl::KernelArg::WriteOnlyNoSize(dst));
106
idx = k.set(idx, cv::ocl::KernelArg::ReadOnlyNoSize(lut));
107
idx = k.set(idx, src.cols);
108
idx = k.set(idx, src.rows);
109
idx = k.set(idx, tile_size);
110
idx = k.set(idx, tilesX);
111
k.set(idx, tilesY);
112
113
return k.run(2, globalThreads, localThreads, false);
114
}
115
}
116
117
#endif
118
119
namespace
120
{
121
template <class T, int histSize, int shift>
122
class CLAHE_CalcLut_Body : public cv::ParallelLoopBody
123
{
124
public:
125
CLAHE_CalcLut_Body(const cv::Mat& src, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& clipLimit, const float& lutScale) :
126
src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale)
127
{
128
}
129
130
void operator ()(const cv::Range& range) const CV_OVERRIDE;
131
132
private:
133
cv::Mat src_;
134
mutable cv::Mat lut_;
135
136
cv::Size tileSize_;
137
int tilesX_;
138
int clipLimit_;
139
float lutScale_;
140
};
141
142
template <class T, int histSize, int shift>
143
void CLAHE_CalcLut_Body<T,histSize,shift>::operator ()(const cv::Range& range) const
144
{
145
T* tileLut = lut_.ptr<T>(range.start);
146
const size_t lut_step = lut_.step / sizeof(T);
147
148
for (int k = range.start; k < range.end; ++k, tileLut += lut_step)
149
{
150
const int ty = k / tilesX_;
151
const int tx = k % tilesX_;
152
153
// retrieve tile submatrix
154
155
cv::Rect tileROI;
156
tileROI.x = tx * tileSize_.width;
157
tileROI.y = ty * tileSize_.height;
158
tileROI.width = tileSize_.width;
159
tileROI.height = tileSize_.height;
160
161
const cv::Mat tile = src_(tileROI);
162
163
// calc histogram
164
165
int tileHist[histSize] = {0, };
166
167
int height = tileROI.height;
168
const size_t sstep = src_.step / sizeof(T);
169
for (const T* ptr = tile.ptr<T>(0); height--; ptr += sstep)
170
{
171
int x = 0;
172
for (; x <= tileROI.width - 4; x += 4)
173
{
174
int t0 = ptr[x], t1 = ptr[x+1];
175
tileHist[t0 >> shift]++; tileHist[t1 >> shift]++;
176
t0 = ptr[x+2]; t1 = ptr[x+3];
177
tileHist[t0 >> shift]++; tileHist[t1 >> shift]++;
178
}
179
180
for (; x < tileROI.width; ++x)
181
tileHist[ptr[x] >> shift]++;
182
}
183
184
// clip histogram
185
186
if (clipLimit_ > 0)
187
{
188
// how many pixels were clipped
189
int clipped = 0;
190
for (int i = 0; i < histSize; ++i)
191
{
192
if (tileHist[i] > clipLimit_)
193
{
194
clipped += tileHist[i] - clipLimit_;
195
tileHist[i] = clipLimit_;
196
}
197
}
198
199
// redistribute clipped pixels
200
int redistBatch = clipped / histSize;
201
int residual = clipped - redistBatch * histSize;
202
203
for (int i = 0; i < histSize; ++i)
204
tileHist[i] += redistBatch;
205
206
if (residual != 0)
207
{
208
int residualStep = MAX(histSize / residual, 1);
209
for (int i = 0; i < histSize && residual > 0; i += residualStep, residual--)
210
tileHist[i]++;
211
}
212
}
213
214
// calc Lut
215
216
int sum = 0;
217
for (int i = 0; i < histSize; ++i)
218
{
219
sum += tileHist[i];
220
tileLut[i] = cv::saturate_cast<T>(sum * lutScale_);
221
}
222
}
223
}
224
225
template <class T, int shift>
226
class CLAHE_Interpolation_Body : public cv::ParallelLoopBody
227
{
228
public:
229
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) :
230
src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY)
231
{
232
buf.allocate(src.cols << 2);
233
ind1_p = buf.data();
234
ind2_p = ind1_p + src.cols;
235
xa_p = (float *)(ind2_p + src.cols);
236
xa1_p = xa_p + src.cols;
237
238
int lut_step = static_cast<int>(lut_.step / sizeof(T));
239
float inv_tw = 1.0f / tileSize_.width;
240
241
for (int x = 0; x < src.cols; ++x)
242
{
243
float txf = x * inv_tw - 0.5f;
244
245
int tx1 = cvFloor(txf);
246
int tx2 = tx1 + 1;
247
248
xa_p[x] = txf - tx1;
249
xa1_p[x] = 1.0f - xa_p[x];
250
251
tx1 = std::max(tx1, 0);
252
tx2 = std::min(tx2, tilesX_ - 1);
253
254
ind1_p[x] = tx1 * lut_step;
255
ind2_p[x] = tx2 * lut_step;
256
}
257
}
258
259
void operator ()(const cv::Range& range) const CV_OVERRIDE;
260
261
private:
262
cv::Mat src_;
263
mutable cv::Mat dst_;
264
cv::Mat lut_;
265
266
cv::Size tileSize_;
267
int tilesX_;
268
int tilesY_;
269
270
cv::AutoBuffer<int> buf;
271
int * ind1_p, * ind2_p;
272
float * xa_p, * xa1_p;
273
};
274
275
template <class T, int shift>
276
void CLAHE_Interpolation_Body<T, shift>::operator ()(const cv::Range& range) const
277
{
278
float inv_th = 1.0f / tileSize_.height;
279
280
for (int y = range.start; y < range.end; ++y)
281
{
282
const T* srcRow = src_.ptr<T>(y);
283
T* dstRow = dst_.ptr<T>(y);
284
285
float tyf = y * inv_th - 0.5f;
286
287
int ty1 = cvFloor(tyf);
288
int ty2 = ty1 + 1;
289
290
float ya = tyf - ty1, ya1 = 1.0f - ya;
291
292
ty1 = std::max(ty1, 0);
293
ty2 = std::min(ty2, tilesY_ - 1);
294
295
const T* lutPlane1 = lut_.ptr<T>(ty1 * tilesX_);
296
const T* lutPlane2 = lut_.ptr<T>(ty2 * tilesX_);
297
298
for (int x = 0; x < src_.cols; ++x)
299
{
300
int srcVal = srcRow[x] >> shift;
301
302
int ind1 = ind1_p[x] + srcVal;
303
int ind2 = ind2_p[x] + srcVal;
304
305
float res = (lutPlane1[ind1] * xa1_p[x] + lutPlane1[ind2] * xa_p[x]) * ya1 +
306
(lutPlane2[ind1] * xa1_p[x] + lutPlane2[ind2] * xa_p[x]) * ya;
307
308
dstRow[x] = cv::saturate_cast<T>(res) << shift;
309
}
310
}
311
}
312
313
class CLAHE_Impl CV_FINAL : public cv::CLAHE
314
{
315
public:
316
CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8);
317
318
void apply(cv::InputArray src, cv::OutputArray dst) CV_OVERRIDE;
319
320
void setClipLimit(double clipLimit) CV_OVERRIDE;
321
double getClipLimit() const CV_OVERRIDE;
322
323
void setTilesGridSize(cv::Size tileGridSize) CV_OVERRIDE;
324
cv::Size getTilesGridSize() const CV_OVERRIDE;
325
326
void collectGarbage() CV_OVERRIDE;
327
328
private:
329
double clipLimit_;
330
int tilesX_;
331
int tilesY_;
332
333
cv::Mat srcExt_;
334
cv::Mat lut_;
335
336
#ifdef HAVE_OPENCL
337
cv::UMat usrcExt_;
338
cv::UMat ulut_;
339
#endif
340
};
341
342
CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY) :
343
clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY)
344
{
345
}
346
347
void CLAHE_Impl::apply(cv::InputArray _src, cv::OutputArray _dst)
348
{
349
CV_INSTRUMENT_REGION();
350
351
CV_Assert( _src.type() == CV_8UC1 || _src.type() == CV_16UC1 );
352
353
#ifdef HAVE_OPENCL
354
bool useOpenCL = cv::ocl::isOpenCLActivated() && _src.isUMat() && _src.dims()<=2 && _src.type() == CV_8UC1;
355
#endif
356
357
int histSize = _src.type() == CV_8UC1 ? 256 : 65536;
358
359
cv::Size tileSize;
360
cv::_InputArray _srcForLut;
361
362
if (_src.size().width % tilesX_ == 0 && _src.size().height % tilesY_ == 0)
363
{
364
tileSize = cv::Size(_src.size().width / tilesX_, _src.size().height / tilesY_);
365
_srcForLut = _src;
366
}
367
else
368
{
369
#ifdef HAVE_OPENCL
370
if(useOpenCL)
371
{
372
cv::copyMakeBorder(_src, usrcExt_, 0, tilesY_ - (_src.size().height % tilesY_), 0, tilesX_ - (_src.size().width % tilesX_), cv::BORDER_REFLECT_101);
373
tileSize = cv::Size(usrcExt_.size().width / tilesX_, usrcExt_.size().height / tilesY_);
374
_srcForLut = usrcExt_;
375
}
376
else
377
#endif
378
{
379
cv::copyMakeBorder(_src, srcExt_, 0, tilesY_ - (_src.size().height % tilesY_), 0, tilesX_ - (_src.size().width % tilesX_), cv::BORDER_REFLECT_101);
380
tileSize = cv::Size(srcExt_.size().width / tilesX_, srcExt_.size().height / tilesY_);
381
_srcForLut = srcExt_;
382
}
383
}
384
385
const int tileSizeTotal = tileSize.area();
386
const float lutScale = static_cast<float>(histSize - 1) / tileSizeTotal;
387
388
int clipLimit = 0;
389
if (clipLimit_ > 0.0)
390
{
391
clipLimit = static_cast<int>(clipLimit_ * tileSizeTotal / histSize);
392
clipLimit = std::max(clipLimit, 1);
393
}
394
395
#ifdef HAVE_OPENCL
396
if (useOpenCL && clahe::calcLut(_srcForLut, ulut_, tilesX_, tilesY_, tileSize, clipLimit, lutScale) )
397
if( clahe::transform(_src, _dst, ulut_, tilesX_, tilesY_, tileSize) )
398
{
399
CV_IMPL_ADD(CV_IMPL_OCL);
400
return;
401
}
402
#endif
403
404
cv::Mat src = _src.getMat();
405
_dst.create( src.size(), src.type() );
406
cv::Mat dst = _dst.getMat();
407
cv::Mat srcForLut = _srcForLut.getMat();
408
lut_.create(tilesX_ * tilesY_, histSize, _src.type());
409
410
cv::Ptr<cv::ParallelLoopBody> calcLutBody;
411
if (_src.type() == CV_8UC1)
412
calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<uchar, 256, 0> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale);
413
else if (_src.type() == CV_16UC1)
414
calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<ushort, 65536, 0> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale);
415
else
416
CV_Error( CV_StsBadArg, "Unsupported type" );
417
418
cv::parallel_for_(cv::Range(0, tilesX_ * tilesY_), *calcLutBody);
419
420
cv::Ptr<cv::ParallelLoopBody> interpolationBody;
421
if (_src.type() == CV_8UC1)
422
interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<uchar, 0> >(src, dst, lut_, tileSize, tilesX_, tilesY_);
423
else if (_src.type() == CV_16UC1)
424
interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<ushort, 0> >(src, dst, lut_, tileSize, tilesX_, tilesY_);
425
426
cv::parallel_for_(cv::Range(0, src.rows), *interpolationBody);
427
}
428
429
void CLAHE_Impl::setClipLimit(double clipLimit)
430
{
431
clipLimit_ = clipLimit;
432
}
433
434
double CLAHE_Impl::getClipLimit() const
435
{
436
return clipLimit_;
437
}
438
439
void CLAHE_Impl::setTilesGridSize(cv::Size tileGridSize)
440
{
441
tilesX_ = tileGridSize.width;
442
tilesY_ = tileGridSize.height;
443
}
444
445
cv::Size CLAHE_Impl::getTilesGridSize() const
446
{
447
return cv::Size(tilesX_, tilesY_);
448
}
449
450
void CLAHE_Impl::collectGarbage()
451
{
452
srcExt_.release();
453
lut_.release();
454
#ifdef HAVE_OPENCL
455
usrcExt_.release();
456
ulut_.release();
457
#endif
458
}
459
}
460
461
cv::Ptr<cv::CLAHE> cv::createCLAHE(double clipLimit, cv::Size tileGridSize)
462
{
463
return makePtr<CLAHE_Impl>(clipLimit, tileGridSize.width, tileGridSize.height);
464
}
465
466