|
shun-iwasawa |
13c4cf |
#include "iwa_tangentflowfx.h"
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
#include <qthreadpool></qthreadpool>
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
namespace {
|
|
shun-iwasawa |
13c4cf |
inline double dotProduct(const double2 v1, const double2 v2) {
|
|
shun-iwasawa |
13c4cf |
return v1.x * v2.x + v1.y * v2.y;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
inline double clamp01(double val) {
|
|
shun-iwasawa |
13c4cf |
return (val > 1.0) ? 1.0 : (val < 0.0) ? 0.0 : val;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
const int MAX_OFFSET = 10000;
|
|
shun-iwasawa |
13c4cf |
}; // namespace
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
// obtain source tile brightness, normalizing 0 to 1
|
|
shun-iwasawa |
13c4cf |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
13c4cf |
void Iwa_TangentFlowFx::setSourceTileToBuffer(const RASTER srcRas,
|
|
shun-iwasawa |
13c4cf |
double* buf) {
|
|
shun-iwasawa |
13c4cf |
double* buf_p = buf;
|
|
shun-iwasawa |
13c4cf |
for (int j = 0; j < srcRas->getLy(); j++) {
|
|
shun-iwasawa |
13c4cf |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
13c4cf |
for (int i = 0; i < srcRas->getLx(); i++, pix++, buf_p++) {
|
|
shun-iwasawa |
13c4cf |
// Value = 0.3R 0.59G 0.11B
|
|
shun-iwasawa |
13c4cf |
(*buf_p) = (double(pix->r) * 0.3 + double(pix->g) * 0.59 +
|
|
shun-iwasawa |
13c4cf |
double(pix->b) * 0.11) /
|
|
shun-iwasawa |
13c4cf |
double(PIXEL::maxChannelValue);
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
void Iwa_TangentFlowFx::alignFlowDirection(double2* flow_buf,
|
|
shun-iwasawa |
13c4cf |
const TDimension dim,
|
|
shun-iwasawa |
13c4cf |
const double2& pivotVec) {
|
|
shun-iwasawa |
13c4cf |
double2* fbuf_p = flow_buf;
|
|
shun-iwasawa |
13c4cf |
for (int i = 0; i < dim.lx * dim.ly; i++, fbuf_p++) {
|
|
shun-iwasawa |
13c4cf |
// 基準ベクトルに揃える
|
|
shun-iwasawa |
13c4cf |
if ((*fbuf_p).x * pivotVec.x + (*fbuf_p).y * pivotVec.y < 0.0) {
|
|
shun-iwasawa |
13c4cf |
(*fbuf_p).x = -(*fbuf_p).x;
|
|
shun-iwasawa |
13c4cf |
(*fbuf_p).y = -(*fbuf_p).y;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
// render vector field in red & green channels
|
|
shun-iwasawa |
13c4cf |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
13c4cf |
void Iwa_TangentFlowFx::setOutputRaster(double2* flow_buf, double* grad_buf,
|
|
shun-iwasawa |
13c4cf |
const RASTER dstRas) {
|
|
shun-iwasawa |
13c4cf |
double2* fbuf_p = flow_buf;
|
|
shun-iwasawa |
13c4cf |
double* gbuf_p = grad_buf;
|
|
shun-iwasawa |
13c4cf |
for (int j = 0; j < dstRas->getLy(); j++) {
|
|
shun-iwasawa |
13c4cf |
PIXEL* pix = dstRas->pixels(j);
|
|
shun-iwasawa |
13c4cf |
for (int i = 0; i < dstRas->getLx(); i++, pix++, fbuf_p++, gbuf_p++) {
|
|
shun-iwasawa |
13c4cf |
double val;
|
|
shun-iwasawa |
13c4cf |
val = clamp01((*fbuf_p).x * 0.5 + 0.5) * (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
13c4cf |
pix->r = (typename PIXEL::Channel)(val);
|
|
shun-iwasawa |
13c4cf |
val = clamp01((*fbuf_p).y * 0.5 + 0.5) * (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
13c4cf |
pix->g = (typename PIXEL::Channel)(val);
|
|
shun-iwasawa |
13c4cf |
val = clamp01(*gbuf_p) * (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
13c4cf |
pix->b = (typename PIXEL::Channel)(val);
|
|
shun-iwasawa |
13c4cf |
pix->m = (typename PIXEL::Channel)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
// compute the initial vector field.
|
|
shun-iwasawa |
13c4cf |
// apply sobel filter, rotate by 90 degrees and normalize.
|
|
shun-iwasawa |
13c4cf |
// also obtaining gradient magnitude (length of the vector length)
|
|
shun-iwasawa |
13c4cf |
// empty regions will be filled by using Fast Sweeping Method
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
void SobelFilterWorker::run() {
|
|
shun-iwasawa |
13c4cf |
// the 5x5 Sobel filter is already rotated by 90 degrees.
|
|
shun-iwasawa |
13c4cf |
// ( i.e. converted as X = -y, Y = x )
|
|
shun-iwasawa |
13c4cf |
const double kernel_x[5][5] = {{0.25, 0.4, 0.5, 0.4, 0.25},
|
|
shun-iwasawa |
13c4cf |
{0.2, 0.5, 1., 0.5, 0.2},
|
|
shun-iwasawa |
13c4cf |
{0., 0., 0., 0., 0.},
|
|
shun-iwasawa |
13c4cf |
{-0.2, -0.5, -1., -0.5, -0.2},
|
|
shun-iwasawa |
13c4cf |
{-0.25, -0.4, -0.5, -0.4, -0.25}};
|
|
shun-iwasawa |
13c4cf |
const double kernel_y[5][5] = {{-0.25, -0.2, 0., 0.2, 0.25},
|
|
shun-iwasawa |
13c4cf |
{-0.4, -0.5, 0., 0.5, 0.4},
|
|
shun-iwasawa |
13c4cf |
{-0.5, -1., 0., 1., 0.5},
|
|
shun-iwasawa |
13c4cf |
{-0.4, -0.5, 0., 0.5, 0.4},
|
|
shun-iwasawa |
13c4cf |
{-0.25, -0.2, 0., 0.2, 0.25}};
|
|
shun-iwasawa |
13c4cf |
/*
|
|
shun-iwasawa |
13c4cf |
const double kernel_x[5][5] = {
|
|
shun-iwasawa |
13c4cf |
{ 0.0297619, 0.047619, 0.0595238, 0.047619, 0.0297619 },
|
|
shun-iwasawa |
13c4cf |
{ 0.0238, 0.0595238, 0.119, 0.0595238, 0.0238 },
|
|
shun-iwasawa |
13c4cf |
{ 0., 0., 0., 0., 0. },
|
|
shun-iwasawa |
13c4cf |
{ -0.0238, -0.0595238, -0.119, -0.0595238, -0.0238 },
|
|
shun-iwasawa |
13c4cf |
{ -0.0297619, -0.047619, -0.0595238, -0.047619, -0.0297619 }
|
|
shun-iwasawa |
13c4cf |
};
|
|
shun-iwasawa |
13c4cf |
const double kernel_y[5][5] = {
|
|
shun-iwasawa |
13c4cf |
{ -0.0297619, -0.0238, 0., 0.0238, 0.0297619 },
|
|
shun-iwasawa |
13c4cf |
{ -0.047619, -0.0595238, 0., 0.0595238, 0.047619 },
|
|
shun-iwasawa |
13c4cf |
{ -0.0595238, -0.119, 0., 0.119, 0.0595238 },
|
|
shun-iwasawa |
13c4cf |
{ -0.047619, -0.0595238, 0., 0.0595238, 0.047619 },
|
|
shun-iwasawa |
13c4cf |
{ -0.0297619, -0.0238, 0., 0.0238, 0.0297619 }
|
|
shun-iwasawa |
13c4cf |
};
|
|
shun-iwasawa |
13c4cf |
*/
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
auto source = [&](int xPos, int yPos) {
|
|
shun-iwasawa |
13c4cf |
return m_source_buf[yPos * m_dim.lx + xPos];
|
|
shun-iwasawa |
13c4cf |
};
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
double2* flow_p = &m_flow_buf[m_yFrom * m_dim.lx];
|
|
shun-iwasawa |
13c4cf |
double* grad_mag_p = &m_grad_mag_buf[m_yFrom * m_dim.lx];
|
|
shun-iwasawa |
13c4cf |
int2* offset_p = &m_offset_buf[m_yFrom * m_dim.lx];
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
for (int y = m_yFrom; y < m_yTo; y++) {
|
|
shun-iwasawa |
13c4cf |
for (int x = 0; x < m_dim.lx; x++, flow_p++, grad_mag_p++, offset_p++) {
|
|
shun-iwasawa |
13c4cf |
double x_accum = 0.;
|
|
shun-iwasawa |
13c4cf |
double y_accum = 0.;
|
|
shun-iwasawa |
13c4cf |
for (int ky = 0; ky < 5; ky++) {
|
|
shun-iwasawa |
13c4cf |
int yPos = y + ky - 2; // y coordinate of sample pos
|
|
shun-iwasawa |
13c4cf |
if (yPos < 0) continue;
|
|
shun-iwasawa |
13c4cf |
if (yPos >= m_dim.ly) break;
|
|
shun-iwasawa |
13c4cf |
for (int kx = 0; kx < 5; kx++) {
|
|
shun-iwasawa |
13c4cf |
int xPos = x + kx - 2; // x coordinate of sample pos
|
|
shun-iwasawa |
13c4cf |
if (xPos < 0) continue;
|
|
shun-iwasawa |
13c4cf |
if (xPos >= m_dim.lx) break;
|
|
shun-iwasawa |
13c4cf |
if (kx == 2 && ky == 2) continue;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
double sourceVal = source(xPos, yPos);
|
|
shun-iwasawa |
13c4cf |
x_accum += kernel_x[ky][kx] * sourceVal;
|
|
shun-iwasawa |
13c4cf |
y_accum += kernel_y[ky][kx] * sourceVal;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
// storing Gradient Magnitude
|
|
shun-iwasawa |
13c4cf |
*grad_mag_p = std::sqrt(x_accum * x_accum + y_accum * y_accum);
|
|
shun-iwasawa |
13c4cf |
(*flow_p).x = (*grad_mag_p == 0.) ? 0.0 : (x_accum / (*grad_mag_p));
|
|
shun-iwasawa |
13c4cf |
(*flow_p).y = (*grad_mag_p == 0.) ? 0.0 : (y_accum / (*grad_mag_p));
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// offset will be used later in Fast Sweeping Method
|
|
shun-iwasawa |
13c4cf |
if (*grad_mag_p < m_mag_threshold) {
|
|
shun-iwasawa |
13c4cf |
(*offset_p).x = MAX_OFFSET;
|
|
shun-iwasawa |
13c4cf |
(*offset_p).y = MAX_OFFSET;
|
|
shun-iwasawa |
13c4cf |
m_hasEmptyVector = true;
|
|
shun-iwasawa |
13c4cf |
} else {
|
|
shun-iwasawa |
13c4cf |
(*offset_p).x = 0;
|
|
shun-iwasawa |
13c4cf |
(*offset_p).y = 0;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
void Iwa_TangentFlowFx::computeInitialFlow(double* source_buf,
|
|
shun-iwasawa |
13c4cf |
double2* flow_buf,
|
|
shun-iwasawa |
13c4cf |
double* grad_mag_buf,
|
|
shun-iwasawa |
13c4cf |
const TDimension dim,
|
|
shun-iwasawa |
13c4cf |
double mag_threshold) {
|
|
shun-iwasawa |
13c4cf |
// empty regions will be filled by using Fast Sweeping Method
|
|
shun-iwasawa |
13c4cf |
// allocate offset buffer
|
|
shun-iwasawa |
13c4cf |
int2* offset_buf;
|
|
shun-iwasawa |
13c4cf |
TRasterGR8P offset_buf_ras(dim.lx * dim.ly * sizeof(int2), 1);
|
|
shun-iwasawa |
13c4cf |
offset_buf_ras->lock();
|
|
shun-iwasawa |
13c4cf |
offset_buf = (int2*)offset_buf_ras->getRawData();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
int activeThreadCount = QThreadPool::globalInstance()->activeThreadCount();
|
|
shun-iwasawa |
13c4cf |
// use half of the available threads
|
|
shun-iwasawa |
13c4cf |
int threadAmount = std::max(1, activeThreadCount / 2);
|
|
shun-iwasawa |
13c4cf |
QList<sobelfilterworker*> threadList;</sobelfilterworker*>
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
int tmpStart = 0;
|
|
shun-iwasawa |
13c4cf |
for (int t = 0; t < threadAmount; t++) {
|
|
shun-iwasawa |
13c4cf |
int tmpEnd =
|
|
shun-iwasawa |
13c4cf |
(int)std::round((float)(dim.ly * (t + 1)) / (float)threadAmount);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
SobelFilterWorker* worker =
|
|
shun-iwasawa |
13c4cf |
new SobelFilterWorker(source_buf, flow_buf, grad_mag_buf, offset_buf,
|
|
shun-iwasawa |
13c4cf |
mag_threshold, dim, tmpStart, tmpEnd);
|
|
shun-iwasawa |
13c4cf |
worker->start();
|
|
shun-iwasawa |
13c4cf |
threadList.append(worker);
|
|
shun-iwasawa |
13c4cf |
tmpStart = tmpEnd;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
bool hasEmptyVector = false;
|
|
shun-iwasawa |
13c4cf |
for (auto worker : threadList) {
|
|
shun-iwasawa |
13c4cf |
worker->wait();
|
|
shun-iwasawa |
13c4cf |
hasEmptyVector = hasEmptyVector | worker->hasEmptyVector();
|
|
shun-iwasawa |
13c4cf |
delete worker;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// return if there is no empty region
|
|
shun-iwasawa |
13c4cf |
if (!hasEmptyVector) {
|
|
shun-iwasawa |
13c4cf |
offset_buf_ras->unlock();
|
|
shun-iwasawa |
13c4cf |
return;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
auto getFlow = [&](int2 xy) { return &flow_buf[xy.y * dim.lx + xy.x]; };
|
|
shun-iwasawa |
13c4cf |
auto getOffset = [&](int2 xy) { return &offset_buf[xy.y * dim.lx + xy.x]; };
|
|
shun-iwasawa |
13c4cf |
auto getGradMag = [&](int2 xy) {
|
|
shun-iwasawa |
13c4cf |
return &grad_mag_buf[xy.y * dim.lx + xy.x];
|
|
shun-iwasawa |
13c4cf |
};
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// update vector field by using Fast Sweeping Method
|
|
shun-iwasawa |
13c4cf |
double2* neighbor_flow[2];
|
|
shun-iwasawa |
13c4cf |
int2* neighbor_offset[2];
|
|
shun-iwasawa |
13c4cf |
double* neighbor_gradMag[2];
|
|
shun-iwasawa |
13c4cf |
double2* flow_p;
|
|
shun-iwasawa |
13c4cf |
int2* offset_p;
|
|
shun-iwasawa |
13c4cf |
double* gradMag_p;
|
|
shun-iwasawa |
13c4cf |
int2 offset_incr[2];
|
|
shun-iwasawa |
13c4cf |
// positive/negative in y axis
|
|
shun-iwasawa |
13c4cf |
for (int ny = -1; ny <= 1; ny += 2) {
|
|
shun-iwasawa |
13c4cf |
offset_incr[0] = {0, ny};
|
|
shun-iwasawa |
13c4cf |
int y_start = (ny == 1) ? 1 : dim.ly - 2;
|
|
shun-iwasawa |
13c4cf |
int y_end = (ny == 1) ? dim.ly : 0;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// positive/negative in x axis
|
|
shun-iwasawa |
13c4cf |
for (int nx = -1; nx <= 1; nx += 2) {
|
|
shun-iwasawa |
13c4cf |
offset_incr[1] = {nx, 0};
|
|
shun-iwasawa |
13c4cf |
int x_start = (nx == 1) ? 1 : dim.lx - 2;
|
|
shun-iwasawa |
13c4cf |
int x_end = (nx == 1) ? dim.lx : 0;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
for (int y = y_start; y != y_end; y += ny) {
|
|
shun-iwasawa |
13c4cf |
int2 startPos(x_start, y);
|
|
shun-iwasawa |
13c4cf |
for (int n = 0; n < 2; n++) {
|
|
shun-iwasawa |
13c4cf |
neighbor_flow[n] = getFlow(startPos - offset_incr[n]);
|
|
shun-iwasawa |
13c4cf |
neighbor_offset[n] = getOffset(startPos - offset_incr[n]);
|
|
shun-iwasawa |
13c4cf |
neighbor_gradMag[n] = getGradMag(startPos - offset_incr[n]);
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
flow_p = getFlow(startPos);
|
|
shun-iwasawa |
13c4cf |
offset_p = getOffset(startPos);
|
|
shun-iwasawa |
13c4cf |
gradMag_p = getGradMag(startPos);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
for (int x = x_start; x != x_end; x += nx, neighbor_flow[0] += nx,
|
|
shun-iwasawa |
13c4cf |
neighbor_flow[1] += nx, flow_p += nx, neighbor_offset[0] += nx,
|
|
shun-iwasawa |
13c4cf |
neighbor_offset[1] += nx, offset_p += nx,
|
|
shun-iwasawa |
13c4cf |
neighbor_gradMag[0] += nx, neighbor_gradMag[1] += nx,
|
|
shun-iwasawa |
13c4cf |
gradMag_p += nx) {
|
|
shun-iwasawa |
13c4cf |
// continue if this pixel is already filled
|
|
shun-iwasawa |
13c4cf |
if ((*offset_p).x == 0 && (*offset_p).y == 0) continue;
|
|
shun-iwasawa |
13c4cf |
// for each neighbor pixel
|
|
shun-iwasawa |
13c4cf |
for (int n = 0; n < 2; n++) {
|
|
shun-iwasawa |
13c4cf |
// continue if the neighbor is still empty
|
|
shun-iwasawa |
13c4cf |
if ((*neighbor_offset[n]).x == MAX_OFFSET ||
|
|
shun-iwasawa |
13c4cf |
(*neighbor_offset[n]).y == MAX_OFFSET)
|
|
shun-iwasawa |
13c4cf |
continue;
|
|
shun-iwasawa |
13c4cf |
int2 tmpOffset = (*neighbor_offset[n]) + offset_incr[n];
|
|
shun-iwasawa |
13c4cf |
if (tmpOffset.len2() < (*offset_p).len2()) {
|
|
shun-iwasawa |
13c4cf |
(*offset_p) = tmpOffset;
|
|
shun-iwasawa |
13c4cf |
(*flow_p) = (*neighbor_flow[n]);
|
|
shun-iwasawa |
13c4cf |
(*gradMag_p) = (*neighbor_gradMag[n]);
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
offset_buf_ras->unlock();
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
Iwa_TangentFlowFx::Iwa_TangentFlowFx()
|
|
shun-iwasawa |
13c4cf |
: m_iteration(4)
|
|
shun-iwasawa |
13c4cf |
, m_kernelRadius(2.5)
|
|
shun-iwasawa |
13c4cf |
, m_threshold(0.15)
|
|
shun-iwasawa |
13c4cf |
, m_alignDirection(false)
|
|
shun-iwasawa |
13c4cf |
, m_pivotAngle(45.0) {
|
|
shun-iwasawa |
13c4cf |
addInputPort("Source", m_source);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
bindParam(this, "iteration", m_iteration);
|
|
shun-iwasawa |
13c4cf |
bindParam(this, "kernelRadius", m_kernelRadius);
|
|
shun-iwasawa |
13c4cf |
bindParam(this, "threshold", m_threshold);
|
|
shun-iwasawa |
13c4cf |
bindParam(this, "alignDirection", m_alignDirection);
|
|
shun-iwasawa |
13c4cf |
bindParam(this, "pivotAngle", m_pivotAngle);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
m_iteration->setValueRange(0, 10);
|
|
shun-iwasawa |
13c4cf |
m_kernelRadius->setMeasureName("fxLength");
|
|
shun-iwasawa |
13c4cf |
m_kernelRadius->setValueRange(0.5, 10);
|
|
shun-iwasawa |
13c4cf |
m_threshold->setValueRange(0.0, 1.0);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
m_pivotAngle->setValueRange(-180.0, 180.0);
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
void TangentFlowWorker::run() {
|
|
shun-iwasawa |
13c4cf |
// flow to be computed
|
|
shun-iwasawa |
13c4cf |
double2* flow_new_p = &m_flow_new_buf[m_yFrom * m_dim.lx];
|
|
shun-iwasawa |
13c4cf |
// current flow
|
|
shun-iwasawa |
13c4cf |
double2* flow_cur_p = &m_flow_cur_buf[m_yFrom * m_dim.lx];
|
|
shun-iwasawa |
13c4cf |
// Gradient Magnitude of the current pos
|
|
shun-iwasawa |
13c4cf |
double* grad_mag_p = &m_grad_mag_buf[m_yFrom * m_dim.lx];
|
|
shun-iwasawa |
13c4cf |
int kr2 = m_kernelRadius * m_kernelRadius;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// loop for Y
|
|
shun-iwasawa |
13c4cf |
for (int y = m_yFrom; y < m_yTo; y++) {
|
|
shun-iwasawa |
13c4cf |
// loop for X
|
|
shun-iwasawa |
13c4cf |
for (int x = 0; x < m_dim.lx;
|
|
shun-iwasawa |
13c4cf |
x++, flow_new_p++, grad_mag_p++, flow_cur_p++) {
|
|
shun-iwasawa |
13c4cf |
// accumulation vector
|
|
shun-iwasawa |
13c4cf |
double2 flow_new_accum;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// loop for kernel y
|
|
shun-iwasawa |
13c4cf |
for (int ky = -m_kernelRadius; ky <= m_kernelRadius; ky++) {
|
|
shun-iwasawa |
13c4cf |
int yPos = y + ky;
|
|
shun-iwasawa |
13c4cf |
// boundary condition
|
|
shun-iwasawa |
13c4cf |
if (yPos < 0) continue;
|
|
shun-iwasawa |
13c4cf |
if (yPos >= m_dim.ly) break;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// loop for kernel x
|
|
shun-iwasawa |
13c4cf |
for (int kx = -m_kernelRadius; kx <= m_kernelRadius; kx++) {
|
|
shun-iwasawa |
13c4cf |
int xPos = x + kx;
|
|
shun-iwasawa |
13c4cf |
// boundary condition
|
|
shun-iwasawa |
13c4cf |
if (xPos < 0) continue;
|
|
shun-iwasawa |
13c4cf |
if (xPos >= m_dim.lx) break;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// Eq.(2) continue if the sample pos is outside of the kernel
|
|
shun-iwasawa |
13c4cf |
if (kx * kx + ky * ky > kr2) continue;
|
|
shun-iwasawa |
13c4cf |
double2 flow_k = m_flow_cur_buf[yPos * m_dim.lx + xPos];
|
|
shun-iwasawa |
13c4cf |
if (flow_k.x == 0.0 && flow_k.y == 0.0) continue;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// Eq.(3) calculate the magnitude weight function (wm)
|
|
shun-iwasawa |
13c4cf |
double grad_mag_k = m_grad_mag_buf[yPos * m_dim.lx + xPos];
|
|
shun-iwasawa |
13c4cf |
double w_m = (1.0 + std::tanh(grad_mag_k - (*grad_mag_p))) / 2.0;
|
|
shun-iwasawa |
13c4cf |
if (w_m == 0.0) continue;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// Eq.(4) calculate the direction weight function (wd)
|
|
shun-iwasawa |
13c4cf |
double flowDot = dotProduct((*flow_cur_p), flow_k);
|
|
shun-iwasawa |
13c4cf |
double w_d = std::abs(flowDot);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// Eq.(5) calculate phi
|
|
shun-iwasawa |
13c4cf |
double phi = (flowDot > 0.) ? 1.0 : -1.0;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// accumulate vector
|
|
shun-iwasawa |
13c4cf |
flow_new_accum.x += phi * w_m * w_d * flow_k.x;
|
|
shun-iwasawa |
13c4cf |
flow_new_accum.y += phi * w_m * w_d * flow_k.y;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// normalize vector
|
|
shun-iwasawa |
13c4cf |
double len = std::sqrt(flow_new_accum.x * flow_new_accum.x +
|
|
shun-iwasawa |
13c4cf |
flow_new_accum.y * flow_new_accum.y);
|
|
shun-iwasawa |
13c4cf |
if (len != 0.0) {
|
|
shun-iwasawa |
13c4cf |
flow_new_accum.x /= len;
|
|
shun-iwasawa |
13c4cf |
flow_new_accum.y /= len;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// update vector
|
|
shun-iwasawa |
13c4cf |
(*flow_new_p).x = flow_new_accum.x;
|
|
shun-iwasawa |
13c4cf |
(*flow_new_p).y = flow_new_accum.y;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
void Iwa_TangentFlowFx::doCompute(TTile& tile, double frame,
|
|
shun-iwasawa |
13c4cf |
const TRenderSettings& settings) {
|
|
shun-iwasawa |
13c4cf |
if (!m_source.isConnected()) {
|
|
shun-iwasawa |
13c4cf |
tile.getRaster()->clear();
|
|
shun-iwasawa |
13c4cf |
return;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
TDimension dim = tile.getRaster()->getSize();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// std::cout << "Iwa_TangentFlowFx dim = (" << dim.lx << ", " << dim.ly <<
|
|
shun-iwasawa |
13c4cf |
// ")";
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
double fac = sqrt(fabs(settings.m_affine.det()));
|
|
shun-iwasawa |
13c4cf |
int kernelRadius = std::round(fac * m_kernelRadius->getValue(frame));
|
|
shun-iwasawa |
13c4cf |
if (kernelRadius == 0) kernelRadius = 1;
|
|
shun-iwasawa |
13c4cf |
int iterationCount = m_iteration->getValue();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
double mag_threshold = m_threshold->getValue(frame) / fac;
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
TTile sourceTile;
|
|
shun-iwasawa |
13c4cf |
m_source->allocateAndCompute(sourceTile, tile.m_pos, dim, tile.getRaster(),
|
|
shun-iwasawa |
13c4cf |
frame, settings);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// allocate source image buffer
|
|
shun-iwasawa |
13c4cf |
double* source_buf;
|
|
shun-iwasawa |
13c4cf |
TRasterGR8P source_buf_ras(dim.lx * dim.ly * sizeof(double), 1);
|
|
shun-iwasawa |
13c4cf |
source_buf_ras->lock();
|
|
shun-iwasawa |
13c4cf |
source_buf = (double*)source_buf_ras->getRawData();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// obtain source tile brightness, normalizing 0 to 1
|
|
shun-iwasawa |
13c4cf |
TRaster32P ras32 = tile.getRaster();
|
|
shun-iwasawa |
13c4cf |
TRaster64P ras64 = tile.getRaster();
|
|
shun-iwasawa |
13c4cf |
if (ras32)
|
|
shun-iwasawa |
13c4cf |
setSourceTileToBuffer<traster32p, tpixel32="">(sourceTile.getRaster(),</traster32p,>
|
|
shun-iwasawa |
13c4cf |
source_buf);
|
|
shun-iwasawa |
13c4cf |
else if (ras64)
|
|
shun-iwasawa |
13c4cf |
setSourceTileToBuffer<traster64p, tpixel64="">(sourceTile.getRaster(),</traster64p,>
|
|
shun-iwasawa |
13c4cf |
source_buf);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// allocate flow buffers (for both current and new)
|
|
shun-iwasawa |
13c4cf |
double2 *flow_cur_buf, *flow_new_buf;
|
|
shun-iwasawa |
13c4cf |
TRasterGR8P flow_cur_buf_ras(dim.lx * dim.ly * sizeof(double2), 1);
|
|
shun-iwasawa |
13c4cf |
TRasterGR8P flow_new_buf_ras(dim.lx * dim.ly * sizeof(double2), 1);
|
|
shun-iwasawa |
13c4cf |
flow_cur_buf_ras->lock();
|
|
shun-iwasawa |
13c4cf |
flow_new_buf_ras->lock();
|
|
shun-iwasawa |
13c4cf |
flow_cur_buf = (double2*)flow_cur_buf_ras->getRawData();
|
|
shun-iwasawa |
13c4cf |
flow_new_buf = (double2*)flow_new_buf_ras->getRawData();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// allocate Gradient Magnitude image buffer
|
|
shun-iwasawa |
13c4cf |
double* grad_mag_buf;
|
|
shun-iwasawa |
13c4cf |
TRasterGR8P grad_mag_buf_ras(dim.lx * dim.ly * sizeof(double), 1);
|
|
shun-iwasawa |
13c4cf |
grad_mag_buf_ras->lock();
|
|
shun-iwasawa |
13c4cf |
grad_mag_buf = (double*)grad_mag_buf_ras->getRawData();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// compute the initial vector field.
|
|
shun-iwasawa |
13c4cf |
// apply sobel filter, rotate by 90 degrees and normalize.
|
|
shun-iwasawa |
13c4cf |
// also obtaining gradient magnitude (length of the vector length)
|
|
shun-iwasawa |
13c4cf |
// empty regions will be filled by using Fast Sweeping Method
|
|
shun-iwasawa |
13c4cf |
computeInitialFlow(source_buf, flow_cur_buf, grad_mag_buf, dim,
|
|
shun-iwasawa |
13c4cf |
mag_threshold);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
source_buf_ras->unlock();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
int activeThreadCount = QThreadPool::globalInstance()->activeThreadCount();
|
|
shun-iwasawa |
13c4cf |
// use half of the available threads
|
|
shun-iwasawa |
13c4cf |
int threadAmount = std::max(1, activeThreadCount / 2);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// start iteration
|
|
shun-iwasawa |
13c4cf |
for (int i = 0; i < iterationCount; i++) {
|
|
shun-iwasawa |
13c4cf |
QList<qthread*> threadList;</qthread*>
|
|
shun-iwasawa |
13c4cf |
int tmpStart = 0;
|
|
shun-iwasawa |
13c4cf |
for (int t = 0; t < threadAmount; t++) {
|
|
shun-iwasawa |
13c4cf |
int tmpEnd =
|
|
shun-iwasawa |
13c4cf |
(int)std::round((float)(dim.ly * (t + 1)) / (float)threadAmount);
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
TangentFlowWorker* worker =
|
|
shun-iwasawa |
13c4cf |
new TangentFlowWorker(flow_cur_buf, flow_new_buf, grad_mag_buf, dim,
|
|
shun-iwasawa |
13c4cf |
kernelRadius, tmpStart, tmpEnd);
|
|
shun-iwasawa |
13c4cf |
worker->start();
|
|
shun-iwasawa |
13c4cf |
threadList.append(worker);
|
|
shun-iwasawa |
13c4cf |
tmpStart = tmpEnd;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
for (auto worker : threadList) {
|
|
shun-iwasawa |
13c4cf |
worker->wait();
|
|
shun-iwasawa |
13c4cf |
delete worker;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// swap buffer pointers
|
|
shun-iwasawa |
13c4cf |
double2* tmp = flow_cur_buf;
|
|
shun-iwasawa |
13c4cf |
flow_cur_buf = flow_new_buf;
|
|
shun-iwasawa |
13c4cf |
flow_new_buf = tmp;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
flow_new_buf_ras->unlock();
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// 基準の角度に向きを合わせる
|
|
shun-iwasawa |
13c4cf |
if (m_alignDirection->getValue()) {
|
|
shun-iwasawa |
13c4cf |
double pivotAngle =
|
|
shun-iwasawa |
13c4cf |
m_pivotAngle->getValue(frame) * M_PI_180; // convert to radian
|
|
shun-iwasawa |
13c4cf |
double2 pivotVec = {std::cos(pivotAngle), std::sin(pivotAngle)};
|
|
shun-iwasawa |
13c4cf |
alignFlowDirection(flow_cur_buf, dim, pivotVec);
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
// render vector field in red & green channels
|
|
shun-iwasawa |
13c4cf |
if (ras32)
|
|
shun-iwasawa |
13c4cf |
setOutputRaster<traster32p, tpixel32="">(flow_cur_buf, grad_mag_buf, ras32);</traster32p,>
|
|
shun-iwasawa |
13c4cf |
else if (ras64)
|
|
shun-iwasawa |
13c4cf |
setOutputRaster<traster64p, tpixel64="">(flow_cur_buf, grad_mag_buf, ras64);</traster64p,>
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
flow_cur_buf_ras->unlock();
|
|
shun-iwasawa |
13c4cf |
grad_mag_buf_ras->unlock();
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
bool Iwa_TangentFlowFx::doGetBBox(double frame, TRectD& bBox,
|
|
shun-iwasawa |
13c4cf |
const TRenderSettings& info) {
|
|
shun-iwasawa |
13c4cf |
if (m_source.isConnected()) {
|
|
shun-iwasawa |
13c4cf |
bBox = TConsts::infiniteRectD;
|
|
shun-iwasawa |
13c4cf |
return true;
|
|
shun-iwasawa |
13c4cf |
// return m_source->doGetBBox(frame, bBox, info);
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
return false;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
//------------------------------------------------------------
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
bool Iwa_TangentFlowFx::canHandle(const TRenderSettings& info, double frame) {
|
|
shun-iwasawa |
13c4cf |
return false;
|
|
shun-iwasawa |
13c4cf |
}
|
|
shun-iwasawa |
13c4cf |
|
|
shun-iwasawa |
13c4cf |
FX_PLUGIN_IDENTIFIER(Iwa_TangentFlowFx, "iwa_TangentFlowFx")
|