TransWikia.com

An Implementation of Two Dimensional Plane as Monochromic Image Container with std::unique_ptr in C++

Code Review Asked by JimmyHu on January 21, 2021

I am trying to implement a monochromic image container with std::unique_ptr.

The example usages

The example usages is as below.

int main()
{
    auto test_data = std::make_unique<unsigned char[]>(100 * 100);
    for (size_t i = 0; i < 100 * 100; i++)
    {
        test_data[i] = 3;
    }
    auto test = DigitalImageProcessing::MonoImage(DigitalImageProcessing::MyImageSize(100, 100), test_data);
    auto test2 = test++;
    std::cout << test.Sum() << std::endl;
    std::cout << test2.Sum() << std::endl;
    return 0;
}

The experimental implementation

The contents in MyImageSize.h file:

#ifndef IMAGESIZE
#define IMAGESIZE

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <concepts>
#include <cstdbool>
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <execution>
#include <exception>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <numeric>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

namespace DigitalImageProcessing
{
    template<class T = unsigned int>
    class MyImageSize
    {
    private:
        T xsize;
        T ysize;

    public:
        MyImageSize(T new_xsize, T new_ysize)
        {
            this->xsize = new_xsize;
            this->ysize = new_ysize;
        }
        ~MyImageSize()
        {
        }

        T GetSizeX()
        {
            return this->xsize;
        }

        T GetSizeY()
        {
            return this->ysize;
        }

        void SetSizeX(T sizex)
        {
            this->xsize = sizex;
            return;
        }

        void SetSizeY(T sizey)
        {
            this->ysize = sizey;
            return;
        }
    };
}

#endif

The contents in MyImageSize.cpp file:

#include "IMAGESIZE.h"

The contents in MonoPixelOperation.h file:

#ifndef MONOPIXELOPERATION
#define MONOPIXELOPERATION

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <concepts>
#include <cstdbool>
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <execution>
#include <exception>
#include <fstream>
#include <functional>
#include <future>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <numeric>
#include <omp.h>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
#include <variant>
#include <vector>

namespace DigitalImageProcessing
{
    class MonoPixelOperation
    {
    public:

        MonoPixelOperation()
        {
        }

        ~MonoPixelOperation()
        {
        }
        static void SetMonoPixelValue(unsigned char *input_pixel, unsigned char value);
        static void SetMonoPixelValue(unsigned char *input_pixel, unsigned int value);
        static void SetMonoPixelValue(unsigned char *input_pixel, signed int value);
        static void SetMonoPixelValue(unsigned char *input_pixel, long double value);
        
        static void SetMonoPixelValue(std::unique_ptr<unsigned char> input_pixel, unsigned char value);
        static void SetMonoPixelValue(std::unique_ptr<unsigned char> input_pixel, long double value);
        
        static void SetMonoPixelValue(std::shared_ptr<unsigned char> input_pixel, unsigned char value);
        
        static unsigned char GetMonoPixelValue(unsigned char *input_pixel);
        
        static void SetMonoPixelValueToZero(unsigned char *input_pixel);
        
    private:

        const static unsigned char PixelMAXValue = 255;
        const static unsigned char PixelMINValue = 0;

        template<class T>
        static unsigned char TruncatePixelValue(T value)
        {
            if (value > PixelMAXValue)
            {
                return PixelMAXValue;
            }
            else if (value < PixelMINValue)
            {
                return PixelMINValue;
            }
            else
            {
                return static_cast<unsigned char>(value);
            }
        }
    };
}

#endif // !MONOPIXELOPERATION

The contents in MonoPixelOperation.cpp file:

