Added 3D boxKDE

This commit is contained in:
2018-07-31 15:41:25 +02:00
parent 32870a62c6
commit 4aec7bae26
6 changed files with 803 additions and 33 deletions

262
math/boxkde/BoxGaus3D.h Normal file
View File

@@ -0,0 +1,262 @@
#pragma once
#include <cmath>
#include <vector>
#include "BoxSizes.h"
#include "Image3D.h"
template <class T>
struct BoxGaus3D
{
void boxFilter(std::vector<T>& input, unsigned w, unsigned h, unsigned d, unsigned filterSize)
{
std::vector<T> buffer(input.size());
boxFilter(input.data(), buffer.data(), w, h, d, filterSize);
input.assign(buffer.begin(), buffer.end());
}
void boxFilter(T* input, T* output, unsigned w, unsigned h, unsigned d, unsigned filterSize)
{
assertMsg((filterSize % 2 == 1), "filterSize must be odd");
unsigned radius = filterSize / 2;
ImageView3D<T> in (w, h, d, input);
ImageView3D<T> out(w, h, d, output);
boxBlur(in, out, radius, radius, radius);
}
void approxGaus(Image3D<T>& input, _Point3<T> sigma, unsigned nFilt)
{
approxGaus(input.data(), input.getWidth(), input.getHeight(), input.getDepth(), sigma, nFilt);
}
void approxGaus(std::vector<T>& input, unsigned w, unsigned h, unsigned d, _Point3<T> sigma, unsigned nFilt)
{
std::vector<T> buffer(input.size());
approxGaus(input.data(), buffer.data(), w, h, d, sigma, nFilt);
input.assign(buffer.begin(), buffer.end());
}
void approxGaus(T* input, T* output, unsigned w, unsigned h, unsigned d, _Point3<T> sigma, unsigned nFilt)
{
ImageView3D<T> in (w, h, d, input);
ImageView3D<T> out(w, h, d, output);
approxGaus(in, out, sigma, nFilt);
}
void approxGaus(ImageView3D<T>& in, ImageView3D<T>& out, _Point3<T> sigma, unsigned nFilt)
{
BoxSizes<T> bsX(sigma.x, nFilt);
BoxSizes<T> bsY(sigma.y, nFilt);
BoxSizes<T> bsZ(sigma.z, nFilt);
assertMsg((nFilt % 2 == 1), "nFilt must be odd");
// if equal, we can save some cond's inside the loop
if (bsX.m == bsY.m && bsX.m == bsZ.m)
{
const size_t m = bsX.m;
for (size_t i = 1; i <= m; i++)
{
if (i % 2) {
boxBlur(in, out, bsX.wl, bsY.wl, bsZ.wl);
} else {
boxBlur(out, in, bsX.wl, bsY.wl, bsZ.wl);
}
}
for (size_t i = 1; i <= nFilt - m; i++)
{
if (i % 2) {
boxBlur(in, out, bsX.wu, bsY.wu, bsZ.wu);
} else {
boxBlur(out, in, bsX.wu, bsY.wu, bsZ.wu);
}
}
}
else
{
for (size_t i = 1; i <= nFilt; i++)
{
const size_t rX = (i < bsX.m ? bsX.wl : bsX.wu);
const size_t rY = (i < bsY.m ? bsY.wl : bsY.wu);
const size_t rZ = (i < bsZ.m ? bsZ.wl : bsZ.wu);
if (i % 2) {
boxBlur(in, out, rX, rY, rZ);
} else{
boxBlur(out, in, rX, rY, rZ);
}
}
}
}
private:
void boxBlur(ImageView3D<T>& in, ImageView3D<T>& out, size_t rX, size_t rY, size_t rZ)
{
//box(in, out, r);
boxBlurX(in, out, rX);
boxBlurY(out, in, rY);
boxBlurZ(in, out, rZ);
}
void boxBlurX(const ImageView3D<T>& src, ImageView3D<T>& dst, size_t r)
{
T iarr = (T)1.0 / (r + r + 1);
for (size_t z = 0; z < src.getDepth(); z++)
{
for (size_t y = 0; y < src.getHeight(); y++)
{
auto srcView = src.getViewYZ(y, z);
auto dstView = dst.getViewYZ(y, z);
boxBlur1D<SlicePlane::YZ>(srcView, dstView, src.getWidth(), r, iarr);
}
}
}
void boxBlurY(const ImageView3D<T>& src, ImageView3D<T>& dst, size_t r)
{
T iarr = (T)1.0 / (r + r + 1);
for (size_t x = 0; x < src.getWidth(); x++)
{
for (size_t z = 0; z < src.getDepth(); z++)
{
auto srcView = src.getViewXZ(x, z);
auto dstView = dst.getViewXZ(x, z);
boxBlur1D<SlicePlane::XZ>(srcView, dstView, src.getHeight(), r, iarr);
}
}
}
void boxBlurZ(const ImageView3D<T>& src, ImageView3D<T>& dst, size_t r)
{
T iarr = (T)1.0 / (r + r + 1);
for (size_t x = 0; x < src.getWidth(); x++)
{
for (size_t y = 0; y < src.getHeight(); y++)
{
auto srcView = src.getViewXY(x, y);
auto dstView = dst.getViewXY(x, y);
boxBlur1D<SlicePlane::XY>(srcView, dstView, src.getDepth(), r, iarr);
}
}
}
template<SlicePlane S>
void boxBlur1D(const ImageView3D<T>::ConstLineView<S>& src, ImageView3D<T>::LineView<S>& dst, size_t len, size_t r, T iarr)
{
size_t li = 0; // left index
size_t ri = r; // right index
T fv = src(0);
T lv = src(len - 1);
T val = (r + 1)*fv;
for (size_t i = 0; i < r; i++)
val += src(i);
// <20>berhangbereich links vom Bild
for (size_t i = 0; i <= r; i++)
{
val += src(ri++) - fv;
dst(i) = val*iarr;
}
// Bildbereich
for (size_t i = r + 1; i < len - r; i++)
{
val += src(ri++) - src(li++);
dst(i) = val*iarr;
}
// <20>berhangbereich rechts vom Bild
for (size_t i = len - r; i < len; i++)
{
val += lv - src(li++);
dst(i) = val*iarr;
}
}
void box(const ImageView3D<T>& src, ImageView3D<T>& dst, size_t r)
{
T iarr = (T)1.0 / (r + r + 1);
ImageView3D<T> buffer(src);
ImageView3D<T> buffer2(dst.getHeight(), dst.getDepth(), dst.getWidth(), dst.val_begin());
for (size_t i = 0; i < 3; i++)
{
for (size_t idx2 = 0; idx2 < buffer.getHeight(); idx2++)
{
for (size_t idx3 = 0; idx3 < buffer.getDepth(); idx3++)
{
box1D(buffer, buffer2, buffer.getWidth(), idx2, idx3, r, iarr);
}
}
// reinteprete buffer sizes after rotation and swap data pointers
buffer = ImageView3D<T>(buffer.getHeight(), buffer.getDepth(), buffer.getWidth(), buffer.val_begin());
buffer2 = ImageView3D<T>(buffer2.getHeight(), buffer2.getDepth(), buffer2.getWidth(), buffer2.val_begin());
buffer.swap(buffer2);
}
buffer.swap(dst);
}
void box1D(const ImageView3D<T>& src, ImageView3D<T>& dst, size_t len, size_t idx2, size_t idx3, size_t r, T iarr)
{
for (size_t i = 0; i < len; i++)
{
// dst(idx2, idx3, i) = src(i, idx2, idx3);
size_t li = 0; // left index
size_t ri = r; // right index
T fv = src(0, idx2, idx3);
T lv = src(len - 1, idx2, idx3);
T val = (r + 1)*fv;
for (size_t i = 0; i < r; i++)
val += src(i, idx2, idx3);
// <20>berhangbereich links vom Bild
for (size_t i = 0; i <= r; i++)
{
val += src(ri++, idx2, idx3) - fv;
dst(idx2, idx3, i) = val*iarr;
}
// Bildbereich
for (size_t i = r + 1; i < len - r; i++)
{
val += src(ri++, idx2, idx3) - src(li++, idx2, idx3);
dst(idx2, idx3, i) = val*iarr;
}
// <20>berhangbereich rechts vom Bild
for (size_t i = len - r; i < len; i++)
{
val += lv - src(li++, idx2, idx3);
dst(idx2, idx3, i) = val*iarr;
}
}
}
};
template struct BoxGaus3D<float>;
template struct BoxGaus3D<double>;

