#pragma once #include #include #include #include #include "DataStructures.h" enum struct LineDirection { X, Y }; template struct ImageView2D { static_assert(std::is_arithmetic::value, "Image only supports integers or floats"); // forward declaration //enum struct LineDirection; template struct LineView; template struct ConstLineView; size_t width, height, size; protected: TValue* values; // contains image data row-wise public: ImageView2D() : width(0), height(0), size(0), values(nullptr) { } ImageView2D(size_t width, size_t height) : width(width), height(height), size(width*height), values(nullptr) { } ImageView2D(size_t width, size_t height, TValue* data) : width(width), height(height), size(width*height), values(data) { } inline TValue& operator() (size_t x, size_t y) { return values[indexFromCoord(x, y)]; } inline const TValue& operator() (size_t x, size_t y) const { return values[indexFromCoord(x, y)]; } TValue* val_begin() { return values; } TValue* val_end () { return values + size; } const TValue* val_begin() const { return values; } const TValue* val_end () const { return values + size; } inline size_t indexFromCoord(size_t x, size_t y) const { assertMsg(x < width && y < height, "(x,y) out of bounds"); return y * width + x; } Point2D coordFromIndex(size_t index) const { assertMsg(index < size, "Index out of bounds"); return Point2D(index % width, index / width); } Point2D maximum() const { size_t maxValueIndex = std::distance(val_begin(), std::max_element(val_begin(), val_end())); return coordFromIndex(maxValueIndex); } TValue maximum(Point2D& pt) const { size_t maxValueIndex = std::distance(val_begin(), std::max_element(val_begin(), val_end())); pt = coordFromIndex(maxValueIndex); return values[maxValueIndex]; } void clear() { fill(TValue(0)); } void fill(TValue value) { std::fill(val_begin(), val_end(), value); } void assign(const ImageView2D& other) { assertMsg(size == other.size, "Other must be of the same size as this"); std::copy(other.val_begin(), other.val_end(), val_begin()); } TValue sum() const { return std::accumulate(val_begin(), val_end(), TValue(0)); } // Returns a transposed view of this image without copying any data. ImageView2D transpose() const { return ImageView2D(height, width, values); } // Returns the value at (x,y). Throws if out of bounds. const TValue& at(size_t x, size_t y) const { return values[indexFromCoord(x, y)]; } // Returns the value at (x,y) but falls back to default if out of bounds. const TValue& get(size_t x, size_t y, TValue dflt = 0) const { if (x >= width || y >= height) return dflt; else return values[indexFromCoord(x, y)]; } std::vector> findLocalMaxima(int radiusX = 1, int radiusY = 1) const { std::vector> result; findLocalMaxima(result, radiusX, radiusY); return result; } void findLocalMaxima(std::vector>& result, int radiusX = 1, int radiusY = 1) const { assertMsg(radiusX > 0, "Search radius must be greater than 0."); for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { TValue val = at(x, y); bool lclMax = true; for (int ry = -radiusY; ry <= radiusY; ry++) { for (int rx = -radiusX; rx <= radiusX; rx++) { TValue other = get(x + rx, y + ry); if (!(rx == 0 && ry == 0) && val <= other) { lclMax = false; } } } if (lclMax) { result.emplace_back(x, y); } } } } }; template struct Image2D : public ImageView2D { static_assert(std::is_arithmetic::value, "Image only supports integers or floats"); std::vector values_vec; Image2D() { } Image2D(size_t width, size_t height) : ImageView2D(width, height), values_vec(width * height) { this->values = values_vec.data(); } Image2D(size_t width, size_t height, std::vector& data) : ImageView2D(width, height), values_vec(data) { assertMsg(data.size() == width*height, "Sizes must be the same"); this->values = values_vec.data(); } std::vector& data() { return values_vec; } const std::vector& data() const { return values_vec; } }; template struct ImageView2D; template struct ImageView2D; template struct Image2D; template struct Image2D;