#include "MonoPixelOperation.h"

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(unsigned char * input_pixel, unsigned char value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(unsigned char * input_pixel, unsigned int value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(unsigned char * input_pixel, signed int value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(unsigned char * input_pixel, long double value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(std::unique_ptr<unsigned char> input_pixel, unsigned char value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(std::unique_ptr<unsigned char> input_pixel, long double value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(std::shared_ptr<unsigned char> input_pixel, unsigned char value)
{
    *input_pixel = TruncatePixelValue(value);
    return;
}

unsigned char DigitalImageProcessing::MonoPixelOperation::GetMonoPixelValue(unsigned char * input_pixel)
{
    return *input_pixel;
}

void DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValueToZero(unsigned char * input_pixel)
{
    *input_pixel = 0;
    return;
}

The contents in MonoImage.h file:

#ifndef MONOIMAGE
#define MONOIMAGE

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <concepts>
#include <cstdbool>
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <execution>
#include <exception>
#include <fstream>
#include <functional>
#include <future>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <numeric>
#include <omp.h>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
#include "ImageSize.h"
#include "MonoPixelOperation.h"

namespace DigitalImageProcessing
{
    template<class SizeT = unsigned int>
    class MonoImage
    {
    public:
        MonoImage()
        {
        }

        constexpr auto GetimageSizeX()
        {
            return image_size.xsize;
        }
        
        constexpr auto GetimageSizeY()
        {
            return this->image_size.ysize;
        }

        constexpr auto GetimageSize()
        {
            return this->image_size;
        }

        std::unique_ptr<unsigned char[]> GetImageData()
        {
            auto output = std::make_unique<unsigned char[]>(image_size.xsize * image_size.ysize);
            if (image_data == NULL)
            {
                std::printf("Memory allocation error!");
                throw std::logic_error("Memory allocation error!");
            }
            
            for (SizeT y = 0; y < image_size.ysize; y++)
            {
                for (SizeT x = 0; x < image_size.xsize; x++)
                {
                    DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValue(&output[y * image_size.xsize + x],
                        DigitalImageProcessing::MonoPixelOperation::GetMonoPixelValue(&image_data[y * image_size.xsize + x]));
                }
            }
            return output;
        }

        MonoImage& operator=(MonoImage const& input)                            //    Copy Assign
        {
            this->image_size.xsize = input.image_size.xsize;
            this->image_size.ysize = input.image_size.ysize;
            this->image_data = std::make_unique<unsigned char[]>(image_size.xsize * image_size.ysize);
            
            for (SizeT y = 0; y < image_size.ysize; y++)
            {
                for (SizeT x = 0; x < image_size.xsize; x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&this->image_data[y * image_size.xsize + x],
                        MonoPixelOperation::GetMonoPixelValue(&input.image_data[y * image_size.xsize + x]));
                }
            }
            return *this;
        }

        MonoImage& operator=(MonoImage&& other)                            //    Move Assign
        {
            image_size = std::move(other.image_size);
            image_data = std::move(other.image_data);
            return *this;
        }

        MonoImage& operator+=(const MonoImage& rhs)
        {
            if (rhs.image_size.xsize == this->image_size.xsize &&
                rhs.image_size.ysize == this->image_size.ysize)
            {
                auto output = MonoImage(rhs.image_size.xsize, rhs.image_size.ysize);
                
                for (SizeT y = 0; y < image_size.ysize; y++)
                {
                    for (SizeT x = 0; x < image_size.xsize; x++)
                    {
                        MonoPixelOperation::SetMonoPixelValue(&output.image_data[y* image_size.xsize + x],
                            MonoPixelOperation::GetMonoPixelValue(&image_data[(y)*(image_size.xsize) + (x)]) + 
                            MonoPixelOperation::GetMonoPixelValue(&rhs.image_data[(y) * (image_size.xsize) + (x)])
                        );
                    }
                }
                return output;
            }
        }

        MonoImage& operator++()
        {
            return *this;
        }

        /*    This operator is used as
            MonoImage MonoImage1(100, 100);
            MonoImage MonoImage2(100, 100);
            MonoImage2 = MonoImage1++;
        */
        MonoImage operator++(int)
        {
            auto output = MonoImage(this->image_size.GetSizeX(), this->image_size.GetSizeY());
            for (SizeT y = 0; y < image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < image_size.GetSizeX(); x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&output.image_data[y* image_size.GetSizeX() + x],
                        MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y)*(image_size.GetSizeX()) + (x)])
                    );
                }
            }
            for (SizeT y = 0; y< image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x< image_size.GetSizeX(); x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&this->image_data[y * image_size.GetSizeX() + x],
                        MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y) * (image_size.GetSizeX()) + (x)]) + 1
                    );
                }
            }
            return output;
        }

        MonoImage& operator--()
        {
            return *this;
        }

        MonoImage operator--(int)
        {
            auto output = MonoImage(this->image_size.xsize, this->image_size.ysize);
            for (SizeT y = 0; y< image_size.ysize; y++)
            {
                for (SizeT x = 0; x< image_size.xsize; x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&output.image_data[y* image_size.xsize + x],
                        MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y)*(image_size.xsize) + (x)]) - 1
                    );
                }
            }
            return output;
        }

        friend bool operator==(const MonoImage& input1, const MonoImage& input2)
        {
            if (input1.image_size != input2.image_size)
            {
                return false;
            }
            for (SizeT y = 0; y < input1.image_size.ysize; y++)
            {
                for (SizeT x = 0; x < input1.image_size.xsize; x++)
                {
                    if (MonoPixelOperation::GetMonoPixelValue(&input1.image_data[y * input1.image_size.xsize + x]) -
                        MonoPixelOperation::GetMonoPixelValue(&input2.image_data[y * input2.image_size.xsize + x]) != 0)
                    {
                        return false;
                    };
                }
            }
            return true;
        }

        friend bool operator!=(const MonoImage& input1, const MonoImage& input2)
        {
            return !(input1 == input2);
        }

        MonoImage Difference(const MonoImage& input)
        {
            auto output = MonoImage(this->image_size.xsize, this->image_size.ysize);
            for (SizeT y = 0; y < image_size.ysize; y++)
            {
                for (SizeT x = 0; x < image_size.xsize; x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&output.image_data[y * image_size.xsize + x],
                        std::abs(
                            MonoPixelOperation::GetMonoPixelValue(&input.image_data[(y) * (image_size.xsize) + (x)]) -
                            MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y) * (image_size.xsize) + (x)])
                        )
                    );
                }
            }
            return output;
        }

        constexpr auto Subtract(const MonoImage& input)
        {
            auto output = MonoImage<SizeT>(this->image_size.GetSizeX(), this->image_size.GetSizeY());
            for (SizeT y = 0; y < image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < image_size.GetSizeX(); x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&output.image_data[y * image_size.GetSizeX() + x],
                        MonoPixelOperation::GetMonoPixelValue(&input.image_data[(y) * (image_size.GetSizeX()) + (x)]) -
                        MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y) * (image_size.GetSizeX()) + (x)])
                    );
                }
            }
            return output;
        }

        constexpr auto Sum()
        {
            unsigned long long int ReturnValue = 0;

            for (SizeT y = 0; y < image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < image_size.GetSizeX(); x++)
                {
                    ReturnValue += MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y) * (image_size.GetSizeX()) + (x)]);
                }
            }
            return ReturnValue;
        }

        MonoImage(const MonoImage<SizeT> &input)
            : image_size(input.image_size)
        {
            if (input.image_size.xsize == 0 || input.image_size.ysize == 0)
            {
                return;
            }
            image_data = std::make_unique<unsigned char[]>(image_size.xsize * image_size.ysize);
            for (SizeT y = 0; y < image_size.ysize; y++)
            {
                for (SizeT x = 0; x < image_size.xsize; x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&image_data[y * image_size.xsize + x],
                        MonoPixelOperation::GetMonoPixelValue(&input.image_data[y * image_size.xsize + x]));
                }
            }
        }

        /*    Move Constructor
         */
        MonoImage(MonoImage &&input) : image_size(input.image_size), image_data(std::move(input.image_data))
        {
        }

        MonoImage(DigitalImageProcessing::MyImageSize<SizeT> input_size)
            : image_size(input_size)
        {
            this->image_data = std::make_unique<unsigned char[]>(this->image_size.GetSizeX() * this->image_size.GetSizeY());
            if (this->image_data == NULL)
            {
                printf("Memory allocation error!");
                throw std::logic_error("Memory allocation error!");
            }
            SetImageDataToBlack();
        }

        MonoImage(DigitalImageProcessing::MyImageSize<SizeT> input_size, const std::unique_ptr<unsigned char[]>& input_image_data)
            : image_size(input_size)
        {
            this->image_data = std::make_unique<unsigned char[]>(this->image_size.GetSizeX() * this->image_size.GetSizeY());
            if (this->image_data == NULL)
            {
                printf("Memory allocation error!");
                throw std::logic_error("Memory allocation error!");
            }
            for (SizeT y = 0; y < input_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < input_size.GetSizeX(); x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&this->image_data[y * input_size.GetSizeX() + x],
                        MonoPixelOperation::GetMonoPixelValue(&input_image_data[y * input_size.GetSizeX() + x]));
                }
            }
        }

        template<class T1, class T2>
        MonoImage(const T1& xsize, const T2& ysize)
            : image_size(MyImageSize<SizeT>(static_cast<SizeT>(xsize), static_cast<SizeT>(ysize)))
        {
            this->image_data = std::make_unique<unsigned char[]>(xsize * ysize);
            if (this->image_data == NULL)
            {
                std::printf("Memory allocation error!");
                throw std::logic_error("Memory allocation error!");
            }
            SetImageDataToBlack();
        }

        template<class T1, class T2>
        MonoImage(const SizeT& xsize, const SizeT& ysize, const std::unique_ptr<unsigned char[]>& input_image_data)
            : image_size(MyImageSize<SizeT>(static_cast<SizeT>(xsize), static_cast<SizeT>(ysize)))
        {
            this->image_data = std::make_unique<unsigned char[]>(xsize * ysize);
            if (this->image_data == NULL)
            {
                printf("Memory allocation error!");
                throw std::logic_error("Memory allocation error!");
            }
            for (SizeT y = 0; y < ysize; y++)
            {
                for (SizeT x = 0; x < xsize; x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&this->image_data[y * xsize + x], 
                        MonoPixelOperation::GetMonoPixelValue(&input_image_data[y * xsize + x]));
                }
            }
        }

        ~MonoImage()
        {}

    private:
        MyImageSize<SizeT> image_size;
        std::unique_ptr<unsigned char[]> image_data;

        void SetImageDataToBlack()
        {
            for (SizeT y = 0; y < this->image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < this->image_size.GetSizeX(); x++)
                {
                    DigitalImageProcessing::MonoPixelOperation::SetMonoPixelValueToZero(&image_data[y * image_size.GetSizeX() + x]);
                }
            }
        }

        void SetImageDataToWhite()
        {
            for (SizeT y = 0; y < image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < image_size.GetSizeX(); x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&image_data[y * image_size.GetSizeX() + x], 255);
                }
            }
        }

        void SetImageDataToSpecificValue(const unsigned char input_value)
        {
            for (SizeT y = 0; y < image_size.GetSizeY(); y++)
            {
                for (SizeT x = 0; x < image_size.GetSizeX(); x++)
                {
                    MonoPixelOperation::SetMonoPixelValue(&image_data[y * image_size.GetSizeX() + x], input_value);
                }
            }
        }
    };
}
#endif // !MONOIMAGE

