vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
@@ -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);
}
}
}
+91
View File
@@ -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__ */
+293
View File
@@ -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);
}
}
}
+222
View File
@@ -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;
}
}
}
}
+459
View File
@@ -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
+570
View File
@@ -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);
}
}
}
+60
View File
@@ -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);
}
}
}
+213
View File
@@ -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);
}
}
}
+294
View File
@@ -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
+624
View File
@@ -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
+985
View File
@@ -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
+682
View File
@@ -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
+645
View File
@@ -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
+691
View File
@@ -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);
}
}
}
+394
View File
@@ -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--;
}
}
}
}
+850
View File
@@ -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> > &alpha;
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> > &alpha;
vector<Mat> &srcCnMean;
vector<Mat> &beta;
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> > &alpha;
vector<Mat> &beta;
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");
}
}
}
}
+385
View File
@@ -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
+39
View File
@@ -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;
}
}
+424
View File
@@ -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);
}
}
}
+56
View File
@@ -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
+52
View File
@@ -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
+200
View File
@@ -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);
}
}
}
+81
View File
@@ -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
+770
View File
@@ -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);
}
}
}
+210
View File
@@ -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);
}
}
}