View File

@@ -1,6 +1,5 @@
#pragma once
#include "DataStructures.h"
template<typename T>
struct BoxSizes

182
math/boxkde/Grid3D.h Normal file
View File

@@ -0,0 +1,182 @@
#pragma once
//#include "DataStructures.h"
#include "Image3D.h"
#include "../../geo/Point3.h"
#include "../../geo/BBox3.h"
template<class T>
struct Grid3D
{
static_assert(std::is_floating_point<T>::value, "Grid3D only supports float values");
public:
const _BBox3<T> bb;
const size_t numBinsX, numBinsY, numBinsZ;
const T binSizeX, binSizeY, binSizeZ;
private:
Image3D<T> data;
bool empty;
public:
Grid3D(_BBox3<T> bb, size_t numBinsAll)
: Grid3D(bb, numBinsAll, numBinsAll, numBinsAll)
{}
Grid3D(_BBox3<T> bb, size_t numBinsX, size_t numBinsY, size_t numBinsZ)
: bb(bb),
numBinsX(numBinsX),
numBinsY(numBinsY),
numBinsZ(numBinsZ),
binSizeX((bb.getSize().x + 1) / numBinsX),
binSizeY((bb.getSize().y + 1) / numBinsY),
binSizeZ((bb.getSize().z + 1) / numBinsZ),
data(numBinsX, numBinsY, numBinsZ),
empty(false)
{
}
Image3D<T>& image() { return data; }
const Image3D<T>& image() const { return data; }
T& operator() (size_t x, size_t y, size_t z) { return data(x, y, z); }
const T& operator() (size_t x, size_t y, size_t z) const { return data(x, y, z); }
void clear() { data.clear(); }
void fill(const std::vector<_Point3<T>>& samples)
{
assertMsg(!samples.empty(), "Samples must be non-empty");
if (!empty)
data.clear();
const T weight = T(1.0) / samples.size();
for (const auto& pt : samples)
{
add(pt.x, pt.y, pt.z, weight);
}
empty = false;
}
void fill(const std::vector<_Point3<T>>& samples, const std::vector<T>& weights)
{
assertMsg(!samples.empty(), "Samples must be non-empty");
assertMsg(weights.size() == samples.size(), "Weights must have the same size as samples");
if (!empty)
data.clear();
for (size_t i = 0; i < samples.size(); i++)
{
add(samples[i].x, samples[i].y, samples[i].z, weights[i]);
}
empty = false;
}
_Point3<size_t> add(_Point3<T> pt, T w)
{
return add(pt.x, pt.y, pt.z, w);
}
_Point3<size_t> add(T x, T y, T z, T w)
{
if (assertCond(bb.contains(_Point3<T>(x, y, z))))
{
std::stringstream ss;
ss << "Point " << "(" << x << "; " << y << "; " << z << ")" << " is out of bounds: "
<< "(X: " << bb.getMin().x << " - " << bb.getMax().x << ";"
<< " Y: " << bb.getMin().y << " - " << bb.getMax().y << ";"
<< " Z: " << bb.getMin().z << " - " << bb.getMax().z << ")";
assertThrow(ss.str());
}
return add_simple_bin(x, y, z, w);
}
T fetch(T x, T y, T z) const
{
size_t bin_x = (size_t)((x - bb.getMin().x) / binSizeX);
size_t bin_y = (size_t)((y - bb.getMin().y) / binSizeY);
size_t bin_z = (size_t)((z - bb.getMin().z) / binSizeZ);
return data(bin_x, bin_y, bin_z);
}
T maximum(_Point3<T>& pt) const
{
_Point3<size_t> gridPt;
T maxValue = image().maximum(gridPt);
pt = asInputSpace(gridPt);
return maxValue;
}
// // Takes a point in input space and converts it to grid space coordinates
//_Point3<size_t> asGridSpace(T x, T y, T z) const
// {
// size_t bin_x = (size_t)((x - bb.MinX) / binSizeX);
// size_t bin_y = (size_t)((y - bb.MinY) / binSizeY);
// size_t bin_z = (size_t)((z - bb.MinZ) / binSizeZ);
// if (bin_x == data.width) bin_x--;
// if (bin_y == data.height) bin_y--;
// if (bin_z == data.depth) bin_z--;
// return Point3D<size_t>(bin_x, bin_y, bin_z);
// }
// // Takes a Size2D in input space and converts it to grid space coordiantes
//_Point3<size_t> asGridSpace(Size3D<T> sz) const
// {
// return _Point3<size_t>(sz.sX / binSizeX, sz.sY / binSizeY, sz.sZ / binSizeZ);
// }
// // Takes a Range2D in input space and converts it to grid space coordinates
// Range3D<size_t> asGridSpace(Range3D<T> range) const
// {
// Point3D<size_t> startPt = asGridSpace(range.X.Start, range.Y.Start, range.Z.Start);
// size_t lengthX = range.X.Length / binSizeX;
// size_t lengthY = range.Y.Length / binSizeY;
// size_t lengthZ = range.Z.Length / binSizeZ;
// return Range3D<size_t>(startPt.X, lengthX, startPt.Y, lengthY, startPt.Z, lengthZ);
// }
// Takes a point in grid space and converts it to input space coordinates
_Point3<T> asInputSpace(_Point3<size_t> pt) const
{
return asInputSpace(pt.x, pt.y, pt.z);
}
// Takes a point in grid space and converts it to input space coordinates
_Point3<T> asInputSpace(size_t x, size_t y, size_t z) const
{
return _Point3<T>(x * binSizeX + bb.getMin().x + T(0.5)*binSizeX,
y * binSizeY + bb.getMin().y + T(0.5)*binSizeY,
z * binSizeZ + bb.getMin().z + T(0.5)*binSizeZ);
}
private:
_Point3<size_t> add_simple_bin(T x, T y, T z, T w)
{
size_t bin_x = (size_t)((x - bb.getMin().x) / binSizeX);
size_t bin_y = (size_t)((y - bb.getMin().y) / binSizeY);
size_t bin_z = (size_t)((z - bb.getMin().z) / binSizeZ);
//if (bin_x == data.width) bin_x--;
//if (bin_y == data.height) bin_y--;
//if (bin_z == data.depth) bin_z--;
data(bin_x, bin_y, bin_z) += w;
return _Point3<size_t>(bin_x, bin_y, bin_z);
}
};
template struct Grid3D<float>;
template struct Grid3D<double>;