The contents in MonoImage.cpp file:

#include "MonoImage.h"

A Godbolt link is here.

If there is any possible improvement about:

  • Performance: including data accessing/updating speed things

  • The naming and readability

  • Potential drawbacks of the implemented member functions

, please let me know.

One Answer

#include only the headers you need where you need them. MyImageSize.h needs no includes. MonoPixelOperation.h needs only <memory>.


template<class T = unsigned int>
class MyImageSize
{
private:
    T xsize;
    T ysize;

public:

    MyImageSize(T new_xsize, T new_ysize)
    {
        this->xsize = new_xsize;
        this->ysize = new_ysize;
    }
    ~MyImageSize()
    {
    }

    T GetSizeX()
    {
        return this->xsize;
    }

    T GetSizeY()
    {
        return this->ysize;
    }

    void SetSizeX(T sizex)
    {
        this->xsize = sizex;
        return;
    }

    void SetSizeY(T sizey)
    {
        this->ysize = sizey;
        return;
    }
};

We could replace this whole thing with:

template<class T = unsigned int>
struct MyImageSize
{
    T xsize;
    T ysize;
};

Only the constructor provides extra functionality (ensuring the variables are initialized), but that's arguably unnecessary too.


    static void SetMonoPixelValue(unsigned char *input_pixel, unsigned char value);
    static void SetMonoPixelValue(unsigned char *input_pixel, unsigned int value);
    static void SetMonoPixelValue(unsigned char *input_pixel, signed int value);
    static void SetMonoPixelValue(unsigned char *input_pixel, long double value);
    
    static void SetMonoPixelValue(std::unique_ptr<unsigned char> input_pixel, unsigned char value);
    static void SetMonoPixelValue(std::unique_ptr<unsigned char> input_pixel, long double value);
    
    static void SetMonoPixelValue(std::shared_ptr<unsigned char> input_pixel, unsigned char value);
    
    static unsigned char GetMonoPixelValue(unsigned char *input_pixel);
    
    static void SetMonoPixelValueToZero(unsigned char *input_pixel);

