188 lines
5.4 KiB
C++
188 lines
5.4 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <numeric>
|
|
#include <vector>
|
|
|
|
#include "DataStructures.h"
|
|
|
|
enum struct LineDirection { X, Y };
|
|
|
|
template <typename TValue>
|
|
struct ImageView2D
|
|
{
|
|
static_assert(std::is_arithmetic<TValue>::value, "Image only supports integers or floats");
|
|
|
|
// forward declaration
|
|
//enum struct LineDirection;
|
|
template<LineDirection D> struct LineView;
|
|
template<LineDirection D> 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<size_t> coordFromIndex(size_t index) const
|
|
{
|
|
assertMsg(index < size, "Index out of bounds");
|
|
return Point2D<size_t>(index % width, index / width);
|
|
}
|
|
|
|
Point2D<size_t> maximum() const
|
|
{
|
|
size_t maxValueIndex = std::distance(val_begin(), std::max_element(val_begin(), val_end()));
|
|
return coordFromIndex(maxValueIndex);
|
|
}
|
|
|
|
TValue maximum(Point2D<size_t>& 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<TValue>& 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<TValue> transpose() const
|
|
{
|
|
return ImageView2D<TValue>(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<Point2D<size_t>> findLocalMaxima(int radiusX = 1, int radiusY = 1) const
|
|
{
|
|
std::vector<Point2D<size_t>> result;
|
|
findLocalMaxima(result, radiusX, radiusY);
|
|
return result;
|
|
}
|
|
|
|
void findLocalMaxima(std::vector<Point2D<size_t>>& 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 <class TValue>
|
|
struct Image2D : public ImageView2D<TValue>
|
|
{
|
|
static_assert(std::is_arithmetic<TValue>::value, "Image only supports integers or floats");
|
|
|
|
std::vector<TValue> values_vec;
|
|
|
|
Image2D()
|
|
{
|
|
}
|
|
|
|
Image2D(size_t width, size_t height)
|
|
: ImageView2D<TValue>(width, height), values_vec(width * height)
|
|
{
|
|
this->values = values_vec.data();
|
|
}
|
|
|
|
Image2D(size_t width, size_t height, std::vector<TValue>& data)
|
|
: ImageView2D<TValue>(width, height), values_vec(data)
|
|
{
|
|
assertMsg(data.size() == width*height, "Sizes must be the same");
|
|
this->values = values_vec.data();
|
|
}
|
|
|
|
std::vector<TValue>& data() { return values_vec; }
|
|
const std::vector<TValue>& data() const { return values_vec; }
|
|
};
|
|
|
|
|
|
template struct ImageView2D<float>;
|
|
template struct ImageView2D<double>;
|
|
|
|
template struct Image2D<float>;
|
|
template struct Image2D<double>;
|