208
math/boxkde/Image3D.h Normal file
View File

@@ -0,0 +1,208 @@
#pragma once
#include <cassert>
#include <numeric>
#include <vector>
#include "../../geo/Point3.h"
enum struct SlicePlane { XY, XZ, YZ };
template <class TValue>
struct ImageView3D
{
static_assert(std::is_arithmetic<TValue>::value, "ImageView3D only supports integers or floats");
// forward declaration
template<SlicePlane S> struct LineView;
template<SlicePlane S> struct ConstLineView;
private:
size_t width, height, depth, size; // TODO private
protected:
TValue* values; // contains image data row-wise
public:
ImageView3D()
: width(0), height(0), depth(0), size(0), values(nullptr)
{
}
ImageView3D(size_t width, size_t height, size_t depth)
: width(width), height(height), depth(depth), size(width*height*depth), values(nullptr)
{
}
ImageView3D(size_t width, size_t height, size_t depth, TValue* data)
: width(width), height(height), depth(depth), size(width*height*depth), values(data)
{
}
TValue& operator() (size_t x, size_t y, size_t z) { return values[indexFromCoord(x, y, z)]; }
const TValue& operator() (size_t x, size_t y, size_t z) const { return values[indexFromCoord(x, y, z)]; }
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; }
size_t getWidth() const { return width; }
size_t getHeight() const { return height; }
size_t getDepth() const { return depth; }
size_t indexFromCoord(size_t x, size_t y, size_t z) const
{
assertMsg(x < width && y < height && z < depth, "(x,y,z) out of bounds");
return z*width*height + y*width + x;
}
_Point3<size_t> coordFromIndex(size_t index) const
{
assertMsg(index < size, "Index out of bounds");
size_t z = index / (width*height);
size_t y = (index - z*width*height) / width;
size_t x = index - y*width - z*width*height;
return _Point3<size_t>(x, y, z);
}
_Point3<size_t> maximum() const
{
size_t maxValueIndex = std::distance(val_begin(), std::max_element(val_begin(), val_end()));
return coordFromIndex(maxValueIndex);
}
TValue maximum(_Point3<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()
{
std::fill(val_begin(), val_end(), TValue(0));
}
void assign(const ImageView3D<TValue>& other)
{
assertMsg(size == other.size, "Other must be of 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));
}
void swap(ImageView3D<TValue>& other)
{
TValue* tmp = other.values;
other.values = this->values;
this->values = tmp;
}
public:
typedef LineView<SlicePlane::XY> LineViewXY;
typedef LineView<SlicePlane::XZ> LineViewXZ;
typedef LineView<SlicePlane::YZ> LineViewYZ;
typedef ConstLineView<SlicePlane::XY> ConstLineViewXY;
typedef ConstLineView<SlicePlane::XZ> ConstLineViewXZ;
typedef ConstLineView<SlicePlane::YZ> ConstLineViewYZ;
LineViewXY getViewXY(size_t fixedX, size_t fixedY) { return LineViewXY(*this, fixedX, fixedY); }
LineViewXZ getViewXZ(size_t fixedX, size_t fixedZ) { return LineViewXZ(*this, fixedX, fixedZ); }
LineViewYZ getViewYZ(size_t fixedY, size_t fixedZ) { return LineViewYZ(*this, fixedY, fixedZ); }
ConstLineViewXY getViewXY(size_t fixedX, size_t fixedY) const { return ConstLineViewXY(*this, fixedX, fixedY); }
ConstLineViewXZ getViewXZ(size_t fixedX, size_t fixedZ) const { return ConstLineViewXZ(*this, fixedX, fixedZ); }
ConstLineViewYZ getViewYZ(size_t fixedY, size_t fixedZ) const { return ConstLineViewYZ(*this, fixedY, fixedZ); }
template<SlicePlane S>
struct LineView
{
size_t fixIdx1, fixIdx2;
ImageView3D<TValue>& img;
LineView(ImageView3D<TValue>& img, size_t fixedIndex1, size_t fixedIndex2)
: fixIdx1(fixedIndex1), fixIdx2(fixedIndex2), img(img)
{}
TValue& operator() (size_t idx)
{
switch (S)
{
case SlicePlane::XY: return img(fixIdx1, fixIdx2, idx);
case SlicePlane::XZ: return img(fixIdx1, idx, fixIdx2);
case SlicePlane::YZ: return img(idx, fixIdx1, fixIdx2);
default: assert(false); abort();
}
}
};
template<SlicePlane S>
struct ConstLineView
{
size_t fixIdx1, fixIdx2;
const ImageView3D<TValue>& img;
ConstLineView(const ImageView3D<TValue>& img, size_t fixedIndex1, size_t fixedIndex2)
: fixIdx1(fixedIndex1), fixIdx2(fixedIndex2), img(img)
{}
const TValue& operator() (size_t idx)
{
switch (S)
{
case SlicePlane::XY: return img(fixIdx1, fixIdx2, idx);
case SlicePlane::XZ: return img(fixIdx1, idx, fixIdx2);
case SlicePlane::YZ: return img(idx, fixIdx1, fixIdx2);
default: assert(false); abort();
}
}
const TValue& operator() (size_t idx) const
{
switch (S)
{
case SlicePlane::XY: return img(fixIdx1, fixIdx2, idx);
case SlicePlane::XZ: return img(fixIdx1, idx, fixIdx2);
case SlicePlane::YZ: return img(idx, fixIdx1, fixIdx2);
default: assert(false); abort();
}
}
};
};
template <class TValue>
struct Image3D : public ImageView3D<TValue>
{
std::vector<TValue> values_vec;
public:
Image3D(size_t width, size_t height, size_t depth)
: ImageView3D<TValue>(width, height, depth), values_vec(width * height * depth)
{
this->values = values_vec.data();
}
Image3D(size_t width, size_t height, size_t depth, std::vector<TValue>& data)
: ImageView3D<TValue>(width, height, depth), values_vec(data)
{
assertMsg(data.size() == width*height*depth, "Data must be of the same size as this");
this->values = values_vec.data();
}
std::vector<TValue>& data() { return values_vec; }
const std::vector<TValue>& data() const { return values_vec; }
};
template struct ImageView3D<float>;
//template struct ImageView3D<double>;
template struct Image3D<float>;
//template struct Image3D<double>;