This is kinda silly...

  • input_pixel is an output for most of these.
  • We should use a reference instead of a pointer (a nullptr wouldn't work).
  • We could define a single Set function that takes the correct type (the same as the pixel type)?
  • We shouldn't hide clamping in a function called "Set".
  • We may well want to set the values without clamping.
  • We can use std::clamp for clamping.

Compare:

pixel = std::clamp(value, min, max);

vs.

MonoPixelOperation::SetMonoPixelValue(&pixel, value);

I don't think there's much point in templating the SizeT of the image, rather than just using std::size_t for the index type.

Consider templating the pixel type instead, so we don't need a separate MonoImage and RGBImage; we could use an Image<unsigned char> or Image<Vec3>.


    MonoImage& operator+=(const MonoImage& rhs)
    {
        if (rhs.image_size.xsize == this->image_size.xsize &&
            rhs.image_size.ysize == this->image_size.ysize)
        {
            ...
            return output;
        }
    }

So if the sizes aren't the same, we... err... don't return anything? I'm surprised that compiles. If you aren't getting a compiler warning about that, you need to turn up the compiler warning level (and set warnings to errors).


    MonoImage& operator++()
    {
        return *this;
    }

// TODO: implement me? ;)

    MonoImage operator++(int)
    {
        auto output = MonoImage(this->image_size.GetSizeX(), this->image_size.GetSizeY());
        for (SizeT y = 0; y < image_size.GetSizeY(); y++)
        {
            for (SizeT x = 0; x < image_size.GetSizeX(); x++)
            {
                MonoPixelOperation::SetMonoPixelValue(&output.image_data[y* image_size.GetSizeX() + x],
                    MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y)*(image_size.GetSizeX()) + (x)])
                );
            }
        }
        for (SizeT y = 0; y< image_size.GetSizeY(); y++)
        {
            for (SizeT x = 0; x< image_size.GetSizeX(); x++)
            {
                MonoPixelOperation::SetMonoPixelValue(&this->image_data[y * image_size.GetSizeX() + x],
                    MonoPixelOperation::GetMonoPixelValue(&this->image_data[(y) * (image_size.GetSizeX()) + (x)]) + 1
                );
            }
        }
        return output;
    }

