v01
This commit is contained in:
615
thirdparty/basalt-headers/include/basalt/image/image.h
vendored
Normal file
615
thirdparty/basalt-headers/include/basalt/image/image.h
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
/**
|
||||
BSD 3-Clause License
|
||||
|
||||
This file is part of the Basalt project.
|
||||
https://gitlab.com/VladyslavUsenko/basalt-headers.git
|
||||
|
||||
Copyright (c) 2019, Vladyslav Usenko and Nikolaus Demmel.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@file
|
||||
@brief Image datatype with views, interpolation, gradients
|
||||
*/
|
||||
|
||||
// This file is adapted from Pangolin. Original license:
|
||||
|
||||
/* This file is part of the Pangolin Project.
|
||||
* http://github.com/stevenlovegrove/Pangolin
|
||||
*
|
||||
* Copyright (c) 2011 Steven Lovegrove
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include <basalt/utils/assert.h>
|
||||
|
||||
// Renamed Pangoling defines to avoid clash
|
||||
#define BASALT_HOST_DEVICE
|
||||
#define BASALT_EXTENSION_IMAGE
|
||||
#ifdef BASALT_ENABLE_BOUNDS_CHECKS
|
||||
#define BASALT_BOUNDS_ASSERT(...) BASALT_ASSERT(##__VA_ARGS__)
|
||||
#else
|
||||
#define BASALT_BOUNDS_ASSERT(...) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace basalt {
|
||||
|
||||
/// @brief Helper class for copying objects.
|
||||
template <typename T>
|
||||
struct CopyObject {
|
||||
CopyObject(const T& obj) : obj(obj) {}
|
||||
const T& obj;
|
||||
};
|
||||
|
||||
inline void PitchedCopy(char* dst, unsigned int dst_pitch_bytes,
|
||||
const char* src, unsigned int src_pitch_bytes,
|
||||
unsigned int width_bytes, unsigned int height) {
|
||||
if (dst_pitch_bytes == width_bytes && src_pitch_bytes == width_bytes) {
|
||||
std::memcpy(dst, src, height * width_bytes);
|
||||
} else {
|
||||
for (unsigned int row = 0; row < height; ++row) {
|
||||
std::memcpy(dst, src, width_bytes);
|
||||
dst += dst_pitch_bytes;
|
||||
src += src_pitch_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Image class that supports sub-images, interpolation, element access.
|
||||
template <typename T>
|
||||
struct Image {
|
||||
using PixelType = T;
|
||||
|
||||
inline Image() : pitch(0), ptr(0), w(0), h(0) {}
|
||||
|
||||
inline Image(T* ptr, size_t w, size_t h, size_t pitch)
|
||||
: pitch(pitch), ptr(ptr), w(w), h(h) {}
|
||||
|
||||
BASALT_HOST_DEVICE inline size_t SizeBytes() const { return pitch * h; }
|
||||
|
||||
BASALT_HOST_DEVICE inline size_t Area() const { return w * h; }
|
||||
|
||||
BASALT_HOST_DEVICE inline bool IsValid() const { return ptr != 0; }
|
||||
|
||||
BASALT_HOST_DEVICE inline bool IsContiguous() const {
|
||||
return w * sizeof(T) == pitch;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Iterators
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
BASALT_HOST_DEVICE inline T* begin() { return ptr; }
|
||||
|
||||
BASALT_HOST_DEVICE inline T* end() { return RowPtr(h - 1) + w; }
|
||||
|
||||
BASALT_HOST_DEVICE inline const T* begin() const { return ptr; }
|
||||
|
||||
BASALT_HOST_DEVICE inline const T* end() const { return RowPtr(h - 1) + w; }
|
||||
|
||||
BASALT_HOST_DEVICE inline size_t size() const { return w * h; }
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Image transforms
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
template <typename UnaryOperation>
|
||||
BASALT_HOST_DEVICE inline void Transform(UnaryOperation unary_op) {
|
||||
BASALT_ASSERT(IsValid());
|
||||
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
T* el = RowPtr(y);
|
||||
const T* el_end = el + w;
|
||||
for (; el != el_end; ++el) {
|
||||
*el = unary_op(*el);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline void Fill(const T& val) {
|
||||
Transform([&](const T&) { return val; });
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline void Replace(const T& oldval, const T& newval) {
|
||||
Transform([&](const T& val) { return (val == oldval) ? newval : val; });
|
||||
}
|
||||
|
||||
inline void Memset(unsigned char v = 0) {
|
||||
BASALT_ASSERT(IsValid());
|
||||
if (IsContiguous()) {
|
||||
std::memset((char*)ptr, v, pitch * h);
|
||||
} else {
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
std::memset((char*)RowPtr(y), v, pitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void CopyFrom(const Image<T>& img) {
|
||||
if (IsValid() && img.IsValid()) {
|
||||
BASALT_ASSERT(w >= img.w && h >= img.h);
|
||||
PitchedCopy((char*)ptr, pitch, (char*)img.ptr, img.pitch,
|
||||
std::min(img.w, w) * sizeof(T), std::min(img.h, h));
|
||||
} else if (img.IsValid() != IsValid()) {
|
||||
BASALT_ASSERT(false && "Cannot copy from / to an unasigned image.");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Reductions
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
template <typename BinaryOperation>
|
||||
BASALT_HOST_DEVICE inline T Accumulate(const T init,
|
||||
BinaryOperation binary_op) {
|
||||
BASALT_ASSERT(IsValid());
|
||||
|
||||
T val = init;
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
T* el = RowPtr(y);
|
||||
const T* el_end = el + w;
|
||||
for (; el != el_end; ++el) {
|
||||
val = binary_op(val, *el);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
std::pair<T, T> MinMax() const {
|
||||
BASALT_ASSERT(IsValid());
|
||||
|
||||
std::pair<T, T> minmax(std::numeric_limits<T>::max(),
|
||||
std::numeric_limits<T>::lowest());
|
||||
for (size_t r = 0; r < h; ++r) {
|
||||
const T* ptr = RowPtr(r);
|
||||
const T* end = ptr + w;
|
||||
while (ptr != end) {
|
||||
minmax.first = std::min(*ptr, minmax.first);
|
||||
minmax.second = std::max(*ptr, minmax.second);
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
return minmax;
|
||||
}
|
||||
|
||||
template <typename Tout = T>
|
||||
Tout Sum() const {
|
||||
return Accumulate((T)0,
|
||||
[](const T& lhs, const T& rhs) { return lhs + rhs; });
|
||||
}
|
||||
|
||||
template <typename Tout = T>
|
||||
Tout Mean() const {
|
||||
return Sum<Tout>() / Area();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Direct Pixel Access
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
BASALT_HOST_DEVICE inline T* RowPtr(size_t y) {
|
||||
return (T*)((unsigned char*)(ptr) + y * pitch);
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline const T* RowPtr(size_t y) const {
|
||||
return (T*)((unsigned char*)(ptr) + y * pitch);
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline T& operator()(size_t x, size_t y) {
|
||||
BASALT_BOUNDS_ASSERT(InBounds(x, y));
|
||||
return RowPtr(y)[x];
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline const T& operator()(size_t x, size_t y) const {
|
||||
BASALT_BOUNDS_ASSERT(InBounds(x, y));
|
||||
return RowPtr(y)[x];
|
||||
}
|
||||
|
||||
template <typename TVec>
|
||||
BASALT_HOST_DEVICE inline T& operator()(const TVec& p) {
|
||||
BASALT_BOUNDS_ASSERT(InBounds(p[0], p[1]));
|
||||
return RowPtr(p[1])[p[0]];
|
||||
}
|
||||
|
||||
template <typename TVec>
|
||||
BASALT_HOST_DEVICE inline const T& operator()(const TVec& p) const {
|
||||
BASALT_BOUNDS_ASSERT(InBounds(p[0], p[1]));
|
||||
return RowPtr(p[1])[p[0]];
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline T& operator[](size_t ix) {
|
||||
BASALT_BOUNDS_ASSERT(InImage(ptr + ix));
|
||||
return ptr[ix];
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline const T& operator[](size_t ix) const {
|
||||
BASALT_BOUNDS_ASSERT(InImage(ptr + ix));
|
||||
return ptr[ix];
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Interpolated Pixel Access
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
template <typename S>
|
||||
inline S interp(const Eigen::Matrix<S, 2, 1>& p) const {
|
||||
return interp<S>(p[0], p[1]);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
inline Eigen::Matrix<S, 3, 1> interpGrad(
|
||||
const Eigen::Matrix<S, 2, 1>& p) const {
|
||||
return interpGrad<S>(p[0], p[1]);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
inline S interp(S x, S y) const {
|
||||
static_assert(std::is_floating_point_v<S>,
|
||||
"interpolation / gradient only makes sense "
|
||||
"for floating point result type");
|
||||
|
||||
int ix = x;
|
||||
int iy = y;
|
||||
|
||||
S dx = x - ix;
|
||||
S dy = y - iy;
|
||||
|
||||
S ddx = S(1.0) - dx;
|
||||
S ddy = S(1.0) - dy;
|
||||
|
||||
return ddx * ddy * (*this)(ix, iy) + ddx * dy * (*this)(ix, iy + 1) +
|
||||
dx * ddy * (*this)(ix + 1, iy) + dx * dy * (*this)(ix + 1, iy + 1);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
inline Eigen::Matrix<S, 3, 1> interpGrad(S x, S y) const {
|
||||
static_assert(std::is_floating_point_v<S>,
|
||||
"interpolation / gradient only makes sense "
|
||||
"for floating point result type");
|
||||
|
||||
int ix = x;
|
||||
int iy = y;
|
||||
|
||||
S dx = x - ix;
|
||||
S dy = y - iy;
|
||||
|
||||
S ddx = S(1.0) - dx;
|
||||
S ddy = S(1.0) - dy;
|
||||
|
||||
Eigen::Matrix<S, 3, 1> res;
|
||||
|
||||
const T& px0y0 = (*this)(ix, iy);
|
||||
const T& px1y0 = (*this)(ix + 1, iy);
|
||||
const T& px0y1 = (*this)(ix, iy + 1);
|
||||
const T& px1y1 = (*this)(ix + 1, iy + 1);
|
||||
|
||||
res[0] = ddx * ddy * px0y0 + ddx * dy * px0y1 + dx * ddy * px1y0 +
|
||||
dx * dy * px1y1;
|
||||
|
||||
const T& pxm1y0 = (*this)(ix - 1, iy);
|
||||
const T& pxm1y1 = (*this)(ix - 1, iy + 1);
|
||||
|
||||
S res_mx = ddx * ddy * pxm1y0 + ddx * dy * pxm1y1 + dx * ddy * px0y0 +
|
||||
dx * dy * px0y1;
|
||||
|
||||
const T& px2y0 = (*this)(ix + 2, iy);
|
||||
const T& px2y1 = (*this)(ix + 2, iy + 1);
|
||||
|
||||
S res_px = ddx * ddy * px1y0 + ddx * dy * px1y1 + dx * ddy * px2y0 +
|
||||
dx * dy * px2y1;
|
||||
|
||||
res[1] = S(0.5) * (res_px - res_mx);
|
||||
|
||||
const T& px0ym1 = (*this)(ix, iy - 1);
|
||||
const T& px1ym1 = (*this)(ix + 1, iy - 1);
|
||||
|
||||
S res_my = ddx * ddy * px0ym1 + ddx * dy * px0y0 + dx * ddy * px1ym1 +
|
||||
dx * dy * px1y0;
|
||||
|
||||
const T& px0y2 = (*this)(ix, iy + 2);
|
||||
const T& px1y2 = (*this)(ix + 1, iy + 2);
|
||||
|
||||
S res_py = ddx * ddy * px0y1 + ddx * dy * px0y2 + dx * ddy * px1y1 +
|
||||
dx * dy * px1y2;
|
||||
|
||||
res[2] = S(0.5) * (res_py - res_my);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Bounds Checking
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
BASALT_HOST_DEVICE
|
||||
bool InImage(const T* ptest) const {
|
||||
return ptr <= ptest && ptest < RowPtr(h);
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline bool InBounds(int x, int y) const {
|
||||
return 0 <= x && x < (int)w && 0 <= y && y < (int)h;
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline bool InBounds(float x, float y,
|
||||
float border) const {
|
||||
return border <= x && x < (w - border - 1) && border <= y &&
|
||||
y < (h - border - 1);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
BASALT_HOST_DEVICE inline bool InBounds(
|
||||
const Eigen::MatrixBase<Derived>& p,
|
||||
const typename Derived::Scalar border) const {
|
||||
EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(Derived, 2);
|
||||
|
||||
using Scalar = typename Derived::Scalar;
|
||||
|
||||
Scalar offset(0);
|
||||
if constexpr (std::is_floating_point_v<Scalar>) {
|
||||
offset = Scalar(1);
|
||||
}
|
||||
|
||||
return border <= p[0] && p[0] < ((int)w - border - offset) &&
|
||||
border <= p[1] && p[1] < ((int)h - border - offset);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Obtain slices / subimages
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
BASALT_HOST_DEVICE inline const Image<const T> SubImage(size_t x, size_t y,
|
||||
size_t width,
|
||||
size_t height) const {
|
||||
BASALT_ASSERT((x + width) <= w && (y + height) <= h);
|
||||
return Image<const T>(RowPtr(y) + x, width, height, pitch);
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline Image<T> SubImage(size_t x, size_t y, size_t width,
|
||||
size_t height) {
|
||||
BASALT_ASSERT((x + width) <= w && (y + height) <= h);
|
||||
return Image<T>(RowPtr(y) + x, width, height, pitch);
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline Image<T> Row(int y) const {
|
||||
return SubImage(0, y, w, 1);
|
||||
}
|
||||
|
||||
BASALT_HOST_DEVICE inline Image<T> Col(int x) const {
|
||||
return SubImage(x, 0, 1, h);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Data mangling
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
template <typename TRecast>
|
||||
BASALT_HOST_DEVICE inline Image<TRecast> Reinterpret() const {
|
||||
BASALT_ASSERT_STREAM(sizeof(TRecast) == sizeof(T),
|
||||
"sizeof(TRecast) must match sizeof(T): "
|
||||
<< sizeof(TRecast) << " != " << sizeof(T));
|
||||
return UnsafeReinterpret<TRecast>();
|
||||
}
|
||||
|
||||
template <typename TRecast>
|
||||
BASALT_HOST_DEVICE inline Image<TRecast> UnsafeReinterpret() const {
|
||||
return Image<TRecast>((TRecast*)ptr, w, h, pitch);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Deprecated methods
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
// PANGOLIN_DEPRECATED inline
|
||||
Image(size_t w, size_t h, size_t pitch, T* ptr)
|
||||
: pitch(pitch), ptr(ptr), w(w), h(h) {}
|
||||
|
||||
// Use RAII/move aware pangolin::ManagedImage instead
|
||||
// PANGOLIN_DEPRECATED inline
|
||||
void Dealloc() {
|
||||
if (ptr) {
|
||||
::operator delete(ptr);
|
||||
ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Use RAII/move aware pangolin::ManagedImage instead
|
||||
// PANGOLIN_DEPRECATED inline
|
||||
void Alloc(size_t w, size_t h, size_t pitch) {
|
||||
Dealloc();
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->pitch = pitch;
|
||||
this->ptr = (T*)::operator new(h* pitch);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Data members
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
size_t pitch;
|
||||
T* ptr;
|
||||
size_t w;
|
||||
size_t h;
|
||||
|
||||
BASALT_EXTENSION_IMAGE
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using DefaultImageAllocator = std::allocator<T>;
|
||||
|
||||
/// @brief Image that manages it's own memory, storing a strong pointer to it's
|
||||
/// memory
|
||||
template <typename T, class Allocator = DefaultImageAllocator<T>>
|
||||
class ManagedImage : public Image<T> {
|
||||
public:
|
||||
using PixelType = T;
|
||||
using Ptr = std::shared_ptr<ManagedImage<T, Allocator>>;
|
||||
|
||||
// Destructor
|
||||
inline ~ManagedImage() { Deallocate(); }
|
||||
|
||||
// Null image
|
||||
inline ManagedImage() {}
|
||||
|
||||
// Row image
|
||||
inline ManagedImage(size_t w)
|
||||
: Image<T>(Allocator().allocate(w), w, 1, w * sizeof(T)) {}
|
||||
|
||||
inline ManagedImage(size_t w, size_t h)
|
||||
: Image<T>(Allocator().allocate(w * h), w, h, w * sizeof(T)) {}
|
||||
|
||||
inline ManagedImage(size_t w, size_t h, size_t pitch_bytes)
|
||||
: Image<T>(Allocator().allocate((h * pitch_bytes) / sizeof(T) + 1), w, h,
|
||||
pitch_bytes) {}
|
||||
|
||||
// Not copy constructable
|
||||
inline ManagedImage(const ManagedImage<T>& other) = delete;
|
||||
|
||||
// Move constructor
|
||||
inline ManagedImage(ManagedImage<T, Allocator>&& img) {
|
||||
*this = std::move(img);
|
||||
}
|
||||
|
||||
// Move asignment
|
||||
inline void operator=(ManagedImage<T, Allocator>&& img) {
|
||||
Deallocate();
|
||||
Image<T>::pitch = img.pitch;
|
||||
Image<T>::ptr = img.ptr;
|
||||
Image<T>::w = img.w;
|
||||
Image<T>::h = img.h;
|
||||
img.ptr = nullptr;
|
||||
}
|
||||
|
||||
// Explicit copy constructor
|
||||
template <typename TOther>
|
||||
ManagedImage(const CopyObject<TOther>& other) {
|
||||
CopyFrom(other.obj);
|
||||
}
|
||||
|
||||
// Explicit copy assignment
|
||||
template <typename TOther>
|
||||
void operator=(const CopyObject<TOther>& other) {
|
||||
CopyFrom(other.obj);
|
||||
}
|
||||
|
||||
inline void Swap(ManagedImage<T>& img) {
|
||||
std::swap(img.pitch, Image<T>::pitch);
|
||||
std::swap(img.ptr, Image<T>::ptr);
|
||||
std::swap(img.w, Image<T>::w);
|
||||
std::swap(img.h, Image<T>::h);
|
||||
}
|
||||
|
||||
inline void CopyFrom(const Image<T>& img) {
|
||||
if (!Image<T>::IsValid() || Image<T>::w != img.w || Image<T>::h != img.h) {
|
||||
Reinitialise(img.w, img.h);
|
||||
}
|
||||
Image<T>::CopyFrom(img);
|
||||
}
|
||||
|
||||
inline void Reinitialise(size_t w, size_t h) {
|
||||
if (!Image<T>::ptr || Image<T>::w != w || Image<T>::h != h) {
|
||||
*this = ManagedImage<T, Allocator>(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Reinitialise(size_t w, size_t h, size_t pitch) {
|
||||
if (!Image<T>::ptr || Image<T>::w != w || Image<T>::h != h ||
|
||||
Image<T>::pitch != pitch) {
|
||||
*this = ManagedImage<T, Allocator>(w, h, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Deallocate() {
|
||||
if (Image<T>::ptr) {
|
||||
Allocator().deallocate(Image<T>::ptr,
|
||||
(Image<T>::h * Image<T>::pitch) / sizeof(T));
|
||||
Image<T>::ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Move asignment
|
||||
template <typename TOther, typename AllocOther>
|
||||
inline void OwnAndReinterpret(ManagedImage<TOther, AllocOther>&& img) {
|
||||
Deallocate();
|
||||
Image<T>::pitch = img.pitch;
|
||||
Image<T>::ptr = (T*)img.ptr;
|
||||
Image<T>::w = img.w;
|
||||
Image<T>::h = img.h;
|
||||
img.ptr = nullptr;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
inline void ConvertFrom(const ManagedImage<T1>& img) {
|
||||
Reinitialise(img.w, img.h);
|
||||
|
||||
for (size_t j = 0; j < img.h; j++) {
|
||||
T* this_row = this->RowPtr(j);
|
||||
const T1* other_row = img.RowPtr(j);
|
||||
for (size_t i = 0; i < img.w; i++) {
|
||||
this_row[i] = T(other_row[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void operator-=(const ManagedImage<T>& img) {
|
||||
for (size_t j = 0; j < img.h; j++) {
|
||||
T* this_row = this->RowPtr(j);
|
||||
const T* other_row = img.RowPtr(j);
|
||||
for (size_t i = 0; i < img.w; i++) {
|
||||
this_row[i] -= other_row[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace basalt
|
||||
200
thirdparty/basalt-headers/include/basalt/image/image_pyr.h
vendored
Normal file
200
thirdparty/basalt-headers/include/basalt/image/image_pyr.h
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
BSD 3-Clause License
|
||||
|
||||
This file is part of the Basalt project.
|
||||
https://gitlab.com/VladyslavUsenko/basalt-headers.git
|
||||
|
||||
Copyright (c) 2019, Vladyslav Usenko and Nikolaus Demmel.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@file
|
||||
@brief Image pyramid implementation stored as mipmap
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <basalt/image/image.h>
|
||||
|
||||
namespace basalt {
|
||||
|
||||
/// @brief Image pyramid that stores levels as mipmap
|
||||
///
|
||||
/// \image html mipmap.jpeg
|
||||
/// Computes image pyramid (see \ref subsample) and stores it as a mipmap
|
||||
/// (https://en.wikipedia.org/wiki/Mipmap).
|
||||
template <typename T, class Allocator = DefaultImageAllocator<T>>
|
||||
class ManagedImagePyr {
|
||||
public:
|
||||
using PixelType = T;
|
||||
using Ptr = std::shared_ptr<ManagedImagePyr<T, Allocator>>;
|
||||
|
||||
/// @brief Default constructor.
|
||||
inline ManagedImagePyr() {}
|
||||
|
||||
/// @brief Construct image pyramid from other image.
|
||||
///
|
||||
/// @param other image to use for the pyramid level 0
|
||||
/// @param num_level number of levels for the pyramid
|
||||
inline ManagedImagePyr(const ManagedImage<T>& other, size_t num_levels) {
|
||||
setFromImage(other, num_levels);
|
||||
}
|
||||
|
||||
/// @brief Set image pyramid from other image.
|
||||
///
|
||||
/// @param other image to use for the pyramid level 0
|
||||
/// @param num_level number of levels for the pyramid
|
||||
inline void setFromImage(const ManagedImage<T>& other, size_t num_levels) {
|
||||
orig_w = other.w;
|
||||
image.Reinitialise(other.w + other.w / 2, other.h);
|
||||
image.Fill(0);
|
||||
lvl_internal(0).CopyFrom(other);
|
||||
|
||||
for (size_t i = 0; i < num_levels; i++) {
|
||||
const Image<const T> l = lvl(i);
|
||||
Image<T> lp1 = lvl_internal(i + 1);
|
||||
subsample(l, lp1);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Extrapolate image after border with reflection.
|
||||
static inline int border101(int x, int h) {
|
||||
return h - 1 - std::abs(h - 1 - x);
|
||||
}
|
||||
|
||||
/// @brief Subsample the image twice in each direction.
|
||||
///
|
||||
/// Subsampling is done by convolution with Gaussian kernel
|
||||
/// \f[
|
||||
/// \frac{1}{256}
|
||||
/// \begin{bmatrix}
|
||||
/// 1 & 4 & 6 & 4 & 1
|
||||
/// \\4 & 16 & 24 & 16 & 4
|
||||
/// \\6 & 24 & 36 & 24 & 6
|
||||
/// \\4 & 16 & 24 & 16 & 4
|
||||
/// \\1 & 4 & 6 & 4 & 1
|
||||
/// \\ \end{bmatrix}
|
||||
/// \f]
|
||||
/// and removing every even-numbered row and column.
|
||||
static void subsample(const Image<const T>& img, Image<T>& img_sub) {
|
||||
static_assert(std::is_same<T, uint16_t>::value ||
|
||||
std::is_same<T, uint8_t>::value);
|
||||
|
||||
constexpr int kernel[5] = {1, 4, 6, 4, 1};
|
||||
|
||||
// accumulator
|
||||
ManagedImage<int> tmp(img_sub.h, img.w);
|
||||
|
||||
// Vertical convolution
|
||||
{
|
||||
for (int r = 0; r < int(img_sub.h); r++) {
|
||||
const T* row_m2 = img.RowPtr(std::abs(2 * r - 2));
|
||||
const T* row_m1 = img.RowPtr(std::abs(2 * r - 1));
|
||||
const T* row = img.RowPtr(2 * r);
|
||||
const T* row_p1 = img.RowPtr(border101(2 * r + 1, img.h));
|
||||
const T* row_p2 = img.RowPtr(border101(2 * r + 2, img.h));
|
||||
|
||||
for (int c = 0; c < int(img.w); c++) {
|
||||
tmp(r, c) = kernel[0] * int(row_m2[c]) + kernel[1] * int(row_m1[c]) +
|
||||
kernel[2] * int(row[c]) + kernel[3] * int(row_p1[c]) +
|
||||
kernel[4] * int(row_p2[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal convolution
|
||||
{
|
||||
for (int c = 0; c < int(img_sub.w); c++) {
|
||||
const int* row_m2 = tmp.RowPtr(std::abs(2 * c - 2));
|
||||
const int* row_m1 = tmp.RowPtr(std::abs(2 * c - 1));
|
||||
const int* row = tmp.RowPtr(2 * c);
|
||||
const int* row_p1 = tmp.RowPtr(border101(2 * c + 1, tmp.h));
|
||||
const int* row_p2 = tmp.RowPtr(border101(2 * c + 2, tmp.h));
|
||||
|
||||
for (int r = 0; r < int(tmp.w); r++) {
|
||||
int val_int = kernel[0] * row_m2[r] + kernel[1] * row_m1[r] +
|
||||
kernel[2] * row[r] + kernel[3] * row_p1[r] +
|
||||
kernel[4] * row_p2[r];
|
||||
T val = ((val_int + (1 << 7)) >> 8);
|
||||
img_sub(c, r) = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Return const image of the certain level
|
||||
///
|
||||
/// @param lvl level to return
|
||||
/// @return const image of with the pyramid level
|
||||
inline const Image<const T> lvl(size_t lvl) const {
|
||||
size_t x = (lvl == 0) ? 0 : orig_w;
|
||||
size_t y = (lvl <= 1) ? 0 : (image.h - (image.h >> (lvl - 1)));
|
||||
size_t width = (orig_w >> lvl);
|
||||
size_t height = (image.h >> lvl);
|
||||
|
||||
return image.SubImage(x, y, width, height);
|
||||
}
|
||||
|
||||
/// @brief Return const image of underlying mipmap
|
||||
///
|
||||
/// @return const image of of the underlying mipmap representation which can
|
||||
/// be for example used for visualization
|
||||
inline const Image<const T> mipmap() const {
|
||||
return image.SubImage(0, 0, image.w, image.h);
|
||||
}
|
||||
|
||||
/// @brief Return coordinate offset of the image in the mipmap image.
|
||||
///
|
||||
/// @param lvl level to return
|
||||
/// @return offset coordinates (2x1 vector)
|
||||
template <typename S>
|
||||
inline Eigen::Matrix<S, 2, 1> lvl_offset(size_t lvl) {
|
||||
size_t x = (lvl == 0) ? 0 : orig_w;
|
||||
size_t y = (lvl <= 1) ? 0 : (image.h - (image.h >> (lvl - 1)));
|
||||
|
||||
return Eigen::Matrix<S, 2, 1>(x, y);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @brief Return image of the certain level
|
||||
///
|
||||
/// @param lvl level to return
|
||||
/// @return image of with the pyramid level
|
||||
inline Image<T> lvl_internal(size_t lvl) {
|
||||
size_t x = (lvl == 0) ? 0 : orig_w;
|
||||
size_t y = (lvl <= 1) ? 0 : (image.h - (image.h >> (lvl - 1)));
|
||||
size_t width = (orig_w >> lvl);
|
||||
size_t height = (image.h >> lvl);
|
||||
|
||||
return image.SubImage(x, y, width, height);
|
||||
}
|
||||
|
||||
size_t orig_w; ///< Width of the original image (level 0)
|
||||
ManagedImage<T> image; ///< Pyramid image stored as a mipmap
|
||||
};
|
||||
|
||||
} // namespace basalt
|
||||
Reference in New Issue
Block a user