You are obviously on a fantastic quest to understand how to implement image processing operations in all sorts of languages. The tag reinventing-the-wheel tells me that you know that you should use an established image processing library as the basis of any serious work.
Here are some comments on your C++ implementation.
image.h
Proper English spelling would be "Developed by Jimmy Hu".
This file includes headers that you don't use. You should only include headers that you actually need.
        Image()
        {
        }
This should be
        Image() = default;
This constructor initializes the array with a constant value:
        Image(const int width, const int height, const ElementT initVal):
            width(width),
            height(height),
            image_data(width * height)
        {
            this->image_data = recursive_transform<1>(this->image_data, [initVal](ElementT element) { return initVal; });
            return;
        }
Use the std::vector constructor for this:
        Image(const int width, const int height, const ElementT initVal):
            width(width),
            height(height),
            image_data(width * height, initVal) {}
If you further add a default value to the last parameter (const ElementT initVal = {}) then you don't need the previous constructor (Image(const size_t width, const size_t height)).
        Image(const std::vector<ElementT>& input, size_t newWidth, size_t newHeight)
        {
            this->width = newWidth;
            this->height = newHeight;
            this->image_data = recursive_transform<1>(input, [](ElementT element) { return element; });   //  Deep copy
        }
This constructor can be easily implemented in terms of std::copy. You do need to add a test here to verify that the input array has the right number of elements considering newWidth and newHeight.
The implementation of at doesn't check bounds. I would suggest adding assert statements for bounds, to catch bugs in debug mode. A release build would then not test bounds.
operator+= should really test that the sizes of the second image match those of the first. You can then simply iterate over the two arrays without worrying about x and y coordinates. If sizes don't match, the operation should fail.
It is not necessary to use this-> to access members. Some people like it, but I think it is superfluous.
The standard way to increment in C++ is ++x, not x++. For an int it doesn't make any difference, but for more complex objects it could be very wasteful. ++x increments the variable, then returns its value. x++ makes a copy of the value of the variable, increments the variable, then returns the copy. If you are not using the value of the expression, use the pre-increment to avoid the unnecessary copy.
image_operations.h
You don't need to forward declare class Image. You include its header file, so the class is already declared.
auto ratiox = (float)image.getWidth() / (float)width;
Please get used to the C++ way of casting:
auto ratiox = static_cast<float>(image.getWidth()) / static_cast<float>(width);
It is a more obvious cast and makes your code more readable.
Your functions gaussianFigure2D and gaussianFigure2D2 don't need different names. I would argue the library would be easier to use if they had the same name. The compiler will pick the one or the other function depending on the arguments passed.
std::exp is implemented for all three floating-point types. Consequently, normalDistribution1D can be implemented as a template:
    template<typename T>
    T normalDistribution1D(const T x, const T standard_deviation)
    {
        return std::exp(-x * x / (2 * standard_deviation * standard_deviation));
    }
The same is true for normalDistribution2D.
Functions here are declared out of order: functions use other functions that are declared later in the file. Somehow this works for you, I think, because templates are instantiated when used. It would be better practice to declare normalDistribution1D before gaussianFigure2D, and gaussianFigure2D2 before gaussianFigure2D. Note that class methods can be declared in any order -- the compiler will first collect all member names (collect the full definition of the class) before attempting to compile each of the function bodies. The same is not true for free functions such as the ones we're talking about here.
basic_functions.h
Again you have way too many headers included here.
This is very complex code, which seems out of place with the relatively straight-forward C++ code elsewhere. With the changes suggested above, you don't need this file at all.
base_types.h
You should not be using <cstdbool>. It is deprecated in C++17 and removed in C++20. The other <c...> headers should not be needed either. Use the C++ library, not the C library.
Instead of #define MAX_PATH 256, use constexpr int MAX_PATH = 256. Preprocessor macros are nice to generate code, but they should not be used for things that you can do with native C++ constructs.
Likewise, instead of the C construct typedef unsigned char BYTE, use using BYTE = unsigned char, which is, to me, much more readable.
This is also a C thing:
typedef struct RGB
{
    unsigned char channels[3];
} RGB;
In C++ it is just
struct RGB
{
    unsigned char channels[3];
};
 
                 
                