Or... we could just:

auto output = *this;
for (auto i = std::size_t{ 0 }; i != image_size.GetImageSize(); ++i)
    ++output.image_data[i];

std::vector<> provides a much cleaner and more useful interface than std::unique_ptr<[]> for holding and manipulating the data.

Consider:

struct ImageSize
{
    std::size_t x, y;
};

template<class PixelT>
class Image
{
public:
    
    Image():
        m_size(0, 0),
        m_data() { }

    Image(std::size_t width, std::size_t height, PixelT value = PixelT()):
        m_size(width, height),
        m_data(GetSize(), value) { }

    // ... other std::vector-style constructors
    
    Image(Image const&) = default; // woo!
    Image& operator=(Image const&) = default; // yay!
    Image(Image&&) = default; // yaaaaaaaaas queen!
    Image& operator=(Image&&) = default; // it's so easy!
    
    std::vector<PixelT> CloneData() const { return m_data; } // copy the whole thing!
    
    Image& operator++()
    {
        for (auto& v : m_data) // easy peasy...
            ++v;
        
        return *this;
    }
    
    Image& operator+=(Image const& rhs)
    {
        if (m_size != rhs.m_size)
            throw std::runtime_error("unequal image sizes in operator+=");
        
        for (auto i = std::size_t{ 0 }; i != m_data.size(); ++i) // this is as hard as it gets
            m_data[i] += rhs.m_data[i];
        
        return *this;
    }
    
    // ...
    
private:
    
    ImageSize m_size;
    std::vector<PixelT> m_data;
};

For the interface I'd suggest to:

  • Look at the std::vector interface: constructors, indexed pixel access, iterators, range-based for loops etc. would all be useful. We can simply write a wrapper around the std::vector implementations for this part of the interface (e.g. use std::vector<T>::iterator).

  • Look at the basic mathematical operators: +, -, /, *, %, and the assignment versions and unary operators would all be useful. We can mainly apply the built-in operations to every pixel in the image (with a bit of bounds checking).

  • Other operations (e.g. Abs(), Difference(), Clamp() etc.) can be implemented as free-functions instead of member functions. They can use the math / vector interfaces and should be a couple of lines of code each. e.g.:

     template<class ImageT>
     void Clamp(ImageT& image, typename ImageT::PixelT min, typename ImageT::PixelT max)
     {
         for (auto& p : image)
             p = std::clamp(p, min, max);
     }
    

Correct answer by user673679 on January 21, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP