You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/GVision/SurfaceGrid/lib/ASGridInterpolatorBlock.h

652 lines
21 KiB
C++

/*------------------------------------------------------------------------------
* Copyright (c) 2023 by Bai Bing (seread@163.com)
* See COPYING file for copying and redistribution conditions.
*
* Alians IT Studio.
*----------------------------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cstring>
#include <numeric>
#include <set>
#include <utility>
#include <vector>
#include "ASMatrix.h"
#include "ASSlice.h"
namespace ais
{
// Quick value access for ais::FMatrix while interpolating
template <typename dtype = double, class Allocator = std::allocator<dtype>>
struct GridInterpolatorBlock
{
GridInterpolatorBlock()
{
clear();
}
//============================================================================
// Method Description:
/// Build a block of values from matrix, and center point at row and col
///
/// @param value
///
GridInterpolatorBlock(dtype &value)
{
clear();
for (int i = 0; i < 25; i++)
{
values[i] = value;
}
}
//============================================================================
// Method Description:
/// Build a block of values from matrix, and center point at row and col
///
/// @param matrix, source extended matrix
/// @param row, col block center point in extended matrix positions (uj, ui) = (j+2, i+2)
///
GridInterpolatorBlock(const ais::Matrix<dtype> &matrixExtended, size_t uj, size_t ui)
{
copy(matrixExtended, uj, ui);
}
//============================================================================
// Method Description:
/// copy matrix values to block, and center point at row and col
///
/// @param matrix, source extended matrix
/// @param row, col block center point in extended matrix positions (uj, ui) = (j+2, i+2)
///
GridInterpolatorBlock &copy(const ais::Matrix<dtype> &matrixExtended, size_t uj, size_t ui)
{
clear();
for (size_t j = uj - 2; j != uj + 3; ++j)
{
auto begin = j * matrixExtended.columns() + ui - 2;
auto end = begin + 5;
for (size_t pos = begin; pos < end; pos++)
{
size_t boxPos = (j - uj + 2) * 5 + pos - begin;
values[boxPos] = matrixExtended[pos];
}
}
return *this;
}
inline void clear() { std::memset(values, 0, 25 * sizeof(dtype)); }
//============================================================================
// Method Description:
/// Get the value at the specified offset position, center point of this block is (0, 0)
/// 2 top (2, 0) (row, col)
/// 1
/// -2 -1 0 1 2 left (0, -2) center (0, 0) right (0, 2)
/// -1
/// -2 bottom (-2, 0)
///
/// @param row, col
///
dtype &operator()(int8_t row, int8_t col) noexcept
{
size_t pos = (row + 2) * 5 + (col + 2);
return values[pos];
}
const dtype &operator()(int8_t row, int8_t col) const noexcept
{
size_t pos = (row + 2) * 5 + (col + 2);
return values[pos];
}
dtype &operator[](size_t pos)
{
return values[pos];
}
const dtype &operator[](size_t pos) const
{
return values[pos];
}
inline const size_t index(int8_t row, int8_t col) const
{
size_t index = (row + 2) * 5 + (col + 2);
if (index > 24)
{
std::string msg = "Out of range";
THROW_INVALID_ARGUMENT(msg);
}
return index;
}
inline const std::pair<int8_t, int8_t> &pos_pair(size_t index) const
{
if (index > 24)
{
std::string msg = "Out of range";
THROW_INVALID_ARGUMENT(msg);
}
auto row = int8_t(index / 5) - 2;
auto col = int8_t(index % 5) - 2;
std::pair<int8_t, int8_t> ret = std::make_pair(row, col);
return ret;
}
inline double avg()
{
double sum = 0.0;
size_t count = 0;
for (auto &d : values)
{
if ((!std::isnan(d)) && (!std::isinf(d)))
{
sum += d;
count++;
}
}
return count == 0 ? 0 : sum / count;
}
inline double mock_value(long mask)
{
double sum = 0.0;
size_t count = 0;
for (int i = 0; i < 25; i++)
{
if ((mask & (1 << i)) == 0)
{
sum += values[i];
count++;
}
}
return count == 0 ? 0 : sum / count;
}
inline static const std::set<std::pair<int32_t, int32_t>> validPos = {
{0, 0},
{0, 1},
{-1, 0},
{1, 0},
{0, -1},
{-1, 1},
{-1, -1},
{1, 1},
{1, -1},
{0, 2},
{-2, 0},
{2, 0},
{0, -2}};
inline static const std::set<std::pair<int32_t, int32_t>> validPos1 = {
{0, 1},
{-1, 0},
{1, 0},
{0, -1}};
inline static const std::set<std::pair<int32_t, int32_t>> validPos2 = {
{-1, 1},
{-1, -1},
{1, 1},
{1, -1}};
inline static const std::set<std::pair<int32_t, int32_t>> validPos3 = {
{0, 2},
{-2, 0},
{2, 0},
{0, -2}};
inline double mock_value2(long mask, int pos, double originValue = 0.0)
{
double sum = 0.0;
size_t count = 0;
for (int index = 0; index < 25; index++)
{
int32_t j = int32_t(index / 5) - 2;
int32_t i = index % 5 - 2;
std::pair<int32_t, int32_t> ppos = std::make_pair(i, j);
if ((mask & (1 << pos)) == 0 && validPos.find(ppos) != validPos.end())
{
sum += values[i];
count++;
}
}
return count == 0 ? originValue : sum / count;
}
inline double mock_value3(long mask, int pos)
{
double origin = values[pos];
double temp = std::nan("1");
double weight = 0.0;
for (int index = 0; index < 25; index++)
{
// use inverse distance weight to get mock value
int32_t j = int32_t(index / 5) - 2;
int32_t i = index % 5 - 2;
std::pair<int32_t, int32_t> ipos = std::make_pair(i, j);
if (((mask & (1 << index)) == 0) && validPos.find(ipos) != validPos.end())
{
// use valid block values to build the invalid node mock value.
if (index == 12)
{
temp += 2 * values[index];
weight += 2;
}
else if (validPos1.find(ipos) != validPos1.end())
{
temp += values[index];
weight += 1;
}
else if (validPos2.find(ipos) != validPos2.end())
{
temp += .7071 * values[index];
weight += .7071;
}
else if (validPos3.find(ipos) != validPos3.end())
{
temp += 0.5 * values[index];
weight += .5;
}
}
}
return std::isnan(temp) ? origin : (temp / weight);
}
inline double nodeInverseDistanceWeight(const std::pair<int32_t, int32_t> &ppos, const std::pair<int32_t, int32_t> &ipos)
{
double temp = std::nan("1");
if (ppos != ipos)
temp = std::sqrt((ppos.first - ipos.first) * (ppos.first - ipos.first) + (ppos.second - ipos.second) * (ppos.second - ipos.second));
return std::isnan(temp) ? 2 : 1 / temp;
}
inline double mock_value4(long mask, int pos, double weightLimit = 0.2)
{
// use the trand between p(0,0) and mirror point to build the mock value for point at pos
double origin = values[pos];
double temp = 0.0;
double weight = 0.0;
int32_t pj = int32_t(pos / 5) - 2;
int32_t pi = pos % 5 - 2;
std::pair<int32_t, int32_t> ppos = std::make_pair(pi, pj);
// use valid block values to build the invalid node mock value.
for (int index = 0; index < 25; index++)
{
// use inverse distance weight to get mock value
int32_t j = int32_t(index / 5) - 2;
int32_t i = index % 5 - 2;
std::pair<int32_t, int32_t> ipos = std::make_pair(i, j);
if (((mask & (1 << index)) == 0) && validPos.find(ipos) != validPos.end())
{
auto pweight = nodeInverseDistanceWeight(ppos, ipos);
if (pweight >= weightLimit)
{
temp += pweight * values[index];
weight += pweight;
}
}
}
return weight != 0.0 ? (temp / weight) : origin;
}
inline double mock_value5(long mask, int pos)
{
// use the trend between p(0,0) and mirror point to build the mock value for point at pos
double origin = values[pos];
double temp = 0.0;
double weight = 0.0;
int32_t pj = int32_t(pos / 5) - 2;
int32_t pi = pos % 5 - 2;
std::pair<int32_t, int32_t> ppos = std::make_pair(pi, pj);
// build mock node by nearest valid point mean
for (int i = -1; i < 2; i++)
{
int32_t ii = pi + i;
if (std::abs(ii) > 2)
continue;
for (int j = -1; j < 2; j++)
{
// loop the nearest point
int32_t ij = pj + j;
if (std::abs(ij) > 2)
continue;
auto index = (ij + 2) * 5 + (ii + 2);
if ((mask & (1 << index)) == 0)
{
temp += values[index];
weight++;
}
}
}
return weight != 0.0 ? (temp / weight) : origin;
}
inline double mock_value6(long mask, int pos)
{
// use the trend between p(0,0) and mirror point to build the mock value for point at pos
double origin = values[pos];
double temp = 0.0;
double weight = 0.0;
int32_t pj = int32_t(pos / 5) - 2;
int32_t pi = pos % 5 - 2;
std::pair<int32_t, int32_t> ppos = std::make_pair(pi, pj);
double mock_p_0_0 = mock_value3(mask, 12);
if (pos == 12)
{
// use mock 4 to build center value
temp = mock_p_0_0;
weight = 1;
}
else if (validPos3.find(ppos) != validPos3.end() || validPos1.find(ppos) != validPos1.end())
{
// build mirror point index
auto mi = pi, mj = pj;
if (std::abs(pj) > 0)
{
// for pj = +/- 2 or +/- 1
mj = -pj;
}
else
{
// for pi = +/- 2 or +/- 1
mi = -pi;
}
auto index = (mj + 2) * 5 + (mi + 2);
if ((mask & (1 << 12)) > 0 && (mask & (1 << index)) > 0)
{
// only make mock value while there p(0,0) and mirror point are valid
temp = 2 * values[12] - values[index];
weight = 1;
}
else
{
// use mock 3 to build center value
temp = mock_value3(mask, pos);
weight = 1;
}
}
else if (validPos2.find(ppos) != validPos2.end())
{
auto mi = -pi, mj = -pj;
auto index = (mj + 2) * 5 + (mi + 2);
if ((mask & (1 << 12)) > 0 && (mask & (1 << index)) > 0)
{
// only make mock value while there p(0,0) and mirror point are valid
temp = 2 * values[12] - values[index];
weight = 1;
}
else
{
// use mock 4 to build center value
temp = mock_value3(mask, pos);
weight = 1;
}
}
return weight != 0.0 ? (temp / weight) : origin;
}
inline double mock_value7(long mask, int pos)
{
double origin = values[pos];
double med;
std::vector<dtype> validData;
for (int i = 0; i < 25; i++)
{
if ((mask & (1 << i)) == 0)
{
validData.push_back(values[i]);
}
}
auto s = validData.size();
if (s == 0)
med = std::nan("-1");
else
{
auto m = validData.begin() + s / 2;
std::partial_sort(std::execution::par, validData.begin(), m, validData.end());
med = validData[s / 2];
}
return std::isnan(med) ? origin : med;
}
inline double mock_value8(long mask, int pos)
{
double origin = values[pos];
return mask & (1 << 12) ? origin : values[12];
}
inline double mock_value9(long mask, int pos, double alpha)
{
// make the origin data as input value
double temp = std::nan("1");
auto [row, col] = pos_pair(pos);
std::pair<int32_t, int32_t> ppos = std::make_pair(row, col);
if (pos == index(0, 0))
{
// for center point, use avg to smooth the isoline
temp = mock_value(mask);
}
else if (validPos1.find(ppos) != validPos1.end())
{
// inner extension edge (0, 1), (0, -1), (1, 0), (-1, 0)
auto centerIndex = index(0, 0);
auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col);
temp = 2 * values[centerIndex] - values[mirrorIndex];
}
else if (validPos2.find(ppos) != validPos2.end())
{
// inner corners (1, 1), (-1, -1), (1, -1), (-1, 1)
auto supportIndex1 = index(-row, col);
auto supportIndex2 = index(row, -col);
auto mirrorIndex = index(-row, -col);
temp = values[supportIndex1] + values[supportIndex2] - values[mirrorIndex];
}
else if (validPos3.find(ppos) != validPos3.end())
{
// exterior extension edge (2, 0), (-2, 0), (0, -2), (0, 2)
double alpha1 = alpha * alpha;
double alpha2 = -2 * (1 + alpha1);
double beta = 1 / alpha;
double beta1 = beta * beta;
double beta2 = -2 * (1 + beta1);
auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col);
double innerCornersValue = values[index(1, 1)] - values[index(-1, -1)];
double innerEdgeValue = 0.0;
double adjustValue = 0;
if (std::abs(row) != 0)
{
// top & bottom (2, 0), (-2, 0)
innerCornersValue += values[index(-1, 1)] - values[index(1, -1)];
innerCornersValue *= alpha1;
innerEdgeValue = alpha2 * (values[index(0, 1)] - values[index(0, -1)]);
}
else
{
// left & right (0, -2), (0, 2)
innerCornersValue += values[index(1, -1)] - values[index(1, -1)];
innerCornersValue *= beta1;
innerEdgeValue = beta2 * (values[index(1, 0)] - values[index(-1, 0)]);
}
adjustValue = innerCornersValue + innerEdgeValue;
adjustValue = (row + col) > 0 ? -adjustValue : adjustValue;
temp = values[mirrorIndex] + adjustValue;
}
else
{
temp = mock_value(mask);
}
return temp;
}
int get_fault_point_type(long mask, int pos)
{
// make the origin data as input value
double temp = std::nan("1");
auto [row, col] = pos_pair(pos);
if (std::abs(row) == std::abs(col))
{
// corner points
}
else if (std::abs(row) < std::abs(col))
{
// left or right
}
else if (std::abs(row) > std::abs(col))
{
// top or bottom
}
return 0;
}
inline double mock_value10(long mask, int pos, double alpha)
{
// make the origin data as input value
double temp = std::nan("1");
auto [row, col] = pos_pair(pos);
std::pair<int32_t, int32_t> ppos = std::make_pair(row, col);
if (pos == index(0, 0))
{
// for center point, use avg to smooth the isoline
temp = mock_value(mask);
}
// else if (validPos1.find(ppos) != validPos1.end())
//{
// // inner extension edge (0, 1), (0, -1), (1, 0), (-1, 0)
// auto centerIndex = index(0, 0);
// auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col);
// temp = 2 * values[centerIndex] - values[mirrorIndex];
//}
// else if (validPos2.find(ppos) != validPos2.end())
//{
// // inner corners (1, 1), (-1, -1), (1, -1), (-1, 1)
// auto supportIndex1 = index(-row, col);
// auto supportIndex2 = index(row, -col);
// auto mirrorIndex = index(-row, -col);
// temp = values[supportIndex1] + values[supportIndex2] - values[mirrorIndex];
//}
// else if (validPos3.find(ppos) != validPos3.end())
//{
// // exterior extension edge (2, 0), (-2, 0), (0, -2), (0, 2)
// double alpha1 = alpha * alpha;
// double alpha2 = -2 * (1 + alpha1);
// double beta = 1 / alpha;
// double beta1 = beta * beta;
// double beta2 = -2 * (1 + beta1);
// auto mirrorIndex = (std::abs(row) != 0) ? index(-row, col) : index(row, -col);
// double innerCornersValue = values[index(1, 1)] - values[index(-1, -1)];
// double innerEdgeValue = 0.0;
// double adjustValue = 0;
// if (std::abs(row) != 0)
// {
// // top & bottom (2, 0), (-2, 0)
// innerCornersValue += values[index(-1, 1)] - values[index(1, -1)];
// innerCornersValue *= alpha1;
// innerEdgeValue = alpha2 * (values[index(0, 1)] - values[index(0, -1)]);
// }
// else
// {
// // left & right (0, -2), (0, 2)
// innerCornersValue += values[index(1, -1)] - values[index(1, -1)];
// innerCornersValue *= beta1;
// innerEdgeValue = beta2 * (values[index(1, 0)] - values[index(-1, 0)]);
// }
// adjustValue = innerCornersValue + innerEdgeValue;
// adjustValue = (row + col) > 0 ? -adjustValue : adjustValue;
// temp = values[mirrorIndex] + adjustValue;
//}
// else
//{
// temp = mock_value(mask);
//}
return temp;
}
std::string str() const
{
std::stringstream ss;
for (size_t j = 0; j < 5; j++)
{
for (size_t i = 0; i < 5; i++)
{
ss << values[j * 5 + i] << " ";
}
ss << std::endl;
}
return ss.str();
}
void print() const
{
std::cout << *this;
}
friend std::ostream &operator<<(std::ostream &stream, const GridInterpolatorBlock<dtype> &block)
{
stream << block.str();
return stream;
}
private:
dtype values[25];
};
} // namespace ais