简体   繁体   中英

C++ clamp function for a std::vector

void Clamp(std::vector<double>& input_image, double min_value, double max_value, int width, int height)
{
    for (int j = 0; j < width; j++)
    {
        for (int i = 0; i < height; i++)
        {
            input_image[i*width + j] = std::min(std::max(input_image[i*width + j], min_value), max_value);
        }
    }
}

I am trying to create this functionality of clamping.

Is there a better way to do it using a std::vector ?

I can also use C++11

C++17 has std::clamp . Also there's no need for the height and width parameters in this case.

void clamp_vec(std::vector<double>& input_image, double min_value, double max_value) {
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image),
                   [=] (auto i) { return std::clamp(i, min_value, max_value); });
}

Template version for generic types will work as long as operator < is defined:

template <typename T>
void clamp_vec(std::vector<T>& input_image, const T& min_value, const T& max_value) {
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image),
                   [&] (const T& v) { return std::clamp(v, min_value, max_value); });
}

If std::clamp is not available, you can implement your own until C++17 comes around, eg :

template <typename T>
constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
    return clamp(v, lo, hi, std::less<>());
}

template <typename T, typename Compare>
constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) {
    return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
}

You could use std::transform , assuming that you apply your function to every element then there is no need for the height and width parameters. Splitting the min and max operations seems more SIMD friendly, thus I suggest:

void Clamp(std::vector<double>& input_image, double min_value, double max_value)
{
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image), [max_value] (double d) { return std::min(d, max_value); }
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image), [min_value] (double d) { return std::max(d, max_value); }
}

Otherwise a C++11 way could be to use a range-based for loop :

void Clamp(std::vector<double>& input_image, double min_value, double max_value) // removed hight and width
{
    for (double& d : input_image)
    {
        d = std::min(std::max(d, min_value), max_value);
    }
}

Or the more SIMD "friendly":

void Clamp(std::vector<double>& input_image, double min_value, double max_value) // removed height and width
{
    for (double& d : input_image)
    {
        d = std::max(d, min_value);
    }
    for (double& d : input_image)
    {
        d = std::min(d, max_value);
    }
}

I think I'd just do it in one pass and let the optimiser do its work.

void Clamp(std::vector<double>& input_image, double min_value, double max_value)
{
    auto clamp = [min_value, max_value](double x) {
        return std::min(std::max(x, min_value), max_value);
    };

    std::transform(input_image.begin(), input_image.end(),
                   input_image.begin(), clamp);
}

testing this on godbolt, gcc vectorised it and completely eliminated the redundant copy of clamp .

There you go. I made two transforms just to give the compiler a better chance for optimizations and vectorization. You can join them if you wish in 1 lambda if you disagree.

std::transform(input_image.begin(), input_image.end(), input_image.begin(), [lo](double v) {return (v < lo ? lo : v);});
std::transform(input_image.begin(), input_image.end(), input_image.begin(), [hi](double v) {return (v > hi ? hi : v);});

Always keep in mind that looping over consecutive memory elements is much faster. Refer to std::transform reference to understand how it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM