/* * This file is part of RawTherapee. * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ #include #include #include #include #include #ifdef _OPENMP #include #endif #include "rt_algo.h" namespace rtengine { void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& minOut, float maxPrct, float& maxOut, bool multithread) { // Copyright (c) 2017 Ingo Weyrich // We need to find the (minPrct*size) smallest value and the (maxPrct*size) smallest value in data. // We use a histogram based search for speed and to reduce memory usage. // Memory usage of this method is histoSize * sizeof(uint32_t) * (t + 1) byte, // where t is the number of threads and histoSize is in [1;65536]. // Processing time is O(n) where n is size of the input array. // It scales well with multiple threads if the size of the input array is large. // The current implementation is not guaranteed to work correctly if size > 2^32 (4294967296). assert(minPrct <= maxPrct); if (size == 0) { return; } size_t numThreads = 1; #ifdef _OPENMP // Because we have an overhead in the critical region of the main loop for each thread // we make a rough calculation to reduce the number of threads for small data size. // This also works fine for the minmax loop. if (multithread) { const size_t maxThreads = omp_get_max_threads(); while (size > numThreads * numThreads * 16384 && numThreads < maxThreads) { ++numThreads; } } #endif // We need min and max value of data to calculate the scale factor for the histogram float minVal = data[0]; float maxVal = data[0]; #ifdef _OPENMP #pragma omp parallel for reduction(min:minVal) reduction(max:maxVal) num_threads(numThreads) #endif for (size_t i = 1; i < size; ++i) { minVal = std::min(minVal, data[i]); maxVal = std::max(maxVal, data[i]); } if (std::fabs(maxVal - minVal) == 0.f) { // fast exit, also avoids division by zero in calculation of scale factor minOut = maxOut = minVal; return; } // Caution: Currently this works correctly only for histoSize in range[1;65536]. // For small data size (i.e. thumbnails) we reduce the size of the histogram to the size of data. const unsigned int histoSize = std::min(65536, size); // calculate scale factor to use full range of histogram const float scale = (histoSize - 1) / (maxVal - minVal); // We need one main histogram std::vector histo(histoSize, 0); if (numThreads == 1) { // just one thread => use main histogram for (size_t i = 0; i < size; ++i) { // we have to subtract minVal and multiply with scale to get the data in [0;histosize] range histo[static_cast(scale * (data[i] - minVal))]++; } } else { #ifdef _OPENMP #pragma omp parallel num_threads(numThreads) #endif { // We need one histogram per thread std::vector histothr(histoSize, 0); #ifdef _OPENMP #pragma omp for nowait #endif for (size_t i = 0; i < size; ++i) { // we have to subtract minVal and multiply with scale to get the data in [0;histosize] range histothr[static_cast(scale * (data[i] - minVal))]++; } #ifdef _OPENMP #pragma omp critical #endif { // add per thread histogram to main histogram #ifdef _OPENMP #pragma omp simd #endif for (size_t i = 0; i < histoSize; ++i) { histo[i] += histothr[i]; } } } } size_t k = 0; size_t count = 0; // find (minPrct*size) smallest value const float threshmin = minPrct * size; while (count < threshmin) { count += histo[k++]; } if (k > 0) { // interpolate const size_t count_ = count - histo[k - 1]; const float c0 = count - threshmin; const float c1 = threshmin - count_; minOut = (c1 * k + c0 * (k - 1)) / (c0 + c1); } else { minOut = k; } // go back to original range minOut /= scale; minOut += minVal; // find (maxPrct*size) smallest value const float threshmax = maxPrct * size; while (count < threshmax) { count += histo[k++]; } if (k > 0) { // interpolate const size_t count_ = count - histo[k - 1]; const float c0 = count - threshmax; const float c1 = threshmax - count_; maxOut = (c1 * k + c0 * (k - 1)) / (c0 + c1); } else { maxOut = k; } // go back to original range maxOut /= scale; maxOut += minVal; } }