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")