vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,894 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "edgeaware_filters_common.hpp"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable: 4512)
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using std::numeric_limits;
|
||||
using std::vector;
|
||||
using namespace cv;
|
||||
using namespace cv::ximgproc;
|
||||
using namespace cv::ximgproc::intrinsics;
|
||||
|
||||
#ifndef SQR
|
||||
#define SQR(x) ((x)*(x))
|
||||
#endif
|
||||
|
||||
inline double Log2(double n)
|
||||
{
|
||||
return log(n) / log(2.0);
|
||||
}
|
||||
|
||||
inline double floor_to_power_of_two(double r)
|
||||
{
|
||||
return pow(2.0, floor(Log2(r)));
|
||||
}
|
||||
|
||||
inline int computeManifoldTreeHeight(double sigma_s, double sigma_r)
|
||||
{
|
||||
const double Hs = floor(Log2(sigma_s)) - 1.0;
|
||||
const double Lr = 1.0 - sigma_r;
|
||||
return max(2, static_cast<int>(ceil(Hs * Lr)));
|
||||
}
|
||||
|
||||
static void splitChannels(InputArrayOfArrays src, vector<Mat>& dst)
|
||||
{
|
||||
CV_Assert(src.isMat() || src.isUMat() || src.isMatVector() || src.isUMatVector());
|
||||
|
||||
if ( src.isMat() || src.isUMat() )
|
||||
{
|
||||
split(src, dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
Size sz;
|
||||
int depth, totalCnNum;
|
||||
|
||||
checkSameSizeAndDepth(src, sz, depth);
|
||||
totalCnNum = getTotalNumberOfChannels(src);
|
||||
|
||||
dst.resize(totalCnNum);
|
||||
vector<int> fromTo(2 * totalCnNum);
|
||||
for (int i = 0; i < totalCnNum; i++)
|
||||
{
|
||||
fromTo[i * 2 + 0] = i;
|
||||
fromTo[i * 2 + 1] = i;
|
||||
|
||||
dst[i].create(sz, CV_MAKE_TYPE(depth, 1));
|
||||
}
|
||||
|
||||
mixChannels(src, dst, fromTo);
|
||||
}
|
||||
}
|
||||
|
||||
class AdaptiveManifoldFilterN : public AdaptiveManifoldFilter
|
||||
{
|
||||
public:
|
||||
AdaptiveManifoldFilterN();
|
||||
|
||||
void filter(InputArray src, OutputArray dst, InputArray joint) CV_OVERRIDE;
|
||||
|
||||
void collectGarbage() CV_OVERRIDE;
|
||||
|
||||
inline double getSigmaS() const CV_OVERRIDE { return sigma_s_; }
|
||||
inline void setSigmaS(double val) CV_OVERRIDE { sigma_s_ = val; }
|
||||
inline double getSigmaR() const CV_OVERRIDE { return sigma_r_; }
|
||||
inline void setSigmaR(double val) CV_OVERRIDE { sigma_r_ = val; }
|
||||
inline int getTreeHeight() const CV_OVERRIDE { return tree_height_; }
|
||||
inline void setTreeHeight(int val) CV_OVERRIDE { tree_height_ = val; }
|
||||
inline int getPCAIterations() const CV_OVERRIDE { return num_pca_iterations_; }
|
||||
inline void setPCAIterations(int val) CV_OVERRIDE { num_pca_iterations_ = val; }
|
||||
inline bool getAdjustOutliers() const CV_OVERRIDE { return adjust_outliers_; }
|
||||
inline void setAdjustOutliers(bool val) CV_OVERRIDE { adjust_outliers_ = val; }
|
||||
inline bool getUseRNG() const CV_OVERRIDE { return useRNG; }
|
||||
inline void setUseRNG(bool val) CV_OVERRIDE { useRNG = val; }
|
||||
|
||||
protected:
|
||||
|
||||
bool adjust_outliers_;
|
||||
double sigma_s_;
|
||||
double sigma_r_;
|
||||
int tree_height_;
|
||||
int num_pca_iterations_;
|
||||
bool useRNG;
|
||||
|
||||
private:
|
||||
|
||||
Size srcSize;
|
||||
Size smallSize;
|
||||
int jointCnNum;
|
||||
int srcCnNum;
|
||||
|
||||
vector<Mat> jointCn;
|
||||
vector<Mat> srcCn;
|
||||
|
||||
vector<Mat> etaFull;
|
||||
|
||||
vector<Mat> sum_w_ki_Psi_blur_;
|
||||
Mat sum_w_ki_Psi_blur_0_;
|
||||
|
||||
Mat w_k;
|
||||
Mat Psi_splat_0_small;
|
||||
vector<Mat> Psi_splat_small;
|
||||
|
||||
Mat1f minDistToManifoldSquared;
|
||||
|
||||
int curTreeHeight;
|
||||
float sigma_r_over_sqrt_2;
|
||||
|
||||
RNG rnd;
|
||||
|
||||
private: /*inline functions*/
|
||||
|
||||
double getNormalizer(int depth)
|
||||
{
|
||||
double normalizer = 1.0;
|
||||
|
||||
if (depth == CV_8U)
|
||||
normalizer = 1.0 / 0xFF;
|
||||
else if (depth == CV_16U)
|
||||
normalizer = 1.0 / 0xFFFF;
|
||||
|
||||
return normalizer;
|
||||
}
|
||||
|
||||
double getResizeRatio()
|
||||
{
|
||||
double df = min(sigma_s_ / 4.0, 256.0 * sigma_r_);
|
||||
df = floor_to_power_of_two(df);
|
||||
df = max(1.0, df);
|
||||
return df;
|
||||
}
|
||||
|
||||
Size getSmallSize()
|
||||
{
|
||||
double df = getResizeRatio();
|
||||
return Size( cvRound(srcSize.width * (1.0/df)), cvRound(srcSize.height*(1.0/df)) ) ;
|
||||
}
|
||||
|
||||
void downsample(const Mat& src, Mat& dst)
|
||||
{
|
||||
double df = getResizeRatio();
|
||||
CV_DbgAssert(src.empty() || src.size() == srcSize);
|
||||
resize(src, dst, Size(), 1.0 / df, 1.0 / df, INTER_LINEAR);
|
||||
CV_DbgAssert(dst.size() == smallSize);
|
||||
}
|
||||
|
||||
void upsample(const Mat& src, Mat& dst)
|
||||
{
|
||||
CV_DbgAssert(src.empty() || src.size() == smallSize);
|
||||
resize(src, dst, srcSize, 0, 0);
|
||||
}
|
||||
|
||||
void downsample(const vector<Mat>& srcv, vector<Mat>& dstv)
|
||||
{
|
||||
mapParallel(&AdaptiveManifoldFilterN::downsample, srcv, dstv);
|
||||
}
|
||||
|
||||
void upsample(const vector<Mat>& srcv, vector<Mat>& dstv)
|
||||
{
|
||||
mapParallel(&AdaptiveManifoldFilterN::upsample, srcv, dstv);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void initBuffers(InputArray src_, InputArray joint_);
|
||||
|
||||
void initSrcAndJoint(InputArray src_, InputArray joint_);
|
||||
|
||||
void buildManifoldsAndPerformFiltering(vector<Mat>& eta, Mat1b& cluster, int treeLevel);
|
||||
|
||||
void gatherResult(InputArray src_, OutputArray dst_);
|
||||
|
||||
void compute_w_k(vector<Mat>& etak, Mat& dst, float sigma, int curTreeLevel);
|
||||
|
||||
void computeClusters(Mat1b& cluster, Mat1b& cluster_minus, Mat1b& cluster_plus);
|
||||
|
||||
void computeEta(Mat& teta, Mat1b& cluster, vector<Mat>& etaDst);
|
||||
|
||||
|
||||
static void h_filter(const Mat1f& src, Mat& dst, float sigma);
|
||||
|
||||
static void RFFilterPass(vector<Mat>& joint, vector<Mat>& Psi_splat, Mat& Psi_splat_0, vector<Mat>& Psi_splat_dst, Mat& Psi_splat_0_dst, float ss, float sr);
|
||||
|
||||
static void computeDTHor(vector<Mat>& srcCn, Mat& dst, float ss, float sr);
|
||||
|
||||
static void computeDTVer(vector<Mat>& srcCn, Mat& dst, float ss, float sr);
|
||||
|
||||
static void computeEigenVector(const vector<Mat>& X, const Mat1b& mask, Mat1f& vecDst, int num_pca_iterations, const Mat1f& vecRand);
|
||||
|
||||
static void computeOrientation(const vector<Mat>& X, const Mat1f& vec, Mat1f& dst);
|
||||
|
||||
private: /*Parallelization routines*/
|
||||
|
||||
typedef void (AdaptiveManifoldFilterN::*MapFunc)(const Mat& src, Mat& dst);
|
||||
|
||||
void mapParallel(MapFunc func, const vector<Mat>& srcv, vector<Mat>& dstv)
|
||||
{
|
||||
dstv.resize(srcv.size());
|
||||
parallel_for_(Range(0, (int)srcv.size()), MapPrallelLoopBody(this, func, srcv, dstv));
|
||||
}
|
||||
|
||||
struct MapPrallelLoopBody : public cv::ParallelLoopBody
|
||||
{
|
||||
MapPrallelLoopBody(AdaptiveManifoldFilterN *_instancePtr, MapFunc _transform, const vector<Mat>& _srcv, vector<Mat>& _dstv)
|
||||
: instancePtr(_instancePtr), transform(_transform), srcv(_srcv), dstv(_dstv)
|
||||
{}
|
||||
|
||||
AdaptiveManifoldFilterN *instancePtr;
|
||||
MapFunc transform;
|
||||
const vector<Mat>& srcv;
|
||||
vector<Mat>& dstv;
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
(instancePtr->*transform)(srcv[i], dstv[i]);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
AdaptiveManifoldFilterN::AdaptiveManifoldFilterN()
|
||||
{
|
||||
sigma_s_ = 16.0;
|
||||
sigma_r_ = 0.2;
|
||||
tree_height_ = -1;
|
||||
num_pca_iterations_ = 1;
|
||||
adjust_outliers_ = false;
|
||||
useRNG = true;
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::initBuffers(InputArray src_, InputArray joint_)
|
||||
{
|
||||
initSrcAndJoint(src_, joint_);
|
||||
|
||||
jointCn.resize(jointCnNum);
|
||||
Psi_splat_small.resize(jointCnNum);
|
||||
for (int i = 0; i < jointCnNum; i++)
|
||||
{
|
||||
//jointCn[i].create(srcSize, CV_32FC1);
|
||||
Psi_splat_small[i].create(smallSize, CV_32FC1);
|
||||
}
|
||||
|
||||
srcCn.resize(srcCnNum);
|
||||
sum_w_ki_Psi_blur_.resize(srcCnNum);
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
{
|
||||
//srcCn[i].create(srcSize, CV_32FC1);
|
||||
sum_w_ki_Psi_blur_[i] = Mat::zeros(srcSize, CV_32FC1);
|
||||
}
|
||||
|
||||
sum_w_ki_Psi_blur_0_ = Mat::zeros(srcSize, CV_32FC1);
|
||||
w_k.create(srcSize, CV_32FC1);
|
||||
Psi_splat_0_small.create(smallSize, CV_32FC1);
|
||||
|
||||
if (adjust_outliers_)
|
||||
minDistToManifoldSquared.create(srcSize);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::initSrcAndJoint(InputArray src_, InputArray joint_)
|
||||
{
|
||||
srcSize = src_.size();
|
||||
smallSize = getSmallSize();
|
||||
srcCnNum = src_.channels();
|
||||
|
||||
split(src_, srcCn);
|
||||
if (src_.depth() != CV_32F)
|
||||
{
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
srcCn[i].convertTo(srcCn[i], CV_32F);
|
||||
}
|
||||
|
||||
if (joint_.empty() || joint_.getObj() == src_.getObj())
|
||||
{
|
||||
jointCnNum = srcCnNum;
|
||||
|
||||
if (src_.depth() == CV_32F)
|
||||
{
|
||||
jointCn = srcCn;
|
||||
}
|
||||
else
|
||||
{
|
||||
jointCn.resize(jointCnNum);
|
||||
for (int i = 0; i < jointCnNum; i++)
|
||||
srcCn[i].convertTo(jointCn[i], CV_32F, getNormalizer(src_.depth()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
splitChannels(joint_, jointCn);
|
||||
|
||||
jointCnNum = (int)jointCn.size();
|
||||
int jointDepth = jointCn[0].depth();
|
||||
Size jointSize = jointCn[0].size();
|
||||
|
||||
CV_Assert( jointSize == srcSize && (jointDepth == CV_8U || jointDepth == CV_16U || jointDepth == CV_32F) );
|
||||
|
||||
if (jointDepth != CV_32F)
|
||||
{
|
||||
for (int i = 0; i < jointCnNum; i++)
|
||||
jointCn[i].convertTo(jointCn[i], CV_32F, getNormalizer(jointDepth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::filter(InputArray src, OutputArray dst, InputArray joint)
|
||||
{
|
||||
CV_Assert(sigma_s_ >= 1 && (sigma_r_ > 0 && sigma_r_ <= 1));
|
||||
num_pca_iterations_ = std::max(1, num_pca_iterations_);
|
||||
|
||||
initBuffers(src, joint);
|
||||
|
||||
curTreeHeight = tree_height_ <= 0 ? computeManifoldTreeHeight(sigma_s_, sigma_r_) : tree_height_;
|
||||
|
||||
sigma_r_over_sqrt_2 = (float) (sigma_r_ / sqrt(2.0));
|
||||
|
||||
const double seedCoef = jointCn[0].at<float>(srcSize.height/2, srcSize.width/2);
|
||||
const uint64 baseCoef = numeric_limits<uint64>::max() / 0xFFFF;
|
||||
rnd.state = static_cast<int64>(baseCoef*seedCoef);
|
||||
|
||||
Mat1b cluster0(srcSize, 0xFF);
|
||||
vector<Mat> eta0(jointCnNum);
|
||||
for (int i = 0; i < jointCnNum; i++)
|
||||
h_filter(jointCn[i], eta0[i], (float)sigma_s_);
|
||||
|
||||
buildManifoldsAndPerformFiltering(eta0, cluster0, 1);
|
||||
|
||||
gatherResult(src, dst);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::gatherResult(InputArray src_, OutputArray dst_)
|
||||
{
|
||||
int dDepth = src_.depth();
|
||||
vector<Mat> dstCn(srcCnNum);
|
||||
|
||||
if (!adjust_outliers_)
|
||||
{
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
divide(sum_w_ki_Psi_blur_[i], sum_w_ki_Psi_blur_0_, dstCn[i], 1.0, dDepth);
|
||||
|
||||
merge(dstCn, dst_);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat1f& alpha = minDistToManifoldSquared;
|
||||
double sigmaMember = -0.5 / (sigma_r_*sigma_r_);
|
||||
multiply(minDistToManifoldSquared, sigmaMember, minDistToManifoldSquared);
|
||||
cv::exp(minDistToManifoldSquared, alpha);
|
||||
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
{
|
||||
Mat& f = srcCn[i];
|
||||
Mat& g = dstCn[i];
|
||||
|
||||
divide(sum_w_ki_Psi_blur_[i], sum_w_ki_Psi_blur_0_, g);
|
||||
|
||||
subtract(g, f, g);
|
||||
multiply(alpha, g, g);
|
||||
add(g, f, g);
|
||||
|
||||
g.convertTo(g, dDepth);
|
||||
}
|
||||
|
||||
merge(dstCn, dst_);
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::buildManifoldsAndPerformFiltering(vector<Mat>& eta, Mat1b& cluster, int treeLevel)
|
||||
{
|
||||
CV_DbgAssert((int)eta.size() == jointCnNum);
|
||||
|
||||
//splatting
|
||||
Size etaSize = eta[0].size();
|
||||
CV_DbgAssert(etaSize == srcSize || etaSize == smallSize);
|
||||
|
||||
if (etaSize == srcSize)
|
||||
{
|
||||
compute_w_k(eta, w_k, sigma_r_over_sqrt_2, treeLevel);
|
||||
etaFull = eta;
|
||||
downsample(eta, eta);
|
||||
}
|
||||
else
|
||||
{
|
||||
upsample(eta, etaFull);
|
||||
compute_w_k(etaFull, w_k, sigma_r_over_sqrt_2, treeLevel);
|
||||
}
|
||||
|
||||
//blurring
|
||||
Psi_splat_small.resize(srcCnNum);
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
Mat tmp;
|
||||
multiply(srcCn[si], w_k, tmp);
|
||||
downsample(tmp, Psi_splat_small[si]);
|
||||
}
|
||||
downsample(w_k, Psi_splat_0_small);
|
||||
|
||||
vector<Mat>& Psi_splat_small_blur = Psi_splat_small;
|
||||
Mat& Psi_splat_0_small_blur = Psi_splat_0_small;
|
||||
|
||||
float rf_ss = (float)(sigma_s_ / getResizeRatio());
|
||||
float rf_sr = (float)(sigma_r_over_sqrt_2);
|
||||
RFFilterPass(eta, Psi_splat_small, Psi_splat_0_small, Psi_splat_small_blur, Psi_splat_0_small_blur, rf_ss, rf_sr);
|
||||
|
||||
//slicing
|
||||
{
|
||||
Mat tmp;
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
{
|
||||
upsample(Psi_splat_small_blur[i], tmp);
|
||||
multiply(tmp, w_k, tmp);
|
||||
add(sum_w_ki_Psi_blur_[i], tmp, sum_w_ki_Psi_blur_[i]);
|
||||
}
|
||||
upsample(Psi_splat_0_small_blur, tmp);
|
||||
multiply(tmp, w_k, tmp);
|
||||
add(sum_w_ki_Psi_blur_0_, tmp, sum_w_ki_Psi_blur_0_);
|
||||
}
|
||||
|
||||
//build new manifolds
|
||||
if (treeLevel < curTreeHeight)
|
||||
{
|
||||
Mat1b cluster_minus, cluster_plus;
|
||||
|
||||
computeClusters(cluster, cluster_minus, cluster_plus);
|
||||
|
||||
vector<Mat> eta_minus(jointCnNum), eta_plus(jointCnNum);
|
||||
{
|
||||
Mat1f teta = 1.0 - w_k;
|
||||
computeEta(teta, cluster_minus, eta_minus);
|
||||
computeEta(teta, cluster_plus, eta_plus);
|
||||
}
|
||||
|
||||
//free memory to continue deep recursion
|
||||
eta.clear();
|
||||
cluster.release();
|
||||
|
||||
buildManifoldsAndPerformFiltering(eta_minus, cluster_minus, treeLevel + 1);
|
||||
buildManifoldsAndPerformFiltering(eta_plus, cluster_plus, treeLevel + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::collectGarbage()
|
||||
{
|
||||
srcCn.clear();
|
||||
jointCn.clear();
|
||||
etaFull.clear();
|
||||
sum_w_ki_Psi_blur_.clear();
|
||||
Psi_splat_small.clear();
|
||||
|
||||
sum_w_ki_Psi_blur_0_.release();
|
||||
w_k.release();
|
||||
Psi_splat_0_small.release();
|
||||
minDistToManifoldSquared.release();
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::h_filter(const Mat1f& src, Mat& dst, float sigma)
|
||||
{
|
||||
CV_DbgAssert(src.depth() == CV_32F);
|
||||
|
||||
const float a = exp(-sqrt(2.0f) / sigma);
|
||||
|
||||
dst.create(src.size(), CV_32FC1);
|
||||
|
||||
for (int y = 0; y < src.rows; ++y)
|
||||
{
|
||||
const float* src_row = src[y];
|
||||
float* dst_row = dst.ptr<float>(y);
|
||||
|
||||
dst_row[0] = src_row[0];
|
||||
int x = 1;
|
||||
#if CV_ENABLE_UNROLLED && defined(_M_ARM64)
|
||||
for ( ; x + 1 < src.cols; x += 2 )
|
||||
{
|
||||
dst_row[x] = src_row[x] + a * (dst_row[x - 1] - src_row[x]);
|
||||
dst_row[x + 1] = src_row[x + 1] + a * (dst_row[x] - src_row[x + 1]);
|
||||
}
|
||||
#endif
|
||||
for ( ; x < src.cols; ++x )
|
||||
{
|
||||
dst_row[x] = src_row[x] + a * (dst_row[x - 1] - src_row[x]);
|
||||
}
|
||||
|
||||
x = src.cols - 2;
|
||||
|
||||
#if CV_ENABLE_UNROLLED && defined(_M_ARM64)
|
||||
for ( ; x - 1 >= 0; x -= 2 )
|
||||
{
|
||||
dst_row[x] = dst_row[x] + a * (dst_row[x + 1] - dst_row[x]);
|
||||
dst_row[x - 1] = dst_row[x - 1] + a * (dst_row[x] - dst_row[x - 1]);
|
||||
}
|
||||
#endif
|
||||
for ( ; x >= 0; --x )
|
||||
{
|
||||
dst_row[x] = dst_row[x] + a * (dst_row[x + 1] - dst_row[x]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 1; y < src.rows; ++y)
|
||||
{
|
||||
float* dst_cur_row = dst.ptr<float>(y);
|
||||
float* dst_prev_row = dst.ptr<float>(y-1);
|
||||
|
||||
rf_vert_row_pass(dst_cur_row, dst_prev_row, a, src.cols);
|
||||
}
|
||||
for (int y = src.rows - 2; y >= 0; --y)
|
||||
{
|
||||
float* dst_cur_row = dst.ptr<float>(y);
|
||||
float* dst_prev_row = dst.ptr<float>(y+1);
|
||||
|
||||
rf_vert_row_pass(dst_cur_row, dst_prev_row, a, src.cols);
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::compute_w_k(vector<Mat>& etak, Mat& dst, float sigma, int curTreeLevel)
|
||||
{
|
||||
CV_DbgAssert((int)etak.size() == jointCnNum);
|
||||
|
||||
dst.create(srcSize, CV_32FC1);
|
||||
float argConst = -0.5f / (sigma*sigma);
|
||||
|
||||
for (int i = 0; i < srcSize.height; i++)
|
||||
{
|
||||
float *dstRow = dst.ptr<float>(i);
|
||||
|
||||
for (int cn = 0; cn < jointCnNum; cn++)
|
||||
{
|
||||
float *eta_kCnRow = etak[cn].ptr<float>(i);
|
||||
float *jointCnRow = jointCn[cn].ptr<float>(i);
|
||||
|
||||
if (cn == 0)
|
||||
{
|
||||
sqr_dif(dstRow, eta_kCnRow, jointCnRow, srcSize.width);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_sqr_dif(dstRow, eta_kCnRow, jointCnRow, srcSize.width);
|
||||
}
|
||||
}
|
||||
|
||||
if (adjust_outliers_)
|
||||
{
|
||||
float *minDistRow = minDistToManifoldSquared.ptr<float>(i);
|
||||
|
||||
if (curTreeLevel != 1)
|
||||
{
|
||||
min_(minDistRow, minDistRow, dstRow, srcSize.width);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(minDistRow, dstRow, srcSize.width*sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
mul(dstRow, dstRow, argConst, srcSize.width);
|
||||
//Exp_32f(dstRow, dstRow, srcSize.width);
|
||||
}
|
||||
|
||||
cv::exp(dst, dst);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::computeDTHor(vector<Mat>& srcCn, Mat& dst, float sigma_s, float sigma_r)
|
||||
{
|
||||
int cnNum = (int)srcCn.size();
|
||||
int h = srcCn[0].rows;
|
||||
int w = srcCn[0].cols;
|
||||
|
||||
float sigmaRatioSqr = (float) SQR(sigma_s / sigma_r);
|
||||
float lnAlpha = (float) (-sqrt(2.0) / sigma_s);
|
||||
|
||||
dst.create(h, w-1, CV_32F);
|
||||
|
||||
for (int i = 0; i < h; i++)
|
||||
{
|
||||
float *dstRow = dst.ptr<float>(i);
|
||||
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
{
|
||||
float *curCnRow = srcCn[cn].ptr<float>(i);
|
||||
|
||||
if (cn == 0)
|
||||
sqr_dif(dstRow, curCnRow, curCnRow + 1, w - 1);
|
||||
else
|
||||
add_sqr_dif(dstRow, curCnRow, curCnRow + 1, w - 1);
|
||||
}
|
||||
|
||||
mad(dstRow, dstRow, sigmaRatioSqr, 1.0f, w - 1);
|
||||
sqrt_(dstRow, dstRow, w - 1);
|
||||
mul(dstRow, dstRow, lnAlpha, w - 1);
|
||||
//Exp_32f(dstRow, dstRow, w - 1);
|
||||
}
|
||||
|
||||
cv::exp(dst, dst);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::computeDTVer(vector<Mat>& srcCn, Mat& dst, float sigma_s, float sigma_r)
|
||||
{
|
||||
int cnNum = (int)srcCn.size();
|
||||
int h = srcCn[0].rows;
|
||||
int w = srcCn[0].cols;
|
||||
|
||||
dst.create(h-1, w, CV_32F);
|
||||
|
||||
float sigmaRatioSqr = (float) SQR(sigma_s / sigma_r);
|
||||
float lnAlpha = (float) (-sqrt(2.0) / sigma_s);
|
||||
|
||||
for (int i = 0; i < h-1; i++)
|
||||
{
|
||||
float *dstRow = dst.ptr<float>(i);
|
||||
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
{
|
||||
float *srcRow1 = srcCn[cn].ptr<float>(i);
|
||||
float *srcRow2 = srcCn[cn].ptr<float>(i+1);
|
||||
|
||||
if (cn == 0)
|
||||
sqr_dif(dstRow, srcRow1, srcRow2, w);
|
||||
else
|
||||
add_sqr_dif(dstRow, srcRow1, srcRow2, w);
|
||||
}
|
||||
|
||||
mad(dstRow, dstRow, sigmaRatioSqr, 1.0f, w);
|
||||
sqrt_(dstRow, dstRow, w);
|
||||
|
||||
mul(dstRow, dstRow, lnAlpha, w);
|
||||
//Exp_32f(dstRow, dstRow, w);
|
||||
}
|
||||
|
||||
cv::exp(dst, dst);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::RFFilterPass(vector<Mat>& joint, vector<Mat>& Psi_splat, Mat& Psi_splat_0, vector<Mat>& Psi_splat_dst, Mat& Psi_splat_0_dst, float ss, float sr)
|
||||
{
|
||||
int h = joint[0].rows;
|
||||
int w = joint[0].cols;
|
||||
int cnNum = (int)Psi_splat.size();
|
||||
|
||||
Mat adth, adtv;
|
||||
computeDTHor(joint, adth, ss, sr);
|
||||
computeDTVer(joint, adtv, ss, sr);
|
||||
|
||||
Psi_splat_0_dst.create(h, w, CV_32FC1);
|
||||
Psi_splat_dst.resize(cnNum);
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
Psi_splat_dst[cn].create(h, w, CV_32FC1);
|
||||
|
||||
Ptr<DTFilter> dtf = createDTFilterRF(adth, adtv, ss, sr, 1);
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
dtf->filter(Psi_splat[cn], Psi_splat_dst[cn]);
|
||||
dtf->filter(Psi_splat_0, Psi_splat_0_dst);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::computeClusters(Mat1b& cluster, Mat1b& cluster_minus, Mat1b& cluster_plus)
|
||||
{
|
||||
|
||||
Mat1f difOreientation;
|
||||
if (jointCnNum > 1)
|
||||
{
|
||||
Mat1f initVec(1, jointCnNum);
|
||||
if (useRNG)
|
||||
{
|
||||
rnd.fill(initVec, RNG::UNIFORM, -0.5, 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < (int)initVec.total(); i++)
|
||||
initVec(0, i) = (i % 2 == 0) ? 0.5f : -0.5f;
|
||||
}
|
||||
|
||||
vector<Mat> difEtaSrc(jointCnNum);
|
||||
for (int i = 0; i < jointCnNum; i++)
|
||||
subtract(jointCn[i], etaFull[i], difEtaSrc[i]);
|
||||
|
||||
Mat1f eigenVec(1, jointCnNum);
|
||||
computeEigenVector(difEtaSrc, cluster, eigenVec, num_pca_iterations_, initVec);
|
||||
|
||||
computeOrientation(difEtaSrc, eigenVec, difOreientation);
|
||||
CV_DbgAssert(difOreientation.size() == srcSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
subtract(jointCn[0], etaFull[0], difOreientation);
|
||||
}
|
||||
|
||||
compare(difOreientation, 0, cluster_minus, CMP_LT);
|
||||
bitwise_and(cluster_minus, cluster, cluster_minus);
|
||||
|
||||
compare(difOreientation, 0, cluster_plus, CMP_GE);
|
||||
bitwise_and(cluster_plus, cluster, cluster_plus);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::computeEta(Mat& teta, Mat1b& cluster, vector<Mat>& etaDst)
|
||||
{
|
||||
CV_DbgAssert(teta.size() == srcSize && cluster.size() == srcSize);
|
||||
|
||||
Mat1f tetaMasked = Mat1f::zeros(srcSize);
|
||||
teta.copyTo(tetaMasked, cluster);
|
||||
|
||||
float sigma_s = (float)(sigma_s_ / getResizeRatio());
|
||||
|
||||
Mat1f tetaMaskedBlur;
|
||||
downsample(tetaMasked, tetaMaskedBlur);
|
||||
h_filter(tetaMaskedBlur, tetaMaskedBlur, sigma_s);
|
||||
|
||||
Mat mul;
|
||||
etaDst.resize(jointCnNum);
|
||||
for (int i = 0; i < jointCnNum; i++)
|
||||
{
|
||||
multiply(tetaMasked, jointCn[i], mul);
|
||||
downsample(mul, etaDst[i]);
|
||||
h_filter(etaDst[i], etaDst[i], sigma_s);
|
||||
divide(etaDst[i], tetaMaskedBlur, etaDst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::computeEigenVector(const vector<Mat>& X, const Mat1b& mask, Mat1f& vecDst, int num_pca_iterations, const Mat1f& vecRand)
|
||||
{
|
||||
int cnNum = (int)X.size();
|
||||
int height = X[0].rows;
|
||||
int width = X[0].cols;
|
||||
|
||||
vecDst.create(1, cnNum);
|
||||
CV_Assert(vecRand.size() == Size(cnNum, 1) && vecDst.size() == Size(cnNum, 1));
|
||||
CV_Assert(mask.rows == height && mask.cols == width);
|
||||
|
||||
const float *pVecRand = vecRand.ptr<float>();
|
||||
Mat1d vecDstd(1, cnNum, 0.0);
|
||||
double *pVecDst = vecDstd.ptr<double>();
|
||||
Mat1f Xw(height, width);
|
||||
|
||||
for (int iter = 0; iter < num_pca_iterations; iter++)
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
const uchar *maskRow = mask.ptr<uchar>(i);
|
||||
float *mulRow = Xw.ptr<float>(i);
|
||||
|
||||
//first multiplication
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
{
|
||||
const float *srcRow = X[cn].ptr<float>(i);
|
||||
const float cnVal = pVecRand[cn];
|
||||
|
||||
if (cn == 0)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
mulRow[j] = cnVal*srcRow[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
mulRow[j] += cnVal*srcRow[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < width; j++)
|
||||
if (!maskRow[j]) mulRow[j] = 0.0f;
|
||||
|
||||
//second multiplication
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
{
|
||||
float curCnSum = 0.0f;
|
||||
const float *srcRow = X[cn].ptr<float>(i);
|
||||
|
||||
for (int j = 0; j < width; j++)
|
||||
curCnSum += mulRow[j]*srcRow[j];
|
||||
|
||||
//TODO: parallel reduce
|
||||
pVecDst[cn] += curCnSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
divide(vecDstd, norm(vecDstd), vecDst);
|
||||
}
|
||||
|
||||
void AdaptiveManifoldFilterN::computeOrientation(const vector<Mat>& X, const Mat1f& vec, Mat1f& dst)
|
||||
{
|
||||
int height = X[0].rows;
|
||||
int width = X[0].cols;
|
||||
int cnNum = (int)X.size();
|
||||
dst.create(height, width);
|
||||
CV_DbgAssert(vec.rows == 1 && vec.cols == cnNum);
|
||||
|
||||
const float *pVec = vec.ptr<float>();
|
||||
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
float *dstRow = dst.ptr<float>(i);
|
||||
|
||||
for (int cn = 0; cn < cnNum; cn++)
|
||||
{
|
||||
const float *srcRow = X[cn].ptr<float>(i);
|
||||
const float cnVal = pVec[cn];
|
||||
|
||||
if (cn == 0)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
dstRow[j] = cnVal*srcRow[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
dstRow[j] += cnVal*srcRow[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
Ptr<AdaptiveManifoldFilter> AdaptiveManifoldFilter::create()
|
||||
{
|
||||
return Ptr<AdaptiveManifoldFilter>(new AdaptiveManifoldFilterN());
|
||||
}
|
||||
|
||||
Ptr<AdaptiveManifoldFilter> createAMFilter(double sigma_s, double sigma_r, bool adjust_outliers)
|
||||
{
|
||||
Ptr<AdaptiveManifoldFilter> amf(new AdaptiveManifoldFilterN());
|
||||
|
||||
amf->setSigmaS(sigma_s);
|
||||
amf->setSigmaR(sigma_r);
|
||||
amf->setAdjustOutliers(adjust_outliers);
|
||||
|
||||
return amf;
|
||||
}
|
||||
|
||||
void amFilter(InputArray joint, InputArray src, OutputArray dst, double sigma_s, double sigma_r, bool adjust_outliers)
|
||||
{
|
||||
Ptr<AdaptiveManifoldFilter> amf = createAMFilter(sigma_s, sigma_r, adjust_outliers);
|
||||
amf->filter(src, dst, joint);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __ADVANCED_TYPES_HPP__
|
||||
#define __ADVANCED_TYPES_HPP__
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
/********************* Defines *********************/
|
||||
|
||||
#ifndef CV_SQR
|
||||
# define CV_SQR(x) ((x)*(x))
|
||||
#endif
|
||||
|
||||
#ifndef CV_CUBE
|
||||
# define CV_CUBE(x) ((x)*(x)*(x))
|
||||
#endif
|
||||
|
||||
#ifndef CV_INIT_VECTOR
|
||||
# define CV_INIT_VECTOR(vname, type, ...) \
|
||||
static const type vname##_a[] = __VA_ARGS__; \
|
||||
std::vector <type> vname(vname##_a, \
|
||||
vname##_a + sizeof(vname##_a) / sizeof(*vname##_a))
|
||||
#endif
|
||||
|
||||
/********************* Types *********************/
|
||||
|
||||
/*! fictitious type to highlight that function
|
||||
* can process n-channels arguments */
|
||||
typedef cv::Mat NChannelsMat;
|
||||
|
||||
/********************* Functions *********************/
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
template <typename _Tp, typename _Tp2> inline
|
||||
cv::Size_<_Tp> operator * (const _Tp2 &x, const cv::Size_<_Tp> &sz)
|
||||
{
|
||||
return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(x*sz.width), cv::saturate_cast<_Tp>(x*sz.height));
|
||||
}
|
||||
|
||||
template <typename _Tp, typename _Tp2> inline
|
||||
cv::Size_<_Tp> operator / (const cv::Size_<_Tp> &sz, const _Tp2 &x)
|
||||
{
|
||||
return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(sz.width/x), cv::saturate_cast<_Tp>(sz.height/x));
|
||||
}
|
||||
|
||||
} // cv
|
||||
}
|
||||
#endif
|
||||
#endif /* __ADVANCED_TYPES_HPP__ */
|
||||
@@ -0,0 +1,293 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2017, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
/* the reference code has been contributed by Chris Sav */
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include "opencl_kernels_ximgproc.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
#if CV_SIMD128
|
||||
inline void v_expand_s(const v_uint8x16& a, v_int16x8& b, v_int16x8& c)
|
||||
{
|
||||
v_uint16x8 t0, t1;
|
||||
v_expand(a, t0, t1);
|
||||
b = v_reinterpret_as_s16(t0);
|
||||
c = v_reinterpret_as_s16(t1);
|
||||
}
|
||||
|
||||
inline void v_expand_f32(const v_int16x8& a, v_float32x4& b, v_float32x4& c)
|
||||
{
|
||||
v_int32x4 t0, t1;
|
||||
v_expand(a, t0, t1);
|
||||
b = v_cvt_f32(t0);
|
||||
c = v_cvt_f32(t1);
|
||||
}
|
||||
|
||||
inline v_uint8x16 v_finalize_pix_ch(const v_int16x8& c0, const v_int16x8& c1,
|
||||
const v_float32x4& s0, const v_float32x4& s1,
|
||||
const v_float32x4& s2, const v_float32x4& s3,
|
||||
const v_float32x4& alpha)
|
||||
{
|
||||
v_float32x4 f0, f1, f2, f3;
|
||||
v_expand_f32(c0, f0, f1);
|
||||
v_expand_f32(c1, f2, f3);
|
||||
|
||||
v_int16x8 d0 = v_pack(v_round(v_add(v_mul(s0, alpha), f0)), v_round(v_add(v_mul(s1, alpha), f1)));
|
||||
v_int16x8 d1 = v_pack(v_round(v_add(v_mul(s2, alpha), f2)), v_round(v_add(v_mul(s3, alpha), f3)));
|
||||
|
||||
return v_pack_u(d0, d1);
|
||||
}
|
||||
#endif
|
||||
|
||||
class ADBody : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ADBody(const Mat* src_, Mat* dst_, const float* exptab, float alpha)
|
||||
{
|
||||
src = src_;
|
||||
dst = dst_;
|
||||
exptab_ = exptab;
|
||||
alpha_ = alpha;
|
||||
}
|
||||
|
||||
void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
const int cn = 3;
|
||||
int cols = src->cols;
|
||||
int step = (int)src->step;
|
||||
int tab[] = { -cn, cn, -step-cn, -step, -step+cn, step-cn, step, step+cn };
|
||||
float alpha = alpha_;
|
||||
const float* exptab = exptab_;
|
||||
|
||||
for( int i = range.start; i < range.end; i++ )
|
||||
{
|
||||
const uchar* psrc0 = src->ptr<uchar>(i);
|
||||
uchar* pdst = dst->ptr<uchar>(i);
|
||||
int j = 0;
|
||||
|
||||
#if CV_SIMD128
|
||||
v_float32x4 v_alpha = v_setall_f32(alpha);
|
||||
for( ; j <= cols - 16; j += 16 )
|
||||
{
|
||||
v_uint8x16 c0, c1, c2;
|
||||
v_load_deinterleave(psrc0 + j*3, c0, c1, c2);
|
||||
v_int16x8 c00, c01, c10, c11, c20, c21;
|
||||
|
||||
v_expand_s(c0, c00, c01);
|
||||
v_expand_s(c1, c10, c11);
|
||||
v_expand_s(c2, c20, c21);
|
||||
|
||||
v_float32x4 s00 = v_setzero_f32(), s01 = s00, s02 = s00, s03 = s00;
|
||||
v_float32x4 s10 = v_setzero_f32(), s11 = s00, s12 = s00, s13 = s00;
|
||||
v_float32x4 s20 = v_setzero_f32(), s21 = s00, s22 = s00, s23 = s00;
|
||||
v_float32x4 fd0, fd1, fd2, fd3;
|
||||
|
||||
for( int k = 0; k < 8; k++ )
|
||||
{
|
||||
const uchar* psrc1 = psrc0 + j*3 + tab[k];
|
||||
v_uint8x16 p0, p1, p2;
|
||||
v_int16x8 p00, p01, p10, p11, p20, p21;
|
||||
v_load_deinterleave(psrc1, p0, p1, p2);
|
||||
|
||||
v_expand_s(p0, p00, p01);
|
||||
v_expand_s(p1, p10, p11);
|
||||
v_expand_s(p2, p20, p21);
|
||||
|
||||
v_int16x8 d00 = v_sub(p00, c00), d01 = v_sub(p01, c01);
|
||||
v_int16x8 d10 = v_sub(p10, c10), d11 = v_sub(p11, c11);
|
||||
v_int16x8 d20 = v_sub(p20, c20), d21 = v_sub(p21, c21);
|
||||
|
||||
v_uint16x8 n0 = v_add(v_add(v_abs(d00), v_abs(d10)), v_abs(d20));
|
||||
v_uint16x8 n1 = v_add(v_add(v_abs(d01), v_abs(d11)), v_abs(d21));
|
||||
|
||||
ushort CV_DECL_ALIGNED(16) nbuf[16];
|
||||
v_store(nbuf, n0);
|
||||
v_store(nbuf + 8, n1);
|
||||
|
||||
v_float32x4 w0(exptab[nbuf[0]], exptab[nbuf[1]], exptab[nbuf[2]], exptab[nbuf[3]]);
|
||||
v_float32x4 w1(exptab[nbuf[4]], exptab[nbuf[5]], exptab[nbuf[6]], exptab[nbuf[7]]);
|
||||
v_float32x4 w2(exptab[nbuf[8]], exptab[nbuf[9]], exptab[nbuf[10]], exptab[nbuf[11]]);
|
||||
v_float32x4 w3(exptab[nbuf[12]], exptab[nbuf[13]], exptab[nbuf[14]], exptab[nbuf[15]]);
|
||||
|
||||
v_expand_f32(d00, fd0, fd1);
|
||||
v_expand_f32(d01, fd2, fd3);
|
||||
s00 = v_add(s00, v_mul(fd0, w0)); s01 = v_add(s01, v_mul(fd1, w1)); s02 = v_add(s02, v_mul(fd2, w2)); s03 = v_add(s03, v_mul(fd3, w3));
|
||||
v_expand_f32(d10, fd0, fd1);
|
||||
v_expand_f32(d11, fd2, fd3);
|
||||
s10 = v_add(s10, v_mul(fd0, w0)); s11 = v_add(s11, v_mul(fd1, w1)); s12 = v_add(s12, v_mul(fd2, w2)); s13 = v_add(s13, v_mul(fd3, w3));
|
||||
v_expand_f32(d20, fd0, fd1);
|
||||
v_expand_f32(d21, fd2, fd3);
|
||||
s20 = v_add(s20, v_mul(fd0, w0)); s21 = v_add(s21, v_mul(fd1, w1)); s22 = v_add(s22, v_mul(fd2, w2)); s23 = v_add(s23, v_mul(fd3, w3));
|
||||
}
|
||||
|
||||
c0 = v_finalize_pix_ch(c00, c01, s00, s01, s02, s03, v_alpha);
|
||||
c1 = v_finalize_pix_ch(c10, c11, s10, s11, s12, s13, v_alpha);
|
||||
c2 = v_finalize_pix_ch(c20, c21, s20, s21, s22, s23, v_alpha);
|
||||
v_store_interleave(pdst + j*3, c0, c1, c2);
|
||||
}
|
||||
j *= 3;
|
||||
#endif
|
||||
|
||||
for( ; j < cols*cn; j += cn )
|
||||
{
|
||||
int c0 = psrc0[j], c1 = psrc0[j+1], c2 = psrc0[j+2];
|
||||
float s0 = 0.f, s1 = 0.f, s2 = 0.f;
|
||||
for( int k = 0; k < 8; k++ )
|
||||
{
|
||||
const uchar* psrc1 = psrc0 + j + tab[k];
|
||||
int delta0 = psrc1[0] - c0;
|
||||
int delta1 = psrc1[1] - c1;
|
||||
int delta2 = psrc1[2] - c2;
|
||||
int nabla = std::abs(delta0) + std::abs(delta1) + std::abs(delta2);
|
||||
float w = exptab[nabla];
|
||||
s0 += delta0*w;
|
||||
s1 += delta1*w;
|
||||
s2 += delta2*w;
|
||||
}
|
||||
pdst[j] = saturate_cast<uchar>(c0 + alpha*s0);
|
||||
pdst[j+1] = saturate_cast<uchar>(c1 + alpha*s1);
|
||||
pdst[j+2] = saturate_cast<uchar>(c2 + alpha*s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Mat* src;
|
||||
Mat* dst;
|
||||
const float* exptab_;
|
||||
float alpha_;
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
static bool ocl_anisotropicDiffusion(InputArray src_, OutputArray dst_,
|
||||
float alpha, int niters,
|
||||
const std::vector<float>& exptab)
|
||||
{
|
||||
UMat src0 = src_.getUMat(), dst0 = dst_.getUMat();
|
||||
int type = src0.type();
|
||||
int rows = src0.rows, cols = src0.cols;
|
||||
|
||||
ocl::Kernel k("anisodiff", ocl::ximgproc::anisodiff_oclsrc, "");
|
||||
if (k.empty())
|
||||
return false;
|
||||
|
||||
UMat temp0x(rows + 2, cols + 2, type);
|
||||
UMat temp1x(rows + 2, cols + 2, type);
|
||||
UMat temp0(temp0x, Rect(1, 1, cols, rows));
|
||||
UMat temp1(temp1x, Rect(1, 1, cols, rows));
|
||||
|
||||
int tabsz = (int)exptab.size();
|
||||
UMat uexptab = Mat(1, tabsz, CV_32F, (void*)&exptab[0]).getUMat(ACCESS_READ);
|
||||
|
||||
for (int t = 0; t < niters; t++)
|
||||
{
|
||||
UMat src = temp0, dst = t == niters-1 ? dst0 : temp1;
|
||||
copyMakeBorder(t == 0 ? src0 : src, temp0x, 1, 1, 1, 1, BORDER_REPLICATE);
|
||||
|
||||
k.args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnly(dst),
|
||||
ocl::KernelArg::PtrReadOnly(uexptab), alpha);
|
||||
|
||||
size_t globalsize[] = { (size_t)cols, (size_t)rows };
|
||||
if(!k.run(2, globalsize, NULL, true))
|
||||
return false;
|
||||
|
||||
std::swap(temp0, temp1);
|
||||
std::swap(temp0x, temp1x);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void anisotropicDiffusion(InputArray src_, OutputArray dst_, float alpha, float K, int niters )
|
||||
{
|
||||
if( niters == 0 )
|
||||
{
|
||||
src_.copyTo(dst_);
|
||||
return;
|
||||
}
|
||||
|
||||
int type = src_.type();
|
||||
CV_Assert(src_.dims() == 2 && type == CV_8UC3);
|
||||
CV_Assert(K != 0);
|
||||
CV_Assert(alpha > 0);
|
||||
CV_Assert(niters >= 0);
|
||||
|
||||
const int cn = 3;
|
||||
float sigma = K * cn * 255.f;
|
||||
float isigma2 = 1 / (sigma * sigma);
|
||||
std::vector<float> exptab_(255*3);
|
||||
float* exptab = &exptab_[0];
|
||||
|
||||
for( int k = 0; k < 255*3; k++ )
|
||||
exptab[k] = std::exp(-k*k*isigma2);
|
||||
|
||||
dst_.create(src_.size(), type);
|
||||
|
||||
CV_OCL_RUN(dst_.isUMat(),
|
||||
ocl_anisotropicDiffusion(src_, dst_, alpha, niters, exptab_));
|
||||
|
||||
Mat src0 = src_.getMat();
|
||||
int rows = src0.rows, cols = src0.cols;
|
||||
|
||||
Mat dst0 = dst_.getMat();
|
||||
Mat temp0x(rows + 2, cols + 2, type);
|
||||
Mat temp1x(rows + 2, cols + 2, type);
|
||||
Mat temp0(temp0x, Rect(1, 1, cols, rows));
|
||||
Mat temp1(temp1x, Rect(1, 1, cols, rows));
|
||||
|
||||
for (int t = 0; t < niters; t++)
|
||||
{
|
||||
Mat src = temp0, dst = t == niters-1 ? dst0 : temp1;
|
||||
copyMakeBorder(t == 0 ? src0 : src, temp0x, 1, 1, 1, 1, BORDER_REPLICATE);
|
||||
|
||||
ADBody body(&src, &dst, exptab, alpha);
|
||||
parallel_for_(Range(0, rows), body, 8);
|
||||
|
||||
std::swap(temp0, temp1);
|
||||
std::swap(temp0x, temp1x);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <opencv2/ximgproc.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
void compute_mRTV(const Mat& L, Mat& mRTV, int fr);
|
||||
void compute_G(const Mat& B, const Mat& mRTV, Mat& G, Mat& alpha, int fr);
|
||||
void joint_bilateral_filter(const Mat& img, const Mat& G, Mat& r_img, int fr2, double sigma_avg);
|
||||
void joint_bilateral_filter3(const Mat& img, const Mat& G, Mat& r_img, int fr2, double sigma_avg);
|
||||
|
||||
void bilateralTextureFilter(InputArray src_, OutputArray dst_, int fr,
|
||||
int numIter, double sigmaAlpha, double sigmaAvg)
|
||||
{
|
||||
CV_Assert(!src_.empty());
|
||||
|
||||
Mat src = src_.getMat();
|
||||
CV_Assert(src.depth() == CV_8U || src.depth() == CV_32F);
|
||||
|
||||
CV_Assert(fr > 0 && numIter > 0);
|
||||
|
||||
if (sigmaAlpha < 0)
|
||||
sigmaAlpha = 5. * fr;
|
||||
if (sigmaAvg < 0)
|
||||
sigmaAvg = 0.05 * sqrt(static_cast<float>(src.channels()));
|
||||
|
||||
Mat I;
|
||||
src.copyTo(I);
|
||||
if (src.type() == CV_8UC1) {
|
||||
I.convertTo(I, CV_32FC1, 1.0 / 255.0);
|
||||
}
|
||||
else if (src.type() == CV_8UC3) {
|
||||
I.convertTo(I, CV_32FC3, 1.0 / 255.0);
|
||||
}
|
||||
|
||||
for (int iter = 0; iter < numIter; iter++)
|
||||
{
|
||||
Mat B;
|
||||
blur(I, B, Size(2 * fr + 1, 2 * fr + 1), Point(-1, -1), BORDER_REFLECT);
|
||||
|
||||
Mat mRTV;
|
||||
compute_mRTV(I, mRTV, fr);
|
||||
|
||||
Mat G, minmRTV;
|
||||
compute_G(B, mRTV, G, minmRTV, fr);
|
||||
|
||||
// alpha blending
|
||||
Mat Gtilde;
|
||||
Mat diff = mRTV - minmRTV;
|
||||
Mat alpha = -diff.mul(sigmaAlpha);
|
||||
exp(alpha, alpha);
|
||||
alpha = alpha + 1.;
|
||||
pow(alpha, -1, alpha);
|
||||
alpha = (alpha - 0.5) * 2;
|
||||
Mat alphainv = -(alpha - 1);
|
||||
|
||||
std::vector<Mat> Gi, Bi;
|
||||
Gi.resize(I.channels());
|
||||
Bi.resize(I.channels());
|
||||
if (I.channels() == 3) {
|
||||
split(G, &Gi[0]);
|
||||
split(B, &Bi[0]);
|
||||
}
|
||||
else {
|
||||
G.copyTo(Gi[0]);
|
||||
B.copyTo(Bi[0]);
|
||||
}
|
||||
|
||||
std::vector<Mat> Gtildei;
|
||||
Gtildei.resize(I.channels());
|
||||
for (int i = 0; i < B.channels(); i++)
|
||||
Gtildei[i] = Gi[i].mul(alpha) + Bi[i].mul(alphainv);
|
||||
merge(&Gtildei[0], B.channels(), Gtilde);
|
||||
|
||||
// joint bilateral filter
|
||||
cv::Mat J;
|
||||
if (I.channels() == 1)
|
||||
joint_bilateral_filter(I, Gtilde, J, fr * 2, sigmaAvg);
|
||||
else if (I.channels() == 3)
|
||||
joint_bilateral_filter3(I, Gtilde, J, fr * 2, sigmaAvg);
|
||||
I = J;
|
||||
}
|
||||
if (src.type() == CV_8UC1) {
|
||||
I.convertTo(I, CV_8UC1, 255.0);
|
||||
}
|
||||
else if (src.type() == CV_8UC3) {
|
||||
I.convertTo(I, CV_8UC3, 255.0);
|
||||
}
|
||||
|
||||
I.copyTo(dst_);
|
||||
}
|
||||
|
||||
void compute_mRTV(const Mat& L, Mat& mRTV, int fr)
|
||||
{
|
||||
mRTV = Mat::zeros(L.size(), CV_32FC1);
|
||||
|
||||
const float eps = 0.00001f;
|
||||
|
||||
// Calculate image derivative(gradient)
|
||||
Mat G;
|
||||
Mat Gx, Gy, kernelx, kernely;
|
||||
kernelx = Mat::zeros(1, 3, CV_32F);
|
||||
kernelx.at<float>(0, 1) = -1.0;
|
||||
kernelx.at<float>(0, 2) = 1.0;
|
||||
filter2D(L, Gx, -1, kernelx, Point(-1, -1), 0, BORDER_REFLECT);
|
||||
kernely = Mat::zeros(3, 1, CV_32F);
|
||||
kernely.at<float>(1, 0) = -1.0;
|
||||
kernely.at<float>(2, 0) = 1.0;
|
||||
filter2D(L, Gy, -1, kernely, Point(-1, -1), 0, BORDER_REFLECT);
|
||||
|
||||
Gx = Gx.mul(Gx);
|
||||
Gy = Gy.mul(Gy);
|
||||
sqrt(Gx + Gy, G);
|
||||
|
||||
// Pad image L and G
|
||||
Mat padL;
|
||||
Mat padG;
|
||||
copyMakeBorder(L, padL, fr, fr, fr, fr, BORDER_REFLECT);
|
||||
copyMakeBorder(G, padG, fr, fr, fr, fr, BORDER_REFLECT);
|
||||
|
||||
// Calculate maxL, minL, maxG, sumG
|
||||
int pu = fr;
|
||||
int pb = pu + L.rows;
|
||||
int pl = fr;
|
||||
int pr = pl + L.cols;
|
||||
|
||||
std::vector<Mat> Li, Gi;
|
||||
Li.resize(L.channels());
|
||||
Gi.resize(L.channels());
|
||||
if (L.channels() == 3) {
|
||||
split(padL, &Li[0]);
|
||||
split(padG, &Gi[0]);
|
||||
}
|
||||
else {
|
||||
padL.copyTo(Li[0]);
|
||||
padG.copyTo(Gi[0]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < L.channels(); i++)
|
||||
{
|
||||
Mat maxL = Mat::zeros(L.size(), CV_32FC1);
|
||||
Mat minL = Mat::ones(L.size(), CV_32FC1);
|
||||
Mat maxG = Mat::zeros(L.size(), CV_32FC1);
|
||||
Mat sumG = Mat::zeros(L.size(), CV_32FC1);
|
||||
for (int y = -fr; y <= fr; y++)
|
||||
{
|
||||
for (int x = -fr; x <= fr; x++)
|
||||
{
|
||||
Mat temp = Li[i](
|
||||
Range(pu + y, pb + y),
|
||||
Range(pl + x, pr + x)
|
||||
);
|
||||
maxL = max(maxL, temp);
|
||||
minL = min(minL, temp);
|
||||
|
||||
temp = Gi[i](
|
||||
Range(pu + y, pb + y),
|
||||
Range(pl + x, pr + x)
|
||||
);
|
||||
maxG = max(maxG, temp);
|
||||
sumG = sumG + temp;
|
||||
}
|
||||
}
|
||||
Mat deltai = maxL - minL;
|
||||
sumG = max(sumG, eps);
|
||||
Mat mRTVi = maxG / sumG * (2 * fr + 1);
|
||||
mRTV = mRTV + mRTVi.mul(deltai);
|
||||
}
|
||||
if (L.channels() == 3)
|
||||
mRTV = mRTV / 3;
|
||||
}
|
||||
|
||||
void compute_G(const Mat& B, const Mat& mRTV, Mat& G, Mat& alpha, int fr)
|
||||
{
|
||||
B.copyTo(G);
|
||||
alpha = Mat::ones(B.size(), CV_32FC1);
|
||||
for (int y = -fr; y <= fr; y++)
|
||||
{
|
||||
for (int x = -fr; x <= fr; x++)
|
||||
{
|
||||
Point pb;
|
||||
Point pt;
|
||||
for (pb.y = 0; pb.y < B.rows; pb.y++)
|
||||
{
|
||||
for (pb.x = 0; pb.x < B.cols; pb.x++)
|
||||
{
|
||||
pt.x = min(max(pb.x + x, 0), B.cols - 1);
|
||||
pt.y = min(max(pb.y + y, 0), B.rows - 1);
|
||||
if (alpha.at<float>(pb) > mRTV.at<float>(pt))
|
||||
{
|
||||
alpha.at<float>(pb) = mRTV.at<float>(pt);
|
||||
if (B.channels() == 3)
|
||||
G.at<Vec3f>(pb) = B.at<Vec3f>(pt);
|
||||
else if (B.channels() == 1)
|
||||
G.at<float>(pb) = B.at<float>(pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void joint_bilateral_filter(const Mat& img, const Mat& G, Mat& r_img, int fr2, double sigma_avg)
|
||||
{
|
||||
Mat p_G;
|
||||
copyMakeBorder(G, p_G, fr2, fr2, fr2, fr2, BORDER_REFLECT);
|
||||
|
||||
Mat p_img;
|
||||
copyMakeBorder(img, p_img, fr2, fr2, fr2, fr2, BORDER_REFLECT);
|
||||
|
||||
Mat SW;
|
||||
if (SW.empty()) {
|
||||
SW = Mat(2*fr2+1, 2*fr2+1, CV_32FC1);
|
||||
int r, c;
|
||||
float y, x;
|
||||
for (r = 0, y = (float)-fr2; r < SW.rows; r++, y += 1.0) {
|
||||
for(c = 0, x = (float)-fr2; c < SW.cols; c++, x += 1.0) {
|
||||
SW.at<float>(r,c) = exp(-(x*x + y*y) / (2*fr2*fr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_img = Mat::zeros(img.size(), CV_32FC1);
|
||||
{
|
||||
Mat sum_d_W = Mat::zeros(img.size(), CV_32FC1);
|
||||
Mat d_W = Mat::zeros(G.size(), CV_32FC1);
|
||||
|
||||
for (int x = -fr2; x <= fr2; x++) {
|
||||
for (int y = -fr2; y <= fr2; y++) {
|
||||
d_W = p_G(Rect(fr2+x, fr2+y, img.cols, img.rows)) - G;
|
||||
multiply(d_W, d_W, d_W);
|
||||
exp(-0.5 * d_W / (sigma_avg*sigma_avg), d_W);
|
||||
|
||||
d_W = d_W * SW.at<float>(fr2+y, fr2+x); //Gaussian weight
|
||||
|
||||
sum_d_W = sum_d_W + d_W;
|
||||
multiply(d_W, p_img(Rect(fr2+x, fr2+y, img.cols, img.rows)), d_W);
|
||||
r_img = r_img + d_W;
|
||||
}
|
||||
}
|
||||
max(1e-5f, sum_d_W, sum_d_W);
|
||||
divide(r_img, sum_d_W, r_img);
|
||||
}
|
||||
}
|
||||
|
||||
void joint_bilateral_filter3(const Mat& img, const Mat& G, Mat& r_img, int fr2, double sigma_avg)
|
||||
{
|
||||
Mat p_G;
|
||||
copyMakeBorder(G, p_G, fr2, fr2, fr2, fr2, BORDER_REFLECT);
|
||||
|
||||
Mat p_img;
|
||||
copyMakeBorder(img, p_img, fr2, fr2, fr2, fr2, BORDER_REFLECT);
|
||||
|
||||
Mat SW;
|
||||
if (SW.empty()) {
|
||||
SW = Mat(2*fr2+1, 2*fr2+1, CV_32FC1);
|
||||
int r, c;
|
||||
float y, x;
|
||||
for (r = 0, y = (float)-fr2; r < SW.rows; r++, y += 1.0) {
|
||||
for(c = 0, x = (float)-fr2; c < SW.cols; c++, x += 1.0) {
|
||||
SW.at<float>(r,c) = exp(-(x*x + y*y) / (2*fr2*fr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Mat> G_channels(3);
|
||||
split(G, G_channels);
|
||||
std::vector<Mat> p_G_channels(3);
|
||||
split(p_G, p_G_channels);
|
||||
std::vector<Mat> p_img_channels(3);
|
||||
split(p_img, p_img_channels);
|
||||
|
||||
Mat sum_d_W = Mat::zeros(img.size(), CV_32FC1);
|
||||
std::vector<Mat> d_W_channels(3);
|
||||
for (int ch = 0; ch < 3; ch++) {
|
||||
d_W_channels[ch] = Mat::zeros(G.size(), CV_32FC1);
|
||||
}
|
||||
Mat d_W = Mat::zeros(G.size(), CV_32FC1);
|
||||
|
||||
std::vector<Mat> r_img_channels(3);
|
||||
for (int ch = 0; ch < 3; ch++) {
|
||||
r_img_channels[ch] = Mat::zeros(G.size(), CV_32FC1);
|
||||
}
|
||||
|
||||
for (int x = -fr2; x <= fr2; x++) {
|
||||
for (int y = -fr2; y <= fr2; y++) {
|
||||
d_W.setTo(0);
|
||||
for (int ch = 0; ch < 3; ch++) {
|
||||
subtract(p_G_channels[ch](Rect(fr2+x, fr2+y, img.cols, img.rows)), G_channels[ch], d_W_channels[ch]);
|
||||
multiply(d_W_channels[ch], d_W_channels[ch], d_W_channels[ch]);
|
||||
}
|
||||
for (int ch = 0; ch < 3; ch++) {
|
||||
add(d_W, d_W_channels[ch], d_W);
|
||||
}
|
||||
exp(-0.5 * d_W / (sigma_avg*sigma_avg), d_W);
|
||||
|
||||
d_W = d_W * SW.at<float>(fr2+y, fr2+x); //Gaussian weight
|
||||
|
||||
add(sum_d_W, d_W, sum_d_W);
|
||||
|
||||
for (int ch = 0; ch < 3; ch++) {
|
||||
Mat n_p_img = p_img_channels[ch](Rect(fr2+x, fr2+y, img.cols, img.rows));
|
||||
accumulateProduct(d_W, n_p_img, r_img_channels[ch]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max(1e-5f, sum_d_W, sum_d_W);
|
||||
for (int ch = 0; ch < 3; ch++) {
|
||||
divide(r_img_channels[ch], sum_d_W, r_img_channels[ch]);
|
||||
}
|
||||
merge(r_img_channels, r_img);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2017, IBM Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Marc Fiammante marc.fiammante@fr.ibm.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of OpenCV Foundation or contributors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "opencv2/ximgproc.hpp"
|
||||
#include "precomp.hpp"
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc {
|
||||
static bool isPixelMinimum(Mat &edge, int row, int col, int contrast) {
|
||||
int count = 0;
|
||||
int pixel = edge.ptr(row)[col] + contrast - 1; // minus 1 is needed for chessboard like images with contrast = 1
|
||||
// to get the vertical borders
|
||||
int m2 = edge.ptr(row - 2)[col - 2];
|
||||
int m1 = edge.ptr(row - 1)[col - 1];
|
||||
int p1 = edge.ptr(row + 1)[col + 1];
|
||||
int p2 = edge.ptr(row + 2)[col + 2];
|
||||
if ((pixel <= m1) && (pixel <= p1) && (pixel < (m1 + m2) / 2) && (pixel < (p1 + p2) / 2)) count++; // Local minimum diagonal
|
||||
m2 = edge.ptr(row - 2)[col];
|
||||
m1 = edge.ptr(row - 1)[col];
|
||||
p1 = edge.ptr(row + 1)[col];
|
||||
p2 = edge.ptr(row + 2)[col];
|
||||
if ((pixel <= m1) && (pixel <= p1) && (pixel < (m1 + m2) / 2) && (pixel < (p1 + p2) / 2)) count++; // Local minimum vertical
|
||||
m2 = edge.ptr(row - 2)[col + 2];
|
||||
m1 = edge.ptr(row - 1)[col + 1];
|
||||
p1 = edge.ptr(row + 1)[col - 1];
|
||||
p2 = edge.ptr(row + 2)[col - 2];
|
||||
if ((pixel <= m1) && (pixel <= p1) && (pixel < (m1 + m2) / 2) && (pixel < (p1 + p2) / 2)) count++; // Local minimum other diagonal
|
||||
m2 = edge.ptr(row)[col + 2];
|
||||
m1 = edge.ptr(row)[col + 1];
|
||||
p1 = edge.ptr(row)[col - 1];
|
||||
p2 = edge.ptr(row)[col - 2];
|
||||
if ((pixel <= m1) && (pixel <= p1) && (pixel < (m1 + m2) / 2) && (pixel < (p1 + p2) / 2)) count++; // Local minimum horizontal
|
||||
if (count > 1) return true; // Avoid corners of black zones
|
||||
return false;
|
||||
}
|
||||
static int correctPixel(Mat &iedge, int row, int col) {
|
||||
// now check in there is a line around pixel to fill gaps
|
||||
// Around Diagonal top left to bottom right
|
||||
int weight = 4 * 255;
|
||||
int lines = 0;
|
||||
int line =
|
||||
iedge.ptr(row - 1)[col - 2] +
|
||||
iedge.ptr(row - 1)[col - 1] +
|
||||
iedge.ptr(row)[col + 1] +
|
||||
iedge.ptr(row)[col + 2];
|
||||
if (line == 0) lines += 1;
|
||||
// Around horizontal
|
||||
line =
|
||||
iedge.ptr(row)[col - 2] +
|
||||
iedge.ptr(row)[col - 1] +
|
||||
iedge.ptr(row + 1)[col + 1] +
|
||||
iedge.ptr(row + 1)[col + 2];
|
||||
if (line == 0) lines += 1;
|
||||
// Around Diagonal top right to bottom left
|
||||
line =
|
||||
iedge.ptr(row - 2)[col] +
|
||||
iedge.ptr(row - 1)[col] +
|
||||
iedge.ptr(row + 1)[col + 1] +
|
||||
iedge.ptr(row + 2)[col + 1];
|
||||
if (line == 0) lines += 1;
|
||||
// Around vertical
|
||||
line =
|
||||
iedge.ptr(row - 2)[col - 1] +
|
||||
iedge.ptr(row - 1)[col - 1] +
|
||||
iedge.ptr(row + 1)[col] +
|
||||
iedge.ptr(row + 2)[col];
|
||||
if (line == 0) lines += 1;
|
||||
line =
|
||||
iedge.ptr(row - 2)[col - 2] +
|
||||
iedge.ptr(row - 1)[col - 2] +
|
||||
iedge.ptr(row - 2)[col - 1] +
|
||||
iedge.ptr(row - 1)[col - 1] +
|
||||
iedge.ptr(row + 1)[col + 1] +
|
||||
iedge.ptr(row + 1)[col + 2] +
|
||||
iedge.ptr(row + 2)[col + 1] +
|
||||
iedge.ptr(row + 2)[col + 2];
|
||||
if (line < weight) lines += 1;
|
||||
// Near vertical
|
||||
line =
|
||||
iedge.ptr(row - 2)[col] +
|
||||
iedge.ptr(row - 1)[col] +
|
||||
iedge.ptr(row - 2)[col - 1] +
|
||||
iedge.ptr(row - 2)[col + 1] +
|
||||
iedge.ptr(row + 1)[col] +
|
||||
iedge.ptr(row + 2)[col] +
|
||||
iedge.ptr(row + 2)[col + 1] +
|
||||
iedge.ptr(row + 2)[col - 1];
|
||||
if (line < weight) lines += 1;
|
||||
// Near diagonal top right to bottom left
|
||||
line =
|
||||
iedge.ptr(row - 2)[col + 2] +
|
||||
iedge.ptr(row - 1)[col + 1] +
|
||||
iedge.ptr(row - 2)[col - 1] +
|
||||
iedge.ptr(row - 1)[col + 2] +
|
||||
iedge.ptr(row + 1)[col - 1] +
|
||||
iedge.ptr(row + 2)[col - 2] +
|
||||
iedge.ptr(row + 2)[col - 1] +
|
||||
iedge.ptr(row + 1)[col - 2];
|
||||
if (line < weight) lines += 1;
|
||||
// Near horizontal
|
||||
line =
|
||||
iedge.ptr(row)[(col - 2)] +
|
||||
iedge.ptr(row)[(col - 1)] +
|
||||
iedge.ptr(row - 1)[(col - 2)] +
|
||||
iedge.ptr(row + 1)[(col - 2)] +
|
||||
iedge.ptr(row)[col + 1] +
|
||||
iedge.ptr(row)[col + 2] +
|
||||
iedge.ptr(row + 1)[col + 2] +
|
||||
iedge.ptr(row - 1)[col + 2];
|
||||
if (line < weight) lines += 1;
|
||||
if (lines == 1) return 0;
|
||||
// Compute surrounding pixels for dark zone
|
||||
int surround = iedge.ptr(row - 1)[col - 1] +
|
||||
iedge.ptr(row - 1)[col] +
|
||||
iedge.ptr(row - 1)[col + 1] +
|
||||
iedge.ptr(row)[col - 1] +
|
||||
iedge.ptr(row)[col + 1] +
|
||||
iedge.ptr(row + 1)[col - 1] +
|
||||
iedge.ptr(row + 1)[col] +
|
||||
iedge.ptr(row + 1)[col + 1];
|
||||
if (surround == 8 * 255) return 255;
|
||||
if (surround == 0) return 255;
|
||||
return iedge.ptr(row)[col];
|
||||
}
|
||||
static int contrastEdges(Mat &minput, Mat &mouput, int contrast) {
|
||||
Mat mwork(minput.size(), minput.type(), Scalar(255));
|
||||
// Now find if other pixels inside are minimum
|
||||
for (int row = 2; row < minput.rows - 2; row++) {
|
||||
for (int col = 2; col < minput.cols - 2; col++) {
|
||||
if (isPixelMinimum(minput, row, col, contrast)) {
|
||||
mwork.ptr(row)[col] = 0;
|
||||
}
|
||||
else {
|
||||
mwork.ptr(row)[col] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
// correct pixels
|
||||
for (int row = 2; row < mwork.rows - 2; row++) {
|
||||
for (int col = 2; col < mwork.cols - 2; col++) {
|
||||
mouput.ptr(row)[col] = (uchar)correctPixel(mwork, row, col);
|
||||
}
|
||||
}
|
||||
// Set border of output matrix to white
|
||||
for (int col = 0; col < mouput.cols; col++) {
|
||||
for (int row = 0; row < 2; row++) {
|
||||
mouput.ptr(row)[col] = 255;
|
||||
}
|
||||
for (int row = mouput.rows - 2; row < mouput.rows; row++) {
|
||||
mouput.ptr(row)[col] = 255;
|
||||
}
|
||||
}
|
||||
for (int row = 0; row < mouput.rows; row++) {
|
||||
for (int col = 0; col < 2; col++) {
|
||||
mouput.ptr(row)[col] = 255;
|
||||
}
|
||||
for (int col = mouput.cols - 2; col < mouput.cols; col++) {
|
||||
mouput.ptr(row)[col] = 255;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
CV_EXPORTS_W void BrightEdges(Mat &image, Mat &edge, int contrast, int shortrange, int longrange)
|
||||
{
|
||||
Mat gray, gblur, bblur, diff, cedge;
|
||||
GaussianBlur(image, gblur, Size(shortrange, shortrange), 0);
|
||||
blur(image, bblur, Size(longrange, longrange));
|
||||
absdiff(gblur, bblur, diff);
|
||||
cvtColor(diff, gray, COLOR_BGR2GRAY);
|
||||
equalizeHist(gray, cedge);
|
||||
if (contrast > 0) {
|
||||
edge = Mat(cedge.size(), cedge.type());
|
||||
contrastEdges(cedge, edge, contrast);
|
||||
}
|
||||
else {
|
||||
edge = cedge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
#include "precomp.hpp"
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
/*
|
||||
If you use this code please cite this @cite deriche1987using
|
||||
Using Canny's criteria to derive a recursively implemented optimal edge detector International journal of computer vision (Volume:1 , Issue: 2 ) 1987
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
template<typename T> static void
|
||||
VerticalIIRFilter(Mat &img,Mat &dst,const Range &r,double alpha,double omega)
|
||||
{
|
||||
float *f2;
|
||||
int tailleSequence = (img.rows>img.cols) ? img.rows : img.cols;
|
||||
Mat matG1(1, tailleSequence, CV_64FC1), matG2(1, tailleSequence, CV_64FC1);
|
||||
double *g1 = matG1.ptr<double>(0), *g2 = (double*)matG2.ptr<double>(0);
|
||||
double a2, a3;
|
||||
double b1, b2;
|
||||
int rows = img.rows, cols = img.cols;
|
||||
double c = (1 - 2 * exp(-alpha)*cos(omega) + exp(-2 * alpha)) / (exp(-alpha)*sin(omega));
|
||||
|
||||
double a = -c * exp(-alpha)*sin(omega);
|
||||
a2 = 1;// kp*exp(-alpha);
|
||||
a3 = 1;//-kp*exp(-alpha);
|
||||
b1 = -2 * exp(-alpha)*cos(omega);
|
||||
b2 = exp(-2 * alpha);
|
||||
|
||||
for (int j = r.start; j<r.end; j++)
|
||||
{
|
||||
// Causal vertical IIR filter
|
||||
T *c1 = img.ptr<T>(0);
|
||||
f2 = dst.ptr<float>(0);
|
||||
f2 += j;
|
||||
c1 += j;
|
||||
int i = 0;
|
||||
g1[i] = a2* *c1;
|
||||
i++;
|
||||
c1 += cols;
|
||||
g1[i] =a2 * c1[-cols] - (b1)* g1[i - 1];
|
||||
i++;
|
||||
c1 += cols;
|
||||
for (i = 2; i<rows; i++, c1 += cols)
|
||||
g1[i] = a2 * c1[-cols] - b1*g1[i - 1] - b2 *g1[i - 2];
|
||||
// Anticausal vertical IIR filter
|
||||
c1 = img.ptr<T>(0);
|
||||
c1 += (rows - 1)*cols + j;
|
||||
i = rows - 1;
|
||||
g2[i] = a3 * *c1;
|
||||
i--;
|
||||
c1 -= cols;
|
||||
g2[i] = a3* c1[cols] + (b1)*g2[i + 1];
|
||||
i--;
|
||||
c1 -= cols;
|
||||
for (i = rows - 3; i >= 0; i--, c1 -= cols)
|
||||
g2[i] = a3*c1[cols] -
|
||||
b1*g2[i + 1] - b2*g2[i + 2];
|
||||
for (i = 0; i<rows; i++, f2 += cols)
|
||||
*f2 = static_cast<float>(a*(g1[i] - g2[i]));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> static void
|
||||
HorizontalIIRFilter(Mat &img, Mat &dst, const Range &r, double alpha, double omega)
|
||||
{
|
||||
float *f1;
|
||||
int rows = img.rows, cols = img.cols;
|
||||
int tailleSequence = (rows>cols) ? rows : cols;
|
||||
Mat matG1(1, tailleSequence, CV_64FC1), matG2(1, tailleSequence, CV_64FC1);
|
||||
double *g1 = (double*)matG1.ptr(0), *g2 = (double*)matG2.ptr(0);
|
||||
double a,a2, a3;
|
||||
double b1, b2;
|
||||
double c = (1 - 2 * exp(-alpha)*cos(omega) + exp(-2 * alpha)) / (exp(-alpha)*sin(omega));
|
||||
|
||||
a = -c*exp(-alpha)*sin(omega);
|
||||
a2 = 1;// kp*exp(-alpha);
|
||||
a3 = 1;//-kp*exp(-alpha);
|
||||
b1 = -2 * exp(-alpha)*cos(omega);
|
||||
b2 = exp(-2 * alpha);
|
||||
|
||||
for (int i = r.start; i<r.end; i++)
|
||||
{
|
||||
f1 = dst.ptr<float>(i);
|
||||
T *c1 = img.ptr<T>(i);
|
||||
int j = 0;
|
||||
g1[j] = a2* *c1;
|
||||
j++;
|
||||
c1++;
|
||||
g1[j] = a2*c1[j - 1] - (b1)* g1[j - 1];
|
||||
j++;
|
||||
c1++;
|
||||
for (j = 2; j<cols; j++, c1++)
|
||||
g1[j] = a2 * c1[-1] - b1*g1[j - 1] - b2*g1[j - 2];
|
||||
c1 = img.ptr<T>(0);
|
||||
c1 += i*cols + cols - 1;
|
||||
j = cols - 1;
|
||||
g2[j] = a3* *c1;
|
||||
j--;
|
||||
c1--;
|
||||
g2[j] = a3 * c1[1] - b1 * g2[j + 1];
|
||||
j--;
|
||||
c1--;
|
||||
for (j = cols - 3; j >= 0; j--, c1--)
|
||||
g2[j] = a3*c1[1] - b1*g2[j + 1] - b2*g2[j + 2];
|
||||
for (j = 0; j<cols; j++, f1++)
|
||||
*f1 = static_cast<float>(a*(g1[j] - g2[j]));
|
||||
}
|
||||
}
|
||||
|
||||
class ParallelGradientDericheYCols : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double alpha;
|
||||
double omega;
|
||||
bool verbose;
|
||||
|
||||
|
||||
public:
|
||||
ParallelGradientDericheYCols(Mat &imgSrc, Mat &d, double ald,double o) :
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
alpha(ald),
|
||||
omega(o),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_8UC1 || type == CV_8SC1 || type == CV_16SC1 || type == CV_16UC1 || type == CV_32FC1, "Wrong input type for GradientDericheYCols");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type==CV_32FC1, "Wrong output type for GradientDericheYCols");
|
||||
}
|
||||
void Verbose(bool b) { verbose = b; }
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum() << "# :Start from row " << range.start << " to " << range.end - 1 << " (" << range.end - range.start << " loops)" << std::endl;
|
||||
|
||||
switch (img.depth()) {
|
||||
case CV_8U:
|
||||
VerticalIIRFilter<uchar>(img,dst,range, alpha,omega);
|
||||
break;
|
||||
case CV_8S:
|
||||
VerticalIIRFilter<char>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
case CV_16U:
|
||||
VerticalIIRFilter<ushort>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
case CV_16S:
|
||||
VerticalIIRFilter<short>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
case CV_32F:
|
||||
VerticalIIRFilter<float>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
||||
ParallelGradientDericheYCols& operator=(const ParallelGradientDericheYCols &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ParallelGradientDericheYRows : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double alpha;
|
||||
double omega;
|
||||
bool verbose;
|
||||
|
||||
public:
|
||||
ParallelGradientDericheYRows(Mat& imgSrc, Mat &d, double ald,double o) :
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
alpha(ald),
|
||||
omega(o),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong input type for GradientDericheYRows");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientDericheYRows");
|
||||
}
|
||||
void Verbose(bool b) { verbose = b; }
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum() << "# :Start from row " << range.start << " to " << range.end - 1 << " (" << range.end - range.start << " loops)" << std::endl;
|
||||
float *f1, *f2;
|
||||
int tailleSequence = (img.rows>img.cols) ? img.rows : img.cols;
|
||||
Mat matG1(1,tailleSequence,CV_64FC1), matG2(1,tailleSequence,CV_64FC1);
|
||||
double *g1 = matG1.ptr<double>(0), *g2 = matG2.ptr<double>(0);
|
||||
int cols = img.cols;
|
||||
|
||||
|
||||
double a2po2 = (alpha*alpha + omega * omega);
|
||||
double k = (1 - 2 * exp(-alpha)*cos(omega) + exp(-2 * alpha))*a2po2;
|
||||
k = k / (2 * alpha*exp(-alpha)*sin(omega) + omega - omega * exp(-2 * alpha));
|
||||
double c1 = k * alpha / a2po2;
|
||||
double c2 = k * omega / a2po2;
|
||||
double a0 = c2;
|
||||
double a1 = (-c2 * cos(omega) + c1 * sin(omega))*exp(-alpha);
|
||||
double b1 = -2 * exp(-alpha)*cos(omega);
|
||||
double b2 = exp(-2 * alpha);
|
||||
double a2 = a1 - c2 * b1, a3 = -c2 * b2;
|
||||
|
||||
for (int i = range.start; i<range.end; i++)
|
||||
{
|
||||
f2 = dst.ptr<float>(i);
|
||||
f1 = img.ptr<float>(i);
|
||||
int j = 0;
|
||||
g1[j] = (a0 + a1)* *f1;
|
||||
j++;
|
||||
f1++;
|
||||
g1[j] = a0 * f1[0] + a1*f1[j - 1] - b1* g1[j - 1];
|
||||
j++;
|
||||
f1++;
|
||||
for (j = 2; j<cols; j++, f1++)
|
||||
g1[j] = a0 * f1[0] + a1 * f1[-1] - b1*g1[j - 1] - b2*g1[j - 2];
|
||||
f1 = ((float*)img.ptr(0));
|
||||
f1 += i*cols + cols - 1;
|
||||
j = cols - 1;
|
||||
g2[j] = (a2 + a3)* *f1;
|
||||
j--;
|
||||
f1--;
|
||||
g2[j] = (a2 + a3) * f1[1] - b2* g2[j + 1];
|
||||
j--;
|
||||
f1--;
|
||||
for (j = cols - 3; j >= 0; j--, f1--)
|
||||
g2[j] = a2*f1[1] + a3*f1[2] - b1*g2[j + 1] - b2*g2[j + 2];
|
||||
for (j = 0; j<cols; j++, f2++)
|
||||
*f2 = (float)(g1[j] + g2[j]);
|
||||
}
|
||||
|
||||
};
|
||||
ParallelGradientDericheYRows& operator=(const ParallelGradientDericheYRows &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ParallelGradientDericheXCols : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double alpha;
|
||||
double omega;
|
||||
bool verbose;
|
||||
|
||||
public:
|
||||
ParallelGradientDericheXCols(Mat& imgSrc, Mat &d, double alm,double o) :
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
alpha(alm),
|
||||
omega(o),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong input type for GradientDericheXCols");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientDericheXCols");
|
||||
}
|
||||
void Verbose(bool b) { verbose = b; }
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum() << "# :Start from row " << range.start << " to " << range.end - 1 << " (" << range.end - range.start << " loops)" << std::endl;
|
||||
float *f1, *f2;
|
||||
int rows = img.rows, cols = img.cols;
|
||||
|
||||
int tailleSequence = (rows>cols) ? rows : cols;
|
||||
Mat matG1(1,tailleSequence,CV_64FC1), matG2(1,tailleSequence,CV_64FC1);
|
||||
double *g1 = (double*)matG1.ptr(0), *g2 = (double*)matG2.ptr(0);
|
||||
double a2po2 = (alpha*alpha + omega * omega);
|
||||
double k = (1 - 2 * exp(-alpha)*cos(omega) + exp(-2 * alpha))*a2po2;
|
||||
k = k / (2 * alpha*exp(-alpha)*sin(omega) + omega - omega * exp(-2 * alpha));
|
||||
double c1 = k * alpha / a2po2;
|
||||
double c2 = k * omega / a2po2;
|
||||
double a0 = c2;
|
||||
double a1 = (-c2 * cos(omega) + c1 * sin(omega))*exp(-alpha);
|
||||
double b1 = -2 * exp(-alpha)*cos(omega);
|
||||
double b2=exp(-2*alpha);
|
||||
double a2=a1-c2*b1, a3=-c2*b2;
|
||||
|
||||
|
||||
for (int j = range.start; j<range.end; j++)
|
||||
{
|
||||
f1 = img.ptr<float>(0);
|
||||
f1 += j;
|
||||
int i = 0;
|
||||
g1[i] = (a0 + a1)* *f1;
|
||||
i++;
|
||||
f1 += cols;
|
||||
g1[i] = a0 * *f1 + a1 * f1[-cols] - (b1)* g1[i - 1];
|
||||
i++;
|
||||
f1 += cols;
|
||||
for (i = 2; i<rows; i++, f1 += cols)
|
||||
g1[i] = a0 * *f1 + a1 * f1[-cols] - b1*g1[i - 1] - b2 *g1[i - 2];
|
||||
f1 = img.ptr<float>(0);
|
||||
f1 += (rows - 1)*cols + j;
|
||||
i = rows - 1;
|
||||
g2[i] = (a2 + a3)* *f1;
|
||||
i--;
|
||||
f1 -= cols;
|
||||
g2[i] = (a2 + a3)* f1[cols] - b2*g2[i + 1];
|
||||
i--;
|
||||
f1 -= cols;
|
||||
for (i = rows - 3; i >= 0; i--, f1 -= cols)
|
||||
g2[i] = a2*f1[cols] + a3* f1[2 * cols] -
|
||||
b1*g2[i + 1] - b2*g2[i + 2];
|
||||
for (i = 0; i<rows; i++, f2 += cols)
|
||||
{
|
||||
f2 = (dst.ptr<float>(i)) + (j*img.channels());
|
||||
*f2 = (float)(g1[i] + g2[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
ParallelGradientDericheXCols& operator=(const ParallelGradientDericheXCols &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ParallelGradientDericheXRows : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double alpha;
|
||||
double omega;
|
||||
bool verbose;
|
||||
|
||||
public:
|
||||
ParallelGradientDericheXRows(Mat& imgSrc, Mat &d, double ald, double o) :
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
alpha(ald),
|
||||
omega(o),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_8UC1 || type == CV_8SC1 || type == CV_16SC1 || type == CV_16UC1 || type == CV_32FC1, "Wrong input type for GradientDericheXRows");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientDericheXRows");
|
||||
}
|
||||
void Verbose(bool b) { verbose = b; }
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum() << "# :Start from row " << range.start << " to " << range.end - 1 << " (" << range.end - range.start << " loops)" << std::endl;
|
||||
|
||||
switch (img.depth()) {
|
||||
case CV_8U:
|
||||
HorizontalIIRFilter<uchar>(img,dst,range, alpha,omega);
|
||||
break;
|
||||
case CV_8S:
|
||||
HorizontalIIRFilter<char>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
case CV_16U:
|
||||
HorizontalIIRFilter<ushort>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
case CV_16S:
|
||||
HorizontalIIRFilter<short>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
case CV_32F:
|
||||
HorizontalIIRFilter<float>(img, dst, range, alpha, omega);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
||||
ParallelGradientDericheXRows& operator=(const ParallelGradientDericheXRows &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
void GradientDericheY(InputArray _op, OutputArray _dst,double alphaDerive, double omega)
|
||||
{
|
||||
std::vector<Mat> planSrc;
|
||||
split(_op, planSrc);
|
||||
|
||||
std::vector<Mat> planTmp;
|
||||
std::vector<Mat> planDst;
|
||||
for (size_t i = 0; i < planSrc.size(); i++)
|
||||
{
|
||||
planTmp.push_back(Mat(_op.size(), CV_32FC1));
|
||||
planDst.push_back(Mat(_op.size(), CV_32FC1));
|
||||
CV_Assert(planSrc[i].isContinuous() && planTmp[i].isContinuous() && planDst[i].isContinuous());
|
||||
ParallelGradientDericheYCols x(planSrc[i], planTmp[i], alphaDerive,omega);
|
||||
parallel_for_(Range(0, planSrc[i].cols), x, getNumThreads());
|
||||
ParallelGradientDericheYRows xr(planTmp[i], planDst[i], alphaDerive, omega);
|
||||
parallel_for_(Range(0, planTmp[i].rows), xr, getNumThreads());
|
||||
}
|
||||
merge(planDst, _dst);
|
||||
}
|
||||
|
||||
void GradientDericheX(InputArray _op, OutputArray _dst, double alpha, double omega)
|
||||
{
|
||||
std::vector<Mat> planSrc;
|
||||
split(_op, planSrc);
|
||||
std::vector<Mat> planTmp;
|
||||
std::vector<Mat> planDst;
|
||||
for (size_t i = 0; i < planSrc.size(); i++)
|
||||
{
|
||||
planTmp.push_back(Mat(_op.size(), CV_32FC1));
|
||||
planDst.push_back(Mat(_op.size(), CV_32FC1));
|
||||
CV_Assert(planSrc[i].isContinuous() && planTmp[i].isContinuous() && planDst[i].isContinuous());
|
||||
|
||||
ParallelGradientDericheXRows x(planSrc[i], planTmp[i], alpha, omega);
|
||||
parallel_for_(Range(0, planSrc[i].rows), x, getNumThreads());
|
||||
ParallelGradientDericheXCols xr(planTmp[i], planDst[i], alpha, omega);
|
||||
parallel_for_(Range(0, planTmp[i].cols), xr, getNumThreads());
|
||||
}
|
||||
merge(planDst, _dst);
|
||||
}
|
||||
|
||||
} //end of cv::ximgproc
|
||||
} //end of cv
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/ximgproc/disparity_filter.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
using std::vector;
|
||||
#define EPS 1e-43f
|
||||
|
||||
class DisparityWLSFilterImpl : public DisparityWLSFilter
|
||||
{
|
||||
protected:
|
||||
int left_offset, right_offset, top_offset, bottom_offset;
|
||||
Rect valid_disp_ROI;
|
||||
Rect right_view_valid_disp_ROI;
|
||||
int min_disp;
|
||||
bool use_confidence;
|
||||
Mat confidence_map;
|
||||
|
||||
double lambda,sigma_color;
|
||||
int LRC_thresh,depth_discontinuity_radius;
|
||||
float depth_discontinuity_roll_off_factor;
|
||||
float resize_factor;
|
||||
int num_stripes;
|
||||
|
||||
void init(double _lambda, double _sigma_color, bool _use_confidence, int l_offs, int r_offs, int t_offs, int b_offs, int _min_disp);
|
||||
void computeDepthDiscontinuityMaps(Mat& left_disp, Mat& right_disp, Mat& left_dst, Mat& right_dst);
|
||||
void computeConfidenceMap(InputArray left_disp, InputArray right_disp);
|
||||
|
||||
protected:
|
||||
struct ComputeDiscontinuityAwareLRC_ParBody : public ParallelLoopBody
|
||||
{
|
||||
DisparityWLSFilterImpl* wls;
|
||||
Mat *left_disp, *right_disp;
|
||||
Mat *left_disc, *right_disc, *dst;
|
||||
Rect left_ROI, right_ROI;
|
||||
int nstripes, stripe_sz;
|
||||
|
||||
ComputeDiscontinuityAwareLRC_ParBody(DisparityWLSFilterImpl& _wls, Mat& _left_disp, Mat& _right_disp, Mat& _left_disc, Mat& _right_disc, Mat& _dst, Rect _left_ROI, Rect _right_ROI, int _nstripes);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeDepthDisc_ParBody : public ParallelLoopBody
|
||||
{
|
||||
DisparityWLSFilterImpl* wls;
|
||||
Mat *disp,*disp_squares,*dst;
|
||||
int nstripes, stripe_sz;
|
||||
|
||||
ComputeDepthDisc_ParBody(DisparityWLSFilterImpl& _wls, Mat& _disp, Mat& _disp_squares, Mat& _dst, int _nstripes);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
typedef void (DisparityWLSFilterImpl::*MatOp)(Mat& src, Mat& dst);
|
||||
|
||||
struct ParallelMatOp_ParBody : public ParallelLoopBody
|
||||
{
|
||||
DisparityWLSFilterImpl* wls;
|
||||
vector<MatOp> ops;
|
||||
vector<Mat*> src;
|
||||
vector<Mat*> dst;
|
||||
|
||||
ParallelMatOp_ParBody(DisparityWLSFilterImpl& _wls, vector<MatOp> _ops, vector<Mat*>& _src, vector<Mat*>& _dst);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
void boxFilterOp(Mat& src,Mat& dst)
|
||||
{
|
||||
int rad = depth_discontinuity_radius;
|
||||
boxFilter(src,dst,CV_32F,Size(2*rad+1,2*rad+1),Point(-1,-1));
|
||||
}
|
||||
|
||||
void sqrBoxFilterOp(Mat& src,Mat& dst)
|
||||
{
|
||||
int rad = depth_discontinuity_radius;
|
||||
sqrBoxFilter(src,dst,CV_32F,Size(2*rad+1,2*rad+1),Point(-1,-1));
|
||||
}
|
||||
|
||||
void copyToOp(Mat& src,Mat& dst)
|
||||
{
|
||||
src.copyTo(dst);
|
||||
}
|
||||
|
||||
public:
|
||||
static Ptr<DisparityWLSFilterImpl> create(bool _use_confidence, int l_offs, int r_offs, int t_offs, int b_offs, int min_disp);
|
||||
void filter(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, InputArray disparity_map_right, Rect ROI, InputArray) CV_OVERRIDE;
|
||||
void filter_(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, InputArray disparity_map_right, Rect ROI);
|
||||
|
||||
double getLambda() CV_OVERRIDE { return lambda; }
|
||||
void setLambda(double _lambda) CV_OVERRIDE { lambda = _lambda; }
|
||||
|
||||
double getSigmaColor() CV_OVERRIDE { return sigma_color; }
|
||||
void setSigmaColor(double _sigma_color) CV_OVERRIDE { sigma_color = _sigma_color; }
|
||||
|
||||
int getLRCthresh() CV_OVERRIDE { return LRC_thresh; }
|
||||
void setLRCthresh(int _LRC_thresh) CV_OVERRIDE { LRC_thresh = _LRC_thresh; }
|
||||
|
||||
int getDepthDiscontinuityRadius() CV_OVERRIDE { return depth_discontinuity_radius; }
|
||||
void setDepthDiscontinuityRadius(int _disc_radius) CV_OVERRIDE { depth_discontinuity_radius = _disc_radius; }
|
||||
|
||||
Mat getConfidenceMap() CV_OVERRIDE { return confidence_map; }
|
||||
Rect getROI() CV_OVERRIDE { return valid_disp_ROI; }
|
||||
};
|
||||
|
||||
void DisparityWLSFilterImpl::init(double _lambda, double _sigma_color, bool _use_confidence, int l_offs, int r_offs, int t_offs, int b_offs, int _min_disp)
|
||||
{
|
||||
left_offset = l_offs; right_offset = r_offs;
|
||||
top_offset = t_offs; bottom_offset = b_offs;
|
||||
min_disp = _min_disp;
|
||||
valid_disp_ROI = Rect();
|
||||
right_view_valid_disp_ROI = Rect();
|
||||
lambda = _lambda;
|
||||
sigma_color = _sigma_color;
|
||||
use_confidence = _use_confidence;
|
||||
confidence_map = Mat();
|
||||
LRC_thresh = 24;
|
||||
depth_discontinuity_radius = 5;
|
||||
depth_discontinuity_roll_off_factor = 0.001f;
|
||||
resize_factor = 1.0;
|
||||
num_stripes = getNumThreads();
|
||||
}
|
||||
|
||||
void DisparityWLSFilterImpl::computeDepthDiscontinuityMaps(Mat& left_disp, Mat& right_disp, Mat& left_dst, Mat& right_dst)
|
||||
{
|
||||
Mat left_disp_ROI (left_disp, valid_disp_ROI);
|
||||
Mat right_disp_ROI(right_disp,right_view_valid_disp_ROI);
|
||||
Mat ldisp,rdisp,ldisp_squared,rdisp_squared;
|
||||
|
||||
{
|
||||
vector<Mat*> _src; _src.push_back(&left_disp_ROI);_src.push_back(&right_disp_ROI);
|
||||
_src.push_back(&left_disp_ROI);_src.push_back(&right_disp_ROI);
|
||||
vector<Mat*> _dst; _dst.push_back(&ldisp); _dst.push_back(&rdisp);
|
||||
_dst.push_back(&ldisp_squared);_dst.push_back(&rdisp_squared);
|
||||
vector<MatOp> _ops; _ops.push_back(&DisparityWLSFilterImpl::copyToOp); _ops.push_back(&DisparityWLSFilterImpl::copyToOp);
|
||||
_ops.push_back(&DisparityWLSFilterImpl::copyToOp); _ops.push_back(&DisparityWLSFilterImpl::copyToOp);
|
||||
parallel_for_(Range(0,4),ParallelMatOp_ParBody(*this,_ops,_src,_dst));
|
||||
}
|
||||
|
||||
{
|
||||
vector<Mat*> _src; _src.push_back(&ldisp); _src.push_back(&rdisp);
|
||||
_src.push_back(&ldisp_squared);_src.push_back(&rdisp_squared);
|
||||
vector<Mat*> _dst; _dst.push_back(&ldisp); _dst.push_back(&rdisp);
|
||||
_dst.push_back(&ldisp_squared);_dst.push_back(&rdisp_squared);
|
||||
vector<MatOp> _ops; _ops.push_back(&DisparityWLSFilterImpl::boxFilterOp); _ops.push_back(&DisparityWLSFilterImpl::boxFilterOp);
|
||||
_ops.push_back(&DisparityWLSFilterImpl::sqrBoxFilterOp);_ops.push_back(&DisparityWLSFilterImpl::sqrBoxFilterOp);
|
||||
parallel_for_(Range(0,4),ParallelMatOp_ParBody(*this,_ops,_src,_dst));
|
||||
}
|
||||
|
||||
left_dst = Mat::zeros(left_disp.rows,left_disp.cols,CV_32F);
|
||||
right_dst = Mat::zeros(right_disp.rows,right_disp.cols,CV_32F);
|
||||
Mat left_dst_ROI (left_dst,valid_disp_ROI);
|
||||
Mat right_dst_ROI(right_dst,right_view_valid_disp_ROI);
|
||||
|
||||
parallel_for_(Range(0,num_stripes),ComputeDepthDisc_ParBody(*this,ldisp,ldisp_squared,left_dst_ROI ,num_stripes));
|
||||
parallel_for_(Range(0,num_stripes),ComputeDepthDisc_ParBody(*this,rdisp,rdisp_squared,right_dst_ROI,num_stripes));
|
||||
}
|
||||
|
||||
|
||||
void DisparityWLSFilterImpl::computeConfidenceMap(InputArray left_disp, InputArray right_disp)
|
||||
{
|
||||
Mat ldisp = left_disp.getMat();
|
||||
Mat rdisp = right_disp.getMat();
|
||||
Mat depth_discontinuity_map_left,depth_discontinuity_map_right;
|
||||
right_view_valid_disp_ROI = Rect(ldisp.cols-(valid_disp_ROI.x+valid_disp_ROI.width),valid_disp_ROI.y,
|
||||
valid_disp_ROI.width,valid_disp_ROI.height);
|
||||
computeDepthDiscontinuityMaps(ldisp,rdisp,depth_discontinuity_map_left,depth_discontinuity_map_right);
|
||||
|
||||
confidence_map = depth_discontinuity_map_left;
|
||||
|
||||
parallel_for_(Range(0,num_stripes),ComputeDiscontinuityAwareLRC_ParBody(*this,ldisp,rdisp, depth_discontinuity_map_left,depth_discontinuity_map_right,confidence_map,valid_disp_ROI,right_view_valid_disp_ROI,num_stripes));
|
||||
confidence_map = 255.0f*confidence_map;
|
||||
}
|
||||
|
||||
Ptr<DisparityWLSFilterImpl> DisparityWLSFilterImpl::create(bool _use_confidence, int l_offs=0, int r_offs=0, int t_offs=0, int b_offs=0, int min_disp=0)
|
||||
{
|
||||
DisparityWLSFilterImpl *wls = new DisparityWLSFilterImpl();
|
||||
wls->init(8000.0,1.0,_use_confidence,l_offs, r_offs, t_offs, b_offs, min_disp);
|
||||
return Ptr<DisparityWLSFilterImpl>(wls);
|
||||
}
|
||||
|
||||
void DisparityWLSFilterImpl::filter(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, InputArray disparity_map_right, Rect ROI, InputArray)
|
||||
{
|
||||
CV_Assert(!disparity_map_left.empty() && (disparity_map_left.channels() == 1));
|
||||
CV_Assert(!left_view.empty() && (left_view.depth() == CV_8U) && (left_view.channels() == 3 || left_view.channels() == 1));
|
||||
Mat left, right, filt_disp;
|
||||
|
||||
if (disparity_map_left.depth() != CV_32F)
|
||||
{
|
||||
disparity_map_left.getMat().convertTo(left, CV_32F);
|
||||
}
|
||||
else
|
||||
{
|
||||
left = disparity_map_left.getMat();
|
||||
filt_disp = filtered_disparity_map.getMat();
|
||||
}
|
||||
|
||||
if (!disparity_map_right.empty() && use_confidence)
|
||||
{
|
||||
if(disparity_map_right.depth() != CV_32F)
|
||||
disparity_map_right.getMat().convertTo(right, CV_32F);
|
||||
else
|
||||
right = disparity_map_right.getMat();
|
||||
}
|
||||
|
||||
filter_(left, left_view, filt_disp, right, ROI);
|
||||
if (disparity_map_left.depth() != CV_32F){
|
||||
filt_disp.convertTo(filtered_disparity_map, disparity_map_left.depth());
|
||||
} else {
|
||||
filt_disp.copyTo(filtered_disparity_map);
|
||||
}
|
||||
}
|
||||
|
||||
void DisparityWLSFilterImpl::filter_(InputArray disparity_map_left, InputArray left_view, OutputArray filtered_disparity_map, InputArray disparity_map_right, Rect ROI)
|
||||
{
|
||||
CV_Assert( !disparity_map_left.empty()
|
||||
&& ( disparity_map_left.depth() == CV_32F)
|
||||
&& (disparity_map_left.channels() == 1) );
|
||||
CV_Assert( !left_view.empty()
|
||||
&& (left_view.depth() == CV_8U)
|
||||
&& (left_view.channels() == 3 || left_view.channels() == 1) );
|
||||
|
||||
Mat disp,src,dst;
|
||||
if(disparity_map_left.size()!=left_view.size())
|
||||
resize_factor = disparity_map_left.cols()/(float)left_view.cols();
|
||||
else
|
||||
resize_factor = 1.0;
|
||||
if(!ROI.empty()) /* user provided a ROI */
|
||||
valid_disp_ROI = ROI;
|
||||
else
|
||||
valid_disp_ROI = Rect(left_offset,top_offset,
|
||||
disparity_map_left.cols()-left_offset-right_offset,
|
||||
disparity_map_left.rows()-top_offset-bottom_offset);
|
||||
|
||||
if(!use_confidence)
|
||||
{
|
||||
Mat disp_full_size = disparity_map_left.getMat();
|
||||
Mat src_full_size = left_view.getMat();
|
||||
if(disp_full_size.size!=src_full_size.size)
|
||||
{
|
||||
float x_ratio = src_full_size.cols/(float)disp_full_size.cols;
|
||||
float y_ratio = src_full_size.rows/(float)disp_full_size.rows;
|
||||
resize(disp_full_size,disp_full_size,src_full_size.size());
|
||||
disp_full_size = disp_full_size*x_ratio;
|
||||
ROI = Rect((int)(valid_disp_ROI.x*x_ratio), (int)(valid_disp_ROI.y*y_ratio),
|
||||
(int)(valid_disp_ROI.width*x_ratio),(int)(valid_disp_ROI.height*y_ratio));
|
||||
}
|
||||
else
|
||||
ROI = valid_disp_ROI;
|
||||
|
||||
disp = Mat(disp_full_size,ROI);
|
||||
src = Mat(src_full_size ,ROI);
|
||||
filtered_disparity_map.create(disp_full_size.size(), disp_full_size.type());
|
||||
Mat& dst_full_size = filtered_disparity_map.getMatRef();
|
||||
dst_full_size = Scalar(16*(min_disp-1));
|
||||
dst = Mat(dst_full_size,ROI);
|
||||
Mat filtered_disp;
|
||||
fastGlobalSmootherFilter(src,disp,filtered_disp,lambda,sigma_color);
|
||||
filtered_disp.copyTo(dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert( !disparity_map_right.empty()
|
||||
&& (disparity_map_right.depth() == CV_32F)
|
||||
&& (disparity_map_right.channels() == 1) );
|
||||
CV_Assert( (disparity_map_left.cols() == disparity_map_right.cols()) );
|
||||
CV_Assert( (disparity_map_left.rows() == disparity_map_right.rows()) );
|
||||
computeConfidenceMap(disparity_map_left,disparity_map_right);
|
||||
Mat disp_full_size = disparity_map_left.getMat();
|
||||
Mat src_full_size = left_view.getMat();
|
||||
if(disp_full_size.size!=src_full_size.size){
|
||||
float x_ratio = src_full_size.cols/(float)disp_full_size.cols;
|
||||
float y_ratio = src_full_size.rows/(float)disp_full_size.rows;
|
||||
resize(disp_full_size,disp_full_size,src_full_size.size());
|
||||
disp_full_size = disp_full_size*x_ratio;
|
||||
resize(confidence_map,confidence_map,src_full_size.size());
|
||||
ROI = Rect((int)(valid_disp_ROI.x*x_ratio), (int)(valid_disp_ROI.y*y_ratio),
|
||||
(int)(valid_disp_ROI.width*x_ratio),(int)(valid_disp_ROI.height*y_ratio));
|
||||
} else {
|
||||
ROI = valid_disp_ROI;
|
||||
}
|
||||
disp = Mat(disp_full_size,ROI);
|
||||
src = Mat(src_full_size ,ROI);
|
||||
filtered_disparity_map.create(disp_full_size.size(), disp_full_size.type());
|
||||
Mat& dst_full_size = filtered_disparity_map.getMatRef();
|
||||
dst_full_size = Scalar(16*(min_disp-1));
|
||||
dst = Mat(dst_full_size,ROI);
|
||||
Mat conf(confidence_map,ROI);
|
||||
|
||||
Mat disp_mul_conf;
|
||||
disp_mul_conf = conf.mul(disp);
|
||||
Mat conf_filtered;
|
||||
Ptr<FastGlobalSmootherFilter> wls = createFastGlobalSmootherFilter(src,lambda,sigma_color);
|
||||
wls->filter(disp_mul_conf,disp_mul_conf);
|
||||
wls->filter(conf,conf_filtered);
|
||||
dst = disp_mul_conf.mul(1/(conf_filtered+EPS));
|
||||
}
|
||||
}
|
||||
|
||||
DisparityWLSFilterImpl::ComputeDiscontinuityAwareLRC_ParBody::ComputeDiscontinuityAwareLRC_ParBody(DisparityWLSFilterImpl& _wls, Mat& _left_disp, Mat& _right_disp, Mat& _left_disc, Mat& _right_disc, Mat& _dst, Rect _left_ROI, Rect _right_ROI, int _nstripes):
|
||||
wls(&_wls),left_disp(&_left_disp),right_disp(&_right_disp),left_disc(&_left_disc),right_disc(&_right_disc),dst(&_dst),left_ROI(_left_ROI),right_ROI(_right_ROI),nstripes(_nstripes)
|
||||
{
|
||||
stripe_sz = (int)ceil(left_disp->rows/(double)nstripes);
|
||||
}
|
||||
|
||||
void DisparityWLSFilterImpl::ComputeDiscontinuityAwareLRC_ParBody::operator() (const Range& range) const
|
||||
{
|
||||
float* row_left;
|
||||
float* row_left_conf;
|
||||
float* row_right;
|
||||
float* row_right_conf;
|
||||
float* row_dst;
|
||||
int right_idx;
|
||||
int h = left_disp->rows;
|
||||
|
||||
int start = std::min(range.start * stripe_sz, h);
|
||||
int end = std::min(range.end * stripe_sz, h);
|
||||
int thresh = (int)(wls->resize_factor*wls->LRC_thresh);
|
||||
for(int i=start;i<end;i++)
|
||||
{
|
||||
row_left = (float*)left_disp->ptr(i);
|
||||
row_left_conf = (float*)left_disc->ptr(i);
|
||||
row_right = (float*)right_disp->ptr(i);
|
||||
row_right_conf = (float*)right_disc->ptr(i);
|
||||
row_dst = (float*)dst->ptr(i);
|
||||
int j_start = left_ROI.x;
|
||||
int j_end = left_ROI.x+left_ROI.width;
|
||||
int right_end = right_ROI.x+right_ROI.width;
|
||||
for(int j=j_start;j<j_end;j++)
|
||||
{
|
||||
right_idx = j-(((int)row_left[j])>>4);
|
||||
if( right_idx>=right_ROI.x && right_idx<right_end)
|
||||
{
|
||||
if(abs(row_left[j] + row_right[right_idx])< thresh)
|
||||
row_dst[j] = min(row_left_conf[j],row_right_conf[right_idx]);
|
||||
else
|
||||
row_dst[j] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisparityWLSFilterImpl::ComputeDepthDisc_ParBody::ComputeDepthDisc_ParBody(DisparityWLSFilterImpl& _wls, Mat& _disp, Mat& _disp_squares, Mat& _dst, int _nstripes):
|
||||
wls(&_wls),disp(&_disp),disp_squares(&_disp_squares),dst(&_dst),nstripes(_nstripes)
|
||||
{
|
||||
stripe_sz = (int)ceil(disp->rows/(double)nstripes);
|
||||
}
|
||||
|
||||
void DisparityWLSFilterImpl::ComputeDepthDisc_ParBody::operator() (const Range& range) const
|
||||
{
|
||||
float* row_disp;
|
||||
float* row_disp_squares;
|
||||
float* row_dst;
|
||||
float variance;
|
||||
int h = disp->rows;
|
||||
int w = disp->cols;
|
||||
int start = std::min(range.start * stripe_sz, h);
|
||||
int end = std::min(range.end * stripe_sz, h);
|
||||
float roll_off = wls->depth_discontinuity_roll_off_factor/(wls->resize_factor*wls->resize_factor);
|
||||
|
||||
for(int i=start;i<end;i++)
|
||||
{
|
||||
row_disp = (float*)disp->ptr(i);
|
||||
row_disp_squares = (float*)disp_squares->ptr(i);
|
||||
row_dst = (float*)dst->ptr(i);
|
||||
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
variance = row_disp_squares[j] - (row_disp[j])*(row_disp[j]);
|
||||
row_dst[j] = max(1.0f - roll_off*variance,0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisparityWLSFilterImpl::ParallelMatOp_ParBody::ParallelMatOp_ParBody(DisparityWLSFilterImpl& _wls, vector<MatOp> _ops, vector<Mat*>& _src, vector<Mat*>& _dst):
|
||||
wls(&_wls),ops(_ops),src(_src),dst(_dst)
|
||||
{}
|
||||
|
||||
void DisparityWLSFilterImpl::ParallelMatOp_ParBody::operator() (const Range& range) const
|
||||
{
|
||||
for(int i=range.start;i<range.end;i++)
|
||||
(wls->*ops[i])(*src[i],*dst[i]);
|
||||
}
|
||||
|
||||
Ptr<DisparityWLSFilter> createDisparityWLSFilter(Ptr<StereoMatcher> matcher_left)
|
||||
{
|
||||
Ptr<DisparityWLSFilter> wls;
|
||||
matcher_left->setDisp12MaxDiff(1000000);
|
||||
matcher_left->setSpeckleWindowSize(0);
|
||||
|
||||
int min_disp = matcher_left->getMinDisparity();
|
||||
int num_disp = matcher_left->getNumDisparities();
|
||||
int wsize = matcher_left->getBlockSize();
|
||||
int wsize2 = wsize/2;
|
||||
|
||||
if(Ptr<StereoBM> bm = matcher_left.dynamicCast<StereoBM>())
|
||||
{
|
||||
bm->setTextureThreshold(0);
|
||||
bm->setUniquenessRatio(0);
|
||||
wls = DisparityWLSFilterImpl::create(true,max(0,min_disp+num_disp)+wsize2,max(0,-min_disp)+wsize2,wsize2,wsize2,min_disp);
|
||||
wls->setDepthDiscontinuityRadius((int)ceil(0.33*wsize));
|
||||
}
|
||||
else if(Ptr<StereoSGBM> sgbm = matcher_left.dynamicCast<StereoSGBM>())
|
||||
{
|
||||
sgbm->setUniquenessRatio(0);
|
||||
wls = DisparityWLSFilterImpl::create(true,max(0,min_disp+num_disp),max(0,-min_disp),0,0,min_disp);
|
||||
wls->setDepthDiscontinuityRadius((int)ceil(0.5*wsize));
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsBadArg, "DisparityWLSFilter natively supports only StereoBM and StereoSGBM");
|
||||
|
||||
return wls;
|
||||
}
|
||||
|
||||
Ptr<StereoMatcher> createRightMatcher(Ptr<StereoMatcher> matcher_left)
|
||||
{
|
||||
int min_disp = matcher_left->getMinDisparity();
|
||||
int num_disp = matcher_left->getNumDisparities();
|
||||
int wsize = matcher_left->getBlockSize();
|
||||
if(Ptr<StereoBM> bm = matcher_left.dynamicCast<StereoBM>())
|
||||
{
|
||||
Ptr<StereoBM> right_bm = StereoBM::create(num_disp,wsize);
|
||||
right_bm->setMinDisparity(-(min_disp+num_disp)+1);
|
||||
right_bm->setTextureThreshold(0);
|
||||
right_bm->setUniquenessRatio(0);
|
||||
right_bm->setDisp12MaxDiff(1000000);
|
||||
right_bm->setSpeckleWindowSize(0);
|
||||
return right_bm;
|
||||
}
|
||||
else if(Ptr<StereoSGBM> sgbm = matcher_left.dynamicCast<StereoSGBM>())
|
||||
{
|
||||
Ptr<StereoSGBM> right_sgbm = StereoSGBM::create(-(min_disp+num_disp)+1,num_disp,wsize);
|
||||
right_sgbm->setUniquenessRatio(0);
|
||||
right_sgbm->setP1(sgbm->getP1());
|
||||
right_sgbm->setP2(sgbm->getP2());
|
||||
right_sgbm->setMode(sgbm->getMode());
|
||||
right_sgbm->setPreFilterCap(sgbm->getPreFilterCap());
|
||||
right_sgbm->setDisp12MaxDiff(1000000);
|
||||
right_sgbm->setSpeckleWindowSize(0);
|
||||
return right_sgbm;
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "createRightMatcher supports only StereoBM and StereoSGBM");
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<DisparityWLSFilter> createDisparityWLSFilterGeneric(bool use_confidence)
|
||||
{
|
||||
return Ptr<DisparityWLSFilter>(DisparityWLSFilterImpl::create(use_confidence));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define UNKNOWN_DISPARITY 16320
|
||||
|
||||
int readGT(String src_path,OutputArray dst)
|
||||
{
|
||||
Mat src = imread(src_path,IMREAD_UNCHANGED);
|
||||
dst.create(src.rows,src.cols,CV_16S);
|
||||
Mat& dstMat = dst.getMatRef();
|
||||
|
||||
if(!src.empty() && src.channels()==3 && src.depth()==CV_8U)
|
||||
{
|
||||
//MPI-Sintel format:
|
||||
for(int i=0;i<src.rows;i++)
|
||||
for(int j=0;j<src.cols;j++)
|
||||
{
|
||||
Vec3b bgrPixel = src.at<Vec3b>(i, j);
|
||||
dstMat.at<short>(i,j) = 64*bgrPixel.val[2]+bgrPixel.val[1]/4; //16-multiplied disparity
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if(!src.empty() && src.channels()==1 && src.depth()==CV_8U)
|
||||
{
|
||||
//Middlebury format:
|
||||
for(int i=0;i<src.rows;i++)
|
||||
for(int j=0;j<src.cols;j++)
|
||||
{
|
||||
short src_val = src.at<unsigned char>(i, j);
|
||||
if(src_val==0)
|
||||
dstMat.at<short>(i,j) = UNKNOWN_DISPARITY;
|
||||
else
|
||||
dstMat.at<short>(i,j) = 16*src_val; //16-multiplied disparity
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
double computeMSE(InputArray GT, InputArray src, Rect ROI)
|
||||
{
|
||||
CV_Assert( !GT.empty() && (GT.depth() == CV_16S || GT.depth() == CV_32F) && (GT.channels() == 1) );
|
||||
CV_Assert( !src.empty() && (src.depth() == CV_16S || src.depth() == CV_32F) && (src.channels() == 1) );
|
||||
CV_Assert( src.rows() == GT.rows() && src.cols() == GT.cols() );
|
||||
Mat GT_ROI (GT.getMat(), ROI);
|
||||
Mat src_ROI(src.getMat(),ROI);
|
||||
|
||||
Mat tmp, dtmp, gt_mask = (GT_ROI == UNKNOWN_DISPARITY);
|
||||
absdiff(GT_ROI, src_ROI, tmp); tmp.setTo(0, gt_mask); multiply(tmp, tmp, tmp);
|
||||
tmp.convertTo(dtmp, CV_64FC1);
|
||||
return sum(dtmp)[0] / ((gt_mask.total() - countNonZero(gt_mask))*256);
|
||||
}
|
||||
|
||||
double computeBadPixelPercent(InputArray GT, InputArray src, Rect ROI, int thresh)
|
||||
{
|
||||
CV_Assert( !GT.empty() && (GT.depth() == CV_16S || GT.depth() == CV_32F) && (GT.channels() == 1) );
|
||||
CV_Assert( !src.empty() && (src.depth() == CV_16S || src.depth() == CV_32F) && (src.channels() == 1) );
|
||||
CV_Assert( src.rows() == GT.rows() && src.cols() == GT.cols() );
|
||||
Mat GT_ROI (GT.getMat(), ROI);
|
||||
Mat src_ROI(src.getMat(),ROI);
|
||||
|
||||
Mat tmp, gt_mask = (GT_ROI == UNKNOWN_DISPARITY);
|
||||
absdiff(GT_ROI, src_ROI, tmp); tmp.setTo(0, gt_mask);
|
||||
cv::threshold(tmp, tmp, thresh - 1, 1, THRESH_BINARY);
|
||||
return (100.0 * countNonZero(tmp)) / (gt_mask.total() - countNonZero(gt_mask));
|
||||
}
|
||||
|
||||
void getDisparityVis(InputArray src,OutputArray dst,double scale)
|
||||
{
|
||||
CV_Assert( !src.empty() && (src.depth() == CV_16S || src.depth() == CV_32F) && (src.channels() == 1) );
|
||||
Mat srcMat = src.getMat();
|
||||
dst.create(srcMat.rows,srcMat.cols,CV_8UC1);
|
||||
Mat& dstMat = dst.getMatRef();
|
||||
|
||||
srcMat.convertTo(dstMat, CV_8UC1, scale / 16.0);
|
||||
dstMat &= (srcMat != UNKNOWN_DISPARITY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "dtfilter_cpu.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
CV_EXPORTS_W
|
||||
Ptr<DTFilter> createDTFilter(InputArray guide, double sigmaSpatial, double sigmaColor, int mode, int numIters)
|
||||
{
|
||||
return Ptr<DTFilter>(DTFilterCPU::create(guide, sigmaSpatial, sigmaColor, mode, numIters));
|
||||
}
|
||||
|
||||
CV_EXPORTS_W
|
||||
void dtFilter(InputArray guide, InputArray src, OutputArray dst, double sigmaSpatial, double sigmaColor, int mode, int numIters)
|
||||
{
|
||||
Ptr<DTFilterCPU> dtf = DTFilterCPU::create(guide, sigmaSpatial, sigmaColor, mode, numIters);
|
||||
dtf->setSingleFilterCall(true);
|
||||
dtf->filter(src, dst);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "dtfilter_cpu.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
typedef Vec<uchar, 1> Vec1b;
|
||||
typedef Vec<float, 1> Vec1f;
|
||||
|
||||
Ptr<DTFilterCPU> DTFilterCPU::create(InputArray guide, double sigmaSpatial, double sigmaColor, int mode, int numIters)
|
||||
{
|
||||
Ptr<DTFilterCPU> dtf(new DTFilterCPU());
|
||||
dtf->init(guide, sigmaSpatial, sigmaColor, mode, numIters);
|
||||
return dtf;
|
||||
}
|
||||
|
||||
Ptr<DTFilterCPU> DTFilterCPU::createRF(InputArray adistHor, InputArray adistVert, double sigmaSpatial, double sigmaColor, int numIters /*= 3*/)
|
||||
{
|
||||
Mat adh = adistHor.getMat();
|
||||
Mat adv = adistVert.getMat();
|
||||
CV_Assert(adh.type() == CV_32FC1 && adv.type() == CV_32FC1 && adh.rows == adv.rows + 1 && adh.cols == adv.cols - 1);
|
||||
|
||||
Ptr<DTFilterCPU> dtf(new DTFilterCPU());
|
||||
dtf->release();
|
||||
dtf->mode = DTF_RF;
|
||||
dtf->numIters = std::max(1, numIters);
|
||||
|
||||
dtf->h = adh.rows;
|
||||
dtf->w = adh.cols + 1;
|
||||
|
||||
dtf->sigmaSpatial = std::max(1.0f, (float)sigmaSpatial);
|
||||
dtf->sigmaColor = std::max(0.01f, (float)sigmaColor);
|
||||
|
||||
dtf->a0distHor = adh;
|
||||
dtf->a0distVert = adv;
|
||||
|
||||
return dtf;
|
||||
}
|
||||
|
||||
void DTFilterCPU::init(InputArray guide_, double sigmaSpatial_, double sigmaColor_, int mode_, int numIters_)
|
||||
{
|
||||
Mat guide = guide_.getMat();
|
||||
|
||||
int cn = guide.channels();
|
||||
int depth = guide.depth();
|
||||
|
||||
CV_Assert(cn <= 4);
|
||||
CV_Assert((depth == CV_8U || depth == CV_32F) && !guide.empty());
|
||||
|
||||
#define CREATE_DTF(Vect) init_<Vect>(guide, sigmaSpatial_, sigmaColor_, mode_, numIters_);
|
||||
|
||||
if (cn == 1)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
CREATE_DTF(Vec1b);
|
||||
if (depth == CV_32F)
|
||||
CREATE_DTF(Vec1f);
|
||||
}
|
||||
else if (cn == 2)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
CREATE_DTF(Vec2b);
|
||||
if (depth == CV_32F)
|
||||
CREATE_DTF(Vec2f);
|
||||
}
|
||||
else if (cn == 3)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
CREATE_DTF(Vec3b);
|
||||
if (depth == CV_32F)
|
||||
CREATE_DTF(Vec3f);
|
||||
}
|
||||
else if (cn == 4)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
CREATE_DTF(Vec4b);
|
||||
if (depth == CV_32F)
|
||||
CREATE_DTF(Vec4f);
|
||||
}
|
||||
|
||||
#undef CREATE_DTF
|
||||
}
|
||||
|
||||
void DTFilterCPU::filter(InputArray src_, OutputArray dst_, int dDepth)
|
||||
{
|
||||
Mat src = src_.getMat();
|
||||
dst_.create(src.size(), src.type());
|
||||
Mat& dst = dst_.getMatRef();
|
||||
|
||||
int cn = src.channels();
|
||||
int depth = src.depth();
|
||||
|
||||
CV_Assert(cn <= 4 && (depth == CV_8U || depth == CV_32F));
|
||||
|
||||
if (cn == 1)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
filter_<Vec1b>(src, dst, dDepth);
|
||||
if (depth == CV_32F)
|
||||
filter_<Vec1f>(src, dst, dDepth);
|
||||
}
|
||||
else if (cn == 2)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
filter_<Vec2b>(src, dst, dDepth);
|
||||
if (depth == CV_32F)
|
||||
filter_<Vec2f>(src, dst, dDepth);
|
||||
}
|
||||
else if (cn == 3)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
filter_<Vec3b>(src, dst, dDepth);
|
||||
if (depth == CV_32F)
|
||||
filter_<Vec3f>(src, dst, dDepth);
|
||||
}
|
||||
else if (cn == 4)
|
||||
{
|
||||
if (depth == CV_8U)
|
||||
filter_<Vec4b>(src, dst, dDepth);
|
||||
if (depth == CV_32F)
|
||||
filter_<Vec4f>(src, dst, dDepth);
|
||||
}
|
||||
}
|
||||
|
||||
void DTFilterCPU::setSingleFilterCall(bool value)
|
||||
{
|
||||
singleFilterCall = value;
|
||||
}
|
||||
|
||||
void DTFilterCPU::release()
|
||||
{
|
||||
if (mode == -1) return;
|
||||
|
||||
idistHor.release();
|
||||
idistVert.release();
|
||||
|
||||
distHor.release();
|
||||
distVert.release();
|
||||
|
||||
a0distHor.release();
|
||||
a0distVert.release();
|
||||
|
||||
adistHor.release();
|
||||
adistVert.release();
|
||||
}
|
||||
|
||||
Mat DTFilterCPU::getWExtendedMat(int h, int w, int type, int brdleft /*= 0*/, int brdRight /*= 0*/, int cacheAlign /*= 0*/)
|
||||
{
|
||||
int wrapperCols = w + brdleft + brdRight;
|
||||
if (cacheAlign > 0)
|
||||
wrapperCols += ((wrapperCols + cacheAlign-1) / cacheAlign)*cacheAlign;
|
||||
Mat mat(h, wrapperCols, type);
|
||||
return mat(Range::all(), Range(brdleft, w + brdleft));
|
||||
}
|
||||
|
||||
|
||||
Range DTFilterCPU::getWorkRangeByThread(const Range& itemsRange, const Range& rangeThread, int declaredNumThreads)
|
||||
{
|
||||
if (declaredNumThreads <= 0)
|
||||
declaredNumThreads = cv::getNumThreads();
|
||||
|
||||
int chunk = itemsRange.size() / declaredNumThreads;
|
||||
int start = itemsRange.start + chunk * rangeThread.start;
|
||||
int end = itemsRange.start + ((rangeThread.end >= declaredNumThreads) ? itemsRange.size() : chunk * rangeThread.end);
|
||||
|
||||
return Range(start, end);
|
||||
}
|
||||
|
||||
Range DTFilterCPU::getWorkRangeByThread(int items, const Range& rangeThread, int declaredNumThreads)
|
||||
{
|
||||
return getWorkRangeByThread(Range(0, items), rangeThread, declaredNumThreads);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_DTFILTER_HPP__
|
||||
#define __OPENCV_DTFILTER_HPP__
|
||||
#include "precomp.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4512)
|
||||
#pragma warning(disable: 4127)
|
||||
#endif
|
||||
|
||||
#define CV_GET_NUM_THREAD_WORKS_PROPERLY
|
||||
#undef CV_GET_NUM_THREAD_WORKS_PROPERLY
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
class DTFilterCPU : public DTFilter
|
||||
{
|
||||
public: /*Non-template methods*/
|
||||
|
||||
static Ptr<DTFilterCPU> create(InputArray guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3);
|
||||
|
||||
static Ptr<DTFilterCPU> createRF(InputArray adistHor, InputArray adistVert, double sigmaSpatial, double sigmaColor, int numIters = 3);
|
||||
|
||||
void filter(InputArray src, OutputArray dst, int dDepth = -1) CV_OVERRIDE;
|
||||
|
||||
void setSingleFilterCall(bool value);
|
||||
|
||||
public: /*Template methods*/
|
||||
|
||||
/*Use this static methods instead of constructor*/
|
||||
template<typename GuideVec>
|
||||
static DTFilterCPU* create_p_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3);
|
||||
|
||||
template<typename GuideVec>
|
||||
static DTFilterCPU create_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3);
|
||||
|
||||
template<typename GuideVec>
|
||||
void init_(Mat& guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3);
|
||||
|
||||
template<typename SrcVec>
|
||||
void filter_(const Mat& src, Mat& dst, int dDepth = -1);
|
||||
|
||||
protected: /*Typedefs declarations*/
|
||||
|
||||
typedef float IDistType;
|
||||
typedef Vec<IDistType, 1> IDistVec;
|
||||
|
||||
typedef float DistType;
|
||||
typedef Vec<DistType, 1> DistVec;
|
||||
|
||||
typedef float WorkType;
|
||||
|
||||
public: /*Members declarations*/
|
||||
|
||||
int h, w, mode;
|
||||
float sigmaSpatial, sigmaColor;
|
||||
|
||||
bool singleFilterCall;
|
||||
int numFilterCalls;
|
||||
|
||||
Mat idistHor, idistVert;
|
||||
Mat distHor, distVert;
|
||||
|
||||
Mat a0distHor, a0distVert;
|
||||
Mat adistHor, adistVert;
|
||||
int numIters;
|
||||
|
||||
protected: /*Functions declarations*/
|
||||
|
||||
DTFilterCPU() : mode(-1), singleFilterCall(false), numFilterCalls(0) {}
|
||||
|
||||
void init(InputArray guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3);
|
||||
|
||||
void release();
|
||||
|
||||
template<typename GuideVec>
|
||||
inline IDistType getTransformedDistance(const GuideVec &l, const GuideVec &r)
|
||||
{
|
||||
return (IDistType)(1.0f + sigmaSpatial / sigmaColor * norm1<IDistType>(l, r));
|
||||
}
|
||||
|
||||
inline double getIterSigmaH(int iterNum)
|
||||
{
|
||||
return sigmaSpatial * std::pow(2.0, numIters - iterNum) / sqrt(std::pow(4.0, numIters) - 1);
|
||||
}
|
||||
|
||||
inline IDistType getIterRadius(int iterNum)
|
||||
{
|
||||
return (IDistType)(3.0*getIterSigmaH(iterNum));
|
||||
}
|
||||
|
||||
inline float getIterAlpha(int iterNum)
|
||||
{
|
||||
return (float)std::exp(-std::sqrt(2.0 / 3.0) / getIterSigmaH(iterNum));
|
||||
}
|
||||
|
||||
protected: /*Wrappers for parallelization*/
|
||||
|
||||
template <typename WorkVec>
|
||||
struct FilterNC_horPass : public ParallelLoopBody
|
||||
{
|
||||
Mat &src, &idist, &dst;
|
||||
float radius;
|
||||
|
||||
FilterNC_horPass(Mat& src_, Mat& idist_, Mat& dst_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
template <typename WorkVec>
|
||||
struct FilterIC_horPass : public ParallelLoopBody
|
||||
{
|
||||
Mat &src, &idist, &dist, &dst, isrcBuf;
|
||||
float radius;
|
||||
|
||||
FilterIC_horPass(Mat& src_, Mat& idist_, Mat& dist_, Mat& dst_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
template <typename WorkVec>
|
||||
struct FilterRF_horPass : public ParallelLoopBody
|
||||
{
|
||||
Mat &res, &alphaD;
|
||||
int iteration;
|
||||
|
||||
FilterRF_horPass(Mat& res_, Mat& alphaD_, int iteration_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
Range getRange() const { return Range(0, res.rows); }
|
||||
};
|
||||
|
||||
template <typename WorkVec>
|
||||
struct FilterRF_vertPass : public ParallelLoopBody
|
||||
{
|
||||
Mat &res, &alphaD;
|
||||
int iteration;
|
||||
|
||||
FilterRF_vertPass(Mat& res_, Mat& alphaD_, int iteration_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
#ifdef CV_GET_NUM_THREAD_WORKS_PROPERLY
|
||||
Range getRange() const { return Range(0, cv::getNumThreads()); }
|
||||
#else
|
||||
Range getRange() const { return Range(0, res.cols); }
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename GuideVec>
|
||||
struct ComputeIDTHor_ParBody: public ParallelLoopBody
|
||||
{
|
||||
DTFilterCPU &dtf;
|
||||
Mat &guide, &dst;
|
||||
|
||||
ComputeIDTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_, Mat& dst_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
Range getRange() { return Range(0, guide.rows); }
|
||||
};
|
||||
|
||||
template <typename GuideVec>
|
||||
struct ComputeDTandIDTHor_ParBody : public ParallelLoopBody
|
||||
{
|
||||
DTFilterCPU &dtf;
|
||||
Mat &guide, &dist, &idist;
|
||||
IDistType maxRadius;
|
||||
|
||||
ComputeDTandIDTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_, Mat& dist_, Mat& idist_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
Range getRange() { return Range(0, guide.rows); }
|
||||
};
|
||||
|
||||
template <typename GuideVec>
|
||||
struct ComputeA0DTHor_ParBody : public ParallelLoopBody
|
||||
{
|
||||
DTFilterCPU &dtf;
|
||||
Mat &guide;
|
||||
float lna;
|
||||
|
||||
ComputeA0DTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
Range getRange() { return Range(0, guide.rows); }
|
||||
~ComputeA0DTHor_ParBody();
|
||||
};
|
||||
|
||||
template <typename GuideVec>
|
||||
struct ComputeA0DTVert_ParBody : public ParallelLoopBody
|
||||
{
|
||||
DTFilterCPU &dtf;
|
||||
Mat &guide;
|
||||
float lna;
|
||||
|
||||
ComputeA0DTVert_ParBody(DTFilterCPU& dtf_, Mat& guide_);
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
Range getRange() const { return Range(0, guide.rows - 1); }
|
||||
~ComputeA0DTVert_ParBody();
|
||||
};
|
||||
|
||||
protected: /*Auxiliary implementation functions*/
|
||||
|
||||
static Range getWorkRangeByThread(const Range& itemsRange, const Range& rangeThread, int maxThreads = 0);
|
||||
static Range getWorkRangeByThread(int items, const Range& rangeThread, int maxThreads = 0);
|
||||
|
||||
template<typename SrcVec>
|
||||
static void prepareSrcImg_IC(const Mat& src, Mat& inner, Mat& outer);
|
||||
|
||||
static Mat getWExtendedMat(int h, int w, int type, int brdleft = 0, int brdRight = 0, int cacheAlign = 0);
|
||||
|
||||
template<typename SrcVec, typename SrcWorkVec>
|
||||
static void integrateSparseRow(const SrcVec *src, const float *dist, SrcWorkVec *dst, int cols);
|
||||
|
||||
template<typename SrcVec, typename SrcWorkVec>
|
||||
static void integrateRow(const SrcVec *src, SrcWorkVec *dst, int cols);
|
||||
|
||||
inline static int getLeftBound(IDistType *idist, int pos, IDistType searchValue)
|
||||
{
|
||||
while (idist[pos] < searchValue)
|
||||
pos++;
|
||||
return pos;
|
||||
}
|
||||
|
||||
inline static int getRightBound(IDistType *idist, int pos, IDistType searchValue)
|
||||
{
|
||||
while (idist[pos + 1] < searchValue)
|
||||
pos++;
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename T, typename T1, typename T2, int n>
|
||||
inline static T norm1(const cv::Vec<T1, n>& v1, const cv::Vec<T2, n>& v2)
|
||||
{
|
||||
T sum = (T) 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
sum += std::abs( (T)v1[i] - (T)v2[i] );
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
/*One-line template wrappers for DT call*/
|
||||
|
||||
template<typename GuideVec, typename SrcVec>
|
||||
void domainTransformFilter( const Mat_<GuideVec>& guide,
|
||||
const Mat_<SrcVec>& source,
|
||||
Mat& dst,
|
||||
double sigmaSpatial, double sigmaColor,
|
||||
int mode = DTF_NC, int numPasses = 3
|
||||
);
|
||||
|
||||
template<typename GuideVec, typename SrcVec>
|
||||
void domainTransformFilter( const Mat& guide,
|
||||
const Mat& source,
|
||||
Mat& dst,
|
||||
double sigmaSpatial, double sigmaColor,
|
||||
int mode = DTF_NC, int numPasses = 3
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#include "dtfilter_cpu.inl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,624 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_DTFILTER_INL_HPP__
|
||||
#define __OPENCV_DTFILTER_INL_HPP__
|
||||
#include "precomp.hpp"
|
||||
#include "edgeaware_filters_common.hpp"
|
||||
#include <limits>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
using namespace cv::ximgproc::intrinsics;
|
||||
|
||||
#define NC_USE_INTEGRAL_SRC
|
||||
//#undef NC_USE_INTEGRAL_SRC
|
||||
|
||||
template<typename GuideVec>
|
||||
DTFilterCPU DTFilterCPU::create_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode, int numIters)
|
||||
{
|
||||
DTFilterCPU dtf;
|
||||
dtf.init_<GuideVec>(guide, sigmaSpatial, sigmaColor, mode, numIters);
|
||||
return dtf;
|
||||
}
|
||||
|
||||
template<typename GuideVec>
|
||||
DTFilterCPU* DTFilterCPU::create_p_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode, int numIters)
|
||||
{
|
||||
DTFilterCPU* dtf = new DTFilterCPU();
|
||||
dtf->init_<GuideVec>(guide, sigmaSpatial, sigmaColor, mode, numIters);
|
||||
return dtf;
|
||||
}
|
||||
|
||||
template<typename GuideVec>
|
||||
void DTFilterCPU::init_(Mat& guide, double sigmaSpatial_, double sigmaColor_, int mode_, int numIters_)
|
||||
{
|
||||
CV_Assert(guide.type() == traits::Type<GuideVec>::value);
|
||||
|
||||
this->release();
|
||||
|
||||
h = guide.rows;
|
||||
w = guide.cols;
|
||||
|
||||
sigmaSpatial = std::max(1.0f, (float)sigmaSpatial_);
|
||||
sigmaColor = std::max(0.01f, (float)sigmaColor_);
|
||||
|
||||
mode = mode_;
|
||||
numIters = std::max(1, numIters_);
|
||||
|
||||
if (mode == DTF_NC)
|
||||
{
|
||||
{
|
||||
ComputeIDTHor_ParBody<GuideVec> horBody(*this, guide, idistHor);
|
||||
parallel_for_(horBody.getRange(), horBody);
|
||||
}
|
||||
{
|
||||
Mat guideT = guide.t();
|
||||
ComputeIDTHor_ParBody<GuideVec> horBody(*this, guideT, idistVert);
|
||||
parallel_for_(horBody.getRange(), horBody);
|
||||
}
|
||||
}
|
||||
else if (mode == DTF_IC)
|
||||
{
|
||||
{
|
||||
ComputeDTandIDTHor_ParBody<GuideVec> horBody(*this, guide, distHor, idistHor);
|
||||
parallel_for_(horBody.getRange(), horBody);
|
||||
}
|
||||
{
|
||||
Mat guideT = guide.t();
|
||||
ComputeDTandIDTHor_ParBody<GuideVec> horBody(*this, guideT, distVert, idistVert);
|
||||
parallel_for_(horBody.getRange(), horBody);
|
||||
}
|
||||
}
|
||||
else if (mode == DTF_RF)
|
||||
{
|
||||
ComputeA0DTHor_ParBody<GuideVec> horBody(*this, guide);
|
||||
ComputeA0DTVert_ParBody<GuideVec> vertBody(*this, guide);
|
||||
parallel_for_(horBody.getRange(), horBody);
|
||||
parallel_for_(vertBody.getRange(), vertBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsBadFlag, "Incorrect DT filter mode");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SrcVec>
|
||||
void DTFilterCPU::filter_(const Mat& src, Mat& dst, int dDepth)
|
||||
{
|
||||
typedef typename DataType<Vec<WorkType, SrcVec::channels> >::vec_type WorkVec;
|
||||
CV_Assert( src.type() == traits::Type<SrcVec>::value );
|
||||
if ( src.cols != w || src.rows != h )
|
||||
{
|
||||
CV_Error(Error::StsBadSize, "Size of filtering image must be equal to size of guide image");
|
||||
}
|
||||
|
||||
if (singleFilterCall)
|
||||
{
|
||||
CV_Assert(numFilterCalls == 0);
|
||||
}
|
||||
numFilterCalls++;
|
||||
|
||||
Mat res;
|
||||
if (dDepth == -1) dDepth = src.depth();
|
||||
|
||||
//small optimization to avoid extra copying of data
|
||||
bool useDstAsRes = (dDepth == traits::Depth<WorkVec>::value && (mode == DTF_NC || mode == DTF_RF));
|
||||
if (useDstAsRes)
|
||||
{
|
||||
dst.create(h, w, traits::Type<WorkVec>::value);
|
||||
res = dst;
|
||||
}
|
||||
|
||||
if (mode == DTF_NC)
|
||||
{
|
||||
Mat resT(src.cols, src.rows, traits::Type<WorkVec>::value);
|
||||
src.convertTo(res, traits::Type<WorkVec>::value);
|
||||
|
||||
FilterNC_horPass<WorkVec> horParBody(res, idistHor, resT);
|
||||
FilterNC_horPass<WorkVec> vertParBody(resT, idistVert, res);
|
||||
|
||||
for (int iter = 1; iter <= numIters; iter++)
|
||||
{
|
||||
horParBody.radius = vertParBody.radius = getIterRadius(iter);
|
||||
|
||||
parallel_for_(Range(0, res.rows), horParBody);
|
||||
parallel_for_(Range(0, resT.rows), vertParBody);
|
||||
}
|
||||
}
|
||||
else if (mode == DTF_IC)
|
||||
{
|
||||
Mat resT;
|
||||
prepareSrcImg_IC<WorkVec>(src, res, resT);
|
||||
|
||||
FilterIC_horPass<WorkVec> horParBody(res, idistHor, distHor, resT);
|
||||
FilterIC_horPass<WorkVec> vertParBody(resT, idistVert, distVert, res);
|
||||
|
||||
for (int iter = 1; iter <= numIters; iter++)
|
||||
{
|
||||
horParBody.radius = vertParBody.radius = getIterRadius(iter);
|
||||
|
||||
parallel_for_(Range(0, res.rows), horParBody);
|
||||
parallel_for_(Range(0, resT.rows), vertParBody);
|
||||
}
|
||||
}
|
||||
else if (mode == DTF_RF)
|
||||
{
|
||||
src.convertTo(res, traits::Type<WorkVec>::value);
|
||||
|
||||
for (int iter = 1; iter <= numIters; iter++)
|
||||
{
|
||||
if (!singleFilterCall && iter == 2)
|
||||
{
|
||||
a0distHor.copyTo(adistHor);
|
||||
a0distVert.copyTo(adistVert);
|
||||
}
|
||||
|
||||
bool useA0DT = (singleFilterCall || iter == 1);
|
||||
Mat& a0dHor = (useA0DT) ? a0distHor : adistHor;
|
||||
Mat& a0dVert = (useA0DT) ? a0distVert : adistVert;
|
||||
|
||||
FilterRF_horPass<WorkVec> horParBody(res, a0dHor, iter);
|
||||
FilterRF_vertPass<WorkVec> vertParBody(res, a0dVert, iter);
|
||||
parallel_for_(horParBody.getRange(), horParBody);
|
||||
parallel_for_(vertParBody.getRange(), vertParBody);
|
||||
}
|
||||
}
|
||||
|
||||
if (!useDstAsRes)
|
||||
{
|
||||
res.convertTo(dst, dDepth);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SrcVec, typename SrcWorkVec>
|
||||
void DTFilterCPU::integrateRow(const SrcVec *src, SrcWorkVec *dst, int cols)
|
||||
{
|
||||
SrcWorkVec sum = SrcWorkVec::all(0);
|
||||
dst[0] = sum;
|
||||
|
||||
for (int j = 0; j < cols; j++)
|
||||
{
|
||||
sum += SrcWorkVec(src[j]);
|
||||
dst[j + 1] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename SrcVec, typename SrcWorkVec>
|
||||
void DTFilterCPU::integrateSparseRow(const SrcVec *src, const float *dist, SrcWorkVec *dst, int cols)
|
||||
{
|
||||
SrcWorkVec sum = SrcWorkVec::all(0);
|
||||
dst[0] = sum;
|
||||
|
||||
for (int j = 0; j < cols-1; j++)
|
||||
{
|
||||
sum += dist[j] * 0.5f * (SrcWorkVec(src[j]) + SrcWorkVec(src[j+1]));
|
||||
dst[j + 1] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename WorkVec>
|
||||
void DTFilterCPU::prepareSrcImg_IC(const Mat& src, Mat& dst, Mat& dstT)
|
||||
{
|
||||
Mat dstOut(src.rows, src.cols + 2, traits::Type<WorkVec>::value);
|
||||
Mat dstOutT(src.cols, src.rows + 2, traits::Type<WorkVec>::value);
|
||||
|
||||
dst = dstOut(Range::all(), Range(1, src.cols+1));
|
||||
dstT = dstOutT(Range::all(), Range(1, src.rows+1));
|
||||
|
||||
src.convertTo(dst, traits::Type<WorkVec>::value);
|
||||
|
||||
WorkVec *line;
|
||||
int ri = dstOut.cols - 1;
|
||||
for (int i = 0; i < src.rows; i++)
|
||||
{
|
||||
line = dstOut.ptr<WorkVec>(i);
|
||||
line[0] = line[1];
|
||||
line[ri] = line[ri - 1];
|
||||
}
|
||||
|
||||
WorkVec *topLine = dst.ptr<WorkVec>(0);
|
||||
WorkVec *bottomLine = dst.ptr<WorkVec>(dst.rows - 1);
|
||||
ri = dstOutT.cols - 1;
|
||||
for (int i = 0; i < src.cols; i++)
|
||||
{
|
||||
line = dstOutT.ptr<WorkVec>(i);
|
||||
line[0] = topLine[i];
|
||||
line[ri] = bottomLine[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename WorkVec>
|
||||
DTFilterCPU::FilterNC_horPass<WorkVec>::FilterNC_horPass(Mat& src_, Mat& idist_, Mat& dst_)
|
||||
: src(src_), idist(idist_), dst(dst_), radius(1.0f)
|
||||
{
|
||||
CV_DbgAssert(src.type() == traits::Type<WorkVec>::value && dst.type() == traits::Type<WorkVec>::value && dst.rows == src.cols && dst.cols == src.rows);
|
||||
}
|
||||
|
||||
template <typename WorkVec>
|
||||
void DTFilterCPU::FilterNC_horPass<WorkVec>::operator()(const Range& range) const
|
||||
{
|
||||
#ifdef NC_USE_INTEGRAL_SRC
|
||||
std::vector<WorkVec> isrcBuf(src.cols + 1);
|
||||
WorkVec *isrcLine = &isrcBuf[0];
|
||||
#endif
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
const WorkVec *srcLine = src.ptr<WorkVec>(i);
|
||||
IDistType *idistLine = idist.ptr<IDistType>(i);
|
||||
int leftBound = 0, rightBound = 0;
|
||||
WorkVec sum;
|
||||
|
||||
#ifdef NC_USE_INTEGRAL_SRC
|
||||
integrateRow(srcLine, isrcLine, src.cols);
|
||||
#else
|
||||
sum = srcLine[0];
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < src.cols; j++)
|
||||
{
|
||||
IDistType curVal = idistLine[j];
|
||||
#ifdef NC_USE_INTEGRAL_SRC
|
||||
leftBound = getLeftBound(idistLine, leftBound, curVal - radius);
|
||||
rightBound = getRightBound(idistLine, rightBound, curVal + radius);
|
||||
sum = (isrcLine[rightBound + 1] - isrcLine[leftBound]);
|
||||
#else
|
||||
while (idistLine[leftBound] < curVal - radius)
|
||||
{
|
||||
sum -= srcLine[leftBound];
|
||||
leftBound++;
|
||||
}
|
||||
|
||||
while (idistLine[rightBound + 1] < curVal + radius)
|
||||
{
|
||||
rightBound++;
|
||||
sum += srcLine[rightBound];
|
||||
}
|
||||
#endif
|
||||
|
||||
dst.at<WorkVec>(j, i) = sum / (float)(rightBound + 1 - leftBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename WorkVec>
|
||||
DTFilterCPU::FilterIC_horPass<WorkVec>::FilterIC_horPass(Mat& src_, Mat& idist_, Mat& dist_, Mat& dst_)
|
||||
: src(src_), idist(idist_), dist(dist_), dst(dst_), radius(1.0f)
|
||||
{
|
||||
CV_DbgAssert(src.type() == traits::Type<WorkVec>::value && dst.type() == traits::Type<WorkVec>::value && dst.rows == src.cols && dst.cols == src.rows);
|
||||
|
||||
#ifdef CV_GET_NUM_THREAD_WORKS_PROPERLY
|
||||
isrcBuf.create(cv::getNumThreads(), src.cols + 1, traits::Type<WorkVec>::value);
|
||||
#else
|
||||
isrcBuf.create(src.rows, src.cols + 1, traits::Type<WorkVec>::value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename WorkVec>
|
||||
void DTFilterCPU::FilterIC_horPass<WorkVec>::operator()(const Range& range) const
|
||||
{
|
||||
#ifdef CV_GET_NUM_THREAD_WORKS_PROPERLY
|
||||
WorkVec *isrcLine = const_cast<WorkVec*>( isrcBuf.ptr<WorkVec>(cv::getThreadNum()) );
|
||||
#else
|
||||
WorkVec *isrcLine = const_cast<WorkVec*>( isrcBuf.ptr<WorkVec>(range.start) );
|
||||
#endif
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
WorkVec *srcLine = src.ptr<WorkVec>(i);
|
||||
DistType *distLine = dist.ptr<DistType>(i);
|
||||
IDistType *idistLine = idist.ptr<IDistType>(i);
|
||||
|
||||
integrateSparseRow(srcLine, distLine, isrcLine, src.cols);
|
||||
|
||||
int leftBound = 0, rightBound = 0;
|
||||
WorkVec sumL, sumR, sumC;
|
||||
|
||||
srcLine[-1] = srcLine[0];
|
||||
srcLine[src.cols] = srcLine[src.cols - 1];
|
||||
|
||||
for (int j = 0; j < src.cols; j++)
|
||||
{
|
||||
IDistType curVal = idistLine[j];
|
||||
IDistType valueLeft = curVal - radius;
|
||||
IDistType valueRight = curVal + radius;
|
||||
|
||||
leftBound = getLeftBound(idistLine, leftBound, valueLeft);
|
||||
rightBound = getRightBound(idistLine, rightBound, valueRight);
|
||||
|
||||
float areaL = idistLine[leftBound] - valueLeft;
|
||||
float areaR = valueRight - idistLine[rightBound];
|
||||
float dl = areaL / distLine[leftBound - 1];
|
||||
float dr = areaR / distLine[rightBound];
|
||||
|
||||
sumL = 0.5f*areaL*(dl*srcLine[leftBound - 1] + (2.0f - dl)*srcLine[leftBound]);
|
||||
sumR = 0.5f*areaR*((2.0f - dr)*srcLine[rightBound] + dr*srcLine[rightBound + 1]);
|
||||
sumC = isrcLine[rightBound] - isrcLine[leftBound];
|
||||
|
||||
dst.at<WorkVec>(j, i) = (sumL + sumC + sumR) / (2.0f * radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename WorkVec>
|
||||
DTFilterCPU::FilterRF_horPass<WorkVec>::FilterRF_horPass(Mat& res_, Mat& alphaD_, int iteration_)
|
||||
: res(res_), alphaD(alphaD_), iteration(iteration_)
|
||||
{
|
||||
CV_DbgAssert(res.type() == traits::Type<WorkVec>::value);
|
||||
CV_DbgAssert(res.type() == traits::Type<WorkVec>::value && res.size() == res.size());
|
||||
}
|
||||
|
||||
|
||||
template <typename WorkVec>
|
||||
void DTFilterCPU::FilterRF_horPass<WorkVec>::operator()(const Range& range) const
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
WorkVec *dstLine = res.ptr<WorkVec>(i);
|
||||
DistType *adLine = alphaD.ptr<DistType>(i);
|
||||
int j;
|
||||
|
||||
if (iteration > 1)
|
||||
{
|
||||
for (j = res.cols - 2; j >= 0; j--)
|
||||
adLine[j] *= adLine[j];
|
||||
}
|
||||
|
||||
for (j = 1; j < res.cols; j++)
|
||||
{
|
||||
dstLine[j] += adLine[j-1] * (dstLine[j-1] - dstLine[j]);
|
||||
}
|
||||
|
||||
for (j = res.cols - 2; j >= 0; j--)
|
||||
{
|
||||
dstLine[j] += adLine[j] * (dstLine[j+1] - dstLine[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename WorkVec>
|
||||
DTFilterCPU::FilterRF_vertPass<WorkVec>::FilterRF_vertPass(Mat& res_, Mat& alphaD_, int iteration_)
|
||||
: res(res_), alphaD(alphaD_), iteration(iteration_)
|
||||
{
|
||||
CV_DbgAssert(res.type() == traits::Type<WorkVec>::value);
|
||||
CV_DbgAssert(res.type() == traits::Type<WorkVec>::value && res.size() == res.size());
|
||||
}
|
||||
|
||||
|
||||
template <typename WorkVec>
|
||||
void DTFilterCPU::FilterRF_vertPass<WorkVec>::operator()(const Range& range) const
|
||||
{
|
||||
#ifdef CV_GET_NUM_THREAD_WORKS_PROPERLY
|
||||
Range rcols = getWorkRangeByThread(res.cols, range);
|
||||
#else
|
||||
Range rcols = range;
|
||||
#endif
|
||||
|
||||
for (int i = 1; i < res.rows; i++)
|
||||
{
|
||||
WorkVec *curRow = res.ptr<WorkVec>(i);
|
||||
WorkVec *prevRow = res.ptr<WorkVec>(i - 1);
|
||||
DistType *adRow = alphaD.ptr<DistType>(i - 1);
|
||||
|
||||
if (iteration > 1)
|
||||
{
|
||||
for (int j = rcols.start; j < rcols.end; j++)
|
||||
adRow[j] *= adRow[j];
|
||||
}
|
||||
|
||||
for (int j = rcols.start; j < rcols.end; j++)
|
||||
{
|
||||
curRow[j] += adRow[j] * (prevRow[j] - curRow[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = res.rows - 2; i >= 0; i--)
|
||||
{
|
||||
WorkVec *prevRow = res.ptr<WorkVec>(i + 1);
|
||||
WorkVec *curRow = res.ptr<WorkVec>(i);
|
||||
DistType *adRow = alphaD.ptr<DistType>(i);
|
||||
|
||||
for (int j = rcols.start; j < rcols.end; j++)
|
||||
{
|
||||
curRow[j] += adRow[j] * (prevRow[j] - curRow[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
DTFilterCPU::ComputeIDTHor_ParBody<GuideVec>::ComputeIDTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_, Mat& dst_)
|
||||
: dtf(dtf_), guide(guide_), dst(dst_)
|
||||
{
|
||||
dst.create(guide.rows, guide.cols + 1, traits::Type<IDistVec>::value);
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
void DTFilterCPU::ComputeIDTHor_ParBody<GuideVec>::operator()(const Range& range) const
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
const GuideVec *guideLine = guide.ptr<GuideVec>(i);
|
||||
IDistType *idistLine = dst.ptr<IDistType>(i);
|
||||
|
||||
IDistType curDist = (IDistType)0;
|
||||
idistLine[0] = (IDistType)0;
|
||||
|
||||
for (int j = 1; j < guide.cols; j++)
|
||||
{
|
||||
curDist += dtf.getTransformedDistance(guideLine[j-1], guideLine[j]);
|
||||
idistLine[j] = curDist;
|
||||
}
|
||||
idistLine[guide.cols] = std::numeric_limits<IDistType>::max();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
DTFilterCPU::ComputeDTandIDTHor_ParBody<GuideVec>::ComputeDTandIDTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_, Mat& dist_, Mat& idist_)
|
||||
: dtf(dtf_), guide(guide_), dist(dist_), idist(idist_)
|
||||
{
|
||||
dist = getWExtendedMat(guide.rows, guide.cols, traits::Type<IDistVec>::value, 1, 1);
|
||||
idist = getWExtendedMat(guide.rows, guide.cols + 1, traits::Type<IDistVec>::value);
|
||||
maxRadius = dtf.getIterRadius(1);
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
void DTFilterCPU::ComputeDTandIDTHor_ParBody<GuideVec>::operator()(const Range& range) const
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
const GuideVec *guideLine = guide.ptr<GuideVec>(i);
|
||||
DistType *distLine = dist.ptr<DistType>(i);
|
||||
IDistType *idistLine = idist.ptr<IDistType>(i);
|
||||
|
||||
DistType curDist;
|
||||
IDistType curIDist = (IDistType)0;
|
||||
int j;
|
||||
|
||||
distLine[-1] = maxRadius;
|
||||
//idistLine[-1] = curIDist - maxRadius;
|
||||
idistLine[0] = curIDist;
|
||||
for (j = 0; j < guide.cols-1; j++)
|
||||
{
|
||||
curDist = (DistType) dtf.getTransformedDistance(guideLine[j], guideLine[j + 1]);
|
||||
curIDist += curDist;
|
||||
|
||||
distLine[j] = curDist;
|
||||
idistLine[j + 1] = curIDist;
|
||||
}
|
||||
idistLine[j + 1] = curIDist + maxRadius;
|
||||
distLine[j] = maxRadius;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
DTFilterCPU::ComputeA0DTHor_ParBody<GuideVec>::ComputeA0DTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_)
|
||||
: dtf(dtf_), guide(guide_)
|
||||
{
|
||||
dtf.a0distHor.create(guide.rows, guide.cols - 1, traits::Type<DistVec>::value);
|
||||
lna = std::log(dtf.getIterAlpha(1));
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
void DTFilterCPU::ComputeA0DTHor_ParBody<GuideVec>::operator()(const Range& range) const
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
const GuideVec *guideLine = guide.ptr<GuideVec>(i);
|
||||
DistType *dstLine = dtf.a0distHor.ptr<DistType>(i);
|
||||
|
||||
for (int j = 0; j < guide.cols - 1; j++)
|
||||
{
|
||||
DistType d = (DistType)dtf.getTransformedDistance(guideLine[j], guideLine[j + 1]);
|
||||
dstLine[j] = lna*d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
DTFilterCPU::ComputeA0DTHor_ParBody<GuideVec>::~ComputeA0DTHor_ParBody()
|
||||
{
|
||||
cv::exp(dtf.a0distHor, dtf.a0distHor);
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
DTFilterCPU::ComputeA0DTVert_ParBody<GuideVec>::ComputeA0DTVert_ParBody(DTFilterCPU& dtf_, Mat& guide_)
|
||||
: dtf(dtf_), guide(guide_)
|
||||
{
|
||||
dtf.a0distVert.create(guide.rows - 1, guide.cols, traits::Type<DistVec>::value);
|
||||
lna = std::log(dtf.getIterAlpha(1));
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
void DTFilterCPU::ComputeA0DTVert_ParBody<GuideVec>::operator()(const Range& range) const
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
DistType *dstLine = dtf.a0distVert.ptr<DistType>(i);
|
||||
GuideVec *guideRow1 = guide.ptr<GuideVec>(i);
|
||||
GuideVec *guideRow2 = guide.ptr<GuideVec>(i+1);
|
||||
|
||||
for (int j = 0; j < guide.cols; j++)
|
||||
{
|
||||
DistType d = (DistType)dtf.getTransformedDistance(guideRow1[j], guideRow2[j]);
|
||||
dstLine[j] = lna*d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GuideVec>
|
||||
DTFilterCPU::ComputeA0DTVert_ParBody<GuideVec>::~ComputeA0DTVert_ParBody()
|
||||
{
|
||||
cv::exp(dtf.a0distVert, dtf.a0distVert);
|
||||
}
|
||||
|
||||
|
||||
template<typename GuideVec, typename SrcVec>
|
||||
void domainTransformFilter( const Mat_<GuideVec>& guide,
|
||||
const Mat_<SrcVec>& source,
|
||||
Mat& dst,
|
||||
double sigmaSpatial, double sigmaColor,
|
||||
int mode, int numPasses
|
||||
)
|
||||
{
|
||||
DTFilterCPU *dtf = DTFilterCPU::create_p_<GuideVec>(guide, sigmaSpatial, sigmaColor, mode, numPasses);
|
||||
dtf->filter_<SrcVec>(source, dst);
|
||||
delete dtf;
|
||||
}
|
||||
|
||||
template<typename GuideVec, typename SrcVec>
|
||||
void domainTransformFilter( const Mat& guide,
|
||||
const Mat& source,
|
||||
Mat& dst,
|
||||
double sigmaSpatial, double sigmaColor,
|
||||
int mode, int numPasses
|
||||
)
|
||||
{
|
||||
DTFilterCPU *dtf = DTFilterCPU::create_p_<GuideVec>(guide, sigmaSpatial, sigmaColor, mode, numPasses);
|
||||
dtf->filter_<SrcVec>(source, dst);
|
||||
delete dtf;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,688 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef __OPENCV_EDGE_DRAWING_COMMON_HPP__
|
||||
#define __OPENCV_EDGE_DRAWING_COMMON_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#define EDGE_VERTICAL 1
|
||||
#define EDGE_HORIZONTAL 2
|
||||
|
||||
#define ANCHOR_PIXEL 254
|
||||
#define EDGE_PIXEL 255
|
||||
|
||||
#define ED_LEFT 1
|
||||
#define ED_RIGHT 2
|
||||
#define ED_UP 3
|
||||
#define ED_DOWN 4
|
||||
|
||||
#define ED_SOUTH_SOUTH 0
|
||||
#define ED_SOUTH_EAST 1
|
||||
#define ED_EAST_SOUTH 2
|
||||
#define ED_EAST_EAST 3
|
||||
|
||||
// Circular arc, circle thresholds
|
||||
#define VERY_SHORT_ARC_ERROR 0.40 // Used for very short arcs (>= CANDIDATE_CIRCLE_RATIO1 && < CANDIDATE_CIRCLE_RATIO2)
|
||||
#define SHORT_ARC_ERROR 1.00 // Used for short arcs (>= CANDIDATE_CIRCLE_RATIO2 && < HALF_CIRCLE_RATIO)
|
||||
#define HALF_ARC_ERROR 1.25 // Used for arcs with length (>=HALF_CIRCLE_RATIO && < FULL_CIRCLE_RATIO)
|
||||
#define LONG_ARC_ERROR 1.50 // Used for long arcs (>= FULL_CIRCLE_RATIO)
|
||||
|
||||
#define CANDIDATE_CIRCLE_RATIO1 0.25 // 25% -- If only 25% of the circle is detected, it may be a candidate for validation
|
||||
#define CANDIDATE_CIRCLE_RATIO2 0.33 // 33% -- If only 33% of the circle is detected, it may be a candidate for validation
|
||||
#define HALF_CIRCLE_RATIO 0.50 // 50% -- If 50% of a circle is detected at any point during joins, we immediately make it a candidate
|
||||
#define FULL_CIRCLE_RATIO 0.67 // 67% -- If 67% of the circle is detected, we assume that it is fully covered
|
||||
|
||||
// Ellipse thresholds
|
||||
#define CANDIDATE_ELLIPSE_RATIO 0.50 // 50% -- If 50% of the ellipse is detected, it may be candidate for validation
|
||||
#define ELLIPSE_ERROR 1.50 // Used for ellipses. (used to be 1.65 for what reason?)
|
||||
#define ED_MAX_GRAD_VALUE 128*256
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
#define TABSIZE 100000
|
||||
#define MAX_LUT_SIZE 1024
|
||||
#define RELATIVE_ERROR_FACTOR 100.0
|
||||
|
||||
class NFALUT
|
||||
{
|
||||
public:
|
||||
|
||||
NFALUT(int size, double _prob, double _logNT);
|
||||
~NFALUT();
|
||||
|
||||
int* LUT; // look up table
|
||||
int LUTSize;
|
||||
|
||||
double prob;
|
||||
double logNT;
|
||||
|
||||
bool checkValidationByNFA(int n, int k);
|
||||
static double myAtan2(double yy, double xx);
|
||||
|
||||
private:
|
||||
double nfa(int n, int k);
|
||||
static double log_gamma_lanczos(double x);
|
||||
static double log_gamma_windschitl(double x);
|
||||
static double log_gamma(double x);
|
||||
static int double_equal(double a, double b);
|
||||
};
|
||||
|
||||
NFALUT::NFALUT(int size, double _prob, double _logNT)
|
||||
{
|
||||
LUTSize = size > 60 ? 60 : size;
|
||||
LUT = new int[LUTSize];
|
||||
prob = _prob;
|
||||
logNT = _logNT;
|
||||
|
||||
LUT[0] = 1;
|
||||
int j = 1;
|
||||
for (int i = 1; i < LUTSize; i++)
|
||||
{
|
||||
LUT[i] = LUTSize + 1;
|
||||
double ret = nfa(i, j);
|
||||
if (ret < 0)
|
||||
{
|
||||
while (j < i)
|
||||
{
|
||||
j++;
|
||||
ret = nfa(i, j);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
continue;
|
||||
}
|
||||
LUT[i] = j;
|
||||
}
|
||||
}
|
||||
|
||||
NFALUT::~NFALUT()
|
||||
{
|
||||
delete[] LUT;
|
||||
}
|
||||
|
||||
bool NFALUT::checkValidationByNFA(int n, int k)
|
||||
{
|
||||
if (n >= LUTSize)
|
||||
return nfa(n, k) >= 0.0;
|
||||
else
|
||||
return k >= LUT[n];
|
||||
}
|
||||
|
||||
double NFALUT::myAtan2(double yy, double xx)
|
||||
{
|
||||
double angle = fastAtan2((float)yy, (float)xx);
|
||||
|
||||
if (angle > 180)
|
||||
angle = angle - 180;
|
||||
|
||||
return angle / 180 * CV_PI;
|
||||
}
|
||||
|
||||
double NFALUT::nfa(int n, int k)
|
||||
{
|
||||
static double inv[TABSIZE]; /* table to keep computed inverse values */
|
||||
double tolerance = 0.1; /* an error of 10% in the result is accepted */
|
||||
double log1term, term, bin_term, mult_term, bin_tail, err, p_term;
|
||||
int i;
|
||||
|
||||
/* check parameters */
|
||||
if (n < 0 || k<0 || k>n || prob <= 0.0 || prob >= 1.0) return -1.0;
|
||||
|
||||
/* trivial cases */
|
||||
if (n == 0 || k == 0) return -logNT;
|
||||
if (n == k) return -logNT - (double)n * log10(prob);
|
||||
|
||||
/* probability term */
|
||||
p_term = prob / (1.0 - prob);
|
||||
|
||||
/* compute the first term of the series */
|
||||
/*
|
||||
binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
|
||||
where bincoef(n,i) are the binomial coefficients.
|
||||
But
|
||||
bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
|
||||
We use this to compute the first term. Actually the log of it.
|
||||
*/
|
||||
log1term = log_gamma((double)n + 1.0) - log_gamma((double)k + 1.0)
|
||||
- log_gamma((double)(n - k) + 1.0)
|
||||
+ (double)k * log(prob) + (double)(n - k) * log(1.0 - prob);
|
||||
term = exp(log1term);
|
||||
|
||||
/* in some cases no more computations are needed */
|
||||
if (double_equal(term, 0.0)) { /* the first term is almost zero */
|
||||
if ((double)k > (double)n * prob) /* at begin or end of the tail? */
|
||||
return -log1term / M_LN10 - logNT; /* end: use just the first term */
|
||||
else
|
||||
return -logNT; /* begin: the tail is roughly 1 */
|
||||
}
|
||||
|
||||
/* compute more terms if needed */
|
||||
bin_tail = term;
|
||||
for (i = k + 1; i <= n; i++) {
|
||||
/*
|
||||
As
|
||||
term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
|
||||
and
|
||||
bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
|
||||
then,
|
||||
term_i / term_i-1 = (n-i+1)/i * p/(1-p)
|
||||
and
|
||||
term_i = term_i-1 * (n-i+1)/i * p/(1-p).
|
||||
1/i is stored in a table as they are computed,
|
||||
because divisions are expensive.
|
||||
p/(1-p) is computed only once and stored in 'p_term'.
|
||||
*/
|
||||
bin_term = (double)(n - i + 1) * (i < TABSIZE ?
|
||||
(inv[i] != 0.0 ? inv[i] : (inv[i] = 1.0 / (double)i)) :
|
||||
1.0 / (double)i);
|
||||
|
||||
mult_term = bin_term * p_term;
|
||||
term *= mult_term;
|
||||
bin_tail += term;
|
||||
|
||||
if (bin_term < 1.0) {
|
||||
/* When bin_term<1 then mult_term_j<mult_term_i for j>i.
|
||||
Then, the error on the binomial tail when truncated at
|
||||
the i term can be bounded by a geometric series of form
|
||||
term_i * sum mult_term_i^j. */
|
||||
err = term * ((1.0 - pow(mult_term, (double)(n - i + 1))) /
|
||||
(1.0 - mult_term) - 1.0);
|
||||
|
||||
/* One wants an error at most of tolerance*final_result, or:
|
||||
tolerance * abs(-log10(bin_tail)-logNT).
|
||||
Now, the error that can be accepted on bin_tail is
|
||||
given by tolerance*final_result divided by the derivative
|
||||
of -log10(x) when x=bin_tail. that is:
|
||||
tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
|
||||
Finally, we truncate the tail if the error is less than:
|
||||
tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
|
||||
if (err < tolerance * fabs(-log10(bin_tail) - logNT) * bin_tail) break;
|
||||
}
|
||||
}
|
||||
|
||||
return -log10(bin_tail) - logNT;
|
||||
}
|
||||
|
||||
double NFALUT::log_gamma_lanczos(double x)
|
||||
{
|
||||
static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
|
||||
8687.24529705, 1168.92649479, 83.8676043424,
|
||||
2.50662827511 };
|
||||
double a = (x + 0.5) * log(x + 5.5) - (x + 5.5);
|
||||
double b = 0.0;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 7; n++)
|
||||
{
|
||||
a -= log(x + (double)n);
|
||||
b += q[n] * pow(x, (double)n);
|
||||
}
|
||||
return a + log(b);
|
||||
}
|
||||
|
||||
double NFALUT::log_gamma_windschitl(double x)
|
||||
{
|
||||
return 0.918938533204673 + (x - 0.5) * log(x) - x
|
||||
+ 0.5 * x * log(x * sinh(1 / x) + 1 / (810.0 * pow(x, 6.0)));
|
||||
}
|
||||
|
||||
double NFALUT::log_gamma(double x)
|
||||
{
|
||||
return x > 15 ? log_gamma_windschitl(x) : log_gamma_lanczos(x);
|
||||
}
|
||||
|
||||
int NFALUT::double_equal(double a, double b)
|
||||
{
|
||||
double abs_diff, aa, bb, abs_max;
|
||||
|
||||
/* trivial case */
|
||||
if (a == b) return true;
|
||||
|
||||
abs_diff = fabs(a - b);
|
||||
aa = fabs(a);
|
||||
bb = fabs(b);
|
||||
abs_max = aa > bb ? aa : bb;
|
||||
|
||||
/* DBL_MIN is the smallest normalized number, thus, the smallest
|
||||
number whose relative error is bounded by DBL_EPSILON. For
|
||||
smaller numbers, the same quantization steps as for DBL_MIN
|
||||
are used. Then, for smaller numbers, a meaningful "relative"
|
||||
error should be computed by dividing the difference by DBL_MIN. */
|
||||
if (abs_max < DBL_MIN) abs_max = DBL_MIN;
|
||||
|
||||
/* equal if relative error <= factor x eps */
|
||||
return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
|
||||
}
|
||||
|
||||
struct StackNode
|
||||
{
|
||||
int r, c; // starting pixel
|
||||
int parent; // parent chain (-1 if no parent)
|
||||
int dir; // direction where you are supposed to go
|
||||
};
|
||||
|
||||
// Used during Edge Linking
|
||||
struct Chain
|
||||
{
|
||||
int dir; // Direction of the chain
|
||||
int len; // # of pixels in the chain
|
||||
int parent; // Parent of this node (-1 if no parent)
|
||||
int children[2]; // Children of this node (-1 if no children)
|
||||
Point *pixels; // Pointer to the beginning of the pixels array
|
||||
};
|
||||
|
||||
// light weight struct for Start & End coordinates of the line segment
|
||||
struct LS
|
||||
{
|
||||
Point2d start;
|
||||
Point2d end;
|
||||
|
||||
LS(Point2d _start, Point2d _end)
|
||||
{
|
||||
start = _start;
|
||||
end = _end;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct EDLineSegment
|
||||
{
|
||||
double a, b; // y = a + bx (if invert = 0) || x = a + by (if invert = 1)
|
||||
int invert;
|
||||
|
||||
double sx, sy; // starting x & y coordinates
|
||||
double ex, ey; // ending x & y coordinates
|
||||
|
||||
int segmentNo; // Edge segment that this line belongs to
|
||||
int firstPixelIndex; // Index of the first pixel within the segment of pixels
|
||||
int len; // No of pixels making up the line segment
|
||||
|
||||
EDLineSegment(double _a, double _b, int _invert, double _sx, double _sy, double _ex, double _ey, int _segmentNo, int _firstPixelIndex, int _len)
|
||||
{
|
||||
a = _a;
|
||||
b = _b;
|
||||
invert = _invert;
|
||||
sx = _sx;
|
||||
sy = _sy;
|
||||
ex = _ex;
|
||||
ey = _ey;
|
||||
segmentNo = _segmentNo;
|
||||
firstPixelIndex = _firstPixelIndex;
|
||||
len = _len;
|
||||
}
|
||||
};
|
||||
|
||||
// Circle equation: (x-xc)^2 + (y-yc)^2 = r^2
|
||||
struct mCircle {
|
||||
Point2d center;
|
||||
double r;
|
||||
mCircle(Point2d _center, double _r) { center = _center; r = _r; }
|
||||
};
|
||||
|
||||
// Ellipse equation: Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
|
||||
struct mEllipse {
|
||||
Point2d center;
|
||||
Size axes;
|
||||
double theta;
|
||||
mEllipse(Point2d _center, Size _axes, double _theta) { center = _center; axes = _axes; theta = _theta; }
|
||||
};
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Ellipse Equation is of the form:
|
||||
// Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
|
||||
//
|
||||
struct EllipseEquation {
|
||||
static constexpr int COEFF_SIZE = 7;
|
||||
double coeff[COEFF_SIZE]; // coeff[1] = A
|
||||
|
||||
// Constructor using an initialization list
|
||||
EllipseEquation() : coeff{} {}
|
||||
|
||||
// Accessor functions marked as const
|
||||
double A() const { return coeff[1]; }
|
||||
double B() const { return coeff[2]; }
|
||||
double C() const { return coeff[3]; }
|
||||
double D() const { return coeff[4]; }
|
||||
double E() const { return coeff[5]; }
|
||||
double F() const { return coeff[6]; }
|
||||
};
|
||||
|
||||
// ================================ CIRCLES ================================
|
||||
struct Circle {
|
||||
double xc, yc, r; // Center (xc, yc) & radius.
|
||||
double circleFitError; // circle fit error
|
||||
double coverRatio; // Percentage of the circle covered by the arcs making up this circle [0-1]
|
||||
|
||||
double *x, *y; // Pointers to buffers containing the pixels making up this circle
|
||||
int noPixels; // # of pixels making up this circle
|
||||
|
||||
// If this circle is better approximated by an ellipse, we set isEllipse to true & eq contains the ellipse's equation
|
||||
EllipseEquation eq;
|
||||
double ellipseFitError; // ellipse fit error
|
||||
bool isEllipse;
|
||||
double majorAxisLength; // Length of the major axis
|
||||
double minorAxisLength; // Length of the minor axis
|
||||
};
|
||||
|
||||
// ------------------------------------------- ARCS ----------------------------------------------------
|
||||
struct MyArc {
|
||||
double xc, yc, r; // center x, y and radius
|
||||
double circleFitError; // Error during circle fit
|
||||
|
||||
double sTheta, eTheta; // Start & end angle in radius
|
||||
double coverRatio; // Ratio of the pixels covered on the covering circle [0-1] (noPixels/circumference)
|
||||
|
||||
int turn; // Turn direction: 1 or -1
|
||||
|
||||
int segmentNo; // SegmentNo where this arc belongs
|
||||
|
||||
int sx, sy; // Start (x, y) coordinate
|
||||
int ex, ey; // End (x, y) coordinate of the arc
|
||||
|
||||
double *x, *y; // Pointer to buffer containing the pixels making up this arc
|
||||
int noPixels; // # of pixels making up the arc
|
||||
|
||||
bool isEllipse; // Did we fit an ellipse to this arc?
|
||||
EllipseEquation eq; // If an ellipse, then the ellipse's equation
|
||||
double ellipseFitError; // Error during ellipse fit
|
||||
};
|
||||
|
||||
// =============================== AngleSet ==================================
|
||||
|
||||
// add a circular arc to the list of arcs
|
||||
inline double ArcLength(double sTheta, double eTheta)
|
||||
{
|
||||
if (eTheta > sTheta)
|
||||
return eTheta - sTheta;
|
||||
else
|
||||
return CV_2PI - sTheta + eTheta;
|
||||
}
|
||||
|
||||
// A fast implementation of the AngleSet class. The slow implementation is really bad. About 10 times slower than this!
|
||||
struct AngleSetArc {
|
||||
double sTheta;
|
||||
double eTheta;
|
||||
int next; // Next AngleSetArc in the linked list
|
||||
};
|
||||
|
||||
struct AngleSet {
|
||||
AngleSetArc angles[360];
|
||||
int head;
|
||||
int next; // Next AngleSetArc to be allocated
|
||||
double overlapAmount; // Total overlap of the arcs in angleSet. Computed during set() function
|
||||
|
||||
AngleSet() { clear(); }
|
||||
void clear() { head = -1; next = 0; overlapAmount = 0; }
|
||||
double overlapRatio() { return overlapAmount / CV_2PI; }
|
||||
|
||||
void _set(double sTheta, double eTheta);
|
||||
void set(double sTheta, double eTheta);
|
||||
|
||||
double _overlap(double sTheta, double eTheta);
|
||||
double overlap(double sTheta, double eTheta);
|
||||
|
||||
void computeStartEndTheta(double& sTheta, double& eTheta);
|
||||
double coverRatio();
|
||||
};
|
||||
|
||||
void AngleSet::_set(double sTheta, double eTheta)
|
||||
{
|
||||
int arc = next++;
|
||||
|
||||
angles[arc].sTheta = sTheta;
|
||||
angles[arc].eTheta = eTheta;
|
||||
angles[arc].next = -1;
|
||||
|
||||
// Add the current arc to the linked list
|
||||
int prev = -1;
|
||||
int current = head;
|
||||
while (1)
|
||||
{
|
||||
// Empty list?
|
||||
if (head < 0)
|
||||
{
|
||||
head = arc;
|
||||
break;
|
||||
}
|
||||
|
||||
// End of the list. Add to the end
|
||||
if (current < 0)
|
||||
{
|
||||
CV_Assert(prev >= 0);
|
||||
angles[prev].next = arc;
|
||||
break;
|
||||
}
|
||||
|
||||
if (angles[arc].eTheta <= angles[current].sTheta)
|
||||
{
|
||||
// Add before current
|
||||
if (prev < 0)
|
||||
{
|
||||
angles[arc].next = current;
|
||||
head = arc;
|
||||
}
|
||||
else
|
||||
{
|
||||
angles[arc].next = current;
|
||||
angles[prev].next = arc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (angles[arc].sTheta >= angles[current].eTheta)
|
||||
{
|
||||
// continue
|
||||
prev = current;
|
||||
current = angles[current].next;
|
||||
|
||||
// End of the list?
|
||||
if (current < 0)
|
||||
{
|
||||
angles[prev].next = arc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// overlaps with current. Join
|
||||
// First delete current from the list
|
||||
if (prev < 0)
|
||||
head = angles[head].next;
|
||||
else
|
||||
angles[prev].next = angles[current].next;
|
||||
|
||||
// Update overlap amount.
|
||||
if (angles[arc].eTheta < angles[current].eTheta)
|
||||
{
|
||||
overlapAmount += angles[arc].eTheta - angles[current].sTheta;
|
||||
}
|
||||
else
|
||||
{
|
||||
overlapAmount += angles[current].eTheta - angles[arc].sTheta;
|
||||
}
|
||||
|
||||
// Now join current with arc
|
||||
if (angles[current].sTheta < angles[arc].sTheta)
|
||||
angles[arc].sTheta = angles[current].sTheta;
|
||||
if (angles[current].eTheta > angles[arc].eTheta)
|
||||
angles[arc].eTheta = angles[current].eTheta;
|
||||
current = angles[current].next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AngleSet::set(double sTheta, double eTheta)
|
||||
{
|
||||
if (eTheta > sTheta)
|
||||
{
|
||||
_set(sTheta, eTheta);
|
||||
}
|
||||
else
|
||||
{
|
||||
_set(sTheta, CV_2PI);
|
||||
_set(0, eTheta);
|
||||
}
|
||||
}
|
||||
|
||||
double AngleSet::_overlap(double sTheta, double eTheta)
|
||||
{
|
||||
double o = 0;
|
||||
|
||||
int current = head;
|
||||
while (current >= 0)
|
||||
{
|
||||
if (sTheta > angles[current].eTheta)
|
||||
{
|
||||
current = angles[current].next;
|
||||
continue;
|
||||
}
|
||||
else if (eTheta < angles[current].sTheta)
|
||||
break;
|
||||
|
||||
// 3 cases.
|
||||
if (sTheta < angles[current].sTheta && eTheta > angles[current].eTheta)
|
||||
{
|
||||
o += angles[current].eTheta - angles[current].sTheta;
|
||||
|
||||
}
|
||||
else if (sTheta < angles[current].sTheta)
|
||||
{
|
||||
o += eTheta - angles[current].sTheta;
|
||||
}
|
||||
else
|
||||
{
|
||||
o += angles[current].eTheta - sTheta;
|
||||
}
|
||||
|
||||
current = angles[current].next;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
double AngleSet::overlap(double sTheta, double eTheta)
|
||||
{
|
||||
double o;
|
||||
|
||||
if (eTheta > sTheta)
|
||||
{
|
||||
o = _overlap(sTheta, eTheta);
|
||||
}
|
||||
else
|
||||
{
|
||||
o = _overlap(sTheta, CV_2PI);
|
||||
o += _overlap(0, eTheta);
|
||||
}
|
||||
return o / ArcLength(sTheta, eTheta);
|
||||
}
|
||||
|
||||
void AngleSet::computeStartEndTheta(double& sTheta, double& eTheta)
|
||||
{
|
||||
// Special case: Just one arc
|
||||
if (angles[head].next < 0)
|
||||
{
|
||||
sTheta = angles[head].sTheta;
|
||||
eTheta = angles[head].eTheta;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// OK. More than one arc. Find the biggest gap
|
||||
int current = head;
|
||||
int nextArc = angles[current].next;
|
||||
|
||||
double biggestGapSTheta = angles[current].eTheta;
|
||||
double biggestGapEtheta = angles[nextArc].sTheta;
|
||||
double biggestGapLength = biggestGapEtheta - biggestGapSTheta;
|
||||
|
||||
double start, end, len;
|
||||
while (1)
|
||||
{
|
||||
current = nextArc;
|
||||
nextArc = angles[nextArc].next;
|
||||
if (nextArc < 0)
|
||||
break;
|
||||
|
||||
start = angles[current].eTheta;
|
||||
end = angles[nextArc].sTheta;
|
||||
len = end - start;
|
||||
|
||||
if (len > biggestGapLength)
|
||||
{
|
||||
biggestGapSTheta = start;
|
||||
biggestGapEtheta = end;
|
||||
biggestGapLength = len;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the gap between the last arc & the first arc
|
||||
start = angles[current].eTheta;
|
||||
end = angles[head].sTheta;
|
||||
len = CV_2PI - start + end;
|
||||
if (len > biggestGapLength)
|
||||
{
|
||||
biggestGapSTheta = start;
|
||||
biggestGapEtheta = end;
|
||||
}
|
||||
sTheta = biggestGapEtheta;
|
||||
eTheta = biggestGapSTheta;
|
||||
}
|
||||
|
||||
double AngleSet::coverRatio()
|
||||
{
|
||||
int current = head;
|
||||
|
||||
double total = 0;
|
||||
while (current >= 0)
|
||||
{
|
||||
total += angles[current].eTheta - angles[current].sTheta;
|
||||
current = angles[current].next;
|
||||
}
|
||||
return total / CV_2PI;
|
||||
}
|
||||
|
||||
struct EDArcs {
|
||||
MyArc *arcs;
|
||||
int noArcs;
|
||||
|
||||
public:
|
||||
EDArcs(int size = 10000) {
|
||||
arcs = new MyArc[size];
|
||||
noArcs = 0;
|
||||
}
|
||||
|
||||
~EDArcs() {
|
||||
delete[] arcs;
|
||||
}
|
||||
};
|
||||
|
||||
struct BufferManager {
|
||||
double *x, *y;
|
||||
int index;
|
||||
|
||||
BufferManager(int maxSize) {
|
||||
x = new double[maxSize];
|
||||
y = new double[maxSize];
|
||||
index = 0;
|
||||
}
|
||||
|
||||
~BufferManager() {
|
||||
delete[] x;
|
||||
delete[] y;
|
||||
}
|
||||
|
||||
double *getX() { return &x[index]; }
|
||||
double *getY() { return &y[index]; }
|
||||
void move(int size) { index += size; }
|
||||
};
|
||||
|
||||
struct Info {
|
||||
int sign; // -1 or 1: sign of the cross product
|
||||
double angle; // angle with the next line (in radians)
|
||||
bool taken; // Is this line taken during arc detection
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "edgeaware_filters_common.hpp"
|
||||
#include "dtfilter_cpu.hpp"
|
||||
|
||||
#include <opencv2/core/cvdef.h>
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <cmath>
|
||||
using namespace std;
|
||||
|
||||
#ifndef SQR
|
||||
#define SQR(x) ((x)*(x))
|
||||
#endif
|
||||
|
||||
#if CV_SSE
|
||||
namespace
|
||||
{
|
||||
|
||||
inline bool CPU_SUPPORT_SSE1()
|
||||
{
|
||||
static const bool is_supported = cv::checkHardwareSupport(CV_CPU_SSE);
|
||||
return is_supported;
|
||||
}
|
||||
|
||||
} // end
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
Ptr<DTFilter> createDTFilterRF(InputArray adistHor, InputArray adistVert, double sigmaSpatial, double sigmaColor, int numIters)
|
||||
{
|
||||
return Ptr<DTFilter>(DTFilterCPU::createRF(adistHor, adistVert, sigmaSpatial, sigmaColor, numIters));
|
||||
}
|
||||
|
||||
int getTotalNumberOfChannels(InputArrayOfArrays src)
|
||||
{
|
||||
CV_Assert(src.isMat() || src.isUMat() || src.isMatVector() || src.isUMatVector());
|
||||
|
||||
if (src.isMat() || src.isUMat())
|
||||
{
|
||||
return src.channels();
|
||||
}
|
||||
else if (src.isMatVector())
|
||||
{
|
||||
int cnNum = 0;
|
||||
const vector<Mat>& srcv = *static_cast<const vector<Mat>*>(src.getObj());
|
||||
for (unsigned i = 0; i < srcv.size(); i++)
|
||||
cnNum += srcv[i].channels();
|
||||
return cnNum;
|
||||
}
|
||||
else if (src.isUMatVector())
|
||||
{
|
||||
int cnNum = 0;
|
||||
const vector<UMat>& srcv = *static_cast<const vector<UMat>*>(src.getObj());
|
||||
for (unsigned i = 0; i < srcv.size(); i++)
|
||||
cnNum += srcv[i].channels();
|
||||
return cnNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void checkSameSizeAndDepth(InputArrayOfArrays src, Size &sz, int &depth)
|
||||
{
|
||||
CV_Assert(src.isMat() || src.isUMat() || src.isMatVector() || src.isUMatVector());
|
||||
|
||||
if (src.isMat() || src.isUMat())
|
||||
{
|
||||
CV_Assert(!src.empty());
|
||||
sz = src.size();
|
||||
depth = src.depth();
|
||||
}
|
||||
else if (src.isMatVector())
|
||||
{
|
||||
const vector<Mat>& srcv = *static_cast<const vector<Mat>*>(src.getObj());
|
||||
CV_Assert(srcv.size() > 0);
|
||||
for (unsigned i = 0; i < srcv.size(); i++)
|
||||
{
|
||||
CV_Assert(srcv[i].depth() == srcv[0].depth());
|
||||
CV_Assert(srcv[i].size() == srcv[0].size());
|
||||
}
|
||||
sz = srcv[0].size();
|
||||
depth = srcv[0].depth();
|
||||
}
|
||||
else if (src.isUMatVector())
|
||||
{
|
||||
const vector<UMat>& srcv = *static_cast<const vector<UMat>*>(src.getObj());
|
||||
CV_Assert(srcv.size() > 0);
|
||||
for (unsigned i = 0; i < srcv.size(); i++)
|
||||
{
|
||||
CV_Assert(srcv[i].depth() == srcv[0].depth());
|
||||
CV_Assert(srcv[i].size() == srcv[0].size());
|
||||
}
|
||||
sz = srcv[0].size();
|
||||
depth = srcv[0].depth();
|
||||
}
|
||||
}
|
||||
|
||||
namespace intrinsics
|
||||
{
|
||||
|
||||
inline float getFloatSignBit()
|
||||
{
|
||||
union
|
||||
{
|
||||
int signInt;
|
||||
float signFloat;
|
||||
};
|
||||
signInt = 0x80000000;
|
||||
|
||||
return signFloat;
|
||||
}
|
||||
|
||||
void add_(float *dst, float *src1, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
b = _mm_loadu_ps(dst + j);
|
||||
b = _mm_add_ps(b, a);
|
||||
_mm_storeu_ps(dst + j, b);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] += src1[j];
|
||||
}
|
||||
|
||||
void mul(float *dst, float *src1, float *src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
b = _mm_loadu_ps(src2 + j);
|
||||
b = _mm_mul_ps(a, b);
|
||||
_mm_storeu_ps(dst + j, b);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = src1[j] * src2[j];
|
||||
}
|
||||
|
||||
void mul(float *dst, float *src1, float src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b;
|
||||
b = _mm_set_ps1(src2);
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
a = _mm_mul_ps(a, b);
|
||||
_mm_storeu_ps(dst + j, a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = src1[j]*src2;
|
||||
}
|
||||
|
||||
void mad(float *dst, float *src1, float alpha, float beta, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b, c;
|
||||
a = _mm_set_ps1(alpha);
|
||||
b = _mm_set_ps1(beta);
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
c = _mm_loadu_ps(src1 + j);
|
||||
c = _mm_mul_ps(c, a);
|
||||
c = _mm_add_ps(c, b);
|
||||
_mm_storeu_ps(dst + j, c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = alpha*src1[j] + beta;
|
||||
}
|
||||
|
||||
void sqr_(float *dst, float *src1, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
a = _mm_mul_ps(a, a);
|
||||
_mm_storeu_ps(dst + j, a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = src1[j] * src1[j];
|
||||
}
|
||||
|
||||
void sqr_dif(float *dst, float *src1, float *src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 d;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
d = _mm_sub_ps(_mm_loadu_ps(src1 + j), _mm_loadu_ps(src2 + j));
|
||||
d = _mm_mul_ps(d, d);
|
||||
_mm_storeu_ps(dst + j, d);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = (src1[j] - src2[j])*(src1[j] - src2[j]);
|
||||
}
|
||||
|
||||
void add_mul(float *dst, float *src1, float *src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b, c;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
b = _mm_loadu_ps(src2 + j);
|
||||
b = _mm_mul_ps(b, a);
|
||||
c = _mm_loadu_ps(dst + j);
|
||||
c = _mm_add_ps(c, b);
|
||||
_mm_storeu_ps(dst + j, c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
{
|
||||
dst[j] += src1[j] * src2[j];
|
||||
}
|
||||
}
|
||||
|
||||
void add_sqr(float *dst, float *src1, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, c;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
a = _mm_mul_ps(a, a);
|
||||
c = _mm_loadu_ps(dst + j);
|
||||
c = _mm_add_ps(c, a);
|
||||
_mm_storeu_ps(dst + j, c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
{
|
||||
dst[j] += src1[j] * src1[j];
|
||||
}
|
||||
}
|
||||
|
||||
void add_sqr_dif(float *dst, float *src1, float *src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, d;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
d = _mm_sub_ps(_mm_loadu_ps(src1 + j), _mm_loadu_ps(src2 + j));
|
||||
d = _mm_mul_ps(d, d);
|
||||
a = _mm_loadu_ps(dst + j);
|
||||
a = _mm_add_ps(a, d);
|
||||
_mm_storeu_ps(dst + j, a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
{
|
||||
dst[j] += (src1[j] - src2[j])*(src1[j] - src2[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void sub_mul(float *dst, float *src1, float *src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b, c;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
b = _mm_loadu_ps(src2 + j);
|
||||
b = _mm_mul_ps(b, a);
|
||||
c = _mm_loadu_ps(dst + j);
|
||||
c = _mm_sub_ps(c, b);
|
||||
_mm_storeu_ps(dst + j, c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] -= src1[j] * src2[j];
|
||||
}
|
||||
|
||||
void sub_mad(float *dst, float *src1, float *src2, float c0, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b, c;
|
||||
__m128 cnst = _mm_set_ps1(c0);
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
b = _mm_loadu_ps(src2 + j);
|
||||
b = _mm_mul_ps(b, a);
|
||||
c = _mm_loadu_ps(dst + j);
|
||||
c = _mm_sub_ps(c, cnst);
|
||||
c = _mm_sub_ps(c, b);
|
||||
_mm_storeu_ps(dst + j, c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] -= src1[j] * src2[j] + c0;
|
||||
}
|
||||
|
||||
void det_2x2(float *dst, float *a00, float *a01, float *a10, float *a11, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_mul_ps(_mm_loadu_ps(a00 + j), _mm_loadu_ps(a11 + j));
|
||||
b = _mm_mul_ps(_mm_loadu_ps(a01 + j), _mm_loadu_ps(a10 + j));
|
||||
a = _mm_sub_ps(a, b);
|
||||
_mm_storeu_ps(dst + j, a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = a00[j]*a11[j] - a01[j]*a10[j];
|
||||
}
|
||||
|
||||
void div_det_2x2(float *a00, float *a01, float *a11, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
const __m128 SIGN_MASK = _mm_set_ps1(getFloatSignBit());
|
||||
|
||||
__m128 a, b, _a00, _a01, _a11;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
_a00 = _mm_loadu_ps(a00 + j);
|
||||
_a11 = _mm_loadu_ps(a11 + j);
|
||||
a = _mm_mul_ps(_a00, _a11);
|
||||
|
||||
_a01 = _mm_loadu_ps(a01 + j);
|
||||
_a01 = _mm_xor_ps(_a01, SIGN_MASK);
|
||||
b = _mm_mul_ps(_a01, _a01);
|
||||
|
||||
a = _mm_sub_ps(a, b);
|
||||
|
||||
_a01 = _mm_div_ps(_a01, a);
|
||||
_a00 = _mm_div_ps(_a00, a);
|
||||
_a11 = _mm_div_ps(_a11, a);
|
||||
|
||||
_mm_storeu_ps(a01 + j, _a01);
|
||||
_mm_storeu_ps(a00 + j, _a00);
|
||||
_mm_storeu_ps(a11 + j, _a11);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
{
|
||||
float det = a00[j] * a11[j] - a01[j] * a01[j];
|
||||
a00[j] /= det;
|
||||
a11[j] /= det;
|
||||
a01[j] /= -det;
|
||||
}
|
||||
}
|
||||
|
||||
void div_1x(float *a1, float *b1, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 _a1, _b1;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
_b1 = _mm_loadu_ps(b1 + j);
|
||||
_a1 = _mm_loadu_ps(a1 + j);
|
||||
_mm_storeu_ps(a1 + j, _mm_div_ps(_a1, _b1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
{
|
||||
a1[j] /= b1[j];
|
||||
}
|
||||
}
|
||||
|
||||
void inv_self(float *src, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_rcp_ps(_mm_loadu_ps(src + j));
|
||||
_mm_storeu_ps(src + j, a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
{
|
||||
src[j] = 1.0f / src[j];
|
||||
}
|
||||
}
|
||||
|
||||
void sqrt_(float *dst, float *src, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_sqrt_ps(_mm_loadu_ps(src + j));
|
||||
_mm_storeu_ps(dst + j, a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = sqrt(src[j]);
|
||||
}
|
||||
|
||||
void min_(float *dst, float *src1, float *src2, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 a, b;
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
a = _mm_loadu_ps(src1 + j);
|
||||
b = _mm_loadu_ps(src2 + j);
|
||||
b = _mm_min_ps(b, a);
|
||||
|
||||
_mm_storeu_ps(dst + j, b);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
dst[j] = std::min(src1[j], src2[j]);
|
||||
}
|
||||
|
||||
void rf_vert_row_pass(float *curRow, float *prevRow, float alphaVal, int w)
|
||||
{
|
||||
int j = 0;
|
||||
#if CV_SSE
|
||||
if (CPU_SUPPORT_SSE1())
|
||||
{
|
||||
__m128 cur, prev, res;
|
||||
__m128 alpha = _mm_set_ps1(alphaVal);
|
||||
for (; j < w - 3; j += 4)
|
||||
{
|
||||
cur = _mm_loadu_ps(curRow + j);
|
||||
prev = _mm_loadu_ps(prevRow + j);
|
||||
|
||||
res = _mm_mul_ps(alpha, _mm_sub_ps(prev, cur));
|
||||
res = _mm_add_ps(res, cur);
|
||||
_mm_storeu_ps(curRow + j, res);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; j < w; j++)
|
||||
curRow[j] += alphaVal*(prevRow[j] - curRow[j]);
|
||||
}
|
||||
|
||||
} //end of cv::ximgproc::intrinsics
|
||||
|
||||
} //end of cv::ximgproc
|
||||
} //end of cv
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#ifndef __EDGEAWAREFILTERS_COMMON_HPP__
|
||||
#define __EDGEAWAREFILTERS_COMMON_HPP__
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
Ptr<DTFilter> createDTFilterRF(InputArray adistHor, InputArray adistVert, double sigmaSpatial, double sigmaColor, int numIters);
|
||||
|
||||
int getTotalNumberOfChannels(InputArrayOfArrays src);
|
||||
|
||||
void checkSameSizeAndDepth(InputArrayOfArrays src, Size &sz, int &depth);
|
||||
|
||||
namespace intrinsics
|
||||
{
|
||||
void add_(float *dst, float *src1, int w);
|
||||
|
||||
void mul(float *dst, float *src1, float *src2, int w);
|
||||
|
||||
void mul(float *dst, float *src1, float src2, int w);
|
||||
|
||||
//dst = alpha*src + beta
|
||||
void mad(float *dst, float *src1, float alpha, float beta, int w);
|
||||
|
||||
void add_mul(float *dst, float *src1, float *src2, int w);
|
||||
|
||||
void sub_mul(float *dst, float *src1, float *src2, int w);
|
||||
|
||||
void sub_mad(float *dst, float *src1, float *src2, float c0, int w);
|
||||
|
||||
void det_2x2(float *dst, float *a00, float *a01, float *a10, float *a11, int w);
|
||||
|
||||
void div_det_2x2(float *a00, float *a01, float *a11, int w);
|
||||
|
||||
void div_1x(float *a1, float *b1, int w);
|
||||
|
||||
void inv_self(float *src, int w);
|
||||
|
||||
|
||||
void sqr_(float *dst, float *src1, int w);
|
||||
|
||||
void sqrt_(float *dst, float *src, int w);
|
||||
|
||||
void sqr_dif(float *dst, float *src1, float *src2, int w);
|
||||
|
||||
void add_sqr_dif(float *dst, float *src1, float *src2, int w);
|
||||
|
||||
void add_sqr(float *dst, float *src1, int w);
|
||||
|
||||
void min_(float *dst, float *src1, float *src2, int w);
|
||||
|
||||
void rf_vert_row_pass(float *curRow, float *prevRow, float alphaVal, int w);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,985 @@
|
||||
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
/*
|
||||
Generate Edge Boxes object proposals in given image(s).
|
||||
Compute Edge Boxes object proposals as described in:
|
||||
C. Lawrence Zitnick and Piotr Dollár
|
||||
"Edge Boxes: Locating Object Proposals from Edges", ECCV 2014.
|
||||
The proposal boxes are fast to compute and give state-of-the-art recall.
|
||||
OpenCV port by: Leonardo Lontra <lhe dot lontra at gmail dot com>
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
inline int clamp(int v, int min, int max)
|
||||
{
|
||||
return v < min ? min : v > max ? max : v;
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
class EdgeBoxesImpl : public EdgeBoxes
|
||||
{
|
||||
public:
|
||||
|
||||
EdgeBoxesImpl(float alpha,
|
||||
float beta,
|
||||
float eta,
|
||||
float minScore,
|
||||
int maxBoxes,
|
||||
float edgeMinMag,
|
||||
float edgeMergeThr,
|
||||
float clusterMinMag,
|
||||
float maxAspectRatio,
|
||||
float minBoxArea,
|
||||
float gamma,
|
||||
float kappa);
|
||||
|
||||
virtual void getBoundingBoxes(InputArray edge_map, InputArray orientation_map, std::vector<Rect> &boxes, OutputArray scores = noArray()) CV_OVERRIDE;
|
||||
|
||||
float getAlpha() const CV_OVERRIDE { return _alpha; }
|
||||
void setAlpha(float value) CV_OVERRIDE
|
||||
{
|
||||
_alpha = value;
|
||||
_sxStep = sqrt(1 / _alpha);
|
||||
_ayStep = (1 + _alpha) / (2 * _alpha);
|
||||
_xyStepRatio = (1 - _alpha) / (1 + _alpha);
|
||||
}
|
||||
|
||||
float getBeta() const CV_OVERRIDE { return _beta; }
|
||||
void setBeta(float value) CV_OVERRIDE { _beta = value; }
|
||||
|
||||
float getEta() const CV_OVERRIDE { return _eta; }
|
||||
void setEta(float value) CV_OVERRIDE { _eta = value; }
|
||||
|
||||
float getMinScore() const CV_OVERRIDE { return _minScore; }
|
||||
void setMinScore(float value) CV_OVERRIDE { _minScore = value; }
|
||||
|
||||
int getMaxBoxes() const CV_OVERRIDE { return _maxBoxes; }
|
||||
void setMaxBoxes(int value) CV_OVERRIDE { _maxBoxes = value; }
|
||||
|
||||
float getEdgeMinMag() const CV_OVERRIDE { return _edgeMinMag; }
|
||||
void setEdgeMinMag(float value) CV_OVERRIDE { _edgeMinMag = value; }
|
||||
|
||||
float getEdgeMergeThr() const CV_OVERRIDE { return _edgeMergeThr; }
|
||||
void setEdgeMergeThr(float value) CV_OVERRIDE { _edgeMergeThr = value; }
|
||||
|
||||
float getClusterMinMag() const CV_OVERRIDE { return _clusterMinMag; }
|
||||
void setClusterMinMag(float value) CV_OVERRIDE { _clusterMinMag = value; }
|
||||
|
||||
float getMaxAspectRatio() const CV_OVERRIDE { return _maxAspectRatio; }
|
||||
void setMaxAspectRatio(float value) CV_OVERRIDE { _maxAspectRatio = value; }
|
||||
|
||||
float getMinBoxArea() const CV_OVERRIDE { return _minBoxArea; }
|
||||
void setMinBoxArea(float value) CV_OVERRIDE { _minBoxArea = value; }
|
||||
|
||||
float getGamma() const CV_OVERRIDE { return _gamma; }
|
||||
void setGamma(float value) CV_OVERRIDE { _gamma = value; }
|
||||
|
||||
float getKappa() const CV_OVERRIDE { return _kappa; }
|
||||
void setKappa(float value) CV_OVERRIDE
|
||||
{
|
||||
_kappa = value;
|
||||
_scaleNorm.resize(10000);
|
||||
for (int i = 0; i < 10000; i++) _scaleNorm[i] = pow(1.f / i, _kappa);
|
||||
}
|
||||
|
||||
//! the destructor
|
||||
virtual ~EdgeBoxesImpl() {}
|
||||
|
||||
private:
|
||||
float _alpha;
|
||||
float _beta;
|
||||
float _eta;
|
||||
float _minScore;
|
||||
int _maxBoxes;
|
||||
float _edgeMinMag;
|
||||
float _edgeMergeThr;
|
||||
float _clusterMinMag;
|
||||
float _maxAspectRatio;
|
||||
float _minBoxArea;
|
||||
float _gamma;
|
||||
float _kappa;
|
||||
|
||||
// edge segment information (see clusterEdges)
|
||||
int h, w; // image dimensions
|
||||
int _segCnt; // total segment count
|
||||
Mat _segIds; // segment ids (-1/0 means no segment)
|
||||
vector<float> _segMag; // segment edge magnitude sums
|
||||
vector<Point2i> _segP; // segment lower-right pixel
|
||||
vector<vector<float> > _segAff; // segment affinities
|
||||
vector<vector<int> > _segAffIdx; // segment neighbors
|
||||
|
||||
// data structures for efficiency (see prepDataStructs)
|
||||
Mat _segIImg, _magIImg;
|
||||
Mat _hIdxImg, _vIdxImg;
|
||||
vector<vector<int> > _hIdxs, _vIdxs;
|
||||
vector<float> _scaleNorm;
|
||||
float _sxStep, _ayStep, _xyStepRatio;
|
||||
|
||||
// data structures for efficiency (see scoreBox)
|
||||
Mat _sWts;
|
||||
Mat _sDone, _sMap, _sIds;
|
||||
int _sId;
|
||||
|
||||
// helper routines
|
||||
static bool boxesCompare(const Box &a, const Box &b) { return a.score < b.score; }
|
||||
void clusterEdges(Mat &edgeMap, Mat &orientationMap);
|
||||
void prepDataStructs(Mat &edgeMap);
|
||||
void scoreAllBoxes(Boxes &boxes);
|
||||
void scoreBox(Box &box);
|
||||
void refineBox(Box &box);
|
||||
float boxesOverlap(Box &a, Box &b);
|
||||
void boxesNms(Boxes &boxes, float thr, float eta, int maxBoxes);
|
||||
};
|
||||
|
||||
|
||||
EdgeBoxesImpl::EdgeBoxesImpl(float alpha,
|
||||
float beta,
|
||||
float eta,
|
||||
float minScore,
|
||||
int maxBoxes,
|
||||
float edgeMinMag,
|
||||
float edgeMergeThr,
|
||||
float clusterMinMag,
|
||||
float maxAspectRatio,
|
||||
float minBoxArea,
|
||||
float gamma,
|
||||
float kappa)
|
||||
: _alpha(alpha),
|
||||
_beta(beta),
|
||||
_eta(eta),
|
||||
_minScore(minScore),
|
||||
_maxBoxes(maxBoxes),
|
||||
_edgeMinMag(edgeMinMag),
|
||||
_edgeMergeThr(edgeMergeThr),
|
||||
_clusterMinMag(clusterMinMag),
|
||||
_maxAspectRatio(maxAspectRatio),
|
||||
_minBoxArea(minBoxArea),
|
||||
_gamma(gamma),
|
||||
_kappa(kappa)
|
||||
|
||||
{
|
||||
// initialize step sizes
|
||||
_sxStep = sqrt(1 / _alpha);
|
||||
_ayStep = (1 + _alpha) / (2 * _alpha);
|
||||
_xyStepRatio = (1 - _alpha) / (1 + _alpha);
|
||||
|
||||
// create _scaleNorm
|
||||
_scaleNorm.resize(10000);
|
||||
for (int i = 0; i < 10000; i++) _scaleNorm[i] = pow(1.f / i, _kappa);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void EdgeBoxesImpl::clusterEdges(Mat &edgeMap, Mat &orientationMap)
|
||||
{
|
||||
int x, y, xd, yd, i, j;
|
||||
|
||||
// greedily merge connected edge pixels into clusters (create _segIds)
|
||||
_segIds = Mat::zeros(w, h, DataType<int>::type);
|
||||
_segCnt = 1;
|
||||
for (x = 0; x < w; x++)
|
||||
{
|
||||
const float *e_ptr = edgeMap.ptr<float>(x);
|
||||
int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 0; y < h; y++)
|
||||
{
|
||||
if (x == 0 || y == 0 || x == w - 1 || y == h - 1 || e_ptr[y] <= _edgeMinMag)
|
||||
{
|
||||
s_ptr[y] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
if (s_ptr[y] != 0) continue;
|
||||
float sumv = 0;
|
||||
int x0 = x;
|
||||
int y0 = y;
|
||||
vector<float> vs;
|
||||
vector<int> xs, ys;
|
||||
|
||||
while (sumv < _edgeMergeThr)
|
||||
{
|
||||
_segIds.at<int>(x0, y0) = _segCnt;
|
||||
float o0 = orientationMap.at<float>(x0, y0);
|
||||
float o1, v;
|
||||
bool found;
|
||||
for (xd = -1; xd <= 1; xd++)
|
||||
{
|
||||
const int *s0_ptr = _segIds.ptr<int>(x0 + xd);
|
||||
const float *o_ptr = orientationMap.ptr<float>(x0 + xd);
|
||||
for (yd = -1; yd <= 1; yd++)
|
||||
{
|
||||
if (s0_ptr[y0 + yd] != 0) continue;
|
||||
found = false;
|
||||
for (i = 0; i < (int)xs.size(); i++)
|
||||
{
|
||||
if (xs[i] == x0 + xd && ys[i] == y0 + yd)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) continue;
|
||||
o1 = o_ptr[y0 + yd];
|
||||
v = fabs(o1 - o0) / (float)CV_PI;
|
||||
if (v > .5f) v = 1 - v;
|
||||
vs.push_back(v);
|
||||
xs.push_back(x0 + xd);
|
||||
ys.push_back(y0 + yd);
|
||||
}
|
||||
}
|
||||
float minv = 1000;
|
||||
j = 0;
|
||||
for (i = 0; i < (int)vs.size(); i++)
|
||||
{
|
||||
if (vs[i] < minv)
|
||||
{
|
||||
minv = vs[i];
|
||||
x0 = xs[i];
|
||||
y0 = ys[i];
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
sumv += minv;
|
||||
if (minv < 1000) vs[j] = 1000;
|
||||
}
|
||||
|
||||
_segCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// merge or remove small segments
|
||||
_segMag.resize(_segCnt, 0);
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
const float *e_ptr = edgeMap.ptr<float>(x);
|
||||
const int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
j = s_ptr[y];
|
||||
if (j > 0) _segMag[j] += e_ptr[y];
|
||||
}
|
||||
}
|
||||
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
j = s_ptr[y];
|
||||
if (j > 0 && _segMag[j] <= _clusterMinMag)
|
||||
s_ptr[y] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
i = 1;
|
||||
while (i > 0)
|
||||
{
|
||||
i = 0;
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
int *s0_ptr = _segIds.ptr<int>(x);
|
||||
const float *o0_ptr = orientationMap.ptr<float>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
if (s0_ptr[y] != 0) continue;
|
||||
float o0 = o0_ptr[y];
|
||||
float o1, v, minv = 1000;
|
||||
j = 0;
|
||||
|
||||
for (xd = -1; xd <= 1; xd++)
|
||||
{
|
||||
const int *s1_ptr = _segIds.ptr<int>(x+xd);
|
||||
const float *o1_ptr = orientationMap.ptr<float>(x+xd);
|
||||
for (yd = -1; yd <= 1; yd++)
|
||||
{
|
||||
if (s1_ptr[y + yd] <= 0) continue;
|
||||
o1 = o1_ptr[y + yd];
|
||||
v = fabs(o1 - o0) / (float)CV_PI;
|
||||
if (v > .5f) v = 1 - v;
|
||||
if (v < minv)
|
||||
{
|
||||
minv = v;
|
||||
j = s1_ptr[y + yd];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s0_ptr[y] = j;
|
||||
if (j > 0) i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compactify representation
|
||||
_segMag.assign(_segCnt, 0);
|
||||
vector<int> map(_segCnt, 0);
|
||||
_segCnt = 1;
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
const float *e_ptr = edgeMap.ptr<float>(x);
|
||||
const int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
j = s_ptr[y];
|
||||
if (j > 0) _segMag[j] += e_ptr[y];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < (int)_segMag.size(); i++)
|
||||
{
|
||||
if (_segMag[i] > 0) map[i] = _segCnt++;
|
||||
}
|
||||
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
j = s_ptr[y];
|
||||
if (j > 0) s_ptr[y] = map[j];
|
||||
}
|
||||
}
|
||||
|
||||
// compute positional means and recompute _segMag
|
||||
_segMag.assign(_segCnt, 0);
|
||||
vector<float> meanX(_segCnt, 0), meanY(_segCnt, 0);
|
||||
vector<float> meanOx(_segCnt, 0), meanOy(_segCnt, 0), meanO(_segCnt, 0);
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
int *s_ptr = _segIds.ptr<int>(x);
|
||||
const float *e_ptr = edgeMap.ptr<float>(x);
|
||||
const float *o_ptr = orientationMap.ptr<float>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
j = s_ptr[y];
|
||||
if (j <= 0) continue;
|
||||
float m = e_ptr[y];
|
||||
float o = o_ptr[y];
|
||||
_segMag[j] += m;
|
||||
meanOx[j] += m * cos(2 * o);
|
||||
meanOy[j] += m * sin(2 * o);
|
||||
meanX[j] += m * x;
|
||||
meanY[j] += m * y;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < _segCnt; i++)
|
||||
{
|
||||
if (_segMag[i] > 0)
|
||||
{
|
||||
float m = _segMag[i];
|
||||
meanX[i] /= m;
|
||||
meanY[i] /= m;
|
||||
meanO[i] = atan2(meanOy[i] / m, meanOx[i] / m) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// compute segment affinities
|
||||
_segAff.resize(_segCnt);
|
||||
_segAffIdx.resize(_segCnt);
|
||||
for (i = 0; i < _segCnt; i++)
|
||||
{
|
||||
_segAff[i].resize(0);
|
||||
_segAffIdx[i].resize(0);
|
||||
}
|
||||
|
||||
const int rad = 2;
|
||||
for (x = rad; x < w - rad; x++)
|
||||
{
|
||||
const int *s0_ptr = _segIds.ptr<int>(x);
|
||||
for (y = rad; y < h - rad; y++)
|
||||
{
|
||||
int s0 = s0_ptr[y];
|
||||
if (s0 <= 0) continue;
|
||||
for (xd = -rad; xd <= rad; xd++)
|
||||
{
|
||||
const int *s1_ptr = _segIds.ptr<int>(x+xd);
|
||||
for (yd = -rad; yd <= rad; yd++)
|
||||
{
|
||||
int s1 = s1_ptr[y + yd];
|
||||
if (s1 <= s0) continue;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < (int)_segAffIdx[s0].size(); i++)
|
||||
{
|
||||
if (_segAffIdx[s0][i] == s1)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) continue;
|
||||
float o = atan2(meanY[s0] - meanY[s1], meanX[s0] - meanX[s1]) + (float)CV_PI / 2.0f;
|
||||
float a = fabs(cos(meanO[s0] - o) * cos(meanO[s1] - o));
|
||||
a = pow(a, _gamma);
|
||||
_segAff[s0].push_back(a);
|
||||
_segAffIdx[s0].push_back(s1);
|
||||
_segAff[s1].push_back(a);
|
||||
_segAffIdx[s1].push_back(s0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute _segC and _segR
|
||||
_segP.resize(_segCnt);
|
||||
for (x = 1; x < w - 1; x++)
|
||||
{
|
||||
const int *s_ptr = _segIds.ptr<int>(x);
|
||||
for (y = 1; y < h - 1; y++)
|
||||
{
|
||||
j = s_ptr[y];
|
||||
if (j > 0)
|
||||
{
|
||||
_segP[j] = Point2i(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EdgeBoxesImpl::prepDataStructs(Mat &edgeMap)
|
||||
{
|
||||
int y, x, i;
|
||||
|
||||
// create _segIImg
|
||||
Mat E1 = Mat::zeros(w, h, DataType<float>::type);
|
||||
|
||||
for (i=0; i < _segCnt; i++)
|
||||
{
|
||||
if (_segMag[i] > 0) E1.at<float>(_segP[i].x, _segP[i].y) = _segMag[i];
|
||||
}
|
||||
|
||||
_segIImg = Mat::zeros(w+1, h+1, DataType<float>::type);
|
||||
_magIImg = Mat::zeros(w+1, h+1, DataType<float>::type);
|
||||
|
||||
for (x=1; x < w; x++)
|
||||
{
|
||||
const float *e_ptr = edgeMap.ptr<float>(x);
|
||||
const float *e1_ptr = E1.ptr<float>(x);
|
||||
const float *si0_ptr = _segIImg.ptr<float>(x);
|
||||
float *si1_ptr = _segIImg.ptr<float>(x+1);
|
||||
const float *mi0_ptr = _magIImg.ptr<float>(x);
|
||||
float *mi1_ptr =_magIImg.ptr<float>(x+1);
|
||||
for (y=1; y < h; y++)
|
||||
{
|
||||
// create _segIImg
|
||||
si1_ptr[y+1] = e1_ptr[y] + si0_ptr[y+1] + si1_ptr[y] - si0_ptr[y];
|
||||
float e = e_ptr[y] > _edgeMinMag ? e_ptr[y] : 0;
|
||||
// create _magIImg
|
||||
mi1_ptr[y+1] = e +mi0_ptr[y+1] + mi1_ptr[y] - mi0_ptr[y];
|
||||
}
|
||||
}
|
||||
|
||||
// create remaining data structures
|
||||
int s = 0;
|
||||
int s1;
|
||||
|
||||
_hIdxs.resize(h);
|
||||
_hIdxImg = Mat::zeros(w, h, DataType<int>::type);
|
||||
for (y = 0; y < h; y++)
|
||||
{
|
||||
s = 0;
|
||||
_hIdxs[y].push_back(s);
|
||||
for (x = 0; x < w; x++)
|
||||
{
|
||||
s1 = _segIds.at<int>(x, y);
|
||||
if (s1 != s)
|
||||
{
|
||||
s = s1;
|
||||
_hIdxs[y].push_back(s);
|
||||
}
|
||||
_hIdxImg.at<int>(x, y) = (int)_hIdxs[y].size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
_vIdxs.resize(w);
|
||||
_vIdxImg = Mat::zeros(w, h, DataType<int>::type);
|
||||
for (x = 0; x < w; x++)
|
||||
{
|
||||
s = 0;
|
||||
_vIdxs[x].push_back(s);
|
||||
for (y = 0; y < h; y++)
|
||||
{
|
||||
s1 = _segIds.at<int>(x, y);
|
||||
if (s1 != s)
|
||||
{
|
||||
s = s1;
|
||||
_vIdxs[x].push_back(s);
|
||||
}
|
||||
_vIdxImg.at<int>(x, y) = (int)_vIdxs[x].size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize scoreBox() data structures
|
||||
int n = _segCnt + 1;
|
||||
_sWts = Mat::zeros(n, 1, DataType<float>::type);
|
||||
_sDone = Mat(n, 1, DataType<int>::type,Scalar(-1));
|
||||
_sMap = Mat::zeros(n, 1, DataType<int>::type);
|
||||
_sIds = Mat::zeros(n, 1, DataType<int>::type);
|
||||
_sId = 0;
|
||||
}
|
||||
|
||||
|
||||
void EdgeBoxesImpl::scoreBox(Box &box)
|
||||
{
|
||||
int i, j, k, q, bh, bw, y0, x0, y1, x1, y0m, y1m, x0m, x1m;
|
||||
float *sWts = (float *)_sWts.data;
|
||||
int *sDone = (int *)_sDone.data;
|
||||
int *sMap = (int *)_sMap.data;
|
||||
int *sIds = (int *)_sIds.data;
|
||||
int sId = _sId++;
|
||||
|
||||
// add edge count inside box
|
||||
y1 = clamp(box.y + box.h, 0, h - 1);
|
||||
y0 = box.y = clamp(box.y, 0, h - 1);
|
||||
x1 = clamp(box.x + box.w, 0, w - 1);
|
||||
x0 = box.x = clamp(box.x, 0, w - 1);
|
||||
bh = box.h = y1 - box.y;
|
||||
bh /= 2;
|
||||
bw = box.w = x1 - box.x;
|
||||
bw /= 2;
|
||||
float v = _segIImg.at<float>(x0, y0) + _segIImg.at<float>(x1 + 1, y1 + 1)
|
||||
- _segIImg.at<float>(x1 + 1, y0) - _segIImg.at<float>(x0, y1 + 1);
|
||||
|
||||
// subtract middle quarter of edges
|
||||
y0m = y0 + bh / 2;
|
||||
y1m = y0m + bh;
|
||||
x0m = x0 + bw / 2;
|
||||
x1m = x0m + bw;
|
||||
v -= _magIImg.at<float>(x0m, y0m) + _magIImg.at<float>(x1m + 1, y1m + 1)
|
||||
- _magIImg.at<float>(x1m + 1, y0m) - _magIImg.at<float>(x0m, y1m + 1);
|
||||
|
||||
// short circuit computation if impossible to score highly
|
||||
float norm = _scaleNorm[bw + bh];
|
||||
box.score = v * norm;
|
||||
if (box.score < _minScore)
|
||||
{
|
||||
box.score = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// find interesecting segments along four boundaries
|
||||
int cs, ce, rs, re, n = 0;
|
||||
cs = _hIdxImg.at<int>(x0, y0);
|
||||
ce = _hIdxImg.at<int>(x1, y0); // top
|
||||
for (i = cs; i <= ce; i++)
|
||||
{
|
||||
j = _hIdxs[y0][i];
|
||||
if (j > 0 && sDone[j] != sId)
|
||||
{
|
||||
sIds[n] = j;
|
||||
sWts[n] = 1;
|
||||
sDone[j] = sId;
|
||||
sMap[j] = n++;
|
||||
}
|
||||
}
|
||||
|
||||
cs = _hIdxImg.at<int>(x0, y1);
|
||||
ce = _hIdxImg.at<int>(x1, y1); // bottom
|
||||
for (i = cs; i <= ce; i++)
|
||||
{
|
||||
j = _hIdxs[y1][i];
|
||||
if (j > 0 && sDone[j] != sId)
|
||||
{
|
||||
sIds[n] = j;
|
||||
sWts[n] = 1;
|
||||
sDone[j] = sId;
|
||||
sMap[j] = n++;
|
||||
}
|
||||
}
|
||||
|
||||
rs = _vIdxImg.at<int>(x0, y0);
|
||||
re = _vIdxImg.at<int>(x0, y1); // left
|
||||
for (i = rs; i <= re; i++)
|
||||
{
|
||||
j = _vIdxs[x0][i];
|
||||
if (j > 0 && sDone[j] != sId)
|
||||
{
|
||||
sIds[n] = j;
|
||||
sWts[n] = 1;
|
||||
sDone[j] = sId;
|
||||
sMap[j] = n++;
|
||||
}
|
||||
}
|
||||
|
||||
rs = _vIdxImg.at<int>(x1, y0);
|
||||
re = _vIdxImg.at<int>(x1, y1); // right
|
||||
for (i = rs; i <= re; i++)
|
||||
{
|
||||
j = _vIdxs[x1][i];
|
||||
if (j > 0 && sDone[j] != sId)
|
||||
{
|
||||
sIds[n] = j;
|
||||
sWts[n] = 1;
|
||||
sDone[j] = sId;
|
||||
sMap[j] = n++;
|
||||
}
|
||||
}
|
||||
|
||||
// follow connected paths and set weights accordingly (ws=1 means remove)
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
float ws = sWts[i];
|
||||
j = sIds[i];
|
||||
for (k = 0; k < (int)_segAffIdx[j].size(); k++)
|
||||
{
|
||||
q = _segAffIdx[j][k];
|
||||
float wq = ws * _segAff[j][k];
|
||||
if (wq < .05f) continue; // short circuit for efficiency
|
||||
if (sDone[q] == sId)
|
||||
{
|
||||
if (wq > sWts[sMap[q]])
|
||||
{
|
||||
sWts[sMap[q]] = wq;
|
||||
i = min(i, sMap[q] - 1);
|
||||
}
|
||||
}
|
||||
else if (_segP[q].x >= x0 && _segP[q].x <= x1 && _segP[q].y >= y0 && _segP[q].y <= y1)
|
||||
{
|
||||
sIds[n] = q;
|
||||
sWts[n] = wq;
|
||||
sDone[q] = sId;
|
||||
sMap[q] = n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally remove segments connected to boundaries
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
k = sIds[i];
|
||||
if (_segP[k].x >= x0 && _segP[k].x <= x1 && _segP[k].y >= y0 && _segP[k].y <= y1) v -= sWts[i] * _segMag[k];
|
||||
}
|
||||
|
||||
v *= norm;
|
||||
if (v < _minScore) v = 0;
|
||||
box.score = v;
|
||||
}
|
||||
|
||||
|
||||
void EdgeBoxesImpl::refineBox(Box &box)
|
||||
{
|
||||
int yStep = (int)(box.h * _xyStepRatio);
|
||||
int xStep = (int)(box.w * _xyStepRatio);
|
||||
while (1)
|
||||
{
|
||||
// prepare for iteration
|
||||
yStep /= 2;
|
||||
xStep /= 2;
|
||||
if (yStep <= 2 && xStep <= 2) break;
|
||||
yStep = max(1, yStep);
|
||||
xStep = max(1, xStep);
|
||||
Box B;
|
||||
// search over y start
|
||||
B = box;
|
||||
B.y = box.y - yStep;
|
||||
B.h = B.h + yStep;
|
||||
scoreBox(B);
|
||||
|
||||
if (B.score <= box.score)
|
||||
{
|
||||
B = box;
|
||||
B.y = box.y + yStep;
|
||||
B.h = B.h - yStep;
|
||||
scoreBox(B);
|
||||
}
|
||||
if (B.score > box.score) box = B;
|
||||
// search over y end
|
||||
B = box;
|
||||
B.h = B.h + yStep;
|
||||
scoreBox(B);
|
||||
|
||||
if (B.score <= box.score)
|
||||
{
|
||||
B = box;
|
||||
B.h = B.h - yStep;
|
||||
scoreBox(B);
|
||||
}
|
||||
if (B.score > box.score) box = B;
|
||||
// search over x start
|
||||
B = box;
|
||||
B.x = box.x - xStep;
|
||||
B.w = B.w + xStep;
|
||||
scoreBox(B);
|
||||
|
||||
if (B.score <= box.score)
|
||||
{
|
||||
B = box;
|
||||
B.x = box.x + xStep;
|
||||
B.w = B.w - xStep;
|
||||
scoreBox(B);
|
||||
}
|
||||
|
||||
if (B.score > box.score) box = B;
|
||||
// search over x end
|
||||
B = box;
|
||||
B.w = B.w + xStep;
|
||||
scoreBox(B);
|
||||
|
||||
if (B.score <= box.score)
|
||||
{
|
||||
B = box;
|
||||
B.w = B.w - xStep;
|
||||
scoreBox(B);
|
||||
}
|
||||
if (B.score > box.score) box = B;
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeBoxesImpl::scoreAllBoxes(Boxes &boxes)
|
||||
{
|
||||
// get list of all boxes roughly distributed in grid
|
||||
boxes.resize(0);
|
||||
int ayRad, sxNum;
|
||||
float minSize = sqrt(_minBoxArea);
|
||||
ayRad = (int)(log(_maxAspectRatio) / log(_ayStep * _ayStep));
|
||||
sxNum = (int)(ceil(log(max(w, h) / minSize) / log(_sxStep)));
|
||||
|
||||
for (int s = 0; s < sxNum; s++)
|
||||
{
|
||||
int a, y, x, bh, bw, ky, kx = -1;
|
||||
float ay, sx;
|
||||
for (a = 0; a < 2 * ayRad + 1; a++)
|
||||
{
|
||||
ay = pow(_ayStep, float(a - ayRad));
|
||||
sx = minSize * pow(_sxStep, float(s));
|
||||
bh = (int)(sx / ay);
|
||||
ky = max(2, (int)(bh * _xyStepRatio));
|
||||
bw = (int)(sx * ay);
|
||||
kx = max(2, (int)(bw * _xyStepRatio));
|
||||
for (x = 0; x < w - bw + kx; x += kx)
|
||||
{
|
||||
for (y = 0; y < h - bh + ky; y += ky)
|
||||
{
|
||||
Box b;
|
||||
b.y = y;
|
||||
b.x = x;
|
||||
b.h = bh;
|
||||
b.w = bw;
|
||||
boxes.push_back(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// score all boxes, refine top candidates
|
||||
int i, k = 0, m = (int)boxes.size();
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
scoreBox(boxes[i]);
|
||||
if (!boxes[i].score) continue;
|
||||
k++;
|
||||
refineBox(boxes[i]);
|
||||
}
|
||||
sort(boxes.rbegin(), boxes.rend(), boxesCompare);
|
||||
boxes.resize(k);
|
||||
}
|
||||
|
||||
|
||||
float EdgeBoxesImpl::boxesOverlap(Box &a, Box &b)
|
||||
{
|
||||
float areai, areaj, areaij;
|
||||
int y0, y1, x0, x1, y1i, x1i, y1j, x1j;
|
||||
y1i = a.y + a.h;
|
||||
x1i = a.x + a.w;
|
||||
if (a.y >= y1i || a.x >= x1i) return 0;
|
||||
|
||||
y1j = b.y + b.h;
|
||||
x1j = b.x + b.w;
|
||||
if (a.y >= y1j || a.x >= x1j) return 0;
|
||||
|
||||
areai = (float) a.w * a.h;
|
||||
y0 = max(a.y, b.y);
|
||||
y1 = min(y1i, y1j);
|
||||
areaj = (float) b.w * b.h;
|
||||
x0 = max(a.x, b.x);
|
||||
x1 = min(x1i, x1j);
|
||||
areaij = (float) max(0, y1 - y0) * max(0, x1 - x0);
|
||||
return areaij / (areai + areaj - areaij);
|
||||
}
|
||||
|
||||
|
||||
void EdgeBoxesImpl::boxesNms(Boxes &boxes, float thr, float eta, int maxBoxes)
|
||||
{
|
||||
sort(boxes.rbegin(), boxes.rend(), boxesCompare);
|
||||
if (thr > .99f) return;
|
||||
|
||||
const int nBin = 10000;
|
||||
const float step = 1 / thr;
|
||||
const float lstep = log(step);
|
||||
|
||||
vector<Boxes> kept;
|
||||
kept.resize(nBin + 1);
|
||||
int n = (int) boxes.size();
|
||||
int i = 0;
|
||||
int j, k, b;
|
||||
int m = 0;
|
||||
int d = 1;
|
||||
|
||||
while (i < n && m < maxBoxes)
|
||||
{
|
||||
b = boxes[i].w * boxes[i].h;
|
||||
|
||||
bool keep = 1;
|
||||
b = clamp((int)(ceil(log(float(b)) / lstep)), d, nBin - d);
|
||||
for (j = b - d; j <= b + d; j++)
|
||||
{
|
||||
for (k = 0; k < (int)kept[j].size(); k++)
|
||||
{
|
||||
if (keep)
|
||||
keep = boxesOverlap(boxes[i], kept[j][k]) <= thr;
|
||||
}
|
||||
}
|
||||
|
||||
if (keep)
|
||||
{
|
||||
kept[b].push_back(boxes[i]);
|
||||
m++;
|
||||
}
|
||||
|
||||
i++;
|
||||
if (keep && eta < 1.0f && thr > .5f)
|
||||
{
|
||||
thr *= eta;
|
||||
d = (int)ceil(log(1.0f / thr) / lstep);
|
||||
}
|
||||
}
|
||||
|
||||
boxes.resize(m);
|
||||
i = 0;
|
||||
for (j = 0; j < nBin; j++)
|
||||
{
|
||||
for (k = 0; k < (int)kept[j].size(); k++)
|
||||
{
|
||||
boxes[i++] = kept[j][k];
|
||||
}
|
||||
}
|
||||
sort(boxes.rbegin(), boxes.rend(), boxesCompare);
|
||||
}
|
||||
|
||||
|
||||
void EdgeBoxesImpl::getBoundingBoxes(InputArray edge_map, InputArray orientation_map, std::vector<Rect> &boxes, OutputArray scores)
|
||||
{
|
||||
CV_Assert(edge_map.depth() == CV_32F);
|
||||
CV_Assert(orientation_map.depth() == CV_32F);
|
||||
|
||||
Mat E = edge_map.getMat().t();
|
||||
Mat O = orientation_map.getMat().t();
|
||||
std::vector<float> _scores;
|
||||
|
||||
h = E.cols;
|
||||
w = E.rows;
|
||||
|
||||
clusterEdges(E, O);
|
||||
prepDataStructs(E);
|
||||
|
||||
Boxes b;
|
||||
scoreAllBoxes(b);
|
||||
boxesNms(b, _beta, _eta, _maxBoxes);
|
||||
|
||||
// create output boxes
|
||||
int n = (int) b.size();
|
||||
boxes.resize(n);
|
||||
|
||||
if (scores.needed())
|
||||
{
|
||||
_scores.resize(n);
|
||||
}
|
||||
|
||||
for(int i=0; i < n; i++)
|
||||
{
|
||||
boxes[i] = Rect((int)b[i].x + 1, (int)b[i].y + 1, (int)b[i].w, (int)b[i].h);
|
||||
if (scores.needed())
|
||||
{
|
||||
_scores[i] = b[i].score;
|
||||
}
|
||||
}
|
||||
|
||||
// return scores if asked for
|
||||
if (scores.needed())
|
||||
{
|
||||
Mat(_scores).copyTo(scores);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ptr<EdgeBoxes> createEdgeBoxes(float alpha,
|
||||
float beta,
|
||||
float eta,
|
||||
float minScore,
|
||||
int maxBoxes,
|
||||
float edgeMinMag,
|
||||
float edgeMergeThr,
|
||||
float clusterMinMag,
|
||||
float maxAspectRatio,
|
||||
float minBoxArea,
|
||||
float gamma,
|
||||
float kappa)
|
||||
{
|
||||
return makePtr<EdgeBoxesImpl>(alpha,
|
||||
beta,
|
||||
eta,
|
||||
minScore,
|
||||
maxBoxes,
|
||||
edgeMinMag,
|
||||
edgeMergeThr,
|
||||
clusterMinMag,
|
||||
maxAspectRatio,
|
||||
minBoxArea,
|
||||
gamma,
|
||||
kappa);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level
|
||||
// directory of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Created by Simon Reich
|
||||
//
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
void edgePreservingFilter(InputArray _src, OutputArray _dst, int d,
|
||||
double threshold)
|
||||
{
|
||||
CV_Assert(_src.type() == CV_8UC3);
|
||||
|
||||
Mat src = _src.getMat();
|
||||
|
||||
// [re]create the output array so that it has the proper size and type.
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
src.copyTo(dst);
|
||||
|
||||
if (d < 3)
|
||||
d = 3;
|
||||
int subwindowX = d, subwindowY = d;
|
||||
|
||||
if (threshold < 0)
|
||||
threshold = 0;
|
||||
|
||||
// number of image channels
|
||||
int nChannel = src.channels();
|
||||
|
||||
vector<double> pixel(nChannel, 0);
|
||||
vector<vector<double>> line1(src.rows, pixel);
|
||||
vector<vector<vector<double>>> weight(src.cols,
|
||||
line1); // global weights
|
||||
vector<vector<vector<double>>> imageResult(
|
||||
src.cols, line1); // global normalized image
|
||||
|
||||
// do algorithm
|
||||
cv::Mat subwindow, subwindow1;
|
||||
for (int posX = 0; posX < src.cols - subwindowX; posX++)
|
||||
{
|
||||
for (int posY = 0; posY < src.rows - subwindowY; posY++)
|
||||
{
|
||||
cv::Rect roi =
|
||||
cv::Rect(posX, posY, subwindowX, subwindowY);
|
||||
subwindow1 = src(roi);
|
||||
cv::GaussianBlur(subwindow1, subwindow, cv::Size(5, 5),
|
||||
0.3, 0.3);
|
||||
|
||||
// compute arithmetic mean of subwindow
|
||||
cv::Scalar ArithmeticMean = cv::mean(subwindow);
|
||||
|
||||
// compute pixelwise distance
|
||||
vector<vector<double>> pixelwiseDist;
|
||||
|
||||
for (int subPosX = 0; subPosX < subwindow.cols;
|
||||
subPosX++)
|
||||
{
|
||||
vector<double> line;
|
||||
for (int subPosY = 0; subPosY < subwindow.rows;
|
||||
subPosY++)
|
||||
{
|
||||
cv::Vec3b intensity =
|
||||
subwindow.at<cv::Vec3b>(subPosY,
|
||||
subPosX);
|
||||
double distance =
|
||||
((double)intensity.val[0] -
|
||||
ArithmeticMean[0]) *
|
||||
((double)intensity.val[0] -
|
||||
ArithmeticMean[0]) +
|
||||
((double)intensity.val[1] -
|
||||
ArithmeticMean[1]) *
|
||||
((double)intensity.val[1] -
|
||||
ArithmeticMean[1]) +
|
||||
((double)intensity.val[2] -
|
||||
ArithmeticMean[2]) *
|
||||
((double)intensity.val[2] -
|
||||
ArithmeticMean[2]);
|
||||
distance = sqrt(distance);
|
||||
|
||||
line.push_back(distance);
|
||||
};
|
||||
|
||||
pixelwiseDist.push_back(line);
|
||||
};
|
||||
|
||||
// compute mean pixelwise distance
|
||||
double meanPixelwiseDist = 0;
|
||||
|
||||
for (int i = 0; i < (int)pixelwiseDist.size(); i++)
|
||||
for (int j = 0;
|
||||
j < (int)pixelwiseDist[i].size(); j++)
|
||||
meanPixelwiseDist +=
|
||||
pixelwiseDist[i][j];
|
||||
|
||||
meanPixelwiseDist /= ((int)pixelwiseDist.size() *
|
||||
(int)pixelwiseDist[0].size());
|
||||
|
||||
// detect edge
|
||||
for (int subPosX = 0; subPosX < subwindow.cols;
|
||||
subPosX++)
|
||||
{
|
||||
for (int subPosY = 0; subPosY < subwindow.rows;
|
||||
subPosY++)
|
||||
{
|
||||
if ((meanPixelwiseDist <= threshold &&
|
||||
pixelwiseDist[subPosX][subPosY] <=
|
||||
threshold) ||
|
||||
(meanPixelwiseDist <= threshold &&
|
||||
pixelwiseDist[subPosX][subPosY] >
|
||||
threshold))
|
||||
{
|
||||
// global Position
|
||||
int globalPosX = posX + subPosX;
|
||||
int globalPosY = posY + subPosY;
|
||||
|
||||
// compute global weight
|
||||
cv::Vec3b intensity =
|
||||
subwindow.at<cv::Vec3b>(
|
||||
subPosY, subPosX);
|
||||
weight[globalPosX][globalPosY]
|
||||
[0] +=
|
||||
intensity.val[0] *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]);
|
||||
weight[globalPosX][globalPosY]
|
||||
[1] +=
|
||||
intensity.val[1] *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]);
|
||||
weight[globalPosX][globalPosY]
|
||||
[2] +=
|
||||
intensity.val[2] *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]);
|
||||
|
||||
// compute final image
|
||||
imageResult[globalPosX]
|
||||
[globalPosY][0] +=
|
||||
intensity.val[0] *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
ArithmeticMean[0];
|
||||
imageResult[globalPosX]
|
||||
[globalPosY][1] +=
|
||||
intensity.val[1] *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
ArithmeticMean[1];
|
||||
imageResult[globalPosX]
|
||||
[globalPosY][2] +=
|
||||
intensity.val[2] *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
(threshold -
|
||||
pixelwiseDist[subPosX]
|
||||
[subPosY]) *
|
||||
ArithmeticMean[2];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// compute final image
|
||||
for (int globalPosX = 0; globalPosX < (int)imageResult.size();
|
||||
globalPosX++)
|
||||
{
|
||||
for (int globalPosY = 0;
|
||||
globalPosY < (int)imageResult[globalPosX].size();
|
||||
globalPosY++)
|
||||
{
|
||||
// cout << "globalPosX: " << globalPosX << "/"
|
||||
// << dst.cols << "," << imageResult.size () <<
|
||||
// "\tglobalPosY: " << globalPosY << "/" <<
|
||||
// dst.rows << "," <<imageResult.at
|
||||
// (globalPosX).size () << endl;
|
||||
|
||||
// add image to result
|
||||
cv::Vec3b intensity =
|
||||
src.at<cv::Vec3b>(globalPosY, globalPosX);
|
||||
imageResult[globalPosX][globalPosY][0] +=
|
||||
(double)intensity.val[0];
|
||||
imageResult[globalPosX][globalPosY][1] +=
|
||||
(double)intensity.val[1];
|
||||
imageResult[globalPosX][globalPosY][2] +=
|
||||
(double)intensity.val[2];
|
||||
|
||||
// normalize using weight
|
||||
imageResult[globalPosX][globalPosY][0] /=
|
||||
(weight[globalPosX][globalPosY][0] + 1);
|
||||
imageResult[globalPosX][globalPosY][1] /=
|
||||
(weight[globalPosX][globalPosY][1] + 1);
|
||||
imageResult[globalPosX][globalPosY][2] /=
|
||||
(weight[globalPosX][globalPosY][2] + 1);
|
||||
|
||||
// copy to output image frame
|
||||
dst.at<cv::Vec3b>(globalPosY, globalPosX)[0] =
|
||||
(uchar)imageResult[globalPosX][globalPosY][0];
|
||||
dst.at<cv::Vec3b>(globalPosY, globalPosX)[1] =
|
||||
(uchar)imageResult[globalPosX][globalPosY][1];
|
||||
dst.at<cv::Vec3b>(globalPosY, globalPosX)[2] =
|
||||
(uchar)imageResult[globalPosX][globalPosY][2];
|
||||
};
|
||||
};
|
||||
}
|
||||
} // namespace ximgproc
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
By downloading, copying, installing or using the software you agree to this license.
|
||||
If you do not agree to this license, do not download, install,
|
||||
copy or use the software.
|
||||
|
||||
|
||||
License Agreement
|
||||
For Open Source Computer Vision Library
|
||||
(3-clause BSD License)
|
||||
|
||||
Copyright (C) 2000-2015, Intel Corporation, all rights reserved.
|
||||
Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
||||
Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved.
|
||||
Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
|
||||
Copyright (C) 2015, OpenCV Foundation, all rights reserved.
|
||||
Copyright (C) 2015, Itseez Inc., all rights reserved.
|
||||
Third party copyrights are property of their respective owners.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
In no event shall copyright holders or contributors be liable for any direct,
|
||||
indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused
|
||||
and on any theory of liability, whether in contract, strict liability,
|
||||
or tort (including negligence or otherwise) arising in any way out of
|
||||
the use of this software, even if advised of the possibility of such damage.
|
||||
|
||||
Algorithmic details of this algorithm can be found at:
|
||||
* O. Green, Y. Birk, "A Computationally Efficient Algorithm for the 2D Covariance Method", ACM/IEEE International Conference on High Performance Computing, Networking, Storage and Analysis, Denver, Colorado, 2013
|
||||
A previous and less efficient version of the algorithm can be found:
|
||||
* O. Green, L. David, A. Galperin, Y. Birk, "Efficient parallel computation of the estimated covariance matrix", arXiv, 2013
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
|
||||
namespace cv{
|
||||
namespace ximgproc{
|
||||
|
||||
class EstimateCovariance{
|
||||
|
||||
public:
|
||||
EstimateCovariance(int pr_, int pc_);
|
||||
~EstimateCovariance();
|
||||
|
||||
void computeEstimateCovariance(Mat inputData,Mat outputData);
|
||||
int combinationCount();
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
int mult1r;
|
||||
int mult1c;
|
||||
int mult2r;
|
||||
int mult2c;
|
||||
int type2; // 0 - for the first P*P. 1 - for the next (P-1)(P-1)
|
||||
int id;
|
||||
} Combination;
|
||||
|
||||
void initInternalDataStructures();
|
||||
void buildCombinationsTable();
|
||||
|
||||
void iterateCombinations(Mat inputData,Mat outputData);
|
||||
void computeOneCombination(int comb_id, Mat inputData , Mat outputData,
|
||||
Mat outputVector,std::vector<int> finalMatPosR, std::vector<int> finalMatPosC);
|
||||
|
||||
inline void complexSubtract(std::complex<float>& src, std::complex<float>& dst){dst-=src;}
|
||||
inline void complexAdd(std::complex<float>& src, std::complex<float>& dst){dst+=src;}
|
||||
inline void complexConjMulAndAdd(std::complex<float>& a, std::complex<float>& b,
|
||||
std::complex<float>& dst){dst += a*b;}
|
||||
inline void complexConjMul(std::complex<float>& a, std::complex<float>& b,
|
||||
std::complex<float>& dst){dst = a*b;}
|
||||
|
||||
private:
|
||||
int nr;
|
||||
int nc;
|
||||
int pr;
|
||||
int pc;
|
||||
|
||||
std::vector<Combination> combinationsTable;
|
||||
};
|
||||
|
||||
|
||||
|
||||
EstimateCovariance::EstimateCovariance(int pr_, int pc_){
|
||||
pr=pr_; pc=pc_;
|
||||
}
|
||||
|
||||
EstimateCovariance::~EstimateCovariance(){
|
||||
|
||||
}
|
||||
|
||||
void EstimateCovariance::initInternalDataStructures(){
|
||||
int combCount = combinationCount();
|
||||
combinationsTable.resize(combCount);
|
||||
buildCombinationsTable();
|
||||
}
|
||||
|
||||
int EstimateCovariance::combinationCount(){
|
||||
return (pr*pc+(pr-1)*(pc-1));
|
||||
}
|
||||
|
||||
void EstimateCovariance::buildCombinationsTable()
|
||||
{
|
||||
int idx_row,idx_col;
|
||||
int comb_idx = 0;
|
||||
Combination comb;
|
||||
|
||||
// The first element of the product is [0,0] and the second is to down and to the right of it.
|
||||
for (idx_col=0; idx_col<pc; ++idx_col) {
|
||||
for (idx_row=0; idx_row<pr; ++idx_row) {
|
||||
comb.mult1r=0;
|
||||
comb.mult1c=0;
|
||||
comb.mult2r=idx_row;
|
||||
comb.mult2c=idx_col;
|
||||
comb.type2 = 0;
|
||||
comb.id = comb_idx;
|
||||
memcpy(&combinationsTable[comb_idx++], &comb, sizeof(Combination));
|
||||
}
|
||||
}
|
||||
|
||||
// The first element is on the top right, and the second element is to the left and down of it.
|
||||
for (idx_row=1; idx_row<pr; ++idx_row) {
|
||||
for (idx_col=1; idx_col<pc; ++idx_col) {
|
||||
comb.mult1r=idx_row;
|
||||
comb.mult1c=0;
|
||||
comb.mult2r=0;
|
||||
comb.mult2c=idx_col;
|
||||
|
||||
comb.type2 = 1;
|
||||
comb.id = comb_idx;
|
||||
memcpy(&combinationsTable[comb_idx++], &comb, sizeof(Combination));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EstimateCovariance::computeEstimateCovariance(Mat inputData,Mat outputData){
|
||||
initInternalDataStructures();
|
||||
nr=inputData.rows;
|
||||
nc=inputData.cols;
|
||||
|
||||
iterateCombinations(inputData,outputData);
|
||||
}
|
||||
|
||||
|
||||
void EstimateCovariance::iterateCombinations(Mat inputData,Mat outputData)
|
||||
{
|
||||
Mat outputVector(pr*pc,1, DataType<std::complex<float> >::type);
|
||||
|
||||
std::vector<int> finalMatPosR(pr*pc,0);
|
||||
std::vector<int> finalMatPosC(pr*pc,0);
|
||||
int combs=combinationCount();
|
||||
for (int idx=0; idx<combs; idx++){
|
||||
outputVector.setTo(Scalar(0,0));
|
||||
for (unsigned x=0; x<finalMatPosR.size(); x++)
|
||||
finalMatPosR[x]=0;
|
||||
for (unsigned x=0; x<finalMatPosC.size(); x++)
|
||||
finalMatPosC[x]=0;
|
||||
computeOneCombination(idx++, inputData, outputData,
|
||||
outputVector,finalMatPosR, finalMatPosC);
|
||||
}
|
||||
}
|
||||
|
||||
void EstimateCovariance::computeOneCombination(int comb_id,Mat inputData, Mat outputData,
|
||||
Mat outputVector,std::vector<int> finalMatPosR, std::vector<int> finalMatPosC)
|
||||
{
|
||||
Combination* comb = &combinationsTable[comb_id];
|
||||
int type2 = comb->type2;
|
||||
int deltaR = (int)abs((int)(comb->mult1r-comb->mult2r));
|
||||
int deltaC = (int)abs((int)(comb->mult1c-comb->mult2c));
|
||||
int numElementsInBlock = pr-abs(deltaR);
|
||||
int numBlocks = pc-deltaC;
|
||||
const int DR= nr-pr;
|
||||
const int DC= nc-pc;
|
||||
|
||||
int elementC=0;
|
||||
std::complex<float> temp_res = std::complex<float>(0,0);
|
||||
int i,j,r,c;
|
||||
|
||||
if (!type2) {
|
||||
// Computing the first index of the combination.
|
||||
// This index is made up
|
||||
for(i=0; i<=( DR); i++) {
|
||||
int iPdr=i+deltaR;
|
||||
for(j=0; j<= (DC); j++) {
|
||||
int jPdc = j+deltaC;
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(i,j), inputData.at<std::complex<float> >(iPdr,jPdc), temp_res);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// Computing the first index of the combination.
|
||||
for(i=0; i<=( DR); i++) {
|
||||
int iPdr=i+deltaR;
|
||||
for(j=0; j<= (DC); j++) {
|
||||
int jPdc = j+deltaC;
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(iPdr,j), inputData.at<std::complex<float> >(i,jPdc), temp_res);
|
||||
}
|
||||
}
|
||||
}
|
||||
outputVector.at<std::complex<float> >(0,0) = temp_res;
|
||||
|
||||
// Checking if the first element belongs to the first set of combinatons.
|
||||
// The combination that the first element is above the second.
|
||||
if (!type2) {
|
||||
finalMatPosR[0]=0;
|
||||
finalMatPosC[0]=pr*deltaC+deltaR;
|
||||
elementC++;
|
||||
}else{
|
||||
finalMatPosR[0]=deltaR;
|
||||
finalMatPosC[0]=pr*deltaC;
|
||||
elementC++;
|
||||
}
|
||||
|
||||
for(r=1;r<(pr-deltaR);r++){
|
||||
std::complex<float> newRowSum = std::complex<float>(0,0),oldRowSum = std::complex<float>(0,0);
|
||||
std::complex<float> addRows = std::complex<float>(0,0);
|
||||
|
||||
int rM1 = r-1;
|
||||
int k = DR+1 + rM1;
|
||||
int kPdr = k+deltaR;
|
||||
int rM1Pdr = rM1 + deltaR;
|
||||
int cPdc;
|
||||
|
||||
if (!type2) {
|
||||
for(c=0;c<=(DC); c++){
|
||||
cPdc = c+deltaC;
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(k,c),inputData.at<std::complex<float> >(kPdr,cPdc),newRowSum);
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(rM1,c),inputData.at<std::complex<float> >(rM1Pdr,cPdc),oldRowSum);
|
||||
}
|
||||
}else{
|
||||
for(c=0;c<=(DC); c++){
|
||||
cPdc = c+deltaC;
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(kPdr,c),inputData.at<std::complex<float> >(k,cPdc),newRowSum);
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(rM1Pdr,c),inputData.at<std::complex<float> >(rM1,cPdc),oldRowSum);
|
||||
}
|
||||
}
|
||||
complexAdd(newRowSum,addRows);
|
||||
complexSubtract(oldRowSum,addRows);
|
||||
complexAdd(outputVector.at<std::complex<float> >(rM1,0),outputVector.at<std::complex<float> >(r,0));
|
||||
complexAdd(addRows,outputVector.at<std::complex<float> >(r,0));
|
||||
|
||||
finalMatPosR[elementC]=finalMatPosR[elementC-1]+1;;
|
||||
finalMatPosC[elementC]=finalMatPosC[elementC-1]+1;
|
||||
elementC++;
|
||||
}
|
||||
|
||||
for(c=1; c<numBlocks; c++) {
|
||||
std::complex<float> newColSum = std::complex<float>(0,0),oldColSum = std::complex<float>(0,0);
|
||||
std::complex<float> addCols = std::complex<float>(0,0);
|
||||
|
||||
// Index arithmetic
|
||||
int cM1 = c-1;
|
||||
int dcPc = DC+c;
|
||||
int q = DC+1 + cM1;
|
||||
int cM1PdeltaC = cM1 + deltaC;
|
||||
int dcPcPdeltaC = dcPc+deltaC;
|
||||
|
||||
if (!type2) {
|
||||
for(r=0;r<=(DR); r++){
|
||||
int rPdr = r+deltaR;
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(r,q),inputData.at<std::complex<float> >(rPdr,q+deltaC),newColSum);
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(r,cM1),inputData.at<std::complex<float> >(rPdr,cM1+deltaC),oldColSum);
|
||||
}
|
||||
}else{
|
||||
for(r=0;r<=(DR); r++){
|
||||
int rPdr = r+deltaR;
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(rPdr,q),inputData.at<std::complex<float> >(r,q+deltaC),newColSum);
|
||||
complexConjMulAndAdd(inputData.at<std::complex<float> >(rPdr,cM1),inputData.at<std::complex<float> >(r,cM1+deltaC),oldColSum);
|
||||
}
|
||||
}
|
||||
complexAdd(newColSum,addCols);
|
||||
complexSubtract(oldColSum,addCols);
|
||||
complexAdd(outputVector.at<std::complex<float> >((c-1)*numElementsInBlock,0),outputVector.at<std::complex<float> >(c*numElementsInBlock,0));
|
||||
complexAdd(addCols,outputVector.at<std::complex<float> >(c*numElementsInBlock,0));
|
||||
|
||||
finalMatPosR[elementC]=finalMatPosR[elementC-numElementsInBlock]+pr;
|
||||
finalMatPosC[elementC]=finalMatPosC[elementC-numElementsInBlock]+pr;
|
||||
elementC++;
|
||||
|
||||
for(r=1; r<numElementsInBlock; r++) {
|
||||
std::complex<float> w = std::complex<float>(0,0),x = std::complex<float>(0,0),
|
||||
y = std::complex<float>(0,0),z = std::complex<float>(0,0),deltaRowSum = std::complex<float>(0,0);
|
||||
std::complex<float> tempRes = std::complex<float>(0,0);
|
||||
// Index arithmetic
|
||||
int rM1 = r-1;
|
||||
int drPr = DR+r;
|
||||
int rM1PdeltaR = rM1 + deltaR;
|
||||
int drPrPdeltaR = drPr+deltaR;
|
||||
|
||||
if (!type2) {
|
||||
complexConjMul(inputData.at<std::complex<float> >(rM1,cM1),inputData.at<std::complex<float> >(rM1PdeltaR,cM1PdeltaC),w);
|
||||
complexConjMul(inputData.at<std::complex<float> >(rM1,dcPc),inputData.at<std::complex<float> >(rM1PdeltaR,dcPcPdeltaC),x);
|
||||
complexConjMul(inputData.at<std::complex<float> >(drPr,cM1),inputData.at<std::complex<float> >(drPrPdeltaR,cM1PdeltaC),y);
|
||||
complexConjMul(inputData.at<std::complex<float> >(drPr,dcPc),inputData.at<std::complex<float> >(drPrPdeltaR,dcPcPdeltaC),z);
|
||||
}else{
|
||||
complexConjMul(inputData.at<std::complex<float> >(rM1PdeltaR,cM1),inputData.at<std::complex<float> >(rM1,cM1PdeltaC),w);
|
||||
complexConjMul(inputData.at<std::complex<float> >(rM1PdeltaR,dcPc),inputData.at<std::complex<float> >(rM1,dcPcPdeltaC),x);
|
||||
complexConjMul(inputData.at<std::complex<float> >(drPrPdeltaR,cM1),inputData.at<std::complex<float> >(drPr,cM1PdeltaC),y);
|
||||
complexConjMul(inputData.at<std::complex<float> >(drPrPdeltaR,dcPc),inputData.at<std::complex<float> >(drPr,dcPcPdeltaC),z);
|
||||
}
|
||||
complexAdd(w,tempRes);
|
||||
complexSubtract(x,tempRes);
|
||||
complexSubtract(y,tempRes);
|
||||
complexAdd(z,tempRes);
|
||||
|
||||
complexAdd(outputVector.at<std::complex<float> >((c-1)*numElementsInBlock + r,0),deltaRowSum);
|
||||
complexSubtract(outputVector.at<std::complex<float> >((c-1)*numElementsInBlock+ rM1,0),deltaRowSum);
|
||||
|
||||
complexAdd(deltaRowSum,tempRes);
|
||||
complexAdd(outputVector.at<std::complex<float> >(c*numElementsInBlock+rM1,0),tempRes);
|
||||
complexAdd(tempRes,outputVector.at<std::complex<float> >(c*numElementsInBlock+r,0));
|
||||
|
||||
finalMatPosR[elementC]=finalMatPosR[elementC-1]+1;
|
||||
finalMatPosC[elementC]=finalMatPosC[elementC-1]+1;
|
||||
elementC++;
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; i<numElementsInBlock*numBlocks; i++){
|
||||
outputData.at<std::complex<float> >(finalMatPosR[i],finalMatPosC[i])=outputVector.at<std::complex<float> >(i,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void covarianceEstimation(InputArray input_, OutputArray output_,int windowRows, int windowCols){
|
||||
|
||||
CV_Assert( input_.channels() <= 2); // Does not take color images.
|
||||
|
||||
Mat input;
|
||||
|
||||
Mat temp=input_.getMat();
|
||||
if(temp.channels() == 1){
|
||||
temp.convertTo(temp,CV_32FC2);
|
||||
Mat zmat = Mat::zeros(temp.size(), CV_32F);
|
||||
Mat twoChannelsbefore[] = {temp,zmat};
|
||||
cv::merge(twoChannelsbefore,2,input);
|
||||
}else{
|
||||
temp.convertTo(input, CV_32FC2);
|
||||
|
||||
}
|
||||
|
||||
EstimateCovariance estCov(windowRows,windowCols);
|
||||
|
||||
//input_.getMat().convertTo(input, CV_32FC2);
|
||||
|
||||
output_.create(windowRows*windowCols,windowRows*windowCols, DataType<std::complex<float> >::type);
|
||||
|
||||
Mat output = output_.getMat();
|
||||
|
||||
estCov.computeEstimateCovariance(input,output);
|
||||
}
|
||||
|
||||
} // namespace ximgproc
|
||||
} // namespace cv
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,682 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "opencv2/geometry.hpp"
|
||||
|
||||
struct SEGMENT
|
||||
{
|
||||
float x1, y1, x2, y2, angle;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace cv{
|
||||
namespace ximgproc{
|
||||
|
||||
class FastLineDetectorImpl : public FastLineDetector
|
||||
{
|
||||
public:
|
||||
|
||||
FastLineDetectorImpl(int _length_threshold = 10, float _distance_threshold = 1.414213562f,
|
||||
double _canny_th1 = 50.0, double _canny_th2 = 50.0, int _canny_aperture_size = 3,
|
||||
bool _do_merge = false);
|
||||
|
||||
void detect(InputArray image, OutputArray lines) CV_OVERRIDE;
|
||||
|
||||
void drawSegments(InputOutputArray image, InputArray lines, bool draw_arrow = false, Scalar linecolor = Scalar(0, 0, 255), int linethickness = 1) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
int imagewidth, imageheight, threshold_length;
|
||||
float threshold_dist;
|
||||
double canny_th1, canny_th2;
|
||||
int canny_aperture_size;
|
||||
bool do_merge;
|
||||
|
||||
FastLineDetectorImpl& operator= (const FastLineDetectorImpl&); // to quiet MSVC
|
||||
template<class T>
|
||||
void incidentPoint(const Mat& l, T& pt);
|
||||
|
||||
void mergeLines(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged);
|
||||
|
||||
bool mergeSegments(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged);
|
||||
|
||||
bool getPointChain(const Mat& img, Point pt, Point& chained_pt, float& direction, int step);
|
||||
|
||||
double distPointLine(const Mat& p, Mat& l);
|
||||
|
||||
void extractSegments(const std::vector<Point2i>& points, std::vector<SEGMENT>& segments);
|
||||
|
||||
void lineDetection(const Mat& src, std::vector<SEGMENT>& segments_all);
|
||||
|
||||
void pointInboardTest(const Size srcSize, Point2i& pt);
|
||||
|
||||
inline void getAngle(SEGMENT& seg);
|
||||
|
||||
void additionalOperationsOnSegment(const Mat& src, SEGMENT& seg);
|
||||
|
||||
void drawSegment(InputOutputArray image, const SEGMENT& seg, Scalar bgr = Scalar(0,255,0), int thickness = 1, bool directed = true);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CV_EXPORTS Ptr<FastLineDetector> createFastLineDetector(
|
||||
int length_threshold, float distance_threshold,
|
||||
double canny_th1, double canny_th2, int canny_aperture_size, bool do_merge)
|
||||
{
|
||||
return makePtr<FastLineDetectorImpl>(
|
||||
length_threshold, distance_threshold,
|
||||
canny_th1, canny_th2, canny_aperture_size, do_merge);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FastLineDetectorImpl::FastLineDetectorImpl(int _length_threshold, float _distance_threshold,
|
||||
double _canny_th1, double _canny_th2, int _canny_aperture_size, bool _do_merge)
|
||||
:threshold_length(_length_threshold), threshold_dist(_distance_threshold),
|
||||
canny_th1(_canny_th1), canny_th2(_canny_th2), canny_aperture_size(_canny_aperture_size), do_merge(_do_merge)
|
||||
{
|
||||
CV_Assert(_length_threshold > 0 && _distance_threshold > 0 &&
|
||||
_canny_th1 > 0 && _canny_th2 > 0 && _canny_aperture_size >= 0);
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::detect(InputArray _image, OutputArray _lines)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat image = _image.getMat();
|
||||
CV_Assert(!image.empty() && image.type() == CV_8UC1);
|
||||
|
||||
std::vector<Vec4f> lines;
|
||||
std::vector<SEGMENT> segments;
|
||||
lineDetection(image, segments);
|
||||
for(size_t i = 0; i < segments.size(); ++i)
|
||||
{
|
||||
const SEGMENT seg = segments[i];
|
||||
Vec4f line(seg.x1, seg.y1, seg.x2, seg.y2);
|
||||
lines.push_back(line);
|
||||
}
|
||||
Mat(lines).copyTo(_lines);
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::drawSegments(InputOutputArray image, InputArray lines, bool draw_arrow, Scalar linecolor, int linethickness)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int cn = image.channels();
|
||||
CV_Assert(!image.empty() && ( cn == 1 || cn == 3 || cn == 4));
|
||||
|
||||
if (cn == 1)
|
||||
{
|
||||
cvtColor(image, image, COLOR_GRAY2BGR);
|
||||
}
|
||||
else
|
||||
{
|
||||
cvtColor(image, image, COLOR_BGRA2GRAY);
|
||||
cvtColor(image, image, cn == 3 ? COLOR_GRAY2BGR : COLOR_GRAY2BGRA);
|
||||
}
|
||||
|
||||
double gap = 10.0;
|
||||
double arrow_angle = 30.0;
|
||||
|
||||
Mat _lines;
|
||||
_lines = lines.getMat();
|
||||
int N = _lines.checkVector(4);
|
||||
// Draw segments
|
||||
for(int i = 0; i < N; ++i)
|
||||
{
|
||||
const Vec4f& v = _lines.at<Vec4f>(i);
|
||||
Point2f b(v[0], v[1]);
|
||||
Point2f e(v[2], v[3]);
|
||||
line(image, b, e, linecolor, linethickness);
|
||||
if(draw_arrow)
|
||||
{
|
||||
SEGMENT seg;
|
||||
seg.x1 = b.x;
|
||||
seg.y1 = b.y;
|
||||
seg.x2 = e.x;
|
||||
seg.y2 = e.y;
|
||||
getAngle(seg);
|
||||
double ang = (double)seg.angle;
|
||||
Point2i p1;
|
||||
p1.x = cvRound(seg.x2 - gap*cos(arrow_angle * CV_PI / 180.0 + ang));
|
||||
p1.y = cvRound(seg.y2 - gap*sin(arrow_angle * CV_PI / 180.0 + ang));
|
||||
pointInboardTest(image.size(), p1);
|
||||
line(image, Point(cvRound(seg.x2), cvRound(seg.y2)), p1, linecolor, linethickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::mergeLines(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged)
|
||||
{
|
||||
double xg = 0.0, yg = 0.0;
|
||||
double delta1x = 0.0, delta1y = 0.0, delta2x = 0.0, delta2y = 0.0;
|
||||
float ax = 0, bx = 0, cx = 0, dx = 0;
|
||||
float ay = 0, by = 0, cy = 0, dy = 0;
|
||||
double li = 0.0, lj = 0.0;
|
||||
double thi = 0.0, thj = 0.0, thr = 0.0;
|
||||
double axg = 0.0, bxg = 0.0, cxg = 0.0, dxg = 0.0, delta1xg = 0.0, delta2xg = 0.0;
|
||||
|
||||
ax = seg1.x1;
|
||||
ay = seg1.y1;
|
||||
|
||||
bx = seg1.x2;
|
||||
by = seg1.y2;
|
||||
cx = seg2.x1;
|
||||
cy = seg2.y1;
|
||||
|
||||
dx = seg2.x2;
|
||||
dy = seg2.y2;
|
||||
|
||||
float dlix = (bx - ax);
|
||||
float dliy = (by - ay);
|
||||
float dljx = (dx - cx);
|
||||
float dljy = (dy - cy);
|
||||
|
||||
li = sqrt((double) (dlix * dlix) + (double) (dliy * dliy));
|
||||
lj = sqrt((double) (dljx * dljx) + (double) (dljy * dljy));
|
||||
|
||||
xg = (li * (double) (ax + bx) + lj * (double) (cx + dx))
|
||||
/ (double) (2.0 * (li + lj));
|
||||
yg = (li * (double) (ay + by) + lj * (double) (cy + dy))
|
||||
/ (double) (2.0 * (li + lj));
|
||||
|
||||
if(dlix == 0.0f) thi = CV_PI / 2.0;
|
||||
else thi = atan(dliy / dlix);
|
||||
|
||||
if(dljx == 0.0f) thj = CV_PI / 2.0;
|
||||
else thj = atan(dljy / dljx);
|
||||
|
||||
if (fabs(thi - thj) <= CV_PI / 2.0)
|
||||
{
|
||||
thr = (li * thi + lj * thj) / (li + lj);
|
||||
}
|
||||
else
|
||||
{
|
||||
double tmp = thj - CV_PI * (thj / fabs(thj));
|
||||
thr = li * thi + lj * tmp;
|
||||
thr /= (li + lj);
|
||||
}
|
||||
|
||||
axg = ((double) ay - yg) * sin(thr) + ((double) ax - xg) * cos(thr);
|
||||
bxg = ((double) by - yg) * sin(thr) + ((double) bx - xg) * cos(thr);
|
||||
cxg = ((double) cy - yg) * sin(thr) + ((double) cx - xg) * cos(thr);
|
||||
dxg = ((double) dy - yg) * sin(thr) + ((double) dx - xg) * cos(thr);
|
||||
|
||||
delta1xg = min(axg,min(bxg,min(cxg,dxg)));
|
||||
delta2xg = max(axg,max(bxg,max(cxg,dxg)));
|
||||
|
||||
delta1x = delta1xg * cos(thr) + xg;
|
||||
delta1y = delta1xg * sin(thr) + yg;
|
||||
delta2x = delta2xg * cos(thr) + xg;
|
||||
delta2y = delta2xg * sin(thr) + yg;
|
||||
|
||||
seg_merged.x1 = (float)delta1x;
|
||||
seg_merged.y1 = (float)delta1y;
|
||||
seg_merged.x2 = (float)delta2x;
|
||||
seg_merged.y2 = (float)delta2y;
|
||||
}
|
||||
|
||||
double FastLineDetectorImpl::distPointLine(const Mat& p, Mat& l)
|
||||
{
|
||||
double x = l.at<double>(0,0);
|
||||
double y = l.at<double>(1,0);
|
||||
double w = sqrt(x*x+y*y);
|
||||
|
||||
l.at<double>(0,0) = x / w;
|
||||
l.at<double>(1,0) = y / w;
|
||||
l.at<double>(2,0) = l.at<double>(2,0) / w;
|
||||
|
||||
return l.dot(p);
|
||||
}
|
||||
|
||||
bool FastLineDetectorImpl::mergeSegments(const SEGMENT& seg1, const SEGMENT& seg2, SEGMENT& seg_merged)
|
||||
{
|
||||
double o[] = { 0.0, 0.0, 1.0 };
|
||||
double a[] = { 0.0, 0.0, 1.0 };
|
||||
double b[] = { 0.0, 0.0, 1.0 };
|
||||
double c[3];
|
||||
|
||||
o[0] = ( seg2.x1 + seg2.x2 ) / 2.0;
|
||||
o[1] = ( seg2.y1 + seg2.y2 ) / 2.0;
|
||||
|
||||
a[0] = seg1.x1;
|
||||
a[1] = seg1.y1;
|
||||
b[0] = seg1.x2;
|
||||
b[1] = seg1.y2;
|
||||
|
||||
Mat ori = Mat(3, 1, CV_64FC1, o).clone();
|
||||
Mat p1 = Mat(3, 1, CV_64FC1, a).clone();
|
||||
Mat p2 = Mat(3, 1, CV_64FC1, b).clone();
|
||||
Mat l1 = Mat(3, 1, CV_64FC1, c).clone();
|
||||
|
||||
l1 = p1.cross(p2);
|
||||
|
||||
Point2f seg1mid, seg2mid;
|
||||
seg1mid.x = (seg1.x1 + seg1.x2) /2.0f;
|
||||
seg1mid.y = (seg1.y1 + seg1.y2) /2.0f;
|
||||
seg2mid.x = (seg2.x1 + seg2.x2) /2.0f;
|
||||
seg2mid.y = (seg2.y1 + seg2.y2) /2.0f;
|
||||
|
||||
float seg1len = sqrt((seg1.x1 - seg1.x2)*(seg1.x1 - seg1.x2)+(seg1.y1 - seg1.y2)*(seg1.y1 - seg1.y2));
|
||||
float seg2len = sqrt((seg2.x1 - seg2.x2)*(seg2.x1 - seg2.x2)+(seg2.y1 - seg2.y2)*(seg2.y1 - seg2.y2));
|
||||
float middist = sqrt((seg1mid.x - seg2mid.x)*(seg1mid.x - seg2mid.x) + (seg1mid.y - seg2mid.y)*(seg1mid.y - seg2mid.y));
|
||||
float angdiff = fabs(seg1.angle - seg2.angle);
|
||||
|
||||
float dist = (float)distPointLine(ori, l1);
|
||||
|
||||
if ( fabs( dist ) <= threshold_dist * 2.0f && middist <= seg1len / 2.0f + seg2len / 2.0f + 20.0f
|
||||
&& angdiff <= CV_PI / 180.0f * 5.0f)
|
||||
{
|
||||
mergeLines(seg1, seg2, seg_merged);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FastLineDetectorImpl::incidentPoint(const Mat& l, T& pt)
|
||||
{
|
||||
double a[] = { (double)pt.x, (double)pt.y, 1.0 };
|
||||
double b[] = { l.at<double>(0,0), l.at<double>(1,0), 0.0 };
|
||||
double c[3];
|
||||
|
||||
Mat xk = Mat(3, 1, CV_64FC1, a).clone();
|
||||
Mat lh = Mat(3, 1, CV_64FC1, b).clone();
|
||||
Mat lk = Mat(3, 1, CV_64FC1, c).clone();
|
||||
|
||||
lk = xk.cross(lh);
|
||||
xk = lk.cross(l);
|
||||
|
||||
xk.convertTo(xk, -1, 1.0 / xk.at<double>(2,0));
|
||||
|
||||
Point2f pt_tmp;
|
||||
pt_tmp.x = (float)xk.at<double>(0,0) < 0.0f ? 0.0f : (float)xk.at<double>(0,0)
|
||||
>= (imagewidth - 1.0f) ? (imagewidth - 1.0f) : (float)xk.at<double>(0,0);
|
||||
pt_tmp.y = (float)xk.at<double>(1,0) < 0.0f ? 0.0f : (float)xk.at<double>(1,0)
|
||||
>= (imageheight - 1.0f) ? (imageheight - 1.0f) : (float)xk.at<double>(1,0);
|
||||
pt = T(pt_tmp);
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::extractSegments(const std::vector<Point2i>& points, std::vector<SEGMENT>& segments)
|
||||
{
|
||||
bool is_line;
|
||||
|
||||
int i, j;
|
||||
SEGMENT seg;
|
||||
Point2i ps, pe, pt;
|
||||
|
||||
std::vector<Point2i> l_points;
|
||||
|
||||
int total = (int)points.size();
|
||||
|
||||
for ( i = 0; i + threshold_length < total; i++ )
|
||||
{
|
||||
ps = points[i];
|
||||
pe = points[i + threshold_length];
|
||||
|
||||
double a[] = { (double)ps.x, (double)ps.y, 1 };
|
||||
double b[] = { (double)pe.x, (double)pe.y, 1 };
|
||||
double c[3], d[3];
|
||||
|
||||
Mat p1 = Mat(3, 1, CV_64FC1, a).clone();
|
||||
Mat p2 = Mat(3, 1, CV_64FC1, b).clone();
|
||||
Mat p = Mat(3, 1, CV_64FC1, c).clone();
|
||||
Mat l = Mat(3, 1, CV_64FC1, d).clone();
|
||||
l = p1.cross(p2);
|
||||
|
||||
is_line = true;
|
||||
|
||||
l_points.clear();
|
||||
l_points.push_back(ps);
|
||||
|
||||
for ( j = 1; j < threshold_length; j++ )
|
||||
{
|
||||
pt.x = points[i+j].x;
|
||||
pt.y = points[i+j].y;
|
||||
|
||||
p.at<double>(0,0) = (double)pt.x;
|
||||
p.at<double>(1,0) = (double)pt.y;
|
||||
p.at<double>(2,0) = 1.0;
|
||||
|
||||
double dist = distPointLine(p, l);
|
||||
|
||||
if ( fabs( dist ) > threshold_dist )
|
||||
{
|
||||
is_line = false;
|
||||
break;
|
||||
}
|
||||
l_points.push_back(pt);
|
||||
}
|
||||
|
||||
// Line check fail, test next point
|
||||
if ( is_line == false )
|
||||
continue;
|
||||
|
||||
l_points.push_back(pe);
|
||||
|
||||
Vec4f line;
|
||||
fitLine( Mat(l_points), line, DIST_L2, 0, 0.01, 0.01);
|
||||
a[0] = line[2];
|
||||
a[1] = line[3];
|
||||
b[0] = line[2] + line[0];
|
||||
b[1] = line[3] + line[1];
|
||||
|
||||
p1 = Mat(3, 1, CV_64FC1, a).clone();
|
||||
p2 = Mat(3, 1, CV_64FC1, b).clone();
|
||||
|
||||
l = p1.cross(p2);
|
||||
|
||||
incidentPoint(l, ps);
|
||||
|
||||
// Extending line
|
||||
for ( j = threshold_length + 1; i + j < total; j++ )
|
||||
{
|
||||
pt.x = points[i+j].x;
|
||||
pt.y = points[i+j].y;
|
||||
|
||||
p.at<double>(0,0) = (double)pt.x;
|
||||
p.at<double>(1,0) = (double)pt.y;
|
||||
p.at<double>(2,0) = 1.0;
|
||||
|
||||
double dist = distPointLine(p, l);
|
||||
if ( fabs( dist ) > threshold_dist )
|
||||
{
|
||||
fitLine( Mat(l_points), line, DIST_L2, 0, 0.01, 0.01);
|
||||
a[0] = line[2];
|
||||
a[1] = line[3];
|
||||
b[0] = line[2] + line[0];
|
||||
b[1] = line[3] + line[1];
|
||||
|
||||
p1 = Mat(3, 1, CV_64FC1, a).clone();
|
||||
p2 = Mat(3, 1, CV_64FC1, b).clone();
|
||||
|
||||
l = p1.cross(p2);
|
||||
dist = distPointLine(p, l);
|
||||
if ( fabs( dist ) > threshold_dist ) {
|
||||
j--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pe = pt;
|
||||
l_points.push_back(pt);
|
||||
}
|
||||
fitLine( Mat(l_points), line, DIST_L2, 0, 0.01, 0.01);
|
||||
a[0] = line[2];
|
||||
a[1] = line[3];
|
||||
b[0] = line[2] + line[0];
|
||||
b[1] = line[3] + line[1];
|
||||
|
||||
p1 = Mat(3, 1, CV_64FC1, a).clone();
|
||||
p2 = Mat(3, 1, CV_64FC1, b).clone();
|
||||
|
||||
l = p1.cross(p2);
|
||||
|
||||
Point2f e1, e2;
|
||||
e1.x = (float)ps.x;
|
||||
e1.y = (float)ps.y;
|
||||
e2.x = (float)pe.x;
|
||||
e2.y = (float)pe.y;
|
||||
|
||||
incidentPoint(l, e1);
|
||||
incidentPoint(l, e2);
|
||||
seg.x1 = e1.x;
|
||||
seg.y1 = e1.y;
|
||||
seg.x2 = e2.x;
|
||||
seg.y2 = e2.y;
|
||||
|
||||
segments.push_back(seg);
|
||||
i = i + j;
|
||||
}
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::pointInboardTest(const Size srcSize, Point2i& pt)
|
||||
{
|
||||
pt.x = pt.x <= 5 ? 5 : pt.x >= srcSize.width - 5 ? srcSize.width - 5 : pt.x;
|
||||
pt.y = pt.y <= 5 ? 5 : pt.y >= srcSize.height - 5 ? srcSize.height - 5 : pt.y;
|
||||
}
|
||||
|
||||
bool FastLineDetectorImpl::getPointChain(const Mat& img, Point pt,
|
||||
Point& chained_pt, float& direction, int step)
|
||||
{
|
||||
int ri, ci;
|
||||
int indices[8][2] = { {1,1}, {1,0}, {1,-1}, {0,-1},
|
||||
{-1,-1},{-1,0}, {-1,1}, {0,1} };
|
||||
|
||||
float min_dir_diff = 7.0f;
|
||||
Point consistent_pt;
|
||||
int consistent_direction = 0;
|
||||
for ( int i = 0; i < 8; i++ )
|
||||
{
|
||||
ci = pt.x + indices[i][1];
|
||||
ri = pt.y + indices[i][0];
|
||||
|
||||
if ( ri < 0 || ri == img.rows || ci < 0 || ci == img.cols )
|
||||
continue;
|
||||
|
||||
if ( img.at<unsigned char>(ri, ci) == 0 )
|
||||
continue;
|
||||
|
||||
if(step == 0)
|
||||
{
|
||||
chained_pt.x = ci;
|
||||
chained_pt.y = ri;
|
||||
// direction = (float)i;
|
||||
direction = i > 4 ? (float)(i - 8) : (float)i;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
float curr_dir = i > 4 ? (float)(i - 8) : (float)i;
|
||||
float dir_diff = abs(curr_dir - direction);
|
||||
dir_diff = dir_diff > 4.0f ? 8.0f - dir_diff : dir_diff;
|
||||
if(dir_diff <= min_dir_diff)
|
||||
{
|
||||
min_dir_diff = dir_diff;
|
||||
consistent_pt.x = ci;
|
||||
consistent_pt.y = ri;
|
||||
consistent_direction = i > 4 ? i - 8 : i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(min_dir_diff < 2.0f)
|
||||
{
|
||||
chained_pt.x = consistent_pt.x;
|
||||
chained_pt.y = consistent_pt.y;
|
||||
direction = (direction * (float)step + (float)consistent_direction)
|
||||
/ (float)(step + 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::lineDetection(const Mat& src, std::vector<SEGMENT>& segments_all)
|
||||
{
|
||||
int r, c;
|
||||
imageheight=src.rows; imagewidth=src.cols;
|
||||
|
||||
std::vector<Point2i> points;
|
||||
std::vector<SEGMENT> segments, segments_tmp;
|
||||
Mat canny;
|
||||
if (canny_aperture_size == 0)
|
||||
{
|
||||
canny = src;
|
||||
}
|
||||
else
|
||||
{
|
||||
Canny(src, canny, canny_th1, canny_th2, canny_aperture_size);
|
||||
}
|
||||
canny.colRange(0,6).rowRange(0,6).setTo(cv::Scalar::all(0));
|
||||
canny.colRange(src.cols-5,src.cols).rowRange(src.rows-5,src.rows).setTo(cv::Scalar::all(0));
|
||||
|
||||
SEGMENT seg, seg1, seg2;
|
||||
|
||||
for ( r = 0; r < imageheight; r++ )
|
||||
{
|
||||
for ( c = 0; c < imagewidth; c++ )
|
||||
{
|
||||
// Find seeds - skip for non-seeds
|
||||
if ( canny.at<unsigned char>(r,c) == 0 )
|
||||
continue;
|
||||
|
||||
// Found seeds
|
||||
Point2i pt = Point2i(c,r);
|
||||
|
||||
points.push_back(pt);
|
||||
canny.at<unsigned char>(pt.y, pt.x) = 0;
|
||||
|
||||
float direction = 0.0f;
|
||||
int step = 0;
|
||||
while(getPointChain(canny, pt, pt, direction, step))
|
||||
{
|
||||
points.push_back(pt);
|
||||
step++;
|
||||
canny.at<unsigned char>(pt.y, pt.x) = 0;
|
||||
}
|
||||
|
||||
if ( points.size() < (unsigned int)threshold_length + 1 )
|
||||
{
|
||||
points.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
extractSegments(points, segments);
|
||||
|
||||
if ( segments.size() == 0 )
|
||||
{
|
||||
points.clear();
|
||||
continue;
|
||||
}
|
||||
for ( int i = 0; i < (int)segments.size(); i++ )
|
||||
{
|
||||
seg = segments[i];
|
||||
float length = sqrt((seg.x1 - seg.x2)*(seg.x1 - seg.x2) +
|
||||
(seg.y1 - seg.y2)*(seg.y1 - seg.y2));
|
||||
if(length < threshold_length)
|
||||
continue;
|
||||
if( (seg.x1 <= 5.0f && seg.x2 <= 5.0f) ||
|
||||
(seg.y1 <= 5.0f && seg.y2 <= 5.0f) ||
|
||||
(seg.x1 >= imagewidth - 5.0f && seg.x2 >= imagewidth - 5.0f) ||
|
||||
(seg.y1 >= imageheight - 5.0f && seg.y2 >= imageheight - 5.0f) )
|
||||
continue;
|
||||
additionalOperationsOnSegment(src, seg);
|
||||
if(!do_merge)
|
||||
segments_all.push_back(seg);
|
||||
segments_tmp.push_back(seg);
|
||||
}
|
||||
points.clear();
|
||||
segments.clear();
|
||||
}
|
||||
}
|
||||
if(!do_merge)
|
||||
return;
|
||||
|
||||
bool is_merged = false;
|
||||
int ith = (int)segments_tmp.size() - 1;
|
||||
int jth = ith - 1;
|
||||
while(ith > 1 || jth >= 0)
|
||||
{
|
||||
seg1 = segments_tmp[ith];
|
||||
seg2 = segments_tmp[jth];
|
||||
SEGMENT seg_merged;
|
||||
is_merged = mergeSegments(seg1, seg2, seg_merged);
|
||||
if(is_merged == true)
|
||||
{
|
||||
seg2 = seg_merged;
|
||||
additionalOperationsOnSegment(src, seg2);
|
||||
std::vector<SEGMENT>::iterator it = segments_tmp.begin() + ith;
|
||||
*it = seg2;
|
||||
segments_tmp.erase(segments_tmp.begin()+jth);
|
||||
ith--;
|
||||
jth = ith - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
jth--;
|
||||
}
|
||||
if(jth < 0) {
|
||||
ith--;
|
||||
jth = ith - 1;
|
||||
}
|
||||
}
|
||||
segments_all = segments_tmp;
|
||||
}
|
||||
|
||||
inline void FastLineDetectorImpl::getAngle(SEGMENT& seg)
|
||||
{
|
||||
seg.angle = (float)(fastAtan2(seg.y2 - seg.y1, seg.x2 - seg.x1) / 180.0f * CV_PI);
|
||||
}
|
||||
|
||||
void FastLineDetectorImpl::additionalOperationsOnSegment(const Mat& src, SEGMENT& seg)
|
||||
{
|
||||
if(seg.x1 == 0.0f && seg.x2 == 0.0f && seg.y1 == 0.0f && seg.y2 == 0.0f)
|
||||
return;
|
||||
|
||||
getAngle(seg);
|
||||
double ang = (double)seg.angle;
|
||||
|
||||
Point2f start = Point2f(seg.x1, seg.y1);
|
||||
Point2f end = Point2f(seg.x2, seg.y2);
|
||||
|
||||
double dx = 0.0, dy = 0.0;
|
||||
dx = (double) end.x - (double) start.x;
|
||||
dy = (double) end.y - (double) start.y;
|
||||
|
||||
int num_points = 10;
|
||||
Point2f *points = new Point2f[num_points];
|
||||
|
||||
points[0] = start;
|
||||
points[num_points - 1] = end;
|
||||
for (int i = 0; i < num_points; i++)
|
||||
{
|
||||
if (i == 0 || i == num_points - 1)
|
||||
continue;
|
||||
points[i].x = points[0].x + ((float)dx / float(num_points - 1) * (float) i);
|
||||
points[i].y = points[0].y + ((float)dy / float(num_points - 1) * (float) i);
|
||||
}
|
||||
|
||||
Point2i *points_right = new Point2i[num_points];
|
||||
Point2i *points_left = new Point2i[num_points];
|
||||
double gap = 1.0;
|
||||
|
||||
for(int i = 0; i < num_points; i++)
|
||||
{
|
||||
points_right[i].x = cvRound(points[i].x + gap*cos(90.0 * CV_PI / 180.0 + ang));
|
||||
points_right[i].y = cvRound(points[i].y + gap*sin(90.0 * CV_PI / 180.0 + ang));
|
||||
points_left[i].x = cvRound(points[i].x - gap*cos(90.0 * CV_PI / 180.0 + ang));
|
||||
points_left[i].y = cvRound(points[i].y - gap*sin(90.0 * CV_PI / 180.0 + ang));
|
||||
pointInboardTest(src.size(), points_right[i]);
|
||||
pointInboardTest(src.size(), points_left[i]);
|
||||
}
|
||||
|
||||
int iR = 0, iL = 0;
|
||||
for(int i = 0; i < num_points; i++)
|
||||
{
|
||||
iR += src.at<unsigned char>(points_right[i].y, points_right[i].x);
|
||||
iL += src.at<unsigned char>(points_left[i].y, points_left[i].x);
|
||||
}
|
||||
|
||||
if(iR > iL)
|
||||
{
|
||||
std::swap(seg.x1, seg.x2);
|
||||
std::swap(seg.y1, seg.y2);
|
||||
getAngle(seg);
|
||||
}
|
||||
|
||||
delete[] points;
|
||||
delete[] points_right;
|
||||
delete[] points_left;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
} // namespace ximgproc
|
||||
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#ifdef HAVE_EIGEN
|
||||
# if defined __GNUC__ && defined __APPLE__
|
||||
# pragma GCC diagnostic ignored "-Wshadow"
|
||||
# endif
|
||||
# include <Eigen/Dense>
|
||||
# include <Eigen/SparseCore>
|
||||
# include <Eigen/SparseCholesky>
|
||||
# include <Eigen/IterativeLinearSolvers>
|
||||
# include <Eigen/Sparse>
|
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus <= 199711L
|
||||
#include <map>
|
||||
typedef std::map<long long /* hash */, int /* vert id */> mapId;
|
||||
#else
|
||||
#include <unordered_map>
|
||||
typedef std::unordered_map<long long /* hash */, int /* vert id */> mapId;
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
class FastBilateralSolverFilterImpl : public FastBilateralSolverFilter
|
||||
{
|
||||
public:
|
||||
|
||||
static Ptr<FastBilateralSolverFilterImpl> create(InputArray guide, double sigma_spatial, double sigma_luma, double sigma_chroma, double lambda, int num_iter, double max_tol)
|
||||
{
|
||||
CV_Assert(guide.type() == CV_8UC1 || guide.type() == CV_8UC3);
|
||||
FastBilateralSolverFilterImpl *fbs = new FastBilateralSolverFilterImpl();
|
||||
Mat gui = guide.getMat();
|
||||
fbs->init(gui,sigma_spatial,sigma_luma,sigma_chroma,lambda,num_iter,max_tol);
|
||||
return Ptr<FastBilateralSolverFilterImpl>(fbs);
|
||||
}
|
||||
|
||||
void filter(InputArray src, InputArray confidence, OutputArray dst) CV_OVERRIDE
|
||||
{
|
||||
|
||||
CV_Assert(!src.empty() && (src.depth() == CV_8U || src.depth() == CV_16S || src.depth() == CV_16U || src.depth() == CV_32F) && src.channels()<=4);
|
||||
CV_Assert(!confidence.empty() && (confidence.depth() == CV_8U || confidence.depth() == CV_32F) && confidence.channels()==1);
|
||||
if (src.rows() != rows || src.cols() != cols)
|
||||
{
|
||||
CV_Error(Error::StsBadSize, "Size of the filtered image must be equal to the size of the guide image");
|
||||
return;
|
||||
}
|
||||
if (confidence.rows() != rows || confidence.cols() != cols)
|
||||
{
|
||||
CV_Error(Error::StsBadSize, "Size of the confidence image must be equal to the size of the guide image");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Mat> src_channels;
|
||||
std::vector<Mat> dst_channels;
|
||||
if(src.channels()==1)
|
||||
src_channels.push_back(src.getMat());
|
||||
else
|
||||
split(src,src_channels);
|
||||
|
||||
Mat conf = confidence.getMat();
|
||||
|
||||
for(int i=0;i<src.channels();i++)
|
||||
{
|
||||
Mat cur_res = src_channels[i].clone();
|
||||
|
||||
solve(cur_res,conf,cur_res);
|
||||
cur_res.convertTo(cur_res, src.type());
|
||||
dst_channels.push_back(cur_res);
|
||||
}
|
||||
|
||||
dst.create(src.size(),src_channels[0].type());
|
||||
if(src.channels()==1)
|
||||
{
|
||||
Mat& dstMat = dst.getMatRef();
|
||||
dstMat = dst_channels[0];
|
||||
}
|
||||
else
|
||||
merge(dst_channels,dst);
|
||||
CV_Assert(src.type() == dst.type() && src.size() == dst.size());
|
||||
}
|
||||
|
||||
// protected:
|
||||
void solve(cv::Mat& src, cv::Mat& confidence, cv::Mat& dst);
|
||||
void init(cv::Mat& reference, double sigma_spatial, double sigma_luma, double sigma_chroma, double lambda, int num_iter, double max_tol);
|
||||
|
||||
void Splat(Eigen::VectorXf& input, Eigen::VectorXf& dst);
|
||||
void Blur(Eigen::VectorXf& input, Eigen::VectorXf& dst);
|
||||
void Slice(Eigen::VectorXf& input, Eigen::VectorXf& dst);
|
||||
|
||||
void diagonal(Eigen::VectorXf& v,Eigen::SparseMatrix<float>& mat)
|
||||
{
|
||||
mat = Eigen::SparseMatrix<float>(v.size(),v.size());
|
||||
for (int i = 0; i < int(v.size()); i++)
|
||||
{
|
||||
mat.insert(i,i) = v(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int npixels;
|
||||
int nvertices;
|
||||
int dim;
|
||||
int cols;
|
||||
int rows;
|
||||
std::vector<int> splat_idx;
|
||||
std::vector<std::pair<int, int> > blur_idx;
|
||||
Eigen::VectorXf m;
|
||||
Eigen::VectorXf n;
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> blurs;
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> S;
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> Dn;
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> Dm;
|
||||
|
||||
struct grid_params
|
||||
{
|
||||
float spatialSigma;
|
||||
float lumaSigma;
|
||||
float chromaSigma;
|
||||
grid_params()
|
||||
{
|
||||
spatialSigma = 8.0;
|
||||
lumaSigma = 8.0;
|
||||
chromaSigma = 8.0;
|
||||
}
|
||||
};
|
||||
|
||||
struct bs_params
|
||||
{
|
||||
float lam;
|
||||
float A_diag_min;
|
||||
float cg_tol;
|
||||
int cg_maxiter;
|
||||
bs_params()
|
||||
{
|
||||
lam = 128.0;
|
||||
A_diag_min = 1e-5f;
|
||||
cg_tol = 1e-5f;
|
||||
cg_maxiter = 25;
|
||||
}
|
||||
};
|
||||
|
||||
grid_params grid_param;
|
||||
bs_params bs_param;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
void FastBilateralSolverFilterImpl::init(cv::Mat& reference, double sigma_spatial, double sigma_luma, double sigma_chroma, double lambda, int num_iter, double max_tol)
|
||||
{
|
||||
|
||||
bs_param.lam = lambda;
|
||||
bs_param.cg_maxiter = num_iter;
|
||||
bs_param.cg_tol = max_tol;
|
||||
|
||||
if(reference.channels()==1)
|
||||
{
|
||||
dim = 3;
|
||||
cols = reference.cols;
|
||||
rows = reference.rows;
|
||||
npixels = cols*rows;
|
||||
long long hash_vec[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
hash_vec[i] = static_cast<long long>(std::pow(255, i));
|
||||
|
||||
mapId hashed_coords;
|
||||
#if __cplusplus <= 199711L
|
||||
#else
|
||||
hashed_coords.reserve(cols*rows);
|
||||
#endif
|
||||
|
||||
const unsigned char* pref = (const unsigned char*)reference.data;
|
||||
int vert_idx = 0;
|
||||
int pix_idx = 0;
|
||||
|
||||
// construct Splat(Slice) matrices
|
||||
splat_idx.resize(npixels);
|
||||
for (int y = 0; y < rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < cols; ++x)
|
||||
{
|
||||
long long coord[3];
|
||||
coord[0] = int(x / sigma_spatial);
|
||||
coord[1] = int(y / sigma_spatial);
|
||||
coord[2] = int(pref[0] / sigma_luma);
|
||||
|
||||
// convert the coordinate to a hash value
|
||||
long long hash_coord = 0;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
hash_coord += coord[i] * hash_vec[i];
|
||||
|
||||
// pixels whom are alike will have the same hash value.
|
||||
// We only want to keep a unique list of hash values, therefore make sure we only insert
|
||||
// unique hash values.
|
||||
mapId::iterator it = hashed_coords.find(hash_coord);
|
||||
if (it == hashed_coords.end())
|
||||
{
|
||||
hashed_coords.insert(std::pair<long long, int>(hash_coord, vert_idx));
|
||||
splat_idx[pix_idx] = vert_idx;
|
||||
++vert_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
splat_idx[pix_idx] = it->second;
|
||||
}
|
||||
|
||||
pref += 1; // skip 1 bytes (y)
|
||||
++pix_idx;
|
||||
}
|
||||
}
|
||||
nvertices = static_cast<int>(hashed_coords.size());
|
||||
|
||||
// construct Blur matrices
|
||||
Eigen::VectorXf ones_nvertices = Eigen::VectorXf::Ones(nvertices);
|
||||
diagonal(ones_nvertices,blurs);
|
||||
blurs *= 10;
|
||||
for(int offset = -1; offset <= 1;++offset)
|
||||
{
|
||||
if(offset == 0) continue;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> blur_temp(hashed_coords.size(), hashed_coords.size());
|
||||
blur_temp.reserve(Eigen::VectorXi::Constant(nvertices,6));
|
||||
long long offset_hash_coord = offset * hash_vec[i];
|
||||
for (mapId::iterator it = hashed_coords.begin(); it != hashed_coords.end(); ++it)
|
||||
{
|
||||
long long neighb_coord = it->first + offset_hash_coord;
|
||||
mapId::iterator it_neighb = hashed_coords.find(neighb_coord);
|
||||
if (it_neighb != hashed_coords.end())
|
||||
{
|
||||
blur_temp.insert(it->second,it_neighb->second) = 1.0f;
|
||||
blur_idx.push_back(std::pair<int,int>(it->second, it_neighb->second));
|
||||
}
|
||||
}
|
||||
blurs += blur_temp;
|
||||
}
|
||||
}
|
||||
blurs.finalize();
|
||||
|
||||
//bistochastize
|
||||
int maxiter = 10;
|
||||
n = ones_nvertices;
|
||||
m = Eigen::VectorXf::Zero(nvertices);
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
m(splat_idx[i]) += 1.0f;
|
||||
}
|
||||
|
||||
Eigen::VectorXf bluredn(nvertices);
|
||||
|
||||
for (int i = 0; i < maxiter; i++)
|
||||
{
|
||||
Blur(n,bluredn);
|
||||
n = ((n.array()*m.array()).array()/bluredn.array()).array().sqrt();
|
||||
}
|
||||
Blur(n,bluredn);
|
||||
|
||||
m = n.array() * (bluredn).array();
|
||||
diagonal(m,Dm);
|
||||
diagonal(n,Dn);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
dim = 5;
|
||||
cv::Mat reference_yuv;
|
||||
cv::cvtColor(reference, reference_yuv, COLOR_BGR2YCrCb);
|
||||
|
||||
cols = reference_yuv.cols;
|
||||
rows = reference_yuv.rows;
|
||||
npixels = cols*rows;
|
||||
long long hash_vec[5];
|
||||
for (int i = 0; i < 5; ++i)
|
||||
hash_vec[i] = static_cast<long long>(std::pow(255, i));
|
||||
|
||||
mapId hashed_coords;
|
||||
#if __cplusplus <= 199711L
|
||||
#else
|
||||
hashed_coords.reserve(cols*rows);
|
||||
#endif
|
||||
|
||||
const unsigned char* pref = (const unsigned char*)reference_yuv.data;
|
||||
int vert_idx = 0;
|
||||
int pix_idx = 0;
|
||||
|
||||
// construct Splat(Slice) matrices
|
||||
splat_idx.resize(npixels);
|
||||
for (int y = 0; y < rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < cols; ++x)
|
||||
{
|
||||
long long coord[5];
|
||||
coord[0] = int(x / sigma_spatial);
|
||||
coord[1] = int(y / sigma_spatial);
|
||||
coord[2] = int(pref[0] / sigma_luma);
|
||||
coord[3] = int(pref[1] / sigma_chroma);
|
||||
coord[4] = int(pref[2] / sigma_chroma);
|
||||
|
||||
// convert the coordinate to a hash value
|
||||
long long hash_coord = 0;
|
||||
for (int i = 0; i < 5; ++i)
|
||||
hash_coord += coord[i] * hash_vec[i];
|
||||
|
||||
// pixels whom are alike will have the same hash value.
|
||||
// We only want to keep a unique list of hash values, therefore make sure we only insert
|
||||
// unique hash values.
|
||||
mapId::iterator it = hashed_coords.find(hash_coord);
|
||||
if (it == hashed_coords.end())
|
||||
{
|
||||
hashed_coords.insert(std::pair<long long, int>(hash_coord, vert_idx));
|
||||
splat_idx[pix_idx] = vert_idx;
|
||||
++vert_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
splat_idx[pix_idx] = it->second;
|
||||
}
|
||||
|
||||
pref += 3; // skip 3 bytes (y u v)
|
||||
++pix_idx;
|
||||
}
|
||||
}
|
||||
nvertices = static_cast<int>(hashed_coords.size());
|
||||
|
||||
// construct Blur matrices
|
||||
Eigen::VectorXf ones_nvertices = Eigen::VectorXf::Ones(nvertices);
|
||||
diagonal(ones_nvertices,blurs);
|
||||
blurs *= 10;
|
||||
for(int offset = -1; offset <= 1;++offset)
|
||||
{
|
||||
if(offset == 0) continue;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> blur_temp(hashed_coords.size(), hashed_coords.size());
|
||||
blur_temp.reserve(Eigen::VectorXi::Constant(nvertices,6));
|
||||
long long offset_hash_coord = offset * hash_vec[i];
|
||||
for (mapId::iterator it = hashed_coords.begin(); it != hashed_coords.end(); ++it)
|
||||
{
|
||||
long long neighb_coord = it->first + offset_hash_coord;
|
||||
mapId::iterator it_neighb = hashed_coords.find(neighb_coord);
|
||||
if (it_neighb != hashed_coords.end())
|
||||
{
|
||||
blur_temp.insert(it->second,it_neighb->second) = 1.0f;
|
||||
blur_idx.push_back(std::pair<int,int>(it->second, it_neighb->second));
|
||||
}
|
||||
}
|
||||
blurs += blur_temp;
|
||||
}
|
||||
}
|
||||
blurs.finalize();
|
||||
|
||||
|
||||
//bistochastize
|
||||
int maxiter = 10;
|
||||
n = ones_nvertices;
|
||||
m = Eigen::VectorXf::Zero(nvertices);
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
m(splat_idx[i]) += 1.0f;
|
||||
}
|
||||
|
||||
Eigen::VectorXf bluredn(nvertices);
|
||||
|
||||
for (int i = 0; i < maxiter; i++)
|
||||
{
|
||||
Blur(n,bluredn);
|
||||
n = ((n.array()*m.array()).array()/bluredn.array()).array().sqrt();
|
||||
}
|
||||
Blur(n,bluredn);
|
||||
|
||||
m = n.array() * (bluredn).array();
|
||||
diagonal(m,Dm);
|
||||
diagonal(n,Dn);
|
||||
}
|
||||
}
|
||||
|
||||
void FastBilateralSolverFilterImpl::Splat(Eigen::VectorXf& input, Eigen::VectorXf& output)
|
||||
{
|
||||
output.setZero();
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
output(splat_idx[i]) += input(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FastBilateralSolverFilterImpl::Blur(Eigen::VectorXf& input, Eigen::VectorXf& output)
|
||||
{
|
||||
output.setZero();
|
||||
output = input * 10;
|
||||
for (int i = 0; i < int(blur_idx.size()); i++)
|
||||
{
|
||||
output(blur_idx[i].first) += input(blur_idx[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FastBilateralSolverFilterImpl::Slice(Eigen::VectorXf& input, Eigen::VectorXf& output)
|
||||
{
|
||||
output.setZero();
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
output(i) = input(splat_idx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FastBilateralSolverFilterImpl::solve(cv::Mat& target,
|
||||
cv::Mat& confidence,
|
||||
cv::Mat& output)
|
||||
{
|
||||
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> M(nvertices,nvertices);
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> A_data(nvertices,nvertices);
|
||||
Eigen::SparseMatrix<float, Eigen::ColMajor> A(nvertices,nvertices);
|
||||
Eigen::VectorXf b(nvertices);
|
||||
Eigen::VectorXf y(nvertices);
|
||||
Eigen::VectorXf y0(nvertices);
|
||||
Eigen::VectorXf y1(nvertices);
|
||||
Eigen::VectorXf w_splat(nvertices);
|
||||
|
||||
Eigen::VectorXf x(npixels);
|
||||
Eigen::VectorXf w(npixels);
|
||||
|
||||
if(target.depth() == CV_16S)
|
||||
{
|
||||
const int16_t *pft = reinterpret_cast<const int16_t*>(target.data);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
{
|
||||
x(i) = (cv::saturate_cast<float>(pft[i])+32768.0f)/65535.0f;
|
||||
}
|
||||
}
|
||||
else if(target.depth() == CV_16U)
|
||||
{
|
||||
const uint16_t *pft = reinterpret_cast<const uint16_t*>(target.data);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
{
|
||||
x(i) = cv::saturate_cast<float>(pft[i])/65535.0f;
|
||||
}
|
||||
}
|
||||
else if(target.depth() == CV_8U)
|
||||
{
|
||||
const uchar *pft = reinterpret_cast<const uchar*>(target.data);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
{
|
||||
x(i) = cv::saturate_cast<float>(pft[i])/255.0f;
|
||||
}
|
||||
}
|
||||
else if(target.depth() == CV_32F)
|
||||
{
|
||||
const float *pft = reinterpret_cast<const float*>(target.data);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
{
|
||||
x(i) = pft[i];
|
||||
}
|
||||
}
|
||||
|
||||
if(confidence.depth() == CV_8U)
|
||||
{
|
||||
const uchar *pfc = reinterpret_cast<const uchar*>(confidence.data);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
{
|
||||
w(i) = cv::saturate_cast<float>(pfc[i])/255.0f;
|
||||
}
|
||||
}
|
||||
else if(confidence.depth() == CV_32F)
|
||||
{
|
||||
const float *pfc = reinterpret_cast<const float*>(confidence.data);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
{
|
||||
w(i) = pfc[i];
|
||||
}
|
||||
}
|
||||
|
||||
//construct A
|
||||
Splat(w,w_splat);
|
||||
|
||||
diagonal(w_splat,A_data);
|
||||
A = bs_param.lam * (Dm - Dn * (blurs*Dn)) + A_data ;
|
||||
|
||||
//construct b
|
||||
b.setZero();
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
b(splat_idx[i]) += x(i) * w(i);
|
||||
}
|
||||
|
||||
//construct guess for y
|
||||
y0.setZero();
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
y0(splat_idx[i]) += x(i);
|
||||
}
|
||||
y1.setZero();
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
y1(splat_idx[i]) += 1.0f;
|
||||
}
|
||||
for (int i = 0; i < nvertices; i++)
|
||||
{
|
||||
y0(i) = y0(i)/y1(i);
|
||||
}
|
||||
|
||||
|
||||
// solve Ay = b
|
||||
Eigen::ConjugateGradient<Eigen::SparseMatrix<float>, Eigen::Lower|Eigen::Upper> cg;
|
||||
cg.compute(A);
|
||||
cg.setMaxIterations(bs_param.cg_maxiter);
|
||||
cg.setTolerance(bs_param.cg_tol);
|
||||
// y = cg.solve(b);
|
||||
y = cg.solveWithGuess(b,y0);
|
||||
std::cout << "#iterations: " << cg.iterations() << std::endl;
|
||||
std::cout << "estimated error: " << cg.error() << std::endl;
|
||||
|
||||
//slice
|
||||
if(target.depth() == CV_16S)
|
||||
{
|
||||
int16_t *pftar = (int16_t*) output.data;
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
pftar[i] = cv::saturate_cast<short>(y(splat_idx[i]) * 65535.0f - 32768.0f);
|
||||
}
|
||||
}
|
||||
else if(target.depth() == CV_16U)
|
||||
{
|
||||
uint16_t *pftar = (uint16_t*) output.data;
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
pftar[i] = cv::saturate_cast<ushort>(y(splat_idx[i]) * 65535.0f);
|
||||
}
|
||||
}
|
||||
else if (target.depth() == CV_8U)
|
||||
{
|
||||
uchar *pftar = (uchar*) output.data;
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
pftar[i] = cv::saturate_cast<uchar>(y(splat_idx[i]) * 255.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float *pftar = (float*)(output.data);
|
||||
for (int i = 0; i < int(splat_idx.size()); i++)
|
||||
{
|
||||
pftar[i] = y(splat_idx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
Ptr<FastBilateralSolverFilter> createFastBilateralSolverFilter(InputArray guide, double sigma_spatial, double sigma_luma, double sigma_chroma, double lambda, int num_iter, double max_tol)
|
||||
{
|
||||
return Ptr<FastBilateralSolverFilter>(FastBilateralSolverFilterImpl::create(guide, sigma_spatial, sigma_luma, sigma_chroma, lambda, num_iter, max_tol));
|
||||
}
|
||||
|
||||
void fastBilateralSolverFilter(InputArray guide, InputArray src, InputArray confidence, OutputArray dst, double sigma_spatial, double sigma_luma, double sigma_chroma, double lambda, int num_iter, double max_tol)
|
||||
{
|
||||
Ptr<FastBilateralSolverFilter> fbs = createFastBilateralSolverFilter(guide, sigma_spatial, sigma_luma, sigma_chroma, lambda, num_iter, max_tol);
|
||||
fbs->filter(src, confidence, dst);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
Ptr<FastBilateralSolverFilter> createFastBilateralSolverFilter(InputArray, double, double, double, double, int, double)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "createFastBilateralSolverFilter : needs to be compiled with EIGEN");
|
||||
}
|
||||
|
||||
void fastBilateralSolverFilter(InputArray, InputArray, InputArray, OutputArray, double, double, double, double, int, double)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "fastBilateralSolverFilter : needs to be compiled with EIGEN");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_EIGEN
|
||||
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
using std::vector;
|
||||
|
||||
typedef float WorkType;
|
||||
typedef Vec<WorkType, 1> WorkVec;
|
||||
typedef WorkType (*get_weight_op)(WorkType*, unsigned char*,unsigned char*);
|
||||
|
||||
inline WorkType get_weight_1channel(WorkType* LUT, unsigned char* p1,unsigned char* p2)
|
||||
{
|
||||
return LUT[ (p1[0]-p2[0])*(p1[0]-p2[0]) ];
|
||||
}
|
||||
inline WorkType get_weight_3channel(WorkType* LUT, unsigned char* p1,unsigned char* p2)
|
||||
{
|
||||
return LUT[ (p1[0]-p2[0])*(p1[0]-p2[0])+
|
||||
(p1[1]-p2[1])*(p1[1]-p2[1])+
|
||||
(p1[2]-p2[2])*(p1[2]-p2[2]) ];
|
||||
}
|
||||
|
||||
class FastGlobalSmootherFilterImpl : public FastGlobalSmootherFilter
|
||||
{
|
||||
public:
|
||||
static Ptr<FastGlobalSmootherFilterImpl> create(InputArray guide, double lambda, double sigma_color, int num_iter,double lambda_attenuation);
|
||||
void filter(InputArray src, OutputArray dst) CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
int w,h;
|
||||
int num_stripes;
|
||||
float sigmaColor,lambda;
|
||||
float lambda_attenuation;
|
||||
int num_iter;
|
||||
Mat weights_LUT;
|
||||
Mat Chor, Cvert;
|
||||
Mat interD;
|
||||
void init(InputArray guide,double _lambda,double _sigmaColor,int _num_iter,double _lambda_attenuation);
|
||||
void horizontalPass(Mat& cur);
|
||||
void verticalPass(Mat& cur);
|
||||
protected:
|
||||
struct HorizontalPass_ParBody : public ParallelLoopBody
|
||||
{
|
||||
FastGlobalSmootherFilterImpl* fgs;
|
||||
Mat* cur;
|
||||
int nstripes, stripe_sz;
|
||||
int h;
|
||||
|
||||
HorizontalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _h);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
inline void process_4row_block(Mat* cur,int i);
|
||||
inline void process_row(Mat* cur,int i);
|
||||
|
||||
struct VerticalPass_ParBody : public ParallelLoopBody
|
||||
{
|
||||
FastGlobalSmootherFilterImpl* fgs;
|
||||
Mat* cur;
|
||||
int nstripes, stripe_sz;
|
||||
int w;
|
||||
|
||||
VerticalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _w);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
template<get_weight_op get_weight, const int num_ch>
|
||||
struct ComputeHorizontalWeights_ParBody : public ParallelLoopBody
|
||||
{
|
||||
FastGlobalSmootherFilterImpl* fgs;
|
||||
Mat* guide;
|
||||
int nstripes, stripe_sz;
|
||||
int h;
|
||||
|
||||
ComputeHorizontalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _h);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
template<get_weight_op get_weight, const int num_ch>
|
||||
struct ComputeVerticalWeights_ParBody : public ParallelLoopBody
|
||||
{
|
||||
FastGlobalSmootherFilterImpl* fgs;
|
||||
Mat* guide;
|
||||
int nstripes, stripe_sz;
|
||||
int w;
|
||||
|
||||
ComputeVerticalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _w);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeLUT_ParBody : public ParallelLoopBody
|
||||
{
|
||||
FastGlobalSmootherFilterImpl* fgs;
|
||||
WorkType* LUT;
|
||||
int nstripes, stripe_sz;
|
||||
int sz;
|
||||
|
||||
ComputeLUT_ParBody(FastGlobalSmootherFilterImpl &_fgs, WorkType* _LUT, int _nstripes, int _sz);
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
void FastGlobalSmootherFilterImpl::init(InputArray guide,double _lambda,double _sigmaColor,int _num_iter,double _lambda_attenuation)
|
||||
{
|
||||
CV_Assert( !guide.empty() && _lambda >= 0 && _sigmaColor >= 0 && _num_iter >=1 );
|
||||
CV_Assert( guide.depth() == CV_8U && (guide.channels() == 1 || guide.channels() == 3) );
|
||||
sigmaColor = (float)_sigmaColor;
|
||||
lambda = (float)_lambda;
|
||||
lambda_attenuation = (float)_lambda_attenuation;
|
||||
num_iter = _num_iter;
|
||||
num_stripes = getNumThreads();
|
||||
int num_levels = 3*256*256;
|
||||
weights_LUT.create(1,num_levels,traits::Type<WorkVec>::value);
|
||||
|
||||
WorkType* LUT = (WorkType*)weights_LUT.ptr(0);
|
||||
parallel_for_(Range(0,num_stripes),ComputeLUT_ParBody(*this,LUT,num_stripes,num_levels));
|
||||
|
||||
w = guide.cols();
|
||||
h = guide.rows();
|
||||
Chor. create(h,w,traits::Type<WorkVec>::value);
|
||||
Cvert. create(h,w,traits::Type<WorkVec>::value);
|
||||
interD.create(h,w,traits::Type<WorkVec>::value);
|
||||
Mat guideMat = guide.getMat();
|
||||
|
||||
if(guide.channels() == 1)
|
||||
{
|
||||
parallel_for_(Range(0,num_stripes),ComputeHorizontalWeights_ParBody<get_weight_1channel,1>(*this,guideMat,num_stripes,h));
|
||||
parallel_for_(Range(0,num_stripes),ComputeVerticalWeights_ParBody <get_weight_1channel,1>(*this,guideMat,num_stripes,w));
|
||||
}
|
||||
if(guide.channels() == 3)
|
||||
{
|
||||
parallel_for_(Range(0,num_stripes),ComputeHorizontalWeights_ParBody<get_weight_3channel,3>(*this,guideMat,num_stripes,h));
|
||||
parallel_for_(Range(0,num_stripes),ComputeVerticalWeights_ParBody <get_weight_3channel,3>(*this,guideMat,num_stripes,w));
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<FastGlobalSmootherFilterImpl> FastGlobalSmootherFilterImpl::create(InputArray guide, double lambda, double sigma_color, int num_iter, double lambda_attenuation)
|
||||
{
|
||||
FastGlobalSmootherFilterImpl *fgs = new FastGlobalSmootherFilterImpl();
|
||||
fgs->init(guide,lambda,sigma_color,num_iter,lambda_attenuation);
|
||||
return Ptr<FastGlobalSmootherFilterImpl>(fgs);
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::filter(InputArray src, OutputArray dst)
|
||||
{
|
||||
CV_Assert(!src.empty() && (src.depth() == CV_8U || src.depth() == CV_16S || src.depth() == CV_32F) && src.channels()<=4);
|
||||
if (src.rows() != h || src.cols() != w)
|
||||
{
|
||||
CV_Error(Error::StsBadSize, "Size of the filtered image must be equal to the size of the guide image");
|
||||
return;
|
||||
}
|
||||
|
||||
vector<Mat> src_channels;
|
||||
vector<Mat> dst_channels;
|
||||
if(src.channels()==1)
|
||||
src_channels.push_back(src.getMat());
|
||||
else
|
||||
split(src,src_channels);
|
||||
|
||||
float lambda_ref = lambda;
|
||||
|
||||
for(int i=0;i<src.channels();i++)
|
||||
{
|
||||
lambda = lambda_ref;
|
||||
Mat cur_res = src_channels[i].clone();
|
||||
if(src.depth()!=traits::Type<WorkVec>::value)
|
||||
cur_res.convertTo(cur_res,traits::Type<WorkVec>::value);
|
||||
|
||||
for(int n=0;n<num_iter;n++)
|
||||
{
|
||||
horizontalPass(cur_res);
|
||||
verticalPass(cur_res);
|
||||
lambda*=lambda_attenuation;
|
||||
}
|
||||
|
||||
Mat dstMat;
|
||||
if(src.depth()!=traits::Type<WorkVec>::value)
|
||||
cur_res.convertTo(dstMat,src.depth());
|
||||
else
|
||||
dstMat = cur_res;
|
||||
|
||||
dst_channels.push_back(dstMat);
|
||||
}
|
||||
|
||||
lambda = lambda_ref;
|
||||
|
||||
dst.create(src.size(),src.type());
|
||||
if(src.channels()==1)
|
||||
{
|
||||
Mat& dstMat = dst.getMatRef();
|
||||
dstMat = dst_channels[0];
|
||||
}
|
||||
else
|
||||
merge(dst_channels,dst);
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::horizontalPass(Mat& cur)
|
||||
{
|
||||
parallel_for_(Range(0,num_stripes),HorizontalPass_ParBody(*this,cur,num_stripes,h));
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::verticalPass(Mat& cur)
|
||||
{
|
||||
parallel_for_(Range(0,num_stripes),VerticalPass_ParBody(*this,cur,num_stripes,w));
|
||||
}
|
||||
|
||||
FastGlobalSmootherFilterImpl::HorizontalPass_ParBody::HorizontalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _h):
|
||||
fgs(&_fgs),cur(&_cur), nstripes(_nstripes), h(_h)
|
||||
{
|
||||
stripe_sz = (int)ceil(h/(double)nstripes);
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::process_4row_block(Mat* cur,int i)
|
||||
{
|
||||
WorkType denom,denom_next,denom_next2,denom_next3;
|
||||
|
||||
WorkType *Chor_row = (WorkType*)Chor.ptr (i);
|
||||
WorkType *interD_row = (WorkType*)interD.ptr(i);
|
||||
WorkType *cur_row = (WorkType*)cur->ptr (i);
|
||||
|
||||
WorkType *Chor_row_next = (WorkType*)Chor.ptr (i+1);
|
||||
WorkType *interD_row_next = (WorkType*)interD.ptr(i+1);
|
||||
WorkType *cur_row_next = (WorkType*)cur->ptr (i+1);
|
||||
|
||||
WorkType *Chor_row_next2 = (WorkType*)Chor.ptr (i+2);
|
||||
WorkType *interD_row_next2 = (WorkType*)interD.ptr(i+2);
|
||||
WorkType *cur_row_next2 = (WorkType*)cur->ptr (i+2);
|
||||
|
||||
WorkType *Chor_row_next3 = (WorkType*)Chor.ptr (i+3);
|
||||
WorkType *interD_row_next3 = (WorkType*)interD.ptr(i+3);
|
||||
WorkType *cur_row_next3 = (WorkType*)cur->ptr (i+3);
|
||||
|
||||
float coef_cur, coef_prev;
|
||||
float coef_cur_row_next, coef_prev_row_next;
|
||||
float coef_cur_row_next2,coef_prev_row_next2;
|
||||
float coef_cur_row_next3,coef_prev_row_next3;
|
||||
|
||||
//forward pass:
|
||||
coef_prev = lambda*Chor_row[0];
|
||||
coef_prev_row_next = lambda*Chor_row_next[0];
|
||||
coef_prev_row_next2 = lambda*Chor_row_next2[0];
|
||||
coef_prev_row_next3 = lambda*Chor_row_next3[0];
|
||||
|
||||
interD_row[0] = coef_prev /(1-coef_prev);
|
||||
interD_row_next[0] = coef_prev_row_next /(1-coef_prev_row_next);
|
||||
interD_row_next2[0] = coef_prev_row_next2/(1-coef_prev_row_next2);
|
||||
interD_row_next3[0] = coef_prev_row_next3/(1-coef_prev_row_next3);
|
||||
|
||||
cur_row[0] = cur_row[0] /(1-coef_prev);
|
||||
cur_row_next[0] = cur_row_next[0] /(1-coef_prev_row_next);
|
||||
cur_row_next2[0] = cur_row_next2[0]/(1-coef_prev_row_next2);
|
||||
cur_row_next3[0] = cur_row_next3[0]/(1-coef_prev_row_next3);
|
||||
int j=1;
|
||||
|
||||
#if CV_SIMD128
|
||||
{
|
||||
v_float32x4 coef_prev_reg(coef_prev,coef_prev_row_next,coef_prev_row_next2,coef_prev_row_next3);
|
||||
v_float32x4 interD_prev_reg(interD_row[0],interD_row_next[0],interD_row_next2[0],interD_row_next3[0]);
|
||||
v_float32x4 cur_prev_reg(cur_row[0],cur_row_next[0],cur_row_next2[0],cur_row_next3[0]);
|
||||
v_float32x4 lambda_reg(lambda,lambda,lambda,lambda);
|
||||
v_float32x4 one_reg(1.0f,1.0f,1.0f,1.0f);
|
||||
|
||||
v_float32x4 a0,a1,a2,a3;
|
||||
v_float32x4 b0,b1,b2,b3;
|
||||
v_float32x4 aux0,aux1,aux2,aux3;
|
||||
|
||||
#define PROC4(Chor_in,cur_in,coef_prev_in,interD_prev_in,cur_prev_in,interD_out,cur_out,coef_cur_out)\
|
||||
coef_cur_out = v_mul(lambda_reg, Chor_in);\
|
||||
aux0 = v_mul(interD_prev_in, coef_prev_in);\
|
||||
aux1 = v_add(coef_cur_out, coef_prev_in);\
|
||||
aux1 = v_sub(one_reg, aux1);\
|
||||
aux0 = v_sub(aux1, aux0);\
|
||||
interD_out = v_div(coef_cur_out, aux0);\
|
||||
aux1 = v_mul(cur_prev_in, coef_prev_in);\
|
||||
aux1 = v_sub(cur_in, aux1);\
|
||||
cur_out = v_div(aux1, aux0);
|
||||
|
||||
for(;j<w-3;j+=4)
|
||||
{
|
||||
// processing a 4x4 block:
|
||||
|
||||
aux0 = v_load(Chor_row+j);
|
||||
aux1 = v_load(Chor_row_next+j);
|
||||
aux2 = v_load(Chor_row_next2+j);
|
||||
aux3 = v_load(Chor_row_next3+j);
|
||||
v_transpose4x4(aux0,aux1,aux2,aux3,a0,a1,a2,a3);
|
||||
|
||||
aux0 = v_load(cur_row+j);
|
||||
aux1 = v_load(cur_row_next+j);
|
||||
aux2 = v_load(cur_row_next2+j);
|
||||
aux3 = v_load(cur_row_next3+j);
|
||||
v_transpose4x4(aux0,aux1,aux2,aux3,b0,b1,b2,b3);
|
||||
|
||||
PROC4(a0,b0,coef_prev_reg,interD_prev_reg,cur_prev_reg,a0,b0,aux2);
|
||||
PROC4(a1,b1,aux2,a0,b0,a1,b1,aux3);
|
||||
PROC4(a2,b2,aux3,a1,b1,a2,b2,aux2);
|
||||
PROC4(a3,b3,aux2,a2,b2,a3,b3,aux3);
|
||||
|
||||
interD_prev_reg = a3;
|
||||
cur_prev_reg = b3;
|
||||
coef_prev_reg = aux3;
|
||||
|
||||
v_transpose4x4(a0,a1,a2,a3,aux0,aux1,aux2,aux3);
|
||||
v_store(interD_row+j,aux0);
|
||||
v_store(interD_row_next+j,aux1);
|
||||
v_store(interD_row_next2+j,aux2);
|
||||
v_store(interD_row_next3+j,aux3);
|
||||
|
||||
v_transpose4x4(b0,b1,b2,b3,aux0,aux1,aux2,aux3);
|
||||
v_store(cur_row+j,aux0);
|
||||
v_store(cur_row_next+j,aux1);
|
||||
v_store(cur_row_next2+j,aux2);
|
||||
v_store(cur_row_next3+j,aux3);
|
||||
}
|
||||
#undef PROC4
|
||||
}
|
||||
#endif
|
||||
|
||||
for(;j<w;j++)
|
||||
{
|
||||
coef_prev = lambda*Chor_row[j-1];
|
||||
coef_prev_row_next = lambda*Chor_row_next[j-1];
|
||||
coef_prev_row_next2 = lambda*Chor_row_next2[j-1];
|
||||
coef_prev_row_next3 = lambda*Chor_row_next3[j-1];
|
||||
|
||||
coef_cur = lambda*Chor_row[j];
|
||||
coef_cur_row_next = lambda*Chor_row_next[j];
|
||||
coef_cur_row_next2 = lambda*Chor_row_next2[j];
|
||||
coef_cur_row_next3 = lambda*Chor_row_next3[j];
|
||||
|
||||
denom = (1-coef_prev -coef_cur) -interD_row[j-1] *coef_prev;
|
||||
denom_next = (1-coef_prev_row_next -coef_cur_row_next) -interD_row_next[j-1] *coef_prev_row_next;
|
||||
denom_next2 = (1-coef_prev_row_next2-coef_cur_row_next2)-interD_row_next2[j-1]*coef_prev_row_next2;
|
||||
denom_next3 = (1-coef_prev_row_next3-coef_cur_row_next3)-interD_row_next3[j-1]*coef_prev_row_next3;
|
||||
|
||||
interD_row[j] = coef_cur /denom;
|
||||
interD_row_next[j] = coef_cur_row_next /denom_next;
|
||||
interD_row_next2[j] = coef_cur_row_next2/denom_next2;
|
||||
interD_row_next3[j] = coef_cur_row_next3/denom_next3;
|
||||
|
||||
cur_row[j] = (cur_row[j] -cur_row[j-1] *coef_prev) /denom;
|
||||
cur_row_next[j] = (cur_row_next[j] -cur_row_next[j-1] *coef_prev_row_next) /denom_next;
|
||||
cur_row_next2[j] = (cur_row_next2[j]-cur_row_next2[j-1]*coef_prev_row_next2)/denom_next2;
|
||||
cur_row_next3[j] = (cur_row_next3[j]-cur_row_next3[j-1]*coef_prev_row_next3)/denom_next3;
|
||||
}
|
||||
//backward pass:
|
||||
j = w-2;
|
||||
|
||||
#if CV_SIMD128
|
||||
{
|
||||
v_float32x4 cur_next_reg(cur_row[w-1],cur_row_next[w-1],cur_row_next2[w-1],cur_row_next3[w-1]);
|
||||
v_float32x4 a0,a1,a2,a3;
|
||||
v_float32x4 b0,b1,b2,b3;
|
||||
v_float32x4 aux0,aux1,aux2,aux3;
|
||||
for(j-=3;j>=0;j-=4)
|
||||
{
|
||||
//process 4x4 block:
|
||||
|
||||
aux0 = v_load(interD_row+j);
|
||||
aux1 = v_load(interD_row_next+j);
|
||||
aux2 = v_load(interD_row_next2+j);
|
||||
aux3 = v_load(interD_row_next3+j);
|
||||
v_transpose4x4(aux0,aux1,aux2,aux3,a0,a1,a2,a3);
|
||||
|
||||
aux0 = v_load(cur_row+j);
|
||||
aux1 = v_load(cur_row_next+j);
|
||||
aux2 = v_load(cur_row_next2+j);
|
||||
aux3 = v_load(cur_row_next3+j);
|
||||
v_transpose4x4(aux0,aux1,aux2,aux3,b0,b1,b2,b3);
|
||||
|
||||
aux0 = v_mul(a3, cur_next_reg);
|
||||
b3 = v_sub(b3, aux0);
|
||||
aux0 = v_mul(a2, b3);
|
||||
b2 = v_sub(b2, aux0);
|
||||
aux0 = v_mul(a1, b2);
|
||||
b1 = v_sub(b1, aux0);
|
||||
aux0 = v_mul(a0, b1);
|
||||
b0 = v_sub(b0, aux0);
|
||||
|
||||
cur_next_reg = b0;
|
||||
|
||||
v_transpose4x4(b0,b1,b2,b3,aux0,aux1,aux2,aux3);
|
||||
v_store(cur_row+j,aux0);
|
||||
v_store(cur_row_next+j,aux1);
|
||||
v_store(cur_row_next2+j,aux2);
|
||||
v_store(cur_row_next3+j,aux3);
|
||||
}
|
||||
j+=3;
|
||||
}
|
||||
#endif
|
||||
|
||||
for(;j>=0;j--)
|
||||
{
|
||||
cur_row[j] = cur_row[j] -interD_row[j] *cur_row[j+1];
|
||||
cur_row_next[j] = cur_row_next[j] -interD_row_next[j] *cur_row_next[j+1];
|
||||
cur_row_next2[j] = cur_row_next2[j]-interD_row_next2[j]*cur_row_next2[j+1];
|
||||
cur_row_next3[j] = cur_row_next3[j]-interD_row_next3[j]*cur_row_next3[j+1];
|
||||
}
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::process_row(Mat* cur,int i)
|
||||
{
|
||||
WorkType denom;
|
||||
WorkType *Chor_row = (WorkType*)Chor.ptr(i);
|
||||
WorkType *interD_row = (WorkType*)interD.ptr(i);
|
||||
WorkType *cur_row = (WorkType*)cur->ptr(i);
|
||||
|
||||
float coef_cur,coef_prev;
|
||||
|
||||
//forward pass:
|
||||
coef_prev = lambda*Chor_row[0];
|
||||
interD_row[0] = coef_prev/(1-coef_prev);
|
||||
cur_row[0] = cur_row[0]/(1-coef_prev);
|
||||
for(int j=1;j<w;j++)
|
||||
{
|
||||
coef_cur = lambda*Chor_row[j];
|
||||
denom = (1-coef_prev-coef_cur)-interD_row[j-1]*coef_prev;
|
||||
interD_row[j] = coef_cur/denom;
|
||||
cur_row[j] = (cur_row[j]-cur_row[j-1]*coef_prev)/denom;
|
||||
coef_prev = coef_cur;
|
||||
}
|
||||
|
||||
//backward pass:
|
||||
for(int j=w-2;j>=0;j--)
|
||||
cur_row[j] = cur_row[j]-interD_row[j]*cur_row[j+1];
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::HorizontalPass_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int start = std::min(range.start * stripe_sz, h);
|
||||
int end = std::min(range.end * stripe_sz, h);
|
||||
|
||||
int i=start;
|
||||
for(;i<end-3;i+=4)
|
||||
fgs->process_4row_block(cur,i);
|
||||
for(;i<end;i++)
|
||||
fgs->process_row(cur,i);
|
||||
}
|
||||
|
||||
FastGlobalSmootherFilterImpl::VerticalPass_ParBody::VerticalPass_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _cur, int _nstripes, int _w):
|
||||
fgs(&_fgs),cur(&_cur), nstripes(_nstripes), w(_w)
|
||||
{
|
||||
stripe_sz = (int)ceil(w/(double)nstripes);
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::VerticalPass_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int start = std::min(range.start * stripe_sz, w);
|
||||
int end = std::min(range.end * stripe_sz, w);
|
||||
|
||||
WorkType denom;
|
||||
WorkType *Cvert_row, *Cvert_row_prev;
|
||||
WorkType *interD_row, *interD_row_prev, *cur_row, *cur_row_prev, *cur_row_next;
|
||||
|
||||
float coef_cur,coef_prev;
|
||||
|
||||
Cvert_row = (WorkType*)fgs->Cvert.ptr(0);
|
||||
interD_row = (WorkType*)fgs->interD.ptr(0);
|
||||
cur_row = (WorkType*)cur->ptr(0);
|
||||
//forward pass:
|
||||
for(int j=start;j<end;j++)
|
||||
{
|
||||
coef_cur = fgs->lambda*Cvert_row[j];
|
||||
interD_row[j] = coef_cur/(1-coef_cur);
|
||||
cur_row[j] = cur_row[j]/(1-coef_cur);
|
||||
}
|
||||
for(int i=1;i<fgs->h;i++)
|
||||
{
|
||||
Cvert_row = (WorkType*)fgs->Cvert.ptr(i);
|
||||
Cvert_row_prev = (WorkType*)fgs->Cvert.ptr(i-1);
|
||||
interD_row = (WorkType*)fgs->interD.ptr(i);
|
||||
interD_row_prev = (WorkType*)fgs->interD.ptr(i-1);
|
||||
cur_row = (WorkType*)cur->ptr(i);
|
||||
cur_row_prev = (WorkType*)cur->ptr(i-1);
|
||||
int j = start;
|
||||
|
||||
#if CV_SIMD128
|
||||
v_float32x4 a,b,c,d,coef_cur_reg,coef_prev_reg;
|
||||
v_float32x4 one_reg(1.0f,1.0f,1.0f,1.0f);
|
||||
v_float32x4 lambda_reg(fgs->lambda,fgs->lambda,fgs->lambda,fgs->lambda);
|
||||
int sz4 = 4*((end-start)/4);
|
||||
int end4 = start+sz4;
|
||||
for(;j<end4;j+=4)
|
||||
{
|
||||
a = v_load(Cvert_row_prev+j);
|
||||
b = v_load(Cvert_row+j);
|
||||
coef_prev_reg = v_mul(lambda_reg, a);
|
||||
coef_cur_reg = v_mul(lambda_reg, b);
|
||||
|
||||
a = v_load(interD_row_prev+j);
|
||||
a = v_mul(a, coef_prev_reg);
|
||||
|
||||
b = v_add(coef_prev_reg, coef_cur_reg);
|
||||
b = v_add(b, a);
|
||||
a = v_sub(one_reg, b); //computed denom
|
||||
|
||||
b = v_div(coef_cur_reg, a); //computed interD_row
|
||||
|
||||
c = v_load(cur_row_prev+j);
|
||||
c = v_mul(c, coef_prev_reg);
|
||||
|
||||
d = v_load(cur_row+j);
|
||||
d = v_sub(d, c);
|
||||
d = v_div(d, a); //computed cur_row
|
||||
|
||||
v_store(interD_row+j,b);
|
||||
v_store(cur_row+j,d);
|
||||
}
|
||||
#endif
|
||||
for(;j<end;j++)
|
||||
{
|
||||
coef_prev = fgs->lambda*Cvert_row_prev[j];
|
||||
coef_cur = fgs->lambda*Cvert_row[j];
|
||||
denom = (1-coef_prev-coef_cur)-interD_row_prev[j]*coef_prev;
|
||||
interD_row[j] = coef_cur/denom;
|
||||
cur_row[j] = (cur_row[j]-cur_row_prev[j]*coef_prev)/denom;
|
||||
}
|
||||
}
|
||||
|
||||
//backward pass:
|
||||
for(int i=fgs->h-2;i>=0;i--)
|
||||
{
|
||||
interD_row = (WorkType*)fgs->interD.ptr(i);
|
||||
cur_row = (WorkType*)cur->ptr(i);
|
||||
cur_row_next = (WorkType*)cur->ptr(i+1);
|
||||
int j = start;
|
||||
#if CV_SIMD128
|
||||
v_float32x4 a,b;
|
||||
int sz4 = 4*((end-start)/4);
|
||||
int end4 = start+sz4;
|
||||
for(;j<end4;j+=4)
|
||||
{
|
||||
a = v_load(interD_row+j);
|
||||
b = v_load(cur_row_next+j);
|
||||
b = v_mul(a, b);
|
||||
|
||||
a = v_load(cur_row+j);
|
||||
b = v_sub(a, b);
|
||||
v_store(cur_row+j,b);
|
||||
}
|
||||
#endif
|
||||
for(;j<end;j++)
|
||||
cur_row[j] = cur_row[j]-interD_row[j]*cur_row_next[j];
|
||||
}
|
||||
}
|
||||
|
||||
template<get_weight_op get_weight, const int num_ch>
|
||||
FastGlobalSmootherFilterImpl::ComputeHorizontalWeights_ParBody<get_weight,num_ch>::ComputeHorizontalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _h):
|
||||
fgs(&_fgs),guide(&_guide), nstripes(_nstripes), h(_h)
|
||||
{
|
||||
stripe_sz = (int)ceil(h/(double)nstripes);
|
||||
}
|
||||
|
||||
template<get_weight_op get_weight, const int num_ch>
|
||||
void FastGlobalSmootherFilterImpl::ComputeHorizontalWeights_ParBody<get_weight,num_ch>::operator()(const Range& range) const
|
||||
{
|
||||
int start = std::min(range.start * stripe_sz, h);
|
||||
int end = std::min(range.end * stripe_sz, h);
|
||||
|
||||
WorkType* LUT = (WorkType*)fgs->weights_LUT.ptr(0);
|
||||
unsigned char *row;
|
||||
WorkType *Chor_row;
|
||||
|
||||
for(int i=start;i<end;i++)
|
||||
{
|
||||
row = guide->ptr(i);
|
||||
Chor_row = (WorkType*)fgs->Chor.ptr(i);
|
||||
Chor_row[0] = get_weight(LUT,row,row+num_ch);
|
||||
row+=num_ch;
|
||||
for(int j=1;j<fgs->w-1;j++)
|
||||
{
|
||||
Chor_row[j] = get_weight(LUT,row,row+num_ch);
|
||||
row+=num_ch;
|
||||
}
|
||||
Chor_row[fgs->w-1]=0;
|
||||
}
|
||||
}
|
||||
|
||||
template<get_weight_op get_weight, const int num_ch>
|
||||
FastGlobalSmootherFilterImpl::ComputeVerticalWeights_ParBody<get_weight,num_ch>::ComputeVerticalWeights_ParBody(FastGlobalSmootherFilterImpl &_fgs, Mat& _guide, int _nstripes, int _w):
|
||||
fgs(&_fgs),guide(&_guide), nstripes(_nstripes), w(_w)
|
||||
{
|
||||
stripe_sz = (int)ceil(w/(double)nstripes);
|
||||
}
|
||||
|
||||
template<get_weight_op get_weight, const int num_ch>
|
||||
void FastGlobalSmootherFilterImpl::ComputeVerticalWeights_ParBody<get_weight,num_ch>::operator()(const Range& range) const
|
||||
{
|
||||
int start = std::min(range.start * stripe_sz, w);
|
||||
int end = std::min(range.end * stripe_sz, w);
|
||||
|
||||
WorkType* LUT = (WorkType*)fgs->weights_LUT.ptr(0);
|
||||
unsigned char *row,*row_next;
|
||||
WorkType *Cvert_row;
|
||||
|
||||
Cvert_row = (WorkType*)fgs->Cvert.ptr(0);
|
||||
row = guide->ptr(0)+start*num_ch;
|
||||
row_next = guide->ptr(1)+start*num_ch;
|
||||
for(int j=start;j<end;j++)
|
||||
{
|
||||
Cvert_row[j] = get_weight(LUT,row,row_next);
|
||||
row+=num_ch;
|
||||
row_next+=num_ch;
|
||||
}
|
||||
|
||||
for(int i=1;i<fgs->h-1;i++)
|
||||
{
|
||||
row = guide->ptr(i)+start*num_ch;
|
||||
row_next = guide->ptr(i+1)+start*num_ch;
|
||||
Cvert_row = (WorkType*)fgs->Cvert.ptr(i);
|
||||
for(int j=start;j<end;j++)
|
||||
{
|
||||
Cvert_row[j] = get_weight(LUT,row,row_next);
|
||||
row+=num_ch;
|
||||
row_next+=num_ch;
|
||||
}
|
||||
}
|
||||
|
||||
Cvert_row = (WorkType*)fgs->Cvert.ptr(fgs->h-1);
|
||||
for(int j=start;j<end;j++)
|
||||
Cvert_row[j] = 0;
|
||||
}
|
||||
|
||||
FastGlobalSmootherFilterImpl::ComputeLUT_ParBody::ComputeLUT_ParBody(FastGlobalSmootherFilterImpl &_fgs, WorkType *_LUT, int _nstripes, int _sz):
|
||||
fgs(&_fgs), LUT(_LUT), nstripes(_nstripes), sz(_sz)
|
||||
{
|
||||
stripe_sz = (int)ceil(sz/(double)nstripes);
|
||||
}
|
||||
|
||||
void FastGlobalSmootherFilterImpl::ComputeLUT_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int start = std::min(range.start * stripe_sz, sz);
|
||||
int end = std::min(range.end * stripe_sz, sz);
|
||||
for(int i=start;i<end;i++)
|
||||
LUT[i] = (WorkType)(-cv::exp(-sqrt((float)i)/fgs->sigmaColor));
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Ptr<FastGlobalSmootherFilter> createFastGlobalSmootherFilter(InputArray guide, double lambda, double sigma_color, double lambda_attenuation, int num_iter)
|
||||
{
|
||||
return Ptr<FastGlobalSmootherFilter>(FastGlobalSmootherFilterImpl::create(guide, lambda, sigma_color, num_iter, lambda_attenuation));
|
||||
}
|
||||
|
||||
void fastGlobalSmootherFilter(InputArray guide, InputArray src, OutputArray dst, double lambda, double sigma_color, double lambda_attenuation, int num_iter)
|
||||
{
|
||||
Ptr<FastGlobalSmootherFilter> fgs = createFastGlobalSmootherFilter(guide, lambda, sigma_color, lambda_attenuation, num_iter);
|
||||
fgs->filter(src, dst);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,330 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
If you use this code please cite this @cite BergerRaghunathan1998
|
||||
Coalescence in 2 dimensions: experiments on thin copolymer films and numerical simulations The European Physical Journal B - Condensed Matter and Complex Systems (Volume:2 , Issue: 1 ) 1998
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
|
||||
void ContourFitting::setCtrSize(int n)
|
||||
{
|
||||
CV_Assert(n>0);
|
||||
ctrSize = n;
|
||||
}
|
||||
|
||||
void ContourFitting::setFDSize(int n)
|
||||
{
|
||||
CV_Assert(n>0);
|
||||
fdSize=n;
|
||||
}
|
||||
|
||||
void ContourFitting::frequencyInit()
|
||||
{
|
||||
int nbElt = ctrSize;
|
||||
frequence.resize(ctrSize);
|
||||
|
||||
for (int i = 0; i <= nbElt / 2; i++)
|
||||
frequence[i] = 2 * M_PI*(float)i / nbElt;
|
||||
for (int i = nbElt / 2 + 1; i<nbElt; i++)
|
||||
frequence[i] = 2 * M_PI*(float)(i - nbElt) / nbElt;
|
||||
}
|
||||
|
||||
void ContourFitting::fAlpha(double x, double &fn, double &df)
|
||||
{
|
||||
int nbElt = static_cast<int>(rho.size());
|
||||
double s1 = 0, s2 = 0, s3 = 0, s4 = 0;
|
||||
double ds1 = 0, ds2 = 0, ds3 = 0, ds4 = 0;
|
||||
|
||||
for (int n = 1; n <= fdSize; n++)
|
||||
{
|
||||
s1 += rho[n] * sin(psi[n] + frequence[n] * x) +
|
||||
rho[nbElt - n] * sin(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
s2 += frequence[n] * rho[n] * cos(psi[n] + frequence[n] * x) +
|
||||
frequence[nbElt - n] * rho[nbElt - n] * cos(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
s3 += rho[n] * cos(psi[n] + frequence[n] * x) +
|
||||
rho[nbElt - n] * cos(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
s4 += frequence[n] * rho[n] * sin(psi[n] + frequence[n] * x) +
|
||||
frequence[nbElt - n] * rho[nbElt - n] * sin(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
ds1 += frequence[n] * rho[n] * cos(psi[n] + frequence[n] * x) +
|
||||
frequence[nbElt - n] * rho[nbElt - n] * cos(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
ds2 += -frequence[n] * frequence[n] * rho[n] * sin(psi[n] + frequence[n] * x) -
|
||||
frequence[nbElt - n] * frequence[nbElt - n] * rho[nbElt - n] * sin(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
ds3 += -frequence[n] * rho[n] * sin(psi[n] + frequence[n] * x) -
|
||||
frequence[nbElt - n] * rho[nbElt - n] * sin(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
ds4 += frequence[n] * frequence[n] * rho[n] * cos(psi[n] + frequence[n] * x) +
|
||||
frequence[nbElt - n] * frequence[nbElt - n] * rho[nbElt - n] * cos(psi[nbElt - n] + frequence[nbElt - n] * x);
|
||||
}
|
||||
fn = s1 * s2 - s3 *s4;
|
||||
df = ds1 * s2 + s1 * ds2 - ds3 * s4 - s3 * ds4;
|
||||
}
|
||||
|
||||
double ContourFitting::distance(std::complex<double> r, double alpha)
|
||||
{
|
||||
std::complex<double> j(0, 1);
|
||||
double d = 0;
|
||||
|
||||
for (int i = 1; i <= fdSize; i++)
|
||||
d += abs(a[i] - b[i] * r*exp(j*alpha*frequence[i])) + abs(a[a.size() - i] - b[a.size() - i] * r*exp(j*alpha*frequence[a.size() - i]));
|
||||
return d/fdSize/2;
|
||||
};
|
||||
|
||||
double ContourFitting::newtonRaphson(double x1, double x2)
|
||||
{
|
||||
double f1,df1;
|
||||
fAlpha(x1,f1,df1);
|
||||
if (f1 < 0)
|
||||
{
|
||||
x1=x2;
|
||||
fAlpha(x1, f1, df1);
|
||||
}
|
||||
CV_Assert(f1>=0);
|
||||
if (f1==0)
|
||||
return x1;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
x1 = x1 -f1/df1;
|
||||
fAlpha(x1, f1, df1);
|
||||
if (f1 == 0)
|
||||
return x1;
|
||||
}
|
||||
return x1;
|
||||
}
|
||||
|
||||
void ContourFitting::estimateTransformation(InputArray _src, InputArray _ref, OutputArray _alphaPhiST, double &distFin, bool fdContour)
|
||||
{
|
||||
estimateTransformation(_src, _ref, _alphaPhiST, &distFin, fdContour);
|
||||
}
|
||||
void ContourFitting::estimateTransformation(InputArray _src, InputArray _ref, OutputArray _alphaPhiST,double *distFin, bool fdContour)
|
||||
{
|
||||
if (!fdContour)
|
||||
CV_Assert( (_src.kind() == _InputArray::STD_VECTOR || _src.kind() == _InputArray::MAT) && (_ref.kind() == _InputArray::STD_VECTOR || _ref.kind() == _InputArray::MAT));
|
||||
else
|
||||
CV_Assert(fdContour && _src.kind() == _InputArray::MAT && _ref.kind() == _InputArray::MAT);
|
||||
CV_Assert(_src.channels() == 2 && _ref.channels() == 2);
|
||||
Mat fdCtr1,fdCtr2;
|
||||
if (!fdContour)
|
||||
{
|
||||
Mat newCtr1,newCtr2;
|
||||
contourSampling(_src, newCtr1, ctrSize);
|
||||
contourSampling(_ref, newCtr2, ctrSize);
|
||||
fourierDescriptor(newCtr1, fdCtr1);
|
||||
fourierDescriptor(newCtr2, fdCtr2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdCtr1=_src.getMat();
|
||||
fdCtr2= _ref.getMat();
|
||||
CV_Assert(fdCtr1.rows == fdCtr2.rows);
|
||||
}
|
||||
CV_Assert( fdSize<= ctrSize / 2 - 1);
|
||||
if (fdCtr1.type() != CV_64FC2)
|
||||
fdCtr1.convertTo(fdCtr1, CV_64F);
|
||||
if (fdCtr2.type() != CV_64FC2)
|
||||
fdCtr2.convertTo(fdCtr2, CV_64F);
|
||||
rho.resize(ctrSize);
|
||||
psi.resize(ctrSize);
|
||||
b.resize(ctrSize);
|
||||
a.resize(ctrSize);
|
||||
frequencyInit();
|
||||
double alphaMin, phiMin, sMin;
|
||||
long n, nbElt = ctrSize;
|
||||
double s1, s2, sign1, sign2, df, x1 = nbElt, x2 = nbElt, dx;
|
||||
double dist, distMin = 10000, alpha, s, phi;
|
||||
std::complex<double> j(0, 1), zz;
|
||||
|
||||
for (n = 0; n<nbElt; n++)
|
||||
{
|
||||
b[n] = std::complex<double>(fdCtr1.at<Vec2d>(n,0)[0], fdCtr1.at<Vec2d>(n, 0)[1]);
|
||||
a[n] = std::complex<double>(fdCtr2.at<Vec2d>(n, 0)[0], fdCtr2.at<Vec2d>(n, 0)[1]);
|
||||
zz = conj(a[n])*b[n];
|
||||
rho[n] = abs(zz);
|
||||
psi[n] = arg(zz);
|
||||
}
|
||||
|
||||
x1 = nbElt, x2 = nbElt;
|
||||
sMin = 1;
|
||||
alphaMin = 0;
|
||||
phiMin = arg(a[1] / b[1]);
|
||||
do
|
||||
{
|
||||
x2 = x1;
|
||||
fAlpha(x2, sign2, df);
|
||||
dx = 1;
|
||||
x1 = x2;
|
||||
do
|
||||
{
|
||||
x2 = x1;
|
||||
x1 -= dx;
|
||||
fAlpha(x1, sign1, df);
|
||||
}
|
||||
while ((sign1*sign2>0) && (x1>-nbElt));
|
||||
if (sign1*sign2<0)
|
||||
{
|
||||
alpha=newtonRaphson(x1,x2);
|
||||
s1 = 0;
|
||||
s2 = 0;
|
||||
for (n = 1; n<nbElt; n++)
|
||||
{
|
||||
s1 += rho[n] * sin(psi[n] + frequence[n] * alpha);
|
||||
s2 += rho[n] * cos(psi[n] + frequence[n] * alpha);
|
||||
}
|
||||
phi = -atan2(s1, s2);
|
||||
s1 = 0;
|
||||
s2 = 0;
|
||||
for (n = 1; n < nbElt; n++)
|
||||
{
|
||||
s1 += rho[n] * cos(psi[n] + frequence[n] * alpha + phi);
|
||||
s2 += abs(b[n] * conj(b[n]));
|
||||
}
|
||||
s = s1 / s2;
|
||||
zz = s*exp(j * phi);
|
||||
if (s>0)
|
||||
dist = distance(zz, alpha);
|
||||
else
|
||||
dist = 10000;
|
||||
if (dist<distMin)
|
||||
{
|
||||
distMin = dist;
|
||||
alphaMin = alpha;
|
||||
phiMin = phi;
|
||||
sMin = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
while ((x1>-nbElt));
|
||||
Mat x=(Mat_<double>(1,5)<<alphaMin/ nbElt,phiMin,sMin, fdCtr2.at<Vec2d>(0, 0)[0]- fdCtr1.at<Vec2d>(0, 0)[0], fdCtr2.at<Vec2d>(0, 0)[1]- fdCtr1.at<Vec2d>(0, 0)[1]);
|
||||
if (distFin)
|
||||
*distFin= distMin;
|
||||
x.copyTo(_alphaPhiST);
|
||||
}
|
||||
|
||||
void fourierDescriptor(InputArray _src, OutputArray _dst, int nbElt, int nbFD)
|
||||
{
|
||||
CV_Assert(_src.kind() == _InputArray::MAT || _src.kind() == _InputArray::STD_VECTOR);
|
||||
CV_Assert(_src.empty() || (_src.channels() == 2 && (_src.depth() == CV_32S || _src.depth() == CV_32F || _src.depth() == CV_64F)));
|
||||
Mat z = _src.getMat();
|
||||
CV_Assert(z.rows == 1 || z.cols == 1);
|
||||
if (nbElt==-1)
|
||||
nbElt = getOptimalDFTSize(max(z.rows, z.cols));
|
||||
CV_Assert((nbFD >= 1 && nbFD <=nbElt/2) || nbFD==-1);
|
||||
Mat Z;
|
||||
if (z.rows*z.cols!=nbElt)
|
||||
contourSampling(_src, z,nbElt);
|
||||
else if (_src.depth() == CV_32S)
|
||||
z.convertTo(z, CV_32F);
|
||||
dft(z, Z, DFT_SCALE | DFT_REAL_OUTPUT);
|
||||
if (nbFD == -1)
|
||||
{
|
||||
Z.copyTo(_dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
int n1 = nbFD / 2, n2 = nbElt - n1;
|
||||
Mat d(nbFD, 1, Z.type());
|
||||
Z.rowRange(Range(1, n1+1)).copyTo(d.rowRange(Range(0, n1)));
|
||||
if (n2>0)
|
||||
Z.rowRange(Range(n2, Z.rows)).copyTo(d.rowRange(Range(n1, nbFD)));
|
||||
d.copyTo(_dst);
|
||||
}
|
||||
}
|
||||
|
||||
void contourSampling(InputArray _src, OutputArray _out, int nbElt)
|
||||
{
|
||||
CV_Assert(_src.kind() == _InputArray::STD_VECTOR || _src.kind() == _InputArray::MAT);
|
||||
CV_Assert(_src.empty() || (_src.channels() == 2 && (_src.depth() == CV_32S || _src.depth() == CV_32F || _src.depth() == CV_64F)));
|
||||
CV_Assert(nbElt>0);
|
||||
Mat ctr;
|
||||
_src.getMat().convertTo(ctr,CV_32F);
|
||||
if (ctr.rows*ctr.cols == 0)
|
||||
{
|
||||
_out.release();
|
||||
return;
|
||||
}
|
||||
CV_Assert(ctr.rows==1 || ctr.cols==1);
|
||||
double l1 = 0, l2, p, d, s;
|
||||
Mat r;
|
||||
if (ctr.rows==1)
|
||||
ctr=ctr.t();
|
||||
int j = 0;
|
||||
int nb = ctr.rows;
|
||||
p = arcLength(ctr, true);
|
||||
l2 = norm(ctr.row(j) - ctr.row(j + 1)) / p;
|
||||
for (int i = 0; i<nbElt; i++)
|
||||
{
|
||||
s = (float)i / (float)nbElt;
|
||||
while (s >= l2)
|
||||
{
|
||||
j++;
|
||||
l1 = l2;
|
||||
d = norm(ctr.row(j % nb) - ctr.row((j + 1) % nb));
|
||||
l2 = l1 + d / p;
|
||||
}
|
||||
if ((s >= l1) && (s < l2))
|
||||
{
|
||||
Mat d1=ctr.row((j + 1) % nb);
|
||||
Mat d0=ctr.row(j % nb);
|
||||
Mat d10 = d1 - d0;
|
||||
Mat pn = d0 + d10 * (s - l1) / (l2 - l1);
|
||||
r.push_back(pn);
|
||||
}
|
||||
}
|
||||
r.copyTo(_out);
|
||||
}
|
||||
|
||||
void transformFD(InputArray _src, InputArray _t,OutputArray _dst, bool fdContour)
|
||||
{
|
||||
if (!fdContour)
|
||||
CV_Assert(_src.kind() == _InputArray::STD_VECTOR || _src.kind() == _InputArray::MAT);
|
||||
else
|
||||
CV_Assert( _src.kind() == _InputArray::MAT );
|
||||
CV_Assert(_src.channels() == 2);
|
||||
CV_Assert(_t.kind() == _InputArray::MAT);
|
||||
Mat t=_t.getMat();
|
||||
CV_Assert(t.rows == 1 && t.cols==5 && t.depth()==CV_64F);
|
||||
Mat Z;
|
||||
if (!fdContour)
|
||||
{
|
||||
Mat ctr1 = _src.getMat();
|
||||
if (ctr1.rows==1)
|
||||
ctr1 = ctr1.t();
|
||||
Mat newCtr1;
|
||||
int M = getOptimalDFTSize(ctr1.rows);
|
||||
contourSampling(ctr1, newCtr1, M);
|
||||
fourierDescriptor(newCtr1, Z);
|
||||
}
|
||||
else
|
||||
Z = _src.getMat();
|
||||
if (Z.type()!=CV_64FC2)
|
||||
Z.convertTo(Z,CV_64F);
|
||||
std::complex<double> expitheta = t.at<double>(0,2) * std::complex<double>(cos(t.at<double>(0, 1)), sin(t.at<double>(0, 1)));
|
||||
for (int j = 1; j<Z.rows; j++)
|
||||
{
|
||||
std::complex<double> zr(Z.at<Vec2d>(j, 0)[0], Z.at<Vec2d>(j,0)[1]);
|
||||
if (j<=Z.rows/2)
|
||||
zr = zr*expitheta*exp(t.at<double>(0, 0) * 2 * (M_PI*j) * std::complex<double>(0, 1));
|
||||
else
|
||||
zr = zr*expitheta*exp(t.at<double>(0, 0)* 2 * (M_PI*(j - Z.rows)) * std::complex<double>(0, 1));
|
||||
Z.at<Vec2d>(j, 0) = Vec2d(zr.real(),zr.imag());
|
||||
}
|
||||
Z.at<Vec2d>(0, 0) += Vec2d(t.at<double>(0, 3), t.at<double>(0, 4));
|
||||
std::vector<Point2d> z;
|
||||
dft(Z, z, DFT_INVERSE);
|
||||
Mat(z).copyTo(_dst);
|
||||
}
|
||||
|
||||
cv::Ptr<ContourFitting> createContourFitting(int ctr, int fd)
|
||||
{
|
||||
return makePtr<ContourFitting>(ctr, fd);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
By downloading, copying, installing or using the software you agree to this
|
||||
license. If you do not agree to this license, do not download, install,
|
||||
copy or use the software.
|
||||
License Agreement
|
||||
For Open Source Computer Vision Library
|
||||
(3-clause BSD License)
|
||||
Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
Third party copyrights are property of their respective owners.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall copyright holders or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused
|
||||
and on any theory of liability, whether in contract, strict liability,
|
||||
or tort (including negligence or otherwise) arising in any way out of
|
||||
the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
/******************************************************************************\
|
||||
* Graph based segmentation *
|
||||
* This code implements the segmentation method described in: *
|
||||
* P. Felzenszwalb, D. Huttenlocher: "Graph-Based Image Segmentation" *
|
||||
* International Journal of Computer Vision, Vol. 59, No. 2, September 2004 *
|
||||
* *
|
||||
* Author: Maximilien Cuony / LTS2 / EPFL / 2015 *
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/ximgproc/segmentation.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
namespace segmentation {
|
||||
|
||||
// Helpers
|
||||
|
||||
// Represent an edge between two pixels
|
||||
class Edge {
|
||||
public:
|
||||
int from;
|
||||
int to;
|
||||
float weight;
|
||||
|
||||
bool operator <(const Edge& e) const {
|
||||
return weight < e.weight;
|
||||
}
|
||||
};
|
||||
|
||||
// A point in the sets of points
|
||||
class PointSetElement {
|
||||
public:
|
||||
int p;
|
||||
int size;
|
||||
|
||||
PointSetElement() { }
|
||||
|
||||
PointSetElement(int p_) {
|
||||
p = p_;
|
||||
size = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// An object to manage set of points, who can be fusionned
|
||||
class PointSet {
|
||||
public:
|
||||
PointSet(int nb_elements_);
|
||||
~PointSet();
|
||||
|
||||
int nb_elements;
|
||||
|
||||
// Return the main point of the point's set
|
||||
int getBasePoint(int p);
|
||||
|
||||
// Join two sets of points, based on their main point
|
||||
void joinPoints(int p_a, int p_b);
|
||||
|
||||
// Return the set size of a set (based on the main point)
|
||||
int size(unsigned int p) { return mapping[p].size; }
|
||||
|
||||
private:
|
||||
PointSetElement* mapping;
|
||||
|
||||
};
|
||||
|
||||
class GraphSegmentationImpl : public GraphSegmentation {
|
||||
public:
|
||||
GraphSegmentationImpl() {
|
||||
sigma = 0.5;
|
||||
k = 300;
|
||||
min_size = 100;
|
||||
name_ = "GraphSegmentation";
|
||||
}
|
||||
|
||||
~GraphSegmentationImpl() CV_OVERRIDE {
|
||||
};
|
||||
|
||||
virtual void processImage(InputArray src, OutputArray dst) CV_OVERRIDE;
|
||||
|
||||
virtual void setSigma(double sigma_) CV_OVERRIDE { if (sigma_ <= 0) { sigma_ = 0.001; } sigma = sigma_; }
|
||||
virtual double getSigma() CV_OVERRIDE { return sigma; }
|
||||
|
||||
virtual void setK(float k_) CV_OVERRIDE { k = k_; }
|
||||
virtual float getK() CV_OVERRIDE { return k; }
|
||||
|
||||
virtual void setMinSize(int min_size_) CV_OVERRIDE { min_size = min_size_; }
|
||||
virtual int getMinSize() CV_OVERRIDE { return min_size; }
|
||||
|
||||
virtual void write(FileStorage& fs) const CV_OVERRIDE {
|
||||
fs << "name" << name_
|
||||
<< "sigma" << sigma
|
||||
<< "k" << k
|
||||
<< "min_size" << (int)min_size;
|
||||
}
|
||||
|
||||
virtual void read(const FileNode& fn) CV_OVERRIDE {
|
||||
CV_Assert( (String)fn["name"] == name_ );
|
||||
|
||||
sigma = (double)fn["sigma"];
|
||||
k = (float)fn["k"];
|
||||
min_size = (int)(int)fn["min_size"];
|
||||
}
|
||||
|
||||
private:
|
||||
double sigma;
|
||||
float k;
|
||||
int min_size;
|
||||
String name_;
|
||||
|
||||
// Pre-filter the image
|
||||
void filter(const Mat &img, Mat &img_filtered);
|
||||
|
||||
// Build the graph between each pixels
|
||||
void buildGraph(Edge **edges, int &nb_edges, const Mat &img_filtered);
|
||||
|
||||
// Segment the graph
|
||||
void segmentGraph(Edge * edges, const int &nb_edges, const Mat & img_filtered, PointSet **es);
|
||||
|
||||
// Remove areas too small
|
||||
void filterSmallAreas(Edge *edges, const int &nb_edges, PointSet *es);
|
||||
|
||||
// Map the segemented graph to a Mat with uniques, sequentials ids
|
||||
void finalMapping(PointSet *es, Mat &output);
|
||||
};
|
||||
|
||||
void GraphSegmentationImpl::filter(const Mat &img, Mat &img_filtered) {
|
||||
|
||||
Mat img_converted;
|
||||
|
||||
// Switch to float
|
||||
img.convertTo(img_converted, CV_32F);
|
||||
|
||||
// Apply gaussian filter
|
||||
GaussianBlur(img_converted, img_filtered, Size(0, 0), sigma, sigma);
|
||||
}
|
||||
|
||||
void GraphSegmentationImpl::buildGraph(Edge **edges, int &nb_edges, const Mat &img_filtered) {
|
||||
|
||||
*edges = new Edge[img_filtered.rows * img_filtered.cols * 4];
|
||||
|
||||
nb_edges = 0;
|
||||
|
||||
int nb_channels = img_filtered.channels();
|
||||
|
||||
for (int i = 0; i < (int)img_filtered.rows; i++) {
|
||||
const float* p = img_filtered.ptr<float>(i);
|
||||
|
||||
for (int j = 0; j < (int)img_filtered.cols; j++) {
|
||||
|
||||
//Take the right, left, top and down pixel
|
||||
for (int delta = -1; delta <= 1; delta += 2) {
|
||||
for (int delta_j = 0, delta_i = 1; delta_j <= 1; delta_j++ || delta_i--) {
|
||||
|
||||
int i2 = i + delta * delta_i;
|
||||
int j2 = j + delta * delta_j;
|
||||
|
||||
if (i2 >= 0 && i2 < img_filtered.rows && j2 >= 0 && j2 < img_filtered.cols) {
|
||||
const float* p2 = img_filtered.ptr<float>(i2);
|
||||
|
||||
float tmp_total = 0;
|
||||
|
||||
for ( int channel = 0; channel < nb_channels; channel++) {
|
||||
float tmp_diff = p[j * nb_channels + channel] - p2[j2 * nb_channels + channel];
|
||||
tmp_total += tmp_diff * tmp_diff;
|
||||
}
|
||||
|
||||
float diff = 0;
|
||||
diff = sqrt(tmp_total);
|
||||
|
||||
(*edges)[nb_edges].weight = diff;
|
||||
(*edges)[nb_edges].from = i * img_filtered.cols + j;
|
||||
(*edges)[nb_edges].to = i2 * img_filtered.cols + j2;
|
||||
|
||||
nb_edges++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphSegmentationImpl::segmentGraph(Edge *edges, const int &nb_edges, const Mat &img_filtered, PointSet **es) {
|
||||
|
||||
int total_points = ( int)(img_filtered.rows * img_filtered.cols);
|
||||
|
||||
// Sort edges
|
||||
std::sort(edges, edges + nb_edges);
|
||||
|
||||
// Create a set with all point (by default mapped to themselves)
|
||||
*es = new PointSet(img_filtered.cols * img_filtered.rows);
|
||||
|
||||
// Thresholds
|
||||
float* thresholds = new float[total_points];
|
||||
|
||||
for (int i = 0; i < total_points; i++)
|
||||
thresholds[i] = k;
|
||||
|
||||
for ( int i = 0; i < nb_edges; i++) {
|
||||
|
||||
int p_a = (*es)->getBasePoint(edges[i].from);
|
||||
int p_b = (*es)->getBasePoint(edges[i].to);
|
||||
|
||||
if (p_a != p_b) {
|
||||
if (edges[i].weight <= thresholds[p_a] && edges[i].weight <= thresholds[p_b]) {
|
||||
(*es)->joinPoints(p_a, p_b);
|
||||
p_a = (*es)->getBasePoint(p_a);
|
||||
thresholds[p_a] = edges[i].weight + k / (*es)->size(p_a);
|
||||
|
||||
edges[i].weight = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] thresholds;
|
||||
}
|
||||
|
||||
void GraphSegmentationImpl::filterSmallAreas(Edge *edges, const int &nb_edges, PointSet *es) {
|
||||
|
||||
for ( int i = 0; i < nb_edges; i++) {
|
||||
|
||||
if (edges[i].weight > 0) {
|
||||
|
||||
int p_a = es->getBasePoint(edges[i].from);
|
||||
int p_b = es->getBasePoint(edges[i].to);
|
||||
|
||||
if (p_a != p_b && (es->size(p_a) < min_size || es->size(p_b) < min_size)) {
|
||||
es->joinPoints(p_a, p_b);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GraphSegmentationImpl::finalMapping(PointSet *es, Mat &output) {
|
||||
|
||||
int maximum_size = ( int)(output.rows * output.cols);
|
||||
|
||||
int last_id = 0;
|
||||
int * mapped_id = new int[maximum_size];
|
||||
|
||||
for ( int i = 0; i < maximum_size; i++)
|
||||
mapped_id[i] = -1;
|
||||
|
||||
int rows = output.rows;
|
||||
int cols = output.cols;
|
||||
|
||||
if (output.isContinuous()) {
|
||||
cols *= rows;
|
||||
rows = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
|
||||
int* p = output.ptr<int>(i);
|
||||
|
||||
for (int j = 0; j < cols; j++) {
|
||||
|
||||
int point = es->getBasePoint(i * cols + j);
|
||||
|
||||
if (mapped_id[point] == -1) {
|
||||
mapped_id[point] = last_id;
|
||||
last_id++;
|
||||
}
|
||||
|
||||
p[j] = mapped_id[point];
|
||||
}
|
||||
}
|
||||
|
||||
delete [] mapped_id;
|
||||
}
|
||||
|
||||
void GraphSegmentationImpl::processImage(InputArray src, OutputArray dst) {
|
||||
|
||||
Mat img = src.getMat();
|
||||
|
||||
dst.create(img.rows, img.cols, CV_32SC1);
|
||||
Mat output = dst.getMat();
|
||||
output.setTo(0);
|
||||
|
||||
// Filter graph
|
||||
Mat img_filtered;
|
||||
filter(img, img_filtered);
|
||||
|
||||
// Build graph
|
||||
Edge *edges;
|
||||
int nb_edges;
|
||||
|
||||
buildGraph(&edges, nb_edges, img_filtered);
|
||||
|
||||
// Segment graph
|
||||
PointSet *es;
|
||||
|
||||
segmentGraph(edges, nb_edges, img_filtered, &es);
|
||||
|
||||
// Remove small areas
|
||||
filterSmallAreas(edges, nb_edges, es);
|
||||
|
||||
// Map to final output
|
||||
finalMapping(es, output);
|
||||
|
||||
delete [] edges;
|
||||
delete es;
|
||||
|
||||
}
|
||||
|
||||
Ptr<GraphSegmentation> createGraphSegmentation(double sigma, float k, int min_size) {
|
||||
|
||||
Ptr<GraphSegmentation> graphseg = makePtr<GraphSegmentationImpl>();
|
||||
|
||||
graphseg->setSigma(sigma);
|
||||
graphseg->setK(k);
|
||||
graphseg->setMinSize(min_size);
|
||||
|
||||
return graphseg;
|
||||
}
|
||||
|
||||
PointSet::PointSet(int nb_elements_) {
|
||||
nb_elements = nb_elements_;
|
||||
|
||||
mapping = new PointSetElement[nb_elements];
|
||||
|
||||
for ( int i = 0; i < nb_elements; i++) {
|
||||
mapping[i] = PointSetElement(i);
|
||||
}
|
||||
}
|
||||
|
||||
PointSet::~PointSet() {
|
||||
delete [] mapping;
|
||||
}
|
||||
|
||||
int PointSet::getBasePoint( int p) {
|
||||
|
||||
int base_p = p;
|
||||
|
||||
while (base_p != mapping[base_p].p) {
|
||||
base_p = mapping[base_p].p;
|
||||
}
|
||||
|
||||
// Save mapping for faster acces later
|
||||
mapping[p].p = base_p;
|
||||
|
||||
return base_p;
|
||||
}
|
||||
|
||||
void PointSet::joinPoints(int p_a, int p_b) {
|
||||
|
||||
// Always target smaller set, to avoid redirection in getBasePoint
|
||||
if (mapping[p_a].size < mapping[p_b].size)
|
||||
swap(p_a, p_b);
|
||||
|
||||
mapping[p_b].p = p_a;
|
||||
mapping[p_a].size += mapping[p_b].size;
|
||||
|
||||
nb_elements--;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,850 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "edgeaware_filters_common.hpp"
|
||||
#include <vector>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable: 4512)
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
using std::vector;
|
||||
using namespace cv::ximgproc::intrinsics;
|
||||
|
||||
template <typename T>
|
||||
struct SymArray2D
|
||||
{
|
||||
vector<T> vec;
|
||||
int sz;
|
||||
|
||||
SymArray2D()
|
||||
{
|
||||
sz = 0;
|
||||
}
|
||||
|
||||
void create(int sz_)
|
||||
{
|
||||
CV_DbgAssert(sz_ > 0);
|
||||
sz = sz_;
|
||||
vec.resize(total());
|
||||
}
|
||||
|
||||
inline T& operator()(int i, int j)
|
||||
{
|
||||
CV_DbgAssert(i >= 0 && i < sz && j >= 0 && j < sz);
|
||||
if (i < j) std::swap(i, j);
|
||||
return vec[i*(i+1)/2 + j];
|
||||
}
|
||||
|
||||
inline T& operator()(int i)
|
||||
{
|
||||
return vec[i];
|
||||
}
|
||||
|
||||
int total() const
|
||||
{
|
||||
return sz*(sz + 1)/2;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
vec.clear();
|
||||
sz = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename XMat>
|
||||
static void splitFirstNChannels(InputArrayOfArrays src, vector<XMat>& dst, int maxDstCn)
|
||||
{
|
||||
CV_Assert(src.isMat() || src.isUMat() || src.isMatVector() || src.isUMatVector());
|
||||
|
||||
if ( (src.isMat() || src.isUMat()) && src.channels() == maxDstCn )
|
||||
{
|
||||
split(src, dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
Size sz;
|
||||
int depth, totalCnNum;
|
||||
|
||||
checkSameSizeAndDepth(src, sz, depth);
|
||||
totalCnNum = std::min(maxDstCn, getTotalNumberOfChannels(src));
|
||||
|
||||
dst.resize(totalCnNum);
|
||||
vector<int> fromTo(2*totalCnNum);
|
||||
for (int i = 0; i < totalCnNum; i++)
|
||||
{
|
||||
fromTo[i*2 + 0] = i;
|
||||
fromTo[i*2 + 1] = i;
|
||||
|
||||
dst[i].create(sz, CV_MAKE_TYPE(depth, 1));
|
||||
}
|
||||
|
||||
mixChannels(src, dst, fromTo);
|
||||
}
|
||||
}
|
||||
|
||||
class GuidedFilterImpl : public GuidedFilter
|
||||
{
|
||||
public:
|
||||
|
||||
static Ptr<GuidedFilterImpl> create(InputArray guide, int radius, double eps, double scale);
|
||||
|
||||
void filter(InputArray src, OutputArray dst, int dDepth = -1) CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
||||
int radius;
|
||||
double eps;
|
||||
double scale;
|
||||
int h, w;
|
||||
int hOriginal, wOriginal;
|
||||
|
||||
vector<Mat> guideCn;
|
||||
vector<Mat> guideCnMean;
|
||||
vector<Mat> guideCnOriginal;
|
||||
|
||||
SymArray2D<Mat> covarsInv;
|
||||
|
||||
int gCnNum;
|
||||
|
||||
protected:
|
||||
|
||||
GuidedFilterImpl() {}
|
||||
|
||||
void init(InputArray guide, int radius, double eps, double scale);
|
||||
|
||||
void computeCovGuide(SymArray2D<Mat>& covars);
|
||||
|
||||
void computeCovGuideAndSrc(vector<Mat>& srcCn, vector<Mat>& srcCnMean, vector<vector<Mat> >& cov);
|
||||
|
||||
void getWalkPattern(int eid, int &cn1, int &cn2);
|
||||
|
||||
inline void meanFilter(Mat& src, Mat& dst)
|
||||
{
|
||||
boxFilter(src, dst, CV_32F, Size(2 * radius + 1, 2 * radius + 1), cv::Point(-1, -1), true, BORDER_REFLECT);
|
||||
}
|
||||
|
||||
inline void convertToWorkType(Mat& src, Mat& dst)
|
||||
{
|
||||
src.convertTo(dst, CV_32F);
|
||||
}
|
||||
|
||||
inline void subsample(Mat& src, Mat& dst)
|
||||
{
|
||||
resize(src, dst, Size(w, h), 0, 0, INTER_LINEAR);
|
||||
}
|
||||
|
||||
inline void upsample(Mat& src, Mat& dst)
|
||||
{
|
||||
resize(src, dst, Size(wOriginal, hOriginal), 0, 0, INTER_LINEAR);
|
||||
}
|
||||
|
||||
private: /*Routines to parallelize boxFilter and convertTo*/
|
||||
|
||||
typedef void (GuidedFilterImpl::*TransformFunc)(Mat& src, Mat& dst);
|
||||
|
||||
struct GFTransform_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
mutable vector<Mat*> src;
|
||||
mutable vector<Mat*> dst;
|
||||
TransformFunc func;
|
||||
|
||||
GFTransform_ParBody(GuidedFilterImpl& gf_, vector<Mat>& srcv, vector<Mat>& dstv, TransformFunc func_);
|
||||
GFTransform_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& srcvv, vector<vector<Mat> >& dstvv, TransformFunc func_);
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
|
||||
Range getRange() const
|
||||
{
|
||||
return Range(0, (int)src.size());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename V>
|
||||
void parConvertToWorkType(V &src, V &dst)
|
||||
{
|
||||
GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::convertToWorkType);
|
||||
parallel_for_(pb.getRange(), pb);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void parMeanFilter(V &src, V &dst)
|
||||
{
|
||||
GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::meanFilter);
|
||||
parallel_for_(pb.getRange(), pb);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void parSubsample(V &src, V &dst)
|
||||
{
|
||||
GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::subsample);
|
||||
parallel_for_(pb.getRange(), pb);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void parUpsample(V &src, V &dst)
|
||||
{
|
||||
GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::upsample);
|
||||
parallel_for_(pb.getRange(), pb);
|
||||
}
|
||||
|
||||
private: /*Parallel body classes*/
|
||||
|
||||
inline void runParBody(const ParallelLoopBody& pb)
|
||||
{
|
||||
parallel_for_(Range(0, h), pb);
|
||||
}
|
||||
|
||||
struct MulChannelsGuide_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
SymArray2D<Mat> &covars;
|
||||
|
||||
MulChannelsGuide_ParBody(GuidedFilterImpl& gf_, SymArray2D<Mat>& covars_)
|
||||
: gf(gf_), covars(covars_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeCovGuideFromChannelsMul_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
SymArray2D<Mat> &covars;
|
||||
|
||||
ComputeCovGuideFromChannelsMul_ParBody(GuidedFilterImpl& gf_, SymArray2D<Mat>& covars_)
|
||||
: gf(gf_), covars(covars_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeCovGuideInv_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
SymArray2D<Mat> &covars;
|
||||
|
||||
ComputeCovGuideInv_ParBody(GuidedFilterImpl& gf_, SymArray2D<Mat>& covars_);
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
|
||||
struct MulChannelsGuideAndSrc_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
vector<vector<Mat> > &cov;
|
||||
vector<Mat> &srcCn;
|
||||
|
||||
MulChannelsGuideAndSrc_ParBody(GuidedFilterImpl& gf_, vector<Mat>& srcCn_, vector<vector<Mat> >& cov_)
|
||||
: gf(gf_), cov(cov_), srcCn(srcCn_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeCovFromSrcChannelsMul_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
vector<vector<Mat> > &cov;
|
||||
vector<Mat> &srcCnMean;
|
||||
|
||||
ComputeCovFromSrcChannelsMul_ParBody(GuidedFilterImpl& gf_, vector<Mat>& srcCnMean_, vector<vector<Mat> >& cov_)
|
||||
: gf(gf_), cov(cov_), srcCnMean(srcCnMean_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeAlpha_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
vector<vector<Mat> > α
|
||||
vector<vector<Mat> > &covSrc;
|
||||
|
||||
ComputeAlpha_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& alpha_, vector<vector<Mat> >& covSrc_)
|
||||
: gf(gf_), alpha(alpha_), covSrc(covSrc_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ComputeBeta_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
vector<vector<Mat> > α
|
||||
vector<Mat> &srcCnMean;
|
||||
vector<Mat> β
|
||||
|
||||
ComputeBeta_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& alpha_, vector<Mat>& srcCnMean_, vector<Mat>& beta_)
|
||||
: gf(gf_), alpha(alpha_), srcCnMean(srcCnMean_), beta(beta_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
struct ApplyTransform_ParBody : public ParallelLoopBody
|
||||
{
|
||||
GuidedFilterImpl &gf;
|
||||
vector<vector<Mat> > α
|
||||
vector<Mat> β
|
||||
|
||||
ApplyTransform_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& alpha_, vector<Mat>& beta_)
|
||||
: gf(gf_), alpha(alpha_), beta(beta_) {}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE;
|
||||
};
|
||||
};
|
||||
|
||||
void GuidedFilterImpl::MulChannelsGuide_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int total = covars.total();
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
int c1, c2;
|
||||
float *cov, *guide1, *guide2;
|
||||
|
||||
for (int k = 0; k < total; k++)
|
||||
{
|
||||
gf.getWalkPattern(k, c1, c2);
|
||||
|
||||
guide1 = gf.guideCn[c1].ptr<float>(i);
|
||||
guide2 = gf.guideCn[c2].ptr<float>(i);
|
||||
cov = covars(c1, c2).ptr<float>(i);
|
||||
|
||||
mul(cov, guide1, guide2, gf.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::ComputeCovGuideFromChannelsMul_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int total = covars.total();
|
||||
float diagSummand = (float)(gf.eps);
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
int c1, c2;
|
||||
float *cov, *guide1, *guide2;
|
||||
|
||||
for (int k = 0; k < total; k++)
|
||||
{
|
||||
gf.getWalkPattern(k, c1, c2);
|
||||
|
||||
guide1 = gf.guideCnMean[c1].ptr<float>(i);
|
||||
guide2 = gf.guideCnMean[c2].ptr<float>(i);
|
||||
cov = covars(c1, c2).ptr<float>(i);
|
||||
|
||||
if (c1 != c2)
|
||||
{
|
||||
sub_mul(cov, guide1, guide2, gf.w);
|
||||
}
|
||||
else
|
||||
{
|
||||
sub_mad(cov, guide1, guide2, -diagSummand, gf.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GuidedFilterImpl::ComputeCovGuideInv_ParBody::ComputeCovGuideInv_ParBody(GuidedFilterImpl& gf_, SymArray2D<Mat>& covars_)
|
||||
: gf(gf_), covars(covars_)
|
||||
{
|
||||
gf.covarsInv.create(gf.gCnNum);
|
||||
|
||||
if (gf.gCnNum == 3)
|
||||
{
|
||||
for (int k = 0; k < 2; k++)
|
||||
for (int l = 0; l < 3; l++)
|
||||
gf.covarsInv(k, l).create(gf.h, gf.w, CV_32FC1);
|
||||
|
||||
////trick to avoid memory allocation
|
||||
gf.covarsInv(2, 0).create(gf.h, gf.w, CV_32FC1);
|
||||
gf.covarsInv(2, 1) = covars(2, 1);
|
||||
gf.covarsInv(2, 2) = covars(2, 2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (gf.gCnNum == 2)
|
||||
{
|
||||
gf.covarsInv(0, 0) = covars(1, 1);
|
||||
gf.covarsInv(0, 1) = covars(0, 1);
|
||||
gf.covarsInv(1, 1) = covars(0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gf.gCnNum == 1)
|
||||
{
|
||||
gf.covarsInv(0, 0) = covars(0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::ComputeCovGuideInv_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
if (gf.gCnNum == 3)
|
||||
{
|
||||
vector<float> covarsDet(gf.w);
|
||||
float *det = &covarsDet[0];
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
for (int k = 0; k < 3; k++)
|
||||
for (int l = 0; l <= k; l++)
|
||||
{
|
||||
float *dst = gf.covarsInv(k, l).ptr<float>(i);
|
||||
|
||||
float *a00 = covars((k + 1) % 3, (l + 1) % 3).ptr<float>(i);
|
||||
float *a01 = covars((k + 1) % 3, (l + 2) % 3).ptr<float>(i);
|
||||
float *a10 = covars((k + 2) % 3, (l + 1) % 3).ptr<float>(i);
|
||||
float *a11 = covars((k + 2) % 3, (l + 2) % 3).ptr<float>(i);
|
||||
|
||||
det_2x2(dst, a00, a01, a10, a11, gf.w);
|
||||
}
|
||||
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
float *a = covars(k, 0).ptr<float>(i);
|
||||
float *ac = gf.covarsInv(k, 0).ptr<float>(i);
|
||||
|
||||
if (k == 0)
|
||||
mul(det, a, ac, gf.w);
|
||||
else
|
||||
add_mul(det, a, ac, gf.w);
|
||||
}
|
||||
|
||||
if (gf.eps < 1e-2)
|
||||
{
|
||||
for (int j = 0; j < gf.w; j++)
|
||||
if (abs(det[j]) < 1e-6f)
|
||||
det[j] = 1.f;
|
||||
}
|
||||
|
||||
for (int k = 0; k < gf.covarsInv.total(); k += 1)
|
||||
{
|
||||
div_1x(gf.covarsInv(k).ptr<float>(i), det, gf.w);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (gf.gCnNum == 2)
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
float *a00 = gf.covarsInv(0, 0).ptr<float>(i);
|
||||
float *a10 = gf.covarsInv(1, 0).ptr<float>(i);
|
||||
float *a11 = gf.covarsInv(1, 1).ptr<float>(i);
|
||||
|
||||
div_det_2x2(a00, a10, a11, gf.w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (gf.gCnNum == 1)
|
||||
{
|
||||
//divide(1.0, covars(0, 0)(range, Range::all()), gf.covarsInv(0, 0)(range, Range::all()));
|
||||
//return;
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
float *res = covars(0, 0).ptr<float>(i);
|
||||
inv_self(res, gf.w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::MulChannelsGuideAndSrc_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int srcCnNum = (int)srcCn.size();
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
int step = (si % 2) * 2 - 1;
|
||||
int start = (si % 2) ? 0 : gf.gCnNum - 1;
|
||||
int end = (si % 2) ? gf.gCnNum : -1;
|
||||
|
||||
float *srcLine = srcCn[si].ptr<float>(i);
|
||||
|
||||
for (int gi = start; gi != end; gi += step)
|
||||
{
|
||||
float *guideLine = gf.guideCn[gi].ptr<float>(i);
|
||||
float *dstLine = cov[si][gi].ptr<float>(i);
|
||||
|
||||
mul(dstLine, srcLine, guideLine, gf.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::ComputeCovFromSrcChannelsMul_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int srcCnNum = (int)srcCnMean.size();
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
int step = (si % 2) * 2 - 1;
|
||||
int start = (si % 2) ? 0 : gf.gCnNum - 1;
|
||||
int end = (si % 2) ? gf.gCnNum : -1;
|
||||
|
||||
float *srcMeanLine = srcCnMean[si].ptr<float>(i);
|
||||
|
||||
for (int gi = start; gi != end; gi += step)
|
||||
{
|
||||
float *guideMeanLine = gf.guideCnMean[gi].ptr<float>(i);
|
||||
float *covLine = cov[si][gi].ptr<float>(i);
|
||||
|
||||
sub_mul(covLine, srcMeanLine, guideMeanLine, gf.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::ComputeAlpha_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int srcCnNum = (int)covSrc.size();
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
for (int gi = 0; gi < gf.gCnNum; gi++)
|
||||
{
|
||||
float *y, *A, *dstAlpha;
|
||||
|
||||
dstAlpha = alpha[si][gi].ptr<float>(i);
|
||||
for (int k = 0; k < gf.gCnNum; k++)
|
||||
{
|
||||
y = covSrc[si][k].ptr<float>(i);
|
||||
A = gf.covarsInv(gi, k).ptr<float>(i);
|
||||
|
||||
if (k == 0)
|
||||
{
|
||||
mul(dstAlpha, A, y, gf.w);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_mul(dstAlpha, A, y, gf.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::ComputeBeta_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int srcCnNum = (int)srcCnMean.size();
|
||||
CV_DbgAssert(&srcCnMean == &beta);
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
float *_g[4];
|
||||
for (int gi = 0; gi < gf.gCnNum; gi++)
|
||||
_g[gi] = gf.guideCnMean[gi].ptr<float>(i);
|
||||
|
||||
float *betaDst, *g, *a;
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
betaDst = beta[si].ptr<float>(i);
|
||||
for (int gi = 0; gi < gf.gCnNum; gi++)
|
||||
{
|
||||
a = alpha[si][gi].ptr<float>(i);
|
||||
g = _g[gi];
|
||||
|
||||
sub_mul(betaDst, a, g, gf.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::ApplyTransform_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
int srcCnNum = (int)alpha.size();
|
||||
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
float *_g[4];
|
||||
for (int gi = 0; gi < gf.gCnNum; gi++)
|
||||
_g[gi] = gf.guideCnOriginal[gi].ptr<float>(i);
|
||||
|
||||
float *betaDst, *g, *a;
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
betaDst = beta[si].ptr<float>(i);
|
||||
for (int gi = 0; gi < gf.gCnNum; gi++)
|
||||
{
|
||||
a = alpha[si][gi].ptr<float>(i);
|
||||
g = _g[gi];
|
||||
|
||||
add_mul(betaDst, a, g, gf.wOriginal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GuidedFilterImpl::GFTransform_ParBody::GFTransform_ParBody(GuidedFilterImpl& gf_, vector<Mat>& srcv, vector<Mat>& dstv, TransformFunc func_)
|
||||
: gf(gf_), func(func_)
|
||||
{
|
||||
CV_DbgAssert(srcv.size() == dstv.size());
|
||||
src.resize(srcv.size());
|
||||
dst.resize(srcv.size());
|
||||
|
||||
for (int i = 0; i < (int)srcv.size(); i++)
|
||||
{
|
||||
src[i] = &srcv[i];
|
||||
dst[i] = &dstv[i];
|
||||
}
|
||||
}
|
||||
|
||||
GuidedFilterImpl::GFTransform_ParBody::GFTransform_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& srcvv, vector<vector<Mat> >& dstvv, TransformFunc func_)
|
||||
: gf(gf_), func(func_)
|
||||
{
|
||||
CV_DbgAssert(srcvv.size() == dstvv.size());
|
||||
int n = (int)srcvv.size();
|
||||
int total = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
CV_DbgAssert(srcvv[i].size() == dstvv[i].size());
|
||||
total += (int)srcvv[i].size();
|
||||
}
|
||||
|
||||
src.resize(total);
|
||||
dst.resize(total);
|
||||
|
||||
int k = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (int j = 0; j < (int)srcvv[i].size(); j++)
|
||||
{
|
||||
src[k] = &srcvv[i][j];
|
||||
dst[k] = &dstvv[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::GFTransform_ParBody::operator()(const Range& range) const
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++)
|
||||
{
|
||||
(gf.*func)(*src[i], *dst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::getWalkPattern(int eid, int &cn1, int &cn2)
|
||||
{
|
||||
static int wdata[] = {
|
||||
0, -1, -1, -1, -1, -1,
|
||||
0, -1, -1, -1, -1, -1,
|
||||
|
||||
0, 0, 1, -1, -1, -1,
|
||||
0, 1, 1, -1, -1, -1,
|
||||
|
||||
0, 0, 0, 2, 1, 1,
|
||||
0, 1, 2, 2, 2, 1,
|
||||
};
|
||||
|
||||
cn1 = wdata[6 * 2 * (gCnNum-1) + eid];
|
||||
cn2 = wdata[6 * 2 * (gCnNum-1) + 6 + eid];
|
||||
}
|
||||
|
||||
Ptr<GuidedFilterImpl> GuidedFilterImpl::create(InputArray guide, int radius, double eps, double scale)
|
||||
{
|
||||
GuidedFilterImpl *gf = new GuidedFilterImpl();
|
||||
gf->init(guide, radius, eps, scale);
|
||||
return Ptr<GuidedFilterImpl>(gf);
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::init(InputArray guide, int radius_, double eps_, double scale_)
|
||||
{
|
||||
CV_Assert( !guide.empty() && radius_ >= 0 && eps_ >= 0 );
|
||||
CV_Assert( (guide.depth() == CV_32F || guide.depth() == CV_8U || guide.depth() == CV_16U) && (guide.channels() <= 3) );
|
||||
CV_Assert( scale_ <= 1.0 );
|
||||
|
||||
radius = radius_;
|
||||
eps = eps_;
|
||||
scale = scale_;
|
||||
|
||||
splitFirstNChannels(guide, guideCnOriginal, 3);
|
||||
gCnNum = (int)guideCnOriginal.size();
|
||||
hOriginal = guideCnOriginal[0].rows;
|
||||
wOriginal = guideCnOriginal[0].cols;
|
||||
h = int(hOriginal * scale);
|
||||
w = int(wOriginal * scale);
|
||||
|
||||
parConvertToWorkType(guideCnOriginal, guideCnOriginal);
|
||||
if (scale < 1.0)
|
||||
{
|
||||
guideCn.resize(gCnNum);
|
||||
parSubsample(guideCnOriginal, guideCn);
|
||||
}
|
||||
else
|
||||
{
|
||||
guideCn = guideCnOriginal;
|
||||
}
|
||||
|
||||
guideCnMean.resize(gCnNum);
|
||||
parMeanFilter(guideCn, guideCnMean);
|
||||
|
||||
SymArray2D<Mat> covars;
|
||||
computeCovGuide(covars);
|
||||
runParBody(ComputeCovGuideInv_ParBody(*this, covars));
|
||||
covars.release();
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::computeCovGuide(SymArray2D<Mat>& covars)
|
||||
{
|
||||
covars.create(gCnNum);
|
||||
for (int i = 0; i < covars.total(); i++)
|
||||
covars(i).create(h, w, CV_32FC1);
|
||||
|
||||
runParBody(MulChannelsGuide_ParBody(*this, covars));
|
||||
|
||||
parMeanFilter(covars.vec, covars.vec);
|
||||
|
||||
runParBody(ComputeCovGuideFromChannelsMul_ParBody(*this, covars));
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::filter(InputArray src, OutputArray dst, int dDepth /*= -1*/)
|
||||
{
|
||||
CV_Assert( !src.empty() && (src.depth() == CV_32F || src.depth() == CV_8U) );
|
||||
if (src.rows() != hOriginal || src.cols() != wOriginal)
|
||||
{
|
||||
CV_Error(Error::StsBadSize, "Size of filtering image must be equal to size of guide image");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dDepth == -1) dDepth = src.depth();
|
||||
int srcCnNum = src.channels();
|
||||
|
||||
vector<Mat> srcCn(srcCnNum);
|
||||
vector<Mat>& srcCnMean = srcCn;
|
||||
split(src, srcCn);
|
||||
|
||||
if (scale < 1.0)
|
||||
{
|
||||
parSubsample(srcCn, srcCn);
|
||||
}
|
||||
|
||||
if (src.depth() != CV_32F)
|
||||
{
|
||||
parConvertToWorkType(srcCn, srcCn);
|
||||
}
|
||||
|
||||
vector<vector<Mat> > covSrcGuide(srcCnNum);
|
||||
computeCovGuideAndSrc(srcCn, srcCnMean, covSrcGuide);
|
||||
|
||||
vector<vector<Mat> > alpha(srcCnNum);
|
||||
for (int si = 0; si < srcCnNum; si++)
|
||||
{
|
||||
alpha[si].resize(gCnNum);
|
||||
for (int gi = 0; gi < gCnNum; gi++)
|
||||
alpha[si][gi].create(h, w, CV_32FC1);
|
||||
}
|
||||
runParBody(ComputeAlpha_ParBody(*this, alpha, covSrcGuide));
|
||||
covSrcGuide.clear();
|
||||
|
||||
vector<Mat>& beta = srcCnMean;
|
||||
runParBody(ComputeBeta_ParBody(*this, alpha, srcCnMean, beta));
|
||||
|
||||
parMeanFilter(beta, beta);
|
||||
parMeanFilter(alpha, alpha);
|
||||
|
||||
if (scale < 1.0)
|
||||
{
|
||||
parUpsample(beta, beta);
|
||||
parUpsample(alpha, alpha);
|
||||
}
|
||||
|
||||
parallel_for_(Range(0, hOriginal), ApplyTransform_ParBody(*this, alpha, beta));
|
||||
if (dDepth != CV_32F)
|
||||
{
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
beta[i].convertTo(beta[i], dDepth);
|
||||
}
|
||||
merge(beta, dst);
|
||||
}
|
||||
|
||||
void GuidedFilterImpl::computeCovGuideAndSrc(vector<Mat>& srcCn, vector<Mat>& srcCnMean, vector<vector<Mat> >& cov)
|
||||
{
|
||||
int srcCnNum = (int)srcCn.size();
|
||||
|
||||
cov.resize(srcCnNum);
|
||||
for (int i = 0; i < srcCnNum; i++)
|
||||
{
|
||||
cov[i].resize(gCnNum);
|
||||
for (int j = 0; j < gCnNum; j++)
|
||||
cov[i][j].create(h, w, CV_32FC1);
|
||||
}
|
||||
|
||||
runParBody(MulChannelsGuideAndSrc_ParBody(*this, srcCn, cov));
|
||||
|
||||
parMeanFilter(srcCn, srcCnMean);
|
||||
parMeanFilter(cov, cov);
|
||||
|
||||
runParBody(ComputeCovFromSrcChannelsMul_ParBody(*this, srcCnMean, cov));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CV_EXPORTS_W
|
||||
Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps, double scale)
|
||||
{
|
||||
return Ptr<GuidedFilter>(GuidedFilterImpl::create(guide, radius, eps, scale));
|
||||
}
|
||||
|
||||
CV_EXPORTS_W
|
||||
void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth, double scale)
|
||||
{
|
||||
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps, scale);
|
||||
gf->filter(src, dst, dDepth);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable: 4512)
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
typedef Vec<float, 1> Vec1f;
|
||||
typedef Vec<uchar, 1> Vec1b;
|
||||
|
||||
#ifndef SQR
|
||||
#define SQR(a) ((a)*(a))
|
||||
#endif
|
||||
|
||||
void jointBilateralFilter_32f(Mat& joint, Mat& src, Mat& dst, int radius, double sigmaColor, double sigmaSpace, int borderType);
|
||||
|
||||
void jointBilateralFilter_8u(Mat& joint, Mat& src, Mat& dst, int radius, double sigmaColor, double sigmaSpace, int borderType);
|
||||
|
||||
template<typename JointVec, typename SrcVec>
|
||||
class JointBilateralFilter_32f : public ParallelLoopBody
|
||||
{
|
||||
Mat &joint, &src;
|
||||
Mat &dst;
|
||||
int radius, maxk;
|
||||
float scaleIndex;
|
||||
int *spaceOfs;
|
||||
float *spaceWeights, *expLUT;
|
||||
|
||||
public:
|
||||
|
||||
JointBilateralFilter_32f(Mat& joint_, Mat& src_, Mat& dst_, int radius_,
|
||||
int maxk_, float scaleIndex_, int *spaceOfs_, float *spaceWeights_, float *expLUT_)
|
||||
:
|
||||
joint(joint_), src(src_), dst(dst_), radius(radius_), maxk(maxk_),
|
||||
scaleIndex(scaleIndex_), spaceOfs(spaceOfs_), spaceWeights(spaceWeights_), expLUT(expLUT_)
|
||||
{
|
||||
CV_DbgAssert(joint.type() == traits::Type<JointVec>::value && src.type() == dst.type() && src.type() == traits::Type<SrcVec>::value);
|
||||
CV_DbgAssert(joint.rows == src.rows && src.rows == dst.rows + 2*radius);
|
||||
CV_DbgAssert(joint.cols == src.cols && src.cols == dst.cols + 2*radius);
|
||||
}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int i = radius + range.start; i < radius + range.end; i++)
|
||||
{
|
||||
for (int j = radius; j < src.cols - radius; j++)
|
||||
{
|
||||
JointVec *jointCenterPixPtr = joint.ptr<JointVec>(i) + j;
|
||||
SrcVec *srcCenterPixPtr = src.ptr<SrcVec>(i) + j;
|
||||
|
||||
JointVec jointPix0 = *jointCenterPixPtr;
|
||||
SrcVec srcSum = SrcVec::all(0.0f);
|
||||
float wSum = 0.0f;
|
||||
|
||||
for (int k = 0; k < maxk; k++)
|
||||
{
|
||||
float *jointPix = reinterpret_cast<float*>(jointCenterPixPtr + spaceOfs[k]);
|
||||
float alpha = 0.0f;
|
||||
|
||||
for (int cn = 0; cn < JointVec::channels; cn++)
|
||||
alpha += std::abs(jointPix0[cn] - jointPix[cn]);
|
||||
alpha *= scaleIndex;
|
||||
int idx = (int)(alpha);
|
||||
alpha -= idx;
|
||||
float weight = spaceWeights[k] * (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]));
|
||||
|
||||
float *srcPix = reinterpret_cast<float*>(srcCenterPixPtr + spaceOfs[k]);
|
||||
for (int cn = 0; cn < SrcVec::channels; cn++)
|
||||
srcSum[cn] += weight*srcPix[cn];
|
||||
wSum += weight;
|
||||
}
|
||||
|
||||
dst.at<SrcVec>(i - radius, j - radius) = srcSum / wSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void jointBilateralFilter_32f(Mat& joint, Mat& src, Mat& dst, int radius, double sigmaColor, double sigmaSpace, int borderType)
|
||||
{
|
||||
CV_DbgAssert(joint.depth() == CV_32F && src.depth() == CV_32F);
|
||||
|
||||
int d = 2*radius + 1;
|
||||
int jCn = joint.channels();
|
||||
const int kExpNumBinsPerChannel = 1 << 12;
|
||||
double minValJoint, maxValJoint;
|
||||
|
||||
minMaxLoc(joint, &minValJoint, &maxValJoint);
|
||||
if (abs(maxValJoint - minValJoint) < FLT_EPSILON)
|
||||
{
|
||||
//TODO: make circle pattern instead of square
|
||||
GaussianBlur(src, dst, Size(d, d), sigmaSpace, 0, borderType);
|
||||
return;
|
||||
}
|
||||
float colorRange = (float)(maxValJoint - minValJoint) * jCn;
|
||||
colorRange = std::max(0.01f, colorRange);
|
||||
|
||||
int kExpNumBins = kExpNumBinsPerChannel * jCn;
|
||||
vector<float> expLUTv(kExpNumBins + 2);
|
||||
float *expLUT = &expLUTv[0];
|
||||
float scaleIndex = kExpNumBins/colorRange;
|
||||
|
||||
double gaussColorCoeff = -0.5 / (sigmaColor*sigmaColor);
|
||||
double gaussSpaceCoeff = -0.5 / (sigmaSpace*sigmaSpace);
|
||||
|
||||
for (int i = 0; i < kExpNumBins + 2; i++)
|
||||
{
|
||||
double val = i / scaleIndex;
|
||||
expLUT[i] = (float) std::exp(val * val * gaussColorCoeff);
|
||||
}
|
||||
|
||||
Mat jointTemp, srcTemp;
|
||||
copyMakeBorder(joint, jointTemp, radius, radius, radius, radius, borderType);
|
||||
copyMakeBorder(src, srcTemp, radius, radius, radius, radius, borderType);
|
||||
size_t srcElemStep = srcTemp.step / srcTemp.elemSize();
|
||||
size_t jElemStep = jointTemp.step / jointTemp.elemSize();
|
||||
CV_Assert(srcElemStep == jElemStep);
|
||||
|
||||
vector<float> spaceWeightsv(d*d);
|
||||
vector<int> spaceOfsJointv(d*d);
|
||||
float *spaceWeights = &spaceWeightsv[0];
|
||||
int *spaceOfsJoint = &spaceOfsJointv[0];
|
||||
|
||||
int maxk = 0;
|
||||
for (int i = -radius; i <= radius; i++)
|
||||
{
|
||||
for (int j = -radius; j <= radius; j++)
|
||||
{
|
||||
double r2 = i*i + j*j;
|
||||
if (r2 > SQR(radius))
|
||||
continue;
|
||||
|
||||
spaceWeights[maxk] = (float) std::exp(r2 * gaussSpaceCoeff);
|
||||
spaceOfsJoint[maxk] = (int) (i*jElemStep + j);
|
||||
maxk++;
|
||||
}
|
||||
}
|
||||
|
||||
Range range(0, joint.rows);
|
||||
if (joint.type() == CV_32FC1)
|
||||
{
|
||||
if (src.type() == CV_32FC1)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_32f<Vec1f, Vec1f>(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
if (src.type() == CV_32FC3)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_32f<Vec1f, Vec3f>(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
}
|
||||
|
||||
if (joint.type() == CV_32FC3)
|
||||
{
|
||||
if (src.type() == CV_32FC1)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_32f<Vec3f, Vec1f>(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
if (src.type() == CV_32FC3)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_32f<Vec3f, Vec3f>(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename JointVec, typename SrcVec>
|
||||
class JointBilateralFilter_8u : public ParallelLoopBody
|
||||
{
|
||||
Mat &joint, &src;
|
||||
Mat &dst;
|
||||
int radius, maxk;
|
||||
float scaleIndex;
|
||||
int *spaceOfs;
|
||||
float *spaceWeights, *expLUT;
|
||||
|
||||
public:
|
||||
|
||||
JointBilateralFilter_8u(Mat& joint_, Mat& src_, Mat& dst_, int radius_,
|
||||
int maxk_, int *spaceOfs_, float *spaceWeights_, float *expLUT_)
|
||||
:
|
||||
joint(joint_), src(src_), dst(dst_), radius(radius_), maxk(maxk_),
|
||||
spaceOfs(spaceOfs_), spaceWeights(spaceWeights_), expLUT(expLUT_)
|
||||
{
|
||||
CV_DbgAssert(joint.type() == traits::Type<JointVec>::value && src.type() == dst.type() && src.type() == traits::Type<SrcVec>::value);
|
||||
CV_DbgAssert(joint.rows == src.rows && src.rows == dst.rows + 2 * radius);
|
||||
CV_DbgAssert(joint.cols == src.cols && src.cols == dst.cols + 2 * radius);
|
||||
}
|
||||
|
||||
void operator () (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
typedef Vec<int, JointVec::channels> JointVeci;
|
||||
typedef Vec<float, SrcVec::channels> SrcVecf;
|
||||
|
||||
for (int i = radius + range.start; i < radius + range.end; i++)
|
||||
{
|
||||
for (int j = radius; j < src.cols - radius; j++)
|
||||
{
|
||||
JointVec *jointCenterPixPtr = joint.ptr<JointVec>(i) + j;
|
||||
SrcVec *srcCenterPixPtr = src.ptr<SrcVec>(i) + j;
|
||||
|
||||
JointVeci jointPix0 = JointVeci(*jointCenterPixPtr);
|
||||
SrcVecf srcSum = SrcVecf::all(0.0f);
|
||||
float wSum = 0.0f;
|
||||
|
||||
for (int k = 0; k < maxk; k++)
|
||||
{
|
||||
uchar *jointPix = reinterpret_cast<uchar*>(jointCenterPixPtr + spaceOfs[k]);
|
||||
int alpha = 0;
|
||||
for (int cn = 0; cn < JointVec::channels; cn++)
|
||||
alpha += std::abs(jointPix0[cn] - (int)jointPix[cn]);
|
||||
|
||||
float weight = spaceWeights[k] * expLUT[alpha];
|
||||
|
||||
uchar *srcPix = reinterpret_cast<uchar*>(srcCenterPixPtr + spaceOfs[k]);
|
||||
for (int cn = 0; cn < SrcVec::channels; cn++)
|
||||
srcSum[cn] += weight*srcPix[cn];
|
||||
wSum += weight;
|
||||
}
|
||||
|
||||
dst.at<SrcVec>(i - radius, j - radius) = SrcVec(srcSum / wSum);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void jointBilateralFilter_8u(Mat& joint, Mat& src, Mat& dst, int radius, double sigmaColor, double sigmaSpace, int borderType)
|
||||
{
|
||||
CV_DbgAssert(joint.depth() == CV_8U && src.depth() == CV_8U);
|
||||
|
||||
int d = 2 * radius + 1;
|
||||
int jCn = joint.channels();
|
||||
|
||||
double gaussColorCoeff = -0.5 / (sigmaColor*sigmaColor);
|
||||
double gaussSpaceCoeff = -0.5 / (sigmaSpace*sigmaSpace);
|
||||
|
||||
vector<float> expLUTv(jCn*256);
|
||||
float *expLUT = &expLUTv[0];
|
||||
|
||||
for (int i = 0; i < (int)expLUTv.size(); i++)
|
||||
{
|
||||
expLUT[i] = (float)std::exp(i * i * gaussColorCoeff);
|
||||
}
|
||||
|
||||
Mat jointTemp, srcTemp;
|
||||
copyMakeBorder(joint, jointTemp, radius, radius, radius, radius, borderType);
|
||||
copyMakeBorder(src, srcTemp, radius, radius, radius, radius, borderType);
|
||||
size_t srcElemStep = srcTemp.step / srcTemp.elemSize();
|
||||
size_t jElemStep = jointTemp.step / jointTemp.elemSize();
|
||||
CV_Assert(srcElemStep == jElemStep);
|
||||
|
||||
vector<float> spaceWeightsv(d*d);
|
||||
vector<int> spaceOfsJointv(d*d);
|
||||
float *spaceWeights = &spaceWeightsv[0];
|
||||
int *spaceOfsJoint = &spaceOfsJointv[0];
|
||||
|
||||
int maxk = 0;
|
||||
for (int i = -radius; i <= radius; i++)
|
||||
{
|
||||
for (int j = -radius; j <= radius; j++)
|
||||
{
|
||||
double r2 = i*i + j*j;
|
||||
if (r2 > SQR(radius))
|
||||
continue;
|
||||
|
||||
spaceWeights[maxk] = (float)std::exp(r2 * gaussSpaceCoeff);
|
||||
spaceOfsJoint[maxk] = (int)(i*jElemStep + j);
|
||||
maxk++;
|
||||
}
|
||||
}
|
||||
|
||||
Range range(0, src.rows);
|
||||
if (joint.type() == CV_8UC1)
|
||||
{
|
||||
if (src.type() == CV_8UC1)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_8u<Vec1b, Vec1b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
if (src.type() == CV_8UC3)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_8u<Vec1b, Vec3b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
}
|
||||
|
||||
if (joint.type() == CV_8UC3)
|
||||
{
|
||||
if (src.type() == CV_8UC1)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_8u<Vec3b, Vec1b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
if (src.type() == CV_8UC3)
|
||||
{
|
||||
parallel_for_(range, JointBilateralFilter_8u<Vec3b, Vec3b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jointBilateralFilter(InputArray joint_, InputArray src_, OutputArray dst_, int d, double sigmaColor, double sigmaSpace, int borderType)
|
||||
{
|
||||
CV_Assert(!src_.empty());
|
||||
|
||||
if (joint_.empty())
|
||||
{
|
||||
bilateralFilter(src_, dst_, d, sigmaColor, sigmaSpace, borderType);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat src = src_.getMat();
|
||||
Mat joint = joint_.getMat();
|
||||
|
||||
if (src.data == joint.data)
|
||||
{
|
||||
bilateralFilter(src_, dst_, d, sigmaColor, sigmaSpace, borderType);
|
||||
return;
|
||||
}
|
||||
|
||||
CV_Assert(src.size() == joint.size());
|
||||
CV_Assert(src.depth() == joint.depth() && (src.depth() == CV_8U || src.depth() == CV_32F) );
|
||||
|
||||
if (sigmaColor <= 0)
|
||||
sigmaColor = 1;
|
||||
if (sigmaSpace <= 0)
|
||||
sigmaSpace = 1;
|
||||
|
||||
int radius;
|
||||
if (d <= 0)
|
||||
radius = cvRound(sigmaSpace*1.5);
|
||||
else
|
||||
radius = d / 2;
|
||||
radius = std::max(radius, 1);
|
||||
|
||||
dst_.create(src.size(), src.type());
|
||||
Mat dst = dst_.getMat();
|
||||
|
||||
if (dst.data == joint.data)
|
||||
joint = joint.clone();
|
||||
if (dst.data == src.data)
|
||||
src = src.clone();
|
||||
|
||||
int jointCnNum = joint.channels();
|
||||
int srcCnNum = src.channels();
|
||||
|
||||
if ( (srcCnNum == 1 || srcCnNum == 3) && (jointCnNum == 1 || jointCnNum == 3) )
|
||||
{
|
||||
if (joint.depth() == CV_8U)
|
||||
{
|
||||
jointBilateralFilter_8u(joint, src, dst, radius, sigmaColor, sigmaSpace, borderType);
|
||||
}
|
||||
else
|
||||
{
|
||||
jointBilateralFilter_32f(joint, src, dst, radius, sigmaColor, sigmaSpace, borderType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::BadNumChannels, "Unsupported number of channels");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <vector>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class ParallelDft : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
vector<Mat> src_;
|
||||
public:
|
||||
ParallelDft(vector<Mat> &s)
|
||||
{
|
||||
src_ = s;
|
||||
}
|
||||
void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int i = range.start; i != range.end; i++)
|
||||
{
|
||||
dft(src_[i], src_[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ParallelIdft : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
vector<Mat> src_;
|
||||
public:
|
||||
ParallelIdft(vector<Mat> &s)
|
||||
{
|
||||
src_ = s;
|
||||
}
|
||||
void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int i = range.start; i != range.end; i++)
|
||||
{
|
||||
idft(src_[i], src_[i],DFT_SCALE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ParallelDivComplexByReal : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
vector<Mat> numer_;
|
||||
vector<Mat> denom_;
|
||||
vector<Mat> dst_;
|
||||
|
||||
public:
|
||||
ParallelDivComplexByReal(vector<Mat> &numer, vector<Mat> &denom, vector<Mat> &dst)
|
||||
{
|
||||
numer_ = numer;
|
||||
denom_ = denom;
|
||||
dst_ = dst;
|
||||
}
|
||||
void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int i = range.start; i != range.end; i++)
|
||||
{
|
||||
Mat aPanels[2];
|
||||
Mat bPanels[2];
|
||||
split(numer_[i], aPanels);
|
||||
split(denom_[i], bPanels);
|
||||
|
||||
Mat realPart;
|
||||
Mat imaginaryPart;
|
||||
|
||||
divide(aPanels[0], denom_[i], realPart);
|
||||
divide(aPanels[1], denom_[i], imaginaryPart);
|
||||
|
||||
aPanels[0] = realPart;
|
||||
aPanels[1] = imaginaryPart;
|
||||
|
||||
merge(aPanels, 2, dst_[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void shift(InputArray src, OutputArray dst, int shift_x, int shift_y)
|
||||
{
|
||||
Mat S = src.getMat();
|
||||
Mat D = dst.getMat();
|
||||
|
||||
if(S.data == D.data)
|
||||
{
|
||||
S = S.clone();
|
||||
}
|
||||
|
||||
D.create(S.size(), S.type());
|
||||
|
||||
Mat s0(S, Rect(0, 0, S.cols - shift_x, S.rows - shift_y));
|
||||
Mat s1(S, Rect(S.cols - shift_x, 0, shift_x, S.rows - shift_y));
|
||||
Mat s2(S, Rect(0, S.rows - shift_y, S.cols-shift_x, shift_y));
|
||||
Mat s3(S, Rect(S.cols - shift_x, S.rows- shift_y, shift_x, shift_y));
|
||||
|
||||
Mat d0(D, Rect(shift_x, shift_y, S.cols - shift_x, S.rows - shift_y));
|
||||
Mat d1(D, Rect(0, shift_y, shift_x, S.rows - shift_y));
|
||||
Mat d2(D, Rect(shift_x, 0, S.cols-shift_x, shift_y));
|
||||
Mat d3(D, Rect(0,0,shift_x, shift_y));
|
||||
|
||||
s0.copyTo(d0);
|
||||
s1.copyTo(d1);
|
||||
s2.copyTo(d2);
|
||||
s3.copyTo(d3);
|
||||
}
|
||||
|
||||
// dft after padding imaginary
|
||||
void fft(InputArray src, OutputArray dst)
|
||||
{
|
||||
Mat S = src.getMat();
|
||||
Mat planes[] = {S.clone(), Mat::zeros(S.size(), S.type())};
|
||||
Mat x;
|
||||
merge(planes, 2, dst);
|
||||
|
||||
// compute the result
|
||||
dft(dst, dst);
|
||||
}
|
||||
|
||||
void psf2otf(InputArray src, OutputArray dst, int height, int width)
|
||||
{
|
||||
Mat S = src.getMat();
|
||||
Mat D = dst.getMat();
|
||||
|
||||
Mat padded;
|
||||
|
||||
if(S.data == D.data){
|
||||
S = S.clone();
|
||||
}
|
||||
|
||||
// add padding
|
||||
copyMakeBorder(S, padded, 0, height - S.rows, 0, width - S.cols,
|
||||
BORDER_CONSTANT, Scalar::all(0));
|
||||
|
||||
shift(padded, padded, width - S.cols / 2, height - S.rows / 2);
|
||||
|
||||
// convert to frequency domain
|
||||
fft(padded, dst);
|
||||
}
|
||||
|
||||
void dftMultiChannel(InputArray src, vector<Mat> &dst)
|
||||
{
|
||||
Mat S = src.getMat();
|
||||
|
||||
split(S, dst);
|
||||
|
||||
for(int i = 0; i < S.channels(); i++){
|
||||
Mat planes[] = {dst[i].clone(), Mat::zeros(dst[i].size(), dst[i].type())};
|
||||
merge(planes, 2, dst[i]);
|
||||
}
|
||||
|
||||
parallel_for_(cv::Range(0,S.channels()), ParallelDft(dst));
|
||||
|
||||
}
|
||||
|
||||
void idftMultiChannel(const vector<Mat> &src, OutputArray dst)
|
||||
{
|
||||
vector<Mat> channels(src);
|
||||
|
||||
parallel_for_(Range(0, int(src.size())), ParallelIdft(channels));
|
||||
|
||||
for(int i = 0; unsigned(i) < src.size(); i++){
|
||||
Mat panels[2];
|
||||
split(channels[i], panels);
|
||||
channels[i] = panels[0];
|
||||
}
|
||||
|
||||
Mat D;
|
||||
merge(channels, D);
|
||||
D.copyTo(dst);
|
||||
}
|
||||
|
||||
void divComplexByRealMultiChannel(vector<Mat> &numer,
|
||||
vector<Mat> &denom, vector<Mat> &dst)
|
||||
{
|
||||
|
||||
for(int i = 0; unsigned(i) < numer.size(); i++)
|
||||
{
|
||||
dst[i].create(numer[i].size(), numer[i].type());
|
||||
}
|
||||
parallel_for_(Range(0, int(numer.size())), ParallelDivComplexByReal(numer, denom, dst));
|
||||
|
||||
}
|
||||
|
||||
// power of 2 of the absolute value of the complex
|
||||
Mat pow2absComplex(InputArray src)
|
||||
{
|
||||
Mat S = src.getMat();
|
||||
|
||||
Mat sPanels[2];
|
||||
split(S, sPanels);
|
||||
|
||||
Mat mag;
|
||||
magnitude(sPanels[0], sPanels[1], mag);
|
||||
pow(mag, 2, mag);
|
||||
|
||||
return mag;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
void l0Smooth(InputArray src, OutputArray dst, double lambda, double kappa)
|
||||
{
|
||||
Mat S = src.getMat();
|
||||
|
||||
CV_Assert(!S.empty());
|
||||
CV_Assert(S.depth() == CV_8U || S.depth() == CV_16U
|
||||
|| S.depth() == CV_32F || S.depth() == CV_64F);
|
||||
CV_Assert(lambda > 0.0);
|
||||
CV_Assert(kappa > 1.0);
|
||||
|
||||
dst.create(src.size(), src.type());
|
||||
|
||||
if(S.data == dst.getMat().data)
|
||||
{
|
||||
S = S.clone();
|
||||
}
|
||||
|
||||
if(S.depth() == CV_8U)
|
||||
{
|
||||
S.convertTo(S, CV_32F, 1/255.0f);
|
||||
}
|
||||
else if(S.depth() == CV_16U)
|
||||
{
|
||||
S.convertTo(S, CV_32F, 1/65535.0f);
|
||||
}
|
||||
else if(S.depth() == CV_64F)
|
||||
{
|
||||
S.convertTo(S, CV_32F);
|
||||
}
|
||||
|
||||
const double betaMax = 100000;
|
||||
|
||||
// gradient operators in frequency domain
|
||||
Mat otfFx, otfFy;
|
||||
float kernel[2] = {-1, 1};
|
||||
float kernel_inv[2] = {1,-1};
|
||||
psf2otf(Mat(1,2,CV_32FC1, kernel_inv), otfFx, S.rows, S.cols);
|
||||
psf2otf(Mat(2,1,CV_32FC1, kernel_inv), otfFy, S.rows, S.cols);
|
||||
|
||||
vector<Mat> denomConst;
|
||||
Mat tmp = pow2absComplex(otfFx) + pow2absComplex(otfFy);
|
||||
|
||||
for(int i = 0; i < S.channels(); i++)
|
||||
{
|
||||
denomConst.push_back(tmp);
|
||||
}
|
||||
|
||||
// input image in frequency domain
|
||||
vector<Mat> numerConst;
|
||||
dftMultiChannel(S, numerConst);
|
||||
/*********************************
|
||||
* solver
|
||||
*********************************/
|
||||
double beta = 2 * lambda;
|
||||
while(beta < betaMax){
|
||||
// h, v subproblem
|
||||
Mat h, v;
|
||||
|
||||
filter2D(S, h, -1, Mat(1, 2, CV_32FC1, kernel), Point(0, 0),
|
||||
0, BORDER_REPLICATE);
|
||||
filter2D(S, v, -1, Mat(2, 1, CV_32FC1, kernel), Point(0, 0),
|
||||
0, BORDER_REPLICATE);
|
||||
|
||||
Mat hvMag = h.mul(h) + v.mul(v);
|
||||
|
||||
Mat mask;
|
||||
if(S.channels() == 1)
|
||||
{
|
||||
threshold(hvMag, mask, lambda/beta, 1, THRESH_BINARY);
|
||||
}
|
||||
else if(S.channels() > 1)
|
||||
{
|
||||
vector<Mat> channels(S.channels());
|
||||
split(hvMag, channels);
|
||||
hvMag = channels[0];
|
||||
|
||||
for(int i = 1; i < S.channels(); i++)
|
||||
{
|
||||
hvMag = hvMag + channels[i];
|
||||
}
|
||||
|
||||
threshold(hvMag, mask, lambda/beta, 1, THRESH_BINARY);
|
||||
|
||||
Mat in[] = {mask, mask, mask};
|
||||
merge(in, 3, mask);
|
||||
}
|
||||
|
||||
h = h.mul(mask);
|
||||
v = v.mul(mask);
|
||||
|
||||
// S subproblem
|
||||
vector<Mat> denom(S.channels());
|
||||
for(int i = 0; i < S.channels(); i++)
|
||||
{
|
||||
denom[i] = beta * denomConst[i] + 1;
|
||||
}
|
||||
|
||||
Mat hGrad, vGrad;
|
||||
filter2D(h, hGrad, -1, Mat(1, 2, CV_32FC1, kernel_inv));
|
||||
filter2D(v, vGrad, -1, Mat(2, 1, CV_32FC1, kernel_inv));
|
||||
|
||||
vector<Mat> hvGradFreq;
|
||||
dftMultiChannel(hGrad+vGrad, hvGradFreq);
|
||||
|
||||
vector<Mat> numer(S.channels());
|
||||
for(int i = 0; i < S.channels(); i++)
|
||||
{
|
||||
numer[i] = numerConst[i] + hvGradFreq[i] * beta;
|
||||
}
|
||||
|
||||
vector<Mat> sFreq(S.channels());
|
||||
divComplexByRealMultiChannel(numer, denom, sFreq);
|
||||
|
||||
idftMultiChannel(sFreq, S);
|
||||
|
||||
beta = beta * kappa;
|
||||
}
|
||||
|
||||
Mat D = dst.getMat();
|
||||
if(D.depth() == CV_8U)
|
||||
{
|
||||
S.convertTo(D, CV_8U, 255);
|
||||
}
|
||||
else if(D.depth() == CV_16U)
|
||||
{
|
||||
S.convertTo(D, CV_16U, 65535);
|
||||
}
|
||||
else if(D.depth() == CV_64F)
|
||||
{
|
||||
S.convertTo(D, CV_64F);
|
||||
}
|
||||
else
|
||||
{
|
||||
S.copyTo(D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2014, Beat Kueng (beat-kueng@gmx.net), Lukas Vogel, Morten Lysgaard
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
void niBlackThreshold( InputArray _src, OutputArray _dst, double maxValue,
|
||||
int type, int blockSize, double k, int binarizationMethod, double r)
|
||||
{
|
||||
// Input grayscale image
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(src.channels() == 1);
|
||||
CV_Assert(blockSize % 2 == 1 && blockSize > 1);
|
||||
if (binarizationMethod == BINARIZATION_SAUVOLA) {
|
||||
CV_Assert(src.depth() == CV_8U);
|
||||
CV_Assert(r != 0);
|
||||
}
|
||||
type &= THRESH_MASK;
|
||||
|
||||
// Compute local threshold (T = mean + k * stddev)
|
||||
// using mean and standard deviation in the neighborhood of each pixel
|
||||
// (intermediate calculations are done with floating-point precision)
|
||||
Mat thresh;
|
||||
{
|
||||
// note that: Var[X] = E[X^2] - E[X]^2
|
||||
Mat mean, sqmean, variance, stddev, sqrtVarianceMeanSum;
|
||||
double srcMin, stddevMax;
|
||||
boxFilter(src, mean, CV_32F, Size(blockSize, blockSize),
|
||||
Point(-1,-1), true, BORDER_REPLICATE);
|
||||
sqrBoxFilter(src, sqmean, CV_32F, Size(blockSize, blockSize),
|
||||
Point(-1,-1), true, BORDER_REPLICATE);
|
||||
variance = sqmean - mean.mul(mean);
|
||||
sqrt(variance, stddev);
|
||||
switch (binarizationMethod)
|
||||
{
|
||||
case BINARIZATION_NIBLACK:
|
||||
thresh = mean + stddev * static_cast<float>(k);
|
||||
break;
|
||||
case BINARIZATION_SAUVOLA:
|
||||
thresh = mean.mul(1. + static_cast<float>(k) * (stddev / r - 1.));
|
||||
break;
|
||||
case BINARIZATION_WOLF:
|
||||
minMaxIdx(src, &srcMin);
|
||||
minMaxIdx(stddev, NULL, &stddevMax);
|
||||
thresh = mean - static_cast<float>(k) * (mean - srcMin - stddev.mul(mean - srcMin) / stddevMax);
|
||||
break;
|
||||
case BINARIZATION_NICK:
|
||||
sqrt(variance + sqmean, sqrtVarianceMeanSum);
|
||||
thresh = mean + static_cast<float>(k) * sqrtVarianceMeanSum;
|
||||
break;
|
||||
default:
|
||||
CV_Error( Error::StsBadArg, "Unknown binarization method" );
|
||||
break;
|
||||
}
|
||||
thresh.convertTo(thresh, src.depth());
|
||||
}
|
||||
|
||||
// Prepare output image
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
CV_Assert(src.data != dst.data); // no inplace processing
|
||||
|
||||
// Apply thresholding: ( pixel > threshold ) ? foreground : background
|
||||
Mat mask;
|
||||
switch (type)
|
||||
{
|
||||
case THRESH_BINARY: // dst = (src > thresh) ? maxval : 0
|
||||
case THRESH_BINARY_INV: // dst = (src > thresh) ? 0 : maxval
|
||||
compare(src, thresh, mask, (type == THRESH_BINARY ? CMP_GT : CMP_LE));
|
||||
dst.setTo(0);
|
||||
dst.setTo(maxValue, mask);
|
||||
break;
|
||||
case THRESH_TRUNC: // dst = (src > thresh) ? thresh : src
|
||||
compare(src, thresh, mask, CMP_GT);
|
||||
src.copyTo(dst);
|
||||
thresh.copyTo(dst, mask);
|
||||
break;
|
||||
case THRESH_TOZERO: // dst = (src > thresh) ? src : 0
|
||||
case THRESH_TOZERO_INV: // dst = (src > thresh) ? 0 : src
|
||||
compare(src, thresh, mask, (type == THRESH_TOZERO ? CMP_GT : CMP_LE));
|
||||
dst.setTo(0);
|
||||
src.copyTo(dst, mask);
|
||||
break;
|
||||
default:
|
||||
CV_Error( Error::StsBadArg, "Unknown threshold type" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ximgproc
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,39 @@
|
||||
__kernel void anisodiff(__global const uchar * srcptr, int srcstep, int srcoffset,
|
||||
__global uchar * dstptr, int dststep, int dstoffset,
|
||||
int rows, int cols, __constant float* exptab, float alpha)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if( x < cols && y < rows )
|
||||
{
|
||||
int yofs = y*dststep + x*3;
|
||||
int xofs = y*srcstep + x*3;
|
||||
float4 s = 0.f;
|
||||
|
||||
float4 c = (float4)(srcptr[xofs], srcptr[xofs+1], srcptr[xofs+2], 0.f);
|
||||
float4 delta, adelta;
|
||||
float w;
|
||||
|
||||
#define UPDATE_SUM(xofs1) \
|
||||
delta = (float4)(srcptr[xofs + xofs1], srcptr[xofs + xofs1 + 1], srcptr[xofs + xofs1 + 2], 0.f) - c; \
|
||||
adelta = fabs(delta); \
|
||||
w = exptab[convert_int(adelta.x + adelta.y + adelta.z)]; \
|
||||
s += delta*w
|
||||
|
||||
UPDATE_SUM(3);
|
||||
UPDATE_SUM(-3);
|
||||
UPDATE_SUM(-srcstep-3);
|
||||
UPDATE_SUM(-srcstep);
|
||||
UPDATE_SUM(-srcstep+3);
|
||||
UPDATE_SUM(srcstep-3);
|
||||
UPDATE_SUM(srcstep);
|
||||
UPDATE_SUM(srcstep+3);
|
||||
|
||||
s = s*alpha + c;
|
||||
uchar4 d = convert_uchar4_sat(convert_int4_rte(s));
|
||||
dstptr[yofs] = d.x;
|
||||
dstptr[yofs+1] = d.y;
|
||||
dstptr[yofs+2] = d.z;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
#include "precomp.hpp"
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
/*
|
||||
If you use this code please cite this @cite paillou1997detecting
|
||||
Detecting step edges in noisy SAR images: a new linear operator IEEE Transactions on Geoscience and Remote Sensing (Volume:35 , Issue: 1 ) 1997
|
||||
Equation xx pyyy mean equation xx at page yyy in previous paper
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
template<typename T> static void
|
||||
VerticalIIRFilter(Mat &img, Mat &dst, const Range &range, double a,double w)
|
||||
{
|
||||
float *f2;
|
||||
int tailleSequence = (img.rows>img.cols) ? img.rows : img.cols;
|
||||
Mat matYp(1, tailleSequence, CV_64FC1), matYm(1, tailleSequence, CV_64FC1);
|
||||
double *yp = matYp.ptr<double>(0), *ym = matYm.ptr<double>(0);
|
||||
int rows = img.rows, cols = img.cols;
|
||||
|
||||
// Equation 12 p193
|
||||
double b1 = -2 * exp(-a)*cosh(w);
|
||||
double a1 = 2 * exp(-a)*cosh(w) - exp(-2 * a) - 1;
|
||||
double b2 = exp(-2 * a);
|
||||
for (int j = range.start; j < range.end; j++)
|
||||
{
|
||||
// Equation 26 p194
|
||||
T *c1 = (T *)img.ptr(0) + j;
|
||||
f2 = dst.ptr<float>(0) + j;
|
||||
double border = *c1;
|
||||
yp[0] = *c1;
|
||||
c1 += cols;
|
||||
yp[1] = *c1 - b1*yp[0] - b2*border;
|
||||
c1 += cols;
|
||||
for (int i = 2; i < rows; i++, c1 += cols)
|
||||
yp[i] = *c1 - b1*yp[i - 1] - b2*yp[i - 2];
|
||||
// Equation 27 p194
|
||||
c1 = (T *)img.ptr(rows - 1) + j;
|
||||
border = *c1;
|
||||
ym[rows - 1] = *c1;
|
||||
c1 -= cols;
|
||||
ym[rows - 2] = *c1 - b1*ym[rows - 1];
|
||||
c1 -= cols;
|
||||
for (int i = rows - 3; i >= 0; i--, c1 -= cols)
|
||||
ym[i] = *c1 - b1*ym[i + 1] - b2*ym[i + 2];
|
||||
// Equation 25 p193
|
||||
for (int i = 0; i < rows; i++, f2 += cols)
|
||||
*f2 = (float)(a1*(ym[i] - yp[i]));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> static void
|
||||
HorizontalIIRFilter(Mat &img, Mat &dst, const Range &range, double a, double w)
|
||||
{
|
||||
int tailleSequence = (img.rows>img.cols) ? img.rows : img.cols;
|
||||
Mat matYp(1, tailleSequence, CV_64FC1), matYm(1, tailleSequence, CV_64FC1);
|
||||
double *yp = matYp.ptr<double>(0), *ym = matYm.ptr<double>(0);
|
||||
int cols = img.cols;
|
||||
float *f2;
|
||||
|
||||
// Equation 12 p193
|
||||
double b1 = -2 * exp(-a)*cosh(w);
|
||||
double a1 = 2 * exp(-a)*cosh(w) - exp(-2 * a) - 1;
|
||||
double b2 = exp(-2 * a);
|
||||
|
||||
for (int i = range.start; i<range.end; i++)
|
||||
{
|
||||
// Equation 26 p194
|
||||
T *c1 = img.ptr<T>(i);
|
||||
double border = *c1;
|
||||
yp[0] = *c1;
|
||||
c1++;
|
||||
yp[1] = *c1 - b1*yp[0] - b2*border;
|
||||
c1++;
|
||||
for (int j = 2; j<cols; j++, c1++)
|
||||
yp[j] = *c1 - b1*yp[j - 1] - b2*yp[j - 2];
|
||||
// Equation 27 p194
|
||||
c1 = img.ptr<T>(i) + cols - 1;
|
||||
border = *c1;
|
||||
ym[cols - 1] = *c1;
|
||||
c1--;
|
||||
ym[cols - 2] = *c1 - b1*ym[cols - 1];
|
||||
c1--;
|
||||
for (int j = cols - 3; j >= 0; j--, c1--)
|
||||
ym[j] = *c1 - b1*ym[j + 1] - b2*ym[j + 2];
|
||||
// Equation 25 p193
|
||||
f2 = dst.ptr<float>(i);
|
||||
for (int j = 0; j<cols; j++, f2++)
|
||||
*f2 = (float)(a1*(ym[j] - yp[j]));
|
||||
}
|
||||
}
|
||||
|
||||
class ParallelGradientPaillouYCols: public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double a;
|
||||
double w;
|
||||
bool verbose;
|
||||
public:
|
||||
ParallelGradientPaillouYCols(Mat& imgSrc, Mat &d,double aa,double ww):
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
a(aa),
|
||||
w(ww),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_8UC1 || type == CV_8SC1 || type == CV_16SC1 || type == CV_16UC1 || type == CV_32FC1, "Wrong input type for GradientPaillouY");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientPaillouYCols");
|
||||
}
|
||||
void Verbose(bool b){verbose=b;}
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum()<<"# :Start from row " << range.start << " to " << range.end-1<<" ("<<range.end-range.start<<" loops)" << std::endl;
|
||||
|
||||
switch(img.depth()){
|
||||
case CV_8U:
|
||||
VerticalIIRFilter<uchar>(img, dst, range, a, w);
|
||||
break;
|
||||
case CV_8S:
|
||||
VerticalIIRFilter<char>(img, dst, range, a, w);
|
||||
break;
|
||||
case CV_16S :
|
||||
VerticalIIRFilter<short>(img, dst, range, a, w);
|
||||
break;
|
||||
case CV_16U:
|
||||
VerticalIIRFilter<short>(img, dst, range, a, w);
|
||||
break;
|
||||
case CV_32F:
|
||||
VerticalIIRFilter<float>(img, dst, range, a, w);
|
||||
break;
|
||||
default :
|
||||
return ;
|
||||
}
|
||||
};
|
||||
ParallelGradientPaillouYCols& operator=(const ParallelGradientPaillouYCols &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ParallelGradientPaillouYRows: public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double a;
|
||||
double w;
|
||||
bool verbose;
|
||||
|
||||
public:
|
||||
ParallelGradientPaillouYRows(Mat& imgSrc, Mat &d,double aa,double ww):
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
a(aa),
|
||||
w(ww),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong input type for GradientPaillouYRows");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientPaillouYRows");
|
||||
}
|
||||
void Verbose(bool b){verbose=b;}
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum()<<"# :Start from row " << range.start << " to " << range.end-1<<" ("<<range.end-range.start<<" loops)" << std::endl;
|
||||
float *iy,*iy0;
|
||||
int tailleSequence=(img.rows>img.cols)?img.rows:img.cols;
|
||||
Mat matIym(1,tailleSequence,CV_64FC1), matIyp(1,tailleSequence,CV_64FC1);
|
||||
double *iym=matIym.ptr<double>(0), *iyp=matIyp.ptr<double>(0);
|
||||
int cols=img.cols;
|
||||
|
||||
// Equation 13 p193
|
||||
double d=(1-2*exp(-a)*cosh(w)+exp(-2*a))/(2*a*exp(-a)*sinh(w)+w*(1-exp(-2*a)));
|
||||
double c1=a*d;
|
||||
double c2=w*d;
|
||||
// Equation 12 p193
|
||||
double b1=-2*exp(-a)*cosh(w);
|
||||
double b2=exp(-2*a);
|
||||
// Equation 14 p193
|
||||
double a0p=c2;
|
||||
double a1p=(c1*sinh(w)-c2*cosh(w))*exp(-a);
|
||||
double a1m=a1p-c2*b1;
|
||||
double a2m=-c2*b2;
|
||||
|
||||
for (int i=range.start;i<range.end;i++)
|
||||
{
|
||||
iy0 = img.ptr<float>(i);
|
||||
int j=0;
|
||||
iyp[0] = a0p*iy0[0] ;
|
||||
iyp[1] = a0p*iy0[1] + a1p*iy0[0] - b1*iyp[0];
|
||||
iy0 += 2;
|
||||
for (j=2;j<cols;j++,iy0++)
|
||||
iyp[j] = a0p*iy0[0] + a1p*iy0[-1] - b1*iyp[j-1] - b2*iyp[j-2];
|
||||
iy0 = img.ptr<float>(i)+cols-1;
|
||||
iym[cols-1] = 0;
|
||||
iy0--;
|
||||
iym[cols-2] = a1m*iy0[1] - b1*iym[cols-1];
|
||||
iy0--;
|
||||
for (j=cols-3;j>=0;j--,iy0--)
|
||||
iym[j] = a1m*iy0[1] + a2m*iy0[2] - b1*iym[j+1] - b2*iym[j+2];
|
||||
iy = dst.ptr<float>(i);
|
||||
for (j=0;j<cols;j++,iy++)
|
||||
*iy = (float)(iym[j]+iyp[j]);
|
||||
}
|
||||
|
||||
};
|
||||
ParallelGradientPaillouYRows& operator=(const ParallelGradientPaillouYRows &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ParallelGradientPaillouXCols: public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &dst;
|
||||
double a;
|
||||
double w;
|
||||
bool verbose;
|
||||
|
||||
public:
|
||||
ParallelGradientPaillouXCols(Mat& imgSrc, Mat &d,double aa,double ww):
|
||||
img(imgSrc),
|
||||
dst(d),
|
||||
a(aa),
|
||||
w(ww),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong input type for GradientPaillouXCols");
|
||||
type = dst.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientPaillouXCols");
|
||||
}
|
||||
void Verbose(bool b){verbose=b;}
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum() << "# :Start from row " << range.start << " to " << range.end - 1 << " (" << range.end - range.start << " loops)" << std::endl;
|
||||
float *iy, *iy0;
|
||||
int tailleSequence = (img.rows>img.cols) ? img.rows : img.cols;
|
||||
Mat matIym(1,tailleSequence,CV_64FC1), matIyp(1,tailleSequence,CV_64FC1);
|
||||
double *iym=matIym.ptr<double>(0), *iyp=matIyp.ptr<double>(0);
|
||||
int rows = img.rows,cols=img.cols;
|
||||
|
||||
// Equation 13 p193
|
||||
double d = (1 - 2 * exp(-a)*cosh(w) + exp(-2 * a)) / (2 * a*exp(-a)*sinh(w) + w*(1 - exp(-2 * a)));
|
||||
double c1 = a*d;
|
||||
double c2 = w*d;
|
||||
// Equation 12 p193
|
||||
double b1 = -2 * exp(-a)*cosh(w);
|
||||
double b2 = exp(-2 * a);
|
||||
// Equation 14 p193
|
||||
double a0p = c2;
|
||||
double a1p = (c1*sinh(w) - c2*cosh(w))*exp(-a);
|
||||
double a1m = a1p - c2*b1;
|
||||
double a2m = -c2*b2;
|
||||
|
||||
for (int j = range.start; j<range.end; j++)
|
||||
{
|
||||
iy0 = img.ptr<float>(0)+j;
|
||||
iyp[0] = a0p*iy0[0];
|
||||
iy0 +=cols;
|
||||
iyp[1] = a0p*iy0[0] + a1p*iy0[-cols] - b1*iyp[0];
|
||||
iy0 +=cols;
|
||||
for (int i = 2; i<rows; i++, iy0+=cols)
|
||||
iyp[i] = a0p*iy0[0] + a1p*iy0[-cols] - b1*iyp[i - 1] - b2*iyp[i - 2];
|
||||
iy0 = img.ptr<float>(rows-1) + j;
|
||||
iym[rows - 1] = 0;
|
||||
iy0 -=cols;
|
||||
iym[rows - 2] = a1m*iy0[cols] - b1*iym[rows-1];
|
||||
iy0-=cols;
|
||||
for (int i = rows - 3; i >= 0; i--, iy0-=cols)
|
||||
iym[i] = a1m*iy0[cols] + a2m*iy0[2*cols] - b1*iym[i + 1] - b2*iym[i + 2];
|
||||
iy = dst.ptr<float>(0)+j;
|
||||
for (int i = 0; i<rows; i++, iy+=cols)
|
||||
*iy = (float)(iym[i] + iyp[i]);
|
||||
}
|
||||
|
||||
};
|
||||
ParallelGradientPaillouXCols& operator=(const ParallelGradientPaillouXCols &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ParallelGradientPaillouXRows: public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Mat &img;
|
||||
Mat &im1;
|
||||
double a;
|
||||
double w;
|
||||
bool verbose;
|
||||
|
||||
public:
|
||||
ParallelGradientPaillouXRows(Mat& imgSrc, Mat &d,double aa,double ww):
|
||||
img(imgSrc),
|
||||
im1(d),
|
||||
a(aa),
|
||||
w(ww),
|
||||
verbose(false)
|
||||
{
|
||||
int type = img.depth();
|
||||
CV_CheckType(type, type == CV_8UC1 || type == CV_8SC1 || type == CV_16SC1 || type == CV_16UC1 || type == CV_32FC1, "Wrong input type for GradientPaillouXRows");
|
||||
type = im1.depth();
|
||||
CV_CheckType(type, type == CV_32FC1, "Wrong output type for GradientPaillouXRows");
|
||||
}
|
||||
void Verbose(bool b){verbose=b;}
|
||||
virtual void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
if (verbose)
|
||||
std::cout << getThreadNum()<<"# :Start from row " << range.start << " to " << range.end-1<<" ("<<range.end-range.start<<" loops)" << std::endl;
|
||||
|
||||
// Equation 12 p193
|
||||
switch(img.depth()){
|
||||
case CV_8U :
|
||||
HorizontalIIRFilter<uchar>(img,im1,range,a,w);
|
||||
break;
|
||||
case CV_8S :
|
||||
HorizontalIIRFilter<char>(img, im1, range, a, w);
|
||||
break;
|
||||
case CV_16S :
|
||||
HorizontalIIRFilter<short>(img, im1, range, a, w);
|
||||
break;
|
||||
case CV_16U:
|
||||
HorizontalIIRFilter<ushort>(img, im1, range, a, w);
|
||||
break;
|
||||
case CV_32F:
|
||||
HorizontalIIRFilter<float>(img, im1, range, a, w);
|
||||
break;
|
||||
default :
|
||||
return ;
|
||||
}
|
||||
};
|
||||
ParallelGradientPaillouXRows& operator=(const ParallelGradientPaillouXRows &) {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
void GradientPaillouY(InputArray _op, OutputArray _dst, double alpha, double omega)
|
||||
{
|
||||
std::vector<Mat> planSrc;
|
||||
split(_op,planSrc);
|
||||
std::vector<Mat> planTmp;
|
||||
std::vector<Mat> planDst;
|
||||
for (int i = 0; i <static_cast<int>(planSrc.size()); i++)
|
||||
{
|
||||
planTmp.push_back(Mat(_op.size(), CV_32FC1));
|
||||
planDst.push_back(Mat(_op.size(), CV_32FC1));
|
||||
CV_Assert(planSrc[i].isContinuous() && planTmp[i].isContinuous() && planDst[i].isContinuous());
|
||||
ParallelGradientPaillouYCols x(planSrc[i],planTmp[i],alpha,omega);
|
||||
parallel_for_(Range(0, planSrc[i].cols), x,getNumThreads());
|
||||
ParallelGradientPaillouYRows xr(planTmp[i],planDst[i],alpha,omega);
|
||||
parallel_for_(Range(0, planTmp[i].rows), xr,getNumThreads());
|
||||
}
|
||||
merge(planDst,_dst);
|
||||
}
|
||||
|
||||
void GradientPaillouX(InputArray _op, OutputArray _dst, double alpha, double omega)
|
||||
{
|
||||
std::vector<Mat> planSrc;
|
||||
split(_op,planSrc);
|
||||
std::vector<Mat> planTmp;
|
||||
std::vector<Mat> planDst;
|
||||
for (int i = 0; i <static_cast<int>(planSrc.size()); i++)
|
||||
{
|
||||
planTmp.push_back(Mat(_op.size(), CV_32FC1));
|
||||
planDst.push_back(Mat(_op.size(), CV_32FC1));
|
||||
CV_Assert(planSrc[i].isContinuous() && planTmp[i].isContinuous() && planDst[i].isContinuous());
|
||||
ParallelGradientPaillouXRows x(planSrc[i],planTmp[i],alpha,omega);
|
||||
parallel_for_(Range(0, planSrc[i].rows), x,getNumThreads());
|
||||
ParallelGradientPaillouXCols xr(planTmp[i],planDst[i],alpha,omega);
|
||||
parallel_for_(Range(0, planTmp[i].cols), xr,getNumThreads());
|
||||
}
|
||||
merge(planDst,_dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv { namespace ximgproc {
|
||||
|
||||
static inline Moments operator& ( const Moments & lhs, const Matx22d & rhs )
|
||||
{
|
||||
return Moments (
|
||||
lhs.m00,
|
||||
rhs ( 0, 0 ) * lhs.m10 + rhs ( 0, 1 ) * lhs.m01,
|
||||
rhs ( 1, 0 ) * lhs.m10 + rhs ( 1, 1 ) * lhs.m01,
|
||||
rhs ( 0, 0 ) * rhs ( 0, 0 ) * lhs.m20 + rhs ( 0, 1 ) * rhs ( 0, 1 ) * lhs.m02 + 2 * rhs ( 0, 0 ) * rhs ( 0, 1 ) * lhs.m11,
|
||||
rhs ( 0, 0 ) * rhs ( 1, 0 ) * lhs.m20 + rhs ( 0, 1 ) * rhs ( 1, 1 ) * lhs.m02 + ( rhs ( 0, 0 ) * rhs ( 1, 1 ) + rhs ( 0, 1 ) * rhs ( 1, 0 ) ) * lhs.m11,
|
||||
rhs ( 1, 0 ) * rhs ( 1, 0 ) * lhs.m20 + rhs ( 1, 1 ) * rhs ( 1, 1 ) * lhs.m02 + 2 * rhs ( 1, 0 ) * rhs ( 1, 1 ) * lhs.m11,
|
||||
rhs ( 0, 0 ) * rhs ( 0, 0 ) * rhs ( 0, 0 ) * lhs.m30 + 3 * rhs ( 0, 0 ) * rhs ( 0, 0 ) * rhs ( 0, 1 ) * lhs.m21 + 3 * rhs ( 0, 0 ) * rhs ( 0, 1 ) * rhs ( 0, 1 ) * lhs.m12 + rhs ( 0, 1 ) * rhs ( 0, 1 ) * rhs ( 0, 1 ) * lhs.m03,
|
||||
rhs ( 0, 0 ) * rhs ( 0, 0 ) * rhs ( 1, 0 ) * lhs.m30 + ( rhs ( 0, 0 ) * rhs ( 0, 0 ) * rhs ( 1, 1 ) + 2 * rhs ( 0, 0 ) * rhs ( 0, 1 ) * rhs ( 1, 0 ) ) * lhs.m21 + ( 2 * rhs ( 0, 0 ) * rhs ( 0, 1 ) * rhs ( 1, 1 ) + rhs ( 0, 1 ) * rhs ( 0, 1 ) * rhs ( 1, 0 ) ) * lhs.m12 + rhs ( 0, 1 ) * rhs ( 0, 1 ) * rhs ( 1, 1 ) * lhs.m03,
|
||||
rhs ( 0, 0 ) * rhs ( 1, 0 ) * rhs ( 1, 0 ) * lhs.m30 + ( rhs ( 1, 0 ) * rhs ( 1, 0 ) * rhs ( 0, 1 ) + 2 * rhs ( 0, 0 ) * rhs ( 1, 0 ) * rhs ( 1, 1 ) ) * lhs.m21 + ( 2 * rhs ( 0, 1 ) * rhs ( 1, 0 ) * rhs ( 1, 1 ) + rhs ( 1, 1 ) * rhs ( 1, 1 ) * rhs ( 0, 0 ) ) * lhs.m12 + rhs ( 0, 1 ) * rhs ( 1, 1 ) * rhs ( 1, 1 ) * lhs.m03,
|
||||
rhs ( 1, 0 ) * rhs ( 1, 0 ) * rhs ( 1, 0 ) * lhs.m30 + 3 * rhs ( 1, 0 ) * rhs ( 1, 0 ) * rhs ( 1, 1 ) * lhs.m21 + 3 * rhs ( 1, 0 ) * rhs ( 1, 1 ) * rhs ( 1, 1 ) * lhs.m12 + rhs ( 1, 1 ) * rhs ( 1, 1 ) * rhs ( 1, 1 ) * lhs.m03
|
||||
);
|
||||
}
|
||||
|
||||
static inline Matx23d operator| ( const Matx22d & lhs, const Matx21d & rhs )
|
||||
{
|
||||
return Matx23d ( lhs ( 0, 0 ), lhs ( 0, 1 ), rhs ( 0 ), lhs ( 1, 0 ), lhs ( 1, 1 ), rhs ( 1 ) );
|
||||
}
|
||||
|
||||
Matx23d PeiLinNormalization ( InputArray I )
|
||||
{
|
||||
const Moments M = moments ( I );
|
||||
const double l1 = ( M.mu20 / M.m00 + M.mu02 / M.m00 + sqrt ( ( M.mu20 / M.m00 - M.mu02 / M.m00 ) * ( M.mu20 / M.m00 - M.mu02 / M.m00 ) + 4 * M.mu11 / M.m00 * M.mu11 / M.m00 ) ) / 2;
|
||||
const double l2 = ( M.mu20 / M.m00 + M.mu02 / M.m00 - sqrt ( ( M.mu20 / M.m00 - M.mu02 / M.m00 ) * ( M.mu20 / M.m00 - M.mu02 / M.m00 ) + 4 * M.mu11 / M.m00 * M.mu11 / M.m00 ) ) / 2;
|
||||
const double ex = ( M.mu11 / M.m00 ) / sqrt ( ( l1 - M.mu20 / M.m00 ) * ( l1 - M.mu20 / M.m00 ) + M.mu11 / M.m00 * M.mu11 / M.m00 );
|
||||
const double ey = ( l1 - M.mu20 / M.m00 ) / sqrt ( ( l1 - M.mu20 / M.m00 ) * ( l1 - M.mu20 / M.m00 ) + M.mu11 / M.m00 * M.mu11 / M.m00 );
|
||||
const Matx22d E = Matx22d ( ex, ey, -ey, ex );
|
||||
const double p = min ( I.size().height, I.size().width ) / 8;
|
||||
const Matx22d W = Matx22d ( p / sqrt ( l1 ), 0, 0, p / sqrt ( l2 ) );
|
||||
const Matx21d c = Matx21d ( M.m10 / M.m00, M.m01 / M.m00 );
|
||||
const Matx21d i = Matx21d ( I.size().width / 2, I.size().height / 2 );
|
||||
const Moments N = M & W * E;
|
||||
const double t1 = N.mu12 + N.mu30;
|
||||
const double t2 = N.mu03 + N.mu21;
|
||||
const double phi = atan2 ( -t1, t2 );
|
||||
const double psi = ( -t1 * sin ( phi ) + t2 * cos ( phi ) >= 0 ) ? phi : ( phi + CV_PI );
|
||||
const Matx22d A = Matx22d ( cos ( psi ), sin ( psi ), -sin ( psi ), cos ( psi ) );
|
||||
return ( A * W * E ) | ( i - A * W * E * c );
|
||||
}
|
||||
|
||||
void PeiLinNormalization ( InputArray I, OutputArray T )
|
||||
{
|
||||
T.assign ( Mat ( PeiLinNormalization ( I ) ) );
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#ifndef _OPENCV_XIMGPROC_PRECOMP_HPP_
|
||||
#define _OPENCV_XIMGPROC_PRECOMP_HPP_
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/core/ocl.hpp>
|
||||
#include <opencv2/core/base.hpp>
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/core/private.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/geometry.hpp>
|
||||
#include <opencv2/ximgproc.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,200 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.#include "precomp.hpp"
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/ximgproc/color_match.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace cv { namespace ximgproc {
|
||||
|
||||
void createQuaternionImage(InputArray _img, OutputArray _qimg)
|
||||
{
|
||||
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_CheckType(depth, depth == CV_8U || depth == CV_32F || depth == CV_64F, "Depth must be CV_8U, CV_32F or CV_64F");
|
||||
CV_Assert(_img.dims() == 2 && cn == 3);
|
||||
vector<Mat> qplane(4);
|
||||
vector<Mat> plane;
|
||||
split(_img, plane);
|
||||
qplane[0] = Mat::zeros(_img.size(), CV_64FC1);
|
||||
for (int i = 0; i < cn; i++)
|
||||
plane[i].convertTo(qplane[3-i], CV_64F);
|
||||
merge(qplane, _qimg);
|
||||
}
|
||||
|
||||
void qconj(InputArray _img, OutputArray _qimg)
|
||||
{
|
||||
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_CheckType(depth, depth == CV_32F || depth == CV_64F, "Depth must be CV_32F or CV_64F");
|
||||
CV_Assert(_img.dims() == 2 && cn == 4);
|
||||
vector<Mat> qplane(4), plane;
|
||||
split(_img, plane);
|
||||
qplane[0] = plane[0];
|
||||
qplane[1] = -plane[1];
|
||||
qplane[2] = -plane[2];
|
||||
qplane[3] = -plane[3];
|
||||
merge(qplane, _qimg);
|
||||
}
|
||||
|
||||
void qunitary(InputArray _img, OutputArray _qimg)
|
||||
{
|
||||
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_Assert((depth == CV_64F) && _img.dims() == 2 && cn == 4);
|
||||
_img.copyTo(_qimg);
|
||||
Mat qimg = _qimg.getMat();
|
||||
qimg.forEach<Vec4d>([](Vec4d &p, const int * /*position*/) -> void {
|
||||
double d = p[0] * p[0] + p[1] * p[1] + p[2] * p[2] + p[3] * p[3];
|
||||
d = 1 / sqrt(d);
|
||||
p *= d;
|
||||
});
|
||||
}
|
||||
|
||||
void qdft(InputArray _img, OutputArray _qimg, int flags, bool sideLeft)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_Assert(depth == CV_64F && _img.dims() == 2 && cn == 4);
|
||||
float c;
|
||||
if (sideLeft)
|
||||
c = 1; // Left qdft
|
||||
else
|
||||
c = -1; // right qdft
|
||||
|
||||
vector<Mat> q;
|
||||
Mat img;
|
||||
img = _img.getMat();
|
||||
|
||||
CV_Assert(getOptimalDFTSize(img.rows) == img.rows && getOptimalDFTSize(img.cols) == img.cols);
|
||||
|
||||
split(img, q);
|
||||
Mat c1r;
|
||||
Mat c1i; // Imaginary part of c1 =x'
|
||||
Mat c2r; // Real part of c2 =y'
|
||||
Mat c2i; // Imaginary part of c2=z'
|
||||
c1r = q[0].clone();
|
||||
c1i = (q[1] + q[2] + q[3]) / sqrt(3);
|
||||
c2r = (q[2] - q[3]) / sqrt(2);
|
||||
c2i = c * (q[3] + q[2] - 2 * q[1]) / sqrt(6);
|
||||
vector<Mat> vc1 = { c1r,c1i }, vc2 = { c2r,c2i };
|
||||
Mat c1, c2, C1, C2;
|
||||
merge(vc1, c1);
|
||||
merge(vc2, c2);
|
||||
if (flags& DFT_INVERSE)
|
||||
{
|
||||
dft(c1, C1, DFT_COMPLEX_OUTPUT | DFT_INVERSE|DFT_SCALE);
|
||||
dft(c2, C2, DFT_COMPLEX_OUTPUT | DFT_INVERSE | DFT_SCALE);
|
||||
}
|
||||
else
|
||||
{
|
||||
dft(c1, C1, DFT_COMPLEX_OUTPUT);
|
||||
dft(c2, C2, DFT_COMPLEX_OUTPUT);
|
||||
}
|
||||
split(C1, vc1);
|
||||
split(C2, vc2);
|
||||
vector<Mat> qdft(4);
|
||||
qdft[0] = vc1[0].clone();
|
||||
qdft[1] = vc1[1] / sqrt(3) - c * 2 * vc2[1] / sqrt(6);
|
||||
qdft[2] = vc1[1] / sqrt(3) + vc2[0] / sqrt(2) + c * vc2[1] / sqrt(6);
|
||||
qdft[3] = vc1[1] / sqrt(3) - vc2[0] / sqrt(2) + c * vc2[1] / sqrt(6);
|
||||
Mat dst0;
|
||||
merge(qdft, dst0);
|
||||
dst0.copyTo(_qimg);
|
||||
}
|
||||
|
||||
|
||||
void qmultiply(InputArray src1, InputArray src2, OutputArray dst)
|
||||
{
|
||||
int type = src1.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_Assert(depth == CV_64F && src1.dims() == 2 && cn == 4);
|
||||
type = src2.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_Assert(depth == CV_64F && src2.dims() == 2 && cn == 4);
|
||||
vector<Mat> q3(4);
|
||||
if (src1.rows() == src2.rows() && src1.cols() == src2.cols())
|
||||
{
|
||||
vector<Mat> q1, q2;
|
||||
split(src1, q1);
|
||||
split(src2, q2);
|
||||
q3[0] = q1[0].mul(q2[0]) - q1[1].mul(q2[1]) - q1[2].mul(q2[2]) - q1[3].mul(q2[3]);
|
||||
q3[1] = q1[0].mul(q2[1]) + q1[1].mul(q2[0]) + q1[2].mul(q2[3]) - q1[3].mul(q2[2]);
|
||||
q3[2] = q1[0].mul(q2[2]) - q1[1].mul(q2[3]) + q1[2].mul(q2[0]) + q1[3].mul(q2[1]);
|
||||
q3[3] = q1[0].mul(q2[3]) + q1[1].mul(q2[2]) - q1[2].mul(q2[1]) + q1[3].mul(q2[0]);
|
||||
}
|
||||
else if (src1.rows() == 1 && src1.cols() == 1)
|
||||
{
|
||||
vector<Mat> q2;
|
||||
Vec4d q1 = src1.getMat().at<Vec4d>(0, 0);
|
||||
split(src2, q2);
|
||||
q3[0] = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
|
||||
q3[1] = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
|
||||
q3[2] = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
|
||||
q3[3] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
|
||||
}
|
||||
else if (src2.rows() == 1 && src2.cols() == 1)
|
||||
{
|
||||
vector<Mat> q1;
|
||||
split(src1, q1);
|
||||
Vec4d q2 = src2.getMat().at<Vec4d>(0, 0);
|
||||
q3[0] = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
|
||||
q3[1] = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
|
||||
q3[2] = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
|
||||
q3[3] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
|
||||
}
|
||||
else
|
||||
CV_Assert(src1.rows() == src2.rows() && src1.cols() == src2.cols());
|
||||
merge(q3, dst);
|
||||
|
||||
}
|
||||
|
||||
void colorMatchTemplate(InputArray _image, InputArray _templ, OutputArray _result)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
Mat image = _image.getMat(), imageF;
|
||||
CV_Assert(image.channels() == 3);
|
||||
Mat colorTemplate = _templ.getMat(), colorTemplateF;
|
||||
CV_Assert(colorTemplate.channels() == 3);
|
||||
int rr = max(getOptimalDFTSize(image.rows), getOptimalDFTSize(colorTemplate.rows));
|
||||
int cc = max(getOptimalDFTSize(image.cols), getOptimalDFTSize(colorTemplate.cols));
|
||||
Mat logo(rr, cc, CV_64FC3, Scalar::all(0));
|
||||
Mat img = Mat(rr, cc, CV_64FC3, Scalar::all(0));
|
||||
colorTemplate.convertTo(colorTemplateF, CV_64F, 1 / 256.),
|
||||
colorTemplateF.copyTo(logo(Rect(0, 0, colorTemplate.cols, colorTemplate.rows)));
|
||||
image.convertTo(imageF, CV_64F, 1 / 256.);
|
||||
imageF.copyTo(img(Rect(0, 0, image.cols, image.rows)));
|
||||
Mat qimg, qlogo;
|
||||
Mat qimgFFT, qimgIFFT, qlogoFFT;
|
||||
// Create quaternion image
|
||||
createQuaternionImage(img, qimg);
|
||||
createQuaternionImage(logo, qlogo);
|
||||
// quaternion fourier transform
|
||||
qdft(qimg, qimgFFT, 0, true);
|
||||
qdft(qimg, qimgIFFT, DFT_INVERSE, true);
|
||||
qdft(qlogo, qlogoFFT, 0, false);
|
||||
double sqrtnn = sqrt(static_cast<int>(qimgFFT.rows*qimgFFT.cols));
|
||||
qimgFFT /= sqrtnn;
|
||||
qimgIFFT *= sqrtnn;
|
||||
qlogoFFT /= sqrtnn;
|
||||
Mat mu(1, 1, CV_64FC4, Scalar(0, 1, 1, 1) / sqrt(3.));
|
||||
Mat qtmp, qlogopara, qlogoortho;
|
||||
qmultiply(mu, qlogoFFT, qtmp);
|
||||
qmultiply(qtmp, mu, qtmp);
|
||||
subtract(qlogoFFT, qtmp, qlogopara);
|
||||
qlogopara = qlogopara / 2;
|
||||
subtract(qlogoFFT, qlogopara, qlogoortho);
|
||||
Mat qcross1, qcross2, cqf, cqfi;
|
||||
qconj(qimgFFT, cqf);
|
||||
qconj(qimgIFFT, cqfi);
|
||||
qmultiply(cqf, qlogopara, qcross1);
|
||||
qmultiply(cqfi, qlogoortho, qcross2);
|
||||
Mat pwsp = qcross1 + qcross2;
|
||||
Mat crossCorr, pwspUnitary;
|
||||
qunitary(pwsp, pwspUnitary);
|
||||
qdft(pwspUnitary, crossCorr, DFT_INVERSE, false);
|
||||
vector<Mat> p;
|
||||
split(crossCorr, p);
|
||||
Mat imgcorr = (p[0].mul(p[0]) + p[1].mul(p[1]) + p[2].mul(p[2]) + p[3].mul(p[3]));
|
||||
sqrt(imgcorr, _result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {namespace ximgproc {
|
||||
void RadonTransform(InputArray src,
|
||||
OutputArray dst,
|
||||
double theta,
|
||||
double start_angle,
|
||||
double end_angle,
|
||||
bool crop,
|
||||
bool norm)
|
||||
{
|
||||
CV_Assert(src.dims() == 2);
|
||||
CV_Assert(src.channels() == 1);
|
||||
CV_Assert((end_angle - start_angle) * theta > 0);
|
||||
|
||||
Mat _srcMat = src.getMat();
|
||||
|
||||
int _row_num, _col_num, _out_mat_type;
|
||||
_col_num = cvRound((end_angle - start_angle) / theta);
|
||||
transpose(_srcMat, _srcMat);
|
||||
Mat _masked_src;
|
||||
cv::Point _center;
|
||||
|
||||
if (_srcMat.type() == CV_32FC1 || _srcMat.type() == CV_64FC1) {
|
||||
_out_mat_type = CV_64FC1;
|
||||
}
|
||||
else {
|
||||
_out_mat_type = CV_32SC1;
|
||||
}
|
||||
|
||||
if (crop) {
|
||||
// crop the source into square
|
||||
_row_num = min(_srcMat.rows, _srcMat.cols);
|
||||
cv::Rect _crop_ROI(
|
||||
_srcMat.cols / 2 - _row_num / 2,
|
||||
_srcMat.rows / 2 - _row_num / 2,
|
||||
_row_num, _row_num);
|
||||
_srcMat = _srcMat(_crop_ROI);
|
||||
// crop the source into circle
|
||||
Mat _mask(_srcMat.size(), CV_8UC1, Scalar(0));
|
||||
_center = Point(_srcMat.cols / 2, _srcMat.rows / 2);
|
||||
circle(_mask, _center, _srcMat.cols / 2, Scalar(255), FILLED);
|
||||
_srcMat.copyTo(_masked_src, _mask);
|
||||
}
|
||||
else {
|
||||
// avoid cropping corner when rotating
|
||||
_row_num = cvCeil(sqrt(_srcMat.rows * _srcMat.rows + _srcMat.cols * _srcMat.cols));
|
||||
_masked_src = Mat(Size(_row_num, _row_num), _srcMat.type(), Scalar(0));
|
||||
_center = Point(_masked_src.cols / 2, _masked_src.rows / 2);
|
||||
_srcMat.copyTo(_masked_src(Rect(
|
||||
(_row_num - _srcMat.cols) / 2,
|
||||
(_row_num - _srcMat.rows) / 2,
|
||||
_srcMat.cols, _srcMat.rows)));
|
||||
}
|
||||
|
||||
double _t;
|
||||
Mat _rotated_src;
|
||||
Mat _radon(_row_num, _col_num, _out_mat_type);
|
||||
|
||||
for (int _col = 0; _col < _col_num; _col++) {
|
||||
// rotate the source by _t
|
||||
_t = (start_angle + _col * theta);
|
||||
cv::Mat _r_matrix = cv::getRotationMatrix2D(_center, _t, 1);
|
||||
cv::warpAffine(_masked_src, _rotated_src, _r_matrix, _masked_src.size());
|
||||
Mat _col_mat = _radon.col(_col);
|
||||
// make projection
|
||||
cv::reduce(_rotated_src, _col_mat, 1, REDUCE_SUM, _out_mat_type);
|
||||
}
|
||||
|
||||
if (norm) {
|
||||
normalize(_radon, _radon, 0, 255, NORM_MINMAX, CV_8UC1);
|
||||
}
|
||||
|
||||
_radon.copyTo(dst);
|
||||
return;
|
||||
}
|
||||
} }
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/ximgproc/ridgefilter.hpp"
|
||||
|
||||
namespace cv { namespace ximgproc {
|
||||
|
||||
class RidgeDetectionFilterImpl : public RidgeDetectionFilter
|
||||
{
|
||||
public:
|
||||
int _ddepth, _dx, _dy, _ksize;
|
||||
double _scale, _delta;
|
||||
int _borderType;
|
||||
int _out_dtype;
|
||||
RidgeDetectionFilterImpl(int ddepth=CV_32FC1, int dx=1, int dy=1, int ksize = 3, int out_dtype=CV_8UC1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT)
|
||||
{
|
||||
CV_Assert((ksize == 1 || ksize == 3 || ksize == 5 || ksize == 7));
|
||||
CV_Assert((ddepth == CV_32FC1 || ddepth == CV_64FC1));
|
||||
_ddepth = ddepth;
|
||||
_dx = dx;
|
||||
_dy = dy;
|
||||
_ksize = ksize;
|
||||
_scale = scale;
|
||||
_delta = delta;
|
||||
_borderType = borderType;
|
||||
_out_dtype = out_dtype;
|
||||
}
|
||||
virtual void getRidgeFilteredImage(InputArray _img, OutputArray out) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
void RidgeDetectionFilterImpl::getRidgeFilteredImage(InputArray _img, OutputArray out)
|
||||
{
|
||||
Mat img = _img.getMat();
|
||||
CV_Assert(img.channels() == 1 || img.channels() == 3);
|
||||
|
||||
if(img.channels() == 3)
|
||||
cvtColor(img, img, COLOR_BGR2GRAY);
|
||||
|
||||
Mat sbx, sby;
|
||||
Sobel(img, sbx, _ddepth, _dx, 0, _ksize, _scale, _delta, _borderType);
|
||||
Sobel(img, sby, _ddepth, 0, _dy, _ksize, _scale, _delta, _borderType);
|
||||
|
||||
Mat sbxx, sbyy, sbxy;
|
||||
Sobel(sbx, sbxx, _ddepth, _dx, 0, _ksize, _scale, _delta, _borderType);
|
||||
Sobel(sby, sbyy, _ddepth, 0, _dy, _ksize, _scale, _delta, _borderType);
|
||||
Sobel(sbx, sbxy, _ddepth, 0, _dy, _ksize, _scale, _delta, _borderType);
|
||||
|
||||
Mat sb2xx, sb2yy, sb2xy;
|
||||
multiply(sbxx, sbxx, sb2xx);
|
||||
multiply(sbyy, sbyy, sb2yy);
|
||||
multiply(sbxy, sbxy, sb2xy);
|
||||
|
||||
Mat sbxxyy;
|
||||
multiply(sbxx, sbyy, sbxxyy);
|
||||
|
||||
Mat rootex;
|
||||
rootex = (sb2xx + (sb2xy + sb2xy + sb2xy + sb2xy) - (sbxxyy + sbxxyy) + sb2yy );
|
||||
Mat root;
|
||||
sqrt(rootex, root);
|
||||
Mat ridgexp;
|
||||
ridgexp = ( (sbxx + sbyy) + root );
|
||||
ridgexp.convertTo(out, _out_dtype, 0.5);
|
||||
}
|
||||
|
||||
Ptr<RidgeDetectionFilter> RidgeDetectionFilter::create(int ddepth, int dx, int dy, int ksize, int out_dtype, double scale, double delta, int borderType)
|
||||
{
|
||||
return makePtr<RidgeDetectionFilterImpl>(ddepth, dx, dy, ksize, out_dtype, scale, delta, borderType);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <opencv2/ximgproc.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
void rollingGuidanceFilter(InputArray src_, OutputArray dst_, int d,
|
||||
double sigmaColor, double sigmaSpace, int numOfIter, int borderType)
|
||||
{
|
||||
CV_Assert(!src_.empty());
|
||||
|
||||
Mat guidance = src_.getMat();
|
||||
Mat src = src_.getMat();
|
||||
|
||||
CV_Assert(src.size() == guidance.size());
|
||||
CV_Assert(src.depth() == guidance.depth() && (src.depth() == CV_8U || src.depth() == CV_32F) );
|
||||
|
||||
if (sigmaColor <= 0)
|
||||
sigmaColor = 1;
|
||||
if (sigmaSpace <= 0)
|
||||
sigmaSpace = 1;
|
||||
|
||||
dst_.create(src.size(), src.type());
|
||||
Mat dst = dst_.getMat();
|
||||
|
||||
if (src.data == guidance.data)
|
||||
guidance = guidance.clone();
|
||||
if (dst.data == src.data)
|
||||
src = src.clone();
|
||||
|
||||
int srcCnNum = src.channels();
|
||||
|
||||
if (srcCnNum == 1 || srcCnNum == 3)
|
||||
{
|
||||
while(numOfIter--){
|
||||
jointBilateralFilter(guidance, src, guidance, d, sigmaColor, sigmaSpace, borderType);
|
||||
}
|
||||
guidance.copyTo(dst_);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::BadNumChannels, "Unsupported number of channels");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
#include "precomp.hpp"
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
namespace rl {
|
||||
|
||||
struct rlType
|
||||
{
|
||||
int cb, ce, r;
|
||||
rlType(int cbIn, int ceIn, int rIn): cb(cbIn), ce(ceIn), r(rIn) {}
|
||||
rlType(): cb(0), ce(0), r(0) {}
|
||||
bool operator < (const rlType& other) const { if (r < other.r || (r == other.r && cb < other.cb)
|
||||
|| (r == other.r && cb == other.cb && ce < other.ce)) return true; else return false; }
|
||||
};
|
||||
|
||||
typedef std::vector<rlType> rlVec;
|
||||
|
||||
template <class T>
|
||||
void _thresholdLine(T* pData, int nWidth, int nRow, T threshold, int type, rlVec& res)
|
||||
{
|
||||
bool bOn = false;
|
||||
int nStartSegment = 0;
|
||||
for (int j = 0; j < nWidth; j++)
|
||||
{
|
||||
bool bAboveThreshold = (pData[j] > threshold);
|
||||
bool bCurOn = (bAboveThreshold == (THRESH_BINARY == type));
|
||||
if (!bOn && bCurOn)
|
||||
{
|
||||
nStartSegment = j;
|
||||
bOn = true;
|
||||
}
|
||||
else if (bOn && !bCurOn)
|
||||
{
|
||||
rlType chord(nStartSegment, j - 1, nRow);
|
||||
res.push_back(chord);
|
||||
bOn = false;
|
||||
}
|
||||
|
||||
}
|
||||
if (bOn)
|
||||
{
|
||||
rlType chord(nStartSegment, nWidth - 1, nRow);
|
||||
res.push_back(chord);
|
||||
}
|
||||
}
|
||||
|
||||
static void _threshold(cv::Mat& img, rlVec& res, double threshold, int type)
|
||||
{
|
||||
res.clear();
|
||||
switch (img.depth())
|
||||
{
|
||||
case CV_8U:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
_thresholdLine<uchar>(img.ptr(i), img.cols, i, (uchar) threshold, type, res);
|
||||
break;
|
||||
case CV_8S:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
_thresholdLine<schar>((schar*) img.ptr(i), img.cols, i, (schar) threshold, type, res);
|
||||
break;
|
||||
case CV_16U:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
{
|
||||
_thresholdLine<unsigned short>((unsigned short*)img.ptr(i), img.cols, i,
|
||||
(unsigned short)threshold, type, res);
|
||||
}
|
||||
break;
|
||||
case CV_16S:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
_thresholdLine<short>((short*) img.ptr(i), img.cols, i, (short) threshold, type, res);
|
||||
break;
|
||||
case CV_32S:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
_thresholdLine<int>((int*) img.ptr(i), img.cols, i, (int) threshold, type, res);
|
||||
break;
|
||||
case CV_32F:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
_thresholdLine<float>((float*) img.ptr(i), img.cols, i, (float) threshold, type, res);
|
||||
break;
|
||||
case CV_64F:
|
||||
for (int i = 0; i < img.rows; ++i)
|
||||
_thresholdLine<double>((double*) img.ptr(i), img.cols, i, threshold, type, res);
|
||||
break;
|
||||
default:
|
||||
CV_Error( Error::StsUnsupportedFormat, "unsupported image type" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void convertToOutputArray(rlVec& runs, Size size, OutputArray& res)
|
||||
{
|
||||
size_t nRuns = runs.size();
|
||||
std::vector<cv::Point3i> segments(nRuns + 1);
|
||||
segments[0] = cv::Point3i(size.width, size.height, 0);
|
||||
for (size_t i = 0; i < nRuns; ++i)
|
||||
{
|
||||
rlType& curRun = runs[i];
|
||||
segments[i + 1] = Point3i(curRun.cb, curRun.ce, curRun.r);
|
||||
}
|
||||
Mat(segments).copyTo(res);
|
||||
}
|
||||
|
||||
|
||||
CV_EXPORTS void threshold(InputArray src, OutputArray rlDest, double thresh, int type)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat image = src.getMat();
|
||||
CV_Assert(!image.empty() && image.channels() == 1);
|
||||
CV_Assert(type == THRESH_BINARY || type == THRESH_BINARY_INV);
|
||||
rlVec runs;
|
||||
_threshold(image, runs, thresh, type);
|
||||
Size size(image.cols, image.rows);
|
||||
|
||||
convertToOutputArray(runs, size, rlDest);
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
void paint_impl(cv::Mat& img, rlType* pRuns, int nSize, T value)
|
||||
{
|
||||
int i;
|
||||
rlType* pCurRun;
|
||||
for (pCurRun = pRuns, i = 0; i< nSize; ++pCurRun, ++i)
|
||||
{
|
||||
rlType& curRun = *pCurRun;
|
||||
if (curRun.r < 0 || curRun.r >= img.rows || curRun.cb >= img.cols || curRun.ce < 0)
|
||||
continue;
|
||||
|
||||
T* rowPtr = (T*)img.ptr(curRun.r);
|
||||
std::fill(rowPtr + std::max(curRun.cb, 0), rowPtr + std::min(curRun.ce + 1, img.cols), value);
|
||||
}
|
||||
}
|
||||
|
||||
CV_EXPORTS void paint(InputOutputArray image, InputArray rlSrc, const Scalar& value)
|
||||
{
|
||||
Mat _runs;
|
||||
_runs = rlSrc.getMat();
|
||||
int N = _runs.checkVector(3);
|
||||
if (N <= 1)
|
||||
return;
|
||||
|
||||
double dValue = value[0];
|
||||
|
||||
cv::Mat _image = image.getMat();
|
||||
|
||||
rlType* pRuns = (rlType*) &(_runs.at<Point3i>(1));
|
||||
switch (_image.type())
|
||||
{
|
||||
case CV_8UC1:
|
||||
paint_impl<uchar>(_image, pRuns, N - 1, (uchar)dValue);
|
||||
break;
|
||||
case CV_8SC1:
|
||||
paint_impl<schar>(_image, pRuns, N - 1, (schar)dValue);
|
||||
break;
|
||||
case CV_16UC1:
|
||||
paint_impl<unsigned short>(_image, pRuns, N - 1, (unsigned short)dValue);
|
||||
break;
|
||||
case CV_16SC1:
|
||||
paint_impl<short>(_image, pRuns, N - 1, (short)dValue);
|
||||
break;
|
||||
case CV_32SC1:
|
||||
paint_impl<int>(_image, pRuns, N - 1, (int)dValue);
|
||||
break;
|
||||
case CV_32FC1:
|
||||
paint_impl<float>(_image, pRuns, N - 1, (float)dValue);
|
||||
break;
|
||||
case CV_64FC1:
|
||||
paint_impl<double>(_image, pRuns, N - 1, dValue);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsUnsupportedFormat, "unsupported image type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void translateRegion(rlVec& reg, Point ptTrans)
|
||||
{
|
||||
for (rlVec::iterator it=reg.begin();it!=reg.end();++it)
|
||||
{
|
||||
it->r += ptTrans.y;
|
||||
it->cb += ptTrans.x;
|
||||
it->ce += ptTrans.x;
|
||||
}
|
||||
}
|
||||
|
||||
CV_EXPORTS Mat getStructuringElement(int shape, Size ksize)
|
||||
{
|
||||
Mat mask = cv::getStructuringElement(shape, ksize);
|
||||
|
||||
rlVec reg;
|
||||
_threshold(mask, reg, 0.0, THRESH_BINARY);
|
||||
|
||||
Point ptTrans = - Point(mask.cols / 2, mask.rows / 2);
|
||||
translateRegion(reg, ptTrans);
|
||||
Mat rlDest;
|
||||
convertToOutputArray(reg, Size(mask.cols, mask.rows), rlDest);
|
||||
|
||||
return rlDest;
|
||||
}
|
||||
|
||||
static void erode_rle (rlVec& regIn, rlVec& regOut, rlVec& se)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
regOut.clear();
|
||||
|
||||
if (regIn.size() == 0)
|
||||
return;
|
||||
|
||||
int nMinRow = regIn[0].r;
|
||||
int nMaxRow = regIn.back().r;
|
||||
|
||||
int nRows = nMaxRow - nMinRow + 1;
|
||||
|
||||
|
||||
const int EMPTY = -1;
|
||||
|
||||
// setup a table which holds the index of the first chord for each row
|
||||
vector<int> pIdxChord1(nRows);
|
||||
vector<int> pIdxNextRow(nRows);
|
||||
|
||||
int i,j;
|
||||
|
||||
for (i=1;i<nRows;i++)
|
||||
{
|
||||
pIdxChord1[i] = EMPTY;
|
||||
pIdxNextRow[i] = EMPTY;
|
||||
}
|
||||
|
||||
pIdxChord1[0] = 0;
|
||||
pIdxNextRow[nRows-1] = (int) regIn.size();
|
||||
|
||||
for (i=1; i < (int) regIn.size();i++)
|
||||
if (regIn[i].r != regIn[i-1].r)
|
||||
{
|
||||
pIdxChord1[regIn[i].r - nMinRow] = i;
|
||||
pIdxNextRow[regIn[i-1].r - nMinRow] = i;
|
||||
}
|
||||
|
||||
int nMinRowSE = se[0].r;
|
||||
int nMaxRowSE = se.back().r;
|
||||
|
||||
int nRowsSE = nMaxRowSE - nMinRowSE + 1;
|
||||
|
||||
assert(nRowsSE == (int) se.size());
|
||||
|
||||
vector<int> pCurIdxRow(nRowsSE);
|
||||
|
||||
// loop through all possible rows
|
||||
for (i=nMinRow - nMinRowSE; i<= nMaxRow - nMaxRowSE; i++)
|
||||
{
|
||||
// check whether all relevant rows are available
|
||||
bool bNextRow = false;
|
||||
|
||||
for (j=0; j < nRowsSE; j++)
|
||||
{
|
||||
// get idx of first chord in regIn for this row of the se
|
||||
pCurIdxRow[j] = pIdxChord1[ j + nMinRowSE + i - nMinRow];
|
||||
if (pCurIdxRow[j] == -1)
|
||||
{
|
||||
bNextRow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bNextRow)
|
||||
continue;
|
||||
|
||||
while (!bNextRow)
|
||||
{
|
||||
int nPossibleStart = std::numeric_limits<int>::min();
|
||||
|
||||
// search for row with max( cb - se.cb) (the leftmost possible position of a result chord
|
||||
for (j=0;j<nRowsSE;j++)
|
||||
nPossibleStart = max(nPossibleStart, regIn[pCurIdxRow[j]].cb - se[j].cb);
|
||||
|
||||
// for all rows skip chords whose end is left from the point
|
||||
// where it can contribute to a result
|
||||
bool bHaveResult = true;
|
||||
int nLimitingRow = 0;
|
||||
int nChordEnd = std::numeric_limits<int>::max(); //INT_MAX;
|
||||
|
||||
for (j=0;j<nRowsSE;j++)
|
||||
{
|
||||
while (regIn[pCurIdxRow[j]].ce < nPossibleStart + se[j].ce &&
|
||||
pCurIdxRow[j] != pIdxNextRow[j + nMinRowSE + i - nMinRow])
|
||||
{
|
||||
pCurIdxRow[j]++;
|
||||
}
|
||||
|
||||
// if all chords in this row skipped -> next row
|
||||
if (pCurIdxRow[j] == pIdxNextRow[ j + nMinRowSE + i - nMinRow])
|
||||
{
|
||||
bNextRow = true;
|
||||
bHaveResult = false;
|
||||
break;
|
||||
}
|
||||
else if ( bHaveResult )
|
||||
{
|
||||
// can the found chord contribute to a result ?
|
||||
if (regIn[ pCurIdxRow[j] ].cb - se[j].cb <= nPossibleStart)
|
||||
{
|
||||
int nCurPossibleEnd = regIn[ pCurIdxRow[j] ].ce - se[j].ce;
|
||||
if (nCurPossibleEnd < nChordEnd)
|
||||
{
|
||||
nChordEnd = nCurPossibleEnd;
|
||||
nLimitingRow = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
bHaveResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bHaveResult)
|
||||
{
|
||||
regOut.push_back(rlType(nPossibleStart, nChordEnd, i));
|
||||
pCurIdxRow[nLimitingRow]++;
|
||||
|
||||
if (pCurIdxRow[nLimitingRow] == pIdxNextRow[ nLimitingRow + nMinRowSE + i - nMinRow])
|
||||
bNextRow = true;
|
||||
}
|
||||
} // end while (!bNextRow
|
||||
} // end for
|
||||
|
||||
}
|
||||
|
||||
static void convertInputArrayToRuns(InputArray& theArray, rlVec& runs, Size& theSize)
|
||||
{
|
||||
Mat _runs;
|
||||
_runs = theArray.getMat();
|
||||
int N = _runs.checkVector(3);
|
||||
if (N == 0)
|
||||
{
|
||||
runs.clear();
|
||||
return;
|
||||
}
|
||||
runs.resize(N - 1);
|
||||
Point3i pt = _runs.at<Point3i>(0);
|
||||
theSize.width = pt.x;
|
||||
theSize.height = pt.y;
|
||||
|
||||
for (int i = 1; i < N; ++i)
|
||||
{
|
||||
pt = _runs.at<Point3i>(i);
|
||||
runs[i-1] = rlType(pt.x, pt.y, pt.z);
|
||||
}
|
||||
}
|
||||
|
||||
static void sortChords(rlVec& lChords)
|
||||
{
|
||||
std::sort(lChords.begin(), lChords.end());
|
||||
}
|
||||
|
||||
static void mergeNeighbouringChords(rlVec& rlIn, rlVec& rlOut)
|
||||
{
|
||||
rlOut.clear();
|
||||
if (rlIn.size() == 0)
|
||||
return;
|
||||
|
||||
rlOut.push_back(rlIn[0]);
|
||||
|
||||
for (int i = 1; i< (int)rlIn.size(); i++)
|
||||
{
|
||||
rlType& curIn = rlIn[i];
|
||||
rlType& lastAddedOut = rlOut.back();
|
||||
if (curIn.r == lastAddedOut.r && curIn.cb <= lastAddedOut.ce + 1)
|
||||
lastAddedOut.ce = max(curIn.ce, lastAddedOut.ce);
|
||||
else
|
||||
rlOut.push_back(curIn);
|
||||
}
|
||||
}
|
||||
|
||||
static void union_regions(rlVec& reg1, rlVec& reg2, rlVec& regUnion)
|
||||
{
|
||||
rlVec lAllChords(reg1);
|
||||
|
||||
lAllChords.insert(lAllChords.end(), reg2.begin(), reg2.end());
|
||||
|
||||
sortChords(lAllChords);
|
||||
mergeNeighbouringChords(lAllChords, regUnion);
|
||||
}
|
||||
|
||||
static void intersect(rlVec& reg1, rlVec& reg2, rlVec& regRes)
|
||||
{
|
||||
rlVec::iterator end1 = reg1.end();
|
||||
rlVec::iterator end2 = reg2.end();
|
||||
|
||||
rlVec::iterator cur1 = reg1.begin();
|
||||
rlVec::iterator cur2 = reg2.begin();
|
||||
regRes.clear();
|
||||
|
||||
while (cur1 != end1 && cur2 != end2)
|
||||
{
|
||||
if (cur1->r < cur2->r || (cur1->r == cur2->r && cur1->ce < cur2->cb))
|
||||
++cur1;
|
||||
else if (cur2->r < cur1->r || (cur1->r == cur2->r && cur2->ce < cur1->cb))
|
||||
++cur2;
|
||||
else
|
||||
{
|
||||
assert(cur1->r == cur2->r);
|
||||
int nStart = max(cur1->cb, cur2->cb);
|
||||
int nEnd = min(cur1->ce, cur2->ce);
|
||||
if (nStart > nEnd)
|
||||
{
|
||||
assert(nStart <= nEnd);
|
||||
}
|
||||
regRes.push_back(rlType(nStart, nEnd, cur1->r));
|
||||
if (cur1->ce < cur2->ce)
|
||||
++cur1;
|
||||
else
|
||||
++cur2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void addBoundary(rlVec& runsIn, int nWidth, int nHeight, int nBoundaryLeft, int nBoundaryTop,
|
||||
int nBoundaryRight, int nBoundaryBottom, rlVec& res)
|
||||
{
|
||||
rlVec boundary;
|
||||
for (int i = -nBoundaryTop; i < 0; ++i)
|
||||
boundary.push_back(rlType(-nBoundaryLeft, nWidth - 1 + nBoundaryRight, i));
|
||||
for (int i = 0; i < nHeight; ++i)
|
||||
{
|
||||
boundary.push_back(rlType(-nBoundaryLeft, -1, i));
|
||||
boundary.push_back(rlType(nWidth, nWidth - 1 + nBoundaryRight, i));
|
||||
}
|
||||
|
||||
for (int i = nHeight; i < nHeight + nBoundaryBottom; ++i)
|
||||
boundary.push_back(rlType(-nBoundaryLeft, nWidth - 1 + nBoundaryRight, i));
|
||||
|
||||
union_regions(runsIn, boundary, res);
|
||||
}
|
||||
|
||||
static cv::Rect getBoundingRectangle(rlVec& reg)
|
||||
{
|
||||
using namespace std;
|
||||
cv::Rect rect;
|
||||
if (reg.empty())
|
||||
{
|
||||
rect.x = rect.y = rect.width = rect.height = 0;
|
||||
return rect;
|
||||
}
|
||||
|
||||
int minX = std::numeric_limits<int>::max();
|
||||
int minY = std::numeric_limits<int>::max();
|
||||
int maxX = std::numeric_limits<int>::min();
|
||||
int maxY = std::numeric_limits<int>::min();
|
||||
|
||||
int i;
|
||||
for (i = 0; i<(int)reg.size(); i++)
|
||||
{
|
||||
minX = min(minX, reg[i].cb);
|
||||
maxX = max(maxX, reg[i].ce);
|
||||
minY = min(minY, reg[i].r);
|
||||
maxY = max(maxY, reg[i].r);
|
||||
}
|
||||
|
||||
rect.x = minX;
|
||||
rect.y = minY;
|
||||
rect.width = maxX - minX + 1;
|
||||
rect.height = maxY - minY + 1;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static void createUprightRectangle(cv::Rect rect, rlVec &rl)
|
||||
{
|
||||
rl.clear();
|
||||
rlType curRL;
|
||||
int j;
|
||||
int cb = rect.x;
|
||||
int ce = rect.x + rect.width - 1;
|
||||
for (j = 0; j < rect.height; j++)
|
||||
{
|
||||
curRL.cb = cb;
|
||||
curRL.ce = ce;
|
||||
curRL.r = j + rect.y;
|
||||
rl.push_back(curRL);
|
||||
}
|
||||
}
|
||||
|
||||
static void erode_with_boundary_rle(rlVec& runsSource, int nWidth, int nHeight, rlVec& runsDestination,
|
||||
rlVec& runsKernel)
|
||||
{
|
||||
cv::Rect rect = getBoundingRectangle(runsKernel);
|
||||
rlVec regExtended, regFrame, regResultRaw;
|
||||
addBoundary(runsSource, nWidth, nHeight, max(0, -rect.x), max(0, -rect.y),
|
||||
max(0, rect.x + rect.width), max(0, rect.y + rect.height), regExtended);
|
||||
|
||||
erode_rle(regExtended, regResultRaw, runsKernel);
|
||||
createUprightRectangle(cv::Rect(0, 0, nWidth, nHeight), regFrame);
|
||||
intersect(regResultRaw, regFrame, runsDestination);
|
||||
}
|
||||
|
||||
|
||||
CV_EXPORTS void erode(InputArray rlSrc, OutputArray rlDest, InputArray rlKernel, bool bBoundaryOn,
|
||||
Point anchor)
|
||||
{
|
||||
rlVec runsSource, runsDestination, runsKernel;
|
||||
Size sizeSource, sizeKernel;
|
||||
convertInputArrayToRuns(rlSrc, runsSource, sizeSource);
|
||||
convertInputArrayToRuns(rlKernel, runsKernel, sizeKernel);
|
||||
|
||||
if (anchor != Point(0,0))
|
||||
translateRegion(runsKernel, -anchor);
|
||||
|
||||
if (bBoundaryOn)
|
||||
erode_with_boundary_rle(runsSource, sizeSource.width, sizeSource.height, runsDestination, runsKernel);
|
||||
else
|
||||
erode_rle(runsSource, runsDestination, runsKernel);
|
||||
convertToOutputArray(runsDestination, sizeSource, rlDest);
|
||||
}
|
||||
|
||||
|
||||
static void subtract_rle( rlVec& regFrom,
|
||||
rlVec& regSubtract,
|
||||
rlVec& regRes)
|
||||
{
|
||||
rlVec::iterator end1 = regFrom.end();
|
||||
rlVec::iterator end2 = regSubtract.end();
|
||||
|
||||
rlVec::iterator cur1 = regFrom.begin();
|
||||
rlVec::iterator cur2 = regSubtract.begin();
|
||||
regRes.clear();
|
||||
|
||||
while( cur1 != end1)
|
||||
{
|
||||
if (cur2 == end2)
|
||||
{
|
||||
regRes.insert( regRes.end(), cur1, end1);
|
||||
return;
|
||||
}
|
||||
else if ( cur1->r < cur2->r || (cur1->r == cur2->r && cur1->ce < cur2->cb))
|
||||
{
|
||||
regRes.push_back(*cur1);
|
||||
++cur1;
|
||||
}
|
||||
else if ( cur2->r < cur1->r || (cur1->r == cur2->r && cur2->ce < cur1->cb))
|
||||
++cur2;
|
||||
else
|
||||
{
|
||||
int curR = cur1->r;
|
||||
assert(curR == cur2->r);
|
||||
rlVec::iterator lastIncluded;
|
||||
|
||||
bool bIncremented = false;
|
||||
for (lastIncluded = cur2;
|
||||
lastIncluded != end2 && lastIncluded->r == curR && lastIncluded->cb <= cur1->ce;
|
||||
++lastIncluded)
|
||||
{
|
||||
bIncremented = true;
|
||||
}
|
||||
|
||||
if (bIncremented)
|
||||
--lastIncluded;
|
||||
|
||||
// now all chords from cur2 to lastIncluded have an intersection with cur1
|
||||
if (cur1->cb < cur2->cb)
|
||||
regRes.push_back(rlType(cur1->cb, cur2->cb - 1, curR));
|
||||
|
||||
// we add the gaps between the chords of reg2 to the result
|
||||
while (cur2 < lastIncluded)
|
||||
{
|
||||
regRes.push_back(rlType(cur2->ce + 1, (cur2 + 1)->cb - 1, curR));
|
||||
if (regRes.back().cb > regRes.back().ce)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
++cur2;
|
||||
}
|
||||
|
||||
if (cur1->ce > lastIncluded->ce)
|
||||
{
|
||||
regRes.push_back(rlType(lastIncluded->ce + 1, cur1->ce, curR));
|
||||
assert(regRes.back().cb <= regRes.back().ce);
|
||||
}
|
||||
++cur1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void invertRegion(rlVec& runsIn, rlVec& runsOut)
|
||||
{
|
||||
// if there is only one chord in row -> do not insert anything for this row
|
||||
// otherwise insert chords for the spaces between chords
|
||||
runsOut.clear();
|
||||
int nCurRow = std::numeric_limits<int>::min();
|
||||
int nLastRight = nCurRow;
|
||||
for (rlVec::iterator it = runsIn.begin(); it != runsIn.end(); ++it)
|
||||
{
|
||||
rlType run = *it;
|
||||
if (run.r != nCurRow)
|
||||
{
|
||||
nCurRow = run.r;
|
||||
nLastRight = run.ce;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(run.cb > nLastRight + 1);
|
||||
runsOut.push_back(rlType(nLastRight + 1, run.cb - 1, nCurRow));
|
||||
nLastRight = run.ce;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dilate_rle(rlVec& runsSource,
|
||||
rlVec& runsDestination,
|
||||
rlVec& runsKernel)
|
||||
{
|
||||
cv::Rect rectSource = getBoundingRectangle(runsSource);
|
||||
cv::Rect rectKernel = getBoundingRectangle(runsKernel);
|
||||
|
||||
cv::Rect background;
|
||||
background.x = rectSource.x - 2 * rectKernel.width;
|
||||
background.y = rectSource.y - 2 * rectKernel.height;
|
||||
background.width = rectSource.width + 4 * rectKernel.width;
|
||||
background.height = rectSource.height + 4 * rectKernel.height;
|
||||
|
||||
rlVec rlBackground, rlSourceInverse, rlResultInverse;
|
||||
createUprightRectangle(background, rlBackground);
|
||||
subtract_rle(rlBackground, runsSource, rlSourceInverse);
|
||||
|
||||
erode_rle(rlSourceInverse, rlResultInverse, runsKernel);
|
||||
|
||||
invertRegion(rlResultInverse, runsDestination);
|
||||
}
|
||||
|
||||
|
||||
|
||||
CV_EXPORTS void dilate(InputArray rlSrc, OutputArray rlDest, InputArray rlKernel, Point anchor)
|
||||
{
|
||||
rlVec runsSource, runsDestination, runsKernel;
|
||||
Size sizeSource, sizeKernel;
|
||||
convertInputArrayToRuns(rlSrc, runsSource, sizeSource);
|
||||
convertInputArrayToRuns(rlKernel, runsKernel, sizeKernel);
|
||||
|
||||
if (anchor != Point(0, 0))
|
||||
translateRegion(runsKernel, -anchor);
|
||||
|
||||
dilate_rle(runsSource, runsDestination, runsKernel);
|
||||
|
||||
convertToOutputArray(runsDestination, sizeSource, rlDest);
|
||||
}
|
||||
|
||||
CV_EXPORTS bool isRLMorphologyPossible(InputArray rlStructuringElement)
|
||||
{
|
||||
rlVec runsKernel;
|
||||
Size sizeKernel;
|
||||
convertInputArrayToRuns(rlStructuringElement, runsKernel, sizeKernel);
|
||||
|
||||
for (int i = 1; i < (int) runsKernel.size(); ++i)
|
||||
if (runsKernel[i].r != runsKernel[i-1].r + 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CV_EXPORTS void createRLEImage(const std::vector<cv::Point3i>& runs, OutputArray res, Size size)
|
||||
{
|
||||
size_t nRuns = runs.size();
|
||||
rlVec runsConverted(nRuns);
|
||||
for (size_t i = 0u; i < nRuns; ++i)
|
||||
{
|
||||
const Point3i &curIn = runs[i];
|
||||
runsConverted[i] = rlType(curIn.x, curIn.y, curIn.z);
|
||||
}
|
||||
sortChords(runsConverted);
|
||||
|
||||
if (size.width == 0 || size.height == 0)
|
||||
{
|
||||
Rect boundingRect = getBoundingRectangle(runsConverted);
|
||||
size.width = std::max(0, boundingRect.x + boundingRect.width);
|
||||
size.height = std::max(0, boundingRect.y + boundingRect.height);
|
||||
}
|
||||
convertToOutputArray(runsConverted, size, res);
|
||||
}
|
||||
|
||||
|
||||
CV_EXPORTS void morphologyEx(InputArray rlSrc, OutputArray rlDest, int op, InputArray rlKernel,
|
||||
bool bBoundaryOnForErosion, Point anchor)
|
||||
{
|
||||
if (op == MORPH_ERODE)
|
||||
rl::erode(rlSrc, rlDest, rlKernel, bBoundaryOnForErosion, anchor);
|
||||
else if (op == MORPH_DILATE)
|
||||
rl::dilate(rlSrc, rlDest, rlKernel, anchor);
|
||||
else
|
||||
{
|
||||
rlVec runsSource, runsKernel, runsDestination;
|
||||
Size sizeSource, sizeKernel;
|
||||
convertInputArrayToRuns(rlKernel, runsKernel, sizeKernel);
|
||||
convertInputArrayToRuns(rlSrc, runsSource, sizeSource);
|
||||
|
||||
if (anchor != Point(0, 0))
|
||||
translateRegion(runsKernel, -anchor);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case MORPH_OPEN:
|
||||
{
|
||||
rlVec runsEroded;
|
||||
|
||||
if (bBoundaryOnForErosion)
|
||||
erode_with_boundary_rle(runsSource, sizeSource.width, sizeSource.height, runsEroded, runsKernel);
|
||||
else
|
||||
erode_rle(runsSource, runsEroded, runsKernel);
|
||||
dilate_rle(runsEroded, runsDestination, runsKernel);
|
||||
}
|
||||
break;
|
||||
case MORPH_CLOSE:
|
||||
{
|
||||
rlVec runsDilated;
|
||||
|
||||
dilate_rle(runsSource, runsDilated, runsKernel);
|
||||
if (bBoundaryOnForErosion)
|
||||
erode_with_boundary_rle(runsDilated, sizeSource.width, sizeSource.height, runsDestination, runsKernel);
|
||||
else
|
||||
erode_rle(runsDilated, runsDestination, runsKernel);
|
||||
}
|
||||
break;
|
||||
case MORPH_GRADIENT:
|
||||
{
|
||||
rlVec runsEroded, runsDilated;
|
||||
if (bBoundaryOnForErosion)
|
||||
erode_with_boundary_rle(runsSource, sizeSource.width, sizeSource.height, runsEroded, runsKernel);
|
||||
else
|
||||
erode_rle(runsSource, runsEroded, runsKernel);
|
||||
dilate_rle(runsSource, runsDilated, runsKernel);
|
||||
subtract_rle(runsDilated, runsEroded, runsDestination);
|
||||
}
|
||||
break;
|
||||
case MORPH_TOPHAT:
|
||||
{
|
||||
rlVec runsEroded, runsOpened;
|
||||
|
||||
if (bBoundaryOnForErosion)
|
||||
erode_with_boundary_rle(runsSource, sizeSource.width, sizeSource.height, runsEroded, runsKernel);
|
||||
else
|
||||
erode_rle(runsSource, runsEroded, runsKernel);
|
||||
dilate_rle(runsEroded, runsOpened, runsKernel);
|
||||
subtract_rle(runsSource, runsOpened, runsDestination);
|
||||
}
|
||||
break;
|
||||
|
||||
case MORPH_BLACKHAT:
|
||||
{
|
||||
rlVec runsClosed, runsDilated;
|
||||
|
||||
dilate_rle(runsSource, runsDilated, runsKernel);
|
||||
if (bBoundaryOnForErosion)
|
||||
erode_with_boundary_rle(runsDilated, sizeSource.width, sizeSource.height, runsClosed, runsKernel);
|
||||
else
|
||||
erode_rle(runsDilated, runsClosed, runsKernel);
|
||||
|
||||
subtract_rle(runsClosed, runsSource, runsDestination);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case MORPH_HITMISS:
|
||||
CV_Error(Error::StsBadArg, "unknown or unsupported morphological operation");
|
||||
}
|
||||
convertToOutputArray(runsDestination, sizeSource, rlDest);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} //end of cv::ximgproc
|
||||
} //end of cv
|
||||
@@ -0,0 +1,770 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2021, Dr Seng Cheong Loke (lokesengcheong@gmail.com)
|
||||
//
|
||||
//
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <atomic>
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
ScanSegment::~ScanSegment()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
class ScanSegmentImpl CV_FINAL : public ScanSegment
|
||||
{
|
||||
#define UNKNOWN 0
|
||||
#define BORDER -1
|
||||
#define UNCLASSIFIED -2
|
||||
#define NONE -3
|
||||
|
||||
public:
|
||||
|
||||
ScanSegmentImpl(int image_width, int image_height, int num_superpixels, int slices, bool merge_small);
|
||||
|
||||
virtual ~ScanSegmentImpl();
|
||||
|
||||
virtual int getNumberOfSuperpixels() CV_OVERRIDE { return clusterCount; }
|
||||
|
||||
virtual void iterate(InputArray img) CV_OVERRIDE;
|
||||
|
||||
virtual void getLabels(OutputArray labels_out) CV_OVERRIDE;
|
||||
|
||||
virtual void getLabelContourMask(OutputArray image, bool thick_line = false) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
static const int neighbourCount = 8; // number of pixel neighbours
|
||||
static const int smallClustersDiv = 10000; // divide total pixels by this to give smallClusters
|
||||
const float tolerance100 = 10.0f; // colour tolerance for image size of 100x100px
|
||||
|
||||
int processthreads; // concurrent threads for parallel processing
|
||||
int width, height; // image size
|
||||
int superpixels; // number of superpixels
|
||||
bool merge; // merge small superpixels
|
||||
int indexSize; // size of label mat vector
|
||||
int clusterSize; // max size of clusters
|
||||
int clusterCount; // number of superpixels from the most recent iterate
|
||||
float adjTolerance; // adjusted colour tolerance
|
||||
|
||||
int horzDiv, vertDiv; // number of horizontal and vertical segments
|
||||
float horzLength, vertLength; // length of each segment
|
||||
int effectivethreads; // effective number of concurrent threads
|
||||
int smallClusters; // clusters below this pixel count are considered small for merging
|
||||
|
||||
cv::AutoBuffer<cv::Rect> seedRects; // autobuffer of seed rectangles
|
||||
cv::AutoBuffer<cv::Rect> seedRectsExt; // autobuffer of extended seed rectangles
|
||||
cv::AutoBuffer<cv::Rect> offsetRects; // autobuffer of offset rectangles
|
||||
cv::Point neighbourLoc[8] = { cv::Point(-1, -1), cv::Point(0, -1), cv::Point(1, -1), cv::Point(-1, 0), cv::Point(1, 0), cv::Point(-1, 1), cv::Point(0, 1), cv::Point(1, 1) }; // neighbour locations
|
||||
|
||||
std::vector<int> indexNeighbourVec; // indices for parallel processing
|
||||
std::vector<std::pair<int, int>> indexProcessVec;
|
||||
|
||||
cv::AutoBuffer<int> labelsBuffer; // label autobuffer
|
||||
cv::AutoBuffer<int> clusterBuffer; // cluster autobuffer
|
||||
cv::AutoBuffer<uchar> pixelBuffer; // pixel autobuffer
|
||||
std::vector<cv::AutoBuffer<int>> offsetVec; // vector of offset autobuffers
|
||||
cv::Vec3b* labBuffer; // lab buffer
|
||||
int neighbourLocBuffer[neighbourCount]; // neighbour locations
|
||||
|
||||
std::atomic<int> clusterIndex, clusterID; // atomic indices
|
||||
|
||||
cv::Mat src, labelsMat; // mats
|
||||
|
||||
struct WSNode
|
||||
{
|
||||
int next;
|
||||
int mask_ofs;
|
||||
int img_ofs;
|
||||
};
|
||||
|
||||
// Queue for WSNodes
|
||||
struct WSQueue
|
||||
{
|
||||
WSQueue() { first = last = 0; }
|
||||
int first, last;
|
||||
};
|
||||
|
||||
void OP1(int v);
|
||||
void OP2(std::pair<int, int> const& p);
|
||||
void OP3(int v);
|
||||
void OP4(std::pair<int, int> const& p);
|
||||
void expandCluster(int* offsetBuffer, const cv::Point& point);
|
||||
void calculateCluster(int* offsetBuffer, int* offsetEnd, int pointIndex, int currentClusterID);
|
||||
static int allocWSNodes(std::vector<WSNode>& storage);
|
||||
static void watershedEx(const cv::Mat& src, cv::Mat& dst);
|
||||
};
|
||||
|
||||
CV_EXPORTS Ptr<ScanSegment> createScanSegment(int image_width, int image_height, int num_superpixels, int slices, bool merge_small)
|
||||
{
|
||||
return makePtr<ScanSegmentImpl>(image_width, image_height, num_superpixels, slices, merge_small);
|
||||
}
|
||||
|
||||
ScanSegmentImpl::ScanSegmentImpl(int image_width, int image_height, int num_superpixels, int slices, bool merge_small)
|
||||
{
|
||||
// set the number of process threads
|
||||
processthreads = (slices > 0) ? slices : cv::getNumThreads();
|
||||
|
||||
width = image_width;
|
||||
height = image_height;
|
||||
superpixels = num_superpixels;
|
||||
merge = merge_small;
|
||||
indexSize = height * width;
|
||||
clusterSize = cvRound(1.1f * (float)(width * height) / (float)superpixels);
|
||||
clusterCount = 0;
|
||||
labelsMat = cv::Mat(height, width, CV_32SC1);
|
||||
|
||||
// divide bounds area into uniformly distributed rectangular segments
|
||||
int shortCount = cvFloor(sqrtf((float)processthreads));
|
||||
int longCount = processthreads / shortCount;
|
||||
horzDiv = width > height ? longCount : shortCount;
|
||||
vertDiv = width > height ? shortCount : longCount;
|
||||
horzLength = (float)width / (float)horzDiv;
|
||||
vertLength = (float)height / (float)vertDiv;
|
||||
effectivethreads = horzDiv * vertDiv;
|
||||
smallClusters = 0;
|
||||
|
||||
// get array of seed rects
|
||||
seedRects = cv::AutoBuffer<cv::Rect>(horzDiv * vertDiv);
|
||||
seedRectsExt = cv::AutoBuffer<cv::Rect>(horzDiv * vertDiv);
|
||||
offsetRects = cv::AutoBuffer<cv::Rect>(horzDiv * vertDiv);
|
||||
for (int y = 0; y < vertDiv; y++) {
|
||||
for (int x = 0; x < horzDiv; x++) {
|
||||
int xStart = cvFloor((float)x * horzLength);
|
||||
int yStart = cvFloor((float)y * vertLength);
|
||||
cv::Rect seedRect = cv::Rect(xStart, yStart, (int)(x == horzDiv - 1 ? width - xStart : horzLength), (int)(y == vertDiv - 1 ? height - yStart : vertLength));
|
||||
|
||||
int bnd_l = seedRect.x;
|
||||
int bnd_t = seedRect.y;
|
||||
int bnd_r = seedRect.x + seedRect.width - 1;
|
||||
int bnd_b = seedRect.y + seedRect.height - 1;
|
||||
if (bnd_l > 0) {
|
||||
bnd_l -= 1;
|
||||
}
|
||||
if (bnd_t > 0) {
|
||||
bnd_t -= 1;
|
||||
}
|
||||
if (bnd_r < width - 1) {
|
||||
bnd_r += 1;
|
||||
}
|
||||
if (bnd_b < height - 1) {
|
||||
bnd_b += 1;
|
||||
}
|
||||
|
||||
seedRects.data()[(y * horzDiv) + x] = seedRect;
|
||||
seedRectsExt.data()[(y * horzDiv) + x] = cv::Rect(bnd_l, bnd_t, bnd_r - bnd_l + 1, bnd_b - bnd_t + 1);
|
||||
offsetRects.data()[(y * horzDiv) + x] = cv::Rect(seedRect.x - bnd_l, seedRect.y - bnd_t, seedRect.width, seedRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
// get adjusted tolerance = (100 / average length (horz/vert)) x sqrt(3) [ie. euclidean lab colour distance sqrt(l2 + a2 + b2)] x tolerance100
|
||||
adjTolerance = (200.0f / (width + height)) * sqrtf(3) * tolerance100;
|
||||
adjTolerance = adjTolerance * adjTolerance;
|
||||
|
||||
// create neighbour vector
|
||||
indexNeighbourVec = std::vector<int>(effectivethreads);
|
||||
std::iota(indexNeighbourVec.begin(), indexNeighbourVec.end(), 0);
|
||||
|
||||
// create process vector
|
||||
indexProcessVec = std::vector<std::pair<int, int>>(processthreads);
|
||||
int processDiv = indexSize / processthreads;
|
||||
int processCurrent = 0;
|
||||
for (int i = 0; i < processthreads - 1; i++) {
|
||||
indexProcessVec[i] = std::make_pair(processCurrent, processCurrent + processDiv);
|
||||
processCurrent += processDiv;
|
||||
}
|
||||
indexProcessVec[processthreads - 1] = std::make_pair(processCurrent, indexSize);
|
||||
|
||||
// create buffers and initialise
|
||||
labelsBuffer = cv::AutoBuffer<int>(indexSize);
|
||||
clusterBuffer = cv::AutoBuffer<int>(indexSize);
|
||||
pixelBuffer = cv::AutoBuffer<uchar>(indexSize);
|
||||
offsetVec = std::vector<cv::AutoBuffer<int>>(effectivethreads);
|
||||
int offsetSize = (clusterSize + 1) * sizeof(int);
|
||||
for (int i = 0; i < effectivethreads; i++) {
|
||||
offsetVec[i] = cv::AutoBuffer<int>(offsetSize);
|
||||
}
|
||||
for (int i = 0; i < neighbourCount; i++) {
|
||||
neighbourLocBuffer[i] = (neighbourLoc[i].y * width) + neighbourLoc[i].x;
|
||||
}
|
||||
}
|
||||
|
||||
ScanSegmentImpl::~ScanSegmentImpl()
|
||||
{
|
||||
// clean up
|
||||
if (!src.empty()) {
|
||||
src.release();
|
||||
}
|
||||
if (!labelsMat.empty()) {
|
||||
labelsMat.release();
|
||||
}
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::iterate(InputArray img)
|
||||
{
|
||||
if (img.isMat())
|
||||
{
|
||||
// get Mat
|
||||
src = img.getMat();
|
||||
|
||||
// image should be valid
|
||||
CV_Assert(!src.empty());
|
||||
}
|
||||
else if (img.isMatVector())
|
||||
{
|
||||
std::vector<cv::Mat> vec;
|
||||
|
||||
// get vector Mat
|
||||
img.getMatVector(vec);
|
||||
|
||||
// array should be valid
|
||||
CV_Assert(!vec.empty());
|
||||
|
||||
// merge into Mat
|
||||
cv::merge(vec, src);
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsInternal, "Invalid InputArray.");
|
||||
|
||||
int depth = src.depth();
|
||||
|
||||
CV_Assert(src.size().width == width && src.size().height == height);
|
||||
CV_Assert(depth == CV_8U);
|
||||
CV_Assert(src.channels() == 3);
|
||||
|
||||
clusterCount = 0;
|
||||
clusterIndex.store(0);
|
||||
clusterID.store(1);
|
||||
|
||||
smallClusters = indexSize / smallClustersDiv;
|
||||
|
||||
// set labels to NONE
|
||||
labelsMat.setTo(NONE);
|
||||
|
||||
// set labels buffer to UNCLASSIFIED
|
||||
std::fill(labelsBuffer.data(), labelsBuffer.data() + indexSize, UNCLASSIFIED);
|
||||
|
||||
// apply light blur
|
||||
cv::medianBlur(src, src, 3);
|
||||
|
||||
// start at the center of the rect, then run through the remainder
|
||||
labBuffer = reinterpret_cast<cv::Vec3b*>(src.data);
|
||||
cv::parallel_for_(Range(0, (int)indexNeighbourVec.size()), [&](const Range& range) {
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
OP1(i);
|
||||
}
|
||||
});
|
||||
|
||||
if (merge) {
|
||||
// get cutoff size for clusters
|
||||
std::vector<std::pair<int, int>> countVec;
|
||||
int clusterIndexSize = clusterIndex.load();
|
||||
countVec.reserve(clusterIndexSize / 2);
|
||||
for (int i = 1; i < clusterIndexSize; i += 2) {
|
||||
int count = clusterBuffer.data()[i];
|
||||
if (count >= smallClusters) {
|
||||
int currentID = clusterBuffer.data()[i - 1];
|
||||
countVec.push_back(std::make_pair(currentID, count));
|
||||
}
|
||||
}
|
||||
|
||||
// sort descending
|
||||
std::sort(countVec.begin(), countVec.end(), [](const std::pair<int, int>& left, const std::pair<int, int>& right) {
|
||||
return left.second > right.second;
|
||||
});
|
||||
|
||||
int countSize = (int)countVec.size();
|
||||
int cutoff = MAX(smallClusters, countVec[MIN(countSize - 1, superpixels - 1)].second);
|
||||
clusterCount = (int)std::count_if(countVec.begin(), countVec.end(), [&cutoff](std::pair<int, int> p) {return p.second > cutoff; });
|
||||
|
||||
// change labels to 1 -> clusterCount, 0 = UNKNOWN, reuse clusterbuffer
|
||||
std::fill_n(clusterBuffer.data(), indexSize, UNKNOWN);
|
||||
int countLimit = cutoff == -1 ? (int)countVec.size() : clusterCount;
|
||||
for (int i = 0; i < countLimit; i++) {
|
||||
clusterBuffer.data()[countVec[i].first] = i + 1;
|
||||
}
|
||||
|
||||
parallel_for_(Range(0, (int)indexProcessVec.size()), [&](const Range& range) {
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
OP2(indexProcessVec[i]);
|
||||
}
|
||||
});
|
||||
|
||||
// make copy of labels buffer
|
||||
memcpy(labelsMat.data, labelsBuffer.data(), indexSize * sizeof(int));
|
||||
|
||||
// run watershed
|
||||
cv::parallel_for_(Range(0, (int)indexNeighbourVec.size()), [&](const Range& range) {
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
OP3(i);
|
||||
}
|
||||
});
|
||||
|
||||
// copy back to labels mat
|
||||
parallel_for_(Range(0, (int)indexProcessVec.size()), [&](const Range& range) {
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
OP4(indexProcessVec[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(labelsMat.data, labelsBuffer.data(), indexSize * sizeof(int));
|
||||
}
|
||||
|
||||
src.release();
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::OP1(int v)
|
||||
{
|
||||
cv::Rect seedRect = seedRects.data()[v];
|
||||
for (int y = seedRect.y; y < seedRect.y + seedRect.height; y++) {
|
||||
for (int x = seedRect.x; x < seedRect.x + seedRect.width; x++) {
|
||||
expandCluster(offsetVec[v].data(), cv::Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::OP2(std::pair<int, int> const& p)
|
||||
{
|
||||
for (int i = p.first; i < p.second; i++) {
|
||||
labelsBuffer.data()[i] = clusterBuffer.data()[labelsBuffer.data()[i]];
|
||||
if (labelsBuffer.data()[i] == UNKNOWN) {
|
||||
pixelBuffer.data()[i] = 255;
|
||||
}
|
||||
else {
|
||||
pixelBuffer.data()[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::OP3(int v)
|
||||
{
|
||||
cv::Rect seedRectExt = seedRectsExt.data()[v];
|
||||
cv::Mat seedLabels = labelsMat(seedRectExt).clone();
|
||||
watershedEx(src(seedRectExt), seedLabels);
|
||||
seedLabels(offsetRects.data()[v]).copyTo(labelsMat(seedRects.data()[v]));
|
||||
seedLabels.release();
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::OP4(std::pair<int, int> const& p)
|
||||
{
|
||||
for (int i = p.first; i < p.second; i++) {
|
||||
if (pixelBuffer.data()[i] == 0) {
|
||||
((int*)labelsMat.data)[i] = labelsBuffer.data()[i] - 1;
|
||||
}
|
||||
else {
|
||||
((int*)labelsMat.data)[i] -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// expand clusters from a point
|
||||
void ScanSegmentImpl::expandCluster(int* offsetBuffer, const cv::Point& point)
|
||||
{
|
||||
int pointIndex = (point.y * width) + point.x;
|
||||
if (labelsBuffer.data()[pointIndex] == UNCLASSIFIED) {
|
||||
int offsetStart = 0;
|
||||
int offsetEnd = 0;
|
||||
int currentClusterID = clusterID.fetch_add(1);
|
||||
|
||||
calculateCluster(offsetBuffer, &offsetEnd, pointIndex, currentClusterID);
|
||||
|
||||
if (offsetStart == offsetEnd) {
|
||||
labelsBuffer.data()[pointIndex] = UNKNOWN;
|
||||
}
|
||||
else {
|
||||
// set cluster id and get core point index
|
||||
labelsBuffer.data()[pointIndex] = currentClusterID;
|
||||
|
||||
while (offsetStart < offsetEnd) {
|
||||
int intoffset2 = *(offsetBuffer + offsetStart);
|
||||
offsetStart++;
|
||||
calculateCluster(offsetBuffer, &offsetEnd, intoffset2, currentClusterID);
|
||||
}
|
||||
|
||||
// add origin point
|
||||
offsetBuffer[offsetEnd] = pointIndex;
|
||||
offsetEnd++;
|
||||
|
||||
// store to buffer
|
||||
int currentClusterIndex = clusterIndex.fetch_add(2);
|
||||
clusterBuffer.data()[currentClusterIndex] = currentClusterID;
|
||||
clusterBuffer.data()[currentClusterIndex + 1] = offsetEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::calculateCluster(int* offsetBuffer, int* offsetEnd, int pointIndex, int currentClusterID)
|
||||
{
|
||||
for (int i = 0; i < neighbourCount; i++) {
|
||||
if (*offsetEnd < clusterSize) {
|
||||
int intoffset2 = pointIndex + neighbourLocBuffer[i];
|
||||
if (intoffset2 >= 0 && intoffset2 < indexSize && labelsBuffer.data()[intoffset2] == UNCLASSIFIED) {
|
||||
int diff1 = (int)labBuffer[pointIndex][0] - (int)labBuffer[intoffset2][0];
|
||||
int diff2 = (int)labBuffer[pointIndex][1] - (int)labBuffer[intoffset2][1];
|
||||
int diff3 = (int)labBuffer[pointIndex][2] - (int)labBuffer[intoffset2][2];
|
||||
|
||||
if ((diff1 * diff1) + (diff2 * diff2) + (diff3 * diff3) <= (int)adjTolerance) {
|
||||
labelsBuffer.data()[intoffset2] = currentClusterID;
|
||||
offsetBuffer[*offsetEnd] = intoffset2;
|
||||
(*offsetEnd)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { break; }
|
||||
}
|
||||
}
|
||||
|
||||
int ScanSegmentImpl::allocWSNodes(std::vector<ScanSegmentImpl::WSNode>& storage)
|
||||
{
|
||||
int sz = (int)storage.size();
|
||||
int newsz = MAX(128, sz * 3 / 2);
|
||||
|
||||
storage.resize(newsz);
|
||||
if (sz == 0)
|
||||
{
|
||||
storage[0].next = 0;
|
||||
sz = 1;
|
||||
}
|
||||
for (int i = sz; i < newsz - 1; i++)
|
||||
storage[i].next = i + 1;
|
||||
storage[newsz - 1].next = 0;
|
||||
return sz;
|
||||
}
|
||||
|
||||
//the modified version of watershed algorithm from OpenCV
|
||||
void ScanSegmentImpl::watershedEx(const cv::Mat& src, cv::Mat& dst)
|
||||
{
|
||||
// https://github.com/Seaball/watershed_with_mask
|
||||
|
||||
// Labels for pixels
|
||||
const int IN_QUEUE = -2; // Pixel visited
|
||||
// possible bit values = 2^8
|
||||
const int NQ = 256;
|
||||
|
||||
cv::Size size = src.size();
|
||||
int channel = 3;
|
||||
// Vector of every created node
|
||||
std::vector<WSNode> storage;
|
||||
int free_node = 0, node;
|
||||
// Priority queue of queues of nodes
|
||||
// from high priority (0) to low priority (255)
|
||||
WSQueue q[NQ];
|
||||
// Non-empty queue with highest priority
|
||||
int active_queue;
|
||||
int i, j;
|
||||
// Color differences
|
||||
int db, dg, dr;
|
||||
int subs_tab[513];
|
||||
|
||||
// MAX(a,b) = b + MAX(a-b,0)
|
||||
#define ws_max(a,b) ((b) + subs_tab[(a)-(b)+NQ])
|
||||
// MIN(a,b) = a - MAX(a-b,0)
|
||||
#define ws_min(a,b) ((a) - subs_tab[(a)-(b)+NQ])
|
||||
|
||||
// Create a new node with offsets mofs and iofs in queue idx
|
||||
#define ws_push(idx,mofs,iofs) \
|
||||
{ \
|
||||
if (!free_node) \
|
||||
free_node = allocWSNodes(storage); \
|
||||
node = free_node; \
|
||||
free_node = storage[free_node].next; \
|
||||
storage[node].next = 0; \
|
||||
storage[node].mask_ofs = mofs; \
|
||||
storage[node].img_ofs = iofs; \
|
||||
if (q[idx].last) \
|
||||
storage[q[idx].last].next = node; \
|
||||
else \
|
||||
q[idx].first = node; \
|
||||
q[idx].last = node; \
|
||||
}
|
||||
|
||||
// Get next node from queue idx
|
||||
#define ws_pop(idx,mofs,iofs) \
|
||||
{ \
|
||||
node = q[idx].first; \
|
||||
q[idx].first = storage[node].next; \
|
||||
if (!storage[node].next) \
|
||||
q[idx].last = 0; \
|
||||
storage[node].next = free_node; \
|
||||
free_node = node; \
|
||||
mofs = storage[node].mask_ofs; \
|
||||
iofs = storage[node].img_ofs; \
|
||||
}
|
||||
|
||||
// Get highest absolute channel difference in diff
|
||||
#define c_diff(ptr1,ptr2,diff) \
|
||||
{ \
|
||||
db = std::abs((ptr1)[0] - (ptr2)[0]); \
|
||||
dg = std::abs((ptr1)[1] - (ptr2)[1]); \
|
||||
dr = std::abs((ptr1)[2] - (ptr2)[2]); \
|
||||
diff = ws_max(db, dg); \
|
||||
diff = ws_max(diff, dr); \
|
||||
CV_Assert(0 <= diff && diff <= 255); \
|
||||
}
|
||||
|
||||
CV_Assert(src.type() == CV_8UC3 && dst.type() == CV_32SC1);
|
||||
CV_Assert(src.size() == dst.size());
|
||||
|
||||
// Current pixel in input image
|
||||
const uchar* img = src.ptr();
|
||||
// Step size to next row in input image
|
||||
int istep = int(src.step / sizeof(img[0]));
|
||||
|
||||
// Current pixel in mask image
|
||||
int* mask = dst.ptr<int>();
|
||||
// Step size to next row in mask image
|
||||
int mstep = int(dst.step / sizeof(mask[0]));
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
subs_tab[i] = 0;
|
||||
for (i = 256; i <= 512; i++)
|
||||
subs_tab[i] = i - 256;
|
||||
|
||||
//for (j = 0; j < size.width; j++)
|
||||
//mask[j] = mask[j + mstep*(size.height - 1)] = 0;
|
||||
|
||||
// initial phase: put all the neighbor pixels of each marker to the ordered queue -
|
||||
// determine the initial boundaries of the basins
|
||||
for (i = 1; i < size.height - 1; i++) {
|
||||
img += istep; mask += mstep;
|
||||
mask[0] = mask[size.width - 1] = 0; // boundary pixels
|
||||
|
||||
for (j = 1; j < size.width - 1; j++) {
|
||||
int* m = mask + j;
|
||||
if (m[0] < 0)
|
||||
m[0] = 0;
|
||||
if (m[0] == 0 && (m[-1] > 0 || m[1] > 0 || m[-mstep] > 0 || m[mstep] > 0))
|
||||
{
|
||||
// Find smallest difference to adjacent markers
|
||||
const uchar* ptr = img + j * channel;
|
||||
int idx = 256, t;
|
||||
if (m[-1] > 0) {
|
||||
c_diff(ptr, ptr - channel, idx);
|
||||
}
|
||||
if (m[1] > 0) {
|
||||
c_diff(ptr, ptr + channel, t);
|
||||
idx = ws_min(idx, t);
|
||||
}
|
||||
if (m[-mstep] > 0) {
|
||||
c_diff(ptr, ptr - istep, t);
|
||||
idx = ws_min(idx, t);
|
||||
}
|
||||
if (m[mstep] > 0) {
|
||||
c_diff(ptr, ptr + istep, t);
|
||||
idx = ws_min(idx, t);
|
||||
}
|
||||
|
||||
// Add to according queue
|
||||
CV_Assert(0 <= idx && idx <= 255);
|
||||
ws_push(idx, i * mstep + j, i * istep + j * channel);
|
||||
m[0] = IN_QUEUE;//initial unvisited
|
||||
}
|
||||
}
|
||||
}
|
||||
// find the first non-empty queue
|
||||
for (i = 0; i < NQ; i++)
|
||||
if (q[i].first)
|
||||
break;
|
||||
|
||||
// if there is no markers, exit immediately
|
||||
if (i == NQ)
|
||||
return;
|
||||
|
||||
active_queue = i;//first non-empty priority queue
|
||||
img = src.ptr();
|
||||
mask = dst.ptr<int>();
|
||||
|
||||
// recursively fill the basins
|
||||
int diff = 0, temp = 0;
|
||||
for (;;)
|
||||
{
|
||||
int mofs, iofs;
|
||||
int lab = 0, t;
|
||||
int* m;
|
||||
const uchar* ptr;
|
||||
|
||||
// Get non-empty queue with highest priority
|
||||
// Exit condition: empty priority queue
|
||||
if (q[active_queue].first == 0)
|
||||
{
|
||||
for (i = active_queue + 1; i < NQ; i++)
|
||||
if (q[i].first)
|
||||
break;
|
||||
if (i == NQ)
|
||||
{
|
||||
std::vector<WSNode>().swap(storage);
|
||||
break;
|
||||
}
|
||||
active_queue = i;
|
||||
}
|
||||
|
||||
// Get next node
|
||||
ws_pop(active_queue, mofs, iofs);
|
||||
int top = 1, bottom = 1, left = 1, right = 1;
|
||||
if (0 <= mofs && mofs < mstep)//pixel on the top
|
||||
top = 0;
|
||||
if ((mofs % mstep) == 0)//pixel in the left column
|
||||
left = 0;
|
||||
if ((mofs + 1) % mstep == 0)//pixel in the right column
|
||||
right = 0;
|
||||
if (mstep * (size.height - 1) <= mofs && mofs < mstep * size.height)//pixel on the bottom
|
||||
bottom = 0;
|
||||
|
||||
// Calculate pointer to current pixel in input and marker image
|
||||
m = mask + mofs;
|
||||
ptr = img + iofs;
|
||||
// Check surrounding pixels for labels to determine label for current pixel
|
||||
if (left) {//the left point can be visited
|
||||
t = m[-1];
|
||||
if (t > 0) {
|
||||
lab = t;
|
||||
c_diff(ptr, ptr - channel, diff);
|
||||
}
|
||||
}
|
||||
if (right) {// Right point can be visited
|
||||
t = m[1];
|
||||
if (t > 0) {
|
||||
if (lab == 0) {//and this point didn't be labeled before
|
||||
lab = t;
|
||||
c_diff(ptr, ptr + channel, diff);
|
||||
}
|
||||
else if (t != lab) {
|
||||
c_diff(ptr, ptr + channel, temp);
|
||||
diff = ws_min(diff, temp);
|
||||
if (diff == temp)
|
||||
lab = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (top) {
|
||||
t = m[-mstep]; // Top
|
||||
if (t > 0) {
|
||||
if (lab == 0) {//and this point didn't be labeled before
|
||||
lab = t;
|
||||
c_diff(ptr, ptr - istep, diff);
|
||||
}
|
||||
else if (t != lab) {
|
||||
c_diff(ptr, ptr - istep, temp);
|
||||
diff = ws_min(diff, temp);
|
||||
if (diff == temp)
|
||||
lab = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bottom) {
|
||||
t = m[mstep]; // Bottom
|
||||
if (t > 0) {
|
||||
if (lab == 0) {
|
||||
lab = t;
|
||||
}
|
||||
else if (t != lab) {
|
||||
c_diff(ptr, ptr + istep, temp);
|
||||
diff = ws_min(diff, temp);
|
||||
if (diff == temp)
|
||||
lab = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set label to current pixel in marker image
|
||||
CV_Assert(lab != 0);//lab must be labeled with a nonzero number
|
||||
m[0] = lab;
|
||||
|
||||
// Add adjacent, unlabeled pixels to corresponding queue
|
||||
if (left) {
|
||||
if (m[-1] == 0)//left pixel with marker 0
|
||||
{
|
||||
c_diff(ptr, ptr - channel, t);
|
||||
ws_push(t, mofs - 1, iofs - channel);
|
||||
active_queue = ws_min(active_queue, t);
|
||||
m[-1] = IN_QUEUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (right)
|
||||
{
|
||||
if (m[1] == 0)//right pixel with marker 0
|
||||
{
|
||||
c_diff(ptr, ptr + channel, t);
|
||||
ws_push(t, mofs + 1, iofs + channel);
|
||||
active_queue = ws_min(active_queue, t);
|
||||
m[1] = IN_QUEUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (top)
|
||||
{
|
||||
if (m[-mstep] == 0)//top pixel with marker 0
|
||||
{
|
||||
c_diff(ptr, ptr - istep, t);
|
||||
ws_push(t, mofs - mstep, iofs - istep);
|
||||
active_queue = ws_min(active_queue, t);
|
||||
m[-mstep] = IN_QUEUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bottom) {
|
||||
if (m[mstep] == 0)//down pixel with marker 0
|
||||
{
|
||||
c_diff(ptr, ptr + istep, t);
|
||||
ws_push(t, mofs + mstep, iofs + istep);
|
||||
active_queue = ws_min(active_queue, t);
|
||||
m[mstep] = IN_QUEUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::getLabels(OutputArray labels_out)
|
||||
{
|
||||
labels_out.assign(labelsMat);
|
||||
}
|
||||
|
||||
void ScanSegmentImpl::getLabelContourMask(OutputArray image, bool thick_line)
|
||||
{
|
||||
image.create(height, width, CV_8UC1);
|
||||
cv::Mat dst = image.getMat();
|
||||
dst.setTo(cv::Scalar(0));
|
||||
|
||||
const int dx8[8] = { -1, -1, 0, 1, 1, 1, 0, -1 };
|
||||
const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1 };
|
||||
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
for (int k = 0; k < width; k++)
|
||||
{
|
||||
int neighbors = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int x = k + dx8[i];
|
||||
int y = j + dy8[i];
|
||||
|
||||
if ((x >= 0 && x < width) && (y >= 0 && y < height))
|
||||
{
|
||||
int index = y * width + x;
|
||||
int mainindex = j * width + k;
|
||||
if (((int*)labelsMat.data)[mainindex] != ((int*)labelsMat.data)[index])
|
||||
{
|
||||
if (thick_line || !*dst.ptr<uchar>(y, x))
|
||||
neighbors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (neighbors > 1)
|
||||
*dst.ptr<uchar>(j, k) = (uchar)255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ximgproc
|
||||
} // namespace cv
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,903 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
#include "advanced_types.hpp"
|
||||
|
||||
#ifdef CV_CXX11
|
||||
#define CV_USE_PARALLEL_PREDICT_EDGES_1 1
|
||||
#define CV_USE_PARALLEL_PREDICT_EDGES_2 0 //1, see https://github.com/opencv/opencv_contrib/issues/2346
|
||||
#else
|
||||
#define CV_USE_PARALLEL_PREDICT_EDGES_1 0
|
||||
#define CV_USE_PARALLEL_PREDICT_EDGES_2 0
|
||||
#endif
|
||||
|
||||
/********************* Helper functions *********************/
|
||||
|
||||
/*!
|
||||
* Lightweight wrapper over cv::resize
|
||||
*
|
||||
* \param src : source image to resize
|
||||
* \param dst : destination image size
|
||||
* \return resized image
|
||||
*/
|
||||
static cv::Mat imresize(const cv::Mat &src, const cv::Size &nSize)
|
||||
{
|
||||
cv::Mat dst;
|
||||
if (nSize.width < src.size().width
|
||||
&& nSize.height < src.size().height)
|
||||
cv::resize(src, dst, nSize, 0.0, 0.0, cv::INTER_AREA);
|
||||
else
|
||||
cv::resize(src, dst, nSize, 0.0, 0.0, cv::INTER_LINEAR);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The function filters src with triangle filter with radius equal rad
|
||||
*
|
||||
* \param src : source image to filter
|
||||
* \param rad : radius of filtering kernel
|
||||
* \return filtering result
|
||||
*/
|
||||
static cv::Mat imsmooth(const cv::Mat &src, const int rad)
|
||||
{
|
||||
if (rad == 0)
|
||||
return src;
|
||||
else
|
||||
{
|
||||
const float p = 12.0f/rad/(rad + 2) - 2;
|
||||
cv::Mat dst;
|
||||
|
||||
if (rad <= 1)
|
||||
{
|
||||
CV_INIT_VECTOR(kernelXY, float, {1/(p + 2), p/(p + 2), 1/(p + 2)});
|
||||
cv::sepFilter2D(src, dst, -1, kernelXY, kernelXY);
|
||||
}
|
||||
else
|
||||
{
|
||||
float nrml = CV_SQR(rad + 1.0f);
|
||||
|
||||
std::vector <float> kernelXY(2*rad + 1);
|
||||
for (int i = 0; i <= rad; ++i)
|
||||
{
|
||||
kernelXY[2*rad - i] = (i + 1) / nrml;
|
||||
kernelXY[i] = (i + 1) / nrml;
|
||||
}
|
||||
sepFilter2D(src, dst, -1, kernelXY, kernelXY);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* The function implements rgb to luv conversion in a way similar
|
||||
* to UCSD computer vision toolbox
|
||||
*
|
||||
* \param src : source image (RGB, float, in [0;1]) to convert
|
||||
* \return converted image in luv colorspace
|
||||
*/
|
||||
static cv::Mat rgb2luv(const cv::Mat &src)
|
||||
{
|
||||
cv::Mat dst(src.size(), src.type());
|
||||
|
||||
const float a = CV_CUBE(29.0f)/27;
|
||||
const float y0 = 8.0f/a;
|
||||
|
||||
const float mX[] = {0.430574f, 0.341550f, 0.178325f};
|
||||
const float mY[] = {0.222015f, 0.706655f, 0.071330f};
|
||||
const float mZ[] = {0.020183f, 0.129553f, 0.939180f};
|
||||
|
||||
const float maxi= 1.0f/270;
|
||||
const float minu= -88*maxi;
|
||||
const float minv= -134*maxi;
|
||||
|
||||
const float un = 0.197833f;
|
||||
const float vn = 0.468331f;
|
||||
|
||||
// build (padded) lookup table for y->l conversion assuming y in [0,1]
|
||||
std::vector <float> lTable(1024);
|
||||
for (int i = 0; i < 1024; ++i)
|
||||
{
|
||||
float y = i/1024.0f;
|
||||
float l = y > y0 ? 116*powf(y, 1.0f/3.0f) - 16 : y*a;
|
||||
|
||||
lTable[i] = l*maxi;
|
||||
}
|
||||
for (int i = 0; i < 40; ++i) {
|
||||
lTable.push_back(*lTable.rbegin());
|
||||
}
|
||||
|
||||
const int nchannels = 3;
|
||||
|
||||
for (int i = 0; i < src.rows; ++i)
|
||||
{
|
||||
const float *pSrc = src.ptr<float>(i);
|
||||
float *pDst = dst.ptr<float>(i);
|
||||
|
||||
for (int j = 0; j < src.cols*nchannels; j += nchannels)
|
||||
{
|
||||
const float rgb[] = {pSrc[j + 0], pSrc[j + 1], pSrc[j + 2]};
|
||||
|
||||
const float xyz[] = {mX[0]*rgb[0] + mX[1]*rgb[1] + mX[2]*rgb[2],
|
||||
mY[0]*rgb[0] + mY[1]*rgb[1] + mY[2]*rgb[2],
|
||||
mZ[0]*rgb[0] + mZ[1]*rgb[1] + mZ[2]*rgb[2]};
|
||||
const float nz = 1.0f / float(xyz[0] + 15*xyz[1] + 3*xyz[2] + 1e-35);
|
||||
|
||||
const float l = pDst[j] = lTable[cvFloor(1024*xyz[1])];
|
||||
|
||||
pDst[j + 1] = l * (13*4*xyz[0]*nz - 13*un) - minu;;
|
||||
pDst[j + 2] = l * (13*9*xyz[1]*nz - 13*vn) - minv;
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The function computes gradient magnitude and weighted (with magnitude)
|
||||
* orientation histogram. Magnitude is additionally normalized
|
||||
* by dividing on imsmooth(M, gnrmRad) + 0.01;
|
||||
*
|
||||
* \param src : source image
|
||||
* \param magnitude : gradient magnitude
|
||||
* \param histogram : gradient orientation nBins-channels histogram
|
||||
* \param nBins : number of gradient orientations
|
||||
* \param pSize : factor to downscale histogram
|
||||
* \param gnrmRad : radius for magnitude normalization
|
||||
*/
|
||||
static void gradientHist(const cv::Mat &src, cv::Mat &magnitude, cv::Mat &histogram,
|
||||
const int nBins, const int pSize, const int gnrmRad)
|
||||
{
|
||||
cv::Mat phase, Dx, Dy;
|
||||
|
||||
magnitude.create( src.size(), cv::DataType<float>::type );
|
||||
phase.create( src.size(), cv::DataType<float>::type );
|
||||
histogram.create( cv::Size( cvCeil(src.size().width/float(pSize)),
|
||||
cvCeil(src.size().height/float(pSize)) ),
|
||||
CV_MAKETYPE(cv::DataType<float>::type, nBins) );
|
||||
|
||||
histogram.setTo(0);
|
||||
|
||||
cv::Sobel( src, Dx, cv::DataType<float>::type,
|
||||
1, 0, 1, 1.0, 0.0, cv::BORDER_REFLECT );
|
||||
cv::Sobel( src, Dy, cv::DataType<float>::type,
|
||||
0, 1, 1, 1.0, 0.0, cv::BORDER_REFLECT );
|
||||
|
||||
int nchannels = src.channels();
|
||||
|
||||
for (int i = 0; i < src.rows; ++i)
|
||||
{
|
||||
const float *pDx = Dx.ptr<float>(i);
|
||||
const float *pDy = Dy.ptr<float>(i);
|
||||
|
||||
float *pMagnitude = magnitude.ptr<float>(i);
|
||||
float *pPhase = phase.ptr<float>(i);
|
||||
|
||||
for (int j = 0; j < src.cols*nchannels; j += nchannels)
|
||||
{
|
||||
float fMagn = float(-1e-5), fdx = 0, fdy = 0;
|
||||
for (int k = 0; k < nchannels; ++k)
|
||||
{
|
||||
float cMagn = CV_SQR( pDx[j + k] ) + CV_SQR( pDy[j + k] );
|
||||
if (cMagn > fMagn)
|
||||
{
|
||||
fMagn = cMagn;
|
||||
fdx = pDx[j + k];
|
||||
fdy = pDy[j + k];
|
||||
}
|
||||
}
|
||||
|
||||
pMagnitude[j/nchannels] = sqrtf(fMagn);
|
||||
|
||||
float angle = cv::fastAtan2(fdy, fdx) / 180.0f - 1.0f * (fdy < 0);
|
||||
if (std::fabs(fdx) + std::fabs(fdy) < 1e-5)
|
||||
angle = 0.5f;
|
||||
pPhase[j/nchannels] = angle;
|
||||
}
|
||||
}
|
||||
|
||||
magnitude /= imsmooth( magnitude, gnrmRad )
|
||||
+ 0.01*cv::Mat::ones( magnitude.size(), magnitude.type() );
|
||||
|
||||
for (int i = 0; i < phase.rows; ++i)
|
||||
{
|
||||
const float *pPhase = phase.ptr<float>(i);
|
||||
const float *pMagn = magnitude.ptr<float>(i);
|
||||
|
||||
float *pHist = histogram.ptr<float>(i/pSize);
|
||||
|
||||
for (int j = 0; j < phase.cols; ++j)
|
||||
{
|
||||
int angle = cvRound(pPhase[j]*nBins);
|
||||
if(angle >= nBins)
|
||||
{
|
||||
angle = 0;
|
||||
}
|
||||
const int index = (j/pSize)*nBins + angle;
|
||||
pHist[index] += pMagn[j] / CV_SQR(pSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* The class parallelizing the edgenms algorithm.
|
||||
*
|
||||
* \param E : edge image
|
||||
* \param O : orientation image
|
||||
* \param dst : destination image
|
||||
* \param r : radius for NMS suppression
|
||||
* \param s : radius for boundary suppression
|
||||
* \param m : multiplier for conservative suppression
|
||||
*/
|
||||
class NmsInvoker : public cv::ParallelLoopBody
|
||||
{
|
||||
|
||||
private:
|
||||
const cv::Mat &E;
|
||||
const cv::Mat &O;
|
||||
cv::Mat &dst;
|
||||
const int r;
|
||||
const float m;
|
||||
|
||||
public:
|
||||
NmsInvoker(const cv::Mat &_E, const cv::Mat &_O, cv::Mat &_dst, const int _r, const float _m)
|
||||
: E(_E), O(_O), dst(_dst), r(_r), m(_m)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const cv::Range &range) const CV_OVERRIDE
|
||||
{
|
||||
for (int x = range.start; x < range.end; x++)
|
||||
{
|
||||
const float *e_ptr = E.ptr<float>(x);
|
||||
const float *o_ptr = O.ptr<float>(x);
|
||||
float *dst_ptr = dst.ptr<float>(x);
|
||||
for (int y=0; y < E.cols; y++)
|
||||
{
|
||||
float e = e_ptr[y];
|
||||
dst_ptr[y] = e;
|
||||
if (!e) continue;
|
||||
e *= m;
|
||||
float coso = cos(o_ptr[y]);
|
||||
float sino = sin(o_ptr[y]);
|
||||
for (int d=-r; d<=r; d++)
|
||||
{
|
||||
if (d)
|
||||
{
|
||||
float xdcos = x+d*coso;
|
||||
float ydsin = y+d*sino;
|
||||
xdcos = xdcos < 0 ? 0 : (xdcos > E.rows - 1.001f ? E.rows - 1.001f : xdcos);
|
||||
ydsin = ydsin < 0 ? 0 : (ydsin > E.cols - 1.001f ? E.cols - 1.001f : ydsin);
|
||||
int x0 = (int)xdcos;
|
||||
int y0 = (int)ydsin;
|
||||
int x1 = x0 + 1;
|
||||
int y1 = y0 + 1;
|
||||
float dx0 = xdcos - x0;
|
||||
float dy0 = ydsin - y0;
|
||||
float dx1 = 1 - dx0;
|
||||
float dy1 = 1 - dy0;
|
||||
float e0 = E.at<float>(x0, y0) * dx1 * dy1 +
|
||||
E.at<float>(x1, y0) * dx0 * dy1 +
|
||||
E.at<float>(x0, y1) * dx1 * dy0 +
|
||||
E.at<float>(x1, y1) * dx0 * dy0;
|
||||
|
||||
if(e < e0)
|
||||
{
|
||||
dst_ptr[y] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/********************* RFFeatureGetter class *********************/
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
class RFFeatureGetterImpl : public RFFeatureGetter
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Default constructor
|
||||
*/
|
||||
RFFeatureGetterImpl() : name("RFFeatureGetter"){}
|
||||
|
||||
/*!
|
||||
* The method extracts features from img and store them to features.
|
||||
* Extracted features are appropriate for StructuredEdgeDetection::predictEdges.
|
||||
*
|
||||
* \param src : source image (RGB, float, in [0;1]) to extract features
|
||||
* \param features : destination feature image
|
||||
*
|
||||
* \param gnrmRad : __rf.options.gradientNormalizationRadius
|
||||
* \param gsmthRad : __rf.options.gradientSmoothingRadius
|
||||
* \param shrink : __rf.options.shrinkNumber
|
||||
* \param outNum : __rf.options.numberOfOutputChannels
|
||||
* \param gradNum : __rf.options.numberOfGradientOrientations
|
||||
*/
|
||||
virtual void getFeatures(const Mat &src, Mat &features, const int gnrmRad, const int gsmthRad,
|
||||
const int shrink, const int outNum, const int gradNum) const CV_OVERRIDE
|
||||
{
|
||||
cv::Mat luvImg = rgb2luv(src);
|
||||
|
||||
std::vector <cv::Mat> featureArray;
|
||||
|
||||
cv::Size nSize = src.size() / float(shrink);
|
||||
split( imresize(luvImg, nSize), featureArray );
|
||||
|
||||
CV_INIT_VECTOR(scales, float, {1.0f, 0.5f});
|
||||
|
||||
for (size_t i = 0; i < scales.size(); ++i)
|
||||
{
|
||||
int pSize = std::max( 1, int(shrink*scales[i]) );
|
||||
|
||||
cv::Mat magnitude, histogram;
|
||||
gradientHist(/**/ imsmooth(imresize(luvImg, scales[i]*src.size()), gsmthRad),
|
||||
magnitude, histogram, gradNum, pSize, gnrmRad /**/);
|
||||
|
||||
featureArray.push_back(/**/ imresize( magnitude, nSize ).clone() /**/);
|
||||
featureArray.push_back(/**/ imresize( histogram, nSize ).clone() /**/);
|
||||
}
|
||||
|
||||
// Mixing
|
||||
int resType = CV_MAKETYPE(cv::DataType<float>::type, outNum);
|
||||
features.create(nSize, resType);
|
||||
|
||||
std::vector <int> fromTo;
|
||||
for (int i = 0; i < 2*outNum; ++i)
|
||||
fromTo.push_back(i/2);
|
||||
|
||||
mixChannels(featureArray, features, fromTo);
|
||||
}
|
||||
|
||||
protected:
|
||||
/*! algorithm name */
|
||||
String name;
|
||||
};
|
||||
|
||||
Ptr<RFFeatureGetter> createRFFeatureGetter()
|
||||
{
|
||||
return makePtr<RFFeatureGetterImpl>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/********************* StructuredEdgeDetection class *********************/
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
|
||||
class StructuredEdgeDetectionImpl : public StructuredEdgeDetection
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* This constructor loads __rf model from filename
|
||||
*
|
||||
* \param filename : name of the file where the model is stored
|
||||
*/
|
||||
StructuredEdgeDetectionImpl(const cv::String &filename,
|
||||
Ptr<const RFFeatureGetter> _howToGetFeatures)
|
||||
: name("StructuredEdgeDetection"),
|
||||
howToGetFeatures( (!_howToGetFeatures.empty())
|
||||
? _howToGetFeatures
|
||||
: createRFFeatureGetter().staticCast<const RFFeatureGetter>() )
|
||||
{
|
||||
cv::FileStorage modelFile(filename, FileStorage::READ);
|
||||
CV_Assert( modelFile.isOpened() );
|
||||
|
||||
__rf.options.stride
|
||||
= modelFile["options"]["stride"];
|
||||
__rf.options.shrinkNumber
|
||||
= modelFile["options"]["shrinkNumber"];
|
||||
__rf.options.patchSize
|
||||
= modelFile["options"]["patchSize"];
|
||||
__rf.options.patchInnerSize
|
||||
= modelFile["options"]["patchInnerSize"];
|
||||
|
||||
__rf.options.numberOfGradientOrientations
|
||||
= modelFile["options"]["numberOfGradientOrientations"];
|
||||
__rf.options.gradientSmoothingRadius
|
||||
= modelFile["options"]["gradientSmoothingRadius"];
|
||||
__rf.options.regFeatureSmoothingRadius
|
||||
= modelFile["options"]["regFeatureSmoothingRadius"];
|
||||
__rf.options.ssFeatureSmoothingRadius
|
||||
= modelFile["options"]["ssFeatureSmoothingRadius"];
|
||||
__rf.options.gradientNormalizationRadius
|
||||
= modelFile["options"]["gradientNormalizationRadius"];
|
||||
|
||||
__rf.options.selfsimilarityGridSize
|
||||
= modelFile["options"]["selfsimilarityGridSize"];
|
||||
|
||||
__rf.options.numberOfTrees
|
||||
= modelFile["options"]["numberOfTrees"];
|
||||
__rf.options.numberOfTreesToEvaluate
|
||||
= modelFile["options"]["numberOfTreesToEvaluate"];
|
||||
|
||||
__rf.options.numberOfOutputChannels =
|
||||
2*(__rf.options.numberOfGradientOrientations + 1) + 3;
|
||||
//--------------------------------------------
|
||||
|
||||
cv::FileNode childs = modelFile["childs"];
|
||||
cv::FileNode featureIds = modelFile["featureIds"];
|
||||
|
||||
std::vector <int> currentTree;
|
||||
|
||||
for(cv::FileNodeIterator it = childs.begin();
|
||||
it != childs.end(); ++it)
|
||||
{
|
||||
(*it) >> currentTree;
|
||||
std::copy(currentTree.begin(), currentTree.end(),
|
||||
std::back_inserter(__rf.childs));
|
||||
}
|
||||
|
||||
for(cv::FileNodeIterator it = featureIds.begin();
|
||||
it != featureIds.end(); ++it)
|
||||
{
|
||||
(*it) >> currentTree;
|
||||
std::copy(currentTree.begin(), currentTree.end(),
|
||||
std::back_inserter(__rf.featureIds));
|
||||
}
|
||||
|
||||
cv::FileNode thresholds = modelFile["thresholds"];
|
||||
std::vector <float> fcurrentTree;
|
||||
|
||||
for(cv::FileNodeIterator it = thresholds.begin();
|
||||
it != thresholds.end(); ++it)
|
||||
{
|
||||
(*it) >> fcurrentTree;
|
||||
std::copy(fcurrentTree.begin(), fcurrentTree.end(),
|
||||
std::back_inserter(__rf.thresholds));
|
||||
}
|
||||
|
||||
cv::FileNode edgeBoundaries = modelFile["edgeBoundaries"];
|
||||
cv::FileNode edgeBins = modelFile["edgeBins"];
|
||||
|
||||
for(cv::FileNodeIterator it = edgeBoundaries.begin();
|
||||
it != edgeBoundaries.end(); ++it)
|
||||
{
|
||||
(*it) >> currentTree;
|
||||
std::copy(currentTree.begin(), currentTree.end(),
|
||||
std::back_inserter(__rf.edgeBoundaries));
|
||||
}
|
||||
|
||||
for(cv::FileNodeIterator it = edgeBins.begin();
|
||||
it != edgeBins.end(); ++it)
|
||||
{
|
||||
(*it) >> currentTree;
|
||||
std::copy(currentTree.begin(), currentTree.end(),
|
||||
std::back_inserter(__rf.edgeBins));
|
||||
}
|
||||
|
||||
__rf.numberOfTreeNodes = int( __rf.childs.size() ) / __rf.options.numberOfTrees;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The function detects edges in src and draw them to dst
|
||||
*
|
||||
* \param _src : source image (RGB, float, in [0;1]) to detect edges
|
||||
* \param _dst : destination image (grayscale, float, in [0;1])
|
||||
* where edges are drawn
|
||||
*/
|
||||
void detectEdges(cv::InputArray _src, cv::OutputArray _dst) const CV_OVERRIDE
|
||||
{
|
||||
CV_Assert( _src.type() == CV_32FC3 );
|
||||
|
||||
_dst.createSameSize( _src, cv::DataType<float>::type );
|
||||
_dst.setTo(0);
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
int padding = ( __rf.options.patchSize
|
||||
- __rf.options.patchInnerSize )/2;
|
||||
|
||||
cv::Mat nSrc;
|
||||
copyMakeBorder( _src, nSrc, padding, padding,
|
||||
padding, padding, BORDER_REFLECT );
|
||||
|
||||
NChannelsMat features;
|
||||
createRFFeatureGetter()->getFeatures( nSrc, features,
|
||||
__rf.options.gradientNormalizationRadius,
|
||||
__rf.options.gradientSmoothingRadius,
|
||||
__rf.options.shrinkNumber,
|
||||
__rf.options.numberOfOutputChannels,
|
||||
__rf.options.numberOfGradientOrientations );
|
||||
predictEdges( features, dst );
|
||||
}
|
||||
|
||||
/*!
|
||||
* The function computes orientation from edge image.
|
||||
*
|
||||
* \param src : edge image.
|
||||
* \param dst : orientation image.
|
||||
* \param r : filter radius.
|
||||
*/
|
||||
void computeOrientation(cv::InputArray _src, cv::OutputArray _dst) const CV_OVERRIDE
|
||||
{
|
||||
CV_Assert( _src.type() == CV_32FC1 );
|
||||
|
||||
cv::Mat Oxx, Oxy, Oyy;
|
||||
|
||||
_dst.createSameSize( _src, _src.type() );
|
||||
_dst.setTo(0);
|
||||
|
||||
Mat src = _src.getMat();
|
||||
cv::Mat E_conv = imsmooth(src, __rf.options.gradientNormalizationRadius);
|
||||
|
||||
Sobel(E_conv, Oxx, -1, 2, 0);
|
||||
Sobel(E_conv, Oxy, -1, 1, 1);
|
||||
Sobel(E_conv, Oyy, -1, 0, 2);
|
||||
|
||||
Mat dst = _dst.getMat();
|
||||
float *o = dst.ptr<float>();
|
||||
float *oxx = Oxx.ptr<float>();
|
||||
float *oxy = Oxy.ptr<float>();
|
||||
float *oyy = Oyy.ptr<float>();
|
||||
for (int i = 0; i < dst.rows * dst.cols; i++)
|
||||
{
|
||||
int xysign = -((oxy[i] > 0) - (oxy[i] < 0));
|
||||
o[i] = (atan((oyy[i] * xysign / (oxx[i] + 1e-5))) > 0) ? (float) fmod(
|
||||
atan((oyy[i] * xysign / (oxx[i] + 1e-5))), CV_PI) : (float) fmod(
|
||||
atan((oyy[i] * xysign / (oxx[i] + 1e-5))) + CV_PI, CV_PI);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* The function suppress edges where edge is stronger in orthogonal direction
|
||||
* \param edge_image : edge image from detectEdges function.
|
||||
* \param orientation_image : orientation image from computeOrientation function.
|
||||
* \param _dst : suppressed image (grayscale, float, in [0;1])
|
||||
* \param r : radius for NMS suppression.
|
||||
* \param s : radius for boundary suppression.
|
||||
* \param m : multiplier for conservative suppression.
|
||||
* \param isParallel: enables/disables parallel computing.
|
||||
*/
|
||||
void edgesNms(cv::InputArray edge_image, cv::InputArray orientation_image, cv::OutputArray _dst, int r, int s, float m, bool isParallel) const CV_OVERRIDE
|
||||
{
|
||||
CV_Assert(edge_image.type() == CV_32FC1);
|
||||
CV_Assert(orientation_image.type() == CV_32FC1);
|
||||
|
||||
cv::Mat E = edge_image.getMat();
|
||||
cv::Mat O = orientation_image.getMat();
|
||||
cv::Mat E_t = E.t();
|
||||
cv::Mat O_t = O.t();
|
||||
|
||||
cv::Mat dst = _dst.getMat();
|
||||
dst.create(E.cols, E.rows, E.type());
|
||||
dst.setTo(0);
|
||||
|
||||
cv::Range sizeRange = cv::Range(0, E_t.rows);
|
||||
NmsInvoker body = NmsInvoker(E_t, O_t, dst, r, m);
|
||||
if (isParallel)
|
||||
{
|
||||
cv::parallel_for_(sizeRange, body);
|
||||
} else
|
||||
{
|
||||
body(sizeRange);
|
||||
}
|
||||
|
||||
s = s > E_t.rows / 2 ? E_t.rows / 2 : s;
|
||||
s = s > E_t.cols / 2 ? E_t.cols / 2 : s;
|
||||
for (int x=0; x<s; x++)
|
||||
{
|
||||
for (int y=0; y<E_t.cols; y++)
|
||||
{
|
||||
dst.at<float>(x, y) *= x / (float)s;
|
||||
dst.at<float>(E_t.rows-1-x, y) *= x / (float)s;
|
||||
}
|
||||
}
|
||||
|
||||
for (int x=0; x < E_t.rows; x++)
|
||||
{
|
||||
for (int y=0; y < s; y++)
|
||||
{
|
||||
dst.at<float>(x, y) *= y / (float)s;
|
||||
dst.at<float>(x, E_t.cols-1-y) *= y / (float)s;
|
||||
}
|
||||
}
|
||||
transpose(dst, dst);
|
||||
dst.copyTo(_dst);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Private method used by process method. The function
|
||||
* predict edges in n-channel feature image and store them to dst.
|
||||
*
|
||||
* \param features : source image (n-channels, float) to detect edges
|
||||
* \param dst : destination image (grayscale, float, in [0;1]) where edges are drawn
|
||||
*/
|
||||
void predictEdges(const NChannelsMat &features, cv::Mat &dst) const
|
||||
{
|
||||
int shrink = __rf.options.shrinkNumber;
|
||||
int rfs = __rf.options.regFeatureSmoothingRadius;
|
||||
int sfs = __rf.options.ssFeatureSmoothingRadius;
|
||||
|
||||
int nTreesEval = __rf.options.numberOfTreesToEvaluate;
|
||||
int nTrees = __rf.options.numberOfTrees;
|
||||
int nTreesNodes = __rf.numberOfTreeNodes;
|
||||
|
||||
const int nchannels = features.channels();
|
||||
int pSize = __rf.options.patchSize;
|
||||
|
||||
int nFeatures = CV_SQR(pSize/shrink)*nchannels;
|
||||
int outNum = __rf.options.numberOfOutputChannels;
|
||||
|
||||
int stride = __rf.options.stride;
|
||||
int ipSize = __rf.options.patchInnerSize;
|
||||
int gridSize = __rf.options.selfsimilarityGridSize;
|
||||
|
||||
const int height = cvCeil( double(features.rows*shrink - pSize) / stride );
|
||||
const int width = cvCeil( double(features.cols*shrink - pSize) / stride );
|
||||
// image size in patches with overlapping
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
NChannelsMat regFeatures = imsmooth(features, cvRound(rfs / float(shrink)));
|
||||
NChannelsMat ssFeatures = imsmooth(features, cvRound(sfs / float(shrink)));
|
||||
|
||||
NChannelsMat indexes(height, width, CV_MAKETYPE(DataType<int>::type, nTreesEval));
|
||||
|
||||
std::vector <int> offsetI(/**/ CV_SQR(pSize/shrink)*nchannels, 0);
|
||||
for (int i = 0; i < CV_SQR(pSize/shrink)*nchannels; ++i)
|
||||
{
|
||||
int z = i / CV_SQR(pSize/shrink);
|
||||
int y = ( i % CV_SQR(pSize/shrink) )/(pSize/shrink);
|
||||
int x = ( i % CV_SQR(pSize/shrink) )%(pSize/shrink);
|
||||
|
||||
offsetI[i] = x*features.cols*nchannels + y*nchannels + z;
|
||||
}
|
||||
// lookup table for mapping linear index to offsets
|
||||
|
||||
std::vector <int> offsetE(/**/ CV_SQR(ipSize)*outNum, 0);
|
||||
for (int i = 0; i < CV_SQR(ipSize)*outNum; ++i)
|
||||
{
|
||||
int z = i / CV_SQR(ipSize);
|
||||
int y = ( i % CV_SQR(ipSize) )/ipSize;
|
||||
int x = ( i % CV_SQR(ipSize) )%ipSize;
|
||||
|
||||
offsetE[i] = x*dst.cols*outNum + y*outNum + z;
|
||||
}
|
||||
// lookup table for mapping linear index to offsets
|
||||
|
||||
std::vector <int> offsetX( CV_SQR(gridSize)*(CV_SQR(gridSize) - 1)/2 * nchannels, 0);
|
||||
std::vector <int> offsetY( CV_SQR(gridSize)*(CV_SQR(gridSize) - 1)/2 * nchannels, 0);
|
||||
|
||||
int hc = cvRound( (pSize/shrink) / (2.0*gridSize) );
|
||||
// half of cell
|
||||
std::vector <int> gridPositions;
|
||||
for(int i = 0; i < gridSize; i++)
|
||||
gridPositions.push_back( int( (i+1)*(pSize/shrink + 2*hc - 1)/(gridSize + 1.0) - hc + 0.5f ) );
|
||||
|
||||
for (int i = 0, n = 0; i < CV_SQR(gridSize)*nchannels; ++i)
|
||||
for (int j = (i%CV_SQR(gridSize)) + 1; j < CV_SQR(gridSize); ++j, ++n)
|
||||
{
|
||||
int z = i / CV_SQR(gridSize);
|
||||
|
||||
int x1 = gridPositions[i%CV_SQR(gridSize)%gridSize];
|
||||
int y1 = gridPositions[i%CV_SQR(gridSize)/gridSize];
|
||||
|
||||
int x2 = gridPositions[j%gridSize];
|
||||
int y2 = gridPositions[j/gridSize];
|
||||
|
||||
offsetX[n] = x1*features.cols*nchannels + y1*nchannels + z;
|
||||
offsetY[n] = x2*features.cols*nchannels + y2*nchannels + z;
|
||||
}
|
||||
// lookup tables for mapping linear index to offset pairs
|
||||
|
||||
#if CV_USE_PARALLEL_PREDICT_EDGES_1
|
||||
parallel_for_(cv::Range(0, height), [&](const cv::Range& range)
|
||||
#else
|
||||
const cv::Range range(0, height);
|
||||
#endif
|
||||
{
|
||||
for(int i = range.start; i < range.end; ++i) {
|
||||
float *regFeaturesPtr = regFeatures.ptr<float>(i*stride/shrink);
|
||||
float *ssFeaturesPtr = ssFeatures.ptr<float>(i*stride/shrink);
|
||||
|
||||
int *indexPtr = indexes.ptr<int>(i);
|
||||
|
||||
for (int j = 0, k = 0; j < width; ++k, j += !(k %= nTreesEval))
|
||||
// for j,k in [0;width)x[0;nTreesEval)
|
||||
{
|
||||
int baseNode = ( ((i + j)%(2*nTreesEval) + k)%nTrees )*nTreesNodes;
|
||||
int currentNode = baseNode;
|
||||
// select root node of the tree to evaluate
|
||||
|
||||
int offset = (j*stride/shrink)*nchannels;
|
||||
while ( __rf.childs[currentNode] != 0 )
|
||||
{
|
||||
int currentId = __rf.featureIds[currentNode];
|
||||
float currentFeature;
|
||||
|
||||
if (currentId >= nFeatures)
|
||||
{
|
||||
int xIndex = offsetX[currentId - nFeatures];
|
||||
float A = ssFeaturesPtr[offset + xIndex];
|
||||
|
||||
int yIndex = offsetY[currentId - nFeatures];
|
||||
float B = ssFeaturesPtr[offset + yIndex];
|
||||
|
||||
currentFeature = A - B;
|
||||
}
|
||||
else
|
||||
currentFeature = regFeaturesPtr[offset + offsetI[currentId]];
|
||||
|
||||
// compare feature to threshold and move left or right accordingly
|
||||
if (currentFeature < __rf.thresholds[currentNode])
|
||||
currentNode = baseNode + __rf.childs[currentNode] - 1;
|
||||
else
|
||||
currentNode = baseNode + __rf.childs[currentNode];
|
||||
}
|
||||
|
||||
indexPtr[j*nTreesEval + k] = currentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if CV_USE_PARALLEL_PREDICT_EDGES_1
|
||||
);
|
||||
#endif
|
||||
|
||||
NChannelsMat dstM(dst.size(),
|
||||
CV_MAKETYPE(DataType<float>::type, outNum));
|
||||
dstM.setTo(0);
|
||||
|
||||
float step = 2.0f * CV_SQR(stride) / CV_SQR(ipSize) / nTreesEval;
|
||||
#if CV_USE_PARALLEL_PREDICT_EDGES_2
|
||||
parallel_for_(cv::Range(0, height), [&](const cv::Range& range)
|
||||
#elif CV_USE_PARALLEL_PREDICT_EDGES_1
|
||||
const cv::Range range(0, height);
|
||||
#endif
|
||||
{
|
||||
for(int i = range.start; i < range.end; ++i)
|
||||
{
|
||||
int *pIndex = indexes.ptr<int>(i);
|
||||
float *pDst = dstM.ptr<float>(i*stride);
|
||||
|
||||
for (int j = 0, k = 0; j < width; ++k, j += !(k %= nTreesEval))
|
||||
{// for j,k in [0;width)x[0;nTreesEval)
|
||||
|
||||
int currentNode = pIndex[j*nTreesEval + k];
|
||||
size_t sizeBoundaries = __rf.edgeBoundaries.size();
|
||||
int convertedBoundaries = static_cast<int>(sizeBoundaries);
|
||||
int nBnds = (convertedBoundaries - 1) / (nTreesNodes * nTrees);
|
||||
int start = __rf.edgeBoundaries[currentNode * nBnds];
|
||||
int finish = __rf.edgeBoundaries[currentNode * nBnds + 1];
|
||||
|
||||
if (start == finish)
|
||||
continue;
|
||||
|
||||
int offset = j*stride*outNum;
|
||||
for (int p = start; p < finish; ++p)
|
||||
pDst[offset + offsetE[__rf.edgeBins[p]]] += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if CV_USE_PARALLEL_PREDICT_EDGES_2
|
||||
);
|
||||
#endif
|
||||
|
||||
cv::reduce( dstM.reshape(1, int( dstM.total() ) ), dstM, 2, REDUCE_SUM);
|
||||
imsmooth( dstM.reshape(1, dst.rows), 1 ).copyTo(dst);
|
||||
}
|
||||
|
||||
/********************* Members *********************/
|
||||
protected:
|
||||
/*! algorithm name */
|
||||
String name;
|
||||
|
||||
/*! optional feature getter (getFeatures method) */
|
||||
Ptr<const RFFeatureGetter> howToGetFeatures;
|
||||
|
||||
/*! random forest used to detect edges */
|
||||
struct RandomForest
|
||||
{
|
||||
/*! random forest options, e.g. number of trees */
|
||||
struct RandomForestOptions
|
||||
{
|
||||
// model params
|
||||
|
||||
int numberOfOutputChannels; /*!< number of edge orientation bins for output */
|
||||
|
||||
int patchSize; /*!< width of image patches */
|
||||
int patchInnerSize; /*!< width of predicted part inside patch*/
|
||||
|
||||
// feature params
|
||||
|
||||
int regFeatureSmoothingRadius; /*!< radius for smoothing of regular features
|
||||
* (using convolution with triangle filter) */
|
||||
|
||||
int ssFeatureSmoothingRadius; /*!< radius for smoothing of additional features
|
||||
* (using convolution with triangle filter) */
|
||||
|
||||
int shrinkNumber; /*!< amount to shrink channels */
|
||||
|
||||
int numberOfGradientOrientations; /*!< number of orientations per gradient scale */
|
||||
|
||||
int gradientSmoothingRadius; /*!< radius for smoothing of gradients
|
||||
* (using convolution with triangle filter) */
|
||||
|
||||
int gradientNormalizationRadius; /*!< gradient normalization radius */
|
||||
int selfsimilarityGridSize; /*!< number of self similarity cells */
|
||||
|
||||
// detection params
|
||||
int numberOfTrees; /*!< number of trees in forest to train */
|
||||
int numberOfTreesToEvaluate; /*!< number of trees to evaluate per location */
|
||||
|
||||
int stride; /*!< stride at which to compute edges */
|
||||
|
||||
} options;
|
||||
|
||||
int numberOfTreeNodes;
|
||||
|
||||
std::vector <int> featureIds; /*!< feature coordinate thresholded at k-th node */
|
||||
std::vector <float> thresholds; /*!< threshold applied to featureIds[k] at k-th node */
|
||||
std::vector <int> childs; /*!< k --> child[k] - 1, child[k] */
|
||||
|
||||
std::vector <int> edgeBoundaries; /*!< ... */
|
||||
std::vector <int> edgeBins; /*!< ... */
|
||||
} __rf;
|
||||
};
|
||||
|
||||
Ptr<StructuredEdgeDetection> createStructuredEdgeDetection(const String &model,
|
||||
Ptr<const RFFeatureGetter> howToGetFeatures)
|
||||
{
|
||||
return makePtr<StructuredEdgeDetectionImpl>(model, howToGetFeatures);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace cv {
|
||||
namespace ximgproc {
|
||||
|
||||
// look up table - there is one entry for each of the 2^8=256 possible
|
||||
// combinations of 8 binary neighbors.
|
||||
static uint8_t lut_zhang_iter0[] = {
|
||||
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
|
||||
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
||||
1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0,
|
||||
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1};
|
||||
|
||||
static uint8_t lut_zhang_iter1[] = {
|
||||
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
|
||||
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
||||
0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0,
|
||||
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1,
|
||||
0, 1, 1, 1};
|
||||
|
||||
static uint8_t lut_guo_iter0[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
|
||||
0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||||
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1};
|
||||
|
||||
static uint8_t lut_guo_iter1[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
|
||||
1, 1, 1, 1};
|
||||
|
||||
// Applies a thinning iteration to a binary image
|
||||
static void thinningIteration(Mat img, int iter, int thinningType){
|
||||
Mat marker = Mat::zeros(img.size(), CV_8UC1);
|
||||
int rows = img.rows;
|
||||
int cols = img.cols;
|
||||
marker.col(0).setTo(1);
|
||||
marker.col(cols - 1).setTo(1);
|
||||
marker.row(0).setTo(1);
|
||||
marker.row(rows - 1).setTo(1);
|
||||
|
||||
if(thinningType == THINNING_ZHANGSUEN){
|
||||
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
|
||||
int i = postion[0];
|
||||
int j = postion[1];
|
||||
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
|
||||
return;
|
||||
|
||||
auto ptr = img.ptr(i, j); // p1
|
||||
|
||||
// p9 p2 p3
|
||||
// p8 p1 p4
|
||||
// p7 p6 p5
|
||||
uchar p2 = ptr[-cols];
|
||||
uchar p3 = ptr[-cols + 1];
|
||||
uchar p4 = ptr[1];
|
||||
uchar p5 = ptr[cols + 1];
|
||||
uchar p6 = ptr[cols];
|
||||
uchar p7 = ptr[cols - 1];
|
||||
uchar p8 = ptr[-1];
|
||||
uchar p9 = ptr[-cols - 1];
|
||||
|
||||
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
|
||||
|
||||
if (iter == 0)
|
||||
value = lut_zhang_iter0[neighbors];
|
||||
else
|
||||
value = lut_zhang_iter1[neighbors];
|
||||
|
||||
//int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
|
||||
// (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
|
||||
// (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
|
||||
// (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
|
||||
//int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
|
||||
//int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
|
||||
//int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
|
||||
//if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) value = 0;
|
||||
// else value = 1;
|
||||
});
|
||||
}
|
||||
if(thinningType == THINNING_GUOHALL){
|
||||
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
|
||||
int i = postion[0];
|
||||
int j = postion[1];
|
||||
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
|
||||
return;
|
||||
|
||||
auto ptr = img.ptr(i, j); // p1
|
||||
|
||||
// p9 p2 p3
|
||||
// p8 p1 p4
|
||||
// p7 p6 p5
|
||||
uchar p2 = ptr[-cols];
|
||||
uchar p3 = ptr[-cols + 1];
|
||||
uchar p4 = ptr[1];
|
||||
uchar p5 = ptr[cols + 1];
|
||||
uchar p6 = ptr[cols];
|
||||
uchar p7 = ptr[cols - 1];
|
||||
uchar p8 = ptr[-1];
|
||||
uchar p9 = ptr[-cols - 1];
|
||||
|
||||
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
|
||||
|
||||
if (iter == 0)
|
||||
value = lut_guo_iter0[neighbors];
|
||||
else
|
||||
value = lut_guo_iter1[neighbors];
|
||||
|
||||
//int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
|
||||
// ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
|
||||
//int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
|
||||
//int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
|
||||
//int N = N1 < N2 ? N1 : N2;
|
||||
//int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
|
||||
//if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) value = 0;
|
||||
// else value = 1;
|
||||
});
|
||||
}
|
||||
|
||||
img &= marker;
|
||||
}
|
||||
|
||||
// Apply the thinning procedure to a given image
|
||||
void thinning(InputArray input, OutputArray output, int thinningType){
|
||||
Mat processed = input.getMat().clone();
|
||||
CV_CheckTypeEQ(processed.type(), CV_8UC1, "");
|
||||
// Enforce the range of the input image to be in between 0 - 255
|
||||
processed /= 255;
|
||||
|
||||
Mat prev = processed.clone();
|
||||
Mat diff;
|
||||
|
||||
do {
|
||||
thinningIteration(processed, 0, thinningType);
|
||||
thinningIteration(processed, 1, thinningType);
|
||||
absdiff(processed, prev, diff);
|
||||
if (!hasNonZero(diff)) break;
|
||||
processed.copyTo(prev);
|
||||
}
|
||||
while (true);
|
||||
|
||||
processed *= 255;
|
||||
|
||||
output.assign(processed);
|
||||
}
|
||||
|
||||
} //namespace ximgproc
|
||||
} //namespace cv
|
||||
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
*
|
||||
* License Agreement
|
||||
* For Open Source Computer Vision Library
|
||||
* (3 - clause BSD License)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met :
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and / or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the names of the copyright holders nor the names of the contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* This software is provided by the copyright holders and contributors "as is" and
|
||||
* any express or implied warranties, including, but not limited to, the implied
|
||||
* warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
* In no event shall copyright holders or contributors be liable for any direct,
|
||||
* indirect, incidental, special, exemplary, or consequential damages
|
||||
* (including, but not limited to, procurement of substitute goods or services;
|
||||
* loss of use, data, or profits; or business interruption) however caused
|
||||
* and on any theory of liability, whether in contract, strict liability,
|
||||
* or tort(including negligence or otherwise) arising in any way out of
|
||||
* the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
namespace{
|
||||
|
||||
using namespace cv::ximgproc;
|
||||
|
||||
/***************************************************************/
|
||||
/* Function: from32FTo32S
|
||||
* Description: adaptive quantization for changing a floating-point 1D image to integer image.
|
||||
* The adaptive quantization strategy is based on binary search, which searches an
|
||||
* upper bound of quantization error.
|
||||
* The function also return a mapping between quantized value (32F) and quantized index (32S).
|
||||
* The mapping is used to convert integer image back to floating-point image after filtering.
|
||||
***************************************************************/
|
||||
void from32FTo32S(Mat &img, Mat &outImg, int nI, float *mapping)
|
||||
{
|
||||
int rows = img.rows, cols = img.cols;
|
||||
size_t alls = (size_t)rows * cols;
|
||||
CV_Assert(alls < INT_MAX);
|
||||
|
||||
CV_Assert(img.isContinuous());
|
||||
float *imgPtr = img.ptr<float>();
|
||||
typedef pair<float,int> pairFI;
|
||||
std::vector<pairFI> data(alls);
|
||||
|
||||
// Sort all pixels of the image by ascending order of pixel value
|
||||
for (size_t i = 0; i < alls; i++)
|
||||
{
|
||||
pairFI& d = data[i];
|
||||
d.second = (int)i;
|
||||
d.first = imgPtr[i];
|
||||
}
|
||||
|
||||
struct PixelValueOrder {
|
||||
static bool compare(const pairFI &a, const pairFI &b) {
|
||||
return a.first < b.first;
|
||||
}
|
||||
};
|
||||
sort(data.begin(), data.end(), PixelValueOrder::compare);
|
||||
|
||||
// Find lower bound and upper bound of the pixel values
|
||||
double maxVal = data[alls - 1].first, minVal = data[0].first;
|
||||
|
||||
const float maxRange = (float)(maxVal - minVal);
|
||||
|
||||
float l = 0, r = maxRange*2.0f/nI;
|
||||
// Perform binary search on error bound
|
||||
while (r > l)
|
||||
{
|
||||
float m = (r + l) * 0.5f;
|
||||
if (m == r || m == l)
|
||||
break; // bailout on numeric accuracy limit
|
||||
bool suc = true;
|
||||
float base = (float)minVal;
|
||||
int cnt = 0;
|
||||
for (size_t i = 0; i < alls; i++)
|
||||
{
|
||||
if (data[i].first > base + m)
|
||||
{
|
||||
cnt++;
|
||||
base = data[i].first;
|
||||
if (cnt == nI)
|
||||
{
|
||||
suc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (suc)
|
||||
r = m;
|
||||
else
|
||||
l = m;
|
||||
}
|
||||
|
||||
Mat retImg(img.size(),CV_32SC1);
|
||||
CV_Assert(retImg.isContinuous());
|
||||
int *retImgPtr = retImg.ptr<int>();
|
||||
|
||||
// In the sorted list, divide pixel values into clusters according to the minimum error bound
|
||||
// Quantize each value to the median of its cluster
|
||||
// Also record the mapping of quantized value and quantized index.
|
||||
float base = (float)minVal;
|
||||
int baseI = 0;
|
||||
int cnt = 0;
|
||||
for (size_t i = 0; i < alls; i++)
|
||||
{
|
||||
if (data[i].first > base + r)
|
||||
{
|
||||
mapping[cnt] = data[(baseI+i-1)>>1].first; //median
|
||||
cnt++;
|
||||
base = data[i].first;
|
||||
baseI = (int)i;
|
||||
}
|
||||
retImgPtr[data[i].second] = cnt;
|
||||
}
|
||||
// tail: i == alls
|
||||
mapping[cnt] = data[(baseI+alls-1)>>1].first; // median
|
||||
|
||||
//end of the function
|
||||
swap(outImg, retImg);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* Function: from32STo32F
|
||||
* Description: convert the quantization index image back to the floating-point image accroding to the mapping
|
||||
***************************************************************/
|
||||
void from32STo32F(Mat &img, Mat &outImg, float *mapping)
|
||||
{
|
||||
Mat retImg(img.size(),CV_32F);
|
||||
CV_Assert(img.isContinuous());
|
||||
CV_Assert(retImg.isContinuous());
|
||||
int rows = img.rows, cols = img.cols, alls = rows*cols;
|
||||
float *retImgPtr = retImg.ptr<float>();
|
||||
int *imgPtr = img.ptr<int>();
|
||||
|
||||
// convert 32S index to 32F real value
|
||||
for(int i=0;i<alls;i++)
|
||||
{
|
||||
retImgPtr[i] = mapping[imgPtr[i]];
|
||||
}
|
||||
|
||||
// end of the function
|
||||
outImg = retImg;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************
|
||||
* Function: float2D
|
||||
* Description: allocate a 2D float array with dimension "dim1 x dim2"
|
||||
***************************************************************/
|
||||
float** float2D(int dim1, int dim2)
|
||||
{
|
||||
float **ret = new float*[dim1];
|
||||
ret[0] = new float[dim1*dim2];
|
||||
for(int i=1;i<dim1;i++)ret[i] = ret[i-1]+dim2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function: float2D_release
|
||||
* Description: deallocate the 2D array created by float2D()
|
||||
***************************************************************/
|
||||
void float2D_release(float **p)
|
||||
{
|
||||
delete []p[0];
|
||||
delete []p;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function: int2D
|
||||
* Description: allocate a 2D integer array with dimension "dim1 x dim2"
|
||||
***************************************************************/
|
||||
int** int2D(int dim1, int dim2)
|
||||
{
|
||||
int **ret = new int*[dim1];
|
||||
ret[0] = new int[dim1*dim2];
|
||||
for(int i=1;i<dim1;i++)ret[i] = ret[i-1]+dim2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function: int2D_release
|
||||
* Description: deallocate the 2D array created by int2D()
|
||||
***************************************************************/
|
||||
void int2D_release(int **p)
|
||||
{
|
||||
delete []p[0];
|
||||
delete []p;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function: updateBCB
|
||||
* Description: maintain the necklace table of BCB
|
||||
***************************************************************/
|
||||
inline void updateBCB(int &num,int *f,int *b,int i,int v)
|
||||
{
|
||||
static int p1,p2;
|
||||
|
||||
if(i)
|
||||
{
|
||||
if(!num)
|
||||
{ // cell is becoming non-empty
|
||||
p2=f[0];
|
||||
f[0]=i;
|
||||
f[i]=p2;
|
||||
b[p2]=i;
|
||||
b[i]=0;
|
||||
}
|
||||
else if(!(num+v))
|
||||
{// cell is becoming empty
|
||||
p1=b[i],p2=f[i];
|
||||
f[p1]=p2;
|
||||
b[p2]=p1;
|
||||
}
|
||||
}
|
||||
|
||||
// update the cell count
|
||||
num += v;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function: featureIndexing
|
||||
* Description: convert uchar feature image "F" to CV_32SC1 type.
|
||||
* If F is 3-channel, perform k-means clustering
|
||||
* If F is 1-channel, only perform type-casting
|
||||
***************************************************************/
|
||||
void featureIndexing(Mat &F, float **&wMap, int &nF, float sigmaI, int weightType){
|
||||
// Configuration and Declaration
|
||||
Mat FNew;
|
||||
int cols = F.cols, rows = F.rows;
|
||||
int alls = cols * rows;
|
||||
int KmeansAttempts=1;
|
||||
|
||||
/* For 1 channel feature image (uchar)*/
|
||||
if(F.channels() == 1)
|
||||
{
|
||||
nF = 256;
|
||||
|
||||
// Type-casting
|
||||
F.convertTo(FNew, CV_32S);
|
||||
|
||||
// Compute weight map (weight between each pair of feature index)
|
||||
wMap = float2D(nF,nF);
|
||||
float nSigmaI = sigmaI;
|
||||
float divider = (1.0f/(2*nSigmaI*nSigmaI));
|
||||
|
||||
for(int i=0;i<nF;i++)
|
||||
{
|
||||
for(int j=i;j<nF;j++)
|
||||
{
|
||||
float diff = fabs((float)(i-j));
|
||||
float val;
|
||||
|
||||
switch(weightType)
|
||||
{
|
||||
case WMF_EXP: val = exp(-(diff*diff)*divider); break;
|
||||
case WMF_IV1: val = 1.0f/(diff+nSigmaI); break;
|
||||
case WMF_IV2: val = 1.0f / (diff*diff+nSigmaI*nSigmaI); break;
|
||||
case WMF_COS: val = 1.0f; break;
|
||||
case WMF_JAC: val = (float)(min(i,j)*1.0/max(i,j)); break;
|
||||
case WMF_OFF: val = 1.0f; break;
|
||||
default: val = exp(-(diff*diff)*divider);
|
||||
}
|
||||
|
||||
wMap[i][j] = wMap[j][i] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For 3 channel feature image (uchar)*/
|
||||
else if(F.channels() == 3)
|
||||
{
|
||||
const int shift = 2; // 256(8-bit)->64(6-bit)
|
||||
const int LOW_NUM = 256>>shift;
|
||||
static int hash[LOW_NUM][LOW_NUM][LOW_NUM]={{{0}}};
|
||||
|
||||
memset(hash,0,sizeof(hash));
|
||||
|
||||
// throw pixels into a 2D histogram
|
||||
int candCnt = 0;
|
||||
{
|
||||
int lowR,lowG,lowB;
|
||||
uchar *FPtr = F.ptr<uchar>();
|
||||
for(int i=0,i3=0;i<alls;i++,i3+=3)
|
||||
{
|
||||
lowB = FPtr[i3]>>shift;
|
||||
lowG = FPtr[i3+1]>>shift;
|
||||
lowR = FPtr[i3+2]>>shift;
|
||||
|
||||
if(hash[lowB][lowG][lowR]==0)
|
||||
{
|
||||
candCnt++;
|
||||
hash[lowB][lowG][lowR]=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nF = min(nF, candCnt);
|
||||
Mat samples(candCnt,3,CV_32F);
|
||||
|
||||
//prepare for K-means
|
||||
int top=0;
|
||||
for(int i=0;i<LOW_NUM;i++)for(int j=0;j<LOW_NUM;j++)for(int k=0;k<LOW_NUM;k++){
|
||||
if(hash[i][j][k]){
|
||||
samples.ptr<float>(top)[0] = (float)i;
|
||||
samples.ptr<float>(top)[1] = (float)j;
|
||||
samples.ptr<float>(top)[2] = (float)k;
|
||||
top++;
|
||||
}
|
||||
}
|
||||
|
||||
//do K-means
|
||||
Mat labels;
|
||||
Mat centers;
|
||||
kmeans(samples, nF, labels, TermCriteria(TermCriteria::MAX_ITER| TermCriteria::EPS, 0, 10000), KmeansAttempts, KMEANS_PP_CENTERS, centers );
|
||||
|
||||
//make connection (i,j,k) <-> index
|
||||
top = 0;
|
||||
for(int i=0;i<LOW_NUM;i++)for(int j=0;j<LOW_NUM;j++)for(int k=0;k<LOW_NUM;k++)
|
||||
{
|
||||
if(hash[i][j][k])
|
||||
{
|
||||
hash[i][j][k] = labels.ptr<int>(top)[0];
|
||||
top++;
|
||||
}
|
||||
}
|
||||
|
||||
// generate index map
|
||||
FNew = Mat(F.size(),CV_32SC1);
|
||||
|
||||
int lowR,lowG,lowB;
|
||||
uchar *FPtr = F.ptr<uchar>();
|
||||
for(int i=0,i3=0;i<alls;i++,i3+=3)
|
||||
{
|
||||
lowB = FPtr[i3]>>shift;
|
||||
lowG = FPtr[i3+1]>>shift;
|
||||
lowR = FPtr[i3+2]>>shift;
|
||||
|
||||
FNew.ptr<int>()[i] = hash[lowB][lowG][lowR];
|
||||
}
|
||||
|
||||
// Compute weight map (weight between each pair of feature index)
|
||||
wMap = float2D(nF,nF);
|
||||
float nSigmaI = sigmaI/256.0f*LOW_NUM;
|
||||
float divider = (1.0f/(2*nSigmaI*nSigmaI));
|
||||
|
||||
float *length = new float[nF];
|
||||
for(int i=0;i<nF;i++)
|
||||
{
|
||||
float a0 = centers.ptr<float>(i)[0];
|
||||
float a1 = centers.ptr<float>(i)[1];
|
||||
float a2 = centers.ptr<float>(i)[2];
|
||||
length[i] = sqrt(a0*a0+a1*a1+a2*a2);
|
||||
}
|
||||
|
||||
for(int i=0;i<nF;i++)
|
||||
{
|
||||
for(int j=i;j<nF;j++)
|
||||
{
|
||||
float a0 = centers.ptr<float>(i)[0], b0 = centers.ptr<float>(j)[0];
|
||||
float a1 = centers.ptr<float>(i)[1], b1 = centers.ptr<float>(j)[1];
|
||||
float a2 = centers.ptr<float>(i)[2], b2 = centers.ptr<float>(j)[2];
|
||||
float diff0 = a0-b0;
|
||||
float diff1 = a1-b1;
|
||||
float diff2 = a2-b2;
|
||||
|
||||
float val;
|
||||
|
||||
switch(weightType)
|
||||
{
|
||||
case WMF_EXP: val = exp(-(diff0*diff0+diff1*diff1+diff2*diff2)*divider); break;
|
||||
case WMF_IV1: val = 1.0f/(fabs(diff0)+fabs(diff1)+fabs(diff2)+nSigmaI); break;
|
||||
case WMF_IV2: val = 1.0f / (diff0*diff0+diff1*diff1+diff2*diff2+nSigmaI*nSigmaI); break;
|
||||
case WMF_COS: val = (a0*b0+a1*b1+a2*b2)/(length[i]*length[j]); break;
|
||||
case WMF_JAC: val = (min(a0,b0)+min(a1,b1)+min(a2,b2))/(max(a0,b0)+max(a1,b1)+max(a2,b2)); break;
|
||||
case WMF_OFF: val = 1.0f; break;
|
||||
default: val = exp(-(diff0*diff0+diff1*diff1+diff2*diff2)*divider);
|
||||
}
|
||||
|
||||
wMap[i][j] = wMap[j][i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
delete []length;
|
||||
}
|
||||
//end of the function
|
||||
F = FNew;
|
||||
}
|
||||
|
||||
Mat filterCore(Mat &I, Mat &F, float **wMap, int r=20, int nF=256, int nI=256, Mat mask=Mat())
|
||||
{
|
||||
// Check validation
|
||||
assert(I.depth() == CV_32S && I.channels()==1);//input image: 32SC1
|
||||
assert(F.depth() == CV_32S && F.channels()==1);//feature image: 32SC1
|
||||
|
||||
// Configuration and declaration
|
||||
int rows = I.rows, cols = I.cols;
|
||||
Mat outImg = I.clone();
|
||||
|
||||
// Handle Mask
|
||||
if(mask.empty())
|
||||
{
|
||||
mask = Mat(I.size(),CV_8U);
|
||||
mask = Scalar(1);
|
||||
}
|
||||
|
||||
// Allocate memory for joint-histogram and BCB
|
||||
int **H = int2D(nI,nF);
|
||||
int *BCB = new int[nF];
|
||||
|
||||
// Allocate links for necklace table
|
||||
int **Hf = int2D(nI,nF);//forward link
|
||||
int **Hb = int2D(nI,nF);//backward link
|
||||
int *BCBf = new int[nF];//forward link
|
||||
int *BCBb = new int[nF];//backward link
|
||||
|
||||
// Column Scanning
|
||||
for(int x=0;x<cols;x++)
|
||||
{
|
||||
// Reset histogram and BCB for each column
|
||||
memset(BCB, 0, sizeof(int)*nF);
|
||||
memset(H[0], 0, sizeof(int)*nF*nI);
|
||||
for(int i=0;i<nI;i++)Hf[i][0]=Hb[i][0]=0;
|
||||
BCBf[0]=BCBb[0]=0;
|
||||
|
||||
// Reset cut-point
|
||||
int medianVal = -1;
|
||||
|
||||
// Precompute "x" range and checks boundary
|
||||
int downX = max(0,x-r);
|
||||
int upX = min(cols-1,x+r);
|
||||
|
||||
// Initialize joint-histogram and BCB for the first window
|
||||
int upY = min(rows-1,r);
|
||||
for(int i=0;i<=upY;i++)
|
||||
{
|
||||
int *IPtr = I.ptr<int>(i);
|
||||
int *FPtr = F.ptr<int>(i);
|
||||
uchar *maskPtr = mask.ptr<uchar>(i);
|
||||
|
||||
for(int j=downX;j<=upX;j++)
|
||||
{
|
||||
if(!maskPtr[j])continue;
|
||||
|
||||
int fval = IPtr[j];
|
||||
int *curHist = H[fval];
|
||||
int gval = FPtr[j];
|
||||
|
||||
// Maintain necklace table of joint-histogram
|
||||
if(!curHist[gval] && gval)
|
||||
{
|
||||
int *curHf = Hf[fval];
|
||||
int *curHb = Hb[fval];
|
||||
|
||||
int p1=0,p2=curHf[0];
|
||||
curHf[p1]=gval;
|
||||
curHf[gval]=p2;
|
||||
curHb[p2]=gval;
|
||||
curHb[gval]=p1;
|
||||
}
|
||||
|
||||
curHist[gval]++;
|
||||
// Maintain necklace table of BCB
|
||||
updateBCB(BCB[gval],BCBf,BCBb,gval,-1);
|
||||
}
|
||||
}
|
||||
|
||||
for(int y=0;y<rows;y++)
|
||||
{
|
||||
// Find weighted median with help of BCB and joint-histogram
|
||||
float balanceWeight = 0;
|
||||
int curIndex = F.ptr<int>(y,x)[0];
|
||||
float *fPtr = wMap[curIndex];
|
||||
int &curMedianVal = medianVal;
|
||||
|
||||
// Compute current balance
|
||||
{
|
||||
int i=0;
|
||||
do
|
||||
{
|
||||
balanceWeight += BCB[i]*fPtr[i];
|
||||
i=BCBf[i];
|
||||
}while(i);
|
||||
}
|
||||
|
||||
// Move cut-point to the left
|
||||
if(balanceWeight >= 0)
|
||||
{
|
||||
for(;balanceWeight >= 0 && curMedianVal > 0; curMedianVal--)
|
||||
{
|
||||
float curWeight = 0;
|
||||
int *nextHist = H[curMedianVal];
|
||||
int *nextHf = Hf[curMedianVal];
|
||||
|
||||
// Compute weight change by shift cut-point
|
||||
int i=0;
|
||||
do
|
||||
{
|
||||
curWeight += (nextHist[i]<<1)*fPtr[i];
|
||||
|
||||
// Update BCB and maintain the necklace table of BCB
|
||||
updateBCB(BCB[i],BCBf,BCBb,i,-(nextHist[i]<<1));
|
||||
|
||||
i=nextHf[i];
|
||||
}while(i);
|
||||
|
||||
balanceWeight -= curWeight;
|
||||
}
|
||||
}
|
||||
// Move cut-point to the right
|
||||
else if(balanceWeight < 0)
|
||||
{
|
||||
for(;balanceWeight < 0 && curMedianVal != nI-1; curMedianVal++)
|
||||
{
|
||||
float curWeight = 0;
|
||||
int *nextHist = H[curMedianVal+1];
|
||||
int *nextHf = Hf[curMedianVal+1];
|
||||
|
||||
// Compute weight change by shift cut-point
|
||||
int i=0;
|
||||
do
|
||||
{
|
||||
curWeight += (nextHist[i]<<1)*fPtr[i];
|
||||
|
||||
// Update BCB and maintain the necklace table of BCB
|
||||
updateBCB(BCB[i],BCBf,BCBb,i,nextHist[i]<<1);
|
||||
|
||||
i=nextHf[i];
|
||||
}while(i);
|
||||
balanceWeight += curWeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Weighted median is found and written to the output image
|
||||
if(curMedianVal != -1)
|
||||
{
|
||||
if(balanceWeight < 0)
|
||||
outImg.ptr<int>(y,x)[0] = curMedianVal+1;
|
||||
else
|
||||
outImg.ptr<int>(y,x)[0] = curMedianVal;
|
||||
}
|
||||
|
||||
// Update joint-histogram and BCB when local window is shifted.
|
||||
int fval,gval,*curHist;
|
||||
|
||||
// Add entering pixels into joint-histogram and BCB
|
||||
int rownum = y + r + 1;
|
||||
if(rownum < rows)
|
||||
{
|
||||
int *inputImgPtr = I.ptr<int>(rownum);
|
||||
int *guideImgPtr = F.ptr<int>(rownum);
|
||||
uchar *maskPtr = mask.ptr<uchar>(rownum);
|
||||
|
||||
for(int j=downX;j<=upX;j++)
|
||||
{
|
||||
if(!maskPtr[j])continue;
|
||||
|
||||
fval = inputImgPtr[j];
|
||||
curHist = H[fval];
|
||||
gval = guideImgPtr[j];
|
||||
|
||||
// Maintain necklace table of joint-histogram
|
||||
if(!curHist[gval] && gval)
|
||||
{
|
||||
int *curHf = Hf[fval];
|
||||
int *curHb = Hb[fval];
|
||||
|
||||
int p1=0,p2=curHf[0];
|
||||
curHf[gval]=p2;
|
||||
curHb[gval]=p1;
|
||||
curHf[p1]=curHb[p2]=gval;
|
||||
}
|
||||
|
||||
curHist[gval]++;
|
||||
|
||||
// Maintain necklace table of BCB
|
||||
updateBCB(BCB[gval],BCBf,BCBb,gval,((fval <= medianVal)<<1)-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delete leaving pixels into joint-histogram and BCB
|
||||
rownum = y - r;
|
||||
if(rownum >= 0)
|
||||
{
|
||||
int *inputImgPtr = I.ptr<int>(rownum);
|
||||
int *guideImgPtr = F.ptr<int>(rownum);
|
||||
uchar *maskPtr = mask.ptr<uchar>(rownum);
|
||||
|
||||
for(int j=downX;j<=upX;j++)
|
||||
{
|
||||
if(!maskPtr[j])continue;
|
||||
|
||||
fval = inputImgPtr[j];
|
||||
curHist = H[fval];
|
||||
gval = guideImgPtr[j];
|
||||
|
||||
curHist[gval]--;
|
||||
|
||||
// Maintain necklace table of joint-histogram
|
||||
if(!curHist[gval] && gval)
|
||||
{
|
||||
int *curHf = Hf[fval];
|
||||
int *curHb = Hb[fval];
|
||||
|
||||
int p1=curHb[gval],p2=curHf[gval];
|
||||
curHf[p1]=p2;
|
||||
curHb[p2]=p1;
|
||||
}
|
||||
|
||||
// Maintain necklace table of BCB
|
||||
updateBCB(BCB[gval],BCBf,BCBb,gval,-((fval <= medianVal)<<1)+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deallocate the memory
|
||||
{
|
||||
delete []BCB;
|
||||
delete []BCBf;
|
||||
delete []BCBb;
|
||||
int2D_release(H);
|
||||
int2D_release(Hf);
|
||||
int2D_release(Hb);
|
||||
}
|
||||
|
||||
// end of the function
|
||||
return outImg;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ximgproc
|
||||
{
|
||||
void weightedMedianFilter(InputArray joint, InputArray src, OutputArray dst, int r, double sigma, int weightType, InputArray mask)
|
||||
{
|
||||
CV_Assert(!src.empty());
|
||||
CV_Assert(r > 0 && sigma > 0);
|
||||
|
||||
int nI = 256;
|
||||
int nF = 256;
|
||||
|
||||
Mat I = src.getMat();
|
||||
Mat F = joint.getMat();
|
||||
if(joint.empty())
|
||||
{
|
||||
medianBlur(src, dst, r);
|
||||
return;
|
||||
}
|
||||
|
||||
CV_Assert(I.depth() == CV_32F || I.depth() == CV_8U);
|
||||
CV_Assert(F.depth() == CV_8U && (F.channels() == 1 || F.channels() == 3));
|
||||
|
||||
dst.create(src.size(), src.type());
|
||||
Mat D = dst.getMat();
|
||||
|
||||
if(D.data == F.data)
|
||||
F = F.clone();
|
||||
if(D.data == I.data)
|
||||
I = I.clone();
|
||||
|
||||
//Preprocess I
|
||||
//OUTPUT OF THIS STEP: Is, iMap
|
||||
//If I is floating point image, "adaptive quantization" is done in from32FTo32S.
|
||||
//The mapping of floating value to integer value is stored in iMap (for each channel).
|
||||
//"Is" stores each channel of "I". The channels are converted to CV_32S type after this step.
|
||||
vector<float *> iMap(I.channels());
|
||||
vector<Mat> Is;
|
||||
split(I,Is);
|
||||
for(int i=0;i<(int)Is.size();i++)
|
||||
{
|
||||
if(I.depth() == CV_32F)
|
||||
{
|
||||
iMap[i] = new float[nI];
|
||||
from32FTo32S(Is[i],Is[i],nI,iMap[i]);
|
||||
}
|
||||
else if(I.depth() == CV_8U)
|
||||
{
|
||||
Is[i].convertTo(Is[i],CV_32S);
|
||||
}
|
||||
}
|
||||
|
||||
//Preprocess F
|
||||
//OUTPUT OF THIS STEP: F(new), wMap
|
||||
//If "F" is 3-channel image, "clustering feature image" is done in featureIndexing.
|
||||
//If "F" is 1-channel image, featureIndexing only does a type-casting on "F".
|
||||
//The output "F" is CV_32S type, containing indexes of feature values.
|
||||
//"wMap" is a 2D array that defines the distance between each pair of feature indexes.
|
||||
// wMap[i][j] is the weight between feature index "i" and "j".
|
||||
float **wMap = NULL;
|
||||
featureIndexing(F, wMap, nF, float(sigma), weightType);
|
||||
|
||||
//Filtering - Joint-Histogram Framework
|
||||
for(int i=0; i<(int)Is.size(); i++)
|
||||
{
|
||||
Is[i] = filterCore(Is[i], F, wMap, r, nF, nI, mask.getMat());
|
||||
}
|
||||
float2D_release(wMap);
|
||||
|
||||
//Postprocess F
|
||||
//Convert input image back to the original type.
|
||||
for(int i = 0; i < (int)Is.size(); i++)
|
||||
{
|
||||
if(I.depth()==CV_32F)
|
||||
{
|
||||
from32STo32F(Is[i],Is[i],iMap[i]);
|
||||
delete []iMap[i];
|
||||
}
|
||||
else if(I.depth()==CV_8U)
|
||||
{
|
||||
Is[i].convertTo(Is[i],CV_8U);
|
||||
}
|
||||
}
|
||||
|
||||
//merge the channels
|
||||
merge(Is, D);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user