Merge branch 'master' of https://github.com/Itseez/opencv_contrib
@ -1,332 +0,0 @@ |
|||||||
/*///////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// 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) 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:
|
|
||||||
//
|
|
||||||
// * 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 "opencv2/video/tracking.hpp" |
|
||||||
#include "opencv2/imgproc.hpp" |
|
||||||
#include "time.h" |
|
||||||
#include <algorithm> |
|
||||||
#include <limits.h> |
|
||||||
#include <math.h> |
|
||||||
#include <opencv2/highgui.hpp> |
|
||||||
#include "TLD.hpp" |
|
||||||
|
|
||||||
namespace cv {namespace tld |
|
||||||
{ |
|
||||||
|
|
||||||
//debug functions and variables
|
|
||||||
Rect2d etalon(14.0,110.0,20.0,20.0); |
|
||||||
void drawWithRects(const Mat& img,std::vector<Rect2d>& blackOnes,Rect2d whiteOne){ |
|
||||||
Mat image; |
|
||||||
img.copyTo(image); |
|
||||||
if(whiteOne.width>=0){ |
|
||||||
rectangle( image,whiteOne, 255, 1, 1 ); |
|
||||||
} |
|
||||||
for(int i=0;i<(int)blackOnes.size();i++){ |
|
||||||
rectangle( image,blackOnes[i], 0, 1, 1 ); |
|
||||||
} |
|
||||||
imshow("img",image); |
|
||||||
} |
|
||||||
void drawWithRects(const Mat& img,std::vector<Rect2d>& blackOnes,std::vector<Rect2d>& whiteOnes){ |
|
||||||
Mat image; |
|
||||||
img.copyTo(image); |
|
||||||
for(int i=0;i<(int)whiteOnes.size();i++){ |
|
||||||
rectangle( image,whiteOnes[i], 255, 1, 1 ); |
|
||||||
} |
|
||||||
for(int i=0;i<(int)blackOnes.size();i++){ |
|
||||||
rectangle( image,blackOnes[i], 0, 1, 1 ); |
|
||||||
} |
|
||||||
imshow("img",image); |
|
||||||
} |
|
||||||
void myassert(const Mat& img){ |
|
||||||
int count=0; |
|
||||||
for(int i=0;i<img.rows;i++){ |
|
||||||
for(int j=0;j<img.cols;j++){ |
|
||||||
if(img.at<uchar>(i,j)==0){ |
|
||||||
count++; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
dprintf(("black: %d out of %d (%f)\n",count,img.rows*img.cols,1.0*count/img.rows/img.cols)); |
|
||||||
} |
|
||||||
|
|
||||||
void printPatch(const Mat_<uchar>& standardPatch){ |
|
||||||
for(int i=0;i<standardPatch.rows;i++){ |
|
||||||
for(int j=0;j<standardPatch.cols;j++){ |
|
||||||
dprintf(("%5.2f, ",(double)standardPatch(i,j))); |
|
||||||
} |
|
||||||
dprintf(("\n")); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
std::string type2str(const Mat& mat){ |
|
||||||
int type=mat.type(); |
|
||||||
std::string r; |
|
||||||
|
|
||||||
uchar depth = type & CV_MAT_DEPTH_MASK; |
|
||||||
uchar chans =(uchar)( 1 + (type >> CV_CN_SHIFT)); |
|
||||||
|
|
||||||
switch ( depth ) { |
|
||||||
case CV_8U: r = "8U"; break; |
|
||||||
case CV_8S: r = "8S"; break; |
|
||||||
case CV_16U: r = "16U"; break; |
|
||||||
case CV_16S: r = "16S"; break; |
|
||||||
case CV_32S: r = "32S"; break; |
|
||||||
case CV_32F: r = "32F"; break; |
|
||||||
case CV_64F: r = "64F"; break; |
|
||||||
default: r = "User"; break; |
|
||||||
} |
|
||||||
|
|
||||||
r += "C"; |
|
||||||
r += (chans+'0'); |
|
||||||
|
|
||||||
return r; |
|
||||||
} |
|
||||||
|
|
||||||
//generic functions
|
|
||||||
double scaleAndBlur(const Mat& originalImg,int scale,Mat& scaledImg,Mat& blurredImg,Size GaussBlurKernelSize){ |
|
||||||
double dScale=1.0; |
|
||||||
for(int i=0;i<scale;i++,dScale*=1.2); |
|
||||||
Size2d size=originalImg.size(); |
|
||||||
size.height/=dScale;size.width/=dScale; |
|
||||||
resize(originalImg,scaledImg,size); |
|
||||||
GaussianBlur(scaledImg,blurredImg,GaussBlurKernelSize,0.0); |
|
||||||
return dScale; |
|
||||||
} |
|
||||||
void getClosestN(std::vector<Rect2d>& scanGrid,Rect2d bBox,int n,std::vector<Rect2d>& res){ |
|
||||||
if(n>=(int)scanGrid.size()){ |
|
||||||
res.assign(scanGrid.begin(),scanGrid.end()); |
|
||||||
return; |
|
||||||
} |
|
||||||
std::vector<double> overlaps(n,0.0); |
|
||||||
res.assign(scanGrid.begin(),scanGrid.begin()+n); |
|
||||||
for(int i=0;i<n;i++){ |
|
||||||
overlaps[i]=overlap(res[i],bBox); |
|
||||||
} |
|
||||||
double otmp; |
|
||||||
Rect2d rtmp; |
|
||||||
for (int i = 1; i < n; i++){ |
|
||||||
int j = i; |
|
||||||
while (j > 0 && overlaps[j - 1] > overlaps[j]) { |
|
||||||
otmp = overlaps[j];overlaps[j] = overlaps[j - 1];overlaps[j - 1] = otmp; |
|
||||||
rtmp = res[j];res[j] = res[j - 1];res[j - 1] = rtmp; |
|
||||||
j--; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
double o=0.0; |
|
||||||
for(int i=n;i<(int)scanGrid.size();i++){ |
|
||||||
if((o=overlap(scanGrid[i],bBox))<=overlaps[0]){ |
|
||||||
continue; |
|
||||||
} |
|
||||||
int j=0; |
|
||||||
for(j=0;j<n && overlaps[j]<o;j++); |
|
||||||
j--; |
|
||||||
for(int k=0;k<j;overlaps[k]=overlaps[k+1],res[k]=res[k+1],k++); |
|
||||||
overlaps[j]=o;res[j]=scanGrid[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
double variance(const Mat& img){ |
|
||||||
double p=0,p2=0; |
|
||||||
for(int i=0;i<img.rows;i++){ |
|
||||||
for(int j=0;j<img.cols;j++){ |
|
||||||
p+=img.at<uchar>(i,j); |
|
||||||
p2+=img.at<uchar>(i,j)*img.at<uchar>(i,j); |
|
||||||
} |
|
||||||
} |
|
||||||
p/=(img.cols*img.rows); |
|
||||||
p2/=(img.cols*img.rows); |
|
||||||
return p2-p*p; |
|
||||||
} |
|
||||||
double variance(Mat_<double>& intImgP,Mat_<double>& intImgP2,Rect box){ |
|
||||||
int x=(box.x),y=(box.y),width=(box.width),height=(box.height); |
|
||||||
CV_Assert(0<=x && (x+width)<intImgP.cols && (x+width)<intImgP2.cols); |
|
||||||
CV_Assert(0<=y && (y+height)<intImgP.rows && (y+height)<intImgP2.rows); |
|
||||||
double p=0,p2=0; |
|
||||||
double A,B,C,D; |
|
||||||
|
|
||||||
A=intImgP(y,x); |
|
||||||
B=intImgP(y,x+width); |
|
||||||
C=intImgP(y+height,x); |
|
||||||
D=intImgP(y+height,x+width); |
|
||||||
p=(0.0+A+D-B-C)/(width*height); |
|
||||||
|
|
||||||
A=intImgP2(y,x); |
|
||||||
B=intImgP2(y,x+width); |
|
||||||
C=intImgP2(y+height,x); |
|
||||||
D=intImgP2(y+height,x+width); |
|
||||||
p2=(0.0+(D-B)-(C-A))/(width*height); |
|
||||||
|
|
||||||
return p2-p*p; |
|
||||||
} |
|
||||||
|
|
||||||
double NCC(Mat_<uchar> patch1,Mat_<uchar> patch2){ |
|
||||||
CV_Assert(patch1.rows==patch2.rows); |
|
||||||
CV_Assert(patch1.cols==patch2.cols); |
|
||||||
|
|
||||||
int N=patch1.rows*patch1.cols; |
|
||||||
double s1=sum(patch1)(0),s2=sum(patch2)(0); |
|
||||||
double n1=norm(patch1),n2=norm(patch2); |
|
||||||
double prod=patch1.dot(patch2); |
|
||||||
double sq1=sqrt(MAX(0.0,n1*n1-s1*s1/N)),sq2=sqrt(MAX(0.0,n2*n2-s2*s2/N)); |
|
||||||
double ares=(sq2==0)?sq1/abs(sq1):(prod-s1*s2/N)/sq1/sq2; |
|
||||||
return ares; |
|
||||||
} |
|
||||||
unsigned int getMedian(const std::vector<unsigned int>& values, int size){ |
|
||||||
if(size==-1){ |
|
||||||
size=(int)values.size(); |
|
||||||
} |
|
||||||
std::vector<int> copy(values.begin(),values.begin()+size); |
|
||||||
std::sort(copy.begin(),copy.end()); |
|
||||||
if(size%2==0){ |
|
||||||
return (copy[size/2-1]+copy[size/2])/2; |
|
||||||
}else{ |
|
||||||
return copy[(size-1)/2]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
double overlap(const Rect2d& r1,const Rect2d& r2){ |
|
||||||
double a1=r1.area(), a2=r2.area(), a0=(r1&r2).area(); |
|
||||||
return a0/(a1+a2-a0); |
|
||||||
} |
|
||||||
|
|
||||||
void resample(const Mat& img,const RotatedRect& r2,Mat_<uchar>& samples){ |
|
||||||
Mat_<float> M(2,3),R(2,2),Si(2,2),s(2,1),o(2,1); |
|
||||||
R(0,0)=(float)cos(r2.angle*CV_PI/180);R(0,1)=(float)(-sin(r2.angle*CV_PI/180)); |
|
||||||
R(1,0)=(float)sin(r2.angle*CV_PI/180);R(1,1)=(float)cos(r2.angle*CV_PI/180); |
|
||||||
Si(0,0)=(float)(samples.cols/r2.size.width); Si(0,1)=0.0f; |
|
||||||
Si(1,0)=0.0f; Si(1,1)=(float)(samples.rows/r2.size.height); |
|
||||||
s(0,0)=(float)samples.cols; s(1,0)=(float)samples.rows; |
|
||||||
o(0,0)=r2.center.x;o(1,0)=r2.center.y; |
|
||||||
Mat_<float> A(2,2),b(2,1); |
|
||||||
A=Si*R; |
|
||||||
b=s/2.0-Si*R*o; |
|
||||||
A.copyTo(M.colRange(Range(0,2))); |
|
||||||
b.copyTo(M.colRange(Range(2,3))); |
|
||||||
warpAffine(img,samples,M,samples.size()); |
|
||||||
} |
|
||||||
void resample(const Mat& img,const Rect2d& r2,Mat_<uchar>& samples){ |
|
||||||
Mat_<float> M(2,3); |
|
||||||
M(0,0)=(float)(samples.cols/r2.width); M(0,1)=0.0f; M(0,2)=(float)(-r2.x*samples.cols/r2.width); |
|
||||||
M(1,0)=0.0f; M(1,1)=(float)(samples.rows/r2.height); M(1,2)=(float)(-r2.y*samples.rows/r2.height); |
|
||||||
warpAffine(img,samples,M,samples.size()); |
|
||||||
} |
|
||||||
|
|
||||||
//other stuff
|
|
||||||
void TLDEnsembleClassifier::stepPrefSuff(std::vector<uchar>& arr,int len){ |
|
||||||
int gridSize=getGridSize(); |
|
||||||
#if 0 |
|
||||||
int step=len/(gridSize-1), pref=(len-step*(gridSize-1))/2; |
|
||||||
for(int i=0;i<(int)(sizeof(x1)/sizeof(x1[0]));i++){ |
|
||||||
arr[i]=pref+arr[i]*step; |
|
||||||
} |
|
||||||
#else |
|
||||||
int total=len-gridSize; |
|
||||||
int quo=total/(gridSize-1),rem=total%(gridSize-1); |
|
||||||
int smallStep=quo,bigStep=quo+1; |
|
||||||
int bigOnes=rem,smallOnes=gridSize-bigOnes-1; |
|
||||||
int bigOnes_front=bigOnes/2,bigOnes_back=bigOnes-bigOnes_front; |
|
||||||
for(int i=0;i<(int)arr.size();i++){ |
|
||||||
if(arr[i]<bigOnes_back){ |
|
||||||
arr[i]=(uchar)(arr[i]*bigStep+arr[i]); |
|
||||||
continue; |
|
||||||
} |
|
||||||
if(arr[i]<(bigOnes_front+smallOnes)){ |
|
||||||
arr[i]=(uchar)(bigOnes_front*bigStep+(arr[i]-bigOnes_front)*smallStep+arr[i]); |
|
||||||
continue; |
|
||||||
} |
|
||||||
if(arr[i]<(bigOnes_front+smallOnes+bigOnes_back)){ |
|
||||||
arr[i]=(uchar)(bigOnes_front*bigStep+smallOnes*smallStep+(arr[i]-(bigOnes_front+smallOnes))*bigStep+arr[i]); |
|
||||||
continue; |
|
||||||
} |
|
||||||
arr[i]=(uchar)(len-1); |
|
||||||
} |
|
||||||
#endif |
|
||||||
} |
|
||||||
TLDEnsembleClassifier::TLDEnsembleClassifier(int ordinal,Size size,int measurePerClassifier){ |
|
||||||
x1=std::vector<uchar>(measurePerClassifier,0); |
|
||||||
x2=std::vector<uchar>(measurePerClassifier,0); |
|
||||||
y1=std::vector<uchar>(measurePerClassifier,0); |
|
||||||
y2=std::vector<uchar>(measurePerClassifier,0); |
|
||||||
|
|
||||||
preinit(ordinal); |
|
||||||
|
|
||||||
stepPrefSuff(x1,size.width); |
|
||||||
stepPrefSuff(x2,size.width); |
|
||||||
stepPrefSuff(y1,size.height); |
|
||||||
stepPrefSuff(y2,size.height); |
|
||||||
|
|
||||||
int posSize=1; |
|
||||||
for(int i=0;i<measurePerClassifier;i++)posSize*=2; |
|
||||||
pos=std::vector<unsigned int>(posSize,0); |
|
||||||
neg=std::vector<unsigned int>(posSize,0); |
|
||||||
} |
|
||||||
void TLDEnsembleClassifier::integrate(Mat_<uchar> patch,bool isPositive){ |
|
||||||
unsigned short int position=code(patch.data,(int)patch.step[0]); |
|
||||||
if(isPositive){ |
|
||||||
pos[position]++; |
|
||||||
}else{ |
|
||||||
neg[position]++; |
|
||||||
} |
|
||||||
} |
|
||||||
double TLDEnsembleClassifier::posteriorProbability(const uchar* data,int rowstep)const{ |
|
||||||
unsigned short int position=code(data,rowstep); |
|
||||||
double posNum=(double)pos[position], negNum=(double)neg[position]; |
|
||||||
if(posNum==0.0 && negNum==0.0){ |
|
||||||
return 0.0; |
|
||||||
}else{ |
|
||||||
return posNum/(posNum+negNum); |
|
||||||
} |
|
||||||
} |
|
||||||
unsigned short int TLDEnsembleClassifier::code(const uchar* data,int rowstep)const{ |
|
||||||
unsigned short int position=0; |
|
||||||
for(int i=0;i<(int)x1.size();i++){ |
|
||||||
position=position<<1; |
|
||||||
if(*(data+rowstep*y1[i]+x1[i])<*(data+rowstep*y2[i]+x2[i])){ |
|
||||||
position++; |
|
||||||
}else{ |
|
||||||
} |
|
||||||
} |
|
||||||
return position; |
|
||||||
} |
|
||||||
|
|
||||||
}} |
|
@ -1,110 +0,0 @@ |
|||||||
/*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) 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:
|
|
||||||
//
|
|
||||||
// * 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 "opencv2/video/tracking.hpp" |
|
||||||
#include "opencv2/imgproc.hpp" |
|
||||||
#include <algorithm> |
|
||||||
#include <limits.h> |
|
||||||
|
|
||||||
namespace cv {namespace tld |
|
||||||
{ |
|
||||||
|
|
||||||
//debug functions and variables
|
|
||||||
#define ALEX_DEBUG |
|
||||||
#ifdef ALEX_DEBUG |
|
||||||
#define dfprintf(x) fprintf x |
|
||||||
#define dprintf(x) printf x |
|
||||||
#else |
|
||||||
#define dfprintf(x) |
|
||||||
#define dprintf(x) |
|
||||||
#endif |
|
||||||
#define MEASURE_TIME(a) {\ |
|
||||||
clock_t start;float milisec=0.0;\
|
|
||||||
start=clock();{a} milisec=1000.0*(clock()-start)/CLOCKS_PER_SEC;\
|
|
||||||
dprintf(("%-90s took %f milis\n",#a,milisec)); } |
|
||||||
#define HERE dprintf(("%d\n",__LINE__));fflush(stderr); |
|
||||||
#define START_TICK(name) { clock_t start;double milisec=0.0; start=clock(); |
|
||||||
#define END_TICK(name) milisec=1000.0*(clock()-start)/CLOCKS_PER_SEC;\ |
|
||||||
dprintf(("%s took %f milis\n",name,milisec)); } |
|
||||||
extern Rect2d etalon; |
|
||||||
void myassert(const Mat& img); |
|
||||||
void printPatch(const Mat_<uchar>& standardPatch); |
|
||||||
std::string type2str(const Mat& mat); |
|
||||||
void drawWithRects(const Mat& img,std::vector<Rect2d>& blackOnes,Rect2d whiteOne=Rect2d(-1.0,-1.0,-1.0,-1.0)); |
|
||||||
void drawWithRects(const Mat& img,std::vector<Rect2d>& blackOnes,std::vector<Rect2d>& whiteOnes); |
|
||||||
|
|
||||||
//aux functions and variables
|
|
||||||
//#define CLIP(x,a,b) MIN(MAX((x),(a)),(b))
|
|
||||||
template<typename T> inline T CLIP(T x,T a,T b){return MIN(MAX(x,a),b);} |
|
||||||
double overlap(const Rect2d& r1,const Rect2d& r2); |
|
||||||
void resample(const Mat& img,const RotatedRect& r2,Mat_<uchar>& samples); |
|
||||||
void resample(const Mat& img,const Rect2d& r2,Mat_<uchar>& samples); |
|
||||||
double variance(const Mat& img); |
|
||||||
double variance(Mat_<double>& intImgP,Mat_<double>& intImgP2,Rect box); |
|
||||||
double NCC(Mat_<uchar> patch1,Mat_<uchar> patch2); |
|
||||||
void getClosestN(std::vector<Rect2d>& scanGrid,Rect2d bBox,int n,std::vector<Rect2d>& res); |
|
||||||
double scaleAndBlur(const Mat& originalImg,int scale,Mat& scaledImg,Mat& blurredImg,Size GaussBlurKernelSize); |
|
||||||
unsigned int getMedian(const std::vector<unsigned int>& values, int size=-1); |
|
||||||
|
|
||||||
class TLDEnsembleClassifier{ |
|
||||||
public: |
|
||||||
TLDEnsembleClassifier(int ordinal,Size size,int measurePerClassifier); |
|
||||||
void integrate(Mat_<uchar> patch,bool isPositive); |
|
||||||
double posteriorProbability(const uchar* data,int rowstep)const; |
|
||||||
static int getMaxOrdinal(); |
|
||||||
private: |
|
||||||
static int getGridSize(); |
|
||||||
inline void stepPrefSuff(std::vector<uchar>& arr,int len); |
|
||||||
void preinit(int ordinal); |
|
||||||
unsigned short int code(const uchar* data,int rowstep)const; |
|
||||||
std::vector<unsigned int> pos,neg; |
|
||||||
std::vector<uchar> x1,y1,x2,y2; |
|
||||||
}; |
|
||||||
|
|
||||||
class TrackerProxy{ |
|
||||||
public: |
|
||||||
virtual bool init( const Mat& image, const Rect2d& boundingBox)=0; |
|
||||||
virtual bool update(const Mat& image, Rect2d& boundingBox)=0; |
|
||||||
virtual ~TrackerProxy(){} |
|
||||||
}; |
|
||||||
|
|
||||||
}} |
|
@ -0,0 +1,949 @@ |
|||||||
|
/*///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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) 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:
|
||||||
|
//
|
||||||
|
// * 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 "opencv2/video/tracking.hpp" |
||||||
|
#include "opencv2/imgproc.hpp" |
||||||
|
#include "time.h" |
||||||
|
#include<algorithm> |
||||||
|
#include<limits.h> |
||||||
|
#include "tld_tracker.hpp" |
||||||
|
#include "opencv2/highgui.hpp" |
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME(optimize): |
||||||
|
* no median |
||||||
|
* direct formula in resamples |
||||||
|
* FIXME(issues) |
||||||
|
* THETA_NN 0.5<->0.6 dramatic change vs video 6 !! |
||||||
|
* TODO(features) |
||||||
|
* benchmark: two streams of photos -->better video |
||||||
|
* (try inter_area for resize) |
||||||
|
* TODO: |
||||||
|
* fix pushbot->pick commits->compare_branches->all in 1->resubmit |
||||||
|
* || video(0.5<->0.6) -->debug if box size is less than 20 |
||||||
|
* perfect PN |
||||||
|
* |
||||||
|
* vadim: |
||||||
|
* ?3. comment each function/method |
||||||
|
* 5. empty lines to separate logical... |
||||||
|
* 6. comment logical sections |
||||||
|
* 11. group decls logically, order of statements |
||||||
|
* |
||||||
|
* ?10. all in one class
|
||||||
|
* todo:
|
||||||
|
* initializer lists;
|
||||||
|
*/ |
||||||
|
|
||||||
|
/* design decisions:
|
||||||
|
*/ |
||||||
|
|
||||||
|
namespace cv |
||||||
|
{ |
||||||
|
|
||||||
|
namespace tld |
||||||
|
{ |
||||||
|
|
||||||
|
const int STANDARD_PATCH_SIZE = 15; |
||||||
|
const int NEG_EXAMPLES_IN_INIT_MODEL = 300; |
||||||
|
const int MAX_EXAMPLES_IN_MODEL = 500; |
||||||
|
const int MEASURES_PER_CLASSIFIER = 13; |
||||||
|
const int GRIDSIZE = 15; |
||||||
|
const int DOWNSCALE_MODE = cv::INTER_LINEAR; |
||||||
|
const double THETA_NN = 0.50; |
||||||
|
const double CORE_THRESHOLD = 0.5; |
||||||
|
const double SCALE_STEP = 1.2; |
||||||
|
const double ENSEMBLE_THRESHOLD = 0.5; |
||||||
|
const double VARIANCE_THRESHOLD = 0.5; |
||||||
|
const double NEXPERT_THRESHOLD = 0.2; |
||||||
|
#define BLUR_AS_VADIM |
||||||
|
#undef CLOSED_LOOP |
||||||
|
static const cv::Size GaussBlurKernelSize(3, 3); |
||||||
|
|
||||||
|
class TLDDetector; |
||||||
|
class MyMouseCallbackDEBUG |
||||||
|
{ |
||||||
|
public: |
||||||
|
MyMouseCallbackDEBUG(Mat& img, Mat& imgBlurred, TLDDetector* detector):img_(img), imgBlurred_(imgBlurred), detector_(detector){} |
||||||
|
static void onMouse(int event, int x, int y, int, void* obj){ ((MyMouseCallbackDEBUG*)obj)->onMouse(event, x, y); } |
||||||
|
MyMouseCallbackDEBUG& operator = (const MyMouseCallbackDEBUG& /*other*/){ return *this; } |
||||||
|
private: |
||||||
|
void onMouse(int event, int x, int y); |
||||||
|
Mat& img_, imgBlurred_; |
||||||
|
TLDDetector* detector_; |
||||||
|
}; |
||||||
|
|
||||||
|
class Data
|
||||||
|
{ |
||||||
|
public: |
||||||
|
Data(Rect2d initBox); |
||||||
|
Size getMinSize(){ return minSize; } |
||||||
|
double getScale(){ return scale; } |
||||||
|
bool confident; |
||||||
|
bool failedLastTime; |
||||||
|
int frameNum; |
||||||
|
void printme(FILE* port = stdout); |
||||||
|
private: |
||||||
|
double scale; |
||||||
|
Size minSize; |
||||||
|
}; |
||||||
|
|
||||||
|
class TLDDetector
|
||||||
|
{ |
||||||
|
public: |
||||||
|
TLDDetector(const TrackerTLD::Params& params, Ptr<TrackerModel> model_in):model(model_in), params_(params){} |
||||||
|
~TLDDetector(){} |
||||||
|
static void generateScanGrid(int rows, int cols, Size initBox, std::vector<Rect2d>& res, bool withScaling = false); |
||||||
|
struct LabeledPatch |
||||||
|
{ |
||||||
|
Rect2d rect; |
||||||
|
bool isObject, shouldBeIntegrated; |
||||||
|
}; |
||||||
|
bool detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector<LabeledPatch>& patches); |
||||||
|
protected: |
||||||
|
friend class MyMouseCallbackDEBUG; |
||||||
|
Ptr<TrackerModel> model; |
||||||
|
void computeIntegralImages(const Mat& img, Mat_<double>& intImgP, Mat_<double>& intImgP2){ integral(img, intImgP, intImgP2, CV_64F); } |
||||||
|
inline bool patchVariance(Mat_<double>& intImgP, Mat_<double>& intImgP2, double originalVariance, Point pt, Size size); |
||||||
|
TrackerTLD::Params params_; |
||||||
|
}; |
||||||
|
|
||||||
|
template<class T, class Tparams> |
||||||
|
class TrackerProxyImpl : public TrackerProxy |
||||||
|
{ |
||||||
|
public: |
||||||
|
TrackerProxyImpl(Tparams params = Tparams()):params_(params){} |
||||||
|
bool init(const Mat& image, const Rect2d& boundingBox) |
||||||
|
{ |
||||||
|
trackerPtr = T::createTracker(); |
||||||
|
return trackerPtr->init(image, boundingBox); |
||||||
|
} |
||||||
|
bool update(const Mat& image, Rect2d& boundingBox) |
||||||
|
{ |
||||||
|
return trackerPtr->update(image, boundingBox); |
||||||
|
} |
||||||
|
private: |
||||||
|
Ptr<T> trackerPtr; |
||||||
|
Tparams params_; |
||||||
|
Rect2d boundingBox_; |
||||||
|
}; |
||||||
|
|
||||||
|
class TrackerTLDModel : public TrackerModel |
||||||
|
{ |
||||||
|
public: |
||||||
|
TrackerTLDModel(TrackerTLD::Params params, const Mat& image, const Rect2d& boundingBox, Size minSize); |
||||||
|
Rect2d getBoundingBox(){ return boundingBox_; } |
||||||
|
void setBoudingBox(Rect2d boundingBox){ boundingBox_ = boundingBox; } |
||||||
|
double getOriginalVariance(){ return originalVariance_; } |
||||||
|
inline double ensembleClassifierNum(const uchar* data); |
||||||
|
inline void prepareClassifiers(int rowstep); |
||||||
|
double Sr(const Mat_<uchar>& patch); |
||||||
|
double Sc(const Mat_<uchar>& patch); |
||||||
|
void integrateRelabeled(Mat& img, Mat& imgBlurred, const std::vector<TLDDetector::LabeledPatch>& patches); |
||||||
|
void integrateAdditional(const std::vector<Mat_<uchar> >& eForModel, const std::vector<Mat_<uchar> >& eForEnsemble, bool isPositive); |
||||||
|
Size getMinSize(){ return minSize_; } |
||||||
|
void printme(FILE* port = stdout); |
||||||
|
|
||||||
|
protected: |
||||||
|
Size minSize_; |
||||||
|
int timeStampPositiveNext, timeStampNegativeNext; |
||||||
|
TrackerTLD::Params params_; |
||||||
|
void pushIntoModel(const Mat_<uchar>& example, bool positive); |
||||||
|
void modelEstimationImpl( const std::vector<Mat>& /*responses*/ ){} |
||||||
|
void modelUpdateImpl(){} |
||||||
|
Rect2d boundingBox_; |
||||||
|
double originalVariance_; |
||||||
|
std::vector<Mat_<uchar> > positiveExamples, negativeExamples; |
||||||
|
std::vector<int> timeStampsPositive, timeStampsNegative; |
||||||
|
RNG rng; |
||||||
|
std::vector<TLDEnsembleClassifier> classifiers; |
||||||
|
}; |
||||||
|
|
||||||
|
class TrackerTLDImpl : public TrackerTLD |
||||||
|
{ |
||||||
|
public: |
||||||
|
TrackerTLDImpl(const TrackerTLD::Params ¶meters = TrackerTLD::Params()); |
||||||
|
void read(const FileNode& fn); |
||||||
|
void write(FileStorage& fs) const; |
||||||
|
|
||||||
|
protected: |
||||||
|
class Pexpert |
||||||
|
{ |
||||||
|
public: |
||||||
|
Pexpert(const Mat& img_in, const Mat& imgBlurred_in, Rect2d& resultBox_in,
|
||||||
|
const TLDDetector* detector_in, TrackerTLD::Params params_in, Size initSize_in): |
||||||
|
img_(img_in), imgBlurred_(imgBlurred_in), resultBox_(resultBox_in), detector_(detector_in), params_(params_in), initSize_(initSize_in){} |
||||||
|
bool operator()(Rect2d /*box*/){ return false; } |
||||||
|
int additionalExamples(std::vector<Mat_<uchar> >& examplesForModel, std::vector<Mat_<uchar> >& examplesForEnsemble); |
||||||
|
protected: |
||||||
|
Pexpert(){} |
||||||
|
Mat img_, imgBlurred_; |
||||||
|
Rect2d resultBox_; |
||||||
|
const TLDDetector* detector_; |
||||||
|
TrackerTLD::Params params_; |
||||||
|
RNG rng; |
||||||
|
Size initSize_; |
||||||
|
}; |
||||||
|
|
||||||
|
class Nexpert : public Pexpert |
||||||
|
{ |
||||||
|
public: |
||||||
|
Nexpert(const Mat& img_in, Rect2d& resultBox_in, const TLDDetector* detector_in, TrackerTLD::Params params_in) |
||||||
|
{ |
||||||
|
img_ = img_in; resultBox_ = resultBox_in; detector_ = detector_in; params_ = params_in; |
||||||
|
} |
||||||
|
bool operator()(Rect2d box); |
||||||
|
int additionalExamples(std::vector<Mat_<uchar> >& examplesForModel, std::vector<Mat_<uchar> >& examplesForEnsemble) |
||||||
|
{ |
||||||
|
examplesForModel.clear(); examplesForEnsemble.clear(); return 0;
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
bool initImpl(const Mat& image, const Rect2d& boundingBox); |
||||||
|
bool updateImpl(const Mat& image, Rect2d& boundingBox); |
||||||
|
|
||||||
|
TrackerTLD::Params params; |
||||||
|
Ptr<Data> data; |
||||||
|
Ptr<TrackerProxy> trackerProxy; |
||||||
|
Ptr<TLDDetector> detector; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
TrackerTLD::Params::Params(){} |
||||||
|
|
||||||
|
void TrackerTLD::Params::read(const cv::FileNode& /*fn*/){} |
||||||
|
|
||||||
|
void TrackerTLD::Params::write(cv::FileStorage& /*fs*/) const {} |
||||||
|
|
||||||
|
Ptr<TrackerTLD> TrackerTLD::createTracker(const TrackerTLD::Params ¶meters) |
||||||
|
{ |
||||||
|
return Ptr<tld::TrackerTLDImpl>(new tld::TrackerTLDImpl(parameters)); |
||||||
|
} |
||||||
|
|
||||||
|
namespace tld |
||||||
|
{ |
||||||
|
|
||||||
|
TrackerTLDImpl::TrackerTLDImpl(const TrackerTLD::Params ¶meters) : |
||||||
|
params( parameters ) |
||||||
|
{ |
||||||
|
isInit = false; |
||||||
|
trackerProxy = Ptr<TrackerProxyImpl<TrackerMedianFlow, TrackerMedianFlow::Params> > |
||||||
|
(new TrackerProxyImpl<TrackerMedianFlow, TrackerMedianFlow::Params>()); |
||||||
|
} |
||||||
|
|
||||||
|
void TrackerTLDImpl::read(const cv::FileNode& fn) |
||||||
|
{ |
||||||
|
params.read( fn ); |
||||||
|
} |
||||||
|
|
||||||
|
void TrackerTLDImpl::write(cv::FileStorage& fs) const |
||||||
|
{ |
||||||
|
params.write( fs ); |
||||||
|
} |
||||||
|
|
||||||
|
bool TrackerTLDImpl::initImpl(const Mat& image, const Rect2d& boundingBox) |
||||||
|
{ |
||||||
|
Mat image_gray; |
||||||
|
trackerProxy->init(image, boundingBox); |
||||||
|
cvtColor( image, image_gray, COLOR_BGR2GRAY ); |
||||||
|
data = Ptr<Data>(new Data(boundingBox)); |
||||||
|
double scale = data->getScale(); |
||||||
|
Rect2d myBoundingBox = boundingBox; |
||||||
|
if( scale > 1.0 ) |
||||||
|
{ |
||||||
|
Mat image_proxy; |
||||||
|
resize(image_gray, image_proxy, Size(cvRound(image.cols * scale), cvRound(image.rows * scale)), 0, 0, DOWNSCALE_MODE); |
||||||
|
image_proxy.copyTo(image_gray); |
||||||
|
myBoundingBox.x *= scale; |
||||||
|
myBoundingBox.y *= scale; |
||||||
|
myBoundingBox.width *= scale; |
||||||
|
myBoundingBox.height *= scale; |
||||||
|
} |
||||||
|
model = Ptr<TrackerTLDModel>(new TrackerTLDModel(params, image_gray, myBoundingBox, data->getMinSize())); |
||||||
|
detector = Ptr<TLDDetector>(new TLDDetector(params, model)); |
||||||
|
data->confident = false; |
||||||
|
data->failedLastTime = false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool TrackerTLDImpl::updateImpl(const Mat& image, Rect2d& boundingBox) |
||||||
|
{ |
||||||
|
Mat image_gray, image_blurred, imageForDetector; |
||||||
|
cvtColor( image, image_gray, COLOR_BGR2GRAY ); |
||||||
|
double scale = data->getScale(); |
||||||
|
if( scale > 1.0 ) |
||||||
|
resize(image_gray, imageForDetector, Size(cvRound(image.cols*scale), cvRound(image.rows*scale)), 0, 0, DOWNSCALE_MODE); |
||||||
|
else |
||||||
|
imageForDetector = image_gray; |
||||||
|
GaussianBlur(imageForDetector, image_blurred, GaussBlurKernelSize, 0.0); |
||||||
|
TrackerTLDModel* tldModel = ((TrackerTLDModel*)static_cast<TrackerModel*>(model)); |
||||||
|
data->frameNum++; |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE); |
||||||
|
std::vector<TLDDetector::LabeledPatch> detectorResults; |
||||||
|
//best overlap around 92%
|
||||||
|
|
||||||
|
std::vector<Rect2d> candidates; |
||||||
|
std::vector<double> candidatesRes; |
||||||
|
bool trackerNeedsReInit = false; |
||||||
|
for( int i = 0; i < 2; i++ ) |
||||||
|
{ |
||||||
|
Rect2d tmpCandid = boundingBox; |
||||||
|
if( ( (i == 0) && !data->failedLastTime && trackerProxy->update(image, tmpCandid) ) ||
|
||||||
|
( (i == 1) && detector->detect(imageForDetector, image_blurred, tmpCandid, detectorResults) ) ) |
||||||
|
{ |
||||||
|
candidates.push_back(tmpCandid); |
||||||
|
if( i == 0 ) |
||||||
|
resample(image_gray, tmpCandid, standardPatch); |
||||||
|
else |
||||||
|
resample(imageForDetector, tmpCandid, standardPatch); |
||||||
|
candidatesRes.push_back(tldModel->Sc(standardPatch)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if( i == 0 ) |
||||||
|
trackerNeedsReInit = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<double>::iterator it = std::max_element(candidatesRes.begin(), candidatesRes.end()); |
||||||
|
|
||||||
|
dfprintf((stdout, "scale = %f\n", log(1.0 * boundingBox.width / (data->getMinSize()).width) / log(SCALE_STEP))); |
||||||
|
for( int i = 0; i < (int)candidatesRes.size(); i++ ) |
||||||
|
dprintf(("\tcandidatesRes[%d] = %f\n", i, candidatesRes[i])); |
||||||
|
data->printme(); |
||||||
|
tldModel->printme(stdout); |
||||||
|
|
||||||
|
if( it == candidatesRes.end() ) |
||||||
|
{ |
||||||
|
data->confident = false; |
||||||
|
data->failedLastTime = true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
boundingBox = candidates[it - candidatesRes.begin()]; |
||||||
|
data->failedLastTime = false; |
||||||
|
if( trackerNeedsReInit || it != candidatesRes.begin() ) |
||||||
|
trackerProxy->init(image, boundingBox); |
||||||
|
} |
||||||
|
|
||||||
|
#if 1 |
||||||
|
if( it != candidatesRes.end() ) |
||||||
|
{ |
||||||
|
resample(imageForDetector, candidates[it - candidatesRes.begin()], standardPatch); |
||||||
|
dfprintf((stderr, "%d %f %f\n", data->frameNum, tldModel->Sc(standardPatch), tldModel->Sr(standardPatch))); |
||||||
|
if( candidatesRes.size() == 2 && it == (candidatesRes.begin() + 1) ) |
||||||
|
dfprintf((stderr, "detector WON\n")); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
dfprintf((stderr, "%d x x\n", data->frameNum)); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
if( *it > CORE_THRESHOLD ) |
||||||
|
data->confident = true; |
||||||
|
|
||||||
|
if( data->confident ) |
||||||
|
{ |
||||||
|
Pexpert pExpert(imageForDetector, image_blurred, boundingBox, detector, params, data->getMinSize()); |
||||||
|
Nexpert nExpert(imageForDetector, boundingBox, detector, params); |
||||||
|
std::vector<Mat_<uchar> > examplesForModel, examplesForEnsemble; |
||||||
|
examplesForModel.reserve(100); examplesForEnsemble.reserve(100); |
||||||
|
int negRelabeled = 0; |
||||||
|
for( int i = 0; i < (int)detectorResults.size(); i++ ) |
||||||
|
{ |
||||||
|
bool expertResult; |
||||||
|
if( detectorResults[i].isObject ) |
||||||
|
{ |
||||||
|
expertResult = nExpert(detectorResults[i].rect); |
||||||
|
if( expertResult != detectorResults[i].isObject ) |
||||||
|
negRelabeled++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
expertResult = pExpert(detectorResults[i].rect); |
||||||
|
} |
||||||
|
|
||||||
|
detectorResults[i].shouldBeIntegrated = detectorResults[i].shouldBeIntegrated || (detectorResults[i].isObject != expertResult); |
||||||
|
detectorResults[i].isObject = expertResult; |
||||||
|
} |
||||||
|
tldModel->integrateRelabeled(imageForDetector, image_blurred, detectorResults); |
||||||
|
dprintf(("%d relabeled by nExpert\n", negRelabeled)); |
||||||
|
pExpert.additionalExamples(examplesForModel, examplesForEnsemble); |
||||||
|
tldModel->integrateAdditional(examplesForModel, examplesForEnsemble, true); |
||||||
|
examplesForModel.clear(); examplesForEnsemble.clear(); |
||||||
|
nExpert.additionalExamples(examplesForModel, examplesForEnsemble); |
||||||
|
tldModel->integrateAdditional(examplesForModel, examplesForEnsemble, false); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
#ifdef CLOSED_LOOP |
||||||
|
tldModel->integrateRelabeled(imageForDetector, image_blurred, detectorResults); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
TrackerTLDModel::TrackerTLDModel(TrackerTLD::Params params, const Mat& image, const Rect2d& boundingBox, Size minSize):minSize_(minSize), |
||||||
|
timeStampPositiveNext(0), timeStampNegativeNext(0), params_(params), boundingBox_(boundingBox) |
||||||
|
{ |
||||||
|
originalVariance_ = variance(image(boundingBox)); |
||||||
|
std::vector<Rect2d> closest, scanGrid; |
||||||
|
Mat scaledImg, blurredImg, image_blurred; |
||||||
|
|
||||||
|
double scale = scaleAndBlur(image, cvRound(log(1.0 * boundingBox.width / (minSize.width)) / log(SCALE_STEP)), |
||||||
|
scaledImg, blurredImg, GaussBlurKernelSize, SCALE_STEP); |
||||||
|
GaussianBlur(image, image_blurred, GaussBlurKernelSize, 0.0); |
||||||
|
TLDDetector::generateScanGrid(image.rows, image.cols, minSize, scanGrid); |
||||||
|
getClosestN(scanGrid, Rect2d(boundingBox.x / scale, boundingBox.y / scale, boundingBox.width / scale, boundingBox.height / scale), 10, closest); |
||||||
|
|
||||||
|
Mat_<uchar> blurredPatch(minSize); |
||||||
|
TLDEnsembleClassifier::makeClassifiers(minSize, MEASURES_PER_CLASSIFIER, GRIDSIZE, classifiers); |
||||||
|
|
||||||
|
positiveExamples.reserve(200); |
||||||
|
for( int i = 0; i < (int)closest.size(); i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < 20; j++ ) |
||||||
|
{ |
||||||
|
Point2f center; |
||||||
|
Size2f size; |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE); |
||||||
|
center.x = (float)(closest[i].x + closest[i].width * (0.5 + rng.uniform(-0.01, 0.01))); |
||||||
|
center.y = (float)(closest[i].y + closest[i].height * (0.5 + rng.uniform(-0.01, 0.01))); |
||||||
|
size.width = (float)(closest[i].width * rng.uniform((double)0.99, (double)1.01)); |
||||||
|
size.height = (float)(closest[i].height * rng.uniform((double)0.99, (double)1.01)); |
||||||
|
float angle = (float)rng.uniform(-10.0, 10.0); |
||||||
|
|
||||||
|
resample(scaledImg, RotatedRect(center, size, angle), standardPatch); |
||||||
|
|
||||||
|
for( int y = 0; y < standardPatch.rows; y++ ) |
||||||
|
{ |
||||||
|
for( int x = 0; x < standardPatch.cols; x++ ) |
||||||
|
{ |
||||||
|
standardPatch(x, y) += (uchar)rng.gaussian(5.0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef BLUR_AS_VADIM |
||||||
|
GaussianBlur(standardPatch, blurredPatch, GaussBlurKernelSize, 0.0); |
||||||
|
resize(blurredPatch, blurredPatch, minSize); |
||||||
|
#else |
||||||
|
resample(blurredImg, RotatedRect(center, size, angle), blurredPatch); |
||||||
|
#endif |
||||||
|
pushIntoModel(standardPatch, true); |
||||||
|
for( int k = 0; k < (int)classifiers.size(); k++ ) |
||||||
|
classifiers[k].integrate(blurredPatch, true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TLDDetector::generateScanGrid(image.rows, image.cols, minSize, scanGrid, true); |
||||||
|
negativeExamples.clear(); |
||||||
|
negativeExamples.reserve(NEG_EXAMPLES_IN_INIT_MODEL); |
||||||
|
std::vector<int> indices; |
||||||
|
indices.reserve(NEG_EXAMPLES_IN_INIT_MODEL); |
||||||
|
while( (int)negativeExamples.size() < NEG_EXAMPLES_IN_INIT_MODEL ) |
||||||
|
{ |
||||||
|
int i = rng.uniform((int)0, (int)scanGrid.size()); |
||||||
|
if( std::find(indices.begin(), indices.end(), i) == indices.end() && overlap(boundingBox, scanGrid[i]) < NEXPERT_THRESHOLD ) |
||||||
|
{ |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE); |
||||||
|
resample(image, scanGrid[i], standardPatch); |
||||||
|
pushIntoModel(standardPatch, false); |
||||||
|
|
||||||
|
resample(image_blurred, scanGrid[i], blurredPatch); |
||||||
|
for( int k = 0; k < (int)classifiers.size(); k++ ) |
||||||
|
classifiers[k].integrate(blurredPatch, false); |
||||||
|
} |
||||||
|
} |
||||||
|
dprintf(("positive patches: %d\nnegative patches: %d\n", (int)positiveExamples.size(), (int)negativeExamples.size())); |
||||||
|
} |
||||||
|
|
||||||
|
void TLDDetector::generateScanGrid(int rows, int cols, Size initBox, std::vector<Rect2d>& res, bool withScaling) |
||||||
|
{ |
||||||
|
res.clear(); |
||||||
|
//scales step: SCALE_STEP; hor step: 10% of width; verstep: 10% of height; minsize: 20pix
|
||||||
|
for( double h = initBox.height, w = initBox.width; h < cols && w < rows; ) |
||||||
|
{ |
||||||
|
for( double x = 0; (x + w + 1.0) <= cols; x += (0.1 * w) ) |
||||||
|
{ |
||||||
|
for( double y = 0; (y + h + 1.0) <= rows; y += (0.1 * h) ) |
||||||
|
res.push_back(Rect2d(x, y, w, h)); |
||||||
|
} |
||||||
|
if( withScaling ) |
||||||
|
{ |
||||||
|
if( h <= initBox.height ) |
||||||
|
{ |
||||||
|
h /= SCALE_STEP; w /= SCALE_STEP; |
||||||
|
if( h < 20 || w < 20 ) |
||||||
|
{ |
||||||
|
h = initBox.height * SCALE_STEP; w = initBox.width * SCALE_STEP; |
||||||
|
CV_Assert( h > initBox.height || w > initBox.width); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
h *= SCALE_STEP; w *= SCALE_STEP; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
dprintf(("%d rects in res\n", (int)res.size())); |
||||||
|
} |
||||||
|
|
||||||
|
bool TLDDetector::detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector<LabeledPatch>& patches) |
||||||
|
{ |
||||||
|
TrackerTLDModel* tldModel = ((TrackerTLDModel*)static_cast<TrackerModel*>(model)); |
||||||
|
Size initSize = tldModel->getMinSize(); |
||||||
|
patches.clear(); |
||||||
|
|
||||||
|
Mat resized_img, blurred_img; |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE); |
||||||
|
img.copyTo(resized_img); |
||||||
|
imgBlurred.copyTo(blurred_img); |
||||||
|
double originalVariance = tldModel->getOriginalVariance(); ; |
||||||
|
int dx = initSize.width / 10, dy = initSize.height / 10; |
||||||
|
Size2d size = img.size(); |
||||||
|
double scale = 1.0; |
||||||
|
int total = 0, pass = 0; |
||||||
|
int npos = 0, nneg = 0; |
||||||
|
double tmp = 0, maxSc = -5.0; |
||||||
|
Rect2d maxScRect; |
||||||
|
|
||||||
|
START_TICK("detector"); |
||||||
|
do |
||||||
|
{ |
||||||
|
Mat_<double> intImgP, intImgP2; |
||||||
|
computeIntegralImages(resized_img, intImgP, intImgP2); |
||||||
|
|
||||||
|
tldModel->prepareClassifiers((int)blurred_img.step[0]); |
||||||
|
for( int i = 0, imax = cvFloor((0.0 + resized_img.cols - initSize.width) / dx); i < imax; i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0, jmax = cvFloor((0.0 + resized_img.rows - initSize.height) / dy); j < jmax; j++ ) |
||||||
|
{ |
||||||
|
LabeledPatch labPatch; |
||||||
|
total++; |
||||||
|
if( !patchVariance(intImgP, intImgP2, originalVariance, Point(dx * i, dy * j), initSize) ) |
||||||
|
continue; |
||||||
|
if( tldModel->ensembleClassifierNum(&blurred_img.at<uchar>(dy * j, dx * i)) <= ENSEMBLE_THRESHOLD ) |
||||||
|
continue; |
||||||
|
pass++; |
||||||
|
|
||||||
|
labPatch.rect = Rect2d(dx * i * scale, dy * j * scale, initSize.width * scale, initSize.height * scale); |
||||||
|
resample(resized_img, Rect2d(Point(dx * i, dy * j), initSize), standardPatch); |
||||||
|
tmp = tldModel->Sr(standardPatch); |
||||||
|
labPatch.isObject = tmp > THETA_NN; |
||||||
|
labPatch.shouldBeIntegrated = abs(tmp - THETA_NN) < 0.1; |
||||||
|
patches.push_back(labPatch); |
||||||
|
|
||||||
|
if( !labPatch.isObject ) |
||||||
|
{ |
||||||
|
nneg++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
npos++; |
||||||
|
} |
||||||
|
tmp = tldModel->Sc(standardPatch); |
||||||
|
if( tmp > maxSc ) |
||||||
|
{ |
||||||
|
maxSc = tmp; |
||||||
|
maxScRect = labPatch.rect; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size.width /= SCALE_STEP; |
||||||
|
size.height /= SCALE_STEP; |
||||||
|
scale *= SCALE_STEP; |
||||||
|
resize(img, resized_img, size, 0, 0, DOWNSCALE_MODE); |
||||||
|
GaussianBlur(resized_img, blurred_img, GaussBlurKernelSize, 0.0f); |
||||||
|
} |
||||||
|
while( size.width >= initSize.width && size.height >= initSize.height ); |
||||||
|
END_TICK("detector"); |
||||||
|
|
||||||
|
dfprintf((stdout, "after NCC: nneg = %d npos = %d\n", nneg, npos)); |
||||||
|
#if !0 |
||||||
|
std::vector<Rect2d> poss, negs; |
||||||
|
|
||||||
|
for( int i = 0; i < (int)patches.size(); i++ ) |
||||||
|
{ |
||||||
|
if( patches[i].isObject ) |
||||||
|
poss.push_back(patches[i].rect); |
||||||
|
else |
||||||
|
negs.push_back(patches[i].rect); |
||||||
|
} |
||||||
|
dfprintf((stdout, "%d pos and %d neg\n", (int)poss.size(), (int)negs.size())); |
||||||
|
drawWithRects(img, negs, poss, "tech"); |
||||||
|
#endif |
||||||
|
|
||||||
|
dfprintf((stdout, "%d after ensemble\n", pass)); |
||||||
|
if( maxSc < 0 ) |
||||||
|
return false; |
||||||
|
res = maxScRect; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Computes the variance of subimage given by box, with the help of two integral
|
||||||
|
* images intImgP and intImgP2 (sum of squares), which should be also provided.*/ |
||||||
|
bool TLDDetector::patchVariance(Mat_<double>& intImgP, Mat_<double>& intImgP2, double originalVariance, Point pt, Size size) |
||||||
|
{ |
||||||
|
int x = (pt.x), y = (pt.y), width = (size.width), height = (size.height); |
||||||
|
CV_Assert( 0 <= x && (x + width) < intImgP.cols && (x + width) < intImgP2.cols ); |
||||||
|
CV_Assert( 0 <= y && (y + height) < intImgP.rows && (y + height) < intImgP2.rows ); |
||||||
|
double p = 0, p2 = 0; |
||||||
|
double A, B, C, D; |
||||||
|
|
||||||
|
A = intImgP(y, x); |
||||||
|
B = intImgP(y, x + width); |
||||||
|
C = intImgP(y + height, x); |
||||||
|
D = intImgP(y + height, x + width); |
||||||
|
p = (A + D - B - C) / (width * height); |
||||||
|
|
||||||
|
A = intImgP2(y, x); |
||||||
|
B = intImgP2(y, x + width); |
||||||
|
C = intImgP2(y + height, x); |
||||||
|
D = intImgP2(y + height, x + width); |
||||||
|
p2 = (A + D - B - C) / (width * height); |
||||||
|
|
||||||
|
return ((p2 - p * p) > VARIANCE_THRESHOLD * originalVariance); |
||||||
|
} |
||||||
|
|
||||||
|
double TrackerTLDModel::ensembleClassifierNum(const uchar* data) |
||||||
|
{ |
||||||
|
double p = 0; |
||||||
|
for( int k = 0; k < (int)classifiers.size(); k++ ) |
||||||
|
p += classifiers[k].posteriorProbabilityFast(data); |
||||||
|
p /= classifiers.size(); |
||||||
|
return p; |
||||||
|
} |
||||||
|
|
||||||
|
double TrackerTLDModel::Sr(const Mat_<uchar>& patch) |
||||||
|
{ |
||||||
|
double splus = 0.0, sminus = 0.0; |
||||||
|
for( int i = 0; i < (int)positiveExamples.size(); i++ ) |
||||||
|
splus = std::max(splus, 0.5 * (NCC(positiveExamples[i], patch) + 1.0)); |
||||||
|
for( int i = 0; i < (int)negativeExamples.size(); i++ ) |
||||||
|
sminus = std::max(sminus, 0.5 * (NCC(negativeExamples[i], patch) + 1.0)); |
||||||
|
if( splus + sminus == 0.0) |
||||||
|
return 0.0; |
||||||
|
return splus / (sminus + splus); |
||||||
|
} |
||||||
|
|
||||||
|
double TrackerTLDModel::Sc(const Mat_<uchar>& patch) |
||||||
|
{ |
||||||
|
double splus = 0.0, sminus = 0.0; |
||||||
|
int med = getMedian(timeStampsPositive); |
||||||
|
for( int i = 0; i < (int)positiveExamples.size(); i++ ) |
||||||
|
{ |
||||||
|
if( (int)timeStampsPositive[i] <= med ) |
||||||
|
splus = std::max(splus, 0.5 * (NCC(positiveExamples[i], patch) + 1.0)); |
||||||
|
} |
||||||
|
for( int i = 0; i < (int)negativeExamples.size(); i++ ) |
||||||
|
sminus = std::max(sminus, 0.5 * (NCC(negativeExamples[i], patch) + 1.0)); |
||||||
|
if( splus + sminus == 0.0 ) |
||||||
|
return 0.0; |
||||||
|
return splus / (sminus + splus); |
||||||
|
} |
||||||
|
|
||||||
|
void TrackerTLDModel::integrateRelabeled(Mat& img, Mat& imgBlurred, const std::vector<TLDDetector::LabeledPatch>& patches) |
||||||
|
{ |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE), blurredPatch(minSize_); |
||||||
|
int positiveIntoModel = 0, negativeIntoModel = 0, positiveIntoEnsemble = 0, negativeIntoEnsemble = 0; |
||||||
|
for( int k = 0; k < (int)patches.size(); k++ ) |
||||||
|
{ |
||||||
|
if( patches[k].shouldBeIntegrated ) |
||||||
|
{ |
||||||
|
resample(img, patches[k].rect, standardPatch); |
||||||
|
if( patches[k].isObject ) |
||||||
|
{ |
||||||
|
positiveIntoModel++; |
||||||
|
pushIntoModel(standardPatch, true); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
negativeIntoModel++; |
||||||
|
pushIntoModel(standardPatch, false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef CLOSED_LOOP |
||||||
|
if( patches[k].shouldBeIntegrated || !patches[k].isPositive ) |
||||||
|
#else |
||||||
|
if( patches[k].shouldBeIntegrated ) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
resample(imgBlurred, patches[k].rect, blurredPatch); |
||||||
|
if( patches[k].isObject ) |
||||||
|
positiveIntoEnsemble++; |
||||||
|
else |
||||||
|
negativeIntoEnsemble++; |
||||||
|
for( int i = 0; i < (int)classifiers.size(); i++ ) |
||||||
|
classifiers[i].integrate(blurredPatch, patches[k].isObject); |
||||||
|
} |
||||||
|
} |
||||||
|
if( negativeIntoModel > 0 ) |
||||||
|
dfprintf((stdout, "negativeIntoModel = %d ", negativeIntoModel)); |
||||||
|
if( positiveIntoModel > 0) |
||||||
|
dfprintf((stdout, "positiveIntoModel = %d ", positiveIntoModel)); |
||||||
|
if( negativeIntoEnsemble > 0 ) |
||||||
|
dfprintf((stdout, "negativeIntoEnsemble = %d ", negativeIntoEnsemble)); |
||||||
|
if( positiveIntoEnsemble > 0 ) |
||||||
|
dfprintf((stdout, "positiveIntoEnsemble = %d ", positiveIntoEnsemble)); |
||||||
|
dfprintf((stdout, "\n")); |
||||||
|
} |
||||||
|
|
||||||
|
void TrackerTLDModel::integrateAdditional(const std::vector<Mat_<uchar> >& eForModel, const std::vector<Mat_<uchar> >& eForEnsemble, bool isPositive) |
||||||
|
{ |
||||||
|
int positiveIntoModel = 0, negativeIntoModel = 0, positiveIntoEnsemble = 0, negativeIntoEnsemble = 0; |
||||||
|
for( int k = 0; k < (int)eForModel.size(); k++ ) |
||||||
|
{ |
||||||
|
double sr = Sr(eForModel[k]); |
||||||
|
if( ( sr > THETA_NN ) != isPositive ) |
||||||
|
{ |
||||||
|
if( isPositive ) |
||||||
|
{ |
||||||
|
positiveIntoModel++; |
||||||
|
pushIntoModel(eForModel[k], true); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
negativeIntoModel++; |
||||||
|
pushIntoModel(eForModel[k], false); |
||||||
|
} |
||||||
|
} |
||||||
|
double p = 0; |
||||||
|
for( int i = 0; i < (int)classifiers.size(); i++ ) |
||||||
|
p += classifiers[i].posteriorProbability(eForEnsemble[k].data, (int)eForEnsemble[k].step[0]); |
||||||
|
p /= classifiers.size(); |
||||||
|
if( ( p > ENSEMBLE_THRESHOLD ) != isPositive ) |
||||||
|
{ |
||||||
|
if( isPositive ) |
||||||
|
positiveIntoEnsemble++; |
||||||
|
else |
||||||
|
negativeIntoEnsemble++; |
||||||
|
for( int i = 0; i < (int)classifiers.size(); i++ ) |
||||||
|
classifiers[i].integrate(eForEnsemble[k], isPositive); |
||||||
|
} |
||||||
|
} |
||||||
|
if( negativeIntoModel > 0 ) |
||||||
|
dfprintf((stdout, "negativeIntoModel = %d ", negativeIntoModel)); |
||||||
|
if( positiveIntoModel > 0 ) |
||||||
|
dfprintf((stdout, "positiveIntoModel = %d ", positiveIntoModel)); |
||||||
|
if( negativeIntoEnsemble > 0 ) |
||||||
|
dfprintf((stdout, "negativeIntoEnsemble = %d ", negativeIntoEnsemble)); |
||||||
|
if( positiveIntoEnsemble > 0 ) |
||||||
|
dfprintf((stdout, "positiveIntoEnsemble = %d ", positiveIntoEnsemble)); |
||||||
|
dfprintf((stdout, "\n")); |
||||||
|
} |
||||||
|
|
||||||
|
int TrackerTLDImpl::Pexpert::additionalExamples(std::vector<Mat_<uchar> >& examplesForModel, std::vector<Mat_<uchar> >& examplesForEnsemble) |
||||||
|
{ |
||||||
|
examplesForModel.clear(); examplesForEnsemble.clear(); |
||||||
|
examplesForModel.reserve(100); examplesForEnsemble.reserve(100); |
||||||
|
|
||||||
|
std::vector<Rect2d> closest, scanGrid; |
||||||
|
Mat scaledImg, blurredImg; |
||||||
|
|
||||||
|
double scale = scaleAndBlur(img_, cvRound(log(1.0 * resultBox_.width / (initSize_.width)) / log(SCALE_STEP)), |
||||||
|
scaledImg, blurredImg, GaussBlurKernelSize, SCALE_STEP); |
||||||
|
TLDDetector::generateScanGrid(img_.rows, img_.cols, initSize_, scanGrid); |
||||||
|
getClosestN(scanGrid, Rect2d(resultBox_.x / scale, resultBox_.y / scale, resultBox_.width / scale, resultBox_.height / scale), 10, closest); |
||||||
|
|
||||||
|
for( int i = 0; i < (int)closest.size(); i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < 10; j++ ) |
||||||
|
{ |
||||||
|
Point2f center; |
||||||
|
Size2f size; |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE), blurredPatch(initSize_); |
||||||
|
center.x = (float)(closest[i].x + closest[i].width * (0.5 + rng.uniform(-0.01, 0.01))); |
||||||
|
center.y = (float)(closest[i].y + closest[i].height * (0.5 + rng.uniform(-0.01, 0.01))); |
||||||
|
size.width = (float)(closest[i].width * rng.uniform((double)0.99, (double)1.01)); |
||||||
|
size.height = (float)(closest[i].height * rng.uniform((double)0.99, (double)1.01)); |
||||||
|
float angle = (float)rng.uniform(-5.0, 5.0); |
||||||
|
|
||||||
|
for( int y = 0; y < standardPatch.rows; y++ ) |
||||||
|
{ |
||||||
|
for( int x = 0; x < standardPatch.cols; x++ ) |
||||||
|
{ |
||||||
|
standardPatch(x, y) += (uchar)rng.gaussian(5.0); |
||||||
|
} |
||||||
|
} |
||||||
|
#ifdef BLUR_AS_VADIM |
||||||
|
GaussianBlur(standardPatch, blurredPatch, GaussBlurKernelSize, 0.0); |
||||||
|
resize(blurredPatch, blurredPatch, initSize_); |
||||||
|
#else |
||||||
|
resample(blurredImg, RotatedRect(center, size, angle), blurredPatch); |
||||||
|
#endif |
||||||
|
resample(scaledImg, RotatedRect(center, size, angle), standardPatch); |
||||||
|
examplesForModel.push_back(standardPatch); |
||||||
|
examplesForEnsemble.push_back(blurredPatch); |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool TrackerTLDImpl::Nexpert::operator()(Rect2d box) |
||||||
|
{ |
||||||
|
if( overlap(resultBox_, box) < NEXPERT_THRESHOLD ) |
||||||
|
return false; |
||||||
|
else |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
Data::Data(Rect2d initBox) |
||||||
|
{ |
||||||
|
double minDim = std::min(initBox.width, initBox.height); |
||||||
|
scale = 20.0 / minDim; |
||||||
|
minSize.width = (int)(initBox.width * 20.0 / minDim); |
||||||
|
minSize.height = (int)(initBox.height * 20.0 / minDim); |
||||||
|
frameNum = 0; |
||||||
|
dprintf(("minSize = %dx%d\n", minSize.width, minSize.height)); |
||||||
|
} |
||||||
|
|
||||||
|
void Data::printme(FILE* port) |
||||||
|
{ |
||||||
|
dfprintf((port, "Data:\n")); |
||||||
|
dfprintf((port, "\tframeNum = %d\n", frameNum)); |
||||||
|
dfprintf((port, "\tconfident = %s\n", confident?"true":"false")); |
||||||
|
dfprintf((port, "\tfailedLastTime = %s\n", failedLastTime?"true":"false")); |
||||||
|
dfprintf((port, "\tminSize = %dx%d\n", minSize.width, minSize.height)); |
||||||
|
} |
||||||
|
|
||||||
|
void TrackerTLDModel::printme(FILE* port) |
||||||
|
{ |
||||||
|
dfprintf((port, "TrackerTLDModel:\n")); |
||||||
|
dfprintf((port, "\tpositiveExamples.size() = %d\n", (int)positiveExamples.size())); |
||||||
|
dfprintf((port, "\tnegativeExamples.size() = %d\n", (int)negativeExamples.size())); |
||||||
|
} |
||||||
|
|
||||||
|
void MyMouseCallbackDEBUG::onMouse(int event, int x, int y) |
||||||
|
{ |
||||||
|
if( event == EVENT_LBUTTONDOWN ) |
||||||
|
{ |
||||||
|
Mat imgCanvas; |
||||||
|
img_.copyTo(imgCanvas); |
||||||
|
TrackerTLDModel* tldModel = ((TrackerTLDModel*)static_cast<TrackerModel*>(detector_->model)); |
||||||
|
Size initSize = tldModel->getMinSize(); |
||||||
|
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE); |
||||||
|
double originalVariance = tldModel->getOriginalVariance(); |
||||||
|
double tmp; |
||||||
|
|
||||||
|
Mat resized_img, blurred_img; |
||||||
|
double scale = SCALE_STEP; |
||||||
|
//double scale = SCALE_STEP * SCALE_STEP * SCALE_STEP * SCALE_STEP;
|
||||||
|
Size2d size(img_.cols / scale, img_.rows / scale); |
||||||
|
resize(img_, resized_img, size); |
||||||
|
resize(imgBlurred_, blurred_img, size); |
||||||
|
|
||||||
|
Mat_<double> intImgP, intImgP2; |
||||||
|
detector_->computeIntegralImages(resized_img, intImgP, intImgP2); |
||||||
|
|
||||||
|
int dx = initSize.width / 10, dy = initSize.height / 10, |
||||||
|
i = (int)(x / scale / dx), j = (int)(y / scale / dy); |
||||||
|
|
||||||
|
dfprintf((stderr, "patchVariance = %s\n", (detector_->patchVariance(intImgP, intImgP2, originalVariance, |
||||||
|
Point(dx * i, dy * j), initSize))?"true":"false")); |
||||||
|
tldModel->prepareClassifiers((int)blurred_img.step[0]); |
||||||
|
dfprintf((stderr, "p = %f\n", (tldModel->ensembleClassifierNum(&blurred_img.at<uchar>(dy * j, dx * i))))); |
||||||
|
fprintf(stderr, "ensembleClassifier = %s\n", |
||||||
|
(!(tldModel->ensembleClassifierNum(&blurred_img.at<uchar>(dy * j, dx * i)) > ENSEMBLE_THRESHOLD))?"true":"false"); |
||||||
|
|
||||||
|
resample(resized_img, Rect2d(Point(dx * i, dy * j), initSize), standardPatch); |
||||||
|
tmp = tldModel->Sr(standardPatch); |
||||||
|
dfprintf((stderr, "Sr = %f\n", tmp)); |
||||||
|
dfprintf((stderr, "isObject = %s\n", (tmp > THETA_NN)?"true":"false")); |
||||||
|
dfprintf((stderr, "shouldBeIntegrated = %s\n", (abs(tmp - THETA_NN) < 0.1)?"true":"false")); |
||||||
|
dfprintf((stderr, "Sc = %f\n", tldModel->Sc(standardPatch))); |
||||||
|
|
||||||
|
rectangle(imgCanvas, Rect2d(Point2d(scale * dx * i, scale * dy * j), Size2d(initSize.width * scale, initSize.height * scale)), 0, 2, 1 ); |
||||||
|
imshow("picker", imgCanvas); |
||||||
|
waitKey(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TrackerTLDModel::pushIntoModel(const Mat_<uchar>& example, bool positive) |
||||||
|
{ |
||||||
|
std::vector<Mat_<uchar> >* proxyV; |
||||||
|
int* proxyN; |
||||||
|
std::vector<int>* proxyT; |
||||||
|
if( positive ) |
||||||
|
{ |
||||||
|
proxyV = &positiveExamples; |
||||||
|
proxyN = &timeStampPositiveNext; |
||||||
|
proxyT = &timeStampsPositive; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
proxyV = &negativeExamples; |
||||||
|
proxyN = &timeStampNegativeNext; |
||||||
|
proxyT = &timeStampsNegative; |
||||||
|
} |
||||||
|
if( (int)proxyV->size() < MAX_EXAMPLES_IN_MODEL ) |
||||||
|
{ |
||||||
|
proxyV->push_back(example); |
||||||
|
proxyT->push_back(*proxyN); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
int index = rng.uniform((int)0, (int)proxyV->size()); |
||||||
|
(*proxyV)[index] = example; |
||||||
|
(*proxyT)[index] = (*proxyN); |
||||||
|
} |
||||||
|
(*proxyN)++; |
||||||
|
} |
||||||
|
void TrackerTLDModel::prepareClassifiers(int rowstep) |
||||||
|
{ |
||||||
|
for( int i = 0; i < (int)classifiers.size(); i++ )
|
||||||
|
classifiers[i].prepareClassifier(rowstep);
|
||||||
|
} |
||||||
|
|
||||||
|
} /* namespace tld */ |
||||||
|
|
||||||
|
} /* namespace cv */ |
@ -0,0 +1,125 @@ |
|||||||
|
/*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) 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:
|
||||||
|
//
|
||||||
|
// * 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 "opencv2/video/tracking.hpp" |
||||||
|
#include "opencv2/imgproc.hpp" |
||||||
|
#include<algorithm> |
||||||
|
#include<limits.h> |
||||||
|
|
||||||
|
namespace cv {namespace tld |
||||||
|
{ |
||||||
|
|
||||||
|
//debug functions and variables
|
||||||
|
#define ALEX_DEBUG |
||||||
|
#ifdef ALEX_DEBUG |
||||||
|
#define dfprintf(x) fprintf x |
||||||
|
#define dprintf(x) printf x |
||||||
|
#else |
||||||
|
#define dfprintf(x) |
||||||
|
#define dprintf(x) |
||||||
|
#endif |
||||||
|
#define MEASURE_TIME(a)\ |
||||||
|
{\
|
||||||
|
clock_t start; float milisec = 0.0; \
|
||||||
|
start = clock(); {a} milisec = 1000.0 * (clock() - start) / CLOCKS_PER_SEC; \
|
||||||
|
dprintf(("%-90s took %f milis\n", #a, milisec));\
|
||||||
|
} |
||||||
|
#define HERE dprintf(("line %d\n", __LINE__)); fflush(stderr); |
||||||
|
#define START_TICK(name)\ |
||||||
|
{ \
|
||||||
|
clock_t start; double milisec = 0.0; start = clock(); |
||||||
|
#define END_TICK(name) milisec = 1000.0 * (clock() - start) / CLOCKS_PER_SEC; \ |
||||||
|
dprintf(("%s took %f milis\n", name, milisec)); \
|
||||||
|
} |
||||||
|
extern Rect2d etalon; |
||||||
|
void myassert(const Mat& img); |
||||||
|
void printPatch(const Mat_<uchar>& standardPatch); |
||||||
|
std::string type2str(const Mat& mat); |
||||||
|
void drawWithRects(const Mat& img, std::vector<Rect2d>& blackOnes, Rect2d whiteOne = Rect2d(-1.0, -1.0, -1.0, -1.0)); |
||||||
|
void drawWithRects(const Mat& img, std::vector<Rect2d>& blackOnes, std::vector<Rect2d>& whiteOnes, String fileName = ""); |
||||||
|
|
||||||
|
//aux functions and variables
|
||||||
|
template<typename T> inline T CLIP(T x, T a, T b){ return std::min(std::max(x, a), b); } |
||||||
|
/** Computes overlap between the two given rectangles. Overlap is computed as ratio of rectangles' intersection to that
|
||||||
|
* of their union.*/ |
||||||
|
double overlap(const Rect2d& r1, const Rect2d& r2); |
||||||
|
/** Resamples the area surrounded by r2 in img so it matches the size of samples, where it is written.*/ |
||||||
|
void resample(const Mat& img, const RotatedRect& r2, Mat_<uchar>& samples); |
||||||
|
/** Specialization of resample() for rectangles without retation for better performance and simplicity.*/ |
||||||
|
void resample(const Mat& img, const Rect2d& r2, Mat_<uchar>& samples); |
||||||
|
/** Computes the variance of single given image.*/ |
||||||
|
double variance(const Mat& img); |
||||||
|
/** Computes normalized corellation coefficient between the two patches (they should be
|
||||||
|
* of the same size).*/ |
||||||
|
double NCC(const Mat_<uchar>& patch1, const Mat_<uchar>& patch2); |
||||||
|
void getClosestN(std::vector<Rect2d>& scanGrid, Rect2d bBox, int n, std::vector<Rect2d>& res); |
||||||
|
double scaleAndBlur(const Mat& originalImg, int scale, Mat& scaledImg, Mat& blurredImg, Size GaussBlurKernelSize, double scaleStep); |
||||||
|
int getMedian(const std::vector<int>& values, int size = -1); |
||||||
|
|
||||||
|
class TLDEnsembleClassifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
static int makeClassifiers(Size size, int measurePerClassifier, int gridSize, std::vector<TLDEnsembleClassifier>& classifiers); |
||||||
|
void integrate(const Mat_<uchar>& patch, bool isPositive); |
||||||
|
double posteriorProbability(const uchar* data, int rowstep) const; |
||||||
|
double posteriorProbabilityFast(const uchar* data) const; |
||||||
|
void prepareClassifier(int rowstep); |
||||||
|
private: |
||||||
|
TLDEnsembleClassifier(const std::vector<Vec4b>& meas, int beg, int end); |
||||||
|
static void stepPrefSuff(std::vector<Vec4b> & arr, int pos, int len, int gridSize); |
||||||
|
int code(const uchar* data, int rowstep) const; |
||||||
|
int codeFast(const uchar* data) const; |
||||||
|
std::vector<Point2i> posAndNeg; |
||||||
|
std::vector<Vec4b> measurements; |
||||||
|
std::vector<Point2i> offset; |
||||||
|
int lastStep_; |
||||||
|
}; |
||||||
|
|
||||||
|
class TrackerProxy |
||||||
|
{ |
||||||
|
public: |
||||||
|
virtual bool init(const Mat& image, const Rect2d& boundingBox) = 0; |
||||||
|
virtual bool update(const Mat& image, Rect2d& boundingBox) = 0; |
||||||
|
virtual ~TrackerProxy(){} |
||||||
|
}; |
||||||
|
|
||||||
|
}} |
@ -0,0 +1,404 @@ |
|||||||
|
/*///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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) 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:
|
||||||
|
//
|
||||||
|
// * 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 "opencv2/video/tracking.hpp" |
||||||
|
#include "opencv2/imgproc.hpp" |
||||||
|
#include "time.h" |
||||||
|
#include<algorithm> |
||||||
|
#include<limits.h> |
||||||
|
#include<math.h> |
||||||
|
#include<opencv2/highgui.hpp> |
||||||
|
#include "tld_tracker.hpp" |
||||||
|
|
||||||
|
namespace cv {namespace tld |
||||||
|
{ |
||||||
|
|
||||||
|
//debug functions and variables
|
||||||
|
Rect2d etalon(14.0, 110.0, 20.0, 20.0); |
||||||
|
void drawWithRects(const Mat& img, std::vector<Rect2d>& blackOnes, Rect2d whiteOne) |
||||||
|
{ |
||||||
|
Mat image; |
||||||
|
img.copyTo(image); |
||||||
|
if( whiteOne.width >= 0 ) |
||||||
|
rectangle( image, whiteOne, 255, 1, 1 ); |
||||||
|
for( int i = 0; i < (int)blackOnes.size(); i++ ) |
||||||
|
rectangle( image, blackOnes[i], 0, 1, 1 ); |
||||||
|
imshow("img", image); |
||||||
|
} |
||||||
|
void drawWithRects(const Mat& img, std::vector<Rect2d>& blackOnes, std::vector<Rect2d>& whiteOnes, String filename) |
||||||
|
{ |
||||||
|
Mat image; |
||||||
|
static int frameCounter = 1; |
||||||
|
img.copyTo(image); |
||||||
|
for( int i = 0; i < (int)whiteOnes.size(); i++ ) |
||||||
|
rectangle( image, whiteOnes[i], 255, 1, 1 ); |
||||||
|
for( int i = 0; i < (int)blackOnes.size(); i++ ) |
||||||
|
rectangle( image, blackOnes[i], 0, 1, 1 ); |
||||||
|
imshow("img", image); |
||||||
|
if( filename.length() > 0 ) |
||||||
|
{ |
||||||
|
char inbuf[100]; |
||||||
|
sprintf(inbuf, "%s%d.jpg", filename.c_str(), frameCounter); |
||||||
|
imwrite(inbuf, image); |
||||||
|
frameCounter++; |
||||||
|
} |
||||||
|
} |
||||||
|
void myassert(const Mat& img) |
||||||
|
{ |
||||||
|
int count = 0; |
||||||
|
for( int i = 0; i < img.rows; i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < img.cols; j++ ) |
||||||
|
{ |
||||||
|
if( img.at<uchar>(i, j) == 0 ) |
||||||
|
count++; |
||||||
|
} |
||||||
|
} |
||||||
|
dprintf(("black: %d out of %d (%f)\n", count, img.rows * img.cols, 1.0 * count / img.rows / img.cols)); |
||||||
|
} |
||||||
|
|
||||||
|
void printPatch(const Mat_<uchar>& standardPatch) |
||||||
|
{ |
||||||
|
for( int i = 0; i < standardPatch.rows; i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < standardPatch.cols; j++ ) |
||||||
|
dprintf(("%5.2f, ", (double)standardPatch(i, j))); |
||||||
|
dprintf(("\n")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string type2str(const Mat& mat) |
||||||
|
{ |
||||||
|
int type = mat.type(); |
||||||
|
std::string r; |
||||||
|
|
||||||
|
uchar depth = type & CV_MAT_DEPTH_MASK; |
||||||
|
uchar chans = (uchar)(1 + (type >> CV_CN_SHIFT)); |
||||||
|
|
||||||
|
switch ( depth ) { |
||||||
|
case CV_8U: r = "8U"; break; |
||||||
|
case CV_8S: r = "8S"; break; |
||||||
|
case CV_16U: r = "16U"; break; |
||||||
|
case CV_16S: r = "16S"; break; |
||||||
|
case CV_32S: r = "32S"; break; |
||||||
|
case CV_32F: r = "32F"; break; |
||||||
|
case CV_64F: r = "64F"; break; |
||||||
|
default: r = "User"; break; |
||||||
|
} |
||||||
|
|
||||||
|
r += "C"; |
||||||
|
r += (chans + '0'); |
||||||
|
|
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
//generic functions
|
||||||
|
double scaleAndBlur(const Mat& originalImg, int scale, Mat& scaledImg, Mat& blurredImg, Size GaussBlurKernelSize, double scaleStep) |
||||||
|
{ |
||||||
|
double dScale = 1.0; |
||||||
|
for( int i = 0; i < scale; i++, dScale *= scaleStep ); |
||||||
|
Size2d size = originalImg.size(); |
||||||
|
size.height /= dScale; size.width /= dScale; |
||||||
|
resize(originalImg, scaledImg, size); |
||||||
|
GaussianBlur(scaledImg, blurredImg, GaussBlurKernelSize, 0.0); |
||||||
|
return dScale; |
||||||
|
} |
||||||
|
void getClosestN(std::vector<Rect2d>& scanGrid, Rect2d bBox, int n, std::vector<Rect2d>& res) |
||||||
|
{ |
||||||
|
if( n >= (int)scanGrid.size() ) |
||||||
|
{ |
||||||
|
res.assign(scanGrid.begin(), scanGrid.end()); |
||||||
|
return; |
||||||
|
} |
||||||
|
std::vector<double> overlaps; |
||||||
|
overlaps.assign(n, 0.0); |
||||||
|
res.assign(scanGrid.begin(), scanGrid.begin() + n); |
||||||
|
for( int i = 0; i < n; i++ ) |
||||||
|
overlaps[i] = overlap(res[i], bBox); |
||||||
|
double otmp; |
||||||
|
Rect2d rtmp; |
||||||
|
for (int i = 1; i < n; i++) |
||||||
|
{ |
||||||
|
int j = i; |
||||||
|
while (j > 0 && overlaps[j - 1] > overlaps[j]) { |
||||||
|
otmp = overlaps[j]; overlaps[j] = overlaps[j - 1]; overlaps[j - 1] = otmp; |
||||||
|
rtmp = res[j]; res[j] = res[j - 1]; res[j - 1] = rtmp; |
||||||
|
j--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for( int i = n; i < (int)scanGrid.size(); i++ ) |
||||||
|
{ |
||||||
|
double o = 0.0; |
||||||
|
if( (o = overlap(scanGrid[i], bBox)) <= overlaps[0] ) |
||||||
|
continue; |
||||||
|
int j = 0; |
||||||
|
while( j < n && overlaps[j] < o ) |
||||||
|
j++; |
||||||
|
j--; |
||||||
|
for( int k = 0; k < j; overlaps[k] = overlaps[k + 1], res[k] = res[k + 1], k++ ); |
||||||
|
overlaps[j] = o; res[j] = scanGrid[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
double variance(const Mat& img) |
||||||
|
{ |
||||||
|
double p = 0, p2 = 0; |
||||||
|
for( int i = 0; i < img.rows; i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < img.cols; j++ ) |
||||||
|
{ |
||||||
|
p += img.at<uchar>(i, j); |
||||||
|
p2 += img.at<uchar>(i, j) * img.at<uchar>(i, j); |
||||||
|
} |
||||||
|
} |
||||||
|
p /= (img.cols * img.rows); |
||||||
|
p2 /= (img.cols * img.rows); |
||||||
|
return p2 - p * p; |
||||||
|
} |
||||||
|
|
||||||
|
double NCC(const Mat_<uchar>& patch1, const Mat_<uchar>& patch2) |
||||||
|
{ |
||||||
|
CV_Assert( patch1.rows == patch2.rows ); |
||||||
|
CV_Assert( patch1.cols == patch2.cols ); |
||||||
|
|
||||||
|
int N = patch1.rows * patch1.cols; |
||||||
|
int s1 = 0, s2 = 0, n1 = 0, n2 = 0, prod = 0; |
||||||
|
for( int i = 0; i < patch1.rows; i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < patch1.cols; j++ ) |
||||||
|
{ |
||||||
|
int p1 = patch1(i, j), p2 = patch2(i, j); |
||||||
|
s1 += p1; s2 += p2; |
||||||
|
n1 += (p1 * p1); n2 += (p2 * p2); |
||||||
|
prod += (p1 * p2); |
||||||
|
} |
||||||
|
} |
||||||
|
double sq1 = sqrt(std::max(0.0, n1 - 1.0 * s1 * s1 / N)), sq2 = sqrt(std::max(0.0, n2 - 1.0 * s2 * s2 / N)); |
||||||
|
double ares = (sq2 == 0) ? sq1 / abs(sq1) : (prod - s1 * s2 / N) / sq1 / sq2; |
||||||
|
return ares; |
||||||
|
} |
||||||
|
int getMedian(const std::vector<int>& values, int size) |
||||||
|
{ |
||||||
|
if( size == -1 ) |
||||||
|
size = (int)values.size(); |
||||||
|
std::vector<int> copy(values.begin(), values.begin() + size); |
||||||
|
std::sort(copy.begin(), copy.end()); |
||||||
|
if( size % 2 == 0 ) |
||||||
|
return (copy[size / 2 - 1] + copy[size / 2]) / 2; |
||||||
|
else |
||||||
|
return copy[(size - 1) / 2]; |
||||||
|
} |
||||||
|
|
||||||
|
double overlap(const Rect2d& r1, const Rect2d& r2) |
||||||
|
{ |
||||||
|
double a1 = r1.area(), a2 = r2.area(), a0 = (r1&r2).area(); |
||||||
|
return a0 / (a1 + a2 - a0); |
||||||
|
} |
||||||
|
|
||||||
|
void resample(const Mat& img, const RotatedRect& r2, Mat_<uchar>& samples) |
||||||
|
{ |
||||||
|
Mat_<float> M(2, 3), R(2, 2), Si(2, 2), s(2, 1), o(2, 1); |
||||||
|
R(0, 0) = (float)cos(r2.angle * CV_PI / 180); R(0, 1) = (float)(-sin(r2.angle * CV_PI / 180)); |
||||||
|
R(1, 0) = (float)sin(r2.angle * CV_PI / 180); R(1, 1) = (float)cos(r2.angle * CV_PI / 180); |
||||||
|
Si(0, 0) = (float)(samples.cols / r2.size.width); Si(0, 1) = 0.0f; |
||||||
|
Si(1, 0) = 0.0f; Si(1, 1) = (float)(samples.rows / r2.size.height); |
||||||
|
s(0, 0) = (float)samples.cols; s(1, 0) = (float)samples.rows; |
||||||
|
o(0, 0) = r2.center.x; o(1, 0) = r2.center.y; |
||||||
|
Mat_<float> A(2, 2), b(2, 1); |
||||||
|
A = Si * R; |
||||||
|
b = s / 2.0 - Si * R * o; |
||||||
|
A.copyTo(M.colRange(Range(0, 2))); |
||||||
|
b.copyTo(M.colRange(Range(2, 3))); |
||||||
|
warpAffine(img, samples, M, samples.size()); |
||||||
|
} |
||||||
|
void resample(const Mat& img, const Rect2d& r2, Mat_<uchar>& samples) |
||||||
|
{ |
||||||
|
Mat_<float> M(2, 3); |
||||||
|
M(0, 0) = (float)(samples.cols / r2.width); M(0, 1) = 0.0f; M(0, 2) = (float)(-r2.x * samples.cols / r2.width); |
||||||
|
M(1, 0) = 0.0f; M(1, 1) = (float)(samples.rows / r2.height); M(1, 2) = (float)(-r2.y * samples.rows / r2.height); |
||||||
|
warpAffine(img, samples, M, samples.size()); |
||||||
|
} |
||||||
|
|
||||||
|
//other stuff
|
||||||
|
void TLDEnsembleClassifier::stepPrefSuff(std::vector<Vec4b>& arr, int pos, int len, int gridSize) |
||||||
|
{ |
||||||
|
#if 0 |
||||||
|
int step = len / (gridSize - 1), pref = (len - step * (gridSize - 1)) / 2; |
||||||
|
for( int i = 0; i < (int)(sizeof(x1) / sizeof(x1[0])); i++ ) |
||||||
|
arr[i] = pref + arr[i] * step; |
||||||
|
#else |
||||||
|
int total = len - gridSize; |
||||||
|
int quo = total / (gridSize - 1), rem = total % (gridSize - 1); |
||||||
|
int smallStep = quo, bigStep = quo + 1; |
||||||
|
int bigOnes = rem, smallOnes = gridSize - bigOnes - 1; |
||||||
|
int bigOnes_front = bigOnes / 2, bigOnes_back = bigOnes - bigOnes_front; |
||||||
|
for( int i = 0; i < (int)arr.size(); i++ ) |
||||||
|
{ |
||||||
|
if( arr[i].val[pos] < bigOnes_back ) |
||||||
|
{ |
||||||
|
arr[i].val[pos] = (uchar)(arr[i].val[pos] * bigStep + arr[i].val[pos]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
if( arr[i].val[pos] < (bigOnes_front + smallOnes) ) |
||||||
|
{ |
||||||
|
arr[i].val[pos] = (uchar)(bigOnes_front * bigStep + (arr[i].val[pos] - bigOnes_front) * smallStep + arr[i].val[pos]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
if( arr[i].val[pos] < (bigOnes_front + smallOnes + bigOnes_back) ) |
||||||
|
{ |
||||||
|
arr[i].val[pos] = |
||||||
|
(uchar)(bigOnes_front * bigStep + smallOnes * smallStep +
|
||||||
|
(arr[i].val[pos] - (bigOnes_front + smallOnes)) * bigStep + arr[i].val[pos]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
arr[i].val[pos] = (uchar)(len - 1); |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
void TLDEnsembleClassifier::prepareClassifier(int rowstep) |
||||||
|
{ |
||||||
|
if( lastStep_ != rowstep ) |
||||||
|
{ |
||||||
|
lastStep_ = rowstep; |
||||||
|
for( int i = 0; i < (int)offset.size(); i++ ) |
||||||
|
{ |
||||||
|
offset[i].x = rowstep * measurements[i].val[0] + measurements[i].val[1]; |
||||||
|
offset[i].y = rowstep * measurements[i].val[2] + measurements[i].val[3]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
TLDEnsembleClassifier::TLDEnsembleClassifier(const std::vector<Vec4b>& meas, int beg, int end):lastStep_(-1) |
||||||
|
{ |
||||||
|
int posSize = 1, mpc = end - beg; |
||||||
|
for( int i = 0; i < mpc; i++ ) |
||||||
|
posSize *= 2; |
||||||
|
posAndNeg.assign(posSize, Point2i(0, 0)); |
||||||
|
measurements.assign(meas.begin() + beg, meas.begin() + end); |
||||||
|
offset.assign(mpc, Point2i(0, 0)); |
||||||
|
} |
||||||
|
void TLDEnsembleClassifier::integrate(const Mat_<uchar>& patch, bool isPositive) |
||||||
|
{ |
||||||
|
int position = code(patch.data, (int)patch.step[0]); |
||||||
|
if( isPositive ) |
||||||
|
posAndNeg[position].x++; |
||||||
|
else |
||||||
|
posAndNeg[position].y++; |
||||||
|
} |
||||||
|
double TLDEnsembleClassifier::posteriorProbability(const uchar* data, int rowstep) const |
||||||
|
{ |
||||||
|
int position = code(data, rowstep); |
||||||
|
double posNum = (double)posAndNeg[position].x, negNum = (double)posAndNeg[position].y; |
||||||
|
if( posNum == 0.0 && negNum == 0.0 ) |
||||||
|
return 0.0; |
||||||
|
else |
||||||
|
return posNum / (posNum + negNum); |
||||||
|
} |
||||||
|
double TLDEnsembleClassifier::posteriorProbabilityFast(const uchar* data) const |
||||||
|
{ |
||||||
|
int position = codeFast(data); |
||||||
|
double posNum = (double)posAndNeg[position].x, negNum = (double)posAndNeg[position].y; |
||||||
|
if( posNum == 0.0 && negNum == 0.0 ) |
||||||
|
return 0.0; |
||||||
|
else |
||||||
|
return posNum / (posNum + negNum); |
||||||
|
} |
||||||
|
int TLDEnsembleClassifier::codeFast(const uchar* data) const |
||||||
|
{ |
||||||
|
int position = 0; |
||||||
|
for( int i = 0; i < (int)measurements.size(); i++ ) |
||||||
|
{ |
||||||
|
position = position << 1; |
||||||
|
if( data[offset[i].x] < data[offset[i].y] ) |
||||||
|
position++; |
||||||
|
} |
||||||
|
return position; |
||||||
|
} |
||||||
|
int TLDEnsembleClassifier::code(const uchar* data, int rowstep) const |
||||||
|
{ |
||||||
|
int position = 0; |
||||||
|
for( int i = 0; i < (int)measurements.size(); i++ ) |
||||||
|
{ |
||||||
|
position = position << 1; |
||||||
|
if( *(data + rowstep * measurements[i].val[0] + measurements[i].val[1]) < |
||||||
|
*(data + rowstep * measurements[i].val[2] + measurements[i].val[3]) ) |
||||||
|
{ |
||||||
|
position++; |
||||||
|
} |
||||||
|
} |
||||||
|
return position; |
||||||
|
} |
||||||
|
int TLDEnsembleClassifier::makeClassifiers(Size size, int measurePerClassifier, int gridSize, |
||||||
|
std::vector<TLDEnsembleClassifier>& classifiers) |
||||||
|
{ |
||||||
|
|
||||||
|
std::vector<Vec4b> measurements; |
||||||
|
|
||||||
|
for( int i = 0; i < gridSize; i++ ) |
||||||
|
{ |
||||||
|
for( int j = 0; j < gridSize; j++ ) |
||||||
|
{ |
||||||
|
for( int k = 0; k < j; k++ ) |
||||||
|
{ |
||||||
|
Vec4b m; |
||||||
|
m.val[0] = m.val[2] = (uchar)i; |
||||||
|
m.val[1] = (uchar)j; m.val[3] = (uchar)k; |
||||||
|
measurements.push_back(m); |
||||||
|
m.val[1] = m.val[3] = (uchar)i; |
||||||
|
m.val[0] = (uchar)j; m.val[2] = (uchar)k; |
||||||
|
measurements.push_back(m); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
random_shuffle(measurements.begin(), measurements.end()); |
||||||
|
|
||||||
|
stepPrefSuff(measurements, 0, size.width, gridSize); |
||||||
|
stepPrefSuff(measurements, 1, size.width, gridSize); |
||||||
|
stepPrefSuff(measurements, 2, size.height, gridSize); |
||||||
|
stepPrefSuff(measurements, 3, size.height, gridSize); |
||||||
|
|
||||||
|
for( int i = 0, howMany = (int)measurements.size() / measurePerClassifier; i < howMany; i++ ) |
||||||
|
classifiers.push_back(TLDEnsembleClassifier(measurements, i * measurePerClassifier, (i + 1) * measurePerClassifier)); |
||||||
|
return (int)classifiers.size(); |
||||||
|
} |
||||||
|
|
||||||
|
}} |
@ -1,880 +0,0 @@ |
|||||||
/*///////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// 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) 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:
|
|
||||||
//
|
|
||||||
// * 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 "opencv2/video/tracking.hpp" |
|
||||||
#include "opencv2/imgproc.hpp" |
|
||||||
#include "time.h" |
|
||||||
#include <algorithm> |
|
||||||
#include <limits.h> |
|
||||||
#include "TLD.hpp" |
|
||||||
#include "opencv2/highgui.hpp" |
|
||||||
|
|
||||||
#define THETA_NN 0.5 |
|
||||||
#define CORE_THRESHOLD 0.5 |
|
||||||
#define NEG_EXAMPLES_IN_INIT_MODEL 300 |
|
||||||
#define MAX_EXAMPLES_IN_MODEL 500 |
|
||||||
#define MEASURES_PER_CLASSIFIER 13 |
|
||||||
#undef BLUR_AS_VADIM |
|
||||||
#undef CLOSED_LOOP |
|
||||||
static const cv::Size GaussBlurKernelSize(3,3); |
|
||||||
|
|
||||||
using namespace cv; |
|
||||||
using namespace tld; |
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME(optimize): |
|
||||||
* no median |
|
||||||
* direct formula in resamples |
|
||||||
* FIXME(issues) |
|
||||||
* THETA_NN 0.5<->0.6 dramatic change vs video 6 !! |
|
||||||
* FIXME(features) |
|
||||||
* benchmark: save photos --> two streams of photos --> better video |
|
||||||
* TODO: |
|
||||||
* schoolPC: codec, libopencv-dev |
|
||||||
* fix pushbot ->pick commits -> compare_branches->all in 1->resubmit |
|
||||||
* ||video(0.5<->0.6) --> debug if box size is less than 20 --> (remove ensemble self-loop) --> (try inter_area for resize) |
|
||||||
* perfect PN |
|
||||||
* |
|
||||||
* vadim: |
|
||||||
* |
|
||||||
* blurred in TrackerTLDModel() |
|
||||||
* |
|
||||||
* warpAffine -- ? |
|
||||||
*/ |
|
||||||
|
|
||||||
/* design decisions:
|
|
||||||
* blur --> resize (vs. resize-->blur) in detect(), ensembleClassifier stage |
|
||||||
* no random gauss noise, when making examples for ensembleClassifier |
|
||||||
*/ |
|
||||||
|
|
||||||
namespace cv |
|
||||||
{ |
|
||||||
|
|
||||||
class TLDDetector; |
|
||||||
class MyMouseCallbackDEBUG{ |
|
||||||
public: |
|
||||||
MyMouseCallbackDEBUG( Mat& img, Mat& imgBlurred,TLDDetector* detector):img_(img),imgBlurred_(imgBlurred),detector_(detector){} |
|
||||||
static void onMouse( int event, int x, int y, int, void* obj){ |
|
||||||
((MyMouseCallbackDEBUG*)obj)->onMouse(event,x,y); |
|
||||||
} |
|
||||||
MyMouseCallbackDEBUG& operator= (const MyMouseCallbackDEBUG& /*other*/){return *this;} |
|
||||||
private: |
|
||||||
void onMouse( int event, int x, int y); |
|
||||||
Mat& img_,imgBlurred_; |
|
||||||
TLDDetector* detector_; |
|
||||||
}; |
|
||||||
|
|
||||||
class Data { |
|
||||||
public: |
|
||||||
Data(Rect2d initBox); |
|
||||||
Size getMinSize(){return minSize;} |
|
||||||
double getScale(){return scale;} |
|
||||||
bool confident; |
|
||||||
bool failedLastTime; |
|
||||||
int frameNum; |
|
||||||
void printme(FILE* port=stdout); |
|
||||||
private: |
|
||||||
double scale; |
|
||||||
Size minSize; |
|
||||||
}; |
|
||||||
|
|
||||||
class TrackerTLDModel; |
|
||||||
|
|
||||||
class TLDDetector { |
|
||||||
public: |
|
||||||
TLDDetector(const TrackerTLD::Params& params,Ptr<TrackerModel>model_in):model(model_in),params_(params){} |
|
||||||
~TLDDetector(){} |
|
||||||
static void generateScanGrid(int rows,int cols,Size initBox,std::vector<Rect2d>& res,bool withScaling=false); |
|
||||||
bool detect(const Mat& img,const Mat& imgBlurred,Rect2d& res,std::vector<Rect2d>& rect,std::vector<bool>& isObject, |
|
||||||
std::vector<bool>& shouldBeIntegrated); |
|
||||||
protected: |
|
||||||
friend class MyMouseCallbackDEBUG; |
|
||||||
Ptr<TrackerModel> model; |
|
||||||
void computeIntegralImages(const Mat& img,Mat_<double>& intImgP,Mat_<double>& intImgP2){integral(img,intImgP,intImgP2,CV_64F);} |
|
||||||
inline bool patchVariance(Mat_<double>& intImgP,Mat_<double>& intImgP2,double originalVariance,Point pt,Size size); |
|
||||||
bool ensembleClassifier(const uchar* data,int rowstep){return ensembleClassifierNum(data,rowstep)>0.5;} |
|
||||||
double ensembleClassifierNum(const uchar* data,int rowstep); |
|
||||||
TrackerTLD::Params params_; |
|
||||||
}; |
|
||||||
|
|
||||||
class Pexpert{ |
|
||||||
public: |
|
||||||
Pexpert(const Mat& img,const Mat& imgBlurred,Rect2d& resultBox,const TLDDetector* detector,TrackerTLD::Params params,Size initSize): |
|
||||||
img_(img),imgBlurred_(imgBlurred),resultBox_(resultBox),detector_(detector),params_(params),initSize_(initSize){} |
|
||||||
bool operator()(Rect2d /*box*/){return false;} |
|
||||||
int additionalExamples(std::vector<Mat_<uchar> >& examplesForModel,std::vector<Mat_<uchar> >& examplesForEnsemble); |
|
||||||
protected: |
|
||||||
Mat img_,imgBlurred_; |
|
||||||
Rect2d resultBox_; |
|
||||||
const TLDDetector* detector_; |
|
||||||
TrackerTLD::Params params_; |
|
||||||
RNG rng; |
|
||||||
Size initSize_; |
|
||||||
}; |
|
||||||
|
|
||||||
class Nexpert{ |
|
||||||
public: |
|
||||||
Nexpert(const Mat& img,Rect2d& resultBox,const TLDDetector* detector,TrackerTLD::Params params):img_(img),resultBox_(resultBox), |
|
||||||
detector_(detector),params_(params){} |
|
||||||
bool operator()(Rect2d box); |
|
||||||
int additionalExamples(std::vector<Mat_<uchar> >& examplesForModel,std::vector<Mat_<uchar> >& examplesForEnsemble){ |
|
||||||
examplesForModel.clear();examplesForEnsemble.clear();return 0;} |
|
||||||
protected: |
|
||||||
Mat img_; |
|
||||||
Rect2d resultBox_; |
|
||||||
const TLDDetector* detector_; |
|
||||||
TrackerTLD::Params params_; |
|
||||||
}; |
|
||||||
|
|
||||||
template <class T,class Tparams> |
|
||||||
class TrackerProxyImpl : public TrackerProxy{ |
|
||||||
public: |
|
||||||
TrackerProxyImpl(Tparams params=Tparams()):params_(params){} |
|
||||||
bool init( const Mat& image, const Rect2d& boundingBox ){ |
|
||||||
trackerPtr=T::createTracker(); |
|
||||||
return trackerPtr->init(image,boundingBox); |
|
||||||
} |
|
||||||
bool update( const Mat& image,Rect2d& boundingBox){ |
|
||||||
return trackerPtr->update(image,boundingBox); |
|
||||||
} |
|
||||||
private: |
|
||||||
Ptr<T> trackerPtr; |
|
||||||
Tparams params_; |
|
||||||
Rect2d boundingBox_; |
|
||||||
}; |
|
||||||
|
|
||||||
class TrackerTLDModel : public TrackerModel{ |
|
||||||
public: |
|
||||||
TrackerTLDModel(TrackerTLD::Params params,const Mat& image, const Rect2d& boundingBox,Size minSize); |
|
||||||
Rect2d getBoundingBox(){return boundingBox_;} |
|
||||||
void setBoudingBox(Rect2d boundingBox){boundingBox_=boundingBox;} |
|
||||||
double getOriginalVariance(){return originalVariance_;} |
|
||||||
std::vector<TLDEnsembleClassifier>* getClassifiers(){return &classifiers;} |
|
||||||
double Sr(const Mat_<uchar> patch); |
|
||||||
double Sc(const Mat_<uchar> patch); |
|
||||||
void integrateRelabeled(Mat& img,Mat& imgBlurred,const std::vector<Rect2d>& box,const std::vector<bool>& isPositive, |
|
||||||
const std::vector<bool>& alsoIntoModel); |
|
||||||
void integrateAdditional(const std::vector<Mat_<uchar> >& eForModel,const std::vector<Mat_<uchar> >& eForEnsemble,bool isPositive); |
|
||||||
Size getMinSize(){return minSize_;} |
|
||||||
void printme(FILE* port=stdout); |
|
||||||
protected: |
|
||||||
Size minSize_; |
|
||||||
unsigned int timeStampPositiveNext,timeStampNegativeNext; |
|
||||||
TrackerTLD::Params params_; |
|
||||||
void pushIntoModel(const Mat_<uchar>& example,bool positive); |
|
||||||
void modelEstimationImpl( const std::vector<Mat>& /*responses*/ ){} |
|
||||||
void modelUpdateImpl(){} |
|
||||||
Rect2d boundingBox_; |
|
||||||
double originalVariance_; |
|
||||||
std::vector<Mat_<uchar> > positiveExamples,negativeExamples; |
|
||||||
std::vector<unsigned int> timeStampsPositive,timeStampsNegative; |
|
||||||
RNG rng; |
|
||||||
std::vector<TLDEnsembleClassifier> classifiers; |
|
||||||
}; |
|
||||||
|
|
||||||
class TrackerTLDImpl : public TrackerTLD |
|
||||||
{ |
|
||||||
public: |
|
||||||
TrackerTLDImpl( const TrackerTLD::Params ¶meters = TrackerTLD::Params() ); |
|
||||||
void read( const FileNode& fn ); |
|
||||||
void write( FileStorage& fs ) const; |
|
||||||
|
|
||||||
protected: |
|
||||||
|
|
||||||
bool initImpl( const Mat& image, const Rect2d& boundingBox ); |
|
||||||
bool updateImpl( const Mat& image, Rect2d& boundingBox ); |
|
||||||
|
|
||||||
TrackerTLD::Params params; |
|
||||||
Ptr<Data> data; |
|
||||||
Ptr<TrackerProxy> trackerProxy; |
|
||||||
Ptr<TLDDetector> detector; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
TrackerTLD::Params::Params(){ |
|
||||||
} |
|
||||||
|
|
||||||
void TrackerTLD::Params::read( const cv::FileNode& /*fn*/ ){ |
|
||||||
} |
|
||||||
|
|
||||||
void TrackerTLD::Params::write( cv::FileStorage& /*fs*/ ) const{ |
|
||||||
} |
|
||||||
|
|
||||||
Ptr<TrackerTLD> TrackerTLD::createTracker(const TrackerTLD::Params ¶meters){ |
|
||||||
return Ptr<TrackerTLDImpl>(new TrackerTLDImpl(parameters)); |
|
||||||
} |
|
||||||
|
|
||||||
TrackerTLDImpl::TrackerTLDImpl( const TrackerTLD::Params ¶meters) : |
|
||||||
params( parameters ){ |
|
||||||
isInit = false; |
|
||||||
trackerProxy=Ptr<TrackerProxyImpl<TrackerMedianFlow,TrackerMedianFlow::Params> >( |
|
||||||
new TrackerProxyImpl<TrackerMedianFlow,TrackerMedianFlow::Params>()); |
|
||||||
} |
|
||||||
|
|
||||||
void TrackerTLDImpl::read( const cv::FileNode& fn ) |
|
||||||
{ |
|
||||||
params.read( fn ); |
|
||||||
} |
|
||||||
|
|
||||||
void TrackerTLDImpl::write( cv::FileStorage& fs ) const |
|
||||||
{ |
|
||||||
params.write( fs ); |
|
||||||
} |
|
||||||
|
|
||||||
bool TrackerTLDImpl::initImpl(const Mat& image, const Rect2d& boundingBox ){ |
|
||||||
Mat image_gray; |
|
||||||
trackerProxy->init(image,boundingBox); |
|
||||||
cvtColor( image, image_gray, COLOR_BGR2GRAY ); |
|
||||||
data=Ptr<Data>(new Data(boundingBox)); |
|
||||||
double scale=data->getScale(); |
|
||||||
Rect2d myBoundingBox=boundingBox; |
|
||||||
if(scale>1.0){ |
|
||||||
Mat image_proxy; |
|
||||||
resize(image_gray,image_proxy,Size(cvRound(image.cols*scale),cvRound(image.rows*scale))); |
|
||||||
image_proxy.copyTo(image_gray); |
|
||||||
myBoundingBox.x*=scale; |
|
||||||
myBoundingBox.y*=scale; |
|
||||||
myBoundingBox.width*=scale; |
|
||||||
myBoundingBox.height*=scale; |
|
||||||
} |
|
||||||
model=Ptr<TrackerTLDModel>(new TrackerTLDModel(params,image_gray,myBoundingBox,data->getMinSize())); |
|
||||||
detector=Ptr<TLDDetector>(new TLDDetector(params,model)); |
|
||||||
data->confident=false; |
|
||||||
data->failedLastTime=false; |
|
||||||
|
|
||||||
#if !1 |
|
||||||
dprintf(("here I am\n")); |
|
||||||
Mat image_blurred; |
|
||||||
GaussianBlur(image_gray,image_blurred,GaussBlurKernelSize,0.0); |
|
||||||
MyMouseCallbackDEBUG* callback=new MyMouseCallbackDEBUG(image_gray,image_blurred,detector); |
|
||||||
imshow("picker",image_gray); |
|
||||||
setMouseCallback( "picker", MyMouseCallbackDEBUG::onMouse, (void*)callback); |
|
||||||
waitKey(); |
|
||||||
#endif |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool TrackerTLDImpl::updateImpl(const Mat& image, Rect2d& boundingBox){ |
|
||||||
Mat image_gray,image_blurred,imageForDetector; |
|
||||||
cvtColor( image, image_gray, COLOR_BGR2GRAY ); |
|
||||||
double scale=data->getScale(); |
|
||||||
if(scale>1.0){ |
|
||||||
resize(image_gray,imageForDetector,Size(cvRound(image.cols*scale),cvRound(image.rows*scale))); |
|
||||||
}else{ |
|
||||||
imageForDetector=image_gray; |
|
||||||
} |
|
||||||
GaussianBlur(imageForDetector,image_blurred,GaussBlurKernelSize,0.0); |
|
||||||
TrackerTLDModel* tldModel=((TrackerTLDModel*)static_cast<TrackerModel*>(model)); |
|
||||||
data->frameNum++; |
|
||||||
Mat_<uchar> standardPatch(15,15); |
|
||||||
std::vector<Rect2d> detectorResults; |
|
||||||
std::vector<bool> isObject,shouldBeIntegrated; |
|
||||||
//best overlap around 92%
|
|
||||||
|
|
||||||
Rect2d tmpCandid=boundingBox; |
|
||||||
std::vector<Rect2d> candidates; |
|
||||||
std::vector<double> candidatesRes; |
|
||||||
bool trackerNeedsReInit=false; |
|
||||||
for(int i=0;i<2;i++){ |
|
||||||
if(((i==0)&&!(data->failedLastTime)&&trackerProxy->update(image,tmpCandid)) ||
|
|
||||||
((i==1)&&(detector->detect(imageForDetector,image_blurred,tmpCandid,detectorResults,isObject,shouldBeIntegrated)))){ |
|
||||||
candidates.push_back(tmpCandid); |
|
||||||
if(i==0){ |
|
||||||
resample(image_gray,tmpCandid,standardPatch); |
|
||||||
}else{ |
|
||||||
resample(imageForDetector,tmpCandid,standardPatch); |
|
||||||
} |
|
||||||
candidatesRes.push_back(tldModel->Sc(standardPatch)); |
|
||||||
}else{ |
|
||||||
if(i==0){ |
|
||||||
trackerNeedsReInit=true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
std::vector<double>::iterator it=std::max_element(candidatesRes.begin(),candidatesRes.end()); |
|
||||||
|
|
||||||
dfprintf((stdout,"scale=%f\n",log(1.0*boundingBox.width/(data->getMinSize()).width)/log(1.2))); |
|
||||||
for(int i=0;i<(int)candidatesRes.size();i++){ |
|
||||||
dprintf(("\tcandidatesRes[%d]=%f\n",i,candidatesRes[i])); |
|
||||||
} |
|
||||||
data->printme(); |
|
||||||
tldModel->printme(stdout); |
|
||||||
#if !1 |
|
||||||
if(data->frameNum==82){ |
|
||||||
dprintf(("here I am\n")); |
|
||||||
MyMouseCallbackDEBUG* callback=new MyMouseCallbackDEBUG(imageForDetector,image_blurred,detector); |
|
||||||
imshow("picker",imageForDetector); |
|
||||||
setMouseCallback( "picker", MyMouseCallbackDEBUG::onMouse, (void*)callback); |
|
||||||
waitKey(); |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
if(it==candidatesRes.end()){ |
|
||||||
data->confident=false; |
|
||||||
data->failedLastTime=true; |
|
||||||
return false; |
|
||||||
}else{ |
|
||||||
boundingBox=candidates[it-candidatesRes.begin()]; |
|
||||||
data->failedLastTime=false; |
|
||||||
if(trackerNeedsReInit || it!=candidatesRes.begin()){ |
|
||||||
trackerProxy->init(image,boundingBox); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if(!false && it!=candidatesRes.end()){ |
|
||||||
resample(imageForDetector,candidates[it-candidatesRes.begin()],standardPatch); |
|
||||||
dfprintf((stderr,"%d %f %f\n",data->frameNum,tldModel->Sc(standardPatch),tldModel->Sr(standardPatch))); |
|
||||||
if(candidatesRes.size()==2 && it==(candidatesRes.begin()+1)) |
|
||||||
dfprintf((stderr,"detector WON\n")); |
|
||||||
}else{ |
|
||||||
dfprintf((stderr,"%d x x\n",data->frameNum)); |
|
||||||
} |
|
||||||
|
|
||||||
if(*it > CORE_THRESHOLD){ |
|
||||||
data->confident=true; |
|
||||||
} |
|
||||||
|
|
||||||
if(data->confident){ |
|
||||||
Pexpert pExpert(imageForDetector,image_blurred,boundingBox,detector,params,data->getMinSize()); |
|
||||||
Nexpert nExpert(imageForDetector,boundingBox,detector,params); |
|
||||||
bool expertResult; |
|
||||||
std::vector<Mat_<uchar> > examplesForModel,examplesForEnsemble; |
|
||||||
examplesForModel.reserve(100);examplesForEnsemble.reserve(100); |
|
||||||
int negRelabeled=0; |
|
||||||
for(int i=0;i<(int)detectorResults.size();i++){ |
|
||||||
if(isObject[i]){ |
|
||||||
expertResult=nExpert(detectorResults[i]); |
|
||||||
if(expertResult!=isObject[i]){negRelabeled++;} |
|
||||||
}else{ |
|
||||||
expertResult=pExpert(detectorResults[i]); |
|
||||||
} |
|
||||||
|
|
||||||
shouldBeIntegrated[i]=shouldBeIntegrated[i] || (isObject[i]!=expertResult); |
|
||||||
isObject[i]=expertResult; |
|
||||||
} |
|
||||||
tldModel->integrateRelabeled(imageForDetector,image_blurred,detectorResults,isObject,shouldBeIntegrated); |
|
||||||
dprintf(("%d relabeled by nExpert\n",negRelabeled)); |
|
||||||
pExpert.additionalExamples(examplesForModel,examplesForEnsemble); |
|
||||||
tldModel->integrateAdditional(examplesForModel,examplesForEnsemble,true); |
|
||||||
examplesForModel.clear();examplesForEnsemble.clear(); |
|
||||||
nExpert.additionalExamples(examplesForModel,examplesForEnsemble); |
|
||||||
tldModel->integrateAdditional(examplesForModel,examplesForEnsemble,false); |
|
||||||
}else{ |
|
||||||
#ifdef CLOSED_LOOP |
|
||||||
tldModel->integrateRelabeled(imageForDetector,image_blurred,detectorResults,isObject,shouldBeIntegrated); |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
TrackerTLDModel::TrackerTLDModel(TrackerTLD::Params params,const Mat& image, const Rect2d& boundingBox,Size minSize):minSize_(minSize), |
|
||||||
timeStampPositiveNext(0),timeStampNegativeNext(0),params_(params){ |
|
||||||
boundingBox_=boundingBox; |
|
||||||
originalVariance_=variance(image(boundingBox)); |
|
||||||
std::vector<Rect2d> closest(10),scanGrid; |
|
||||||
Mat scaledImg,blurredImg,image_blurred; |
|
||||||
|
|
||||||
double scale=scaleAndBlur(image,cvRound(log(1.0*boundingBox.width/(minSize.width))/log(1.2)),scaledImg,blurredImg,GaussBlurKernelSize); |
|
||||||
GaussianBlur(image,image_blurred,GaussBlurKernelSize,0.0); |
|
||||||
TLDDetector::generateScanGrid(image.rows,image.cols,minSize,scanGrid); |
|
||||||
getClosestN(scanGrid,Rect2d(boundingBox.x/scale,boundingBox.y/scale,boundingBox.width/scale,boundingBox.height/scale),10,closest); |
|
||||||
|
|
||||||
Mat_<uchar> blurredPatch(minSize); |
|
||||||
for(int i=0,howMany=TLDEnsembleClassifier::getMaxOrdinal();i<howMany;i++){ |
|
||||||
classifiers.push_back(TLDEnsembleClassifier(i+1,minSize,MEASURES_PER_CLASSIFIER)); |
|
||||||
} |
|
||||||
|
|
||||||
positiveExamples.reserve(200); |
|
||||||
Point2f center; |
|
||||||
Size2f size; |
|
||||||
for(int i=0;i<(int)closest.size();i++){ |
|
||||||
for(int j=0;j<20;j++){ |
|
||||||
Mat_<uchar> standardPatch(15,15); |
|
||||||
center.x=(float)(closest[i].x+closest[i].width*(0.5+rng.uniform(-0.01,0.01))); |
|
||||||
center.y=(float)(closest[i].y+closest[i].height*(0.5+rng.uniform(-0.01,0.01))); |
|
||||||
size.width=(float)(closest[i].width*rng.uniform((double)0.99,(double)1.01)); |
|
||||||
size.height=(float)(closest[i].height*rng.uniform((double)0.99,(double)1.01)); |
|
||||||
float angle=(float)rng.uniform(-10.0,10.0); |
|
||||||
|
|
||||||
resample(scaledImg,RotatedRect(center,size,angle),standardPatch); |
|
||||||
|
|
||||||
for(int y=0;y<standardPatch.rows;y++){ |
|
||||||
for(int x=0;x<standardPatch.cols;x++){ |
|
||||||
standardPatch(x,y)+=(uchar)rng.gaussian(5.0); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#ifdef BLUR_AS_VADIM |
|
||||||
GaussianBlur(standardPatch,blurredPatch,GaussBlurKernelSize,0.0); |
|
||||||
#else |
|
||||||
resample(blurredImg,RotatedRect(center,size,angle),blurredPatch); |
|
||||||
#endif |
|
||||||
pushIntoModel(standardPatch,true); |
|
||||||
for(int k=0;k<(int)classifiers.size();k++){ |
|
||||||
classifiers[k].integrate(blurredPatch,true); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
TLDDetector::generateScanGrid(image.rows,image.cols,minSize,scanGrid,true); |
|
||||||
negativeExamples.clear(); |
|
||||||
negativeExamples.reserve(NEG_EXAMPLES_IN_INIT_MODEL); |
|
||||||
std::vector<int> indices; |
|
||||||
indices.reserve(NEG_EXAMPLES_IN_INIT_MODEL); |
|
||||||
while(negativeExamples.size()<NEG_EXAMPLES_IN_INIT_MODEL){ |
|
||||||
int i=rng.uniform((int)0,(int)scanGrid.size()); |
|
||||||
if(std::find(indices.begin(),indices.end(),i)==indices.end() && overlap(boundingBox,scanGrid[i])<0.2){ |
|
||||||
Mat_<uchar> standardPatch(15,15); |
|
||||||
resample(image,scanGrid[i],standardPatch); |
|
||||||
pushIntoModel(standardPatch,false); |
|
||||||
|
|
||||||
resample(image_blurred,scanGrid[i],blurredPatch); |
|
||||||
for(int k=0;k<(int)classifiers.size();k++){ |
|
||||||
classifiers[k].integrate(blurredPatch,false); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
dprintf(("positive patches: %d\nnegative patches: %d\n",(int)positiveExamples.size(),(int)negativeExamples.size())); |
|
||||||
} |
|
||||||
|
|
||||||
void TLDDetector::generateScanGrid(int rows,int cols,Size initBox,std::vector<Rect2d>& res,bool withScaling){ |
|
||||||
res.clear(); |
|
||||||
//scales step: 1.2; hor step: 10% of width; verstep: 10% of height; minsize: 20pix
|
|
||||||
for(double h=initBox.height, w=initBox.width;h<cols && w<rows;){ |
|
||||||
for(double x=0;(x+w)<=(cols-1.0);x+=(0.1*w)){ |
|
||||||
for(double y=0;(y+h)<=(rows-1.0);y+=(0.1*h)){ |
|
||||||
res.push_back(Rect2d(x,y,w,h)); |
|
||||||
} |
|
||||||
} |
|
||||||
if(withScaling){ |
|
||||||
if(h<=initBox.height){ |
|
||||||
h/=1.2; w/=1.2; |
|
||||||
if(h<20 || w<20){ |
|
||||||
h=initBox.height*1.2; w=initBox.width*1.2; |
|
||||||
CV_Assert(h>initBox.height || w>initBox.width); |
|
||||||
} |
|
||||||
}else{ |
|
||||||
h*=1.2; w*=1.2; |
|
||||||
} |
|
||||||
}else{ |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
dprintf(("%d rects in res\n",(int)res.size())); |
|
||||||
} |
|
||||||
|
|
||||||
bool TLDDetector::detect(const Mat& img,const Mat& imgBlurred,Rect2d& res,std::vector<Rect2d>& rect,std::vector<bool>& isObject, |
|
||||||
std::vector<bool>& shouldBeIntegrated){ |
|
||||||
TrackerTLDModel* tldModel=((TrackerTLDModel*)static_cast<TrackerModel*>(model)); |
|
||||||
Size initSize=tldModel->getMinSize(); |
|
||||||
rect.clear(); |
|
||||||
isObject.clear(); |
|
||||||
shouldBeIntegrated.clear(); |
|
||||||
|
|
||||||
Mat resized_img,blurred_img; |
|
||||||
Mat_<uchar> standardPatch(15,15); |
|
||||||
img.copyTo(resized_img); |
|
||||||
imgBlurred.copyTo(blurred_img); |
|
||||||
double originalVariance=tldModel->getOriginalVariance();; |
|
||||||
int dx=initSize.width/10,dy=initSize.height/10; |
|
||||||
Size2d size=img.size(); |
|
||||||
double scale=1.0; |
|
||||||
int total=0,pass=0; |
|
||||||
int npos=0,nneg=0; |
|
||||||
double tmp=0,maxSc=-5.0; |
|
||||||
Rect2d maxScRect; |
|
||||||
START_TICK("detector"); |
|
||||||
do{ |
|
||||||
Mat_<double> intImgP,intImgP2; |
|
||||||
computeIntegralImages(resized_img,intImgP,intImgP2); |
|
||||||
|
|
||||||
for(int i=0,imax=cvFloor((0.0+resized_img.cols-initSize.width)/dx);i<imax;i++){ |
|
||||||
for(int j=0,jmax=cvFloor((0.0+resized_img.rows-initSize.height)/dy);j<jmax;j++){ |
|
||||||
total++; |
|
||||||
if(!patchVariance(intImgP,intImgP2,originalVariance,Point(dx*i,dy*j),initSize)){ |
|
||||||
continue; |
|
||||||
} |
|
||||||
if(!ensembleClassifier(&blurred_img.at<uchar>(dy*j,dx*i),(int)blurred_img.step[0])){ |
|
||||||
continue; |
|
||||||
} |
|
||||||
pass++; |
|
||||||
|
|
||||||
rect.push_back(Rect2d(dx*i*scale,dy*j*scale,initSize.width*scale,initSize.height*scale)); |
|
||||||
resample(resized_img,Rect2d(Point(dx*i,dy*j),initSize),standardPatch); |
|
||||||
tmp=tldModel->Sr(standardPatch); |
|
||||||
isObject.push_back(tmp>THETA_NN); |
|
||||||
shouldBeIntegrated.push_back(abs(tmp-THETA_NN)<0.1); |
|
||||||
if(!isObject[isObject.size()-1]){ |
|
||||||
nneg++; |
|
||||||
continue; |
|
||||||
}else{ |
|
||||||
npos++; |
|
||||||
} |
|
||||||
tmp=tldModel->Sc(standardPatch); |
|
||||||
if(tmp>maxSc){ |
|
||||||
maxSc=tmp; |
|
||||||
maxScRect=rect[rect.size()-1]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
size.width/=1.2; |
|
||||||
size.height/=1.2; |
|
||||||
scale*=1.2; |
|
||||||
resize(img,resized_img,size); |
|
||||||
GaussianBlur(resized_img,blurred_img,GaussBlurKernelSize,0.0f); |
|
||||||
}while(size.width>=initSize.width && size.height>=initSize.height); |
|
||||||
END_TICK("detector"); |
|
||||||
|
|
||||||
dfprintf((stdout,"after NCC: nneg=%d npos=%d\n",nneg,npos)); |
|
||||||
#if !0 |
|
||||||
std::vector<Rect2d> poss,negs; |
|
||||||
for(int i=0;i<(int)rect.size();i++){ |
|
||||||
if(isObject[i]) |
|
||||||
poss.push_back(rect[i]); |
|
||||||
else |
|
||||||
negs.push_back(rect[i]); |
|
||||||
} |
|
||||||
dfprintf((stdout,"%d pos and %d neg\n",(int)poss.size(),(int)negs.size())); |
|
||||||
drawWithRects(img,negs,poss); |
|
||||||
#endif |
|
||||||
#if !1 |
|
||||||
std::vector<Rect2d> scanGrid; |
|
||||||
generateScanGrid(img.rows,img.cols,initSize,scanGrid); |
|
||||||
std::vector<double> results; |
|
||||||
Mat_<uchar> standardPatch_inner(15,15); |
|
||||||
for(int i=0;i<(int)scanGrid.size();i++){ |
|
||||||
resample(img,scanGrid[i],standardPatch_inner); |
|
||||||
results.push_back(tldModel->Sr(standardPatch_inner)); |
|
||||||
} |
|
||||||
std::vector<double>::iterator it=std::max_element(results.begin(),results.end()); |
|
||||||
Mat image; |
|
||||||
img.copyTo(image); |
|
||||||
rectangle( image,scanGrid[it-results.begin()], 255, 1, 1 ); |
|
||||||
imshow("img",image); |
|
||||||
waitKey(); |
|
||||||
#endif |
|
||||||
#if !1 |
|
||||||
Mat image; |
|
||||||
img.copyTo(image); |
|
||||||
rectangle( image,res, 255, 1, 1 ); |
|
||||||
for(int i=0;i<(int)rect.size();i++){ |
|
||||||
rectangle( image,rect[i], 0, 1, 1 ); |
|
||||||
} |
|
||||||
imshow("img",image); |
|
||||||
waitKey(); |
|
||||||
#endif |
|
||||||
|
|
||||||
dfprintf((stdout,"%d after ensemble\n",pass)); |
|
||||||
if(maxSc<0){ |
|
||||||
return false; |
|
||||||
} |
|
||||||
res=maxScRect; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool TLDDetector::patchVariance(Mat_<double>& intImgP,Mat_<double>& intImgP2,double originalVariance,Point pt,Size size){ |
|
||||||
return variance(intImgP,intImgP2,Rect(pt.x,pt.y,size.width,size.height)) >= 0.5*originalVariance; |
|
||||||
} |
|
||||||
|
|
||||||
double TLDDetector::ensembleClassifierNum(const uchar* data,int rowstep){ |
|
||||||
TrackerTLDModel* tldModel=((TrackerTLDModel*)static_cast<TrackerModel*>(model)); |
|
||||||
std::vector<TLDEnsembleClassifier>* classifiers=tldModel->getClassifiers(); |
|
||||||
double p=0; |
|
||||||
for(int k=0;k<(int)classifiers->size();k++){ |
|
||||||
p+=(*classifiers)[k].posteriorProbability(data,rowstep); |
|
||||||
} |
|
||||||
p/=classifiers->size(); |
|
||||||
return p; |
|
||||||
} |
|
||||||
|
|
||||||
double TrackerTLDModel::Sr(const Mat_<uchar> patch){ |
|
||||||
double splus=0.0; |
|
||||||
for(int i=0;i<(int)positiveExamples.size();i++){ |
|
||||||
splus=MAX(splus,0.5*(NCC(positiveExamples[i],patch)+1.0)); |
|
||||||
} |
|
||||||
double sminus=0.0; |
|
||||||
for(int i=0;i<(int)negativeExamples.size();i++){ |
|
||||||
sminus=MAX(sminus,0.5*(NCC(negativeExamples[i],patch)+1.0)); |
|
||||||
} |
|
||||||
if(splus+sminus==0.0){ |
|
||||||
return 0.0; |
|
||||||
} |
|
||||||
return splus/(sminus+splus); |
|
||||||
} |
|
||||||
|
|
||||||
double TrackerTLDModel::Sc(const Mat_<uchar> patch){ |
|
||||||
double splus=0.0; |
|
||||||
int med=getMedian(timeStampsPositive); |
|
||||||
for(int i=0;i<(int)positiveExamples.size();i++){ |
|
||||||
if((int)timeStampsPositive[i]<=med){ |
|
||||||
splus=MAX(splus,0.5*(NCC(positiveExamples[i],patch)+1.0)); |
|
||||||
} |
|
||||||
} |
|
||||||
double sminus=0.0; |
|
||||||
for(int i=0;i<(int)negativeExamples.size();i++){ |
|
||||||
sminus=MAX(sminus,0.5*(NCC(negativeExamples[i],patch)+1.0)); |
|
||||||
} |
|
||||||
if(splus+sminus==0.0){ |
|
||||||
return 0.0; |
|
||||||
} |
|
||||||
return splus/(sminus+splus); |
|
||||||
} |
|
||||||
|
|
||||||
void TrackerTLDModel::integrateRelabeled(Mat& img,Mat& imgBlurred,const std::vector<Rect2d>& box,const std::vector<bool>& isPositive, |
|
||||||
const std::vector<bool>& alsoIntoModel){ |
|
||||||
Mat_<uchar> standardPatch(15,15),blurredPatch(minSize_); |
|
||||||
int positiveIntoModel=0,negativeIntoModel=0,positiveIntoEnsemble=0,negativeIntoEnsemble=0; |
|
||||||
for(int k=0;k<(int)box.size();k++){ |
|
||||||
if(alsoIntoModel[k]){ |
|
||||||
resample(img,box[k],standardPatch); |
|
||||||
if(isPositive[k]){ |
|
||||||
positiveIntoModel++; |
|
||||||
pushIntoModel(standardPatch,true); |
|
||||||
}else{ |
|
||||||
negativeIntoModel++; |
|
||||||
pushIntoModel(standardPatch,false); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#ifdef CLOSED_LOOP |
|
||||||
if(alsoIntoModel[k] || (isPositive[k]==false)){ |
|
||||||
#else |
|
||||||
if(alsoIntoModel[k]){ |
|
||||||
#endif |
|
||||||
resample(imgBlurred,box[k],blurredPatch); |
|
||||||
if(isPositive[k]){ |
|
||||||
positiveIntoEnsemble++; |
|
||||||
}else{ |
|
||||||
negativeIntoEnsemble++; |
|
||||||
} |
|
||||||
for(int i=0;i<(int)classifiers.size();i++){ |
|
||||||
classifiers[i].integrate(blurredPatch,isPositive[k]); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if(negativeIntoModel>0) |
|
||||||
dfprintf((stdout,"negativeIntoModel=%d ",negativeIntoModel)); |
|
||||||
if(positiveIntoModel>0) |
|
||||||
dfprintf((stdout,"positiveIntoModel=%d ",positiveIntoModel)); |
|
||||||
if(negativeIntoEnsemble>0) |
|
||||||
dfprintf((stdout,"negativeIntoEnsemble=%d ",negativeIntoEnsemble)); |
|
||||||
if(positiveIntoEnsemble>0) |
|
||||||
dfprintf((stdout,"positiveIntoEnsemble=%d ",positiveIntoEnsemble)); |
|
||||||
dfprintf((stdout,"\n")); |
|
||||||
} |
|
||||||
|
|
||||||
void TrackerTLDModel::integrateAdditional(const std::vector<Mat_<uchar> >& eForModel,const std::vector<Mat_<uchar> >& eForEnsemble,bool isPositive){ |
|
||||||
int positiveIntoModel=0,negativeIntoModel=0,positiveIntoEnsemble=0,negativeIntoEnsemble=0; |
|
||||||
for(int k=0;k<(int)eForModel.size();k++){ |
|
||||||
double sr=Sr(eForModel[k]); |
|
||||||
if((sr>THETA_NN)!=isPositive){ |
|
||||||
if(isPositive){ |
|
||||||
positiveIntoModel++; |
|
||||||
pushIntoModel(eForModel[k],true); |
|
||||||
}else{ |
|
||||||
negativeIntoModel++; |
|
||||||
pushIntoModel(eForModel[k],false); |
|
||||||
} |
|
||||||
} |
|
||||||
double p=0; |
|
||||||
for(int i=0;i<(int)classifiers.size();i++){ |
|
||||||
p+=classifiers[i].posteriorProbability(eForEnsemble[k].data,(int)eForEnsemble[k].step[0]); |
|
||||||
} |
|
||||||
p/=classifiers.size(); |
|
||||||
if((p>0.5)!=isPositive){ |
|
||||||
if(isPositive){ |
|
||||||
positiveIntoEnsemble++; |
|
||||||
}else{ |
|
||||||
negativeIntoEnsemble++; |
|
||||||
} |
|
||||||
for(int i=0;i<(int)classifiers.size();i++){ |
|
||||||
classifiers[i].integrate(eForEnsemble[k],isPositive); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if(negativeIntoModel>0) |
|
||||||
dfprintf((stdout,"negativeIntoModel=%d ",negativeIntoModel)); |
|
||||||
if(positiveIntoModel>0) |
|
||||||
dfprintf((stdout,"positiveIntoModel=%d ",positiveIntoModel)); |
|
||||||
if(negativeIntoEnsemble>0) |
|
||||||
dfprintf((stdout,"negativeIntoEnsemble=%d ",negativeIntoEnsemble)); |
|
||||||
if(positiveIntoEnsemble>0) |
|
||||||
dfprintf((stdout,"positiveIntoEnsemble=%d ",positiveIntoEnsemble)); |
|
||||||
dfprintf((stdout,"\n")); |
|
||||||
} |
|
||||||
|
|
||||||
int Pexpert::additionalExamples(std::vector<Mat_<uchar> >& examplesForModel,std::vector<Mat_<uchar> >& examplesForEnsemble){ |
|
||||||
examplesForModel.clear();examplesForEnsemble.clear(); |
|
||||||
examplesForModel.reserve(100);examplesForEnsemble.reserve(100); |
|
||||||
|
|
||||||
std::vector<Rect2d> closest(10),scanGrid; |
|
||||||
Mat scaledImg,blurredImg; |
|
||||||
|
|
||||||
double scale=scaleAndBlur(img_,cvRound(log(1.0*resultBox_.width/(initSize_.width))/log(1.2)),scaledImg,blurredImg,GaussBlurKernelSize); |
|
||||||
TLDDetector::generateScanGrid(img_.rows,img_.cols,initSize_,scanGrid); |
|
||||||
getClosestN(scanGrid,Rect2d(resultBox_.x/scale,resultBox_.y/scale,resultBox_.width/scale,resultBox_.height/scale),10,closest); |
|
||||||
|
|
||||||
Point2f center; |
|
||||||
Size2f size; |
|
||||||
for(int i=0;i<(int)closest.size();i++){ |
|
||||||
for(int j=0;j<10;j++){ |
|
||||||
Mat_<uchar> standardPatch(15,15),blurredPatch(initSize_); |
|
||||||
center.x=(float)(closest[i].x+closest[i].width*(0.5+rng.uniform(-0.01,0.01))); |
|
||||||
center.y=(float)(closest[i].y+closest[i].height*(0.5+rng.uniform(-0.01,0.01))); |
|
||||||
size.width=(float)(closest[i].width*rng.uniform((double)0.99,(double)1.01)); |
|
||||||
size.height=(float)(closest[i].height*rng.uniform((double)0.99,(double)1.01)); |
|
||||||
float angle=(float)rng.uniform(-5.0,5.0); |
|
||||||
|
|
||||||
#ifdef BLUR_AS_VADIM |
|
||||||
GaussianBlur(standardPatch,blurredPatch,GaussBlurKernelSize,0.0); |
|
||||||
#else |
|
||||||
resample(blurredImg,RotatedRect(center,size,angle),blurredPatch); |
|
||||||
#endif |
|
||||||
resample(scaledImg,RotatedRect(center,size,angle),standardPatch); |
|
||||||
for(int y=0;y<standardPatch.rows;y++){ |
|
||||||
for(int x=0;x<standardPatch.cols;x++){ |
|
||||||
standardPatch(x,y)+=(uchar)rng.gaussian(5.0); |
|
||||||
} |
|
||||||
} |
|
||||||
examplesForModel.push_back(standardPatch); |
|
||||||
examplesForEnsemble.push_back(blurredPatch); |
|
||||||
} |
|
||||||
} |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
bool Nexpert::operator()(Rect2d box){ |
|
||||||
if(overlap(resultBox_,box)<0.2){ |
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
Data::Data(Rect2d initBox){ |
|
||||||
double minDim=MIN(initBox.width,initBox.height); |
|
||||||
scale = 20.0/minDim; |
|
||||||
minSize.width=(int)(initBox.width*20.0/minDim); |
|
||||||
minSize.height=(int)(initBox.height*20.0/minDim); |
|
||||||
frameNum=0; |
|
||||||
dprintf(("minSize= %dx%d\n",minSize.width,minSize.height)); |
|
||||||
} |
|
||||||
|
|
||||||
void Data::printme(FILE* port){ |
|
||||||
dfprintf((port,"Data:\n")); |
|
||||||
dfprintf((port,"\tframeNum=%d\n",frameNum)); |
|
||||||
dfprintf((port,"\tconfident=%s\n",confident?"true":"false")); |
|
||||||
dfprintf((port,"\tfailedLastTime=%s\n",failedLastTime?"true":"false")); |
|
||||||
dfprintf((port,"\tminSize=%dx%d\n",minSize.width,minSize.height)); |
|
||||||
} |
|
||||||
void TrackerTLDModel::printme(FILE* port){ |
|
||||||
dfprintf((port,"TrackerTLDModel:\n")); |
|
||||||
dfprintf((port,"\tpositiveExamples.size()=%d\n",(int)positiveExamples.size())); |
|
||||||
dfprintf((port,"\tnegativeExamples.size()=%d\n",(int)negativeExamples.size())); |
|
||||||
} |
|
||||||
void MyMouseCallbackDEBUG::onMouse( int event, int x, int y){ |
|
||||||
if(event== EVENT_LBUTTONDOWN){ |
|
||||||
Mat imgCanvas; |
|
||||||
img_.copyTo(imgCanvas); |
|
||||||
TrackerTLDModel* tldModel=((TrackerTLDModel*)static_cast<TrackerModel*>(detector_->model)); |
|
||||||
Size initSize=tldModel->getMinSize(); |
|
||||||
Mat_<uchar> standardPatch(15,15); |
|
||||||
double originalVariance=tldModel->getOriginalVariance();; |
|
||||||
double tmp; |
|
||||||
|
|
||||||
Mat resized_img,blurred_img; |
|
||||||
double scale=1.2; |
|
||||||
//double scale=1.2*1.2*1.2*1.2;
|
|
||||||
Size2d size(img_.cols/scale,img_.rows/scale); |
|
||||||
resize(img_,resized_img,size); |
|
||||||
resize(imgBlurred_,blurred_img,size); |
|
||||||
|
|
||||||
Mat_<double> intImgP,intImgP2; |
|
||||||
detector_->computeIntegralImages(resized_img,intImgP,intImgP2); |
|
||||||
|
|
||||||
int dx=initSize.width/10, dy=initSize.height/10, |
|
||||||
i=(int)(x/scale/dx), j=(int)(y/scale/dy); |
|
||||||
|
|
||||||
dfprintf((stderr,"patchVariance=%s\n",(detector_->patchVariance(intImgP,intImgP2,originalVariance,Point(dx*i,dy*j),initSize))?"true":"false")); |
|
||||||
dfprintf((stderr,"p=%f\n",(detector_->ensembleClassifierNum(&blurred_img.at<uchar>(dy*j,dx*i),(int)blurred_img.step[0])))); |
|
||||||
fprintf(stderr,"ensembleClassifier=%s\n", |
|
||||||
(detector_->ensembleClassifier(&blurred_img.at<uchar>(dy*j,dx*i),(int)blurred_img.step[0]))?"true":"false"); |
|
||||||
|
|
||||||
resample(resized_img,Rect2d(Point(dx*i,dy*j),initSize),standardPatch); |
|
||||||
tmp=tldModel->Sr(standardPatch); |
|
||||||
dfprintf((stderr,"Sr=%f\n",tmp)); |
|
||||||
dfprintf((stderr,"isObject=%s\n",(tmp>THETA_NN)?"true":"false")); |
|
||||||
dfprintf((stderr,"shouldBeIntegrated=%s\n",(abs(tmp-THETA_NN)<0.1)?"true":"false")); |
|
||||||
dfprintf((stderr,"Sc=%f\n",tldModel->Sc(standardPatch))); |
|
||||||
|
|
||||||
rectangle(imgCanvas,Rect2d(Point2d(scale*dx*i,scale*dy*j),Size2d(initSize.width*scale,initSize.height*scale)), 0, 2, 1 ); |
|
||||||
imshow("picker",imgCanvas); |
|
||||||
waitKey(); |
|
||||||
} |
|
||||||
} |
|
||||||
void TrackerTLDModel::pushIntoModel(const Mat_<uchar>& example,bool positive){ |
|
||||||
std::vector<Mat_<uchar> >* proxyV; |
|
||||||
unsigned int* proxyN; |
|
||||||
std::vector<unsigned int>* proxyT; |
|
||||||
if(positive){ |
|
||||||
proxyV=&positiveExamples; |
|
||||||
proxyN=&timeStampPositiveNext; |
|
||||||
proxyT=&timeStampsPositive; |
|
||||||
}else{ |
|
||||||
proxyV=&negativeExamples; |
|
||||||
proxyN=&timeStampNegativeNext; |
|
||||||
proxyT=&timeStampsNegative; |
|
||||||
} |
|
||||||
if(proxyV->size()<MAX_EXAMPLES_IN_MODEL){ |
|
||||||
proxyV->push_back(example); |
|
||||||
proxyT->push_back(*proxyN); |
|
||||||
}else{ |
|
||||||
int index=rng.uniform((int)0,(int)proxyV->size()); |
|
||||||
(*proxyV)[index]=example; |
|
||||||
(*proxyT)[index]=(*proxyN); |
|
||||||
} |
|
||||||
(*proxyN)++; |
|
||||||
} |
|
||||||
|
|
||||||
} /* namespace cv */ |
|
@ -0,0 +1,4 @@ |
|||||||
|
set(the_description "Extended image processing module. It includes edge-aware filters and etc.") |
||||||
|
ocv_define_module(ximgproc opencv_imgproc opencv_core opencv_highgui) |
||||||
|
|
||||||
|
target_link_libraries(opencv_ximgproc) |
@ -0,0 +1,259 @@ |
|||||||
|
.. highlight:: cpp |
||||||
|
|
||||||
|
Domain Transform filter |
||||||
|
==================================== |
||||||
|
|
||||||
|
This section describes interface for Domain Transform filter. |
||||||
|
For more details about this filter see [Gastal11]_ and References_. |
||||||
|
|
||||||
|
DTFilter |
||||||
|
------------------------------------ |
||||||
|
.. ocv:class:: DTFilter : public Algorithm |
||||||
|
|
||||||
|
Interface for realizations of Domain Transform filter. |
||||||
|
|
||||||
|
createDTFilter |
||||||
|
------------------------------------ |
||||||
|
Factory method, create instance of :ocv:class:`DTFilter` and produce initialization routines. |
||||||
|
|
||||||
|
.. ocv:function:: Ptr<DTFilter> createDTFilter(InputArray guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.createDTFilter(guide, sigmaSpatial, sigmaColor[, mode[, numIters]]) -> instance |
||||||
|
|
||||||
|
:param guide: guided image (used to build transformed distance, which describes edge structure of guided image). |
||||||
|
:param sigmaSpatial: :math:`{\sigma}_H` parameter in the original article, it's similar to the sigma in the coordinate space into :ocv:func:`bilateralFilter`. |
||||||
|
:param sigmaColor: :math:`{\sigma}_r` parameter in the original article, it's similar to the sigma in the color space into :ocv:func:`bilateralFilter`. |
||||||
|
:param mode: one form three modes ``DTF_NC``, ``DTF_RF`` and ``DTF_IC`` which corresponds to three modes for filtering 2D signals in the article. |
||||||
|
:param numIters: optional number of iterations used for filtering, 3 is quite enough. |
||||||
|
|
||||||
|
For more details about Domain Transform filter parameters, see the original article [Gastal11]_ and `Domain Transform filter homepage <http://www.inf.ufrgs.br/~eslgastal/DomainTransform/>`_. |
||||||
|
|
||||||
|
DTFilter::filter |
||||||
|
------------------------------------ |
||||||
|
Produce domain transform filtering operation on source image. |
||||||
|
|
||||||
|
.. ocv:function:: void DTFilter::filter(InputArray src, OutputArray dst, int dDepth = -1) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.DTFilter.filter(src, dst[, dDepth]) -> None |
||||||
|
|
||||||
|
:param src: filtering image with unsigned 8-bit or floating-point 32-bit depth and up to 4 channels. |
||||||
|
:param dst: destination image. |
||||||
|
:param dDepth: optional depth of the output image. ``dDepth`` can be set to -1, which will be equivalent to ``src.depth()``. |
||||||
|
|
||||||
|
dtFilter |
||||||
|
------------------------------------ |
||||||
|
Simple one-line Domain Transform filter call. |
||||||
|
If you have multiple images to filter with the same guided image then use :ocv:class:`DTFilter` interface to avoid extra computations on initialization stage. |
||||||
|
|
||||||
|
.. ocv:function:: void dtFilter(InputArray guide, InputArray src, OutputArray dst, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.dtFilter(guide, src, sigmaSpatial, sigmaColor[, mode[, numIters]]) -> None |
||||||
|
|
||||||
|
:param guide: guided image (also called as joint image) with unsigned 8-bit or floating-point 32-bit depth and up to 4 channels. |
||||||
|
:param src: filtering image with unsigned 8-bit or floating-point 32-bit depth and up to 4 channels. |
||||||
|
:param sigmaSpatial: :math:`{\sigma}_H` parameter in the original article, it's similar to the sigma in the coordinate space into :ocv:func:`bilateralFilter`. |
||||||
|
:param sigmaColor: :math:`{\sigma}_r` parameter in the original article, it's similar to the sigma in the color space into :ocv:func:`bilateralFilter`. |
||||||
|
:param mode: one form three modes ``DTF_NC``, ``DTF_RF`` and ``DTF_IC`` which corresponds to three modes for filtering 2D signals in the article. |
||||||
|
:param numIters: optional number of iterations used for filtering, 3 is quite enough. |
||||||
|
|
||||||
|
.. seealso:: :ocv:func:`bilateralFilter`, :ocv:func:`guidedFilter`, :ocv:func:`amFilter` |
||||||
|
|
||||||
|
Guided Filter |
||||||
|
==================================== |
||||||
|
|
||||||
|
This section describes interface for Guided Filter. |
||||||
|
For more details about this filter see [Kaiming10]_ and References_. |
||||||
|
|
||||||
|
GuidedFilter |
||||||
|
------------------------------------ |
||||||
|
.. ocv:class:: GuidedFilter : public Algorithm |
||||||
|
|
||||||
|
Interface for realizations of Guided Filter. |
||||||
|
|
||||||
|
createGuidedFilter |
||||||
|
------------------------------------ |
||||||
|
Factory method, create instance of :ocv:class:`GuidedFilter` and produce initialization routines. |
||||||
|
|
||||||
|
.. ocv:function:: Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.createGuidedFilter(guide, radius, eps) -> instance |
||||||
|
|
||||||
|
:param guide: guided image (or array of images) with up to 3 channels, if it have more then 3 channels then only first 3 channels will be used. |
||||||
|
:param radius: radius of Guided Filter. |
||||||
|
:param eps: regularization term of Guided Filter. :math:`{eps}^2` is similar to the sigma in the color space into :ocv:func:`bilateralFilter`. |
||||||
|
|
||||||
|
For more details about Guided Filter parameters, see the original article [Kaiming10]_. |
||||||
|
|
||||||
|
GuidedFilter::filter |
||||||
|
------------------------------------ |
||||||
|
Apply Guided Filter to the filtering image. |
||||||
|
|
||||||
|
.. ocv:function:: void GuidedFilter::filter(InputArray src, OutputArray dst, int dDepth = -1) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.GuidedFilter.filter(src, dst[, dDepth]) -> None |
||||||
|
|
||||||
|
:param src: filtering image with any numbers of channels. |
||||||
|
:param dst: output image. |
||||||
|
:param dDepth: optional depth of the output image. ``dDepth`` can be set to -1, which will be equivalent to ``src.depth()``. |
||||||
|
|
||||||
|
guidedFilter |
||||||
|
------------------------------------ |
||||||
|
Simple one-line Guided Filter call. |
||||||
|
If you have multiple images to filter with the same guided image then use :ocv:class:`GuidedFilter` interface to avoid extra computations on initialization stage. |
||||||
|
|
||||||
|
.. ocv:function:: void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth = -1) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.guidedFilter(guide, src, dst, radius, eps, [, dDepth]) -> None |
||||||
|
|
||||||
|
:param guide: guided image (or array of images) with up to 3 channels, if it have more then 3 channels then only first 3 channels will be used. |
||||||
|
:param src: filtering image with any numbers of channels. |
||||||
|
:param dst: output image. |
||||||
|
:param radius: radius of Guided Filter. |
||||||
|
:param eps: regularization term of Guided Filter. :math:`{eps}^2` is similar to the sigma in the color space into :ocv:func:`bilateralFilter`. |
||||||
|
:param dDepth: optional depth of the output image. |
||||||
|
|
||||||
|
.. seealso:: :ocv:func:`bilateralFilter`, :ocv:func:`dtFilter`, :ocv:func:`amFilter` |
||||||
|
|
||||||
|
Adaptive Manifold Filter |
||||||
|
==================================== |
||||||
|
|
||||||
|
This section describes interface for Adaptive Manifold Filter. |
||||||
|
|
||||||
|
For more details about this filter see [Gastal12]_ and References_. |
||||||
|
|
||||||
|
AdaptiveManifoldFilter |
||||||
|
------------------------------------ |
||||||
|
.. ocv:class:: AdaptiveManifoldFilter : public Algorithm |
||||||
|
|
||||||
|
Interface for Adaptive Manifold Filter realizations. |
||||||
|
|
||||||
|
Below listed optional parameters which may be set up with :ocv:func:`Algorithm::set` function. |
||||||
|
|
||||||
|
.. ocv:member:: double sigma_s = 16.0 |
||||||
|
|
||||||
|
Spatial standard deviation. |
||||||
|
|
||||||
|
.. ocv:member:: double sigma_r = 0.2 |
||||||
|
|
||||||
|
Color space standard deviation. |
||||||
|
|
||||||
|
.. ocv:member:: int tree_height = -1 |
||||||
|
|
||||||
|
Height of the manifold tree (default = -1 : automatically computed). |
||||||
|
|
||||||
|
.. ocv:member:: int num_pca_iterations = 1 |
||||||
|
|
||||||
|
Number of iterations to computed the eigenvector. |
||||||
|
|
||||||
|
.. ocv:member:: bool adjust_outliers = false |
||||||
|
|
||||||
|
Specify adjust outliers using Eq. 9 or not. |
||||||
|
|
||||||
|
.. ocv:member:: bool use_RNG = true |
||||||
|
|
||||||
|
Specify use random number generator to compute eigenvector or not. |
||||||
|
|
||||||
|
createAMFilter |
||||||
|
------------------------------------ |
||||||
|
Factory method, create instance of :ocv:class:`AdaptiveManifoldFilter` and produce some initialization routines. |
||||||
|
|
||||||
|
.. ocv:function:: Ptr<AdaptiveManifoldFilter> createAMFilter(double sigma_s, double sigma_r, bool adjust_outliers = false) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.createAMFilter(sigma_s, sigma_r, adjust_outliers) -> instance |
||||||
|
|
||||||
|
:param sigma_s: spatial standard deviation. |
||||||
|
:param sigma_r: color space standard deviation, it is similar to the sigma in the color space into :ocv:func:`bilateralFilter`. |
||||||
|
:param adjust_outliers: optional, specify perform outliers adjust operation or not, (Eq. 9) in the original paper. |
||||||
|
|
||||||
|
For more details about Adaptive Manifold Filter parameters, see the original article [Gastal12]_. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
Joint images with `CV_8U` and `CV_16U` depth converted to images with `CV_32F` depth and [0; 1] color range before processing. |
||||||
|
Hence color space sigma `sigma_r` must be in [0; 1] range, unlike same sigmas in :ocv:func:`bilateralFilter` and :ocv:func:`dtFilter` functions. |
||||||
|
|
||||||
|
AdaptiveManifoldFilter::filter |
||||||
|
------------------------------------ |
||||||
|
Apply high-dimensional filtering using adaptive manifolds. |
||||||
|
|
||||||
|
.. ocv:function:: void AdaptiveManifoldFilter::filter(InputArray src, OutputArray dst, InputArray joint = noArray()) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.AdaptiveManifoldFilter.filter(src, dst[, joint]) -> None |
||||||
|
|
||||||
|
:param src: filtering image with any numbers of channels. |
||||||
|
:param dst: output image. |
||||||
|
:param joint: optional joint (also called as guided) image with any numbers of channels. |
||||||
|
|
||||||
|
amFilter |
||||||
|
------------------------------------ |
||||||
|
Simple one-line Adaptive Manifold Filter call. |
||||||
|
|
||||||
|
.. ocv:function:: void amFilter(InputArray joint, InputArray src, OutputArray dst, double sigma_s, double sigma_r, bool adjust_outliers = false) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.amFilter(joint, src, dst, sigma_s, sigma_r, [, adjust_outliers]) -> None |
||||||
|
|
||||||
|
:param joint: joint (also called as guided) image or array of images with any numbers of channels. |
||||||
|
:param src: filtering image with any numbers of channels. |
||||||
|
:param dst: output image. |
||||||
|
:param sigma_s: spatial standard deviation. |
||||||
|
:param sigma_r: color space standard deviation, it is similar to the sigma in the color space into :ocv:func:`bilateralFilter`. |
||||||
|
:param adjust_outliers: optional, specify perform outliers adjust operation or not, (Eq. 9) in the original paper. |
||||||
|
|
||||||
|
.. note:: |
||||||
|
Joint images with `CV_8U` and `CV_16U` depth converted to images with `CV_32F` depth and [0; 1] color range before processing. |
||||||
|
Hence color space sigma `sigma_r` must be in [0; 1] range, unlike same sigmas in :ocv:func:`bilateralFilter` and :ocv:func:`dtFilter` functions. |
||||||
|
|
||||||
|
.. seealso:: :ocv:func:`bilateralFilter`, :ocv:func:`dtFilter`, :ocv:func:`guidedFilter` |
||||||
|
|
||||||
|
Joint Bilateral Filter |
||||||
|
==================================== |
||||||
|
|
||||||
|
jointBilateralFilter |
||||||
|
------------------------------------ |
||||||
|
Applies the joint bilateral filter to an image. |
||||||
|
|
||||||
|
.. ocv:function:: void jointBilateralFilter(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT) |
||||||
|
|
||||||
|
.. ocv:pyfunction:: cv2.jointBilateralFilter(joint, src, dst, d, sigmaColor, sigmaSpace, [, borderType]) -> None |
||||||
|
|
||||||
|
:param joint: Joint 8-bit or floating-point, 1-channel or 3-channel image. |
||||||
|
|
||||||
|
:param src: Source 8-bit or floating-point, 1-channel or 3-channel image with the same depth as joint image. |
||||||
|
|
||||||
|
:param dst: Destination image of the same size and type as ``src`` . |
||||||
|
|
||||||
|
:param d: Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, it is computed from ``sigmaSpace`` . |
||||||
|
|
||||||
|
:param sigmaColor: Filter sigma in the color space. A larger value of the parameter means that farther colors within the pixel neighborhood (see ``sigmaSpace`` ) will be mixed together, resulting in larger areas of semi-equal color. |
||||||
|
|
||||||
|
:param sigmaSpace: Filter sigma in the coordinate space. A larger value of the parameter means that farther pixels will influence each other as long as their colors are close enough (see ``sigmaColor`` ). When ``d>0`` , it specifies the neighborhood size regardless of ``sigmaSpace`` . Otherwise, ``d`` is proportional to ``sigmaSpace`` . |
||||||
|
|
||||||
|
.. note:: :ocv:func:`bilateralFilter` and :ocv:func:`jointBilateralFilter` use L1 norm to compute difference between colors. |
||||||
|
|
||||||
|
.. seealso:: :ocv:func:`bilateralFilter`, :ocv:func:`amFilter` |
||||||
|
|
||||||
|
References |
||||||
|
========== |
||||||
|
|
||||||
|
.. [Gastal11] E. Gastal and M. Oliveira, "Domain Transform for Edge-Aware Image and Video Processing", Proceedings of SIGGRAPH, 2011, vol. 30, pp. 69:1 - 69:12. |
||||||
|
|
||||||
|
The paper is available `online <http://www.inf.ufrgs.br/~eslgastal/DomainTransform/>`__. |
||||||
|
|
||||||
|
|
||||||
|
.. [Gastal12] E. Gastal and M. Oliveira, "Adaptive manifolds for real-time high-dimensional filtering," Proceedings of SIGGRAPH, 2012, vol. 31, pp. 33:1 - 33:13. |
||||||
|
|
||||||
|
The paper is available `online <http://inf.ufrgs.br/~eslgastal/AdaptiveManifolds/>`__. |
||||||
|
|
||||||
|
|
||||||
|
.. [Kaiming10] Kaiming He et. al., "Guided Image Filtering," ECCV 2010, pp. 1 - 14. |
||||||
|
|
||||||
|
The paper is available `online <http://research.microsoft.com/en-us/um/people/kahe/eccv10/>`__. |
||||||
|
|
||||||
|
|
||||||
|
.. [Tomasi98] Carlo Tomasi and Roberto Manduchi, “Bilateral filtering for gray and color images,” in Computer Vision, 1998. Sixth International Conference on . IEEE, 1998, pp. 839– 846. |
||||||
|
|
||||||
|
The paper is available `online <https://www.cs.duke.edu/~tomasi/papers/tomasi/tomasiIccv98.pdf>`__. |
||||||
|
|
||||||
|
|
||||||
|
.. [Ziyang13] Ziyang Ma et al., "Constant Time Weighted Median Filtering for Stereo Matching and Beyond," ICCV, 2013, pp. 49 - 56. |
||||||
|
|
||||||
|
The paper is available `online <http://www.cv-foundation.org/openaccess/content_iccv_2013/papers/Ma_Constant_Time_Weighted_2013_ICCV_paper.pdf>`__. |
@ -0,0 +1,10 @@ |
|||||||
|
******************************************** |
||||||
|
ximgproc. Extended image processing module. |
||||||
|
******************************************** |
||||||
|
|
||||||
|
.. highlight:: cpp |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 2 |
||||||
|
|
||||||
|
edge_aware_filters |
@ -0,0 +1,41 @@ |
|||||||
|
/*
|
||||||
|
* Software License Agreement (BSD License) |
||||||
|
* |
||||||
|
* Copyright (c) 2009, Willow Garage, Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* 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 name of Willow Garage, Inc. nor the names of its |
||||||
|
* 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 THE |
||||||
|
* COPYRIGHT OWNER 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_HPP__ |
||||||
|
#define __OPENCV_XIMGPROC_HPP__ |
||||||
|
|
||||||
|
#include "ximgproc/edge_filter.hpp" |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,127 @@ |
|||||||
|
/*
|
||||||
|
* Software License Agreement (BSD License) |
||||||
|
* |
||||||
|
* Copyright (c) 2009, Willow Garage, Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* 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 name of Willow Garage, Inc. nor the names of its |
||||||
|
* 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 THE |
||||||
|
* COPYRIGHT OWNER 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_EDGEFILTER_HPP__ |
||||||
|
#define __OPENCV_EDGEFILTER_HPP__ |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include <opencv2/core.hpp> |
||||||
|
|
||||||
|
namespace cv |
||||||
|
{ |
||||||
|
namespace ximgproc |
||||||
|
{ |
||||||
|
|
||||||
|
enum EdgeAwareFiltersList |
||||||
|
{ |
||||||
|
DTF_NC, |
||||||
|
DTF_IC, |
||||||
|
DTF_RF, |
||||||
|
|
||||||
|
GUIDED_FILTER, |
||||||
|
AM_FILTER |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/*Interface for DT filters*/ |
||||||
|
class CV_EXPORTS DTFilter : public Algorithm |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
virtual void filter(InputArray src, OutputArray dst, int dDepth = -1) = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef Ptr<DTFilter> DTFilterPtr; |
||||||
|
|
||||||
|
/*Fabric function for DT filters*/ |
||||||
|
CV_EXPORTS |
||||||
|
Ptr<DTFilter> createDTFilter(InputArray guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3); |
||||||
|
|
||||||
|
/*One-line DT filter call*/ |
||||||
|
CV_EXPORTS |
||||||
|
void dtFilter(InputArray guide, InputArray src, OutputArray dst, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3); |
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*Interface for Guided Filter*/ |
||||||
|
class CV_EXPORTS GuidedFilter : public Algorithm |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
virtual void filter(InputArray src, OutputArray dst, int dDepth = -1) = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
/*Fabric function for Guided Filter*/ |
||||||
|
CV_EXPORTS Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps); |
||||||
|
|
||||||
|
/*One-line Guided Filter call*/ |
||||||
|
CV_EXPORTS void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth = -1); |
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CV_EXPORTS AdaptiveManifoldFilter : public Algorithm |
||||||
|
{ |
||||||
|
public: |
||||||
|
/**
|
||||||
|
* @brief Apply High-dimensional filtering using adaptive manifolds |
||||||
|
* @param src Input image to be filtered. |
||||||
|
* @param dst Adaptive-manifold filter response. |
||||||
|
* @param src_joint Image for joint filtering (optional). |
||||||
|
*/ |
||||||
|
virtual void filter(InputArray src, OutputArray dst, InputArray joint = noArray()) = 0; |
||||||
|
|
||||||
|
virtual void collectGarbage() = 0; |
||||||
|
|
||||||
|
static Ptr<AdaptiveManifoldFilter> create(); |
||||||
|
}; |
||||||
|
|
||||||
|
//Fabric function for AM filter algorithm
|
||||||
|
CV_EXPORTS Ptr<AdaptiveManifoldFilter> createAMFilter(double sigma_s, double sigma_r, bool adjust_outliers = false); |
||||||
|
|
||||||
|
//One-line Adaptive Manifold filter call
|
||||||
|
CV_EXPORTS void amFilter(InputArray joint, InputArray src, OutputArray dst, double sigma_s, double sigma_r, bool adjust_outliers = false); |
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CV_EXPORTS |
||||||
|
void jointBilateralFilter(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,57 @@ |
|||||||
|
#include "perf_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using std::tr1::tuple; |
||||||
|
using std::tr1::get; |
||||||
|
using namespace perf; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
|
||||||
|
typedef tuple<bool, Size, int, int, MatType> AMPerfTestParam; |
||||||
|
typedef TestBaseWithParam<AMPerfTestParam> AdaptiveManifoldPerfTest; |
||||||
|
|
||||||
|
PERF_TEST_P( AdaptiveManifoldPerfTest, perf, |
||||||
|
Combine( |
||||||
|
Values(true, false), //adjust_outliers flag
|
||||||
|
Values(sz1080p, sz720p), //size
|
||||||
|
Values(1, 3, 8), //joint channels num
|
||||||
|
Values(1, 3), //source channels num
|
||||||
|
Values(CV_8U, CV_32F) //source and joint depth
|
||||||
|
) |
||||||
|
) |
||||||
|
{ |
||||||
|
AMPerfTestParam params = GetParam(); |
||||||
|
bool adjustOutliers = get<0>(params); |
||||||
|
Size sz = get<1>(params); |
||||||
|
int jointCnNum = get<2>(params); |
||||||
|
int srcCnNum = get<3>(params); |
||||||
|
int depth = get<4>(params); |
||||||
|
|
||||||
|
Mat joint(sz, CV_MAKE_TYPE(depth, jointCnNum)); |
||||||
|
Mat src(sz, CV_MAKE_TYPE(depth, srcCnNum)); |
||||||
|
Mat dst(sz, CV_MAKE_TYPE(depth, srcCnNum)); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
|
||||||
|
declare.in(joint, src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); |
||||||
|
|
||||||
|
double sigma_s = 16; |
||||||
|
double sigma_r = 0.5; |
||||||
|
TEST_CYCLE_N(3) |
||||||
|
{ |
||||||
|
Mat res; |
||||||
|
amFilter(joint, src, res, sigma_s, sigma_r, adjustOutliers); |
||||||
|
|
||||||
|
//at 5th cycle sigma_s will be five times more and tree depth will be 5
|
||||||
|
sigma_s *= 1.38; |
||||||
|
sigma_r /= 1.38; |
||||||
|
} |
||||||
|
|
||||||
|
SANITY_CHECK(dst); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
#include "perf_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using std::tr1::tuple; |
||||||
|
using std::tr1::get; |
||||||
|
using namespace perf; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
CV_ENUM(GuideMatType, CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3) //reduced set
|
||||||
|
CV_ENUM(SourceMatType, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4) //full supported set
|
||||||
|
CV_ENUM(DTFMode, DTF_NC, DTF_IC, DTF_RF) |
||||||
|
typedef tuple<GuideMatType, SourceMatType, Size, double, double, DTFMode> DTTestParams; |
||||||
|
|
||||||
|
typedef TestBaseWithParam<DTTestParams> DomainTransformTest; |
||||||
|
|
||||||
|
PERF_TEST_P( DomainTransformTest, perf, |
||||||
|
Combine( |
||||||
|
GuideMatType::all(), |
||||||
|
SourceMatType::all(), |
||||||
|
Values(szVGA, sz720p), |
||||||
|
Values(10.0, 80.0), |
||||||
|
Values(30.0, 50.0), |
||||||
|
DTFMode::all() |
||||||
|
) |
||||||
|
) |
||||||
|
{ |
||||||
|
int guideType = get<0>(GetParam()); |
||||||
|
int srcType = get<1>(GetParam()); |
||||||
|
Size size = get<2>(GetParam()); |
||||||
|
double sigmaSpatial = get<3>(GetParam()); |
||||||
|
double sigmaColor = get<4>(GetParam()); |
||||||
|
int dtfType = get<5>(GetParam()); |
||||||
|
|
||||||
|
Mat guide(size, guideType); |
||||||
|
Mat src(size, srcType); |
||||||
|
Mat dst(size, srcType); |
||||||
|
|
||||||
|
declare.in(guide, src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
TEST_CYCLE_N(5) |
||||||
|
{ |
||||||
|
dtFilter(guide, src, dst, sigmaSpatial, sigmaColor, dtfType); |
||||||
|
} |
||||||
|
|
||||||
|
SANITY_CHECK(dst); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
#include "perf_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using std::tr1::tuple; |
||||||
|
using std::tr1::get; |
||||||
|
using namespace perf; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC2, CV_8UC3, CV_32FC1, CV_32FC2, CV_32FC3); |
||||||
|
CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3); |
||||||
|
typedef tuple<GuideTypes, SrcTypes, Size> GFParams; |
||||||
|
|
||||||
|
typedef TestBaseWithParam<GFParams> GuidedFilterPerfTest; |
||||||
|
|
||||||
|
PERF_TEST_P( GuidedFilterPerfTest, perf, Combine(GuideTypes::all(), SrcTypes::all(), Values(sz1080p, sz2K)) ) |
||||||
|
{ |
||||||
|
RNG rng(0); |
||||||
|
|
||||||
|
GFParams params = GetParam(); |
||||||
|
int guideType = get<0>(params); |
||||||
|
int srcType = get<1>(params); |
||||||
|
Size sz = get<2>(params); |
||||||
|
|
||||||
|
Mat guide(sz, guideType); |
||||||
|
Mat src(sz, srcType); |
||||||
|
Mat dst(sz, srcType); |
||||||
|
|
||||||
|
declare.in(guide, src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
TEST_CYCLE_N(3) |
||||||
|
{ |
||||||
|
int radius = rng.uniform(5, 30); |
||||||
|
double eps = rng.uniform(0.1, 1e5); |
||||||
|
guidedFilter(guide, src, dst, radius, eps); |
||||||
|
} |
||||||
|
|
||||||
|
SANITY_CHECK(dst); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
#include "perf_precomp.hpp" |
||||||
|
|
||||||
|
CV_PERF_TEST_MAIN(edgefilter) |
@ -0,0 +1,17 @@ |
|||||||
|
#ifdef __GNUC__ |
||||||
|
# pragma GCC diagnostic ignored "-Wmissing-declarations" |
||||||
|
# if defined __clang__ || defined __APPLE__ |
||||||
|
# pragma GCC diagnostic ignored "-Wmissing-prototypes" |
||||||
|
# pragma GCC diagnostic ignored "-Wextra" |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef __OPENCV_PERF_PRECOMP_HPP__ |
||||||
|
#define __OPENCV_PERF_PRECOMP_HPP__ |
||||||
|
|
||||||
|
#include <opencv2/ts.hpp> |
||||||
|
#include <opencv2/imgproc.hpp> |
||||||
|
#include <opencv2/highgui.hpp> |
||||||
|
#include <opencv2/ximgproc.hpp> |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,49 @@ |
|||||||
|
#include "perf_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using std::tr1::tuple; |
||||||
|
using std::tr1::get; |
||||||
|
using namespace perf; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
typedef tuple<double, Size, MatType, int, int> JBFTestParam; |
||||||
|
typedef TestBaseWithParam<JBFTestParam> JointBilateralFilterTest; |
||||||
|
|
||||||
|
PERF_TEST_P(JointBilateralFilterTest, perf,
|
||||||
|
Combine( |
||||||
|
Values(2.0, 4.0, 6.0, 10.0), |
||||||
|
SZ_TYPICAL, |
||||||
|
Values(CV_8U, CV_32F), |
||||||
|
Values(1, 3), |
||||||
|
Values(1, 3)) |
||||||
|
) |
||||||
|
{ |
||||||
|
JBFTestParam params = GetParam(); |
||||||
|
double sigmaS = get<0>(params); |
||||||
|
Size sz = get<1>(params); |
||||||
|
int depth = get<2>(params); |
||||||
|
int jCn = get<3>(params); |
||||||
|
int srcCn = get<4>(params); |
||||||
|
|
||||||
|
Mat joint(sz, CV_MAKE_TYPE(depth, jCn)); |
||||||
|
Mat src(sz, CV_MAKE_TYPE(depth, srcCn)); |
||||||
|
Mat dst(sz, src.type()); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
declare.in(joint, src, WARMUP_RNG).out(dst).tbb_threads(cv::getNumberOfCPUs()); |
||||||
|
|
||||||
|
RNG rnd(cvRound(10*sigmaS) + sz.height + depth + jCn + srcCn); |
||||||
|
double sigmaC = rnd.uniform(1.0, 255.0); |
||||||
|
|
||||||
|
TEST_CYCLE_N(1) |
||||||
|
{ |
||||||
|
jointBilateralFilter(joint, src, dst, 0, sigmaC, sigmaS); |
||||||
|
} |
||||||
|
|
||||||
|
SANITY_CHECK(dst); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
cmake_minimum_required(VERSION 2.8) |
||||||
|
project(live_demo) |
||||||
|
find_package(OpenCV 3.0 REQUIRED) |
||||||
|
|
||||||
|
set(SOURCES live_demo.cpp) |
||||||
|
|
||||||
|
include_directories(${OpenCV_INCLUDE_DIRS}) |
||||||
|
add_executable(live_demo ${SOURCES} ${HEADERS}) |
||||||
|
target_link_libraries(live_demo ${OpenCV_LIBS}) |
@ -0,0 +1,195 @@ |
|||||||
|
#include <opencv2/core.hpp> |
||||||
|
#include <opencv2/core/utility.hpp> |
||||||
|
#include <opencv2/highgui.hpp> |
||||||
|
#include <opencv2/imgproc.hpp> |
||||||
|
#include <opencv2/ximgproc.hpp> |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
typedef void(*FilteringOperation)(const Mat& src, Mat& dst); |
||||||
|
//current mode (filtering operation example)
|
||||||
|
FilteringOperation g_filterOp = NULL; |
||||||
|
|
||||||
|
//list of filtering operations
|
||||||
|
void filterDoNothing(const Mat& frame, Mat& dst); |
||||||
|
void filterBlurring(const Mat& frame, Mat& dst); |
||||||
|
void filterStylize(const Mat& frame, Mat& dst); |
||||||
|
void filterDetailEnhancement(const Mat& frame8u, Mat& dst); |
||||||
|
|
||||||
|
//common sliders for every mode
|
||||||
|
int g_sigmaColor = 25; |
||||||
|
int g_sigmaSpatial = 10; |
||||||
|
|
||||||
|
//for Stylizing mode
|
||||||
|
int g_edgesGamma = 100; |
||||||
|
|
||||||
|
//for Details Enhancement mode
|
||||||
|
int g_contrastBase = 100; |
||||||
|
int g_detailsLevel = 100; |
||||||
|
|
||||||
|
int g_numberOfCPUs = cv::getNumberOfCPUs(); |
||||||
|
|
||||||
|
//We will use two callbacks to change parameters
|
||||||
|
void changeModeCallback(int state, void *filter); |
||||||
|
void changeNumberOfCpuCallback(int count, void*); |
||||||
|
|
||||||
|
void splitScreen(const Mat& rawFrame, Mat& outputFrame, Mat& srcFrame, Mat& processedFrame); |
||||||
|
|
||||||
|
//trivial filter
|
||||||
|
void filterDoNothing(const Mat& frame, Mat& dst) |
||||||
|
{ |
||||||
|
frame.copyTo(dst); |
||||||
|
} |
||||||
|
|
||||||
|
//simple edge-aware blurring
|
||||||
|
void filterBlurring(const Mat& frame, Mat& dst) |
||||||
|
{ |
||||||
|
dtFilter(frame, frame, dst, g_sigmaSpatial, g_sigmaColor, DTF_RF); |
||||||
|
} |
||||||
|
|
||||||
|
//stylizing filter
|
||||||
|
void filterStylize(const Mat& frame, Mat& dst) |
||||||
|
{ |
||||||
|
//blur frame
|
||||||
|
Mat filtered; |
||||||
|
dtFilter(frame, frame, filtered, g_sigmaSpatial, g_sigmaColor, DTF_NC); |
||||||
|
|
||||||
|
//compute grayscale blurred frame
|
||||||
|
Mat filteredGray; |
||||||
|
cvtColor(filtered, filteredGray, COLOR_BGR2GRAY); |
||||||
|
|
||||||
|
//find gradients of blurred image
|
||||||
|
Mat gradX, gradY; |
||||||
|
Sobel(filteredGray, gradX, CV_32F, 1, 0, 3, 1.0/255); |
||||||
|
Sobel(filteredGray, gradY, CV_32F, 0, 1, 3, 1.0/255); |
||||||
|
|
||||||
|
//compute magnitude of gradient and fit it accordingly the gamma parameter
|
||||||
|
Mat gradMagnitude; |
||||||
|
magnitude(gradX, gradY, gradMagnitude); |
||||||
|
cv::pow(gradMagnitude, g_edgesGamma/100.0, gradMagnitude); |
||||||
|
|
||||||
|
//multiply a blurred frame to the value inversely proportional to the magnitude
|
||||||
|
Mat multiplier = 1.0/(1.0 + gradMagnitude); |
||||||
|
cvtColor(multiplier, multiplier, COLOR_GRAY2BGR); |
||||||
|
multiply(filtered, multiplier, dst, 1, dst.type()); |
||||||
|
} |
||||||
|
|
||||||
|
void filterDetailEnhancement(const Mat& frame8u, Mat& dst) |
||||||
|
{ |
||||||
|
Mat frame; |
||||||
|
frame8u.convertTo(frame, CV_32F, 1.0/255); |
||||||
|
|
||||||
|
//Decompose image to 3 Lab channels
|
||||||
|
Mat frameLab, frameLabCn[3]; |
||||||
|
cvtColor(frame, frameLab, COLOR_BGR2Lab); |
||||||
|
split(frameLab, frameLabCn); |
||||||
|
|
||||||
|
//Generate progressively smoother versions of the lightness channel
|
||||||
|
Mat layer0 = frameLabCn[0]; //first channel is original lightness
|
||||||
|
Mat layer1, layer2; |
||||||
|
dtFilter(layer0, layer0, layer1, g_sigmaSpatial, g_sigmaColor, DTF_IC); |
||||||
|
dtFilter(layer1, layer1, layer2, 2*g_sigmaSpatial, g_sigmaColor, DTF_IC); |
||||||
|
|
||||||
|
//Compute detail layers
|
||||||
|
Mat detailLayer1 = layer0 - layer1; |
||||||
|
Mat detailLayer2 = layer1 - layer2; |
||||||
|
|
||||||
|
double cBase = g_contrastBase / 100.0; |
||||||
|
double cDetails1 = g_detailsLevel / 100.0; |
||||||
|
double cDetails2 = 2.0 - g_detailsLevel / 100.0; |
||||||
|
|
||||||
|
//Generate lightness
|
||||||
|
double meanLigtness = mean(frameLabCn[0])[0]; |
||||||
|
frameLabCn[0] = cBase*(layer2 - meanLigtness) + meanLigtness; //fit contrast of base (most blurred) layer
|
||||||
|
frameLabCn[0] += cDetails1*detailLayer1; //add weighted sum of detail layers to new lightness
|
||||||
|
frameLabCn[0] += cDetails2*detailLayer2; //
|
||||||
|
|
||||||
|
//Update new lightness
|
||||||
|
merge(frameLabCn, 3, frameLab); |
||||||
|
cvtColor(frameLab, frame, COLOR_Lab2BGR); |
||||||
|
frame.convertTo(dst, CV_8U, 255); |
||||||
|
} |
||||||
|
|
||||||
|
void changeModeCallback(int state, void *filter) |
||||||
|
{ |
||||||
|
if (state == 1) |
||||||
|
g_filterOp = (FilteringOperation) filter; |
||||||
|
} |
||||||
|
|
||||||
|
void changeNumberOfCpuCallback(int count, void*) |
||||||
|
{ |
||||||
|
count = std::max(1, count); |
||||||
|
cv::setNumThreads(count); |
||||||
|
g_numberOfCPUs = count; |
||||||
|
} |
||||||
|
|
||||||
|
//divide screen on two parts: srcFrame and processed Frame
|
||||||
|
void splitScreen(const Mat& rawFrame, Mat& outputFrame, Mat& srcFrame, Mat& processedFrame) |
||||||
|
{ |
||||||
|
int h = rawFrame.rows; |
||||||
|
int w = rawFrame.cols; |
||||||
|
int cn = rawFrame.channels(); |
||||||
|
|
||||||
|
outputFrame.create(h, 2 * w, CV_MAKE_TYPE(CV_8U, cn)); |
||||||
|
srcFrame = outputFrame(Range::all(), Range(0, w)); |
||||||
|
processedFrame = outputFrame(Range::all(), Range(w, 2 * w)); |
||||||
|
rawFrame.convertTo(srcFrame, srcFrame.type()); |
||||||
|
} |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
VideoCapture cap(0); |
||||||
|
if (!cap.isOpened()) |
||||||
|
{ |
||||||
|
cerr << "Capture device was not found" << endl; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
namedWindow("Demo"); |
||||||
|
displayOverlay("Demo", "Press Ctrl+P to show property window", 5000); |
||||||
|
|
||||||
|
//Thread trackbar
|
||||||
|
cv::setNumThreads(g_numberOfCPUs); //speedup filtering
|
||||||
|
createTrackbar("Threads", NULL, &g_numberOfCPUs, cv::getNumberOfCPUs(), changeNumberOfCpuCallback); |
||||||
|
|
||||||
|
//Buttons to choose different modes
|
||||||
|
createButton("Mode Details Enhancement", changeModeCallback, (void*)filterDetailEnhancement, QT_RADIOBOX, true); |
||||||
|
createButton("Mode Stylizing", changeModeCallback, (void*)filterStylize, QT_RADIOBOX, false); |
||||||
|
createButton("Mode Blurring", changeModeCallback, (void*)filterBlurring, QT_RADIOBOX, false); |
||||||
|
createButton("Mode DoNothing", changeModeCallback, (void*)filterDoNothing, QT_RADIOBOX, false); |
||||||
|
|
||||||
|
//sliders for Details Enhancement mode
|
||||||
|
g_filterOp = filterDetailEnhancement; //set Details Enhancement as default filter
|
||||||
|
createTrackbar("Detail contrast", NULL, &g_contrastBase, 200); |
||||||
|
createTrackbar("Detail level" , NULL, &g_detailsLevel, 200); |
||||||
|
|
||||||
|
//sliders for Stylizing mode
|
||||||
|
createTrackbar("Style gamma", NULL, &g_edgesGamma, 300); |
||||||
|
|
||||||
|
//sliders for every mode
|
||||||
|
createTrackbar("Sigma Spatial", NULL, &g_sigmaSpatial, 200); |
||||||
|
createTrackbar("Sigma Color" , NULL, &g_sigmaColor, 200); |
||||||
|
|
||||||
|
Mat rawFrame, outputFrame; |
||||||
|
Mat srcFrame, processedFrame; |
||||||
|
|
||||||
|
for (;;) |
||||||
|
{ |
||||||
|
do |
||||||
|
{ |
||||||
|
cap >> rawFrame; |
||||||
|
} while (rawFrame.empty()); |
||||||
|
|
||||||
|
splitScreen(rawFrame, outputFrame, srcFrame, processedFrame); |
||||||
|
g_filterOp(srcFrame, processedFrame); |
||||||
|
|
||||||
|
imshow("Demo", outputFrame); |
||||||
|
|
||||||
|
if (waitKey(1) == 27) break; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,774 @@ |
|||||||
|
#include "precomp.hpp" |
||||||
|
#include "edgeaware_filters_common.hpp" |
||||||
|
#include <cmath> |
||||||
|
#include <cstring> |
||||||
|
#include <limits> |
||||||
|
|
||||||
|
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 |
||||||
|
|
||||||
|
void computeEigenVector(const Mat1f& X, const Mat1b& mask, Mat1f& dst, int num_pca_iterations, const Mat1f& rand_vec); |
||||||
|
|
||||||
|
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: |
||||||
|
AlgorithmInfo* info() const; |
||||||
|
|
||||||
|
AdaptiveManifoldFilterN(); |
||||||
|
|
||||||
|
void filter(InputArray src, OutputArray dst, InputArray joint); |
||||||
|
|
||||||
|
void collectGarbage(); |
||||||
|
|
||||||
|
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(InputArray src, OutputArray dst) |
||||||
|
{ |
||||||
|
if (src.isMatVector()) |
||||||
|
{ |
||||||
|
vector<Mat>& srcv = *static_cast< vector<Mat>* >(src.getObj()); |
||||||
|
vector<Mat>& dstv = *static_cast< vector<Mat>* >(dst.getObj()); |
||||||
|
dstv.resize(srcv.size()); |
||||||
|
for (int i = 0; i < (int)srcv.size(); i++) |
||||||
|
downsample(srcv[i], dstv[i]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
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(InputArray src, OutputArray dst) |
||||||
|
{ |
||||||
|
if (src.isMatVector()) |
||||||
|
{ |
||||||
|
vector<Mat>& srcv = *static_cast< vector<Mat>* >(src.getObj()); |
||||||
|
vector<Mat>& dstv = *static_cast< vector<Mat>* >(dst.getObj()); |
||||||
|
dstv.resize(srcv.size()); |
||||||
|
for (int i = 0; i < (int)srcv.size(); i++) |
||||||
|
upsample(srcv[i], dstv[i]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CV_DbgAssert(src.empty() || src.size() == smallSize); |
||||||
|
resize(src, dst, srcSize, 0, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
}; |
||||||
|
|
||||||
|
CV_INIT_ALGORITHM(AdaptiveManifoldFilterN, "AdaptiveManifoldFilter", |
||||||
|
obj.info()->addParam(obj, "sigma_s", obj.sigma_s_, false, 0, 0, "Filter spatial standard deviation"); |
||||||
|
obj.info()->addParam(obj, "sigma_r", obj.sigma_r_, false, 0, 0, "Filter range standard deviation"); |
||||||
|
obj.info()->addParam(obj, "tree_height", obj.tree_height_, false, 0, 0, "Height of the manifold tree (default = -1 : automatically computed)"); |
||||||
|
obj.info()->addParam(obj, "num_pca_iterations", obj.num_pca_iterations_, false, 0, 0, "Number of iterations to computed the eigenvector v1"); |
||||||
|
obj.info()->addParam(obj, "adjust_outliers", obj.adjust_outliers_, false, 0, 0, "Specify adjust outliers using Eq. 9 or not"); |
||||||
|
obj.info()->addParam(obj, "use_RNG", obj.useRNG, false, 0, 0, "Specify use random to compute eigenvector or not");) |
||||||
|
|
||||||
|
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]; |
||||||
|
for (int x = 1; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
dst_row[x] = src_row[x] + a * (dst_row[x - 1] - src_row[x]); |
||||||
|
} |
||||||
|
for (int x = src.cols - 2; 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) |
||||||
|
{ |
||||||
|
Mat difEtaSrc; |
||||||
|
{ |
||||||
|
vector<Mat> eta_difCn(jointCnNum); |
||||||
|
for (int i = 0; i < jointCnNum; i++) |
||||||
|
subtract(jointCn[i], etaFull[i], eta_difCn[i]); |
||||||
|
|
||||||
|
merge(eta_difCn, difEtaSrc); |
||||||
|
difEtaSrc = difEtaSrc.reshape(1, (int)difEtaSrc.total()); |
||||||
|
CV_DbgAssert(difEtaSrc.cols == jointCnNum); |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
Mat1f eigenVec(1, jointCnNum); |
||||||
|
computeEigenVector(difEtaSrc, cluster, eigenVec, num_pca_iterations_, initVec); |
||||||
|
|
||||||
|
Mat1f difOreientation; |
||||||
|
gemm(difEtaSrc, eigenVec, 1, noArray(), 0, difOreientation, GEMM_2_T); |
||||||
|
difOreientation = difOreientation.reshape(1, srcSize.height); |
||||||
|
CV_DbgAssert(difOreientation.size() == srcSize); |
||||||
|
|
||||||
|
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 computeEigenVector(const Mat1f& X, const Mat1b& mask, Mat1f& dst, int num_pca_iterations, const Mat1f& rand_vec) |
||||||
|
{ |
||||||
|
CV_DbgAssert( X.cols == rand_vec.cols ); |
||||||
|
CV_DbgAssert( X.rows == mask.size().area() ); |
||||||
|
CV_DbgAssert( rand_vec.rows == 1 ); |
||||||
|
|
||||||
|
dst.create(rand_vec.size()); |
||||||
|
rand_vec.copyTo(dst); |
||||||
|
|
||||||
|
Mat1f t(X.size()); |
||||||
|
|
||||||
|
float* dst_row = dst[0]; |
||||||
|
|
||||||
|
for (int i = 0; i < num_pca_iterations; ++i) |
||||||
|
{ |
||||||
|
t.setTo(Scalar::all(0)); |
||||||
|
|
||||||
|
for (int y = 0, ind = 0; y < mask.rows; ++y) |
||||||
|
{ |
||||||
|
const uchar* mask_row = mask[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < mask.cols; ++x, ++ind) |
||||||
|
{ |
||||||
|
if (mask_row[x]) |
||||||
|
{ |
||||||
|
const float* X_row = X[ind]; |
||||||
|
float* t_row = t[ind]; |
||||||
|
|
||||||
|
float dots = 0.0; |
||||||
|
for (int c = 0; c < X.cols; ++c) |
||||||
|
dots += dst_row[c] * X_row[c]; |
||||||
|
|
||||||
|
for (int c = 0; c < X.cols; ++c) |
||||||
|
t_row[c] = dots * X_row[c]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dst.setTo(0.0); |
||||||
|
for (int k = 0; k < X.rows; ++k) |
||||||
|
{ |
||||||
|
const float* t_row = t[k]; |
||||||
|
|
||||||
|
for (int c = 0; c < X.cols; ++c) |
||||||
|
{ |
||||||
|
dst_row[c] += t_row[c]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
double n = norm(dst); |
||||||
|
divide(dst, n, dst); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace cv |
||||||
|
{ |
||||||
|
namespace ximgproc |
||||||
|
{ |
||||||
|
|
||||||
|
Ptr<AdaptiveManifoldFilter> AdaptiveManifoldFilter::create() |
||||||
|
{ |
||||||
|
return Ptr<AdaptiveManifoldFilter>(new AdaptiveManifoldFilterN()); |
||||||
|
} |
||||||
|
|
||||||
|
CV_EXPORTS_W |
||||||
|
Ptr<AdaptiveManifoldFilter> createAMFilter(double sigma_s, double sigma_r, bool adjust_outliers) |
||||||
|
{ |
||||||
|
Ptr<AdaptiveManifoldFilter> amf(new AdaptiveManifoldFilterN()); |
||||||
|
|
||||||
|
amf->set("sigma_s", sigma_s); |
||||||
|
amf->set("sigma_r", sigma_r); |
||||||
|
amf->set("adjust_outliers", adjust_outliers); |
||||||
|
|
||||||
|
return amf; |
||||||
|
} |
||||||
|
|
||||||
|
CV_EXPORTS_W |
||||||
|
void amFilter(InputArray joint, InputArray src, OutputArray dst, double sigma_s, double sigma_r, bool adjust_outliers) |
||||||
|
{ |
||||||
|
Ptr<AdaptiveManifoldFilter> amf = createAMFilter(sigma_s, sigma_r, adjust_outliers); |
||||||
|
amf->filter(src, dst, joint); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
#include "precomp.hpp" |
||||||
|
#include "dtfilter_cpu.hpp" |
||||||
|
|
||||||
|
namespace cv |
||||||
|
{ |
||||||
|
namespace ximgproc |
||||||
|
{ |
||||||
|
|
||||||
|
CV_EXPORTS_W |
||||||
|
Ptr<DTFilter> createDTFilter(InputArray guide, double sigmaSpatial, double sigmaColor, int mode, int numIters) |
||||||
|
{ |
||||||
|
return Ptr<DTFilter>(DTFilterCPU::create(guide, sigmaSpatial, sigmaColor, mode, numIters)); |
||||||
|
} |
||||||
|
|
||||||
|
CV_EXPORTS_W |
||||||
|
void dtFilter(InputArray guide, InputArray src, OutputArray dst, double sigmaSpatial, double sigmaColor, int mode, int numIters) |
||||||
|
{ |
||||||
|
Ptr<DTFilterCPU> dtf = DTFilterCPU::create(guide, sigmaSpatial, sigmaColor, mode, numIters); |
||||||
|
dtf->setSingleFilterCall(true); |
||||||
|
dtf->filter(src, dst); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
#include "precomp.hpp" |
||||||
|
#include "dtfilter_cpu.hpp" |
||||||
|
|
||||||
|
namespace cv |
||||||
|
{ |
||||||
|
namespace ximgproc |
||||||
|
{ |
||||||
|
|
||||||
|
typedef Vec<uchar, 1> Vec1b; |
||||||
|
typedef Vec<float, 1> Vec1f; |
||||||
|
|
||||||
|
Ptr<DTFilterCPU> DTFilterCPU::create(InputArray guide, double sigmaSpatial, double sigmaColor, int mode, int numIters) |
||||||
|
{ |
||||||
|
Ptr<DTFilterCPU> dtf(new DTFilterCPU()); |
||||||
|
dtf->init(guide, sigmaSpatial, sigmaColor, mode, numIters); |
||||||
|
return dtf; |
||||||
|
} |
||||||
|
|
||||||
|
Ptr<DTFilterCPU> DTFilterCPU::createRF(InputArray adistHor, InputArray adistVert, double sigmaSpatial, double sigmaColor, int numIters /*= 3*/) |
||||||
|
{ |
||||||
|
Mat adh = adistHor.getMat(); |
||||||
|
Mat adv = adistVert.getMat(); |
||||||
|
CV_Assert(adh.type() == CV_32FC1 && adv.type() == CV_32FC1 && adh.rows == adv.rows + 1 && adh.cols == adv.cols - 1); |
||||||
|
|
||||||
|
Ptr<DTFilterCPU> dtf(new DTFilterCPU()); |
||||||
|
dtf->release(); |
||||||
|
dtf->mode = DTF_RF; |
||||||
|
dtf->numIters = std::max(1, numIters); |
||||||
|
|
||||||
|
dtf->h = adh.rows; |
||||||
|
dtf->w = adh.cols + 1; |
||||||
|
|
||||||
|
dtf->sigmaSpatial = std::max(1.0f, (float)sigmaSpatial); |
||||||
|
dtf->sigmaColor = std::max(0.01f, (float)sigmaColor); |
||||||
|
|
||||||
|
dtf->a0distHor = adh; |
||||||
|
dtf->a0distVert = adv; |
||||||
|
|
||||||
|
return dtf; |
||||||
|
} |
||||||
|
|
||||||
|
void DTFilterCPU::init(InputArray guide_, double sigmaSpatial_, double sigmaColor_, int mode_, int numIters_) |
||||||
|
{ |
||||||
|
Mat guide = guide_.getMat(); |
||||||
|
|
||||||
|
int cn = guide.channels(); |
||||||
|
int depth = guide.depth(); |
||||||
|
|
||||||
|
CV_Assert(cn <= 4); |
||||||
|
CV_Assert((depth == CV_8U || depth == CV_32F) && !guide.empty()); |
||||||
|
|
||||||
|
#define CREATE_DTF(Vect) init_<Vect>(guide, sigmaSpatial_, sigmaColor_, mode_, numIters_); |
||||||
|
|
||||||
|
if (cn == 1) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
CREATE_DTF(Vec1b); |
||||||
|
if (depth == CV_32F) |
||||||
|
CREATE_DTF(Vec1f); |
||||||
|
} |
||||||
|
else if (cn == 2) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
CREATE_DTF(Vec2b); |
||||||
|
if (depth == CV_32F) |
||||||
|
CREATE_DTF(Vec2f); |
||||||
|
} |
||||||
|
else if (cn == 3) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
CREATE_DTF(Vec3b); |
||||||
|
if (depth == CV_32F) |
||||||
|
CREATE_DTF(Vec3f); |
||||||
|
} |
||||||
|
else if (cn == 4) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
CREATE_DTF(Vec4b); |
||||||
|
if (depth == CV_32F) |
||||||
|
CREATE_DTF(Vec4f); |
||||||
|
} |
||||||
|
|
||||||
|
#undef CREATE_DTF |
||||||
|
} |
||||||
|
|
||||||
|
void DTFilterCPU::filter(InputArray src_, OutputArray dst_, int dDepth) |
||||||
|
{ |
||||||
|
Mat src = src_.getMat(); |
||||||
|
dst_.create(src.size(), src.type()); |
||||||
|
Mat& dst = dst_.getMatRef(); |
||||||
|
|
||||||
|
int cn = src.channels(); |
||||||
|
int depth = src.depth(); |
||||||
|
|
||||||
|
CV_Assert(cn <= 4 && (depth == CV_8U || depth == CV_32F)); |
||||||
|
|
||||||
|
if (cn == 1) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
filter_<Vec1b>(src, dst, dDepth); |
||||||
|
if (depth == CV_32F) |
||||||
|
filter_<Vec1f>(src, dst, dDepth); |
||||||
|
} |
||||||
|
else if (cn == 2) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
filter_<Vec2b>(src, dst, dDepth); |
||||||
|
if (depth == CV_32F) |
||||||
|
filter_<Vec2f>(src, dst, dDepth); |
||||||
|
} |
||||||
|
else if (cn == 3) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
filter_<Vec3b>(src, dst, dDepth); |
||||||
|
if (depth == CV_32F) |
||||||
|
filter_<Vec3f>(src, dst, dDepth); |
||||||
|
} |
||||||
|
else if (cn == 4) |
||||||
|
{ |
||||||
|
if (depth == CV_8U) |
||||||
|
filter_<Vec4b>(src, dst, dDepth); |
||||||
|
if (depth == CV_32F) |
||||||
|
filter_<Vec4f>(src, dst, dDepth); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DTFilterCPU::setSingleFilterCall(bool value) |
||||||
|
{ |
||||||
|
singleFilterCall = value; |
||||||
|
} |
||||||
|
|
||||||
|
void DTFilterCPU::release() |
||||||
|
{ |
||||||
|
if (mode == -1) return; |
||||||
|
|
||||||
|
idistHor.release(); |
||||||
|
idistVert.release(); |
||||||
|
|
||||||
|
distHor.release(); |
||||||
|
distVert.release(); |
||||||
|
|
||||||
|
a0distHor.release(); |
||||||
|
a0distVert.release(); |
||||||
|
|
||||||
|
adistHor.release(); |
||||||
|
adistVert.release(); |
||||||
|
} |
||||||
|
|
||||||
|
Mat DTFilterCPU::getWExtendedMat(int h, int w, int type, int brdleft /*= 0*/, int brdRight /*= 0*/, int cacheAlign /*= 0*/) |
||||||
|
{ |
||||||
|
int wrapperCols = w + brdleft + brdRight; |
||||||
|
if (cacheAlign > 0) |
||||||
|
wrapperCols += ((wrapperCols + cacheAlign-1) / cacheAlign)*cacheAlign; |
||||||
|
Mat mat(h, wrapperCols, type); |
||||||
|
return mat(Range::all(), Range(brdleft, w + brdleft)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Range DTFilterCPU::getWorkRangeByThread(const Range& itemsRange, const Range& rangeThread, int declaredNumThreads) |
||||||
|
{ |
||||||
|
if (declaredNumThreads <= 0) |
||||||
|
declaredNumThreads = cv::getNumThreads(); |
||||||
|
|
||||||
|
int chunk = itemsRange.size() / declaredNumThreads; |
||||||
|
int start = itemsRange.start + chunk * rangeThread.start; |
||||||
|
int end = itemsRange.start + ((rangeThread.end >= declaredNumThreads) ? itemsRange.size() : chunk * rangeThread.end); |
||||||
|
|
||||||
|
return Range(start, end); |
||||||
|
} |
||||||
|
|
||||||
|
Range DTFilterCPU::getWorkRangeByThread(int items, const Range& rangeThread, int declaredNumThreads) |
||||||
|
{ |
||||||
|
return getWorkRangeByThread(Range(0, items), rangeThread, declaredNumThreads); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,258 @@ |
|||||||
|
#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); |
||||||
|
|
||||||
|
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; |
||||||
|
}; |
||||||
|
|
||||||
|
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; |
||||||
|
}; |
||||||
|
|
||||||
|
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; |
||||||
|
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; |
||||||
|
#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; |
||||||
|
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; |
||||||
|
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; |
||||||
|
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; |
||||||
|
Range getRange() const { return Range(0, guide.rows - 1); } |
||||||
|
~ComputeA0DTVert_ParBody(); |
||||||
|
}; |
||||||
|
|
||||||
|
protected: /*Auxiliary implementation functions*/ |
||||||
|
|
||||||
|
static Range getWorkRangeByThread(const Range& itemsRange, const Range& rangeThread, int maxThreads = 0); |
||||||
|
static Range getWorkRangeByThread(int items, const Range& rangeThread, int maxThreads = 0); |
||||||
|
|
||||||
|
template<typename SrcVec> |
||||||
|
static void prepareSrcImg_IC(const Mat& src, Mat& inner, Mat& outer); |
||||||
|
|
||||||
|
static Mat getWExtendedMat(int h, int w, int type, int brdleft = 0, int brdRight = 0, int cacheAlign = 0); |
||||||
|
|
||||||
|
template<typename SrcVec, typename SrcWorkVec> |
||||||
|
static void integrateSparseRow(const SrcVec *src, const float *dist, SrcWorkVec *dst, int cols); |
||||||
|
|
||||||
|
template<typename SrcVec, typename SrcWorkVec> |
||||||
|
static void integrateRow(const SrcVec *src, SrcWorkVec *dst, int cols); |
||||||
|
|
||||||
|
inline static int getLeftBound(IDistType *idist, int pos, IDistType searchValue) |
||||||
|
{ |
||||||
|
while (idist[pos] < searchValue) |
||||||
|
pos++; |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
inline static int getRightBound(IDistType *idist, int pos, IDistType searchValue) |
||||||
|
{ |
||||||
|
while (idist[pos + 1] < searchValue) |
||||||
|
pos++; |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T, typename T1, typename T2, int n> |
||||||
|
inline static T norm1(const cv::Vec<T1, n>& v1, const cv::Vec<T2, n>& v2) |
||||||
|
{ |
||||||
|
T sum = (T) 0; |
||||||
|
for (int i = 0; i < n; i++) |
||||||
|
sum += std::abs( (T)v1[i] - (T)v2[i] ); |
||||||
|
return sum; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/*One-line template wrappers for DT call*/ |
||||||
|
|
||||||
|
template<typename GuideVec, typename SrcVec> |
||||||
|
void domainTransformFilter( const Mat_<GuideVec>& guide, |
||||||
|
const Mat_<SrcVec>& source, |
||||||
|
Mat& dst, |
||||||
|
double sigmaSpatial, double sigmaColor, |
||||||
|
int mode = DTF_NC, int numPasses = 3 |
||||||
|
); |
||||||
|
|
||||||
|
template<typename GuideVec, typename SrcVec> |
||||||
|
void domainTransformFilter( const Mat& guide, |
||||||
|
const Mat& source, |
||||||
|
Mat& dst, |
||||||
|
double sigmaSpatial, double sigmaColor, |
||||||
|
int mode = DTF_NC, int numPasses = 3 |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#include "dtfilter_cpu.inl.hpp" |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,588 @@ |
|||||||
|
#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() == cv::DataType<GuideVec>::type); |
||||||
|
|
||||||
|
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() == SrcVec::type ); |
||||||
|
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 == WorkVec::depth && (mode == DTF_NC || mode == DTF_RF)); |
||||||
|
if (useDstAsRes) |
||||||
|
{ |
||||||
|
dst.create(h, w, WorkVec::type); |
||||||
|
res = dst; |
||||||
|
} |
||||||
|
|
||||||
|
if (mode == DTF_NC) |
||||||
|
{ |
||||||
|
Mat resT(src.cols, src.rows, WorkVec::type); |
||||||
|
src.convertTo(res, WorkVec::type); |
||||||
|
|
||||||
|
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, WorkVec::type); |
||||||
|
|
||||||
|
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, WorkVec::type); |
||||||
|
Mat dstOutT(src.cols, src.rows + 2, WorkVec::type); |
||||||
|
|
||||||
|
dst = dstOut(Range::all(), Range(1, src.cols+1)); |
||||||
|
dstT = dstOutT(Range::all(), Range(1, src.rows+1)); |
||||||
|
|
||||||
|
src.convertTo(dst, WorkVec::type); |
||||||
|
|
||||||
|
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() == WorkVec::type && dst.type() == WorkVec::type && 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() == WorkVec::type && dst.type() == WorkVec::type && dst.rows == src.cols && dst.cols == src.rows); |
||||||
|
|
||||||
|
#ifdef CV_GET_NUM_THREAD_WORKS_PROPERLY |
||||||
|
isrcBuf.create(cv::getNumThreads(), src.cols + 1, WorkVec::type); |
||||||
|
#else |
||||||
|
isrcBuf.create(src.rows, src.cols + 1, WorkVec::type); |
||||||
|
#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() == WorkVec::type); |
||||||
|
CV_DbgAssert(res.type() == WorkVec::type && 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() == WorkVec::type); |
||||||
|
CV_DbgAssert(res.type() == WorkVec::type && 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, IDistVec::type); |
||||||
|
} |
||||||
|
|
||||||
|
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, IDistVec::type, 1, 1); |
||||||
|
idist = getWExtendedMat(guide.rows, guide.cols + 1, IDistVec::type); |
||||||
|
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, DistVec::type); |
||||||
|
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, DistVec::type); |
||||||
|
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 |
@ -0,0 +1,515 @@ |
|||||||
|
#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 defined(CV_SSE) |
||||||
|
static volatile bool CPU_SUPPORT_SSE1 = cv::checkHardwareSupport(CV_CPU_SSE); |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef CV_SSE2 |
||||||
|
static volatile bool CPU_SUPPORT_SSE2 = cv::checkHardwareSupport(CV_CPU_SSE2); |
||||||
|
#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_(register float *dst, register float *src1, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, register float *src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, float src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, float alpha, float beta, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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_(register float *dst, register float *src1, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, register float *src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, register float *src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, register float *src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, register float *src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *src1, register float *src2, float c0, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *dst, register float *a00, register float *a01, register float *a10, register float *a11, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *a00, register float *a01, register float *a11, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *a1, register float *b1, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *src, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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_(register float *dst, register float *src, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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_(register float *dst, register float *src1, register float *src2, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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(register float *curRow, register float *prevRow, float alphaVal, int w) |
||||||
|
{ |
||||||
|
register int j = 0; |
||||||
|
#ifdef 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,61 @@ |
|||||||
|
#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_(register float *dst, register float *src1, int w); |
||||||
|
|
||||||
|
void mul(register float *dst, register float *src1, register float *src2, int w); |
||||||
|
|
||||||
|
void mul(register float *dst, register float *src1, float src2, int w); |
||||||
|
|
||||||
|
//dst = alpha*src + beta
|
||||||
|
void mad(register float *dst, register float *src1, float alpha, float beta, int w); |
||||||
|
|
||||||
|
void add_mul(register float *dst, register float *src1, register float *src2, int w); |
||||||
|
|
||||||
|
void sub_mul(register float *dst, register float *src1, register float *src2, int w); |
||||||
|
|
||||||
|
void sub_mad(register float *dst, register float *src1, register float *src2, float c0, int w); |
||||||
|
|
||||||
|
void det_2x2(register float *dst, register float *a00, register float *a01, register float *a10, register float *a11, int w); |
||||||
|
|
||||||
|
void div_det_2x2(register float *a00, register float *a01, register float *a11, int w); |
||||||
|
|
||||||
|
void div_1x(register float *a1, register float *b1, int w); |
||||||
|
|
||||||
|
void inv_self(register float *src, int w); |
||||||
|
|
||||||
|
|
||||||
|
void sqr_(register float *dst, register float *src1, int w); |
||||||
|
|
||||||
|
void sqrt_(register float *dst, register float *src, int w); |
||||||
|
|
||||||
|
void sqr_dif(register float *dst, register float *src1, register float *src2, int w); |
||||||
|
|
||||||
|
void add_sqr_dif(register float *dst, register float *src1, register float *src2, int w); |
||||||
|
|
||||||
|
void add_sqr(register float *dst, register float *src1, int w); |
||||||
|
|
||||||
|
void min_(register float *dst, register float *src1, register float *src2, int w); |
||||||
|
|
||||||
|
void rf_vert_row_pass(register float *curRow, register float *prevRow, float alphaVal, int w); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,755 @@ |
|||||||
|
#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); |
||||||
|
|
||||||
|
void filter(InputArray src, OutputArray dst, int dDepth = -1); |
||||||
|
|
||||||
|
protected: |
||||||
|
|
||||||
|
int radius; |
||||||
|
double eps; |
||||||
|
int h, w; |
||||||
|
|
||||||
|
vector<Mat> guideCn; |
||||||
|
vector<Mat> guideCnMean; |
||||||
|
|
||||||
|
SymArray2D<Mat> covarsInv; |
||||||
|
|
||||||
|
int gCnNum; |
||||||
|
|
||||||
|
protected: |
||||||
|
|
||||||
|
GuidedFilterImpl() {} |
||||||
|
|
||||||
|
void init(InputArray guide, int radius, double eps); |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
}; |
||||||
|
|
||||||
|
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; |
||||||
|
}; |
||||||
|
|
||||||
|
struct ComputeCovGuideInv_ParBody : public ParallelLoopBody |
||||||
|
{ |
||||||
|
GuidedFilterImpl &gf; |
||||||
|
SymArray2D<Mat> &covars; |
||||||
|
|
||||||
|
ComputeCovGuideInv_ParBody(GuidedFilterImpl& gf_, SymArray2D<Mat>& covars_); |
||||||
|
|
||||||
|
void operator () (const Range& range) const; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
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; |
||||||
|
}; |
||||||
|
|
||||||
|
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; |
||||||
|
}; |
||||||
|
|
||||||
|
struct ComputeAlpha_ParBody : public ParallelLoopBody |
||||||
|
{ |
||||||
|
GuidedFilterImpl &gf; |
||||||
|
vector<vector<Mat> > α |
||||||
|
vector<vector<Mat> > &covSrc; |
||||||
|
|
||||||
|
ComputeAlpha_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& alpha_, vector<vector<Mat> >& covSrc_) |
||||||
|
: gf(gf_), alpha(alpha_), covSrc(covSrc_) {} |
||||||
|
|
||||||
|
void operator () (const Range& range) const; |
||||||
|
}; |
||||||
|
|
||||||
|
struct ComputeBeta_ParBody : public ParallelLoopBody |
||||||
|
{ |
||||||
|
GuidedFilterImpl &gf; |
||||||
|
vector<vector<Mat> > α |
||||||
|
vector<Mat> &srcCnMean; |
||||||
|
vector<Mat> β |
||||||
|
|
||||||
|
ComputeBeta_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& alpha_, vector<Mat>& srcCnMean_, vector<Mat>& beta_) |
||||||
|
: gf(gf_), alpha(alpha_), srcCnMean(srcCnMean_), beta(beta_) {} |
||||||
|
|
||||||
|
void operator () (const Range& range) const; |
||||||
|
}; |
||||||
|
|
||||||
|
struct ApplyTransform_ParBody : public ParallelLoopBody |
||||||
|
{ |
||||||
|
GuidedFilterImpl &gf; |
||||||
|
vector<vector<Mat> > α |
||||||
|
vector<Mat> β |
||||||
|
|
||||||
|
ApplyTransform_ParBody(GuidedFilterImpl& gf_, vector<vector<Mat> >& alpha_, vector<Mat>& beta_) |
||||||
|
: gf(gf_), alpha(alpha_), beta(beta_) {} |
||||||
|
|
||||||
|
void operator () (const Range& range) const; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
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++) |
||||||
|
{ |
||||||
|
register float *a = covars(k, 0).ptr<float>(i); |
||||||
|
register 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); |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
|
||||||
|
register 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.guideCn[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.w); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
{ |
||||||
|
GuidedFilterImpl *gf = new GuidedFilterImpl(); |
||||||
|
gf->init(guide, radius, eps); |
||||||
|
return Ptr<GuidedFilterImpl>(gf); |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterImpl::init(InputArray guide, int radius_, double eps_) |
||||||
|
{ |
||||||
|
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) ); |
||||||
|
|
||||||
|
radius = radius_; |
||||||
|
eps = eps_; |
||||||
|
|
||||||
|
splitFirstNChannels(guide, guideCn, 3); |
||||||
|
gCnNum = (int)guideCn.size(); |
||||||
|
h = guideCn[0].rows; |
||||||
|
w = guideCn[0].cols; |
||||||
|
|
||||||
|
guideCnMean.resize(gCnNum); |
||||||
|
parConvertToWorkType(guideCn, guideCn); |
||||||
|
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() != h || src.cols() != w) |
||||||
|
{ |
||||||
|
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 (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); |
||||||
|
|
||||||
|
runParBody(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) |
||||||
|
{ |
||||||
|
return Ptr<GuidedFilter>(GuidedFilterImpl::create(guide, radius, eps)); |
||||||
|
} |
||||||
|
|
||||||
|
CV_EXPORTS_W |
||||||
|
void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth) |
||||||
|
{ |
||||||
|
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps); |
||||||
|
gf->filter(src, dst, dDepth); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,366 @@ |
|||||||
|
#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() == JointVec::type && src.type() == dst.type() && src.type() == SrcVec::type); |
||||||
|
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 |
||||||
|
{ |
||||||
|
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() == JointVec::type && src.type() == dst.type() && src.type() == SrcVec::type); |
||||||
|
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 |
||||||
|
{ |
||||||
|
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*0xFF); |
||||||
|
float *expLUT = &expLUTv[0]; |
||||||
|
|
||||||
|
for (int i = 0; i < (int)expLUTv.size(); i++) |
||||||
|
{ |
||||||
|
expLUT[i] = (float)std::exp(i * i * gaussColorCoeff); |
||||||
|
} |
||||||
|
|
||||||
|
Mat jointTemp, srcTemp; |
||||||
|
copyMakeBorder(joint, jointTemp, radius, radius, radius, radius, borderType); |
||||||
|
copyMakeBorder(src, srcTemp, radius, radius, radius, radius, borderType); |
||||||
|
size_t srcElemStep = srcTemp.step / srcTemp.elemSize(); |
||||||
|
size_t jElemStep = jointTemp.step / jointTemp.elemSize(); |
||||||
|
CV_Assert(srcElemStep == jElemStep); |
||||||
|
|
||||||
|
vector<float> spaceWeightsv(d*d); |
||||||
|
vector<int> spaceOfsJointv(d*d); |
||||||
|
float *spaceWeights = &spaceWeightsv[0]; |
||||||
|
int *spaceOfsJoint = &spaceOfsJointv[0]; |
||||||
|
|
||||||
|
int maxk = 0; |
||||||
|
for (int i = -radius; i <= radius; i++) |
||||||
|
{ |
||||||
|
for (int j = -radius; j <= radius; j++) |
||||||
|
{ |
||||||
|
double r2 = i*i + j*j; |
||||||
|
if (r2 > SQR(radius)) |
||||||
|
continue; |
||||||
|
|
||||||
|
spaceWeights[maxk] = (float)std::exp(r2 * gaussSpaceCoeff); |
||||||
|
spaceOfsJoint[maxk] = (int)(i*jElemStep + j); |
||||||
|
maxk++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Range range(0, src.rows); |
||||||
|
if (joint.type() == CV_8UC1) |
||||||
|
{ |
||||||
|
if (src.type() == CV_8UC1) |
||||||
|
{ |
||||||
|
parallel_for_(range, JointBilateralFilter_8u<Vec1b, Vec1b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); |
||||||
|
} |
||||||
|
if (src.type() == CV_8UC3) |
||||||
|
{ |
||||||
|
parallel_for_(range, JointBilateralFilter_8u<Vec1b, Vec3b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (joint.type() == CV_8UC3) |
||||||
|
{ |
||||||
|
if (src.type() == CV_8UC1) |
||||||
|
{ |
||||||
|
parallel_for_(range, JointBilateralFilter_8u<Vec3b, Vec1b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); |
||||||
|
} |
||||||
|
if (src.type() == CV_8UC3) |
||||||
|
{ |
||||||
|
parallel_for_(range, JointBilateralFilter_8u<Vec3b, Vec3b>(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void jointBilateralFilter(InputArray joint_, InputArray src_, OutputArray dst_, int d, double sigmaColor, double sigmaSpace, int borderType) |
||||||
|
{ |
||||||
|
CV_Assert(!src_.empty()); |
||||||
|
|
||||||
|
if (joint_.empty()) |
||||||
|
{ |
||||||
|
bilateralFilter(src_, dst_, d, sigmaColor, sigmaSpace, borderType); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Mat src = src_.getMat(); |
||||||
|
Mat joint = joint_.getMat(); |
||||||
|
|
||||||
|
if (src.data == joint.data) |
||||||
|
{ |
||||||
|
bilateralFilter(src_, dst_, d, sigmaColor, sigmaSpace, borderType); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
CV_Assert(src.size() == joint.size()); |
||||||
|
CV_Assert(src.depth() == joint.depth() && (src.depth() == CV_8U || src.depth() == CV_32F) ); |
||||||
|
|
||||||
|
if (sigmaColor <= 0) |
||||||
|
sigmaColor = 1; |
||||||
|
if (sigmaSpace <= 0) |
||||||
|
sigmaSpace = 1; |
||||||
|
|
||||||
|
int radius; |
||||||
|
if (d <= 0) |
||||||
|
radius = cvRound(sigmaSpace*1.5); |
||||||
|
else |
||||||
|
radius = d / 2; |
||||||
|
radius = std::max(radius, 1); |
||||||
|
|
||||||
|
dst_.create(src.size(), src.type()); |
||||||
|
Mat dst = dst_.getMat(); |
||||||
|
|
||||||
|
if (dst.data == joint.data) |
||||||
|
joint = joint.clone(); |
||||||
|
if (dst.data == src.data) |
||||||
|
src = src.clone(); |
||||||
|
|
||||||
|
int jointCnNum = joint.channels(); |
||||||
|
int srcCnNum = src.channels(); |
||||||
|
|
||||||
|
if ( (srcCnNum == 1 || srcCnNum == 3) && (jointCnNum == 1 || jointCnNum == 3) ) |
||||||
|
{ |
||||||
|
if (joint.depth() == CV_8U) |
||||||
|
{ |
||||||
|
jointBilateralFilter_8u(joint, src, dst, radius, sigmaColor, sigmaSpace, borderType); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
jointBilateralFilter_32f(joint, src, dst, radius, sigmaColor, sigmaSpace, borderType); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CV_Error(Error::BadNumChannels, "Unsupported number of channels"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
#ifndef _OPENCV_EDGEFILTER_PRECOMP_HPP_ |
||||||
|
#define _OPENCV_EDGEFILTER_PRECOMP_HPP_ |
||||||
|
|
||||||
|
#include <opencv2/core.hpp> |
||||||
|
#include <opencv2/core/ocl.hpp> |
||||||
|
#include <opencv2/core/base.hpp> |
||||||
|
#include <opencv2/core/utility.hpp> |
||||||
|
#include <opencv2/core/cvdef.h> |
||||||
|
#include <opencv2/core/core_c.h> |
||||||
|
#include <opencv2/core/private.hpp> |
||||||
|
#include <opencv2/imgproc.hpp> |
||||||
|
|
||||||
|
#include <opencv2/ximgproc.hpp> |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,174 @@ |
|||||||
|
#include "test_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace std::tr1; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
#ifndef SQR |
||||||
|
#define SQR(x) ((x)*(x)) |
||||||
|
#endif |
||||||
|
|
||||||
|
static string getOpenCVExtraDir() |
||||||
|
{ |
||||||
|
return cvtest::TS::ptr()->get_data_path(); |
||||||
|
} |
||||||
|
|
||||||
|
static void checkSimilarity(InputArray res, InputArray ref) |
||||||
|
{ |
||||||
|
double normInf = cvtest::norm(res, ref, NORM_INF); |
||||||
|
double normL2 = cvtest::norm(res, ref, NORM_L2) / res.total(); |
||||||
|
|
||||||
|
EXPECT_LE(normInf, 1); |
||||||
|
EXPECT_LE(normL2, 1.0 / 64); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(AdaptiveManifoldTest, SplatSurfaceAccuracy) |
||||||
|
{ |
||||||
|
RNG rnd(0); |
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) |
||||||
|
{ |
||||||
|
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024)); |
||||||
|
|
||||||
|
int guideCn = rnd.uniform(1, 8); |
||||||
|
Mat guide(sz, CV_MAKE_TYPE(CV_32F, guideCn)); |
||||||
|
randu(guide, 0, 1); |
||||||
|
|
||||||
|
Scalar surfaceValue; |
||||||
|
int srcCn = rnd.uniform(1, 4); |
||||||
|
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255); |
||||||
|
Mat src(sz, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue); |
||||||
|
|
||||||
|
double sigma_s = rnd.uniform(1.0, 50.0); |
||||||
|
double sigma_r = rnd.uniform(0.1, 0.9); |
||||||
|
|
||||||
|
Mat res; |
||||||
|
amFilter(guide, src, res, sigma_s, sigma_r, false); |
||||||
|
|
||||||
|
double normInf = cvtest::norm(src, res, NORM_INF); |
||||||
|
EXPECT_EQ(normInf, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST(AdaptiveManifoldTest, AuthorsReferenceAccuracy) |
||||||
|
{ |
||||||
|
String srcImgPath = "cv/edgefilter/kodim23.png"; |
||||||
|
|
||||||
|
String refPaths[] = |
||||||
|
{ |
||||||
|
"cv/edgefilter/amf/kodim23_amf_ss5_sr0.3_ref.png", |
||||||
|
"cv/edgefilter/amf/kodim23_amf_ss30_sr0.1_ref.png", |
||||||
|
"cv/edgefilter/amf/kodim23_amf_ss50_sr0.3_ref.png" |
||||||
|
}; |
||||||
|
|
||||||
|
pair<double, double> refParams[] =
|
||||||
|
{ |
||||||
|
make_pair(5.0, 0.3), |
||||||
|
make_pair(30.0, 0.1), |
||||||
|
make_pair(50.0, 0.3) |
||||||
|
}; |
||||||
|
|
||||||
|
String refOutliersPaths[] =
|
||||||
|
{ |
||||||
|
"cv/edgefilter/amf/kodim23_amf_ss5_sr0.1_outliers_ref.png", |
||||||
|
"cv/edgefilter/amf/kodim23_amf_ss15_sr0.3_outliers_ref.png", |
||||||
|
"cv/edgefilter/amf/kodim23_amf_ss50_sr0.5_outliers_ref.png" |
||||||
|
}; |
||||||
|
|
||||||
|
pair<double, double> refOutliersParams[] = |
||||||
|
{ |
||||||
|
make_pair(5.0, 0.1), |
||||||
|
make_pair(15.0, 0.3), |
||||||
|
make_pair(50.0, 0.5), |
||||||
|
}; |
||||||
|
|
||||||
|
Mat srcImg = imread(getOpenCVExtraDir() + srcImgPath); |
||||||
|
ASSERT_TRUE(!srcImg.empty()); |
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) |
||||||
|
{ |
||||||
|
Mat refRes = imread(getOpenCVExtraDir() + refPaths[i]); |
||||||
|
double sigma_s = refParams[i].first; |
||||||
|
double sigma_r = refParams[i].second; |
||||||
|
ASSERT_TRUE(!refRes.empty()); |
||||||
|
|
||||||
|
Mat res; |
||||||
|
Ptr<AdaptiveManifoldFilter> amf = createAMFilter(sigma_s, sigma_r, false); |
||||||
|
amf->setBool("use_RNG", false); |
||||||
|
amf->filter(srcImg, res, srcImg); |
||||||
|
amf->collectGarbage(); |
||||||
|
|
||||||
|
checkSimilarity(res, refRes); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) |
||||||
|
{ |
||||||
|
Mat refRes = imread(getOpenCVExtraDir() + refOutliersPaths[i]); |
||||||
|
double sigma_s = refOutliersParams[i].first; |
||||||
|
double sigma_r = refOutliersParams[i].second; |
||||||
|
ASSERT_TRUE(!refRes.empty()); |
||||||
|
|
||||||
|
Mat res; |
||||||
|
Ptr<AdaptiveManifoldFilter> amf = createAMFilter(sigma_s, sigma_r, true); |
||||||
|
amf->setBool("use_RNG", false); |
||||||
|
amf->filter(srcImg, res, srcImg); |
||||||
|
amf->collectGarbage(); |
||||||
|
|
||||||
|
checkSimilarity(res, refRes); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
typedef tuple<string, string> AMRefTestParams; |
||||||
|
typedef TestWithParam<AMRefTestParams> AdaptiveManifoldRefImplTest; |
||||||
|
|
||||||
|
Ptr<AdaptiveManifoldFilter> createAMFilterRefImpl(double sigma_s, double sigma_r, bool adjust_outliers = false); |
||||||
|
|
||||||
|
TEST_P(AdaptiveManifoldRefImplTest, RefImplAccuracy) |
||||||
|
{ |
||||||
|
AMRefTestParams params = GetParam(); |
||||||
|
|
||||||
|
string guideFileName = get<0>(params); |
||||||
|
string srcFileName = get<1>(params); |
||||||
|
|
||||||
|
Mat guide = imread(getOpenCVExtraDir() + guideFileName); |
||||||
|
Mat src = imread(getOpenCVExtraDir() + srcFileName); |
||||||
|
ASSERT_TRUE(!guide.empty() && !src.empty()); |
||||||
|
|
||||||
|
int seed = 10 * (int)guideFileName.length() + (int)srcFileName.length(); |
||||||
|
RNG rnd(seed); |
||||||
|
|
||||||
|
//inconsistent downsample/upsample operations in reference implementation
|
||||||
|
Size dstSize((guide.cols + 15) & ~15, (guide.rows + 15) & ~15); |
||||||
|
|
||||||
|
resize(guide, guide, dstSize); |
||||||
|
resize(src, src, dstSize); |
||||||
|
|
||||||
|
for (int iter = 0; iter < 6; iter++) |
||||||
|
{ |
||||||
|
double sigma_s = rnd.uniform(1.0, 50.0); |
||||||
|
double sigma_r = rnd.uniform(0.1, 0.9); |
||||||
|
bool adjust_outliers = (iter % 2 == 0); |
||||||
|
|
||||||
|
Mat res; |
||||||
|
amFilter(guide, src, res, sigma_s, sigma_r, adjust_outliers); |
||||||
|
|
||||||
|
Mat resRef; |
||||||
|
Ptr<AdaptiveManifoldFilter> amf = createAMFilterRefImpl(sigma_s, sigma_r, adjust_outliers); |
||||||
|
amf->filter(src, resRef, guide); |
||||||
|
|
||||||
|
checkSimilarity(res, resRef); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(TypicalSet, AdaptiveManifoldRefImplTest,
|
||||||
|
Combine( |
||||||
|
Values("cv/shared/lena.png", "cv/edgefilter/kodim23.png", "cv/npr/test4.png"), |
||||||
|
Values("cv/shared/lena.png", "cv/edgefilter/kodim23.png", "cv/npr/test4.png") |
||||||
|
)); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,948 @@ |
|||||||
|
/*
|
||||||
|
* The MIT License(MIT) |
||||||
|
*
|
||||||
|
* Copyright(c) 2013 Vladislav Vinogradov |
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||||
|
* this software and associated documentation files(the "Software"), to deal in |
||||||
|
* the Software without restriction, including without limitation the rights to |
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of |
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so, |
||||||
|
* subject to the following conditions : |
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR |
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "test_precomp.hpp" |
||||||
|
#include <opencv2/core/private.hpp> |
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
namespace |
||||||
|
{ |
||||||
|
|
||||||
|
using std::numeric_limits; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
struct Buf |
||||||
|
{ |
||||||
|
Mat_<Point3f> eta_1; |
||||||
|
Mat_<uchar> cluster_1; |
||||||
|
|
||||||
|
Mat_<Point3f> tilde_dst; |
||||||
|
Mat_<float> alpha; |
||||||
|
Mat_<Point3f> diff; |
||||||
|
Mat_<Point3f> dst; |
||||||
|
|
||||||
|
Mat_<float> V; |
||||||
|
|
||||||
|
Mat_<Point3f> dIcdx; |
||||||
|
Mat_<Point3f> dIcdy; |
||||||
|
Mat_<float> dIdx; |
||||||
|
Mat_<float> dIdy; |
||||||
|
Mat_<float> dHdx; |
||||||
|
Mat_<float> dVdy; |
||||||
|
|
||||||
|
Mat_<float> t; |
||||||
|
|
||||||
|
Mat_<float> theta_masked; |
||||||
|
Mat_<Point3f> mul; |
||||||
|
Mat_<Point3f> numerator; |
||||||
|
Mat_<float> denominator; |
||||||
|
Mat_<Point3f> numerator_filtered; |
||||||
|
Mat_<float> denominator_filtered; |
||||||
|
|
||||||
|
Mat_<Point3f> X; |
||||||
|
Mat_<Point3f> eta_k_small; |
||||||
|
Mat_<Point3f> eta_k_big; |
||||||
|
Mat_<Point3f> X_squared; |
||||||
|
Mat_<float> pixel_dist_to_manifold_squared; |
||||||
|
Mat_<float> gaussian_distance_weights; |
||||||
|
Mat_<Point3f> Psi_splat; |
||||||
|
Mat_<Vec4f> Psi_splat_joined; |
||||||
|
Mat_<Vec4f> Psi_splat_joined_resized; |
||||||
|
Mat_<Vec4f> blurred_projected_values; |
||||||
|
Mat_<Point3f> w_ki_Psi_blur; |
||||||
|
Mat_<float> w_ki_Psi_blur_0; |
||||||
|
Mat_<Point3f> w_ki_Psi_blur_resized; |
||||||
|
Mat_<float> w_ki_Psi_blur_0_resized; |
||||||
|
Mat_<float> rand_vec; |
||||||
|
Mat_<float> v1; |
||||||
|
Mat_<float> Nx_v1_mult; |
||||||
|
Mat_<float> theta; |
||||||
|
|
||||||
|
std::vector<Mat_<Point3f> > eta_minus; |
||||||
|
std::vector<Mat_<uchar> > cluster_minus; |
||||||
|
std::vector<Mat_<Point3f> > eta_plus; |
||||||
|
std::vector<Mat_<uchar> > cluster_plus; |
||||||
|
|
||||||
|
void release(); |
||||||
|
}; |
||||||
|
|
||||||
|
void Buf::release() |
||||||
|
{ |
||||||
|
eta_1.release(); |
||||||
|
cluster_1.release(); |
||||||
|
|
||||||
|
tilde_dst.release(); |
||||||
|
alpha.release(); |
||||||
|
diff.release(); |
||||||
|
dst.release(); |
||||||
|
|
||||||
|
V.release(); |
||||||
|
|
||||||
|
dIcdx.release(); |
||||||
|
dIcdy.release(); |
||||||
|
dIdx.release(); |
||||||
|
dIdy.release(); |
||||||
|
dHdx.release(); |
||||||
|
dVdy.release(); |
||||||
|
|
||||||
|
t.release(); |
||||||
|
|
||||||
|
theta_masked.release(); |
||||||
|
mul.release(); |
||||||
|
numerator.release(); |
||||||
|
denominator.release(); |
||||||
|
numerator_filtered.release(); |
||||||
|
denominator_filtered.release(); |
||||||
|
|
||||||
|
X.release(); |
||||||
|
eta_k_small.release(); |
||||||
|
eta_k_big.release(); |
||||||
|
X_squared.release(); |
||||||
|
pixel_dist_to_manifold_squared.release(); |
||||||
|
gaussian_distance_weights.release(); |
||||||
|
Psi_splat.release(); |
||||||
|
Psi_splat_joined.release(); |
||||||
|
Psi_splat_joined_resized.release(); |
||||||
|
blurred_projected_values.release(); |
||||||
|
w_ki_Psi_blur.release(); |
||||||
|
w_ki_Psi_blur_0.release(); |
||||||
|
w_ki_Psi_blur_resized.release(); |
||||||
|
w_ki_Psi_blur_0_resized.release(); |
||||||
|
rand_vec.release(); |
||||||
|
v1.release(); |
||||||
|
Nx_v1_mult.release(); |
||||||
|
theta.release(); |
||||||
|
|
||||||
|
eta_minus.clear(); |
||||||
|
cluster_minus.clear(); |
||||||
|
eta_plus.clear(); |
||||||
|
cluster_plus.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
class AdaptiveManifoldFilterRefImpl : public AdaptiveManifoldFilter |
||||||
|
{ |
||||||
|
public: |
||||||
|
AlgorithmInfo* info() const; |
||||||
|
|
||||||
|
AdaptiveManifoldFilterRefImpl(); |
||||||
|
|
||||||
|
void filter(InputArray src, OutputArray dst, InputArray joint); |
||||||
|
|
||||||
|
void collectGarbage(); |
||||||
|
|
||||||
|
protected: |
||||||
|
bool adjust_outliers_; |
||||||
|
double sigma_s_; |
||||||
|
double sigma_r_; |
||||||
|
int tree_height_; |
||||||
|
int num_pca_iterations_; |
||||||
|
bool useRNG; |
||||||
|
|
||||||
|
private: |
||||||
|
void buildManifoldsAndPerformFiltering(const Mat_<Point3f>& eta_k, const Mat_<uchar>& cluster_k, int current_tree_level); |
||||||
|
|
||||||
|
Buf buf_; |
||||||
|
|
||||||
|
Mat_<Point3f> src_f_; |
||||||
|
Mat_<Point3f> src_joint_f_; |
||||||
|
|
||||||
|
Mat_<Point3f> sum_w_ki_Psi_blur_; |
||||||
|
Mat_<float> sum_w_ki_Psi_blur_0_; |
||||||
|
|
||||||
|
Mat_<float> min_pixel_dist_to_manifold_squared_; |
||||||
|
|
||||||
|
RNG rng_; |
||||||
|
|
||||||
|
int cur_tree_height_; |
||||||
|
float sigma_r_over_sqrt_2_; |
||||||
|
}; |
||||||
|
|
||||||
|
AdaptiveManifoldFilterRefImpl::AdaptiveManifoldFilterRefImpl() |
||||||
|
{ |
||||||
|
sigma_s_ = 16.0; |
||||||
|
sigma_r_ = 0.2; |
||||||
|
tree_height_ = -1; |
||||||
|
num_pca_iterations_ = 1; |
||||||
|
adjust_outliers_ = false; |
||||||
|
useRNG = true; |
||||||
|
} |
||||||
|
|
||||||
|
void AdaptiveManifoldFilterRefImpl::collectGarbage() |
||||||
|
{ |
||||||
|
buf_.release(); |
||||||
|
|
||||||
|
src_f_.release(); |
||||||
|
src_joint_f_.release(); |
||||||
|
|
||||||
|
sum_w_ki_Psi_blur_.release(); |
||||||
|
sum_w_ki_Psi_blur_0_.release(); |
||||||
|
|
||||||
|
min_pixel_dist_to_manifold_squared_.release(); |
||||||
|
} |
||||||
|
|
||||||
|
CV_INIT_ALGORITHM(AdaptiveManifoldFilterRefImpl, "AdaptiveManifoldFilterRefImpl", |
||||||
|
obj.info()->addParam(obj, "sigma_s", obj.sigma_s_, false, 0, 0, "Filter spatial standard deviation"); |
||||||
|
obj.info()->addParam(obj, "sigma_r", obj.sigma_r_, false, 0, 0, "Filter range standard deviation"); |
||||||
|
obj.info()->addParam(obj, "tree_height", obj.tree_height_, false, 0, 0, "Height of the manifold tree (default = -1 : automatically computed)"); |
||||||
|
obj.info()->addParam(obj, "num_pca_iterations", obj.num_pca_iterations_, false, 0, 0, "Number of iterations to computed the eigenvector v1"); |
||||||
|
obj.info()->addParam(obj, "adjust_outliers", obj.adjust_outliers_, false, 0, 0, "Specify supress outliers using Eq. 9 or not"); |
||||||
|
obj.info()->addParam(obj, "use_RNG", obj.useRNG, false, 0, 0, "Specify use random to compute eigenvector or not");) |
||||||
|
|
||||||
|
inline double Log2(double n) |
||||||
|
{ |
||||||
|
return log(n) / log(2.0); |
||||||
|
} |
||||||
|
|
||||||
|
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))); |
||||||
|
} |
||||||
|
|
||||||
|
void ensureSizeIsEnough(int rows, int cols, int type, Mat& m) |
||||||
|
{ |
||||||
|
if (m.empty() || m.type() != type || m.data != m.datastart) |
||||||
|
m.create(rows, cols, type); |
||||||
|
else |
||||||
|
{ |
||||||
|
const size_t esz = m.elemSize(); |
||||||
|
const ptrdiff_t delta2 = m.dataend - m.datastart; |
||||||
|
|
||||||
|
const size_t minstep = m.cols * esz; |
||||||
|
|
||||||
|
Size wholeSize; |
||||||
|
wholeSize.height = std::max(static_cast<int>((delta2 - minstep) / m.step + 1), m.rows); |
||||||
|
wholeSize.width = std::max(static_cast<int>((delta2 - m.step * (wholeSize.height - 1)) / esz), m.cols); |
||||||
|
|
||||||
|
if (wholeSize.height < rows || wholeSize.width < cols) |
||||||
|
m.create(rows, cols, type); |
||||||
|
else |
||||||
|
{ |
||||||
|
m.cols = cols; |
||||||
|
m.rows = rows; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline void ensureSizeIsEnough(Size size, int type, Mat& m) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(size.height, size.width, type, m); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
inline void ensureSizeIsEnough(int rows, int cols, Mat_<T>& m) |
||||||
|
{ |
||||||
|
if (m.empty() || m.data != m.datastart) |
||||||
|
m.create(rows, cols); |
||||||
|
else |
||||||
|
{ |
||||||
|
const size_t esz = m.elemSize(); |
||||||
|
const ptrdiff_t delta2 = m.dataend - m.datastart; |
||||||
|
|
||||||
|
const size_t minstep = m.cols * esz; |
||||||
|
|
||||||
|
Size wholeSize; |
||||||
|
wholeSize.height = std::max(static_cast<int>((delta2 - minstep) / m.step + 1), m.rows); |
||||||
|
wholeSize.width = std::max(static_cast<int>((delta2 - m.step * (wholeSize.height - 1)) / esz), m.cols); |
||||||
|
|
||||||
|
if (wholeSize.height < rows || wholeSize.width < cols) |
||||||
|
m.create(rows, cols); |
||||||
|
else |
||||||
|
{ |
||||||
|
m.cols = cols; |
||||||
|
m.rows = rows; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
inline void ensureSizeIsEnough(Size size, Mat_<T>& m) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(size.height, size.width, m); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void h_filter(const Mat_<T>& src, Mat_<T>& dst, float sigma) |
||||||
|
{ |
||||||
|
CV_DbgAssert( src.depth() == CV_32F ); |
||||||
|
|
||||||
|
const float a = exp(-sqrt(2.0f) / sigma); |
||||||
|
|
||||||
|
ensureSizeIsEnough(src.size(), dst); |
||||||
|
|
||||||
|
for (int y = 0; y < src.rows; ++y) |
||||||
|
{ |
||||||
|
const T* src_row = src[y]; |
||||||
|
T* dst_row = dst[y]; |
||||||
|
|
||||||
|
dst_row[0] = src_row[0]; |
||||||
|
for (int x = 1; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
//dst_row[x] = src_row[x] + a * (src_row[x - 1] - src_row[x]);
|
||||||
|
dst_row[x] = src_row[x] + a * (dst_row[x - 1] - src_row[x]); //!!!
|
||||||
|
} |
||||||
|
for (int x = src.cols - 2; 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) |
||||||
|
{ |
||||||
|
T* dst_cur_row = dst[y]; |
||||||
|
T* dst_prev_row = dst[y - 1]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
dst_cur_row[x] = dst_cur_row[x] + a * (dst_prev_row[x] - dst_cur_row[x]); |
||||||
|
} |
||||||
|
} |
||||||
|
for (int y = src.rows - 2; y >= 0; --y) |
||||||
|
{ |
||||||
|
T* dst_cur_row = dst[y]; |
||||||
|
T* dst_prev_row = dst[y + 1]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
dst_cur_row[x] = dst_cur_row[x] + a * (dst_prev_row[x] - dst_cur_row[x]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void rdivide(const Mat_<T>& a, const Mat_<float>& b, Mat_<T>& dst) |
||||||
|
{ |
||||||
|
CV_DbgAssert( a.depth() == CV_32F ); |
||||||
|
CV_DbgAssert( a.size() == b.size() ); |
||||||
|
|
||||||
|
ensureSizeIsEnough(a.size(), dst); |
||||||
|
dst.setTo(0); |
||||||
|
|
||||||
|
for (int y = 0; y < a.rows; ++y) |
||||||
|
{ |
||||||
|
const T* a_row = a[y]; |
||||||
|
const float* b_row = b[y]; |
||||||
|
T* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < a.cols; ++x) |
||||||
|
{ |
||||||
|
//if (b_row[x] > numeric_limits<float>::epsilon())
|
||||||
|
dst_row[x] = a_row[x] * (1.0f / b_row[x]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void times(const Mat_<T>& a, const Mat_<float>& b, Mat_<T>& dst) |
||||||
|
{ |
||||||
|
CV_DbgAssert( a.depth() == CV_32F ); |
||||||
|
CV_DbgAssert( a.size() == b.size() ); |
||||||
|
|
||||||
|
ensureSizeIsEnough(a.size(), dst); |
||||||
|
|
||||||
|
for (int y = 0; y < a.rows; ++y) |
||||||
|
{ |
||||||
|
const T* a_row = a[y]; |
||||||
|
const float* b_row = b[y]; |
||||||
|
T* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < a.cols; ++x) |
||||||
|
{ |
||||||
|
dst_row[x] = a_row[x] * b_row[x]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void AdaptiveManifoldFilterRefImpl::filter(InputArray _src, OutputArray _dst, InputArray _joint) |
||||||
|
{ |
||||||
|
const Mat src = _src.getMat(); |
||||||
|
const Mat src_joint = _joint.getMat(); |
||||||
|
|
||||||
|
const Size srcSize = src.size(); |
||||||
|
|
||||||
|
CV_Assert( src.type() == CV_8UC3 ); |
||||||
|
CV_Assert( src_joint.empty() || (src_joint.type() == src.type() && src_joint.size() == srcSize) ); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, src_f_); |
||||||
|
src.convertTo(src_f_, src_f_.type(), 1.0 / 255.0); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, sum_w_ki_Psi_blur_); |
||||||
|
sum_w_ki_Psi_blur_.setTo(Scalar::all(0)); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, sum_w_ki_Psi_blur_0_); |
||||||
|
sum_w_ki_Psi_blur_0_.setTo(Scalar::all(0)); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, min_pixel_dist_to_manifold_squared_); |
||||||
|
min_pixel_dist_to_manifold_squared_.setTo(Scalar::all(numeric_limits<float>::max())); |
||||||
|
|
||||||
|
// If the tree_height was not specified, compute it using Eq. (10) of our paper.
|
||||||
|
cur_tree_height_ = tree_height_ > 0 ? tree_height_ : computeManifoldTreeHeight(sigma_s_, sigma_r_); |
||||||
|
|
||||||
|
// If no joint signal was specified, use the original signal
|
||||||
|
ensureSizeIsEnough(srcSize, src_joint_f_); |
||||||
|
if (src_joint.empty()) |
||||||
|
src_f_.copyTo(src_joint_f_); |
||||||
|
else |
||||||
|
src_joint.convertTo(src_joint_f_, src_joint_f_.type(), 1.0 / 255.0); |
||||||
|
|
||||||
|
// Use the center pixel as seed to random number generation.
|
||||||
|
const double seedCoef = src_joint_f_(src_joint_f_.rows / 2, src_joint_f_.cols / 2).x; |
||||||
|
const uint64 baseCoef = numeric_limits<uint64>::max() / 0xFFFF; |
||||||
|
rng_.state = static_cast<uint64>(baseCoef*seedCoef); |
||||||
|
|
||||||
|
// Dividing the covariance matrix by 2 is equivalent to dividing the standard deviations by sqrt(2).
|
||||||
|
sigma_r_over_sqrt_2_ = static_cast<float>(sigma_r_ / sqrt(2.0)); |
||||||
|
|
||||||
|
// Algorithm 1, Step 1: compute the first manifold by low-pass filtering.
|
||||||
|
h_filter(src_joint_f_, buf_.eta_1, static_cast<float>(sigma_s_)); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, buf_.cluster_1); |
||||||
|
buf_.cluster_1.setTo(Scalar::all(1)); |
||||||
|
|
||||||
|
buf_.eta_minus.resize(cur_tree_height_); |
||||||
|
buf_.cluster_minus.resize(cur_tree_height_); |
||||||
|
buf_.eta_plus.resize(cur_tree_height_); |
||||||
|
buf_.cluster_plus.resize(cur_tree_height_); |
||||||
|
buildManifoldsAndPerformFiltering(buf_.eta_1, buf_.cluster_1, 1); |
||||||
|
|
||||||
|
// Compute the filter response by normalized convolution -- Eq. (4)
|
||||||
|
rdivide(sum_w_ki_Psi_blur_, sum_w_ki_Psi_blur_0_, buf_.tilde_dst); |
||||||
|
|
||||||
|
if (!adjust_outliers_) |
||||||
|
{ |
||||||
|
buf_.tilde_dst.convertTo(_dst, CV_8U, 255.0); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// Adjust the filter response for outlier pixels -- Eq. (10)
|
||||||
|
ensureSizeIsEnough(srcSize, buf_.alpha); |
||||||
|
exp(min_pixel_dist_to_manifold_squared_ * (-0.5 / sigma_r_ / sigma_r_), buf_.alpha); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, buf_.diff); |
||||||
|
subtract(buf_.tilde_dst, src_f_, buf_.diff); |
||||||
|
times(buf_.diff, buf_.alpha, buf_.diff); |
||||||
|
|
||||||
|
ensureSizeIsEnough(srcSize, buf_.dst); |
||||||
|
add(src_f_, buf_.diff, buf_.dst); |
||||||
|
|
||||||
|
buf_.dst.convertTo(_dst, CV_8U, 255.0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline double floor_to_power_of_two(double r) |
||||||
|
{ |
||||||
|
return pow(2.0, floor(Log2(r))); |
||||||
|
} |
||||||
|
|
||||||
|
void channelsSum(const Mat_<Point3f>& src, Mat_<float>& dst) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(src.size(), dst); |
||||||
|
|
||||||
|
for (int y = 0; y < src.rows; ++y) |
||||||
|
{ |
||||||
|
const Point3f* src_row = src[y]; |
||||||
|
float* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
const Point3f src_val = src_row[x]; |
||||||
|
dst_row[x] = src_val.x + src_val.y + src_val.z; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void phi(const Mat_<float>& src, Mat_<float>& dst, float sigma) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(src.size(), dst); |
||||||
|
|
||||||
|
for (int y = 0; y < dst.rows; ++y) |
||||||
|
{ |
||||||
|
const float* src_row = src[y]; |
||||||
|
float* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < dst.cols; ++x) |
||||||
|
{ |
||||||
|
dst_row[x] = exp(-0.5f * src_row[x] / sigma / sigma); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void catCn(const Mat_<Point3f>& a, const Mat_<float>& b, Mat_<Vec4f>& dst) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(a.size(), dst); |
||||||
|
|
||||||
|
for (int y = 0; y < a.rows; ++y) |
||||||
|
{ |
||||||
|
const Point3f* a_row = a[y]; |
||||||
|
const float* b_row = b[y]; |
||||||
|
Vec4f* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < a.cols; ++x) |
||||||
|
{ |
||||||
|
const Point3f a_val = a_row[x]; |
||||||
|
const float b_val = b_row[x]; |
||||||
|
dst_row[x] = Vec4f(a_val.x, a_val.y, a_val.z, b_val); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void diffY(const Mat_<Point3f>& src, Mat_<Point3f>& dst) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(src.rows - 1, src.cols, dst); |
||||||
|
|
||||||
|
for (int y = 0; y < src.rows - 1; ++y) |
||||||
|
{ |
||||||
|
const Point3f* src_cur_row = src[y]; |
||||||
|
const Point3f* src_next_row = src[y + 1]; |
||||||
|
Point3f* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
dst_row[x] = src_next_row[x] - src_cur_row[x]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void diffX(const Mat_<Point3f>& src, Mat_<Point3f>& dst) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(src.rows, src.cols - 1, dst); |
||||||
|
|
||||||
|
for (int y = 0; y < src.rows; ++y) |
||||||
|
{ |
||||||
|
const Point3f* src_row = src[y]; |
||||||
|
Point3f* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols - 1; ++x) |
||||||
|
{ |
||||||
|
dst_row[x] = src_row[x + 1] - src_row[x]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedDomainRecursiveFilter(const Mat_<Vec4f>& I, const Mat_<float>& DH, const Mat_<float>& DV, Mat_<Vec4f>& dst, float sigma, Buf& buf) |
||||||
|
{ |
||||||
|
CV_DbgAssert( I.size() == DH.size() ); |
||||||
|
|
||||||
|
const float a = exp(-sqrt(2.0f) / sigma); |
||||||
|
|
||||||
|
ensureSizeIsEnough(I.size(), dst); |
||||||
|
I.copyTo(dst); |
||||||
|
|
||||||
|
ensureSizeIsEnough(DH.size(), buf.V); |
||||||
|
|
||||||
|
for (int y = 0; y < DH.rows; ++y) |
||||||
|
{ |
||||||
|
const float* D_row = DH[y]; |
||||||
|
float* V_row = buf.V[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < DH.cols; ++x) |
||||||
|
{ |
||||||
|
V_row[x] = pow(a, D_row[x]); |
||||||
|
} |
||||||
|
} |
||||||
|
for (int y = 0; y < I.rows; ++y) |
||||||
|
{ |
||||||
|
const float* V_row = buf.V[y]; |
||||||
|
Vec4f* dst_row = dst[y]; |
||||||
|
|
||||||
|
for (int x = 1; x < I.cols; ++x) |
||||||
|
{ |
||||||
|
Vec4f dst_cur_val = dst_row[x]; |
||||||
|
const Vec4f dst_prev_val = dst_row[x - 1]; |
||||||
|
const float V_val = V_row[x]; |
||||||
|
|
||||||
|
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]); |
||||||
|
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]); |
||||||
|
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]); |
||||||
|
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]); |
||||||
|
|
||||||
|
dst_row[x] = dst_cur_val; |
||||||
|
} |
||||||
|
for (int x = I.cols - 2; x >= 0; --x) |
||||||
|
{ |
||||||
|
Vec4f dst_cur_val = dst_row[x]; |
||||||
|
const Vec4f dst_prev_val = dst_row[x + 1]; |
||||||
|
//const float V_val = V_row[x];
|
||||||
|
const float V_val = V_row[x+1]; |
||||||
|
|
||||||
|
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]); |
||||||
|
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]); |
||||||
|
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]); |
||||||
|
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]); |
||||||
|
|
||||||
|
dst_row[x] = dst_cur_val; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (int y = 0; y < DV.rows; ++y) |
||||||
|
{ |
||||||
|
const float* D_row = DV[y]; |
||||||
|
float* V_row = buf.V[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < DV.cols; ++x) |
||||||
|
{ |
||||||
|
V_row[x] = pow(a, D_row[x]); |
||||||
|
} |
||||||
|
} |
||||||
|
for (int y = 1; y < I.rows; ++y) |
||||||
|
{ |
||||||
|
const float* V_row = buf.V[y]; |
||||||
|
Vec4f* dst_cur_row = dst[y]; |
||||||
|
Vec4f* dst_prev_row = dst[y - 1]; |
||||||
|
|
||||||
|
for (int x = 0; x < I.cols; ++x) |
||||||
|
{ |
||||||
|
Vec4f dst_cur_val = dst_cur_row[x]; |
||||||
|
const Vec4f dst_prev_val = dst_prev_row[x]; |
||||||
|
const float V_val = V_row[x]; |
||||||
|
|
||||||
|
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]); |
||||||
|
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]); |
||||||
|
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]); |
||||||
|
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]); |
||||||
|
|
||||||
|
dst_cur_row[x] = dst_cur_val; |
||||||
|
} |
||||||
|
} |
||||||
|
for (int y = I.rows - 2; y >= 0; --y) |
||||||
|
{ |
||||||
|
//const float* V_row = buf.V[y];
|
||||||
|
const float* V_row = buf.V[y + 1]; |
||||||
|
Vec4f* dst_cur_row = dst[y]; |
||||||
|
Vec4f* dst_prev_row = dst[y + 1]; |
||||||
|
|
||||||
|
for (int x = 0; x < I.cols; ++x) |
||||||
|
{ |
||||||
|
Vec4f dst_cur_val = dst_cur_row[x]; |
||||||
|
const Vec4f dst_prev_val = dst_prev_row[x]; |
||||||
|
const float V_val = V_row[x]; |
||||||
|
|
||||||
|
dst_cur_val[0] += V_val * (dst_prev_val[0] - dst_cur_val[0]); |
||||||
|
dst_cur_val[1] += V_val * (dst_prev_val[1] - dst_cur_val[1]); |
||||||
|
dst_cur_val[2] += V_val * (dst_prev_val[2] - dst_cur_val[2]); |
||||||
|
dst_cur_val[3] += V_val * (dst_prev_val[3] - dst_cur_val[3]); |
||||||
|
|
||||||
|
dst_cur_row[x] = dst_cur_val; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void RF_filter(const Mat_<Vec4f>& src, const Mat_<Point3f>& src_joint, Mat_<Vec4f>& dst, float sigma_s, float sigma_r, Buf& buf) |
||||||
|
{ |
||||||
|
CV_DbgAssert( src_joint.size() == src.size() ); |
||||||
|
|
||||||
|
diffX(src_joint, buf.dIcdx); |
||||||
|
diffY(src_joint, buf.dIcdy); |
||||||
|
|
||||||
|
ensureSizeIsEnough(src.size(), buf.dIdx); |
||||||
|
buf.dIdx.setTo(Scalar::all(0)); |
||||||
|
for (int y = 0; y < src.rows; ++y) |
||||||
|
{ |
||||||
|
const Point3f* dIcdx_row = buf.dIcdx[y]; |
||||||
|
float* dIdx_row = buf.dIdx[y]; |
||||||
|
|
||||||
|
for (int x = 1; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
const Point3f val = dIcdx_row[x - 1]; |
||||||
|
dIdx_row[x] = val.dot(val); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ensureSizeIsEnough(src.size(), buf.dIdy); |
||||||
|
buf.dIdy.setTo(Scalar::all(0)); |
||||||
|
for (int y = 1; y < src.rows; ++y) |
||||||
|
{ |
||||||
|
const Point3f* dIcdy_row = buf.dIcdy[y - 1]; |
||||||
|
float* dIdy_row = buf.dIdy[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
const Point3f val = dIcdy_row[x]; |
||||||
|
dIdy_row[x] = val.dot(val); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ensureSizeIsEnough(buf.dIdx.size(), buf.dHdx); |
||||||
|
buf.dIdx.convertTo(buf.dHdx, buf.dHdx.type(), (sigma_s / sigma_r) * (sigma_s / sigma_r), (sigma_s / sigma_s) * (sigma_s / sigma_s)); |
||||||
|
sqrt(buf.dHdx, buf.dHdx); |
||||||
|
|
||||||
|
ensureSizeIsEnough(buf.dIdy.size(), buf.dVdy); |
||||||
|
buf.dIdy.convertTo(buf.dVdy, buf.dVdy.type(), (sigma_s / sigma_r) * (sigma_s / sigma_r), (sigma_s / sigma_s) * (sigma_s / sigma_s)); |
||||||
|
sqrt(buf.dVdy, buf.dVdy); |
||||||
|
|
||||||
|
ensureSizeIsEnough(src.size(), dst); |
||||||
|
src.copyTo(dst); |
||||||
|
TransformedDomainRecursiveFilter(src, buf.dHdx, buf.dVdy, dst, sigma_s, buf); |
||||||
|
} |
||||||
|
|
||||||
|
void split_3_1(const Mat_<Vec4f>& src, Mat_<Point3f>& dst1, Mat_<float>& dst2) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(src.size(), dst1); |
||||||
|
ensureSizeIsEnough(src.size(), dst2); |
||||||
|
|
||||||
|
for (int y = 0; y < src.rows; ++y) |
||||||
|
{ |
||||||
|
const Vec4f* src_row = src[y]; |
||||||
|
Point3f* dst1_row = dst1[y]; |
||||||
|
float* dst2_row = dst2[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < src.cols; ++x) |
||||||
|
{ |
||||||
|
Vec4f val = src_row[x]; |
||||||
|
dst1_row[x] = Point3f(val[0], val[1], val[2]); |
||||||
|
dst2_row[x] = val[3]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void computeEigenVector(const Mat_<float>& X, const Mat_<uchar>& mask, Mat_<float>& dst, int num_pca_iterations, const Mat_<float>& rand_vec, Buf& buf) |
||||||
|
{ |
||||||
|
CV_DbgAssert( X.cols == rand_vec.cols ); |
||||||
|
CV_DbgAssert( X.rows == mask.size().area() ); |
||||||
|
CV_DbgAssert( rand_vec.rows == 1 ); |
||||||
|
|
||||||
|
ensureSizeIsEnough(rand_vec.size(), dst); |
||||||
|
rand_vec.copyTo(dst); |
||||||
|
|
||||||
|
ensureSizeIsEnough(X.size(), buf.t); |
||||||
|
|
||||||
|
float* dst_row = dst[0]; |
||||||
|
|
||||||
|
for (int i = 0; i < num_pca_iterations; ++i) |
||||||
|
{ |
||||||
|
buf.t.setTo(Scalar::all(0)); |
||||||
|
|
||||||
|
for (int y = 0, ind = 0; y < mask.rows; ++y) |
||||||
|
{ |
||||||
|
const uchar* mask_row = mask[y]; |
||||||
|
|
||||||
|
for (int x = 0; x < mask.cols; ++x, ++ind) |
||||||
|
{ |
||||||
|
if (mask_row[x]) |
||||||
|
{ |
||||||
|
const float* X_row = X[ind]; |
||||||
|
float* t_row = buf.t[ind]; |
||||||
|
|
||||||
|
float dots = 0.0; |
||||||
|
for (int c = 0; c < X.cols; ++c) |
||||||
|
dots += dst_row[c] * X_row[c]; |
||||||
|
|
||||||
|
for (int c = 0; c < X.cols; ++c) |
||||||
|
t_row[c] = dots * X_row[c]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dst.setTo(0.0); |
||||||
|
for (int k = 0; k < X.rows; ++k) |
||||||
|
{ |
||||||
|
const float* t_row = buf.t[k]; |
||||||
|
|
||||||
|
for (int c = 0; c < X.cols; ++c) |
||||||
|
{ |
||||||
|
dst_row[c] += t_row[c]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
double n = norm(dst); |
||||||
|
divide(dst, n, dst); |
||||||
|
} |
||||||
|
|
||||||
|
void calcEta(const Mat_<Point3f>& src_joint_f, const Mat_<float>& theta, const Mat_<uchar>& cluster, Mat_<Point3f>& dst, float sigma_s, float df, Buf& buf) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(theta.size(), buf.theta_masked); |
||||||
|
buf.theta_masked.setTo(Scalar::all(0)); |
||||||
|
theta.copyTo(buf.theta_masked, cluster); |
||||||
|
|
||||||
|
times(src_joint_f, buf.theta_masked, buf.mul); |
||||||
|
|
||||||
|
const Size nsz = Size(saturate_cast<int>(buf.mul.cols * (1.0 / df)), saturate_cast<int>(buf.mul.rows * (1.0 / df))); |
||||||
|
|
||||||
|
ensureSizeIsEnough(nsz, buf.numerator); |
||||||
|
resize(buf.mul, buf.numerator, Size(), 1.0 / df, 1.0 / df); |
||||||
|
|
||||||
|
ensureSizeIsEnough(nsz, buf.denominator); |
||||||
|
resize(buf.theta_masked, buf.denominator, Size(), 1.0 / df, 1.0 / df);
|
||||||
|
h_filter(buf.numerator, buf.numerator_filtered, sigma_s / df); |
||||||
|
h_filter(buf.denominator, buf.denominator_filtered, sigma_s / df); |
||||||
|
|
||||||
|
|
||||||
|
rdivide(buf.numerator_filtered, buf.denominator_filtered, dst); |
||||||
|
} |
||||||
|
|
||||||
|
void AdaptiveManifoldFilterRefImpl::buildManifoldsAndPerformFiltering(const Mat_<Point3f>& eta_k, const Mat_<uchar>& cluster_k, int current_tree_level) |
||||||
|
{ |
||||||
|
// Compute downsampling factor
|
||||||
|
|
||||||
|
double df = min(sigma_s_ / 4.0, 256.0 * sigma_r_); |
||||||
|
df = floor_to_power_of_two(df); |
||||||
|
df = max(1.0, df); |
||||||
|
|
||||||
|
// Splatting: project the pixel values onto the current manifold eta_k
|
||||||
|
|
||||||
|
if (eta_k.rows == src_joint_f_.rows) |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(src_joint_f_.size(), buf_.X); |
||||||
|
subtract(src_joint_f_, eta_k, buf_.X); |
||||||
|
|
||||||
|
const Size nsz = Size(saturate_cast<int>(eta_k.cols * (1.0 / df)), saturate_cast<int>(eta_k.rows * (1.0 / df))); |
||||||
|
ensureSizeIsEnough(nsz, buf_.eta_k_small); |
||||||
|
resize(eta_k, buf_.eta_k_small, Size(), 1.0 / df, 1.0 / df); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
ensureSizeIsEnough(eta_k.size(), buf_.eta_k_small); |
||||||
|
eta_k.copyTo(buf_.eta_k_small); |
||||||
|
|
||||||
|
ensureSizeIsEnough(src_joint_f_.size(), buf_.eta_k_big); |
||||||
|
resize(eta_k, buf_.eta_k_big, src_joint_f_.size()); |
||||||
|
|
||||||
|
ensureSizeIsEnough(src_joint_f_.size(), buf_.X); |
||||||
|
subtract(src_joint_f_, buf_.eta_k_big, buf_.X); |
||||||
|
} |
||||||
|
|
||||||
|
// Project pixel colors onto the manifold -- Eq. (3), Eq. (5)
|
||||||
|
|
||||||
|
ensureSizeIsEnough(buf_.X.size(), buf_.X_squared); |
||||||
|
multiply(buf_.X, buf_.X, buf_.X_squared); |
||||||
|
|
||||||
|
channelsSum(buf_.X_squared, buf_.pixel_dist_to_manifold_squared); |
||||||
|
|
||||||
|
phi(buf_.pixel_dist_to_manifold_squared, buf_.gaussian_distance_weights, sigma_r_over_sqrt_2_); |
||||||
|
|
||||||
|
times(src_f_, buf_.gaussian_distance_weights, buf_.Psi_splat); |
||||||
|
|
||||||
|
const Mat_<float>& Psi_splat_0 = buf_.gaussian_distance_weights; |
||||||
|
|
||||||
|
// Save min distance to later perform adjustment of outliers -- Eq. (10)
|
||||||
|
|
||||||
|
if (adjust_outliers_) |
||||||
|
{ |
||||||
|
cv::min(_InputArray(min_pixel_dist_to_manifold_squared_), _InputArray(buf_.pixel_dist_to_manifold_squared), _OutputArray(min_pixel_dist_to_manifold_squared_)); |
||||||
|
} |
||||||
|
|
||||||
|
// Blurring: perform filtering over the current manifold eta_k
|
||||||
|
|
||||||
|
catCn(buf_.Psi_splat, Psi_splat_0, buf_.Psi_splat_joined); |
||||||
|
|
||||||
|
ensureSizeIsEnough(buf_.eta_k_small.size(), buf_.Psi_splat_joined_resized); |
||||||
|
resize(buf_.Psi_splat_joined, buf_.Psi_splat_joined_resized, buf_.eta_k_small.size()); |
||||||
|
|
||||||
|
RF_filter(buf_.Psi_splat_joined_resized, buf_.eta_k_small, buf_.blurred_projected_values, static_cast<float>(sigma_s_ / df), sigma_r_over_sqrt_2_, buf_); |
||||||
|
|
||||||
|
split_3_1(buf_.blurred_projected_values, buf_.w_ki_Psi_blur, buf_.w_ki_Psi_blur_0); |
||||||
|
|
||||||
|
// Slicing: gather blurred values from the manifold
|
||||||
|
|
||||||
|
// Since we perform splatting and slicing at the same points over the manifolds,
|
||||||
|
// the interpolation weights are equal to the gaussian weights used for splatting.
|
||||||
|
const Mat_<float>& w_ki = buf_.gaussian_distance_weights; |
||||||
|
|
||||||
|
ensureSizeIsEnough(src_f_.size(), buf_.w_ki_Psi_blur_resized); |
||||||
|
resize(buf_.w_ki_Psi_blur, buf_.w_ki_Psi_blur_resized, src_f_.size()); |
||||||
|
times(buf_.w_ki_Psi_blur_resized, w_ki, buf_.w_ki_Psi_blur_resized); |
||||||
|
add(sum_w_ki_Psi_blur_, buf_.w_ki_Psi_blur_resized, sum_w_ki_Psi_blur_); |
||||||
|
|
||||||
|
ensureSizeIsEnough(src_f_.size(), buf_.w_ki_Psi_blur_0_resized); |
||||||
|
resize(buf_.w_ki_Psi_blur_0, buf_.w_ki_Psi_blur_0_resized, src_f_.size()); |
||||||
|
times(buf_.w_ki_Psi_blur_0_resized, w_ki, buf_.w_ki_Psi_blur_0_resized); |
||||||
|
add(sum_w_ki_Psi_blur_0_, buf_.w_ki_Psi_blur_0_resized, sum_w_ki_Psi_blur_0_); |
||||||
|
|
||||||
|
// Compute two new manifolds eta_minus and eta_plus
|
||||||
|
|
||||||
|
if (current_tree_level < cur_tree_height_) |
||||||
|
{ |
||||||
|
// Algorithm 1, Step 2: compute the eigenvector v1
|
||||||
|
const Mat_<float> nX(src_joint_f_.size().area(), 3, (float*) buf_.X.data); |
||||||
|
|
||||||
|
ensureSizeIsEnough(1, nX.cols, buf_.rand_vec); |
||||||
|
if (useRNG) |
||||||
|
{ |
||||||
|
rng_.fill(buf_.rand_vec, RNG::UNIFORM, -0.5, 0.5); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
for (int i = 0; i < (int)buf_.rand_vec.total(); i++) |
||||||
|
buf_.rand_vec(0, i) = (i % 2 == 0) ? 0.5f : -0.5f; |
||||||
|
} |
||||||
|
|
||||||
|
computeEigenVector(nX, cluster_k, buf_.v1, num_pca_iterations_, buf_.rand_vec, buf_); |
||||||
|
|
||||||
|
// Algorithm 1, Step 3: Segment pixels into two clusters -- Eq. (6)
|
||||||
|
|
||||||
|
ensureSizeIsEnough(nX.rows, buf_.v1.rows, buf_.Nx_v1_mult); |
||||||
|
gemm(nX, buf_.v1, 1.0, noArray(), 0.0, buf_.Nx_v1_mult, GEMM_2_T); |
||||||
|
|
||||||
|
const Mat_<float> dot(src_joint_f_.rows, src_joint_f_.cols, (float*) buf_.Nx_v1_mult.data); |
||||||
|
|
||||||
|
Mat_<uchar>& cluster_minus = buf_.cluster_minus[current_tree_level]; |
||||||
|
ensureSizeIsEnough(dot.size(), cluster_minus); |
||||||
|
compare(dot, 0, cluster_minus, CMP_LT); |
||||||
|
bitwise_and(cluster_minus, cluster_k, cluster_minus); |
||||||
|
|
||||||
|
Mat_<uchar>& cluster_plus = buf_.cluster_plus[current_tree_level]; |
||||||
|
ensureSizeIsEnough(dot.size(), cluster_plus); |
||||||
|
//compare(dot, 0, cluster_plus, CMP_GT);
|
||||||
|
compare(dot, 0, cluster_plus, CMP_GE); |
||||||
|
bitwise_and(cluster_plus, cluster_k, cluster_plus); |
||||||
|
|
||||||
|
// Algorithm 1, Step 4: Compute new manifolds by weighted low-pass filtering -- Eq. (7-8)
|
||||||
|
|
||||||
|
ensureSizeIsEnough(w_ki.size(), buf_.theta); |
||||||
|
buf_.theta.setTo(Scalar::all(1.0)); |
||||||
|
subtract(buf_.theta, w_ki, buf_.theta); |
||||||
|
|
||||||
|
Mat_<Point3f>& eta_minus = buf_.eta_minus[current_tree_level]; |
||||||
|
calcEta(src_joint_f_, buf_.theta, cluster_minus, eta_minus, (float)sigma_s_, (float)df, buf_); |
||||||
|
|
||||||
|
Mat_<Point3f>& eta_plus = buf_.eta_plus[current_tree_level]; |
||||||
|
calcEta(src_joint_f_, buf_.theta, cluster_plus, eta_plus, (float)sigma_s_, (float)df, buf_); |
||||||
|
|
||||||
|
// Algorithm 1, Step 5: recursively build more manifolds.
|
||||||
|
|
||||||
|
buildManifoldsAndPerformFiltering(eta_minus, cluster_minus, current_tree_level + 1); |
||||||
|
buildManifoldsAndPerformFiltering(eta_plus, cluster_plus, current_tree_level + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
Ptr<AdaptiveManifoldFilter> createAMFilterRefImpl(double sigma_s, double sigma_r, bool adjust_outliers) |
||||||
|
{ |
||||||
|
Ptr<AdaptiveManifoldFilter> amf(new AdaptiveManifoldFilterRefImpl()); |
||||||
|
|
||||||
|
amf->set("sigma_s", sigma_s); |
||||||
|
amf->set("sigma_r", sigma_r); |
||||||
|
amf->set("adjust_outliers", adjust_outliers); |
||||||
|
|
||||||
|
return amf; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,220 @@ |
|||||||
|
#include "test_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace std::tr1; |
||||||
|
using namespace testing; |
||||||
|
using namespace perf; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
static string getOpenCVExtraDir() |
||||||
|
{ |
||||||
|
return cvtest::TS::ptr()->get_data_path(); |
||||||
|
} |
||||||
|
|
||||||
|
CV_ENUM(SupportedTypes, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4); |
||||||
|
CV_ENUM(ModeType, DTF_NC, DTF_IC, DTF_RF) |
||||||
|
typedef tuple<Size, ModeType, SupportedTypes, SupportedTypes> DTParams; |
||||||
|
|
||||||
|
Mat convertTypeAndSize(Mat src, int dstType, Size dstSize) |
||||||
|
{ |
||||||
|
Mat dst; |
||||||
|
CV_Assert(src.channels() == 3); |
||||||
|
|
||||||
|
int dstChannels = CV_MAT_CN(dstType); |
||||||
|
|
||||||
|
if (dstChannels == 1) |
||||||
|
{ |
||||||
|
cvtColor(src, dst, COLOR_BGR2GRAY); |
||||||
|
} |
||||||
|
else if (dstChannels == 2) |
||||||
|
{ |
||||||
|
Mat srcCn[3]; |
||||||
|
split(src, srcCn); |
||||||
|
merge(srcCn, 2, dst); |
||||||
|
} |
||||||
|
else if (dstChannels == 3) |
||||||
|
{ |
||||||
|
dst = src.clone(); |
||||||
|
} |
||||||
|
else if (dstChannels == 4) |
||||||
|
{ |
||||||
|
Mat srcCn[4]; |
||||||
|
split(src, srcCn); |
||||||
|
srcCn[3] = srcCn[0].clone(); |
||||||
|
merge(srcCn, 4, dst); |
||||||
|
} |
||||||
|
|
||||||
|
dst.convertTo(dst, dstType); |
||||||
|
resize(dst, dst, dstSize); |
||||||
|
|
||||||
|
return dst; |
||||||
|
} |
||||||
|
|
||||||
|
TEST(DomainTransformTest, SplatSurfaceAccuracy) |
||||||
|
{ |
||||||
|
static int dtModes[] = {DTF_NC, DTF_RF, DTF_IC}; |
||||||
|
RNG rnd(0); |
||||||
|
|
||||||
|
for (int i = 0; i < 15; i++) |
||||||
|
{ |
||||||
|
Size sz(rnd.uniform(512, 1024), rnd.uniform(512, 1024)); |
||||||
|
|
||||||
|
int guideCn = rnd.uniform(1, 4); |
||||||
|
Mat guide(sz, CV_MAKE_TYPE(CV_32F, guideCn)); |
||||||
|
randu(guide, 0, 255); |
||||||
|
|
||||||
|
Scalar surfaceValue; |
||||||
|
int srcCn = rnd.uniform(1, 4); |
||||||
|
rnd.fill(surfaceValue, RNG::UNIFORM, 0, 255); |
||||||
|
Mat src(sz, CV_MAKE_TYPE(CV_8U, srcCn), surfaceValue); |
||||||
|
|
||||||
|
double sigma_s = rnd.uniform(1.0, 100.0); |
||||||
|
double sigma_r = rnd.uniform(1.0, 100.0); |
||||||
|
int mode = dtModes[i%3]; |
||||||
|
|
||||||
|
Mat res; |
||||||
|
dtFilter(guide, src, res, sigma_s, sigma_r, mode, 1); |
||||||
|
|
||||||
|
double normL1 = cvtest::norm(src, res, NORM_L1)/src.total()/src.channels(); |
||||||
|
EXPECT_LE(normL1, 1.0/64); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
typedef TestWithParam<DTParams> DomainTransformTest; |
||||||
|
TEST_P(DomainTransformTest, MultiThreadReproducibility) |
||||||
|
{ |
||||||
|
if (cv::getNumberOfCPUs() == 1) |
||||||
|
return; |
||||||
|
|
||||||
|
double MAX_DIF = 1.0; |
||||||
|
double MAX_MEAN_DIF = 1.0 / 256.0; |
||||||
|
int loopsCount = 2; |
||||||
|
RNG rng(0); |
||||||
|
|
||||||
|
DTParams params = GetParam(); |
||||||
|
Size size = get<0>(params); |
||||||
|
int mode = get<1>(params); |
||||||
|
int guideType = get<2>(params); |
||||||
|
int srcType = get<3>(params); |
||||||
|
|
||||||
|
Mat original = imread(getOpenCVExtraDir() + "cv/edgefilter/statue.png"); |
||||||
|
Mat guide = convertTypeAndSize(original, guideType, size); |
||||||
|
Mat src = convertTypeAndSize(original, srcType, size); |
||||||
|
|
||||||
|
for (int iter = 0; iter <= loopsCount; iter++) |
||||||
|
{ |
||||||
|
double ss = rng.uniform(0.0, 100.0); |
||||||
|
double sc = rng.uniform(0.0, 100.0); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
Mat resMultithread; |
||||||
|
dtFilter(guide, src, resMultithread, ss, sc, mode); |
||||||
|
|
||||||
|
cv::setNumThreads(1); |
||||||
|
Mat resSingleThread; |
||||||
|
dtFilter(guide, src, resSingleThread, ss, sc, mode); |
||||||
|
|
||||||
|
EXPECT_LE(cv::norm(resSingleThread, resMultithread, NORM_INF), MAX_DIF); |
||||||
|
EXPECT_LE(cv::norm(resSingleThread, resMultithread, NORM_L1), MAX_MEAN_DIF*src.total()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(FullSet, DomainTransformTest, |
||||||
|
Combine(Values(szODD, szQVGA), ModeType::all(), SupportedTypes::all(), SupportedTypes::all()) |
||||||
|
); |
||||||
|
|
||||||
|
template<typename SrcVec> |
||||||
|
Mat getChessMat1px(Size sz, double whiteIntensity = 255) |
||||||
|
{ |
||||||
|
typedef typename DataType<SrcVec>::channel_type SrcType; |
||||||
|
|
||||||
|
Mat dst(sz, DataType<SrcVec>::type); |
||||||
|
|
||||||
|
SrcVec black = SrcVec::all(0); |
||||||
|
SrcVec white = SrcVec::all((SrcType)whiteIntensity); |
||||||
|
|
||||||
|
for (int i = 0; i < dst.rows; i++) |
||||||
|
for (int j = 0; j < dst.cols; j++) |
||||||
|
dst.at<SrcVec>(i, j) = ((i + j) % 2) ? white : black; |
||||||
|
|
||||||
|
return dst; |
||||||
|
} |
||||||
|
|
||||||
|
TEST(DomainTransformTest, ChessBoard_NC_accuracy) |
||||||
|
{ |
||||||
|
RNG rng(0); |
||||||
|
double MAX_DIF = 1; |
||||||
|
Size sz = szVGA; |
||||||
|
double ss = 80; |
||||||
|
double sc = 60; |
||||||
|
|
||||||
|
Mat srcb = randomMat(rng, sz, CV_8UC4, 0, 255, true); |
||||||
|
Mat srcf = randomMat(rng, sz, CV_32FC4, 0, 255, true); |
||||||
|
Mat chessb = getChessMat1px<Vec3b>(sz); |
||||||
|
|
||||||
|
Mat dstb, dstf; |
||||||
|
dtFilter(chessb, srcb.clone(), dstb, ss, sc, DTF_NC); |
||||||
|
dtFilter(chessb, srcf.clone(), dstf, ss, sc, DTF_NC); |
||||||
|
|
||||||
|
EXPECT_LE(cv::norm(srcb, dstb, NORM_INF), MAX_DIF); |
||||||
|
EXPECT_LE(cv::norm(srcf, dstf, NORM_INF), MAX_DIF); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(DomainTransformTest, BoxFilter_NC_accuracy) |
||||||
|
{ |
||||||
|
double MAX_DIF = 1; |
||||||
|
int radius = 5; |
||||||
|
double sc = 1.0; |
||||||
|
double ss = 1.01*radius / sqrt(3.0); |
||||||
|
|
||||||
|
Mat src = imread(getOpenCVExtraDir() + "cv/edgefilter/statue.png"); |
||||||
|
ASSERT_TRUE(!src.empty()); |
||||||
|
|
||||||
|
Mat1b guide(src.size(), 200); |
||||||
|
Mat res_dt, res_box; |
||||||
|
|
||||||
|
blur(src, res_box, Size(2 * radius + 1, 2 * radius + 1)); |
||||||
|
dtFilter(guide, src, res_dt, ss, sc, DTF_NC, 1); |
||||||
|
|
||||||
|
EXPECT_LE(cv::norm(res_dt, res_box, NORM_L2), MAX_DIF*src.total()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(DomainTransformTest, AuthorReferenceAccuracy) |
||||||
|
{ |
||||||
|
string dir = getOpenCVExtraDir() + "cv/edgefilter"; |
||||||
|
double ss = 30; |
||||||
|
double sc = 0.2 * 255; |
||||||
|
|
||||||
|
Mat src = imread(dir + "/statue.png"); |
||||||
|
Mat ref_NC = imread(dir + "/dt/authors_statue_NC_ss30_sc0.2.png"); |
||||||
|
Mat ref_IC = imread(dir + "/dt/authors_statue_IC_ss30_sc0.2.png"); |
||||||
|
Mat ref_RF = imread(dir + "/dt/authors_statue_RF_ss30_sc0.2.png"); |
||||||
|
|
||||||
|
ASSERT_FALSE(src.empty()); |
||||||
|
ASSERT_FALSE(ref_NC.empty()); |
||||||
|
ASSERT_FALSE(ref_IC.empty()); |
||||||
|
ASSERT_FALSE(ref_RF.empty()); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
Mat res_NC, res_IC, res_RF; |
||||||
|
dtFilter(src, src, res_NC, ss, sc, DTF_NC); |
||||||
|
dtFilter(src, src, res_IC, ss, sc, DTF_IC); |
||||||
|
dtFilter(src, src, res_RF, ss, sc, DTF_RF); |
||||||
|
|
||||||
|
double totalMaxError = 1.0/64.0*src.total(); |
||||||
|
|
||||||
|
EXPECT_LE(cvtest::norm(res_NC, ref_NC, NORM_L2), totalMaxError); |
||||||
|
EXPECT_LE(cvtest::norm(res_NC, ref_NC, NORM_INF), 1); |
||||||
|
|
||||||
|
EXPECT_LE(cvtest::norm(res_IC, ref_IC, NORM_L2), totalMaxError); |
||||||
|
EXPECT_LE(cvtest::norm(res_IC, ref_IC, NORM_INF), 1); |
||||||
|
|
||||||
|
EXPECT_LE(cvtest::norm(res_RF, ref_RF, NORM_L2), totalMaxError); |
||||||
|
EXPECT_LE(cvtest::norm(res_IC, ref_IC, NORM_INF), 1); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,362 @@ |
|||||||
|
#include "test_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace std::tr1; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
#ifndef SQR |
||||||
|
#define SQR(x) ((x)*(x)) |
||||||
|
#endif |
||||||
|
|
||||||
|
static string getOpenCVExtraDir() |
||||||
|
{ |
||||||
|
return cvtest::TS::ptr()->get_data_path(); |
||||||
|
} |
||||||
|
|
||||||
|
static Mat convertTypeAndSize(Mat src, int dstType, Size dstSize) |
||||||
|
{ |
||||||
|
Mat dst; |
||||||
|
int srcCnNum = src.channels(); |
||||||
|
int dstCnNum = CV_MAT_CN(dstType); |
||||||
|
CV_Assert(srcCnNum == 3); |
||||||
|
|
||||||
|
if (srcCnNum == dstCnNum) |
||||||
|
{ |
||||||
|
src.copyTo(dst); |
||||||
|
} |
||||||
|
else if (dstCnNum == 1 && srcCnNum == 3) |
||||||
|
{ |
||||||
|
cvtColor(src, dst, COLOR_BGR2GRAY); |
||||||
|
} |
||||||
|
else if (dstCnNum == 1 && srcCnNum == 4) |
||||||
|
{ |
||||||
|
cvtColor(src, dst, COLOR_BGRA2GRAY); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
vector<Mat> srcCn; |
||||||
|
split(src, srcCn); |
||||||
|
srcCn.resize(dstCnNum); |
||||||
|
|
||||||
|
uint64 seed = 10000 * src.rows + 1000 * src.cols + 100 * dstSize.height + 10 * dstSize.width + dstType; |
||||||
|
RNG rnd(seed); |
||||||
|
|
||||||
|
for (int i = srcCnNum; i < dstCnNum; i++) |
||||||
|
{ |
||||||
|
Mat& donor = srcCn[i % srcCnNum]; |
||||||
|
|
||||||
|
double minVal, maxVal; |
||||||
|
minMaxLoc(donor, &minVal, &maxVal); |
||||||
|
|
||||||
|
Mat randItem(src.size(), CV_MAKE_TYPE(src.depth(), 1)); |
||||||
|
randn(randItem, 0, (maxVal - minVal) / 100); |
||||||
|
|
||||||
|
add(donor, randItem, srcCn[i]); |
||||||
|
} |
||||||
|
|
||||||
|
merge(srcCn, dst); |
||||||
|
} |
||||||
|
|
||||||
|
dst.convertTo(dst, dstType); |
||||||
|
resize(dst, dst, dstSize); |
||||||
|
|
||||||
|
return dst; |
||||||
|
} |
||||||
|
|
||||||
|
class GuidedFilterRefImpl : public GuidedFilter |
||||||
|
{ |
||||||
|
int height, width, rad, chNum; |
||||||
|
Mat det; |
||||||
|
Mat *channels, *exps, **vars, **A; |
||||||
|
double eps; |
||||||
|
|
||||||
|
void meanFilter(const Mat &src, Mat & dst); |
||||||
|
|
||||||
|
void computeCovGuide(); |
||||||
|
|
||||||
|
void computeCovGuideInv(); |
||||||
|
|
||||||
|
void applyTransform(int cNum, Mat *Ichannels, Mat *beta, Mat **alpha, int dDepth); |
||||||
|
|
||||||
|
void computeCovGuideAndSrc(int cNum, Mat **vars_I, Mat *Ichannels, Mat *exp_I); |
||||||
|
|
||||||
|
void computeBeta(int cNum, Mat *beta, Mat *exp_I, Mat **alpha); |
||||||
|
|
||||||
|
void computeAlpha(int cNum, Mat **alpha, Mat **vars_I); |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
GuidedFilterRefImpl(InputArray guide_, int rad, double eps); |
||||||
|
|
||||||
|
void filter(InputArray src, OutputArray dst, int dDepth = -1); |
||||||
|
|
||||||
|
~GuidedFilterRefImpl(); |
||||||
|
}; |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::meanFilter(const Mat &src, Mat & dst) |
||||||
|
{ |
||||||
|
boxFilter(src, dst, CV_32F, Size(2 * rad + 1, 2 * rad + 1), Point(-1, -1), true, BORDER_REFLECT); |
||||||
|
} |
||||||
|
|
||||||
|
GuidedFilterRefImpl::GuidedFilterRefImpl(InputArray _guide, int _rad, double _eps) : |
||||||
|
height(_guide.rows()), width(_guide.cols()), rad(_rad), chNum(_guide.channels()), eps(_eps) |
||||||
|
{ |
||||||
|
Mat guide = _guide.getMat(); |
||||||
|
CV_Assert(chNum > 0 && chNum <= 3); |
||||||
|
|
||||||
|
channels = new Mat[chNum]; |
||||||
|
exps = new Mat[chNum]; |
||||||
|
|
||||||
|
A = new Mat *[chNum]; |
||||||
|
vars = new Mat *[chNum]; |
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
{ |
||||||
|
A[i] = new Mat[chNum]; |
||||||
|
vars[i] = new Mat[chNum]; |
||||||
|
} |
||||||
|
|
||||||
|
split(guide, channels); |
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
{ |
||||||
|
channels[i].convertTo(channels[i], CV_32F); |
||||||
|
meanFilter(channels[i], exps[i]); |
||||||
|
} |
||||||
|
|
||||||
|
computeCovGuide(); |
||||||
|
|
||||||
|
computeCovGuideInv(); |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::computeCovGuide() |
||||||
|
{ |
||||||
|
static const int pY[] = { 0, 0, 1, 0, 1, 2 }; |
||||||
|
static const int pX[] = { 0, 1, 1, 2, 2, 2 }; |
||||||
|
|
||||||
|
int numOfIterations = (SQR(chNum) - chNum) / 2 + chNum; |
||||||
|
for (int k = 0; k < numOfIterations; ++k) |
||||||
|
{ |
||||||
|
int i = pY[k], j = pX[k]; |
||||||
|
|
||||||
|
vars[i][j] = channels[i].mul(channels[j]); |
||||||
|
meanFilter(vars[i][j], vars[i][j]); |
||||||
|
vars[i][j] -= exps[i].mul(exps[j]); |
||||||
|
|
||||||
|
if (i == j) |
||||||
|
vars[i][j] += eps * Mat::ones(height, width, CV_32F); |
||||||
|
else |
||||||
|
vars[j][i] = vars[i][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::computeCovGuideInv() |
||||||
|
{ |
||||||
|
static const int pY[] = { 0, 0, 1, 0, 1, 2 }; |
||||||
|
static const int pX[] = { 0, 1, 1, 2, 2, 2 }; |
||||||
|
|
||||||
|
int numOfIterations = (SQR(chNum) - chNum) / 2 + chNum; |
||||||
|
if (chNum == 3) |
||||||
|
{ |
||||||
|
for (int k = 0; k < numOfIterations; ++k){ |
||||||
|
int i = pY[k], i1 = (pY[k] + 1) % 3, i2 = (pY[k] + 2) % 3; |
||||||
|
int j = pX[k], j1 = (pX[k] + 1) % 3, j2 = (pX[k] + 2) % 3; |
||||||
|
|
||||||
|
A[i][j] = vars[i1][j1].mul(vars[i2][j2]) |
||||||
|
- vars[i1][j2].mul(vars[i2][j1]); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (chNum == 2) |
||||||
|
{ |
||||||
|
A[0][0] = vars[1][1]; |
||||||
|
A[1][1] = vars[0][0]; |
||||||
|
A[0][1] = -vars[0][1]; |
||||||
|
} |
||||||
|
else if (chNum == 1) |
||||||
|
A[0][0] = Mat::ones(height, width, CV_32F); |
||||||
|
|
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
for (int j = 0; j < i; ++j) |
||||||
|
A[i][j] = A[j][i]; |
||||||
|
|
||||||
|
det = vars[0][0].mul(A[0][0]); |
||||||
|
for (int k = 0; k < chNum - 1; ++k) |
||||||
|
det += vars[0][k + 1].mul(A[0][k + 1]); |
||||||
|
} |
||||||
|
|
||||||
|
GuidedFilterRefImpl::~GuidedFilterRefImpl(){ |
||||||
|
delete [] channels; |
||||||
|
delete [] exps; |
||||||
|
|
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
{ |
||||||
|
delete [] A[i]; |
||||||
|
delete [] vars[i]; |
||||||
|
} |
||||||
|
|
||||||
|
delete [] A; |
||||||
|
delete [] vars; |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::filter(InputArray src_, OutputArray dst_, int dDepth) |
||||||
|
{ |
||||||
|
if (dDepth == -1) dDepth = src_.depth(); |
||||||
|
dst_.create(height, width, src_.type()); |
||||||
|
Mat src = src_.getMat(); |
||||||
|
Mat dst = dst_.getMat(); |
||||||
|
int cNum = src.channels(); |
||||||
|
|
||||||
|
CV_Assert(height == src.rows && width == src.cols); |
||||||
|
|
||||||
|
Mat *Ichannels, *exp_I, **vars_I, **alpha, *beta; |
||||||
|
Ichannels = new Mat[cNum]; |
||||||
|
exp_I = new Mat[cNum]; |
||||||
|
beta = new Mat[cNum]; |
||||||
|
|
||||||
|
vars_I = new Mat *[chNum]; |
||||||
|
alpha = new Mat *[chNum]; |
||||||
|
for (int i = 0; i < chNum; ++i){ |
||||||
|
vars_I[i] = new Mat[cNum]; |
||||||
|
alpha[i] = new Mat[cNum]; |
||||||
|
} |
||||||
|
|
||||||
|
split(src, Ichannels); |
||||||
|
for (int i = 0; i < cNum; ++i) |
||||||
|
{ |
||||||
|
Ichannels[i].convertTo(Ichannels[i], CV_32F); |
||||||
|
meanFilter(Ichannels[i], exp_I[i]); |
||||||
|
} |
||||||
|
|
||||||
|
computeCovGuideAndSrc(cNum, vars_I, Ichannels, exp_I); |
||||||
|
|
||||||
|
computeAlpha(cNum, alpha, vars_I); |
||||||
|
|
||||||
|
computeBeta(cNum, beta, exp_I, alpha); |
||||||
|
|
||||||
|
for (int i = 0; i < chNum + 1; ++i) |
||||||
|
for (int j = 0; j < cNum; ++j) |
||||||
|
if (i < chNum) |
||||||
|
meanFilter(alpha[i][j], alpha[i][j]); |
||||||
|
else |
||||||
|
meanFilter(beta[j], beta[j]); |
||||||
|
|
||||||
|
applyTransform(cNum, Ichannels, beta, alpha, dDepth); |
||||||
|
merge(Ichannels, cNum, dst); |
||||||
|
|
||||||
|
delete [] Ichannels; |
||||||
|
delete [] exp_I; |
||||||
|
delete [] beta; |
||||||
|
|
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
{ |
||||||
|
delete [] vars_I[i]; |
||||||
|
delete [] alpha[i]; |
||||||
|
} |
||||||
|
delete [] vars_I; |
||||||
|
delete [] alpha; |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::computeAlpha(int cNum, Mat **alpha, Mat **vars_I) |
||||||
|
{ |
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
for (int j = 0; j < cNum; ++j) |
||||||
|
{ |
||||||
|
alpha[i][j] = vars_I[0][j].mul(A[i][0]); |
||||||
|
for (int k = 1; k < chNum; ++k) |
||||||
|
alpha[i][j] += vars_I[k][j].mul(A[i][k]); |
||||||
|
alpha[i][j] /= det; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::computeBeta(int cNum, Mat *beta, Mat *exp_I, Mat **alpha) |
||||||
|
{ |
||||||
|
for (int i = 0; i < cNum; ++i) |
||||||
|
{ |
||||||
|
beta[i] = exp_I[i]; |
||||||
|
for (int j = 0; j < chNum; ++j) |
||||||
|
beta[i] -= alpha[j][i].mul(exps[j]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::computeCovGuideAndSrc(int cNum, Mat **vars_I, Mat *Ichannels, Mat *exp_I) |
||||||
|
{ |
||||||
|
for (int i = 0; i < chNum; ++i) |
||||||
|
for (int j = 0; j < cNum; ++j) |
||||||
|
{ |
||||||
|
vars_I[i][j] = channels[i].mul(Ichannels[j]); |
||||||
|
meanFilter(vars_I[i][j], vars_I[i][j]); |
||||||
|
vars_I[i][j] -= exp_I[j].mul(exps[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void GuidedFilterRefImpl::applyTransform(int cNum, Mat *Ichannels, Mat *beta, Mat **alpha, int dDepth) |
||||||
|
{ |
||||||
|
for (int i = 0; i < cNum; ++i) |
||||||
|
{ |
||||||
|
Ichannels[i] = beta[i]; |
||||||
|
for (int j = 0; j < chNum; ++j) |
||||||
|
Ichannels[i] += alpha[j][i].mul(channels[j]); |
||||||
|
Ichannels[i].convertTo(Ichannels[i], dDepth); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
typedef tuple<int, int, string, string> GFParams; |
||||||
|
typedef TestWithParam<GFParams> GuidedFilterTest; |
||||||
|
|
||||||
|
TEST_P(GuidedFilterTest, accuracy) |
||||||
|
{ |
||||||
|
GFParams params = GetParam(); |
||||||
|
|
||||||
|
int guideCnNum = get<0>(params); |
||||||
|
int srcCnNum = get<1>(params); |
||||||
|
|
||||||
|
string guideFileName = get<2>(params); |
||||||
|
string srcFileName = get<3>(params); |
||||||
|
|
||||||
|
int seed = 100 * guideCnNum + 50 * srcCnNum + 5*(int)guideFileName.length() + (int)srcFileName.length(); |
||||||
|
RNG rng(seed); |
||||||
|
|
||||||
|
Mat guide = imread(getOpenCVExtraDir() + guideFileName); |
||||||
|
Mat src = imread(getOpenCVExtraDir() + srcFileName); |
||||||
|
ASSERT_TRUE(!guide.empty() && !src.empty()); |
||||||
|
|
||||||
|
Size dstSize(guide.cols + 1 + rng.uniform(0, 3), guide.rows); |
||||||
|
|
||||||
|
guide = convertTypeAndSize(guide, CV_MAKE_TYPE(guide.depth(), guideCnNum), dstSize); |
||||||
|
src = convertTypeAndSize(src, CV_MAKE_TYPE(src.depth(), srcCnNum), dstSize); |
||||||
|
|
||||||
|
for (int iter = 0; iter < 3; iter++) |
||||||
|
{ |
||||||
|
int radius = rng.uniform(0, 50); |
||||||
|
double eps = rng.uniform(0.0, SQR(255.0)); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
Mat res; |
||||||
|
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps); |
||||||
|
gf->filter(src, res); |
||||||
|
|
||||||
|
cv::setNumThreads(1); |
||||||
|
Mat resRef; |
||||||
|
Ptr<GuidedFilter> gfRef(new GuidedFilterRefImpl(guide, radius, eps)); |
||||||
|
gfRef->filter(src, resRef); |
||||||
|
|
||||||
|
double normInf = cv::norm(res, resRef, NORM_INF); |
||||||
|
double normL2 = cv::norm(res, resRef, NORM_L2) / guide.total(); |
||||||
|
|
||||||
|
EXPECT_LE(normInf, 1.0); |
||||||
|
EXPECT_LE(normL2, 1.0/64.0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(TypicalSet, GuidedFilterTest,
|
||||||
|
Combine( |
||||||
|
Values(1, 2, 3), |
||||||
|
Values(1, 2, 3),
|
||||||
|
Values("cv/shared/lena.png", "cv/shared/baboon.png", "cv/npr/test2.png"), |
||||||
|
Values("cv/shared/lena.png", "cv/shared/baboon.png", "cv/npr/test2.png") |
||||||
|
)); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,256 @@ |
|||||||
|
#include "test_precomp.hpp" |
||||||
|
|
||||||
|
namespace cvtest |
||||||
|
{ |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace std::tr1; |
||||||
|
using namespace testing; |
||||||
|
using namespace cv; |
||||||
|
using namespace cv::ximgproc; |
||||||
|
|
||||||
|
static std::string getOpenCVExtraDir() |
||||||
|
{ |
||||||
|
return cvtest::TS::ptr()->get_data_path(); |
||||||
|
} |
||||||
|
|
||||||
|
static void checkSimilarity(InputArray src, InputArray ref) |
||||||
|
{ |
||||||
|
double normInf = cvtest::norm(src, ref, NORM_INF); |
||||||
|
double normL2 = cvtest::norm(src, ref, NORM_L2) / (src.total()*src.channels()); |
||||||
|
|
||||||
|
EXPECT_LE(normInf, 1.0); |
||||||
|
EXPECT_LE(normL2, 1.0 / 16); |
||||||
|
} |
||||||
|
|
||||||
|
static Mat convertTypeAndSize(Mat src, int dstType, Size dstSize) |
||||||
|
{ |
||||||
|
Mat dst; |
||||||
|
int srcCnNum = src.channels(); |
||||||
|
int dstCnNum = CV_MAT_CN(dstType); |
||||||
|
|
||||||
|
if (srcCnNum == dstCnNum) |
||||||
|
{ |
||||||
|
src.copyTo(dst); |
||||||
|
} |
||||||
|
else if (srcCnNum == 3 && dstCnNum == 1) |
||||||
|
{ |
||||||
|
cvtColor(src, dst, COLOR_BGR2GRAY); |
||||||
|
} |
||||||
|
else if (srcCnNum == 1 && dstCnNum == 3) |
||||||
|
{ |
||||||
|
cvtColor(src, dst, COLOR_GRAY2BGR); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CV_Error(Error::BadNumChannels, "Bad num channels in src"); |
||||||
|
} |
||||||
|
|
||||||
|
dst.convertTo(dst, dstType); |
||||||
|
resize(dst, dst, dstSize); |
||||||
|
|
||||||
|
return dst; |
||||||
|
} |
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void jointBilateralFilterNaive(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT); |
||||||
|
|
||||||
|
typedef Vec<float, 1> Vec1f; |
||||||
|
typedef Vec<uchar, 1> Vec1b; |
||||||
|
|
||||||
|
#ifndef SQR |
||||||
|
#define SQR(a) ((a)*(a)) |
||||||
|
#endif |
||||||
|
|
||||||
|
template<typename T, int cn> |
||||||
|
float normL1Sqr(const Vec<T, cn>& a, const Vec<T, cn>& b) |
||||||
|
{ |
||||||
|
float res = 0.0f; |
||||||
|
for (int i = 0; i < cn; i++) |
||||||
|
res += std::abs((float)a[i] - (float)b[i]); |
||||||
|
return res*res; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename JointVec, typename SrcVec> |
||||||
|
void jointBilateralFilterNaive_(InputArray joint_, InputArray src_, OutputArray dst_, int d, double sigmaColor, double sigmaSpace, int borderType) |
||||||
|
{ |
||||||
|
CV_Assert(joint_.size() == src_.size()); |
||||||
|
CV_Assert(joint_.type() == JointVec::type && src_.type() == SrcVec::type); |
||||||
|
typedef Vec<float, SrcVec::channels> SrcVecf; |
||||||
|
|
||||||
|
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); |
||||||
|
d = 2 * radius + 1; |
||||||
|
|
||||||
|
dst_.create(src_.size(), src_.type()); |
||||||
|
Mat_<SrcVec> dst = dst_.getMat(); |
||||||
|
Mat_<JointVec> jointExt; |
||||||
|
Mat_<SrcVec> srcExt; |
||||||
|
copyMakeBorder(src_, srcExt, radius, radius, radius, radius, borderType); |
||||||
|
copyMakeBorder(joint_, jointExt, radius, radius, radius, radius, borderType); |
||||||
|
|
||||||
|
float colorGaussCoef = (float)(-0.5 / (sigmaColor*sigmaColor)); |
||||||
|
float spaceGaussCoef = (float)(-0.5 / (sigmaSpace*sigmaSpace)); |
||||||
|
|
||||||
|
for (int i = radius; i < srcExt.rows - radius; i++) |
||||||
|
{ |
||||||
|
for (int j = radius; j < srcExt.cols - radius; j++) |
||||||
|
{ |
||||||
|
JointVec joint0 = jointExt(i, j); |
||||||
|
SrcVecf sum = SrcVecf::all(0.0f); |
||||||
|
float sumWeights = 0.0f; |
||||||
|
|
||||||
|
for (int k = -radius; k <= radius; k++) |
||||||
|
{ |
||||||
|
for (int l = -radius; l <= radius; l++) |
||||||
|
{ |
||||||
|
float spatDistSqr = (float)(k*k + l*l); |
||||||
|
if (spatDistSqr > SQR(radius)) continue; |
||||||
|
|
||||||
|
float colorDistSqr = normL1Sqr(joint0, jointExt(i + k, j + l)); |
||||||
|
|
||||||
|
float weight = std::exp(spatDistSqr*spaceGaussCoef + colorDistSqr*colorGaussCoef); |
||||||
|
|
||||||
|
sum += weight*SrcVecf(srcExt(i + k, j + l)); |
||||||
|
sumWeights += weight; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dst(i - radius, j - radius) = sum / sumWeights; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void jointBilateralFilterNaive(InputArray joint, InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType) |
||||||
|
{ |
||||||
|
CV_Assert(src.size() == joint.size() && src.depth() == joint.depth()); |
||||||
|
CV_Assert(src.type() == CV_32FC1 || src.type() == CV_32FC3 || src.type() == CV_8UC1 || src.type() == CV_8UC3); |
||||||
|
CV_Assert(joint.type() == CV_32FC1 || joint.type() == CV_32FC3 || joint.type() == CV_8UC1 || joint.type() == CV_8UC3); |
||||||
|
|
||||||
|
int jointType = joint.type(); |
||||||
|
int srcType = src.type(); |
||||||
|
|
||||||
|
#define JBF_naive(VecJoint, VecSrc) jointBilateralFilterNaive_<VecJoint, VecSrc>(joint, src, dst, d, sigmaColor, sigmaSpace, borderType); |
||||||
|
if (jointType == CV_8UC1) |
||||||
|
{ |
||||||
|
if (srcType == CV_8UC1) JBF_naive(Vec1b, Vec1b); |
||||||
|
if (srcType == CV_8UC3) JBF_naive(Vec1b, Vec3b); |
||||||
|
} |
||||||
|
if (jointType == CV_8UC3) |
||||||
|
{ |
||||||
|
if (srcType == CV_8UC1) JBF_naive(Vec3b, Vec1b); |
||||||
|
if (srcType == CV_8UC3) JBF_naive(Vec3b, Vec3b); |
||||||
|
} |
||||||
|
if (jointType == CV_32FC1) |
||||||
|
{ |
||||||
|
if (srcType == CV_32FC1) JBF_naive(Vec1f, Vec1f); |
||||||
|
if (srcType == CV_32FC3) JBF_naive(Vec1f, Vec3f); |
||||||
|
} |
||||||
|
if (jointType == CV_32FC3) |
||||||
|
{ |
||||||
|
if (srcType == CV_32FC1) JBF_naive(Vec3f, Vec1f); |
||||||
|
if (srcType == CV_32FC3) JBF_naive(Vec3f, Vec3f); |
||||||
|
} |
||||||
|
#undef JBF_naive |
||||||
|
} |
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef tuple<double, string, string, int, int, int> JBFTestParam; |
||||||
|
typedef TestWithParam<JBFTestParam> JointBilateralFilterTest_NaiveRef; |
||||||
|
|
||||||
|
TEST_P(JointBilateralFilterTest_NaiveRef, Accuracy) |
||||||
|
{ |
||||||
|
JBFTestParam param = GetParam(); |
||||||
|
double sigmaS = get<0>(param); |
||||||
|
string jointPath = get<1>(param); |
||||||
|
string srcPath = get<2>(param); |
||||||
|
int depth = get<3>(param); |
||||||
|
int jCn = get<4>(param); |
||||||
|
int srcCn = get<5>(param); |
||||||
|
int jointType = CV_MAKE_TYPE(depth, jCn); |
||||||
|
int srcType = CV_MAKE_TYPE(depth, srcCn); |
||||||
|
|
||||||
|
Mat joint = imread(getOpenCVExtraDir() + jointPath); |
||||||
|
Mat src = imread(getOpenCVExtraDir() + srcPath); |
||||||
|
ASSERT_TRUE(!joint.empty() && !src.empty()); |
||||||
|
|
||||||
|
joint = convertTypeAndSize(joint, jointType, joint.size()); |
||||||
|
src = convertTypeAndSize(src, srcType, joint.size()); |
||||||
|
|
||||||
|
RNG rnd(cvRound(10*sigmaS) + jointType + srcType + jointPath.length() + srcPath.length() + joint.rows + joint.cols); |
||||||
|
double sigmaC = rnd.uniform(0, 255); |
||||||
|
|
||||||
|
Mat resNaive; |
||||||
|
jointBilateralFilterNaive(joint, src, resNaive, 0, sigmaC, sigmaS); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
Mat res; |
||||||
|
jointBilateralFilter(joint, src, res, 0, sigmaC, sigmaS); |
||||||
|
cv::setNumThreads(1); |
||||||
|
|
||||||
|
checkSimilarity(res, resNaive); |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Set2, JointBilateralFilterTest_NaiveRef, |
||||||
|
Combine( |
||||||
|
Values(4.0, 6.0, 8.0), |
||||||
|
Values("/cv/shared/airplane.png", "/cv/shared/fruits.png"), |
||||||
|
Values("/cv/shared/airplane.png", "/cv/shared/lena.png", "/cv/shared/fruits.png"), |
||||||
|
Values(CV_8U, CV_32F), |
||||||
|
Values(1, 3), |
||||||
|
Values(1, 3)) |
||||||
|
); |
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef tuple<double, string, int> BFTestParam; |
||||||
|
typedef TestWithParam<BFTestParam> JointBilateralFilterTest_BilateralRef; |
||||||
|
|
||||||
|
TEST_P(JointBilateralFilterTest_BilateralRef, Accuracy) |
||||||
|
{ |
||||||
|
BFTestParam param = GetParam(); |
||||||
|
double sigmaS = get<0>(param); |
||||||
|
string srcPath = get<1>(param); |
||||||
|
int srcType = get<2>(param); |
||||||
|
|
||||||
|
Mat src = imread(getOpenCVExtraDir() + srcPath); |
||||||
|
ASSERT_TRUE(!src.empty()); |
||||||
|
src = convertTypeAndSize(src, srcType, src.size()); |
||||||
|
|
||||||
|
RNG rnd(cvRound(10*sigmaS) + srcPath.length() + srcType + src.rows); |
||||||
|
double sigmaC = rnd.uniform(0.0, 255.0); |
||||||
|
|
||||||
|
cv::setNumThreads(cv::getNumberOfCPUs()); |
||||||
|
|
||||||
|
Mat resRef; |
||||||
|
bilateralFilter(src, resRef, 0, sigmaC, sigmaS); |
||||||
|
|
||||||
|
Mat res, joint = src.clone(); |
||||||
|
jointBilateralFilter(joint, src, res, 0, sigmaC, sigmaS); |
||||||
|
|
||||||
|
checkSimilarity(res, resRef); |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Set1, JointBilateralFilterTest_BilateralRef, |
||||||
|
Combine( |
||||||
|
Values(4.0, 6.0, 8.0), |
||||||
|
Values("/cv/shared/pic2.png", "/cv/shared/lena.png", "cv/shared/box_in_scene.png"), |
||||||
|
Values(CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
#include "test_precomp.hpp" |
||||||
|
|
||||||
|
CV_TEST_MAIN("") |
@ -0,0 +1,20 @@ |
|||||||
|
#ifdef __GNUC__ |
||||||
|
# pragma GCC diagnostic ignored "-Wmissing-declarations" |
||||||
|
# if defined __clang__ || defined __APPLE__ |
||||||
|
# pragma GCC diagnostic ignored "-Wmissing-prototypes" |
||||||
|
# pragma GCC diagnostic ignored "-Wextra" |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef __OPENCV_TEST_PRECOMP_HPP__ |
||||||
|
#define __OPENCV_TEST_PRECOMP_HPP__ |
||||||
|
|
||||||
|
#include <opencv2/ts.hpp> |
||||||
|
#include <opencv2/ts/ts_perf.hpp> |
||||||
|
#include <opencv2/core.hpp> |
||||||
|
#include <opencv2/core/utility.hpp> |
||||||
|
#include <opencv2/imgproc.hpp> |
||||||
|
#include <opencv2/highgui.hpp> |
||||||
|
#include <opencv2/ximgproc.hpp> |
||||||
|
|
||||||
|
#endif |
After Width: | Height: | Size: 285 KiB |
After Width: | Height: | Size: 345 KiB |
After Width: | Height: | Size: 274 KiB |
After Width: | Height: | Size: 255 KiB |
After Width: | Height: | Size: 343 KiB |
After Width: | Height: | Size: 291 KiB |
After Width: | Height: | Size: 396 KiB |
After Width: | Height: | Size: 417 KiB |
After Width: | Height: | Size: 452 KiB |
After Width: | Height: | Size: 549 KiB |
After Width: | Height: | Size: 666 KiB |