pathanimator/c++/algorithm/distancepath.cpp

134 lines
4.9 KiB
C++

/// \file algorithm/distancepath.cpp
/// \copyright 2022 Sascha Nitsch
/// Licenced under MIT license
/// \brief implementation of the DistancePath class
// system includes
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <utility>
// own includes
#include "distancepath.h"
namespace Algorithm {
DistancePath::DistancePath(int argc, const char* argv[]) {
if (argc < 6) {
printf("DistancePath needs min 6 arguments <weight for traversalable pixel> <weight for blocked pixel> <Threshhold to mark as traversable> <max Distance> <StartX> <StartY> [<StartX> <StartY>] ...");
} else {
m_weightPath = atof(argv[0]);
m_weightBlock = atof(argv[1]);
m_threshhold = atoi(argv[2]);
m_maxDistance = atof(argv[3]);
for (uint8_t i = 4; i < argc; i+=2) {
m_queue1.push_back((uint32_t)(atoi(argv[i]) << 16) + atoi(argv[i + 1]));
}
}
}
DistancePath::~DistancePath() {
}
inline void DistancePath::processPixel(uint8_t* inRAW, float* outRow, uint16_t x, uint16_t y, float lastValue, uint16_t width, std::list<uint32_t>* next, uint16_t* outPixels, uint16_t iteration) {
float *oR = &outRow[x];
uint32_t offset = (y * width + x) * 3;
float avg = 255 - (0.3333 * (inRAW[offset] + inRAW[offset + 1] + inRAW[offset + 2]));
float newValue = lastValue + avg * m_weightBlock + (255.0 - avg) * m_weightPath;
if (newValue > m_maxDistance) newValue = m_maxDistance;
if (newValue < *oR) {
next->push_back((x << 16) + y);
outPixels[offset + 1] = iteration;
*oR = newValue;
}
}
inline void DistancePath::processRow(uint8_t* inRAW, float* outRAW, uint16_t x, uint16_t y, float lastValue, uint16_t width, std::list<uint32_t>* next, uint16_t* outPixels, uint16_t iteration) {
float* outRow = &outRAW[y * width];
if (x > 0) {
processPixel(inRAW, outRow, x - 1, y, lastValue, width, next, outPixels, iteration);
}
processPixel(inRAW, outRow, x, y, lastValue, width, next, outPixels, iteration);
if (x < width - 1) {
processPixel(inRAW, outRow, x + 1, y, lastValue, width, next, outPixels, iteration);
}
}
int8_t DistancePath::process(Image* input, Image* output) {
std::list<uint32_t> queue2;
std::list<uint32_t>* last = &m_queue1;
std::list<uint32_t>* next = &queue2;
uint16_t width = input->getWidth();
uint16_t height = input->getHeight();
uint8_t* inRAW = input->getPixels8();
uint32_t end = width * height;
// allocate output storage and set to m_maxDistance
float* outRAW = reinterpret_cast<float*>(malloc(width * height * sizeof(float)));
for (uint32_t i = 0; i < end; ++i) {
outRAW[i] = m_maxDistance;
}
// set start pixels to distance of 0
uint16_t* outPixels = output->getPixels16();
for (uint32_t coordinate : m_queue1) {
uint16_t x = coordinate >> 16;
uint16_t y = coordinate & 0xFFFFFFFF;
outRAW[(width * y + x)] = 0;
}
uint16_t iteration = 1;
do {
// process pixel from last time
for (uint32_t coordinate : *last) {
uint16_t x = coordinate >> 16;
uint16_t y = coordinate & 0xFFFFFFFF;
float lastValue = outRAW[y * width + x];
if (y > 0) { // process row on top
processRow(inRAW, outRAW, x, y-1, lastValue, width, next, outPixels, iteration);
}
processRow(inRAW, outRAW, x, y, lastValue, width, next, outPixels, iteration);
if (y < height - 1) { // process row on bottom
processRow(inRAW, outRAW, x, y+1, lastValue, width, next, outPixels, iteration);
}
}
++iteration;
// clear out old list
last->clear();
// swap list to get new input queue
std::swap(last, next);
if (iteration > 10000) break;
} while (next->size() > 0);
printf("iterations: %i\n", iteration);
// find max value
float max = 0;
uint16_t threshhold = m_threshhold * 3; // multiply by 3 here saves many division from sum to average in the loops below
// could be optimized, but it is only run once for every pixel
for (uint32_t i = 0; i < end; ++i) {
uint32_t offset = i * 3;
uint16_t sum = (inRAW[offset] + inRAW[offset + 1] + inRAW[offset + 2]);
// only update max if it is larger than previous, but still smaller than our max distance and larger as the threshhold (visible path cheat)
if (outRAW[i] > max && outRAW[i] < m_maxDistance && (sum > threshhold)) max = outRAW[i];
}
float iterMult = 65535.0 / iteration; // multiplicator to map iteration from 0 to 65535
max = 65535.0 / max; // results in 0 ... 65535 range
// write result to output
uint32_t offset = 0;
for (uint32_t i = 0; i < end; ++i) {
uint16_t sum = (inRAW[offset] + inRAW[offset + 1] + inRAW[offset + 2]);
if (sum > threshhold) {
outPixels[offset] = outRAW[i] * max; // red = distance
outPixels[offset + 1] = iterMult * outPixels[offset + 1]; // green = iteration
outPixels[offset + 2] = 65535; // blue= a pathable pixel
} else {
outPixels[offset] = 0;
outPixels[offset + 1] = 0;
outPixels[offset + 2] = 0;
}
offset += 3;
}
free(outRAW);
return 0;
}
} // namespace Algorithm