diff --git a/modules/tracking/doc/tracker_algorithms.rst b/modules/tracking/doc/tracker_algorithms.rst index d20b10607..dee541a26 100644 --- a/modules/tracking/doc/tracker_algorithms.rst +++ b/modules/tracking/doc/tracker_algorithms.rst @@ -9,6 +9,10 @@ The following algorithms are implemented at the moment. .. [OLB] H Grabner, M Grabner, and H Bischof, Real-time tracking via on-line boosting, In Proc. BMVC, volume 1, pages 47– 56, 2006 +.. [MedianFlow] Z. Kalal, K. Mikolajczyk, and J. Matas, “Forward-Backward Error: Automatic Detection of Tracking Failures,” International Conference on Pattern Recognition, 2010, pp. 23-26. + +.. [TLD] Z. Kalal, K. Mikolajczyk, and J. Matas, “Tracking-Learning-Detection,” Pattern Analysis and Machine Intelligence 2011. + TrackerBoosting --------------- @@ -63,7 +67,7 @@ Constructor :param parameters: BOOSTING parameters :ocv:struct:`TrackerBoosting::Params` TrackerMIL ----------- +---------------------- The MIL algorithm trains a classifier in an online manner to separate the object from the background. Multiple Instance Learning avoids the drift problem for a robust tracking. The implementation is based on [MIL]_. @@ -118,3 +122,105 @@ Constructor .. ocv:function:: Ptr TrackerMIL::createTracker(const trackerMIL::Params ¶meters=trackerMIL::Params()) :param parameters: MIL parameters :ocv:struct:`TrackerMIL::Params` + +TrackerMedianFlow +---------------------- + +Implementation of a paper "Forward-Backward Error: Automatic Detection of Tracking Failures" by Z. Kalal, K. Mikolajczyk +and Jiri Matas. The implementation is based on [MedianFlow]_. + +The tracker is suitable for very smooth and predictable movements when object is visible throughout the whole sequence. It's quite and +accurate for this type of problems (in particular, it was shown by authors to outperform MIL). During the implementation period the code at +http://www.aonsquared.co.uk/node/5, the courtesy of the author Arthur Amarra, was used for the reference purpose. + +.. ocv:class:: TrackerMedianFlow + +Implementation of TrackerMedianFlow from :ocv:class:`Tracker`:: + + class CV_EXPORTS_W TrackerMedianFlow : public Tracker + { + public: + void read( const FileNode& fn ); + void write( FileStorage& fs ) const; + static Ptr createTracker(const trackerMedianFlow::Params ¶meters=trackerMedianFlow::Params()); + virtual ~trackerMedianFlow(){}; + + protected: + bool initImpl( const Mat& image, const Rect2d& boundingBox ); + bool updateImpl( const Mat& image, Rect2d& boundingBox ); + }; + +TrackerMedianFlow::Params +------------------------------------ + +.. ocv:struct:: TrackerMedianFlow::Params + +List of MedianFlow parameters:: + + struct CV_EXPORTS Params + { + Params(); + int pointsInGrid; //square root of number of keypoints used; increase it to trade + //accurateness for speed; default value is sensible and recommended + + void read( const FileNode& fn ); + void write( FileStorage& fs ) const; + }; + +TrackerMedianFlow::createTracker +----------------------------------- + +Constructor + +.. ocv:function:: Ptr TrackerMedianFlow::createTracker(const trackerMedianFlow::Params ¶meters=trackerMedianFlow::Params()) + + :param parameters: Median Flow parameters :ocv:struct:`TrackerMedianFlow::Params` + +TrackerTLD +---------------------- + +TLD is a novel tracking framework that explicitly decomposes the long-term tracking task into tracking, learning and detection. The tracker follows the object from frame to frame. The detector localizes all appearances that have been observed so far and corrects the tracker if necessary. The learning estimates detector’s errors and updates it to avoid these errors in the future. The implementation is based on [TLD]_. + +The Median Flow algorithm (see above) was chosen as a tracking component in this implementation, following authors. Tracker is supposed to be able +to handle rapid motions, partial occlusions, object absence etc. + +.. ocv:class:: TrackerTLD + +Implementation of TrackerTLD from :ocv:class:`Tracker`:: + + class CV_EXPORTS_W TrackerTLD : public Tracker + { + public: + void read( const FileNode& fn ); + void write( FileStorage& fs ) const; + static Ptr createTracker(const trackerTLD::Params ¶meters=trackerTLD::Params()); + virtual ~trackerTLD(){}; + + protected: + bool initImpl( const Mat& image, const Rect2d& boundingBox ); + bool updateImpl( const Mat& image, Rect2d& boundingBox ); + }; + +TrackerTLD::Params +------------------------ + +.. ocv:struct:: TrackerTLD::Params + +List of TLD parameters:: + + struct CV_EXPORTS Params + { + Params(); + + void read( const FileNode& fn ); + void write( FileStorage& fs ) const; + }; + +TrackerTLD::createTracker +------------------------------- + +Constructor + +.. ocv:function:: Ptr TrackerTLD::createTracker(const trackerTLD::Params ¶meters=trackerTLD::Params()) + + :param parameters: TLD parameters :ocv:struct:`TrackerTLD::Params` diff --git a/modules/tracking/include/opencv2/tracking/tracker.hpp b/modules/tracking/include/opencv2/tracking/tracker.hpp index 795753cc7..e043f53e2 100644 --- a/modules/tracking/include/opencv2/tracking/tracker.hpp +++ b/modules/tracking/include/opencv2/tracking/tracker.hpp @@ -1017,7 +1017,8 @@ class CV_EXPORTS_W TrackerMedianFlow : public Tracker struct CV_EXPORTS Params { Params(); - int pointsInGrid; + int pointsInGrid; //square root of number of keypoints used; increase it to trade + //accurateness for speed; default value is sensible and recommended void read( const FileNode& /*fn*/ ); void write( FileStorage& /*fs*/ ) const; }; diff --git a/modules/tracking/samples/benchmark.cpp b/modules/tracking/samples/benchmark.cpp index 7d2324d34..72c59caa2 100644 --- a/modules/tracking/samples/benchmark.cpp +++ b/modules/tracking/samples/benchmark.cpp @@ -7,9 +7,9 @@ #include #include -#define CMDLINEMAX 10 -#define ASSESS_TILL 100 -#define LINEMAX 40 +const int CMDLINEMAX = 30; +const int ASSESS_TILL = 100; +const int LINEMAX = 40; using namespace std; using namespace cv; @@ -20,7 +20,8 @@ using namespace cv; static Mat image; static bool paused; -vector palette; +static bool saveImageKey; +static vector palette; void print_table(char* videos[],int videoNum,char* algorithms[],int algNum,const vector >& results,char* tableName); @@ -67,20 +68,15 @@ static void help(){ exit(EXIT_SUCCESS); } static void parseCommandLineArgs(int argc, char** argv,char* videos[],char* gts[], - int* vc,char* algorithms[],char* initBoxes[][CMDLINEMAX],int* ac){ + int* vc,char* algorithms[],char* initBoxes[][CMDLINEMAX],int* ac,char keys[CMDLINEMAX][LINEMAX]){ *ac=*vc=0; for(int i=1;i averageMillisPerFrame(algnum,0.0); + static int videoNum=0; + videoNum++; FILE* gt=fopen(gt_str,"r"); if(gt==NULL){ @@ -312,6 +310,11 @@ static AssessmentRes assessment(char* video,char* gt_str, char* algorithms[],cha res.results[i][j]->assess(boundingBox,initBoxes[i]); } imshow( "Tracking API", image ); + if(saveImageKey){ + char inbuf[LINEMAX]; + sprintf(inbuf,"image%d_%d.jpg",videoNum,frameCounter); + imwrite(inbuf,image); + } if((frameCounter+1)>=ASSESS_TILL){ break; @@ -342,7 +345,11 @@ int main( int argc, char** argv ){ palette.push_back(Scalar(0,255,255)); int vcount=0,acount=0; char* videos[CMDLINEMAX],*gts[CMDLINEMAX],*algorithms[CMDLINEMAX],*initBoxes[CMDLINEMAX][CMDLINEMAX]; - parseCommandLineArgs(argc,argv,videos,gts,&vcount,algorithms,initBoxes,&acount); + char keys[CMDLINEMAX][LINEMAX]; + strcpy(keys[0],"-s"); + parseCommandLineArgs(argc,argv,videos,gts,&vcount,algorithms,initBoxes,&acount,keys); + saveImageKey=(keys[0][0]=='\0'); + CV_Assert(acount -#include -#include -#include -#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& 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& blackOnes,std::vector& 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(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_& standardPatch){ - for(int i=0;i> 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& scanGrid,Rect2d bBox,int n,std::vector& res){ - if(n>=(int)scanGrid.size()){ - res.assign(scanGrid.begin(),scanGrid.end()); - return; - } - std::vector overlaps(n,0.0); - res.assign(scanGrid.begin(),scanGrid.begin()+n); - for(int i=0;i 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(i,j); - p2+=img.at(i,j)*img.at(i,j); - } - } - p/=(img.cols*img.rows); - p2/=(img.cols*img.rows); - return p2-p*p; -} -double variance(Mat_& intImgP,Mat_& intImgP2,Rect box){ - int x=(box.x),y=(box.y),width=(box.width),height=(box.height); - CV_Assert(0<=x && (x+width) patch1,Mat_ 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& values, int size){ - if(size==-1){ - size=(int)values.size(); - } - std::vector 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_& samples){ - Mat_ 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_ 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_& samples){ - Mat_ 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& 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](measurePerClassifier,0); - x2=std::vector(measurePerClassifier,0); - y1=std::vector(measurePerClassifier,0); - y2=std::vector(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(posSize,0); - neg=std::vector(posSize,0); -} -void TLDEnsembleClassifier::integrate(Mat_ 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; -} - -}} diff --git a/modules/tracking/src/TLD.hpp b/modules/tracking/src/TLD.hpp deleted file mode 100644 index c2c96272a..000000000 --- a/modules/tracking/src/TLD.hpp +++ /dev/null @@ -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 -#include - -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_& standardPatch); -std::string type2str(const Mat& mat); -void drawWithRects(const Mat& img,std::vector& blackOnes,Rect2d whiteOne=Rect2d(-1.0,-1.0,-1.0,-1.0)); -void drawWithRects(const Mat& img,std::vector& blackOnes,std::vector& whiteOnes); - -//aux functions and variables -//#define CLIP(x,a,b) MIN(MAX((x),(a)),(b)) -template 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_& samples); -void resample(const Mat& img,const Rect2d& r2,Mat_& samples); -double variance(const Mat& img); -double variance(Mat_& intImgP,Mat_& intImgP2,Rect box); -double NCC(Mat_ patch1,Mat_ patch2); -void getClosestN(std::vector& scanGrid,Rect2d bBox,int n,std::vector& res); -double scaleAndBlur(const Mat& originalImg,int scale,Mat& scaledImg,Mat& blurredImg,Size GaussBlurKernelSize); -unsigned int getMedian(const std::vector& values, int size=-1); - -class TLDEnsembleClassifier{ -public: - TLDEnsembleClassifier(int ordinal,Size size,int measurePerClassifier); - void integrate(Mat_ patch,bool isPositive); - double posteriorProbability(const uchar* data,int rowstep)const; - static int getMaxOrdinal(); -private: - static int getGridSize(); - inline void stepPrefSuff(std::vector& arr,int len); - void preinit(int ordinal); - unsigned short int code(const uchar* data,int rowstep)const; - std::vector pos,neg; - std::vector 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(){} -}; - -}} diff --git a/modules/tracking/src/TLDEnsembleClassifier.cpp b/modules/tracking/src/TLDEnsembleClassifier.cpp deleted file mode 100644 index c574d31fc..000000000 --- a/modules/tracking/src/TLDEnsembleClassifier.cpp +++ /dev/null @@ -1,3693 +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 -#include -#include "TLD.hpp" - -using namespace cv; - -namespace cv {namespace tld -{ -int TLDEnsembleClassifier::getGridSize(){ - return 15; -} -int TLDEnsembleClassifier::getMaxOrdinal(){ - return 242; -} -void TLDEnsembleClassifier::preinit(int ordinal){ - CV_Assert(ordinal>0 && ordinal<=getMaxOrdinal()); - switch(ordinal){ - case 1: - x1[0]=5; x2[0]=5; y1[0]=12; y2[0]=11; - x1[1]=5; x2[1]=5; y1[1]=12; y2[1]=4; - x1[2]=8; x2[2]=7; y1[2]=3; y2[2]=3; - x1[3]=14; x2[3]=14; y1[3]=13; y2[3]=1; - x1[4]=11; x2[4]=11; y1[4]=10; y2[4]=0; - x1[5]=2; x2[5]=2; y1[5]=7; y2[5]=3; - x1[6]=3; x2[6]=3; y1[6]=9; y2[6]=2; - x1[7]=9; x2[7]=7; y1[7]=13; y2[7]=13; - x1[8]=0; x2[8]=0; y1[8]=13; y2[8]=4; - x1[9]=9; x2[9]=3; y1[9]=11; y2[9]=11; - x1[10]=13; x2[10]=3; y1[10]=5; y2[10]=5; - x1[11]=14; x2[11]=3; y1[11]=4; y2[11]=4; - x1[12]=12; x2[12]=9; y1[12]=1; y2[12]=1; - break; - case 2: - x1[0]=13; x2[0]=11; y1[0]=7; y2[0]=7; - x1[1]=7; x2[1]=6; y1[1]=8; y2[1]=8; - x1[2]=9; x2[2]=8; y1[2]=9; y2[2]=9; - x1[3]=6; x2[3]=6; y1[3]=13; y2[3]=0; - x1[4]=5; x2[4]=5; y1[4]=12; y2[4]=6; - x1[5]=4; x2[5]=0; y1[5]=3; y2[5]=3; - x1[6]=6; x2[6]=1; y1[6]=14; y2[6]=14; - x1[7]=10; x2[7]=7; y1[7]=7; y2[7]=7; - x1[8]=5; x2[8]=2; y1[8]=13; y2[8]=13; - x1[9]=2; x2[9]=2; y1[9]=1; y2[9]=0; - x1[10]=11; x2[10]=11; y1[10]=3; y2[10]=1; - x1[11]=12; x2[11]=1; y1[11]=5; y2[11]=5; - x1[12]=9; x2[12]=9; y1[12]=10; y2[12]=1; - break; - case 3: - x1[0]=14; x2[0]=14; y1[0]=14; y2[0]=9; - x1[1]=3; x2[1]=0; y1[1]=8; y2[1]=8; - x1[2]=11; x2[2]=10; y1[2]=4; y2[2]=4; - x1[3]=14; x2[3]=14; y1[3]=9; y2[3]=3; - x1[4]=13; x2[4]=8; y1[4]=12; y2[4]=12; - x1[5]=4; x2[5]=4; y1[5]=9; y2[5]=0; - x1[6]=11; x2[6]=6; y1[6]=2; y2[6]=2; - x1[7]=12; x2[7]=11; y1[7]=10; y2[7]=10; - x1[8]=14; x2[8]=14; y1[8]=14; y2[8]=0; - x1[9]=0; x2[9]=0; y1[9]=11; y2[9]=2; - x1[10]=3; x2[10]=1; y1[10]=3; y2[10]=3; - x1[11]=3; x2[11]=1; y1[11]=6; y2[11]=6; - x1[12]=11; x2[12]=1; y1[12]=14; y2[12]=14; - break; - case 4: - x1[0]=1; x2[0]=1; y1[0]=8; y2[0]=7; - x1[1]=0; x2[1]=0; y1[1]=8; y2[1]=5; - x1[2]=3; x2[2]=3; y1[2]=7; y2[2]=0; - x1[3]=14; x2[3]=9; y1[3]=14; y2[3]=14; - x1[4]=7; x2[4]=7; y1[4]=12; y2[4]=11; - x1[5]=14; x2[5]=10; y1[5]=8; y2[5]=8; - x1[6]=14; x2[6]=14; y1[6]=14; y2[6]=7; - x1[7]=0; x2[7]=0; y1[7]=10; y2[7]=8; - x1[8]=3; x2[8]=3; y1[8]=13; y2[8]=6; - x1[9]=12; x2[9]=11; y1[9]=7; y2[9]=7; - x1[10]=1; x2[10]=1; y1[10]=8; y2[10]=2; - x1[11]=6; x2[11]=6; y1[11]=3; y2[11]=1; - x1[12]=7; x2[12]=4; y1[12]=6; y2[12]=6; - break; - case 5: - x1[0]=10; x2[0]=10; y1[0]=14; y2[0]=2; - x1[1]=7; x2[1]=7; y1[1]=12; y2[1]=5; - x1[2]=13; x2[2]=12; y1[2]=1; y2[2]=1; - x1[3]=0; x2[3]=0; y1[3]=14; y2[3]=10; - x1[4]=6; x2[4]=6; y1[4]=11; y2[4]=8; - x1[5]=13; x2[5]=12; y1[5]=6; y2[5]=6; - x1[6]=13; x2[6]=2; y1[6]=11; y2[6]=11; - x1[7]=10; x2[7]=2; y1[7]=14; y2[7]=14; - x1[8]=13; x2[8]=10; y1[8]=1; y2[8]=1; - x1[9]=13; x2[9]=11; y1[9]=1; y2[9]=1; - x1[10]=12; x2[10]=4; y1[10]=0; y2[10]=0; - x1[11]=1; x2[11]=1; y1[11]=14; y2[11]=8; - x1[12]=9; x2[12]=9; y1[12]=12; y2[12]=9; - break; - case 6: - x1[0]=3; x2[0]=3; y1[0]=11; y2[0]=0; - x1[1]=9; x2[1]=7; y1[1]=3; y2[1]=3; - x1[2]=6; x2[2]=1; y1[2]=0; y2[2]=0; - x1[3]=7; x2[3]=7; y1[3]=9; y2[3]=2; - x1[4]=8; x2[4]=8; y1[4]=10; y2[4]=2; - x1[5]=5; x2[5]=2; y1[5]=0; y2[5]=0; - x1[6]=12; x2[6]=9; y1[6]=3; y2[6]=3; - x1[7]=3; x2[7]=3; y1[7]=9; y2[7]=4; - x1[8]=13; x2[8]=5; y1[8]=7; y2[8]=7; - x1[9]=12; x2[9]=12; y1[9]=14; y2[9]=3; - x1[10]=2; x2[10]=2; y1[10]=4; y2[10]=1; - x1[11]=2; x2[11]=2; y1[11]=11; y2[11]=6; - x1[12]=2; x2[12]=1; y1[12]=9; y2[12]=9; - break; - case 7: - x1[0]=7; x2[0]=1; y1[0]=1; y2[0]=1; - x1[1]=6; x2[1]=6; y1[1]=7; y2[1]=6; - x1[2]=12; x2[2]=5; y1[2]=5; y2[2]=5; - x1[3]=6; x2[3]=6; y1[3]=4; y2[3]=0; - x1[4]=5; x2[4]=5; y1[4]=13; y2[4]=8; - x1[5]=14; x2[5]=13; y1[5]=12; y2[5]=12; - x1[6]=12; x2[6]=10; y1[6]=13; y2[6]=13; - x1[7]=0; x2[7]=0; y1[7]=7; y2[7]=1; - x1[8]=14; x2[8]=14; y1[8]=11; y2[8]=4; - x1[9]=14; x2[9]=14; y1[9]=6; y2[9]=4; - x1[10]=13; x2[10]=4; y1[10]=10; y2[10]=10; - x1[11]=13; x2[11]=13; y1[11]=5; y2[11]=4; - x1[12]=10; x2[12]=7; y1[12]=10; y2[12]=10; - break; - case 8: - x1[0]=14; x2[0]=7; y1[0]=7; y2[0]=7; - x1[1]=12; x2[1]=12; y1[1]=12; y2[1]=4; - x1[2]=6; x2[2]=1; y1[2]=7; y2[2]=7; - x1[3]=12; x2[3]=11; y1[3]=8; y2[3]=8; - x1[4]=12; x2[4]=1; y1[4]=2; y2[4]=2; - x1[5]=14; x2[5]=11; y1[5]=9; y2[5]=9; - x1[6]=13; x2[6]=11; y1[6]=10; y2[6]=10; - x1[7]=12; x2[7]=12; y1[7]=11; y2[7]=0; - x1[8]=4; x2[8]=4; y1[8]=8; y2[8]=6; - x1[9]=2; x2[9]=2; y1[9]=11; y2[9]=3; - x1[10]=7; x2[10]=4; y1[10]=10; y2[10]=10; - x1[11]=12; x2[11]=2; y1[11]=14; y2[11]=14; - x1[12]=14; x2[12]=0; y1[12]=4; y2[12]=4; - break; - case 9: - x1[0]=9; x2[0]=9; y1[0]=12; y2[0]=11; - x1[1]=14; x2[1]=7; y1[1]=14; y2[1]=14; - x1[2]=9; x2[2]=1; y1[2]=9; y2[2]=9; - x1[3]=8; x2[3]=1; y1[3]=8; y2[3]=8; - x1[4]=3; x2[4]=3; y1[4]=3; y2[4]=1; - x1[5]=5; x2[5]=4; y1[5]=8; y2[5]=8; - x1[6]=9; x2[6]=9; y1[6]=9; y2[6]=8; - x1[7]=0; x2[7]=0; y1[7]=7; y2[7]=3; - x1[8]=8; x2[8]=2; y1[8]=0; y2[8]=0; - x1[9]=2; x2[9]=2; y1[9]=9; y2[9]=7; - x1[10]=0; x2[10]=0; y1[10]=14; y2[10]=4; - x1[11]=8; x2[11]=5; y1[11]=0; y2[11]=0; - x1[12]=5; x2[12]=5; y1[12]=12; y2[12]=9; - break; - case 10: - x1[0]=8; x2[0]=0; y1[0]=3; y2[0]=3; - x1[1]=7; x2[1]=4; y1[1]=11; y2[1]=11; - x1[2]=5; x2[2]=5; y1[2]=7; y2[2]=2; - x1[3]=3; x2[3]=3; y1[3]=8; y2[3]=5; - x1[4]=6; x2[4]=6; y1[4]=9; y2[4]=8; - x1[5]=4; x2[5]=2; y1[5]=7; y2[5]=7; - x1[6]=13; x2[6]=6; y1[6]=3; y2[6]=3; - x1[7]=12; x2[7]=12; y1[7]=11; y2[7]=3; - x1[8]=7; x2[8]=7; y1[8]=12; y2[8]=1; - x1[9]=6; x2[9]=6; y1[9]=14; y2[9]=3; - x1[10]=2; x2[10]=2; y1[10]=14; y2[10]=5; - x1[11]=14; x2[11]=14; y1[11]=7; y2[11]=4; - x1[12]=5; x2[12]=1; y1[12]=12; y2[12]=12; - break; - case 11: - x1[0]=10; x2[0]=10; y1[0]=4; y2[0]=0; - x1[1]=3; x2[1]=3; y1[1]=12; y2[1]=10; - x1[2]=13; x2[2]=8; y1[2]=9; y2[2]=9; - x1[3]=12; x2[3]=12; y1[3]=9; y2[3]=5; - x1[4]=14; x2[4]=14; y1[4]=7; y2[4]=0; - x1[5]=5; x2[5]=5; y1[5]=14; y2[5]=4; - x1[6]=4; x2[6]=4; y1[6]=13; y2[6]=3; - x1[7]=6; x2[7]=6; y1[7]=12; y2[7]=1; - x1[8]=10; x2[8]=10; y1[8]=5; y2[8]=1; - x1[9]=9; x2[9]=9; y1[9]=3; y2[9]=1; - x1[10]=9; x2[10]=4; y1[10]=14; y2[10]=14; - x1[11]=12; x2[11]=3; y1[11]=14; y2[11]=14; - x1[12]=6; x2[12]=2; y1[12]=13; y2[12]=13; - break; - case 12: - x1[0]=12; x2[0]=3; y1[0]=3; y2[0]=3; - x1[1]=12; x2[1]=9; y1[1]=8; y2[1]=8; - x1[2]=2; x2[2]=2; y1[2]=9; y2[2]=1; - x1[3]=4; x2[3]=4; y1[3]=10; y2[3]=9; - x1[4]=12; x2[4]=9; y1[4]=5; y2[4]=5; - x1[5]=14; x2[5]=8; y1[5]=12; y2[5]=12; - x1[6]=9; x2[6]=9; y1[6]=12; y2[6]=7; - x1[7]=11; x2[7]=3; y1[7]=6; y2[7]=6; - x1[8]=8; x2[8]=2; y1[8]=12; y2[8]=12; - x1[9]=10; x2[9]=1; y1[9]=12; y2[9]=12; - x1[10]=2; x2[10]=2; y1[10]=11; y2[10]=1; - x1[11]=11; x2[11]=4; y1[11]=13; y2[11]=13; - x1[12]=13; x2[12]=2; y1[12]=0; y2[12]=0; - break; - case 13: - x1[0]=4; x2[0]=4; y1[0]=14; y2[0]=8; - x1[1]=10; x2[1]=10; y1[1]=11; y2[1]=5; - x1[2]=7; x2[2]=7; y1[2]=8; y2[2]=3; - x1[3]=0; x2[3]=0; y1[3]=9; y2[3]=3; - x1[4]=8; x2[4]=8; y1[4]=9; y2[4]=7; - x1[5]=12; x2[5]=1; y1[5]=0; y2[5]=0; - x1[6]=14; x2[6]=5; y1[6]=8; y2[6]=8; - x1[7]=12; x2[7]=12; y1[7]=13; y2[7]=2; - x1[8]=9; x2[8]=6; y1[8]=6; y2[8]=6; - x1[9]=11; x2[9]=2; y1[9]=3; y2[9]=3; - x1[10]=1; x2[10]=1; y1[10]=8; y2[10]=5; - x1[11]=3; x2[11]=3; y1[11]=12; y2[11]=4; - x1[12]=1; x2[12]=1; y1[12]=14; y2[12]=11; - break; - case 14: - x1[0]=12; x2[0]=7; y1[0]=13; y2[0]=13; - x1[1]=10; x2[1]=10; y1[1]=7; y2[1]=5; - x1[2]=11; x2[2]=2; y1[2]=10; y2[2]=10; - x1[3]=11; x2[3]=11; y1[3]=2; y2[3]=0; - x1[4]=9; x2[4]=9; y1[4]=8; y2[4]=7; - x1[5]=8; x2[5]=8; y1[5]=14; y2[5]=5; - x1[6]=13; x2[6]=13; y1[6]=7; y2[6]=6; - x1[7]=11; x2[7]=11; y1[7]=14; y2[7]=11; - x1[8]=8; x2[8]=8; y1[8]=13; y2[8]=11; - x1[9]=10; x2[9]=10; y1[9]=12; y2[9]=5; - x1[10]=1; x2[10]=1; y1[10]=13; y2[10]=8; - x1[11]=13; x2[11]=13; y1[11]=14; y2[11]=13; - x1[12]=7; x2[12]=6; y1[12]=14; y2[12]=14; - break; - case 15: - x1[0]=1; x2[0]=1; y1[0]=12; y2[0]=2; - x1[1]=12; x2[1]=12; y1[1]=3; y2[1]=1; - x1[2]=10; x2[2]=5; y1[2]=8; y2[2]=8; - x1[3]=9; x2[3]=2; y1[3]=6; y2[3]=6; - x1[4]=11; x2[4]=5; y1[4]=0; y2[4]=0; - x1[5]=10; x2[5]=7; y1[5]=4; y2[5]=4; - x1[6]=4; x2[6]=3; y1[6]=14; y2[6]=14; - x1[7]=1; x2[7]=1; y1[7]=8; y2[7]=3; - x1[8]=8; x2[8]=8; y1[8]=12; y2[8]=2; - x1[9]=7; x2[9]=0; y1[9]=2; y2[9]=2; - x1[10]=1; x2[10]=0; y1[10]=14; y2[10]=14; - x1[11]=11; x2[11]=11; y1[11]=12; y2[11]=6; - x1[12]=9; x2[12]=5; y1[12]=7; y2[12]=7; - break; - case 16: - x1[0]=8; x2[0]=4; y1[0]=10; y2[0]=10; - x1[1]=0; x2[1]=0; y1[1]=8; y2[1]=7; - x1[2]=3; x2[2]=3; y1[2]=9; y2[2]=1; - x1[3]=6; x2[3]=2; y1[3]=3; y2[3]=3; - x1[4]=10; x2[4]=10; y1[4]=8; y2[4]=5; - x1[5]=8; x2[5]=2; y1[5]=4; y2[5]=4; - x1[6]=9; x2[6]=9; y1[6]=14; y2[6]=11; - x1[7]=0; x2[7]=0; y1[7]=8; y2[7]=2; - x1[8]=11; x2[8]=3; y1[8]=13; y2[8]=13; - x1[9]=7; x2[9]=4; y1[9]=13; y2[9]=13; - x1[10]=14; x2[10]=2; y1[10]=3; y2[10]=3; - x1[11]=8; x2[11]=8; y1[11]=13; y2[11]=4; - x1[12]=7; x2[12]=7; y1[12]=7; y2[12]=0; - break; - case 17: - x1[0]=14; x2[0]=8; y1[0]=3; y2[0]=3; - x1[1]=9; x2[1]=9; y1[1]=1; y2[1]=0; - x1[2]=14; x2[2]=14; y1[2]=3; y2[2]=2; - x1[3]=5; x2[3]=5; y1[3]=12; y2[3]=10; - x1[4]=8; x2[4]=8; y1[4]=12; y2[4]=4; - x1[5]=8; x2[5]=6; y1[5]=1; y2[5]=1; - x1[6]=5; x2[6]=5; y1[6]=6; y2[6]=4; - x1[7]=2; x2[7]=2; y1[7]=8; y2[7]=1; - x1[8]=14; x2[8]=5; y1[8]=1; y2[8]=1; - x1[9]=7; x2[9]=1; y1[9]=0; y2[9]=0; - x1[10]=5; x2[10]=5; y1[10]=7; y2[10]=1; - x1[11]=9; x2[11]=9; y1[11]=6; y2[11]=3; - x1[12]=13; x2[12]=0; y1[12]=11; y2[12]=11; - break; - case 18: - x1[0]=12; x2[0]=1; y1[0]=6; y2[0]=6; - x1[1]=4; x2[1]=4; y1[1]=12; y2[1]=8; - x1[2]=9; x2[2]=5; y1[2]=0; y2[2]=0; - x1[3]=8; x2[3]=4; y1[3]=11; y2[3]=11; - x1[4]=13; x2[4]=3; y1[4]=7; y2[4]=7; - x1[5]=10; x2[5]=10; y1[5]=12; y2[5]=4; - x1[6]=13; x2[6]=13; y1[6]=14; y2[6]=6; - x1[7]=0; x2[7]=0; y1[7]=9; y2[7]=2; - x1[8]=2; x2[8]=2; y1[8]=14; y2[8]=2; - x1[9]=7; x2[9]=3; y1[9]=10; y2[9]=10; - x1[10]=9; x2[10]=4; y1[10]=1; y2[10]=1; - x1[11]=10; x2[11]=10; y1[11]=10; y2[11]=2; - x1[12]=4; x2[12]=4; y1[12]=11; y2[12]=3; - break; - case 19: - x1[0]=4; x2[0]=4; y1[0]=5; y2[0]=1; - x1[1]=9; x2[1]=9; y1[1]=13; y2[1]=1; - x1[2]=5; x2[2]=0; y1[2]=3; y2[2]=3; - x1[3]=8; x2[3]=0; y1[3]=2; y2[3]=2; - x1[4]=9; x2[4]=9; y1[4]=8; y2[4]=0; - x1[5]=3; x2[5]=3; y1[5]=12; y2[5]=1; - x1[6]=6; x2[6]=0; y1[6]=2; y2[6]=2; - x1[7]=8; x2[7]=7; y1[7]=14; y2[7]=14; - x1[8]=10; x2[8]=1; y1[8]=14; y2[8]=14; - x1[9]=2; x2[9]=2; y1[9]=7; y2[9]=1; - x1[10]=10; x2[10]=0; y1[10]=2; y2[10]=2; - x1[11]=8; x2[11]=8; y1[11]=6; y2[11]=5; - x1[12]=13; x2[12]=13; y1[12]=11; y2[12]=9; - break; - case 20: - x1[0]=9; x2[0]=0; y1[0]=5; y2[0]=5; - x1[1]=13; x2[1]=12; y1[1]=14; y2[1]=14; - x1[2]=9; x2[2]=9; y1[2]=13; y2[2]=2; - x1[3]=12; x2[3]=8; y1[3]=9; y2[3]=9; - x1[4]=7; x2[4]=6; y1[4]=4; y2[4]=4; - x1[5]=11; x2[5]=9; y1[5]=3; y2[5]=3; - x1[6]=4; x2[6]=2; y1[6]=4; y2[6]=4; - x1[7]=13; x2[7]=7; y1[7]=13; y2[7]=13; - x1[8]=12; x2[8]=11; y1[8]=5; y2[8]=5; - x1[9]=4; x2[9]=1; y1[9]=7; y2[9]=7; - x1[10]=9; x2[10]=7; y1[10]=2; y2[10]=2; - x1[11]=11; x2[11]=11; y1[11]=13; y2[11]=6; - x1[12]=6; x2[12]=6; y1[12]=5; y2[12]=3; - break; - case 21: - x1[0]=14; x2[0]=9; y1[0]=10; y2[0]=10; - x1[1]=8; x2[1]=2; y1[1]=14; y2[1]=14; - x1[2]=14; x2[2]=14; y1[2]=9; y2[2]=1; - x1[3]=8; x2[3]=8; y1[3]=2; y2[3]=0; - x1[4]=10; x2[4]=1; y1[4]=6; y2[4]=6; - x1[5]=7; x2[5]=2; y1[5]=3; y2[5]=3; - x1[6]=12; x2[6]=12; y1[6]=7; y2[6]=4; - x1[7]=12; x2[7]=6; y1[7]=7; y2[7]=7; - x1[8]=8; x2[8]=8; y1[8]=4; y2[8]=0; - x1[9]=9; x2[9]=9; y1[9]=10; y2[9]=6; - x1[10]=6; x2[10]=6; y1[10]=9; y2[10]=1; - x1[11]=10; x2[11]=2; y1[11]=0; y2[11]=0; - x1[12]=1; x2[12]=1; y1[12]=13; y2[12]=2; - break; - case 22: - x1[0]=14; x2[0]=12; y1[0]=4; y2[0]=4; - x1[1]=13; x2[1]=1; y1[1]=14; y2[1]=14; - x1[2]=7; x2[2]=7; y1[2]=10; y2[2]=9; - x1[3]=6; x2[3]=1; y1[3]=12; y2[3]=12; - x1[4]=11; x2[4]=11; y1[4]=13; y2[4]=9; - x1[5]=5; x2[5]=4; y1[5]=1; y2[5]=1; - x1[6]=11; x2[6]=11; y1[6]=11; y2[6]=2; - x1[7]=3; x2[7]=0; y1[7]=5; y2[7]=5; - x1[8]=13; x2[8]=13; y1[8]=13; y2[8]=10; - x1[9]=12; x2[9]=12; y1[9]=14; y2[9]=0; - x1[10]=10; x2[10]=3; y1[10]=4; y2[10]=4; - x1[11]=10; x2[11]=8; y1[11]=1; y2[11]=1; - x1[12]=5; x2[12]=5; y1[12]=11; y2[12]=5; - break; - case 23: - x1[0]=8; x2[0]=8; y1[0]=11; y2[0]=4; - x1[1]=14; x2[1]=14; y1[1]=13; y2[1]=8; - x1[2]=13; x2[2]=13; y1[2]=13; y2[2]=8; - x1[3]=10; x2[3]=8; y1[3]=5; y2[3]=5; - x1[4]=14; x2[4]=14; y1[4]=12; y2[4]=7; - x1[5]=10; x2[5]=8; y1[5]=0; y2[5]=0; - x1[6]=9; x2[6]=2; y1[6]=4; y2[6]=4; - x1[7]=14; x2[7]=14; y1[7]=6; y2[7]=3; - x1[8]=5; x2[8]=5; y1[8]=9; y2[8]=1; - x1[9]=10; x2[9]=4; y1[9]=8; y2[9]=8; - x1[10]=5; x2[10]=5; y1[10]=4; y2[10]=2; - x1[11]=8; x2[11]=2; y1[11]=10; y2[11]=10; - x1[12]=4; x2[12]=4; y1[12]=12; y2[12]=6; - break; - case 24: - x1[0]=14; x2[0]=3; y1[0]=0; y2[0]=0; - x1[1]=6; x2[1]=4; y1[1]=13; y2[1]=13; - x1[2]=11; x2[2]=6; y1[2]=10; y2[2]=10; - x1[3]=2; x2[3]=2; y1[3]=13; y2[3]=10; - x1[4]=9; x2[4]=9; y1[4]=10; y2[4]=4; - x1[5]=6; x2[5]=6; y1[5]=14; y2[5]=6; - x1[6]=13; x2[6]=8; y1[6]=5; y2[6]=5; - x1[7]=0; x2[7]=0; y1[7]=12; y2[7]=10; - x1[8]=13; x2[8]=8; y1[8]=0; y2[8]=0; - x1[9]=9; x2[9]=9; y1[9]=13; y2[9]=7; - x1[10]=14; x2[10]=3; y1[10]=9; y2[10]=9; - x1[11]=6; x2[11]=6; y1[11]=6; y2[11]=5; - x1[12]=10; x2[12]=1; y1[12]=2; y2[12]=2; - break; - case 25: - x1[0]=13; x2[0]=10; y1[0]=14; y2[0]=14; - x1[1]=7; x2[1]=7; y1[1]=13; y2[1]=9; - x1[2]=12; x2[2]=10; y1[2]=10; y2[2]=10; - x1[3]=7; x2[3]=7; y1[3]=8; y2[3]=2; - x1[4]=12; x2[4]=1; y1[4]=7; y2[4]=7; - x1[5]=14; x2[5]=5; y1[5]=12; y2[5]=12; - x1[6]=8; x2[6]=8; y1[6]=9; y2[6]=3; - x1[7]=2; x2[7]=1; y1[7]=11; y2[7]=11; - x1[8]=12; x2[8]=2; y1[8]=9; y2[8]=9; - x1[9]=12; x2[9]=3; y1[9]=0; y2[9]=0; - x1[10]=6; x2[10]=1; y1[10]=2; y2[10]=2; - x1[11]=6; x2[11]=6; y1[11]=8; y2[11]=3; - x1[12]=7; x2[12]=7; y1[12]=8; y2[12]=6; - break; - case 26: - x1[0]=13; x2[0]=13; y1[0]=5; y2[0]=0; - x1[1]=13; x2[1]=10; y1[1]=7; y2[1]=7; - x1[2]=14; x2[2]=14; y1[2]=7; y2[2]=5; - x1[3]=9; x2[3]=9; y1[3]=13; y2[3]=5; - x1[4]=13; x2[4]=7; y1[4]=1; y2[4]=1; - x1[5]=10; x2[5]=8; y1[5]=3; y2[5]=3; - x1[6]=1; x2[6]=1; y1[6]=6; y2[6]=4; - x1[7]=6; x2[7]=6; y1[7]=6; y2[7]=1; - x1[8]=14; x2[8]=14; y1[8]=7; y2[8]=3; - x1[9]=14; x2[9]=10; y1[9]=0; y2[9]=0; - x1[10]=14; x2[10]=9; y1[10]=1; y2[10]=1; - x1[11]=3; x2[11]=3; y1[11]=13; y2[11]=1; - x1[12]=12; x2[12]=5; y1[12]=8; y2[12]=8; - break; - case 27: - x1[0]=10; x2[0]=3; y1[0]=6; y2[0]=6; - x1[1]=10; x2[1]=4; y1[1]=12; y2[1]=12; - x1[2]=13; x2[2]=13; y1[2]=8; y2[2]=6; - x1[3]=14; x2[3]=14; y1[3]=14; y2[3]=13; - x1[4]=13; x2[4]=1; y1[4]=1; y2[4]=1; - x1[5]=10; x2[5]=10; y1[5]=13; y2[5]=4; - x1[6]=13; x2[6]=8; y1[6]=13; y2[6]=13; - x1[7]=5; x2[7]=5; y1[7]=11; y2[7]=0; - x1[8]=14; x2[8]=14; y1[8]=10; y2[8]=8; - x1[9]=9; x2[9]=4; y1[9]=10; y2[9]=10; - x1[10]=13; x2[10]=10; y1[10]=6; y2[10]=6; - x1[11]=9; x2[11]=9; y1[11]=8; y2[11]=4; - x1[12]=5; x2[12]=1; y1[12]=11; y2[12]=11; - break; - case 28: - x1[0]=12; x2[0]=12; y1[0]=12; y2[0]=9; - x1[1]=6; x2[1]=2; y1[1]=11; y2[1]=11; - x1[2]=12; x2[2]=12; y1[2]=6; y2[2]=1; - x1[3]=9; x2[3]=7; y1[3]=14; y2[3]=14; - x1[4]=14; x2[4]=14; y1[4]=11; y2[4]=0; - x1[5]=5; x2[5]=3; y1[5]=13; y2[5]=13; - x1[6]=0; x2[6]=0; y1[6]=6; y2[6]=5; - x1[7]=11; x2[7]=9; y1[7]=14; y2[7]=14; - x1[8]=12; x2[8]=5; y1[8]=14; y2[8]=14; - x1[9]=0; x2[9]=0; y1[9]=4; y2[9]=3; - x1[10]=12; x2[10]=3; y1[10]=13; y2[10]=13; - x1[11]=5; x2[11]=4; y1[11]=6; y2[11]=6; - x1[12]=6; x2[12]=3; y1[12]=8; y2[12]=8; - break; - case 29: - x1[0]=8; x2[0]=7; y1[0]=9; y2[0]=9; - x1[1]=8; x2[1]=8; y1[1]=14; y2[1]=2; - x1[2]=11; x2[2]=11; y1[2]=12; y2[2]=1; - x1[3]=13; x2[3]=5; y1[3]=13; y2[3]=13; - x1[4]=1; x2[4]=1; y1[4]=10; y2[4]=0; - x1[5]=13; x2[5]=12; y1[5]=3; y2[5]=3; - x1[6]=7; x2[6]=7; y1[6]=13; y2[6]=1; - x1[7]=3; x2[7]=3; y1[7]=7; y2[7]=4; - x1[8]=11; x2[8]=3; y1[8]=11; y2[8]=11; - x1[9]=9; x2[9]=8; y1[9]=13; y2[9]=13; - x1[10]=7; x2[10]=7; y1[10]=12; y2[10]=8; - x1[11]=8; x2[11]=8; y1[11]=14; y2[11]=8; - x1[12]=3; x2[12]=1; y1[12]=4; y2[12]=4; - break; - case 30: - x1[0]=2; x2[0]=2; y1[0]=10; y2[0]=4; - x1[1]=11; x2[1]=6; y1[1]=13; y2[1]=13; - x1[2]=12; x2[2]=8; y1[2]=11; y2[2]=11; - x1[3]=14; x2[3]=14; y1[3]=13; y2[3]=11; - x1[4]=6; x2[4]=6; y1[4]=7; y2[4]=4; - x1[5]=7; x2[5]=4; y1[5]=14; y2[5]=14; - x1[6]=9; x2[6]=5; y1[6]=9; y2[6]=9; - x1[7]=6; x2[7]=0; y1[7]=4; y2[7]=4; - x1[8]=1; x2[8]=1; y1[8]=10; y2[8]=5; - x1[9]=9; x2[9]=1; y1[9]=2; y2[9]=2; - x1[10]=13; x2[10]=7; y1[10]=0; y2[10]=0; - x1[11]=8; x2[11]=8; y1[11]=12; y2[11]=10; - x1[12]=8; x2[12]=3; y1[12]=12; y2[12]=12; - break; - case 31: - x1[0]=2; x2[0]=2; y1[0]=13; y2[0]=2; - x1[1]=0; x2[1]=0; y1[1]=4; y2[1]=2; - x1[2]=9; x2[2]=9; y1[2]=6; y2[2]=5; - x1[3]=1; x2[3]=0; y1[3]=11; y2[3]=11; - x1[4]=4; x2[4]=4; y1[4]=7; y2[4]=3; - x1[5]=4; x2[5]=4; y1[5]=7; y2[5]=4; - x1[6]=1; x2[6]=1; y1[6]=7; y2[6]=0; - x1[7]=8; x2[7]=4; y1[7]=4; y2[7]=4; - x1[8]=12; x2[8]=12; y1[8]=10; y2[8]=5; - x1[9]=4; x2[9]=2; y1[9]=12; y2[9]=12; - x1[10]=12; x2[10]=3; y1[10]=8; y2[10]=8; - x1[11]=4; x2[11]=4; y1[11]=14; y2[11]=0; - x1[12]=7; x2[12]=6; y1[12]=0; y2[12]=0; - break; - case 32: - x1[0]=7; x2[0]=7; y1[0]=10; y2[0]=1; - x1[1]=11; x2[1]=5; y1[1]=10; y2[1]=10; - x1[2]=14; x2[2]=7; y1[2]=4; y2[2]=4; - x1[3]=8; x2[3]=8; y1[3]=10; y2[3]=6; - x1[4]=4; x2[4]=4; y1[4]=6; y2[4]=5; - x1[5]=10; x2[5]=10; y1[5]=1; y2[5]=0; - x1[6]=11; x2[6]=2; y1[6]=9; y2[6]=9; - x1[7]=12; x2[7]=12; y1[7]=4; y2[7]=3; - x1[8]=3; x2[8]=2; y1[8]=13; y2[8]=13; - x1[9]=11; x2[9]=11; y1[9]=14; y2[9]=8; - x1[10]=5; x2[10]=1; y1[10]=5; y2[10]=5; - x1[11]=10; x2[11]=10; y1[11]=14; y2[11]=7; - x1[12]=1; x2[12]=1; y1[12]=11; y2[12]=8; - break; - case 33: - x1[0]=11; x2[0]=11; y1[0]=12; y2[0]=5; - x1[1]=6; x2[1]=2; y1[1]=14; y2[1]=14; - x1[2]=9; x2[2]=9; y1[2]=7; y2[2]=2; - x1[3]=2; x2[3]=2; y1[3]=12; y2[3]=1; - x1[4]=11; x2[4]=11; y1[4]=13; y2[4]=5; - x1[5]=11; x2[5]=2; y1[5]=11; y2[5]=11; - x1[6]=3; x2[6]=0; y1[6]=11; y2[6]=11; - x1[7]=7; x2[7]=6; y1[7]=10; y2[7]=10; - x1[8]=5; x2[8]=0; y1[8]=11; y2[8]=11; - x1[9]=5; x2[9]=5; y1[9]=8; y2[9]=5; - x1[10]=13; x2[10]=11; y1[10]=13; y2[10]=13; - x1[11]=0; x2[11]=0; y1[11]=10; y2[11]=3; - x1[12]=5; x2[12]=5; y1[12]=11; y2[12]=10; - break; - case 34: - x1[0]=7; x2[0]=6; y1[0]=6; y2[0]=6; - x1[1]=6; x2[1]=6; y1[1]=13; y2[1]=12; - x1[2]=11; x2[2]=2; y1[2]=2; y2[2]=2; - x1[3]=1; x2[3]=1; y1[3]=9; y2[3]=6; - x1[4]=7; x2[4]=7; y1[4]=11; y2[4]=3; - x1[5]=2; x2[5]=2; y1[5]=12; y2[5]=0; - x1[6]=6; x2[6]=6; y1[6]=7; y2[6]=5; - x1[7]=11; x2[7]=9; y1[7]=6; y2[7]=6; - x1[8]=14; x2[8]=5; y1[8]=10; y2[8]=10; - x1[9]=8; x2[9]=3; y1[9]=2; y2[9]=2; - x1[10]=14; x2[10]=9; y1[10]=6; y2[10]=6; - x1[11]=13; x2[11]=8; y1[11]=6; y2[11]=6; - x1[12]=13; x2[12]=13; y1[12]=1; y2[12]=0; - break; - case 35: - x1[0]=2; x2[0]=2; y1[0]=8; y2[0]=7; - x1[1]=8; x2[1]=8; y1[1]=6; y2[1]=2; - x1[2]=2; x2[2]=2; y1[2]=2; y2[2]=0; - x1[3]=8; x2[3]=0; y1[3]=14; y2[3]=14; - x1[4]=14; x2[4]=14; y1[4]=4; y2[4]=3; - x1[5]=11; x2[5]=2; y1[5]=1; y2[5]=1; - x1[6]=14; x2[6]=9; y1[6]=2; y2[6]=2; - x1[7]=13; x2[7]=13; y1[7]=9; y2[7]=2; - x1[8]=9; x2[8]=8; y1[8]=2; y2[8]=2; - x1[9]=7; x2[9]=3; y1[9]=14; y2[9]=14; - x1[10]=13; x2[10]=13; y1[10]=8; y2[10]=7; - x1[11]=11; x2[11]=11; y1[11]=10; y2[11]=4; - x1[12]=13; x2[12]=12; y1[12]=7; y2[12]=7; - break; - case 36: - x1[0]=8; x2[0]=6; y1[0]=4; y2[0]=4; - x1[1]=12; x2[1]=3; y1[1]=4; y2[1]=4; - x1[2]=5; x2[2]=5; y1[2]=9; y2[2]=8; - x1[3]=3; x2[3]=3; y1[3]=10; y2[3]=2; - x1[4]=11; x2[4]=11; y1[4]=9; y2[4]=5; - x1[5]=9; x2[5]=9; y1[5]=13; y2[5]=0; - x1[6]=4; x2[6]=0; y1[6]=11; y2[6]=11; - x1[7]=9; x2[7]=0; y1[7]=9; y2[7]=9; - x1[8]=7; x2[8]=7; y1[8]=4; y2[8]=3; - x1[9]=7; x2[9]=4; y1[9]=5; y2[9]=5; - x1[10]=12; x2[10]=12; y1[10]=11; y2[10]=8; - x1[11]=9; x2[11]=1; y1[11]=11; y2[11]=11; - x1[12]=8; x2[12]=1; y1[12]=11; y2[12]=11; - break; - case 37: - x1[0]=5; x2[0]=5; y1[0]=9; y2[0]=3; - x1[1]=5; x2[1]=5; y1[1]=8; y2[1]=1; - x1[2]=3; x2[2]=3; y1[2]=13; y2[2]=12; - x1[3]=2; x2[3]=2; y1[3]=7; y2[3]=5; - x1[4]=10; x2[4]=2; y1[4]=1; y2[4]=1; - x1[5]=8; x2[5]=2; y1[5]=1; y2[5]=1; - x1[6]=2; x2[6]=0; y1[6]=11; y2[6]=11; - x1[7]=9; x2[7]=1; y1[7]=7; y2[7]=7; - x1[8]=1; x2[8]=1; y1[8]=6; y2[8]=1; - x1[9]=12; x2[9]=12; y1[9]=2; y2[9]=1; - x1[10]=4; x2[10]=4; y1[10]=2; y2[10]=1; - x1[11]=13; x2[11]=4; y1[11]=4; y2[11]=4; - x1[12]=12; x2[12]=12; y1[12]=12; y2[12]=5; - break; - case 38: - x1[0]=10; x2[0]=3; y1[0]=10; y2[0]=10; - x1[1]=10; x2[1]=3; y1[1]=8; y2[1]=8; - x1[2]=1; x2[2]=1; y1[2]=8; y2[2]=4; - x1[3]=10; x2[3]=10; y1[3]=11; y2[3]=1; - x1[4]=6; x2[4]=6; y1[4]=13; y2[4]=4; - x1[5]=10; x2[5]=10; y1[5]=5; y2[5]=2; - x1[6]=11; x2[6]=5; y1[6]=1; y2[6]=1; - x1[7]=14; x2[7]=13; y1[7]=6; y2[7]=6; - x1[8]=14; x2[8]=14; y1[8]=9; y2[8]=4; - x1[9]=14; x2[9]=1; y1[9]=0; y2[9]=0; - x1[10]=7; x2[10]=7; y1[10]=14; y2[10]=11; - x1[11]=5; x2[11]=3; y1[11]=11; y2[11]=11; - x1[12]=5; x2[12]=5; y1[12]=10; y2[12]=8; - break; - case 39: - x1[0]=8; x2[0]=8; y1[0]=13; y2[0]=1; - x1[1]=14; x2[1]=12; y1[1]=6; y2[1]=6; - x1[2]=0; x2[2]=0; y1[2]=14; y2[2]=11; - x1[3]=6; x2[3]=6; y1[3]=10; y2[3]=3; - x1[4]=14; x2[4]=1; y1[4]=7; y2[4]=7; - x1[5]=6; x2[5]=6; y1[5]=14; y2[5]=1; - x1[6]=10; x2[6]=10; y1[6]=12; y2[6]=3; - x1[7]=14; x2[7]=1; y1[7]=10; y2[7]=10; - x1[8]=10; x2[8]=10; y1[8]=13; y2[8]=10; - x1[9]=6; x2[9]=6; y1[9]=9; y2[9]=4; - x1[10]=14; x2[10]=0; y1[10]=10; y2[10]=10; - x1[11]=14; x2[11]=11; y1[11]=4; y2[11]=4; - x1[12]=11; x2[12]=11; y1[12]=11; y2[12]=8; - break; - case 40: - x1[0]=1; x2[0]=1; y1[0]=10; y2[0]=3; - x1[1]=11; x2[1]=11; y1[1]=10; y2[1]=1; - x1[2]=12; x2[2]=12; y1[2]=8; y2[2]=4; - x1[3]=1; x2[3]=1; y1[3]=10; y2[3]=8; - x1[4]=1; x2[4]=1; y1[4]=7; y2[4]=5; - x1[5]=5; x2[5]=5; y1[5]=5; y2[5]=3; - x1[6]=1; x2[6]=1; y1[6]=13; y2[6]=12; - x1[7]=3; x2[7]=3; y1[7]=5; y2[7]=0; - x1[8]=7; x2[8]=4; y1[8]=7; y2[8]=7; - x1[9]=10; x2[9]=2; y1[9]=5; y2[9]=5; - x1[10]=7; x2[10]=3; y1[10]=4; y2[10]=4; - x1[11]=1; x2[11]=1; y1[11]=4; y2[11]=2; - x1[12]=5; x2[12]=5; y1[12]=1; y2[12]=0; - break; - case 41: - x1[0]=8; x2[0]=7; y1[0]=7; y2[0]=7; - x1[1]=2; x2[1]=0; y1[1]=4; y2[1]=4; - x1[2]=11; x2[2]=4; y1[2]=0; y2[2]=0; - x1[3]=7; x2[3]=2; y1[3]=12; y2[3]=12; - x1[4]=10; x2[4]=10; y1[4]=4; y2[4]=1; - x1[5]=11; x2[5]=8; y1[5]=10; y2[5]=10; - x1[6]=9; x2[6]=9; y1[6]=10; y2[6]=5; - x1[7]=1; x2[7]=1; y1[7]=14; y2[7]=12; - x1[8]=7; x2[8]=3; y1[8]=2; y2[8]=2; - x1[9]=14; x2[9]=14; y1[9]=12; y2[9]=6; - x1[10]=7; x2[10]=7; y1[10]=13; y2[10]=7; - x1[11]=2; x2[11]=0; y1[11]=13; y2[11]=13; - x1[12]=14; x2[12]=6; y1[12]=3; y2[12]=3; - break; - case 42: - x1[0]=5; x2[0]=3; y1[0]=5; y2[0]=5; - x1[1]=5; x2[1]=1; y1[1]=4; y2[1]=4; - x1[2]=4; x2[2]=4; y1[2]=12; y2[2]=1; - x1[3]=13; x2[3]=13; y1[3]=12; y2[3]=0; - x1[4]=9; x2[4]=3; y1[4]=3; y2[4]=3; - x1[5]=10; x2[5]=9; y1[5]=8; y2[5]=8; - x1[6]=10; x2[6]=4; y1[6]=11; y2[6]=11; - x1[7]=7; x2[7]=1; y1[7]=10; y2[7]=10; - x1[8]=9; x2[8]=4; y1[8]=0; y2[8]=0; - x1[9]=7; x2[9]=0; y1[9]=10; y2[9]=10; - x1[10]=5; x2[10]=5; y1[10]=8; y2[10]=3; - x1[11]=6; x2[11]=3; y1[11]=9; y2[11]=9; - x1[12]=8; x2[12]=8; y1[12]=7; y2[12]=1; - break; - case 43: - x1[0]=0; x2[0]=0; y1[0]=13; y2[0]=1; - x1[1]=7; x2[1]=0; y1[1]=1; y2[1]=1; - x1[2]=6; x2[2]=6; y1[2]=9; y2[2]=3; - x1[3]=14; x2[3]=7; y1[3]=12; y2[3]=12; - x1[4]=4; x2[4]=4; y1[4]=13; y2[4]=8; - x1[5]=11; x2[5]=2; y1[5]=5; y2[5]=5; - x1[6]=9; x2[6]=6; y1[6]=7; y2[6]=7; - x1[7]=4; x2[7]=4; y1[7]=14; y2[7]=10; - x1[8]=2; x2[8]=2; y1[8]=6; y2[8]=5; - x1[9]=2; x2[9]=2; y1[9]=4; y2[9]=2; - x1[10]=11; x2[10]=5; y1[10]=7; y2[10]=7; - x1[11]=13; x2[11]=11; y1[11]=11; y2[11]=11; - x1[12]=13; x2[12]=3; y1[12]=13; y2[12]=13; - break; - case 44: - x1[0]=10; x2[0]=0; y1[0]=6; y2[0]=6; - x1[1]=9; x2[1]=5; y1[1]=11; y2[1]=11; - x1[2]=7; x2[2]=0; y1[2]=4; y2[2]=4; - x1[3]=13; x2[3]=0; y1[3]=1; y2[3]=1; - x1[4]=13; x2[4]=13; y1[4]=13; y2[4]=4; - x1[5]=12; x2[5]=12; y1[5]=9; y2[5]=8; - x1[6]=14; x2[6]=4; y1[6]=3; y2[6]=3; - x1[7]=0; x2[7]=0; y1[7]=11; y2[7]=10; - x1[8]=13; x2[8]=13; y1[8]=9; y2[8]=4; - x1[9]=1; x2[9]=0; y1[9]=4; y2[9]=4; - x1[10]=6; x2[10]=3; y1[10]=12; y2[10]=12; - x1[11]=2; x2[11]=2; y1[11]=8; y2[11]=6; - x1[12]=14; x2[12]=0; y1[12]=3; y2[12]=3; - break; - case 45: - x1[0]=3; x2[0]=2; y1[0]=9; y2[0]=9; - x1[1]=7; x2[1]=7; y1[1]=14; y2[1]=1; - x1[2]=5; x2[2]=2; y1[2]=14; y2[2]=14; - x1[3]=13; x2[3]=13; y1[3]=10; y2[3]=8; - x1[4]=7; x2[4]=3; y1[4]=6; y2[4]=6; - x1[5]=14; x2[5]=14; y1[5]=5; y2[5]=2; - x1[6]=5; x2[6]=5; y1[6]=14; y2[6]=10; - x1[7]=13; x2[7]=13; y1[7]=12; y2[7]=8; - x1[8]=2; x2[8]=2; y1[8]=14; y2[8]=7; - x1[9]=14; x2[9]=14; y1[9]=13; y2[9]=3; - x1[10]=9; x2[10]=1; y1[10]=0; y2[10]=0; - x1[11]=9; x2[11]=9; y1[11]=14; y2[11]=2; - x1[12]=4; x2[12]=1; y1[12]=8; y2[12]=8; - break; - case 46: - x1[0]=6; x2[0]=6; y1[0]=8; y2[0]=6; - x1[1]=14; x2[1]=10; y1[1]=10; y2[1]=10; - x1[2]=7; x2[2]=3; y1[2]=7; y2[2]=7; - x1[3]=14; x2[3]=14; y1[3]=9; y2[3]=6; - x1[4]=12; x2[4]=5; y1[4]=0; y2[4]=0; - x1[5]=7; x2[5]=6; y1[5]=13; y2[5]=13; - x1[6]=13; x2[6]=13; y1[6]=6; y2[6]=4; - x1[7]=11; x2[7]=11; y1[7]=13; y2[7]=10; - x1[8]=2; x2[8]=2; y1[8]=12; y2[8]=5; - x1[9]=8; x2[9]=1; y1[9]=6; y2[9]=6; - x1[10]=12; x2[10]=6; y1[10]=11; y2[10]=11; - x1[11]=14; x2[11]=14; y1[11]=12; y2[11]=2; - x1[12]=10; x2[12]=1; y1[12]=0; y2[12]=0; - break; - case 47: - x1[0]=12; x2[0]=12; y1[0]=13; y2[0]=6; - x1[1]=1; x2[1]=1; y1[1]=3; y2[1]=1; - x1[2]=6; x2[2]=6; y1[2]=12; y2[2]=3; - x1[3]=10; x2[3]=9; y1[3]=1; y2[3]=1; - x1[4]=12; x2[4]=10; y1[4]=9; y2[4]=9; - x1[5]=14; x2[5]=14; y1[5]=11; y2[5]=8; - x1[6]=9; x2[6]=2; y1[6]=7; y2[6]=7; - x1[7]=12; x2[7]=12; y1[7]=6; y2[7]=3; - x1[8]=7; x2[8]=6; y1[8]=11; y2[8]=11; - x1[9]=8; x2[9]=8; y1[9]=11; y2[9]=5; - x1[10]=2; x2[10]=2; y1[10]=14; y2[10]=1; - x1[11]=3; x2[11]=3; y1[11]=10; y2[11]=4; - x1[12]=7; x2[12]=7; y1[12]=13; y2[12]=10; - break; - case 48: - x1[0]=13; x2[0]=6; y1[0]=14; y2[0]=14; - x1[1]=12; x2[1]=12; y1[1]=7; y2[1]=1; - x1[2]=6; x2[2]=6; y1[2]=10; y2[2]=5; - x1[3]=4; x2[3]=1; y1[3]=10; y2[3]=10; - x1[4]=2; x2[4]=2; y1[4]=6; y2[4]=1; - x1[5]=5; x2[5]=5; y1[5]=9; y2[5]=7; - x1[6]=10; x2[6]=8; y1[6]=8; y2[6]=8; - x1[7]=13; x2[7]=1; y1[7]=12; y2[7]=12; - x1[8]=7; x2[8]=6; y1[8]=2; y2[8]=2; - x1[9]=9; x2[9]=9; y1[9]=13; y2[9]=12; - x1[10]=10; x2[10]=10; y1[10]=10; y2[10]=5; - x1[11]=12; x2[11]=12; y1[11]=8; y2[11]=2; - x1[12]=9; x2[12]=0; y1[12]=14; y2[12]=14; - break; - case 49: - x1[0]=10; x2[0]=10; y1[0]=14; y2[0]=9; - x1[1]=4; x2[1]=4; y1[1]=14; y2[1]=12; - x1[2]=13; x2[2]=7; y1[2]=14; y2[2]=14; - x1[3]=13; x2[3]=5; y1[3]=14; y2[3]=14; - x1[4]=13; x2[4]=13; y1[4]=14; y2[4]=7; - x1[5]=14; x2[5]=7; y1[5]=5; y2[5]=5; - x1[6]=11; x2[6]=9; y1[6]=13; y2[6]=13; - x1[7]=14; x2[7]=10; y1[7]=9; y2[7]=9; - x1[8]=8; x2[8]=6; y1[8]=8; y2[8]=8; - x1[9]=9; x2[9]=9; y1[9]=12; y2[9]=6; - x1[10]=14; x2[10]=14; y1[10]=11; y2[10]=6; - x1[11]=7; x2[11]=0; y1[11]=3; y2[11]=3; - x1[12]=2; x2[12]=2; y1[12]=5; y2[12]=3; - break; - case 50: - x1[0]=0; x2[0]=0; y1[0]=4; y2[0]=1; - x1[1]=11; x2[1]=2; y1[1]=12; y2[1]=12; - x1[2]=13; x2[2]=0; y1[2]=7; y2[2]=7; - x1[3]=3; x2[3]=3; y1[3]=14; y2[3]=0; - x1[4]=9; x2[4]=6; y1[4]=1; y2[4]=1; - x1[5]=14; x2[5]=14; y1[5]=13; y2[5]=2; - x1[6]=13; x2[6]=4; y1[6]=6; y2[6]=6; - x1[7]=13; x2[7]=5; y1[7]=11; y2[7]=11; - x1[8]=14; x2[8]=12; y1[8]=12; y2[8]=12; - x1[9]=12; x2[9]=12; y1[9]=4; y2[9]=0; - x1[10]=13; x2[10]=8; y1[10]=10; y2[10]=10; - x1[11]=14; x2[11]=4; y1[11]=8; y2[11]=8; - x1[12]=5; x2[12]=1; y1[12]=7; y2[12]=7; - break; - case 51: - x1[0]=12; x2[0]=12; y1[0]=13; y2[0]=10; - x1[1]=12; x2[1]=12; y1[1]=11; y2[1]=6; - x1[2]=12; x2[2]=2; y1[2]=7; y2[2]=7; - x1[3]=10; x2[3]=10; y1[3]=10; y2[3]=6; - x1[4]=7; x2[4]=7; y1[4]=13; y2[4]=0; - x1[5]=13; x2[5]=2; y1[5]=6; y2[5]=6; - x1[6]=1; x2[6]=1; y1[6]=12; y2[6]=9; - x1[7]=12; x2[7]=12; y1[7]=5; y2[7]=3; - x1[8]=14; x2[8]=11; y1[8]=2; y2[8]=2; - x1[9]=6; x2[9]=0; y1[9]=0; y2[9]=0; - x1[10]=9; x2[10]=6; y1[10]=12; y2[10]=12; - x1[11]=10; x2[11]=10; y1[11]=14; y2[11]=3; - x1[12]=9; x2[12]=2; y1[12]=11; y2[12]=11; - break; - case 52: - x1[0]=8; x2[0]=8; y1[0]=13; y2[0]=5; - x1[1]=5; x2[1]=0; y1[1]=4; y2[1]=4; - x1[2]=6; x2[2]=6; y1[2]=14; y2[2]=8; - x1[3]=13; x2[3]=13; y1[3]=14; y2[3]=12; - x1[4]=11; x2[4]=11; y1[4]=14; y2[4]=4; - x1[5]=9; x2[5]=7; y1[5]=10; y2[5]=10; - x1[6]=1; x2[6]=0; y1[6]=6; y2[6]=6; - x1[7]=11; x2[7]=8; y1[7]=2; y2[7]=2; - x1[8]=1; x2[8]=0; y1[8]=2; y2[8]=2; - x1[9]=12; x2[9]=12; y1[9]=11; y2[9]=4; - x1[10]=6; x2[10]=0; y1[10]=13; y2[10]=13; - x1[11]=11; x2[11]=11; y1[11]=9; y2[11]=2; - x1[12]=9; x2[12]=9; y1[12]=12; y2[12]=10; - break; - case 53: - x1[0]=8; x2[0]=1; y1[0]=3; y2[0]=3; - x1[1]=5; x2[1]=0; y1[1]=13; y2[1]=13; - x1[2]=8; x2[2]=8; y1[2]=12; y2[2]=3; - x1[3]=11; x2[3]=11; y1[3]=11; y2[3]=9; - x1[4]=14; x2[4]=4; y1[4]=7; y2[4]=7; - x1[5]=12; x2[5]=9; y1[5]=13; y2[5]=13; - x1[6]=13; x2[6]=0; y1[6]=2; y2[6]=2; - x1[7]=13; x2[7]=5; y1[7]=5; y2[7]=5; - x1[8]=6; x2[8]=6; y1[8]=5; y2[8]=2; - x1[9]=3; x2[9]=3; y1[9]=8; y2[9]=1; - x1[10]=12; x2[10]=2; y1[10]=4; y2[10]=4; - x1[11]=14; x2[11]=1; y1[11]=8; y2[11]=8; - x1[12]=6; x2[12]=3; y1[12]=7; y2[12]=7; - break; - case 54: - x1[0]=11; x2[0]=11; y1[0]=6; y2[0]=0; - x1[1]=4; x2[1]=4; y1[1]=9; y2[1]=6; - x1[2]=2; x2[2]=2; y1[2]=10; y2[2]=0; - x1[3]=11; x2[3]=4; y1[3]=1; y2[3]=1; - x1[4]=4; x2[4]=0; y1[4]=13; y2[4]=13; - x1[5]=12; x2[5]=12; y1[5]=4; y2[5]=1; - x1[6]=11; x2[6]=7; y1[6]=2; y2[6]=2; - x1[7]=5; x2[7]=5; y1[7]=12; y2[7]=7; - x1[8]=5; x2[8]=2; y1[8]=1; y2[8]=1; - x1[9]=0; x2[9]=0; y1[9]=11; y2[9]=5; - x1[10]=2; x2[10]=2; y1[10]=11; y2[10]=10; - x1[11]=12; x2[11]=4; y1[11]=5; y2[11]=5; - x1[12]=2; x2[12]=0; y1[12]=12; y2[12]=12; - break; - case 55: - x1[0]=12; x2[0]=4; y1[0]=12; y2[0]=12; - x1[1]=12; x2[1]=11; y1[1]=9; y2[1]=9; - x1[2]=8; x2[2]=3; y1[2]=13; y2[2]=13; - x1[3]=11; x2[3]=0; y1[3]=12; y2[3]=12; - x1[4]=7; x2[4]=2; y1[4]=4; y2[4]=4; - x1[5]=0; x2[5]=0; y1[5]=11; y2[5]=1; - x1[6]=11; x2[6]=11; y1[6]=4; y2[6]=3; - x1[7]=4; x2[7]=3; y1[7]=13; y2[7]=13; - x1[8]=9; x2[8]=9; y1[8]=14; y2[8]=1; - x1[9]=14; x2[9]=11; y1[9]=0; y2[9]=0; - x1[10]=11; x2[10]=11; y1[10]=5; y2[10]=3; - x1[11]=11; x2[11]=11; y1[11]=10; y2[11]=2; - x1[12]=9; x2[12]=0; y1[12]=11; y2[12]=11; - break; - case 56: - x1[0]=4; x2[0]=4; y1[0]=14; y2[0]=13; - x1[1]=9; x2[1]=9; y1[1]=14; y2[1]=12; - x1[2]=0; x2[2]=0; y1[2]=3; y2[2]=1; - x1[3]=8; x2[3]=4; y1[3]=2; y2[3]=2; - x1[4]=4; x2[4]=2; y1[4]=2; y2[4]=2; - x1[5]=13; x2[5]=11; y1[5]=8; y2[5]=8; - x1[6]=9; x2[6]=9; y1[6]=11; y2[6]=9; - x1[7]=2; x2[7]=2; y1[7]=12; y2[7]=9; - x1[8]=13; x2[8]=13; y1[8]=13; y2[8]=7; - x1[9]=6; x2[9]=6; y1[9]=2; y2[9]=1; - x1[10]=9; x2[10]=0; y1[10]=12; y2[10]=12; - x1[11]=5; x2[11]=5; y1[11]=7; y2[11]=3; - x1[12]=6; x2[12]=6; y1[12]=4; y2[12]=1; - break; - case 57: - x1[0]=0; x2[0]=0; y1[0]=8; y2[0]=3; - x1[1]=8; x2[1]=5; y1[1]=1; y2[1]=1; - x1[2]=13; x2[2]=8; y1[2]=11; y2[2]=11; - x1[3]=8; x2[3]=0; y1[3]=5; y2[3]=5; - x1[4]=10; x2[4]=7; y1[4]=13; y2[4]=13; - x1[5]=6; x2[5]=6; y1[5]=10; y2[5]=7; - x1[6]=11; x2[6]=9; y1[6]=8; y2[6]=8; - x1[7]=3; x2[7]=3; y1[7]=14; y2[7]=4; - x1[8]=9; x2[8]=9; y1[8]=9; y2[8]=7; - x1[9]=9; x2[9]=8; y1[9]=14; y2[9]=14; - x1[10]=6; x2[10]=6; y1[10]=14; y2[10]=2; - x1[11]=9; x2[11]=9; y1[11]=9; y2[11]=2; - x1[12]=4; x2[12]=4; y1[12]=9; y2[12]=8; - break; - case 58: - x1[0]=5; x2[0]=2; y1[0]=8; y2[0]=8; - x1[1]=14; x2[1]=14; y1[1]=13; y2[1]=0; - x1[2]=6; x2[2]=4; y1[2]=14; y2[2]=14; - x1[3]=7; x2[3]=5; y1[3]=9; y2[3]=9; - x1[4]=13; x2[4]=9; y1[4]=5; y2[4]=5; - x1[5]=0; x2[5]=0; y1[5]=12; y2[5]=11; - x1[6]=0; x2[6]=0; y1[6]=9; y2[6]=8; - x1[7]=2; x2[7]=2; y1[7]=10; y2[7]=9; - x1[8]=13; x2[8]=4; y1[8]=9; y2[8]=9; - x1[9]=13; x2[9]=3; y1[9]=0; y2[9]=0; - x1[10]=5; x2[10]=5; y1[10]=11; y2[10]=9; - x1[11]=11; x2[11]=2; y1[11]=8; y2[11]=8; - x1[12]=10; x2[12]=10; y1[12]=5; y2[12]=0; - break; - case 59: - x1[0]=5; x2[0]=5; y1[0]=10; y2[0]=5; - x1[1]=1; x2[1]=1; y1[1]=12; y2[1]=10; - x1[2]=12; x2[2]=12; y1[2]=9; y2[2]=1; - x1[3]=14; x2[3]=13; y1[3]=14; y2[3]=14; - x1[4]=0; x2[4]=0; y1[4]=10; y2[4]=4; - x1[5]=10; x2[5]=0; y1[5]=10; y2[5]=10; - x1[6]=13; x2[6]=12; y1[6]=13; y2[6]=13; - x1[7]=14; x2[7]=6; y1[7]=9; y2[7]=9; - x1[8]=0; x2[8]=0; y1[8]=6; y2[8]=3; - x1[9]=10; x2[9]=9; y1[9]=0; y2[9]=0; - x1[10]=0; x2[10]=0; y1[10]=13; y2[10]=9; - x1[11]=2; x2[11]=2; y1[11]=14; y2[11]=6; - x1[12]=9; x2[12]=7; y1[12]=1; y2[12]=1; - break; - case 60: - x1[0]=12; x2[0]=12; y1[0]=10; y2[0]=7; - x1[1]=9; x2[1]=9; y1[1]=6; y2[1]=0; - x1[2]=12; x2[2]=12; y1[2]=14; y2[2]=2; - x1[3]=8; x2[3]=1; y1[3]=14; y2[3]=14; - x1[4]=6; x2[4]=1; y1[4]=1; y2[4]=1; - x1[5]=11; x2[5]=8; y1[5]=14; y2[5]=14; - x1[6]=9; x2[6]=9; y1[6]=5; y2[6]=2; - x1[7]=14; x2[7]=14; y1[7]=9; y2[7]=7; - x1[8]=5; x2[8]=5; y1[8]=7; y2[8]=4; - x1[9]=2; x2[9]=2; y1[9]=3; y2[9]=0; - x1[10]=4; x2[10]=4; y1[10]=7; y2[10]=6; - x1[11]=11; x2[11]=11; y1[11]=12; y2[11]=7; - x1[12]=3; x2[12]=3; y1[12]=7; y2[12]=3; - break; - case 61: - x1[0]=14; x2[0]=14; y1[0]=9; y2[0]=0; - x1[1]=14; x2[1]=7; y1[1]=13; y2[1]=13; - x1[2]=10; x2[2]=10; y1[2]=9; y2[2]=8; - x1[3]=14; x2[3]=14; y1[3]=6; y2[3]=2; - x1[4]=6; x2[4]=6; y1[4]=8; y2[4]=4; - x1[5]=6; x2[5]=6; y1[5]=11; y2[5]=5; - x1[6]=8; x2[6]=8; y1[6]=12; y2[6]=9; - x1[7]=7; x2[7]=0; y1[7]=13; y2[7]=13; - x1[8]=2; x2[8]=2; y1[8]=5; y2[8]=2; - x1[9]=14; x2[9]=7; y1[9]=10; y2[9]=10; - x1[10]=8; x2[10]=8; y1[10]=8; y2[10]=4; - x1[11]=13; x2[11]=7; y1[11]=5; y2[11]=5; - x1[12]=11; x2[12]=10; y1[12]=13; y2[12]=13; - break; - case 62: - x1[0]=5; x2[0]=5; y1[0]=5; y2[0]=4; - x1[1]=11; x2[1]=0; y1[1]=10; y2[1]=10; - x1[2]=10; x2[2]=3; y1[2]=14; y2[2]=14; - x1[3]=11; x2[3]=3; y1[3]=10; y2[3]=10; - x1[4]=2; x2[4]=0; y1[4]=1; y2[4]=1; - x1[5]=6; x2[5]=6; y1[5]=7; y2[5]=2; - x1[6]=11; x2[6]=8; y1[6]=1; y2[6]=1; - x1[7]=10; x2[7]=10; y1[7]=14; y2[7]=12; - x1[8]=5; x2[8]=5; y1[8]=10; y2[8]=9; - x1[9]=6; x2[9]=2; y1[9]=12; y2[9]=12; - x1[10]=10; x2[10]=0; y1[10]=8; y2[10]=8; - x1[11]=5; x2[11]=1; y1[11]=1; y2[11]=1; - x1[12]=11; x2[12]=1; y1[12]=8; y2[12]=8; - break; - case 63: - x1[0]=12; x2[0]=8; y1[0]=13; y2[0]=13; - x1[1]=12; x2[1]=12; y1[1]=9; y2[1]=4; - x1[2]=4; x2[2]=4; y1[2]=12; y2[2]=2; - x1[3]=7; x2[3]=7; y1[3]=11; y2[3]=6; - x1[4]=10; x2[4]=4; y1[4]=10; y2[4]=10; - x1[5]=0; x2[5]=0; y1[5]=12; y2[5]=4; - x1[6]=9; x2[6]=7; y1[6]=9; y2[6]=9; - x1[7]=3; x2[7]=3; y1[7]=10; y2[7]=7; - x1[8]=9; x2[8]=9; y1[8]=4; y2[8]=2; - x1[9]=5; x2[9]=5; y1[9]=3; y2[9]=1; - x1[10]=10; x2[10]=0; y1[10]=9; y2[10]=9; - x1[11]=5; x2[11]=1; y1[11]=6; y2[11]=6; - x1[12]=9; x2[12]=9; y1[12]=4; y2[12]=3; - break; - case 64: - x1[0]=11; x2[0]=6; y1[0]=14; y2[0]=14; - x1[1]=11; x2[1]=10; y1[1]=7; y2[1]=7; - x1[2]=8; x2[2]=3; y1[2]=6; y2[2]=6; - x1[3]=7; x2[3]=7; y1[3]=11; y2[3]=8; - x1[4]=10; x2[4]=6; y1[4]=4; y2[4]=4; - x1[5]=10; x2[5]=6; y1[5]=11; y2[5]=11; - x1[6]=14; x2[6]=14; y1[6]=14; y2[6]=10; - x1[7]=3; x2[7]=3; y1[7]=8; y2[7]=3; - x1[8]=14; x2[8]=14; y1[8]=11; y2[8]=5; - x1[9]=9; x2[9]=9; y1[9]=7; y2[9]=5; - x1[10]=9; x2[10]=5; y1[10]=8; y2[10]=8; - x1[11]=12; x2[11]=6; y1[11]=1; y2[11]=1; - x1[12]=14; x2[12]=11; y1[12]=8; y2[12]=8; - break; - case 65: - x1[0]=11; x2[0]=5; y1[0]=14; y2[0]=14; - x1[1]=8; x2[1]=6; y1[1]=9; y2[1]=9; - x1[2]=1; x2[2]=1; y1[2]=5; y2[2]=0; - x1[3]=11; x2[3]=11; y1[3]=7; y2[3]=6; - x1[4]=13; x2[4]=13; y1[4]=7; y2[4]=2; - x1[5]=4; x2[5]=4; y1[5]=9; y2[5]=4; - x1[6]=8; x2[6]=7; y1[6]=1; y2[6]=1; - x1[7]=13; x2[7]=11; y1[7]=4; y2[7]=4; - x1[8]=3; x2[8]=3; y1[8]=4; y2[8]=3; - x1[9]=13; x2[9]=13; y1[9]=14; y2[9]=3; - x1[10]=6; x2[10]=2; y1[10]=1; y2[10]=1; - x1[11]=10; x2[11]=10; y1[11]=13; y2[11]=0; - x1[12]=4; x2[12]=1; y1[12]=4; y2[12]=4; - break; - case 66: - x1[0]=1; x2[0]=1; y1[0]=9; y2[0]=2; - x1[1]=11; x2[1]=11; y1[1]=9; y2[1]=1; - x1[2]=10; x2[2]=10; y1[2]=8; y2[2]=1; - x1[3]=13; x2[3]=13; y1[3]=10; y2[3]=2; - x1[4]=9; x2[4]=9; y1[4]=13; y2[4]=6; - x1[5]=14; x2[5]=13; y1[5]=8; y2[5]=8; - x1[6]=13; x2[6]=13; y1[6]=4; y2[6]=3; - x1[7]=6; x2[7]=4; y1[7]=7; y2[7]=7; - x1[8]=13; x2[8]=8; y1[8]=1; y2[8]=1; - x1[9]=13; x2[9]=13; y1[9]=6; y2[9]=0; - x1[10]=7; x2[10]=0; y1[10]=11; y2[10]=11; - x1[11]=6; x2[11]=6; y1[11]=6; y2[11]=3; - x1[12]=8; x2[12]=8; y1[12]=7; y2[12]=6; - break; - case 67: - x1[0]=5; x2[0]=5; y1[0]=13; y2[0]=4; - x1[1]=12; x2[1]=1; y1[1]=11; y2[1]=11; - x1[2]=10; x2[2]=5; y1[2]=10; y2[2]=10; - x1[3]=13; x2[3]=3; y1[3]=12; y2[3]=12; - x1[4]=6; x2[4]=6; y1[4]=13; y2[4]=11; - x1[5]=11; x2[5]=7; y1[5]=6; y2[5]=6; - x1[6]=12; x2[6]=12; y1[6]=8; y2[6]=7; - x1[7]=11; x2[7]=11; y1[7]=5; y2[7]=4; - x1[8]=1; x2[8]=1; y1[8]=13; y2[8]=0; - x1[9]=8; x2[9]=8; y1[9]=10; y2[9]=9; - x1[10]=2; x2[10]=2; y1[10]=9; y2[10]=3; - x1[11]=5; x2[11]=1; y1[11]=14; y2[11]=14; - x1[12]=4; x2[12]=2; y1[12]=10; y2[12]=10; - break; - case 68: - x1[0]=8; x2[0]=8; y1[0]=14; y2[0]=1; - x1[1]=8; x2[1]=8; y1[1]=5; y2[1]=1; - x1[2]=1; x2[2]=1; y1[2]=11; y2[2]=6; - x1[3]=4; x2[3]=0; y1[3]=9; y2[3]=9; - x1[4]=10; x2[4]=10; y1[4]=10; y2[4]=3; - x1[5]=13; x2[5]=3; y1[5]=1; y2[5]=1; - x1[6]=13; x2[6]=13; y1[6]=14; y2[6]=10; - x1[7]=13; x2[7]=13; y1[7]=4; y2[7]=1; - x1[8]=7; x2[8]=3; y1[8]=1; y2[8]=1; - x1[9]=6; x2[9]=1; y1[9]=8; y2[9]=8; - x1[10]=0; x2[10]=0; y1[10]=5; y2[10]=4; - x1[11]=1; x2[11]=1; y1[11]=11; y2[11]=4; - x1[12]=12; x2[12]=9; y1[12]=0; y2[12]=0; - break; - case 69: - x1[0]=0; x2[0]=0; y1[0]=14; y2[0]=1; - x1[1]=8; x2[1]=1; y1[1]=2; y2[1]=2; - x1[2]=13; x2[2]=5; y1[2]=9; y2[2]=9; - x1[3]=4; x2[3]=4; y1[3]=14; y2[3]=9; - x1[4]=5; x2[4]=0; y1[4]=0; y2[4]=0; - x1[5]=14; x2[5]=8; y1[5]=1; y2[5]=1; - x1[6]=1; x2[6]=1; y1[6]=13; y2[6]=3; - x1[7]=6; x2[7]=6; y1[7]=4; y2[7]=2; - x1[8]=3; x2[8]=1; y1[8]=2; y2[8]=2; - x1[9]=6; x2[9]=1; y1[9]=3; y2[9]=3; - x1[10]=11; x2[10]=3; y1[10]=5; y2[10]=5; - x1[11]=9; x2[11]=0; y1[11]=8; y2[11]=8; - x1[12]=13; x2[12]=11; y1[12]=6; y2[12]=6; - break; - case 70: - x1[0]=11; x2[0]=0; y1[0]=2; y2[0]=2; - x1[1]=6; x2[1]=6; y1[1]=9; y2[1]=7; - x1[2]=2; x2[2]=2; y1[2]=5; y2[2]=1; - x1[3]=12; x2[3]=12; y1[3]=13; y2[3]=8; - x1[4]=2; x2[4]=2; y1[4]=9; y2[4]=4; - x1[5]=14; x2[5]=9; y1[5]=4; y2[5]=4; - x1[6]=9; x2[6]=9; y1[6]=4; y2[6]=1; - x1[7]=4; x2[7]=3; y1[7]=5; y2[7]=5; - x1[8]=10; x2[8]=5; y1[8]=3; y2[8]=3; - x1[9]=1; x2[9]=1; y1[9]=13; y2[9]=10; - x1[10]=12; x2[10]=3; y1[10]=12; y2[10]=12; - x1[11]=11; x2[11]=1; y1[11]=4; y2[11]=4; - x1[12]=5; x2[12]=3; y1[12]=3; y2[12]=3; - break; - case 71: - x1[0]=12; x2[0]=8; y1[0]=0; y2[0]=0; - x1[1]=10; x2[1]=7; y1[1]=3; y2[1]=3; - x1[2]=3; x2[2]=0; y1[2]=2; y2[2]=2; - x1[3]=12; x2[3]=12; y1[3]=12; y2[3]=10; - x1[4]=11; x2[4]=0; y1[4]=7; y2[4]=7; - x1[5]=12; x2[5]=12; y1[5]=3; y2[5]=0; - x1[6]=13; x2[6]=13; y1[6]=9; y2[6]=5; - x1[7]=10; x2[7]=9; y1[7]=6; y2[7]=6; - x1[8]=4; x2[8]=2; y1[8]=14; y2[8]=14; - x1[9]=11; x2[9]=11; y1[9]=6; y2[9]=4; - x1[10]=7; x2[10]=7; y1[10]=9; y2[10]=3; - x1[11]=7; x2[11]=7; y1[11]=10; y2[11]=8; - x1[12]=13; x2[12]=0; y1[12]=6; y2[12]=6; - break; - case 72: - x1[0]=10; x2[0]=3; y1[0]=5; y2[0]=5; - x1[1]=10; x2[1]=1; y1[1]=8; y2[1]=8; - x1[2]=12; x2[2]=10; y1[2]=7; y2[2]=7; - x1[3]=1; x2[3]=1; y1[3]=12; y2[3]=4; - x1[4]=4; x2[4]=4; y1[4]=9; y2[4]=2; - x1[5]=10; x2[5]=5; y1[5]=14; y2[5]=14; - x1[6]=14; x2[6]=2; y1[6]=13; y2[6]=13; - x1[7]=1; x2[7]=1; y1[7]=11; y2[7]=10; - x1[8]=13; x2[8]=13; y1[8]=9; y2[8]=1; - x1[9]=13; x2[9]=8; y1[9]=3; y2[9]=3; - x1[10]=11; x2[10]=7; y1[10]=9; y2[10]=9; - x1[11]=12; x2[11]=4; y1[11]=3; y2[11]=3; - x1[12]=4; x2[12]=0; y1[12]=2; y2[12]=2; - break; - case 73: - x1[0]=4; x2[0]=0; y1[0]=12; y2[0]=12; - x1[1]=2; x2[1]=1; y1[1]=10; y2[1]=10; - x1[2]=9; x2[2]=8; y1[2]=3; y2[2]=3; - x1[3]=13; x2[3]=13; y1[3]=12; y2[3]=11; - x1[4]=9; x2[4]=5; y1[4]=2; y2[4]=2; - x1[5]=3; x2[5]=3; y1[5]=2; y2[5]=1; - x1[6]=0; x2[6]=0; y1[6]=12; y2[6]=2; - x1[7]=14; x2[7]=5; y1[7]=14; y2[7]=14; - x1[8]=2; x2[8]=2; y1[8]=14; y2[8]=8; - x1[9]=12; x2[9]=12; y1[9]=10; y2[9]=6; - x1[10]=10; x2[10]=10; y1[10]=8; y2[10]=4; - x1[11]=12; x2[11]=12; y1[11]=11; y2[11]=1; - x1[12]=2; x2[12]=2; y1[12]=14; y2[12]=9; - break; - case 74: - x1[0]=9; x2[0]=9; y1[0]=11; y2[0]=2; - x1[1]=8; x2[1]=8; y1[1]=14; y2[1]=13; - x1[2]=7; x2[2]=7; y1[2]=11; y2[2]=10; - x1[3]=9; x2[3]=9; y1[3]=11; y2[3]=7; - x1[4]=14; x2[4]=10; y1[4]=3; y2[4]=3; - x1[5]=9; x2[5]=6; y1[5]=9; y2[5]=9; - x1[6]=10; x2[6]=10; y1[6]=9; y2[6]=4; - x1[7]=3; x2[7]=1; y1[7]=1; y2[7]=1; - x1[8]=13; x2[8]=12; y1[8]=2; y2[8]=2; - x1[9]=8; x2[9]=8; y1[9]=10; y2[9]=3; - x1[10]=9; x2[10]=9; y1[10]=2; y2[10]=0; - x1[11]=8; x2[11]=5; y1[11]=12; y2[11]=12; - x1[12]=11; x2[12]=11; y1[12]=14; y2[12]=0; - break; - case 75: - x1[0]=12; x2[0]=12; y1[0]=11; y2[0]=2; - x1[1]=4; x2[1]=4; y1[1]=8; y2[1]=3; - x1[2]=7; x2[2]=2; y1[2]=14; y2[2]=14; - x1[3]=14; x2[3]=4; y1[3]=4; y2[3]=4; - x1[4]=7; x2[4]=7; y1[4]=5; y2[4]=1; - x1[5]=14; x2[5]=6; y1[5]=10; y2[5]=10; - x1[6]=13; x2[6]=9; y1[6]=0; y2[6]=0; - x1[7]=5; x2[7]=5; y1[7]=11; y2[7]=4; - x1[8]=10; x2[8]=1; y1[8]=10; y2[8]=10; - x1[9]=0; x2[9]=0; y1[9]=10; y2[9]=7; - x1[10]=5; x2[10]=4; y1[10]=7; y2[10]=7; - x1[11]=14; x2[11]=4; y1[11]=14; y2[11]=14; - x1[12]=9; x2[12]=0; y1[12]=2; y2[12]=2; - break; - case 76: - x1[0]=14; x2[0]=4; y1[0]=6; y2[0]=6; - x1[1]=14; x2[1]=3; y1[1]=3; y2[1]=3; - x1[2]=13; x2[2]=7; y1[2]=2; y2[2]=2; - x1[3]=2; x2[3]=0; y1[3]=10; y2[3]=10; - x1[4]=4; x2[4]=4; y1[4]=13; y2[4]=4; - x1[5]=9; x2[5]=3; y1[5]=1; y2[5]=1; - x1[6]=11; x2[6]=11; y1[6]=13; y2[6]=4; - x1[7]=13; x2[7]=13; y1[7]=8; y2[7]=1; - x1[8]=1; x2[8]=0; y1[8]=1; y2[8]=1; - x1[9]=8; x2[9]=3; y1[9]=14; y2[9]=14; - x1[10]=12; x2[10]=12; y1[10]=6; y2[10]=0; - x1[11]=11; x2[11]=11; y1[11]=10; y2[11]=5; - x1[12]=7; x2[12]=6; y1[12]=9; y2[12]=9; - break; - case 77: - x1[0]=3; x2[0]=3; y1[0]=12; y2[0]=6; - x1[1]=0; x2[1]=0; y1[1]=7; y2[1]=6; - x1[2]=3; x2[2]=0; y1[2]=9; y2[2]=9; - x1[3]=14; x2[3]=3; y1[3]=1; y2[3]=1; - x1[4]=9; x2[4]=0; y1[4]=1; y2[4]=1; - x1[5]=1; x2[5]=1; y1[5]=14; y2[5]=6; - x1[6]=13; x2[6]=6; y1[6]=7; y2[6]=7; - x1[7]=12; x2[7]=7; y1[7]=0; y2[7]=0; - x1[8]=14; x2[8]=4; y1[8]=2; y2[8]=2; - x1[9]=3; x2[9]=3; y1[9]=13; y2[9]=7; - x1[10]=7; x2[10]=7; y1[10]=10; y2[10]=6; - x1[11]=4; x2[11]=4; y1[11]=13; y2[11]=2; - x1[12]=13; x2[12]=13; y1[12]=10; y2[12]=1; - break; - case 78: - x1[0]=13; x2[0]=13; y1[0]=13; y2[0]=12; - x1[1]=1; x2[1]=1; y1[1]=13; y2[1]=1; - x1[2]=0; x2[2]=0; y1[2]=5; y2[2]=3; - x1[3]=14; x2[3]=14; y1[3]=13; y2[3]=10; - x1[4]=14; x2[4]=14; y1[4]=10; y2[4]=2; - x1[5]=7; x2[5]=2; y1[5]=1; y2[5]=1; - x1[6]=2; x2[6]=2; y1[6]=13; y2[6]=4; - x1[7]=14; x2[7]=9; y1[7]=11; y2[7]=11; - x1[8]=5; x2[8]=3; y1[8]=4; y2[8]=4; - x1[9]=12; x2[9]=6; y1[9]=9; y2[9]=9; - x1[10]=4; x2[10]=4; y1[10]=10; y2[10]=1; - x1[11]=11; x2[11]=3; y1[11]=4; y2[11]=4; - x1[12]=1; x2[12]=1; y1[12]=8; y2[12]=1; - break; - case 79: - x1[0]=5; x2[0]=5; y1[0]=11; y2[0]=6; - x1[1]=2; x2[1]=2; y1[1]=10; y2[1]=5; - x1[2]=13; x2[2]=0; y1[2]=10; y2[2]=10; - x1[3]=10; x2[3]=8; y1[3]=7; y2[3]=7; - x1[4]=11; x2[4]=11; y1[4]=7; y2[4]=2; - x1[5]=11; x2[5]=1; y1[5]=0; y2[5]=0; - x1[6]=10; x2[6]=5; y1[6]=0; y2[6]=0; - x1[7]=10; x2[7]=10; y1[7]=14; y2[7]=13; - x1[8]=5; x2[8]=5; y1[8]=4; y2[8]=3; - x1[9]=3; x2[9]=3; y1[9]=5; y2[9]=2; - x1[10]=12; x2[10]=8; y1[10]=2; y2[10]=2; - x1[11]=5; x2[11]=5; y1[11]=13; y2[11]=12; - x1[12]=2; x2[12]=2; y1[12]=8; y2[12]=2; - break; - case 80: - x1[0]=12; x2[0]=12; y1[0]=6; y2[0]=4; - x1[1]=7; x2[1]=2; y1[1]=9; y2[1]=9; - x1[2]=7; x2[2]=7; y1[2]=3; y2[2]=2; - x1[3]=7; x2[3]=2; y1[3]=2; y2[3]=2; - x1[4]=10; x2[4]=10; y1[4]=11; y2[4]=9; - x1[5]=4; x2[5]=3; y1[5]=11; y2[5]=11; - x1[6]=1; x2[6]=1; y1[6]=7; y2[6]=4; - x1[7]=4; x2[7]=4; y1[7]=14; y2[7]=3; - x1[8]=9; x2[8]=6; y1[8]=14; y2[8]=14; - x1[9]=9; x2[9]=9; y1[9]=3; y2[9]=0; - x1[10]=14; x2[10]=14; y1[10]=1; y2[10]=0; - x1[11]=11; x2[11]=10; y1[11]=12; y2[11]=12; - x1[12]=13; x2[12]=12; y1[12]=12; y2[12]=12; - break; - case 81: - x1[0]=8; x2[0]=8; y1[0]=8; y2[0]=0; - x1[1]=6; x2[1]=5; y1[1]=9; y2[1]=9; - x1[2]=13; x2[2]=5; y1[2]=12; y2[2]=12; - x1[3]=12; x2[3]=12; y1[3]=10; y2[3]=3; - x1[4]=13; x2[4]=13; y1[4]=13; y2[4]=1; - x1[5]=9; x2[5]=2; y1[5]=0; y2[5]=0; - x1[6]=12; x2[6]=12; y1[6]=14; y2[6]=9; - x1[7]=14; x2[7]=14; y1[7]=8; y2[7]=4; - x1[8]=10; x2[8]=2; y1[8]=7; y2[8]=7; - x1[9]=13; x2[9]=11; y1[9]=3; y2[9]=3; - x1[10]=9; x2[10]=3; y1[10]=12; y2[10]=12; - x1[11]=6; x2[11]=0; y1[11]=6; y2[11]=6; - x1[12]=8; x2[12]=7; y1[12]=12; y2[12]=12; - break; - case 82: - x1[0]=14; x2[0]=14; y1[0]=12; y2[0]=8; - x1[1]=3; x2[1]=2; y1[1]=12; y2[1]=12; - x1[2]=9; x2[2]=7; y1[2]=7; y2[2]=7; - x1[3]=9; x2[3]=2; y1[3]=3; y2[3]=3; - x1[4]=6; x2[4]=6; y1[4]=11; y2[4]=10; - x1[5]=11; x2[5]=11; y1[5]=13; y2[5]=2; - x1[6]=2; x2[6]=2; y1[6]=12; y2[6]=4; - x1[7]=14; x2[7]=0; y1[7]=8; y2[7]=8; - x1[8]=5; x2[8]=5; y1[8]=9; y2[8]=5; - x1[9]=6; x2[9]=5; y1[9]=3; y2[9]=3; - x1[10]=9; x2[10]=9; y1[10]=9; y2[10]=0; - x1[11]=5; x2[11]=1; y1[11]=9; y2[11]=9; - x1[12]=8; x2[12]=8; y1[12]=14; y2[12]=3; - break; - case 83: - x1[0]=10; x2[0]=10; y1[0]=14; y2[0]=6; - x1[1]=10; x2[1]=1; y1[1]=3; y2[1]=3; - x1[2]=10; x2[2]=10; y1[2]=13; y2[2]=2; - x1[3]=13; x2[3]=1; y1[3]=10; y2[3]=10; - x1[4]=1; x2[4]=1; y1[4]=14; y2[4]=5; - x1[5]=8; x2[5]=0; y1[5]=8; y2[5]=8; - x1[6]=12; x2[6]=7; y1[6]=1; y2[6]=1; - x1[7]=4; x2[7]=4; y1[7]=12; y2[7]=7; - x1[8]=10; x2[8]=9; y1[8]=2; y2[8]=2; - x1[9]=11; x2[9]=5; y1[9]=13; y2[9]=13; - x1[10]=0; x2[10]=0; y1[10]=6; y2[10]=0; - x1[11]=2; x2[11]=2; y1[11]=9; y2[11]=6; - x1[12]=3; x2[12]=3; y1[12]=4; y2[12]=1; - break; - case 84: - x1[0]=4; x2[0]=4; y1[0]=13; y2[0]=6; - x1[1]=10; x2[1]=10; y1[1]=7; y2[1]=3; - x1[2]=14; x2[2]=2; y1[2]=4; y2[2]=4; - x1[3]=10; x2[3]=0; y1[3]=1; y2[3]=1; - x1[4]=9; x2[4]=8; y1[4]=4; y2[4]=4; - x1[5]=5; x2[5]=2; y1[5]=2; y2[5]=2; - x1[6]=10; x2[6]=9; y1[6]=14; y2[6]=14; - x1[7]=11; x2[7]=11; y1[7]=2; y2[7]=1; - x1[8]=6; x2[8]=6; y1[8]=11; y2[8]=3; - x1[9]=13; x2[9]=3; y1[9]=9; y2[9]=9; - x1[10]=10; x2[10]=0; y1[10]=4; y2[10]=4; - x1[11]=9; x2[11]=5; y1[11]=12; y2[11]=12; - x1[12]=3; x2[12]=1; y1[12]=9; y2[12]=9; - break; - case 85: - x1[0]=8; x2[0]=8; y1[0]=12; y2[0]=7; - x1[1]=10; x2[1]=4; y1[1]=13; y2[1]=13; - x1[2]=14; x2[2]=1; y1[2]=13; y2[2]=13; - x1[3]=9; x2[3]=7; y1[3]=11; y2[3]=11; - x1[4]=5; x2[4]=5; y1[4]=14; y2[4]=9; - x1[5]=5; x2[5]=5; y1[5]=10; y2[5]=7; - x1[6]=7; x2[6]=5; y1[6]=4; y2[6]=4; - x1[7]=8; x2[7]=1; y1[7]=12; y2[7]=12; - x1[8]=11; x2[8]=4; y1[8]=10; y2[8]=10; - x1[9]=5; x2[9]=3; y1[9]=9; y2[9]=9; - x1[10]=11; x2[10]=4; y1[10]=4; y2[10]=4; - x1[11]=11; x2[11]=11; y1[11]=10; y2[11]=9; - x1[12]=14; x2[12]=6; y1[12]=0; y2[12]=0; - break; - case 86: - x1[0]=9; x2[0]=9; y1[0]=5; y2[0]=1; - x1[1]=6; x2[1]=0; y1[1]=3; y2[1]=3; - x1[2]=2; x2[2]=2; y1[2]=2; y2[2]=1; - x1[3]=0; x2[3]=0; y1[3]=9; y2[3]=0; - x1[4]=10; x2[4]=6; y1[4]=2; y2[4]=2; - x1[5]=8; x2[5]=2; y1[5]=8; y2[5]=8; - x1[6]=8; x2[6]=8; y1[6]=13; y2[6]=3; - x1[7]=11; x2[7]=1; y1[7]=7; y2[7]=7; - x1[8]=12; x2[8]=4; y1[8]=1; y2[8]=1; - x1[9]=14; x2[9]=14; y1[9]=11; y2[9]=10; - x1[10]=6; x2[10]=6; y1[10]=13; y2[10]=7; - x1[11]=14; x2[11]=14; y1[11]=6; y2[11]=5; - x1[12]=11; x2[12]=5; y1[12]=5; y2[12]=5; - break; - case 87: - x1[0]=2; x2[0]=1; y1[0]=8; y2[0]=8; - x1[1]=14; x2[1]=14; y1[1]=13; y2[1]=9; - x1[2]=4; x2[2]=4; y1[2]=13; y2[2]=9; - x1[3]=11; x2[3]=11; y1[3]=8; y2[3]=0; - x1[4]=11; x2[4]=7; y1[4]=13; y2[4]=13; - x1[5]=11; x2[5]=8; y1[5]=3; y2[5]=3; - x1[6]=8; x2[6]=2; y1[6]=13; y2[6]=13; - x1[7]=12; x2[7]=0; y1[7]=13; y2[7]=13; - x1[8]=9; x2[8]=3; y1[8]=14; y2[8]=14; - x1[9]=13; x2[9]=7; y1[9]=11; y2[9]=11; - x1[10]=0; x2[10]=0; y1[10]=12; y2[10]=5; - x1[11]=12; x2[11]=3; y1[11]=5; y2[11]=5; - x1[12]=4; x2[12]=2; y1[12]=9; y2[12]=9; - break; - case 88: - x1[0]=14; x2[0]=2; y1[0]=11; y2[0]=11; - x1[1]=12; x2[1]=10; y1[1]=12; y2[1]=12; - x1[2]=14; x2[2]=7; y1[2]=3; y2[2]=3; - x1[3]=14; x2[3]=14; y1[3]=13; y2[3]=4; - x1[4]=0; x2[4]=0; y1[4]=6; y2[4]=4; - x1[5]=10; x2[5]=2; y1[5]=4; y2[5]=4; - x1[6]=0; x2[6]=0; y1[6]=10; y2[6]=9; - x1[7]=9; x2[7]=4; y1[7]=3; y2[7]=3; - x1[8]=11; x2[8]=2; y1[8]=13; y2[8]=13; - x1[9]=13; x2[9]=1; y1[9]=2; y2[9]=2; - x1[10]=10; x2[10]=6; y1[10]=5; y2[10]=5; - x1[11]=14; x2[11]=10; y1[11]=4; y2[11]=4; - x1[12]=1; x2[12]=1; y1[12]=12; y2[12]=6; - break; - case 89: - x1[0]=4; x2[0]=2; y1[0]=8; y2[0]=8; - x1[1]=6; x2[1]=4; y1[1]=8; y2[1]=8; - x1[2]=12; x2[2]=12; y1[2]=6; y2[2]=2; - x1[3]=4; x2[3]=4; y1[3]=9; y2[3]=3; - x1[4]=12; x2[4]=0; y1[4]=0; y2[4]=0; - x1[5]=9; x2[5]=3; y1[5]=6; y2[5]=6; - x1[6]=11; x2[6]=0; y1[6]=3; y2[6]=3; - x1[7]=2; x2[7]=2; y1[7]=13; y2[7]=7; - x1[8]=12; x2[8]=5; y1[8]=13; y2[8]=13; - x1[9]=13; x2[9]=13; y1[9]=12; y2[9]=3; - x1[10]=2; x2[10]=2; y1[10]=12; y2[10]=11; - x1[11]=4; x2[11]=4; y1[11]=12; y2[11]=5; - x1[12]=6; x2[12]=6; y1[12]=2; y2[12]=0; - break; - case 90: - x1[0]=8; x2[0]=5; y1[0]=9; y2[0]=9; - x1[1]=13; x2[1]=10; y1[1]=10; y2[1]=10; - x1[2]=4; x2[2]=4; y1[2]=7; y2[2]=2; - x1[3]=12; x2[3]=7; y1[3]=14; y2[3]=14; - x1[4]=3; x2[4]=3; y1[4]=12; y2[4]=7; - x1[5]=10; x2[5]=2; y1[5]=12; y2[5]=12; - x1[6]=13; x2[6]=5; y1[6]=2; y2[6]=2; - x1[7]=14; x2[7]=10; y1[7]=7; y2[7]=7; - x1[8]=10; x2[8]=8; y1[8]=14; y2[8]=14; - x1[9]=10; x2[9]=7; y1[9]=12; y2[9]=12; - x1[10]=12; x2[10]=6; y1[10]=4; y2[10]=4; - x1[11]=12; x2[11]=5; y1[11]=12; y2[11]=12; - x1[12]=11; x2[12]=11; y1[12]=14; y2[12]=13; - break; - case 91: - x1[0]=9; x2[0]=2; y1[0]=2; y2[0]=2; - x1[1]=3; x2[1]=3; y1[1]=9; y2[1]=7; - x1[2]=12; x2[2]=11; y1[2]=0; y2[2]=0; - x1[3]=14; x2[3]=4; y1[3]=5; y2[3]=5; - x1[4]=2; x2[4]=2; y1[4]=11; y2[4]=0; - x1[5]=0; x2[5]=0; y1[5]=13; y2[5]=10; - x1[6]=14; x2[6]=10; y1[6]=2; y2[6]=2; - x1[7]=10; x2[7]=10; y1[7]=8; y2[7]=2; - x1[8]=3; x2[8]=3; y1[8]=5; y2[8]=4; - x1[9]=5; x2[9]=2; y1[9]=4; y2[9]=4; - x1[10]=14; x2[10]=14; y1[10]=10; y2[10]=7; - x1[11]=13; x2[11]=10; y1[11]=9; y2[11]=9; - x1[12]=14; x2[12]=1; y1[12]=11; y2[12]=11; - break; - case 92: - x1[0]=10; x2[0]=1; y1[0]=11; y2[0]=11; - x1[1]=9; x2[1]=6; y1[1]=4; y2[1]=4; - x1[2]=9; x2[2]=9; y1[2]=7; y2[2]=4; - x1[3]=13; x2[3]=12; y1[3]=11; y2[3]=11; - x1[4]=9; x2[4]=9; y1[4]=11; y2[4]=4; - x1[5]=14; x2[5]=4; y1[5]=1; y2[5]=1; - x1[6]=9; x2[6]=3; y1[6]=10; y2[6]=10; - x1[7]=5; x2[7]=0; y1[7]=6; y2[7]=6; - x1[8]=2; x2[8]=2; y1[8]=12; y2[8]=8; - x1[9]=10; x2[9]=10; y1[9]=6; y2[9]=5; - x1[10]=10; x2[10]=1; y1[10]=4; y2[10]=4; - x1[11]=12; x2[11]=0; y1[11]=10; y2[11]=10; - x1[12]=4; x2[12]=4; y1[12]=9; y2[12]=7; - break; - case 93: - x1[0]=10; x2[0]=10; y1[0]=10; y2[0]=8; - x1[1]=12; x2[1]=8; y1[1]=10; y2[1]=10; - x1[2]=8; x2[2]=1; y1[2]=10; y2[2]=10; - x1[3]=14; x2[3]=8; y1[3]=6; y2[3]=6; - x1[4]=2; x2[4]=2; y1[4]=8; y2[4]=4; - x1[5]=9; x2[5]=9; y1[5]=10; y2[5]=8; - x1[6]=13; x2[6]=3; y1[6]=4; y2[6]=4; - x1[7]=9; x2[7]=8; y1[7]=5; y2[7]=5; - x1[8]=0; x2[8]=0; y1[8]=11; y2[8]=0; - x1[9]=9; x2[9]=4; y1[9]=13; y2[9]=13; - x1[10]=1; x2[10]=1; y1[10]=9; y2[10]=5; - x1[11]=9; x2[11]=6; y1[11]=3; y2[11]=3; - x1[12]=0; x2[12]=0; y1[12]=14; y2[12]=0; - break; - case 94: - x1[0]=5; x2[0]=5; y1[0]=5; y2[0]=0; - x1[1]=9; x2[1]=9; y1[1]=10; y2[1]=9; - x1[2]=4; x2[2]=4; y1[2]=12; y2[2]=11; - x1[3]=10; x2[3]=4; y1[3]=1; y2[3]=1; - x1[4]=13; x2[4]=4; y1[4]=3; y2[4]=3; - x1[5]=9; x2[5]=9; y1[5]=8; y2[5]=6; - x1[6]=13; x2[6]=13; y1[6]=13; y2[6]=0; - x1[7]=0; x2[7]=0; y1[7]=7; y2[7]=4; - x1[8]=4; x2[8]=1; y1[8]=6; y2[8]=6; - x1[9]=8; x2[9]=3; y1[9]=5; y2[9]=5; - x1[10]=11; x2[10]=6; y1[10]=11; y2[10]=11; - x1[11]=10; x2[11]=10; y1[11]=12; y2[11]=8; - x1[12]=10; x2[12]=4; y1[12]=9; y2[12]=9; - break; - case 95: - x1[0]=12; x2[0]=12; y1[0]=7; y2[0]=2; - x1[1]=12; x2[1]=8; y1[1]=5; y2[1]=5; - x1[2]=9; x2[2]=8; y1[2]=10; y2[2]=10; - x1[3]=3; x2[3]=3; y1[3]=7; y2[3]=5; - x1[4]=5; x2[4]=5; y1[4]=12; y2[4]=3; - x1[5]=14; x2[5]=11; y1[5]=5; y2[5]=5; - x1[6]=0; x2[6]=0; y1[6]=6; y2[6]=2; - x1[7]=5; x2[7]=5; y1[7]=6; y2[7]=5; - x1[8]=11; x2[8]=11; y1[8]=14; y2[8]=6; - x1[9]=10; x2[9]=7; y1[9]=6; y2[9]=6; - x1[10]=14; x2[10]=14; y1[10]=14; y2[10]=3; - x1[11]=4; x2[11]=3; y1[11]=8; y2[11]=8; - x1[12]=1; x2[12]=1; y1[12]=5; y2[12]=1; - break; - case 96: - x1[0]=9; x2[0]=9; y1[0]=8; y2[0]=5; - x1[1]=2; x2[1]=2; y1[1]=14; y2[1]=3; - x1[2]=14; x2[2]=5; y1[2]=11; y2[2]=11; - x1[3]=11; x2[3]=7; y1[3]=7; y2[3]=7; - x1[4]=14; x2[4]=12; y1[4]=7; y2[4]=7; - x1[5]=5; x2[5]=5; y1[5]=13; y2[5]=5; - x1[6]=8; x2[6]=1; y1[6]=4; y2[6]=4; - x1[7]=3; x2[7]=0; y1[7]=14; y2[7]=14; - x1[8]=8; x2[8]=8; y1[8]=14; y2[8]=7; - x1[9]=9; x2[9]=1; y1[9]=3; y2[9]=3; - x1[10]=4; x2[10]=4; y1[10]=8; y2[10]=4; - x1[11]=5; x2[11]=5; y1[11]=6; y2[11]=0; - x1[12]=11; x2[12]=11; y1[12]=3; y2[12]=0; - break; - case 97: - x1[0]=12; x2[0]=6; y1[0]=6; y2[0]=6; - x1[1]=10; x2[1]=6; y1[1]=13; y2[1]=13; - x1[2]=11; x2[2]=8; y1[2]=5; y2[2]=5; - x1[3]=0; x2[3]=0; y1[3]=9; y2[3]=1; - x1[4]=13; x2[4]=1; y1[4]=6; y2[4]=6; - x1[5]=8; x2[5]=0; y1[5]=12; y2[5]=12; - x1[6]=1; x2[6]=1; y1[6]=6; y2[6]=3; - x1[7]=1; x2[7]=1; y1[7]=3; y2[7]=0; - x1[8]=7; x2[8]=4; y1[8]=12; y2[8]=12; - x1[9]=11; x2[9]=6; y1[9]=1; y2[9]=1; - x1[10]=10; x2[10]=7; y1[10]=1; y2[10]=1; - x1[11]=8; x2[11]=5; y1[11]=6; y2[11]=6; - x1[12]=5; x2[12]=3; y1[12]=6; y2[12]=6; - break; - case 98: - x1[0]=1; x2[0]=1; y1[0]=7; y2[0]=1; - x1[1]=9; x2[1]=9; y1[1]=14; y2[1]=5; - x1[2]=9; x2[2]=9; y1[2]=11; y2[2]=6; - x1[3]=11; x2[3]=11; y1[3]=4; y2[3]=0; - x1[4]=0; x2[4]=0; y1[4]=11; y2[4]=8; - x1[5]=12; x2[5]=8; y1[5]=3; y2[5]=3; - x1[6]=11; x2[6]=7; y1[6]=11; y2[6]=11; - x1[7]=7; x2[7]=0; y1[7]=8; y2[7]=8; - x1[8]=12; x2[8]=12; y1[8]=3; y2[8]=2; - x1[9]=10; x2[9]=4; y1[9]=14; y2[9]=14; - x1[10]=6; x2[10]=6; y1[10]=8; y2[10]=0; - x1[11]=6; x2[11]=6; y1[11]=14; y2[11]=12; - x1[12]=11; x2[12]=9; y1[12]=9; y2[12]=9; - break; - case 99: - x1[0]=8; x2[0]=5; y1[0]=14; y2[0]=14; - x1[1]=13; x2[1]=6; y1[1]=1; y2[1]=1; - x1[2]=8; x2[2]=0; y1[2]=13; y2[2]=13; - x1[3]=5; x2[3]=5; y1[3]=2; y2[3]=0; - x1[4]=9; x2[4]=1; y1[4]=14; y2[4]=14; - x1[5]=12; x2[5]=12; y1[5]=11; y2[5]=10; - x1[6]=7; x2[6]=3; y1[6]=0; y2[6]=0; - x1[7]=8; x2[7]=8; y1[7]=5; y2[7]=4; - x1[8]=10; x2[8]=5; y1[8]=9; y2[8]=9; - x1[9]=14; x2[9]=6; y1[9]=14; y2[9]=14; - x1[10]=9; x2[10]=9; y1[10]=11; y2[10]=8; - x1[11]=8; x2[11]=5; y1[11]=13; y2[11]=13; - x1[12]=12; x2[12]=5; y1[12]=1; y2[12]=1; - break; - case 100: - x1[0]=5; x2[0]=5; y1[0]=14; y2[0]=12; - x1[1]=8; x2[1]=8; y1[1]=14; y2[1]=12; - x1[2]=8; x2[2]=8; y1[2]=13; y2[2]=8; - x1[3]=11; x2[3]=2; y1[3]=7; y2[3]=7; - x1[4]=5; x2[4]=2; y1[4]=6; y2[4]=6; - x1[5]=14; x2[5]=14; y1[5]=5; y2[5]=4; - x1[6]=7; x2[6]=7; y1[6]=10; y2[6]=2; - x1[7]=5; x2[7]=2; y1[7]=7; y2[7]=7; - x1[8]=14; x2[8]=11; y1[8]=13; y2[8]=13; - x1[9]=1; x2[9]=1; y1[9]=13; y2[9]=9; - x1[10]=10; x2[10]=10; y1[10]=13; y2[10]=5; - x1[11]=7; x2[11]=1; y1[11]=13; y2[11]=13; - x1[12]=6; x2[12]=3; y1[12]=13; y2[12]=13; - break; - case 101: - x1[0]=4; x2[0]=1; y1[0]=0; y2[0]=0; - x1[1]=6; x2[1]=0; y1[1]=14; y2[1]=14; - x1[2]=13; x2[2]=4; y1[2]=8; y2[2]=8; - x1[3]=5; x2[3]=4; y1[3]=11; y2[3]=11; - x1[4]=2; x2[4]=1; y1[4]=14; y2[4]=14; - x1[5]=4; x2[5]=1; y1[5]=11; y2[5]=11; - x1[6]=12; x2[6]=12; y1[6]=7; y2[6]=0; - x1[7]=7; x2[7]=7; y1[7]=10; y2[7]=3; - x1[8]=4; x2[8]=4; y1[8]=4; y2[8]=3; - x1[9]=2; x2[9]=2; y1[9]=14; y2[9]=13; - x1[10]=0; x2[10]=0; y1[10]=9; y2[10]=7; - x1[11]=12; x2[11]=3; y1[11]=6; y2[11]=6; - x1[12]=2; x2[12]=2; y1[12]=11; y2[12]=2; - break; - case 102: - x1[0]=13; x2[0]=9; y1[0]=1; y2[0]=1; - x1[1]=6; x2[1]=6; y1[1]=13; y2[1]=2; - x1[2]=11; x2[2]=11; y1[2]=13; y2[2]=11; - x1[3]=11; x2[3]=11; y1[3]=12; y2[3]=11; - x1[4]=6; x2[4]=6; y1[4]=12; y2[4]=6; - x1[5]=12; x2[5]=6; y1[5]=3; y2[5]=3; - x1[6]=12; x2[6]=12; y1[6]=7; y2[6]=5; - x1[7]=7; x2[7]=7; y1[7]=9; y2[7]=5; - x1[8]=7; x2[8]=1; y1[8]=6; y2[8]=6; - x1[9]=9; x2[9]=9; y1[9]=14; y2[9]=3; - x1[10]=3; x2[10]=3; y1[10]=11; y2[10]=7; - x1[11]=5; x2[11]=2; y1[11]=9; y2[11]=9; - x1[12]=14; x2[12]=6; y1[12]=11; y2[12]=11; - break; - case 103: - x1[0]=11; x2[0]=4; y1[0]=12; y2[0]=12; - x1[1]=7; x2[1]=5; y1[1]=11; y2[1]=11; - x1[2]=1; x2[2]=1; y1[2]=9; y2[2]=3; - x1[3]=10; x2[3]=9; y1[3]=4; y2[3]=4; - x1[4]=6; x2[4]=2; y1[4]=9; y2[4]=9; - x1[5]=3; x2[5]=3; y1[5]=14; y2[5]=8; - x1[6]=10; x2[6]=10; y1[6]=12; y2[6]=2; - x1[7]=10; x2[7]=8; y1[7]=4; y2[7]=4; - x1[8]=4; x2[8]=4; y1[8]=4; y2[8]=2; - x1[9]=8; x2[9]=8; y1[9]=9; y2[9]=5; - x1[10]=5; x2[10]=0; y1[10]=14; y2[10]=14; - x1[11]=11; x2[11]=7; y1[11]=1; y2[11]=1; - x1[12]=6; x2[12]=6; y1[12]=14; y2[12]=0; - break; - case 104: - x1[0]=8; x2[0]=4; y1[0]=1; y2[0]=1; - x1[1]=5; x2[1]=2; y1[1]=3; y2[1]=3; - x1[2]=7; x2[2]=7; y1[2]=10; y2[2]=4; - x1[3]=4; x2[3]=4; y1[3]=1; y2[3]=0; - x1[4]=6; x2[4]=5; y1[4]=13; y2[4]=13; - x1[5]=12; x2[5]=5; y1[5]=3; y2[5]=3; - x1[6]=13; x2[6]=13; y1[6]=10; y2[6]=4; - x1[7]=14; x2[7]=14; y1[7]=8; y2[7]=2; - x1[8]=1; x2[8]=1; y1[8]=9; y2[8]=8; - x1[9]=2; x2[9]=2; y1[9]=11; y2[9]=7; - x1[10]=1; x2[10]=1; y1[10]=2; y2[10]=1; - x1[11]=2; x2[11]=2; y1[11]=14; y2[11]=10; - x1[12]=3; x2[12]=3; y1[12]=8; y2[12]=4; - break; - case 105: - x1[0]=13; x2[0]=10; y1[0]=0; y2[0]=0; - x1[1]=7; x2[1]=5; y1[1]=2; y2[1]=2; - x1[2]=14; x2[2]=8; y1[2]=8; y2[2]=8; - x1[3]=10; x2[3]=10; y1[3]=3; y2[3]=1; - x1[4]=7; x2[4]=7; y1[4]=6; y2[4]=3; - x1[5]=10; x2[5]=7; y1[5]=8; y2[5]=8; - x1[6]=8; x2[6]=8; y1[6]=5; y2[6]=0; - x1[7]=8; x2[7]=5; y1[7]=10; y2[7]=10; - x1[8]=10; x2[8]=4; y1[8]=6; y2[8]=6; - x1[9]=12; x2[9]=7; y1[9]=9; y2[9]=9; - x1[10]=11; x2[10]=0; y1[10]=13; y2[10]=13; - x1[11]=2; x2[11]=2; y1[11]=13; y2[11]=0; - x1[12]=4; x2[12]=3; y1[12]=12; y2[12]=12; - break; - case 106: - x1[0]=4; x2[0]=1; y1[0]=5; y2[0]=5; - x1[1]=12; x2[1]=1; y1[1]=9; y2[1]=9; - x1[2]=12; x2[2]=12; y1[2]=6; y2[2]=5; - x1[3]=8; x2[3]=8; y1[3]=9; y2[3]=6; - x1[4]=8; x2[4]=1; y1[4]=5; y2[4]=5; - x1[5]=13; x2[5]=13; y1[5]=8; y2[5]=0; - x1[6]=4; x2[6]=4; y1[6]=12; y2[6]=10; - x1[7]=3; x2[7]=3; y1[7]=12; y2[7]=2; - x1[8]=8; x2[8]=8; y1[8]=13; y2[8]=0; - x1[9]=11; x2[9]=10; y1[9]=6; y2[9]=6; - x1[10]=13; x2[10]=12; y1[10]=8; y2[10]=8; - x1[11]=3; x2[11]=3; y1[11]=12; y2[11]=0; - x1[12]=6; x2[12]=6; y1[12]=5; y2[12]=1; - break; - case 107: - x1[0]=8; x2[0]=8; y1[0]=7; y2[0]=3; - x1[1]=9; x2[1]=0; y1[1]=0; y2[1]=0; - x1[2]=8; x2[2]=6; y1[2]=7; y2[2]=7; - x1[3]=9; x2[3]=0; y1[3]=3; y2[3]=3; - x1[4]=7; x2[4]=7; y1[4]=6; y2[4]=4; - x1[5]=11; x2[5]=5; y1[5]=8; y2[5]=8; - x1[6]=10; x2[6]=4; y1[6]=3; y2[6]=3; - x1[7]=2; x2[7]=2; y1[7]=10; y2[7]=1; - x1[8]=10; x2[8]=4; y1[8]=7; y2[8]=7; - x1[9]=6; x2[9]=2; y1[9]=10; y2[9]=10; - x1[10]=10; x2[10]=10; y1[10]=14; y2[10]=0; - x1[11]=3; x2[11]=3; y1[11]=13; y2[11]=2; - x1[12]=9; x2[12]=7; y1[12]=8; y2[12]=8; - break; - case 108: - x1[0]=13; x2[0]=13; y1[0]=5; y2[0]=1; - x1[1]=13; x2[1]=7; y1[1]=6; y2[1]=6; - x1[2]=14; x2[2]=12; y1[2]=9; y2[2]=9; - x1[3]=3; x2[3]=3; y1[3]=13; y2[3]=11; - x1[4]=14; x2[4]=14; y1[4]=10; y2[4]=9; - x1[5]=9; x2[5]=5; y1[5]=4; y2[5]=4; - x1[6]=12; x2[6]=1; y1[6]=12; y2[6]=12; - x1[7]=14; x2[7]=14; y1[7]=10; y2[7]=6; - x1[8]=7; x2[8]=7; y1[8]=13; y2[8]=6; - x1[9]=0; x2[9]=0; y1[9]=13; y2[9]=5; - x1[10]=10; x2[10]=10; y1[10]=9; y2[10]=1; - x1[11]=12; x2[11]=1; y1[11]=3; y2[11]=3; - x1[12]=5; x2[12]=5; y1[12]=9; y2[12]=0; - break; - case 109: - x1[0]=1; x2[0]=1; y1[0]=2; y2[0]=0; - x1[1]=6; x2[1]=1; y1[1]=10; y2[1]=10; - x1[2]=9; x2[2]=9; y1[2]=9; y2[2]=1; - x1[3]=0; x2[3]=0; y1[3]=13; y2[3]=12; - x1[4]=0; x2[4]=0; y1[4]=12; y2[4]=8; - x1[5]=12; x2[5]=9; y1[5]=7; y2[5]=7; - x1[6]=13; x2[6]=0; y1[6]=14; y2[6]=14; - x1[7]=4; x2[7]=4; y1[7]=5; y2[7]=2; - x1[8]=9; x2[8]=9; y1[8]=9; y2[8]=3; - x1[9]=11; x2[9]=7; y1[9]=5; y2[9]=5; - x1[10]=10; x2[10]=0; y1[10]=11; y2[10]=11; - x1[11]=12; x2[11]=12; y1[11]=4; y2[11]=2; - x1[12]=13; x2[12]=13; y1[12]=3; y2[12]=2; - break; - case 110: - x1[0]=8; x2[0]=8; y1[0]=13; y2[0]=6; - x1[1]=2; x2[1]=2; y1[1]=3; y2[1]=1; - x1[2]=13; x2[2]=10; y1[2]=8; y2[2]=8; - x1[3]=9; x2[3]=2; y1[3]=1; y2[3]=1; - x1[4]=7; x2[4]=6; y1[4]=12; y2[4]=12; - x1[5]=6; x2[5]=1; y1[5]=5; y2[5]=5; - x1[6]=0; x2[6]=0; y1[6]=10; y2[6]=2; - x1[7]=12; x2[7]=0; y1[7]=7; y2[7]=7; - x1[8]=11; x2[8]=0; y1[8]=11; y2[8]=11; - x1[9]=13; x2[9]=1; y1[9]=11; y2[9]=11; - x1[10]=13; x2[10]=13; y1[10]=7; y2[10]=0; - x1[11]=0; x2[11]=0; y1[11]=14; y2[11]=8; - x1[12]=4; x2[12]=4; y1[12]=4; y2[12]=0; - break; - case 111: - x1[0]=4; x2[0]=2; y1[0]=13; y2[0]=13; - x1[1]=6; x2[1]=0; y1[1]=11; y2[1]=11; - x1[2]=14; x2[2]=7; y1[2]=11; y2[2]=11; - x1[3]=3; x2[3]=3; y1[3]=11; y2[3]=2; - x1[4]=3; x2[4]=0; y1[4]=3; y2[4]=3; - x1[5]=13; x2[5]=1; y1[5]=13; y2[5]=13; - x1[6]=5; x2[6]=0; y1[6]=9; y2[6]=9; - x1[7]=9; x2[7]=2; y1[7]=10; y2[7]=10; - x1[8]=13; x2[8]=13; y1[8]=11; y2[8]=5; - x1[9]=7; x2[9]=2; y1[9]=6; y2[9]=6; - x1[10]=0; x2[10]=0; y1[10]=13; y2[10]=3; - x1[11]=10; x2[11]=7; y1[11]=0; y2[11]=0; - x1[12]=1; x2[12]=1; y1[12]=14; y2[12]=1; - break; - case 112: - x1[0]=7; x2[0]=7; y1[0]=12; y2[0]=7; - x1[1]=7; x2[1]=7; y1[1]=14; y2[1]=2; - x1[2]=4; x2[2]=4; y1[2]=14; y2[2]=5; - x1[3]=5; x2[3]=5; y1[3]=9; y2[3]=2; - x1[4]=2; x2[4]=2; y1[4]=11; y2[4]=9; - x1[5]=4; x2[5]=4; y1[5]=14; y2[5]=2; - x1[6]=9; x2[6]=8; y1[6]=12; y2[6]=12; - x1[7]=12; x2[7]=5; y1[7]=6; y2[7]=6; - x1[8]=13; x2[8]=9; y1[8]=14; y2[8]=14; - x1[9]=2; x2[9]=1; y1[9]=6; y2[9]=6; - x1[10]=4; x2[10]=4; y1[10]=14; y2[10]=11; - x1[11]=14; x2[11]=14; y1[11]=6; y2[11]=0; - x1[12]=13; x2[12]=13; y1[12]=11; y2[12]=6; - break; - case 113: - x1[0]=10; x2[0]=0; y1[0]=7; y2[0]=7; - x1[1]=7; x2[1]=5; y1[1]=7; y2[1]=7; - x1[2]=13; x2[2]=5; y1[2]=4; y2[2]=4; - x1[3]=5; x2[3]=5; y1[3]=14; y2[3]=11; - x1[4]=8; x2[4]=0; y1[4]=0; y2[4]=0; - x1[5]=11; x2[5]=11; y1[5]=7; y2[5]=1; - x1[6]=4; x2[6]=4; y1[6]=5; y2[6]=3; - x1[7]=3; x2[7]=3; y1[7]=12; y2[7]=9; - x1[8]=4; x2[8]=1; y1[8]=9; y2[8]=9; - x1[9]=14; x2[9]=0; y1[9]=7; y2[9]=7; - x1[10]=12; x2[10]=9; y1[10]=2; y2[10]=2; - x1[11]=13; x2[11]=10; y1[11]=2; y2[11]=2; - x1[12]=1; x2[12]=0; y1[12]=5; y2[12]=5; - break; - case 114: - x1[0]=3; x2[0]=3; y1[0]=2; y2[0]=0; - x1[1]=4; x2[1]=4; y1[1]=11; y2[1]=4; - x1[2]=7; x2[2]=3; y1[2]=11; y2[2]=11; - x1[3]=4; x2[3]=4; y1[3]=13; y2[3]=12; - x1[4]=10; x2[4]=10; y1[4]=14; y2[4]=8; - x1[5]=3; x2[5]=2; y1[5]=6; y2[5]=6; - x1[6]=5; x2[6]=5; y1[6]=12; y2[6]=8; - x1[7]=8; x2[7]=3; y1[7]=3; y2[7]=3; - x1[8]=13; x2[8]=5; y1[8]=1; y2[8]=1; - x1[9]=14; x2[9]=3; y1[9]=13; y2[9]=13; - x1[10]=11; x2[10]=11; y1[10]=5; y2[10]=0; - x1[11]=8; x2[11]=8; y1[11]=11; y2[11]=7; - x1[12]=7; x2[12]=7; y1[12]=7; y2[12]=6; - break; - case 115: - x1[0]=11; x2[0]=11; y1[0]=11; y2[0]=4; - x1[1]=7; x2[1]=7; y1[1]=14; y2[1]=0; - x1[2]=14; x2[2]=10; y1[2]=12; y2[2]=12; - x1[3]=13; x2[3]=13; y1[3]=10; y2[3]=3; - x1[4]=6; x2[4]=6; y1[4]=13; y2[4]=1; - x1[5]=13; x2[5]=13; y1[5]=13; y2[5]=11; - x1[6]=12; x2[6]=11; y1[6]=12; y2[6]=12; - x1[7]=10; x2[7]=10; y1[7]=8; y2[7]=3; - x1[8]=3; x2[8]=1; y1[8]=13; y2[8]=13; - x1[9]=8; x2[9]=3; y1[9]=9; y2[9]=9; - x1[10]=13; x2[10]=12; y1[10]=10; y2[10]=10; - x1[11]=14; x2[11]=14; y1[11]=12; y2[11]=9; - x1[12]=10; x2[12]=8; y1[12]=13; y2[12]=13; - break; - case 116: - x1[0]=8; x2[0]=8; y1[0]=14; y2[0]=0; - x1[1]=7; x2[1]=7; y1[1]=4; y2[1]=0; - x1[2]=12; x2[2]=8; y1[2]=1; y2[2]=1; - x1[3]=12; x2[3]=10; y1[3]=1; y2[3]=1; - x1[4]=13; x2[4]=4; y1[4]=14; y2[4]=14; - x1[5]=6; x2[5]=4; y1[5]=11; y2[5]=11; - x1[6]=13; x2[6]=10; y1[6]=3; y2[6]=3; - x1[7]=5; x2[7]=5; y1[7]=3; y2[7]=2; - x1[8]=14; x2[8]=6; y1[8]=7; y2[8]=7; - x1[9]=7; x2[9]=7; y1[9]=8; y2[9]=1; - x1[10]=8; x2[10]=1; y1[10]=9; y2[10]=9; - x1[11]=13; x2[11]=13; y1[11]=8; y2[11]=4; - x1[12]=9; x2[12]=6; y1[12]=8; y2[12]=8; - break; - case 117: - x1[0]=11; x2[0]=11; y1[0]=10; y2[0]=6; - x1[1]=12; x2[1]=12; y1[1]=12; y2[1]=8; - x1[2]=4; x2[2]=4; y1[2]=10; y2[2]=4; - x1[3]=7; x2[3]=0; y1[3]=6; y2[3]=6; - x1[4]=7; x2[4]=1; y1[4]=2; y2[4]=2; - x1[5]=3; x2[5]=3; y1[5]=9; y2[5]=5; - x1[6]=12; x2[6]=12; y1[6]=8; y2[6]=5; - x1[7]=13; x2[7]=13; y1[7]=9; y2[7]=3; - x1[8]=5; x2[8]=5; y1[8]=13; y2[8]=0; - x1[9]=11; x2[9]=7; y1[9]=4; y2[9]=4; - x1[10]=7; x2[10]=3; y1[10]=12; y2[10]=12; - x1[11]=14; x2[11]=14; y1[11]=11; y2[11]=9; - x1[12]=2; x2[12]=2; y1[12]=13; y2[12]=12; - break; - case 118: - x1[0]=3; x2[0]=3; y1[0]=12; y2[0]=11; - x1[1]=5; x2[1]=3; y1[1]=12; y2[1]=12; - x1[2]=13; x2[2]=0; y1[2]=8; y2[2]=8; - x1[3]=12; x2[3]=12; y1[3]=10; y2[3]=2; - x1[4]=9; x2[4]=0; y1[4]=13; y2[4]=13; - x1[5]=1; x2[5]=1; y1[5]=7; y2[5]=3; - x1[6]=13; x2[6]=13; y1[6]=11; y2[6]=0; - x1[7]=4; x2[7]=4; y1[7]=8; y2[7]=7; - x1[8]=12; x2[8]=12; y1[8]=9; y2[8]=3; - x1[9]=11; x2[9]=5; y1[9]=2; y2[9]=2; - x1[10]=6; x2[10]=6; y1[10]=13; y2[10]=3; - x1[11]=9; x2[11]=9; y1[11]=12; y2[11]=3; - x1[12]=13; x2[12]=0; y1[12]=13; y2[12]=13; - break; - case 119: - x1[0]=13; x2[0]=10; y1[0]=5; y2[0]=5; - x1[1]=11; x2[1]=10; y1[1]=11; y2[1]=11; - x1[2]=12; x2[2]=12; y1[2]=14; y2[2]=12; - x1[3]=8; x2[3]=2; y1[3]=11; y2[3]=11; - x1[4]=12; x2[4]=10; y1[4]=8; y2[4]=8; - x1[5]=13; x2[5]=2; y1[5]=4; y2[5]=4; - x1[6]=5; x2[6]=5; y1[6]=8; y2[6]=2; - x1[7]=5; x2[7]=0; y1[7]=2; y2[7]=2; - x1[8]=7; x2[8]=7; y1[8]=13; y2[8]=11; - x1[9]=3; x2[9]=3; y1[9]=10; y2[9]=8; - x1[10]=4; x2[10]=4; y1[10]=5; y2[10]=4; - x1[11]=5; x2[11]=5; y1[11]=14; y2[11]=1; - x1[12]=12; x2[12]=2; y1[12]=2; y2[12]=2; - break; - case 120: - x1[0]=8; x2[0]=1; y1[0]=1; y2[0]=1; - x1[1]=7; x2[1]=2; y1[1]=11; y2[1]=11; - x1[2]=8; x2[2]=4; y1[2]=8; y2[2]=8; - x1[3]=6; x2[3]=6; y1[3]=11; y2[3]=4; - x1[4]=4; x2[4]=4; y1[4]=13; y2[4]=11; - x1[5]=1; x2[5]=1; y1[5]=11; y2[5]=1; - x1[6]=8; x2[6]=0; y1[6]=4; y2[6]=4; - x1[7]=14; x2[7]=0; y1[7]=12; y2[7]=12; - x1[8]=13; x2[8]=13; y1[8]=10; y2[8]=5; - x1[9]=6; x2[9]=5; y1[9]=14; y2[9]=14; - x1[10]=12; x2[10]=3; y1[10]=2; y2[10]=2; - x1[11]=5; x2[11]=1; y1[11]=13; y2[11]=13; - x1[12]=0; x2[12]=0; y1[12]=13; y2[12]=11; - break; - case 121: - x1[0]=3; x2[0]=2; y1[0]=7; y2[0]=7; - x1[1]=13; x2[1]=6; y1[1]=4; y2[1]=4; - x1[2]=4; x2[2]=4; y1[2]=7; y2[2]=5; - x1[3]=7; x2[3]=7; y1[3]=10; y2[3]=7; - x1[4]=8; x2[4]=4; y1[4]=9; y2[4]=9; - x1[5]=14; x2[5]=10; y1[5]=1; y2[5]=1; - x1[6]=9; x2[6]=5; y1[6]=1; y2[6]=1; - x1[7]=10; x2[7]=6; y1[7]=9; y2[7]=9; - x1[8]=8; x2[8]=4; y1[8]=7; y2[8]=7; - x1[9]=6; x2[9]=6; y1[9]=5; y2[9]=4; - x1[10]=7; x2[10]=7; y1[10]=14; y2[10]=9; - x1[11]=9; x2[11]=9; y1[11]=11; y2[11]=0; - x1[12]=14; x2[12]=12; y1[12]=2; y2[12]=2; - break; - case 122: - x1[0]=0; x2[0]=0; y1[0]=6; y2[0]=1; - x1[1]=13; x2[1]=13; y1[1]=5; y2[1]=2; - x1[2]=9; x2[2]=9; y1[2]=8; y2[2]=1; - x1[3]=1; x2[3]=1; y1[3]=4; y2[3]=3; - x1[4]=6; x2[4]=6; y1[4]=10; y2[4]=4; - x1[5]=6; x2[5]=6; y1[5]=6; y2[5]=4; - x1[6]=1; x2[6]=1; y1[6]=10; y2[6]=1; - x1[7]=3; x2[7]=3; y1[7]=10; y2[7]=9; - x1[8]=13; x2[8]=13; y1[8]=6; y2[8]=1; - x1[9]=12; x2[9]=6; y1[9]=2; y2[9]=2; - x1[10]=11; x2[10]=11; y1[10]=11; y2[10]=7; - x1[11]=8; x2[11]=8; y1[11]=6; y2[11]=1; - x1[12]=12; x2[12]=12; y1[12]=9; y2[12]=2; - break; - case 123: - x1[0]=12; x2[0]=2; y1[0]=12; y2[0]=12; - x1[1]=14; x2[1]=14; y1[1]=2; y2[1]=0; - x1[2]=11; x2[2]=7; y1[2]=0; y2[2]=0; - x1[3]=7; x2[3]=7; y1[3]=12; y2[3]=2; - x1[4]=13; x2[4]=5; y1[4]=6; y2[4]=6; - x1[5]=14; x2[5]=14; y1[5]=10; y2[5]=4; - x1[6]=13; x2[6]=13; y1[6]=13; y2[6]=6; - x1[7]=12; x2[7]=12; y1[7]=5; y2[7]=4; - x1[8]=14; x2[8]=13; y1[8]=1; y2[8]=1; - x1[9]=14; x2[9]=14; y1[9]=6; y2[9]=1; - x1[10]=8; x2[10]=8; y1[10]=6; y2[10]=4; - x1[11]=5; x2[11]=5; y1[11]=3; y2[11]=0; - x1[12]=9; x2[12]=3; y1[12]=8; y2[12]=8; - break; - case 124: - x1[0]=11; x2[0]=11; y1[0]=4; y2[0]=2; - x1[1]=7; x2[1]=2; y1[1]=5; y2[1]=5; - x1[2]=6; x2[2]=5; y1[2]=4; y2[2]=4; - x1[3]=14; x2[3]=13; y1[3]=5; y2[3]=5; - x1[4]=10; x2[4]=5; y1[4]=1; y2[4]=1; - x1[5]=14; x2[5]=14; y1[5]=10; y2[5]=3; - x1[6]=10; x2[6]=10; y1[6]=12; y2[6]=11; - x1[7]=13; x2[7]=13; y1[7]=10; y2[7]=6; - x1[8]=13; x2[8]=11; y1[8]=9; y2[8]=9; - x1[9]=2; x2[9]=2; y1[9]=6; y2[9]=4; - x1[10]=2; x2[10]=1; y1[10]=13; y2[10]=13; - x1[11]=5; x2[11]=5; y1[11]=12; y2[11]=2; - x1[12]=7; x2[12]=7; y1[12]=12; y2[12]=0; - break; - case 125: - x1[0]=7; x2[0]=0; y1[0]=0; y2[0]=0; - x1[1]=1; x2[1]=0; y1[1]=0; y2[1]=0; - x1[2]=2; x2[2]=2; y1[2]=13; y2[2]=6; - x1[3]=5; x2[3]=5; y1[3]=14; y2[3]=13; - x1[4]=12; x2[4]=8; y1[4]=14; y2[4]=14; - x1[5]=9; x2[5]=3; y1[5]=2; y2[5]=2; - x1[6]=11; x2[6]=4; y1[6]=14; y2[6]=14; - x1[7]=12; x2[7]=7; y1[7]=7; y2[7]=7; - x1[8]=7; x2[8]=7; y1[8]=8; y2[8]=4; - x1[9]=12; x2[9]=9; y1[9]=4; y2[9]=4; - x1[10]=11; x2[10]=3; y1[10]=3; y2[10]=3; - x1[11]=14; x2[11]=5; y1[11]=9; y2[11]=9; - x1[12]=4; x2[12]=1; y1[12]=12; y2[12]=12; - break; - case 126: - x1[0]=8; x2[0]=8; y1[0]=12; y2[0]=5; - x1[1]=8; x2[1]=5; y1[1]=2; y2[1]=2; - x1[2]=11; x2[2]=11; y1[2]=13; y2[2]=7; - x1[3]=1; x2[3]=1; y1[3]=11; y2[3]=2; - x1[4]=4; x2[4]=4; y1[4]=13; y2[4]=10; - x1[5]=5; x2[5]=2; y1[5]=11; y2[5]=11; - x1[6]=11; x2[6]=11; y1[6]=11; y2[6]=6; - x1[7]=14; x2[7]=7; y1[7]=8; y2[7]=8; - x1[8]=6; x2[8]=6; y1[8]=13; y2[8]=6; - x1[9]=7; x2[9]=4; y1[9]=8; y2[9]=8; - x1[10]=5; x2[10]=5; y1[10]=14; y2[10]=2; - x1[11]=2; x2[11]=0; y1[11]=2; y2[11]=2; - x1[12]=7; x2[12]=4; y1[12]=9; y2[12]=9; - break; - case 127: - x1[0]=8; x2[0]=8; y1[0]=12; y2[0]=11; - x1[1]=4; x2[1]=4; y1[1]=13; y2[1]=0; - x1[2]=14; x2[2]=14; y1[2]=8; y2[2]=7; - x1[3]=4; x2[3]=1; y1[3]=2; y2[3]=2; - x1[4]=5; x2[4]=4; y1[4]=9; y2[4]=9; - x1[5]=11; x2[5]=5; y1[5]=3; y2[5]=3; - x1[6]=10; x2[6]=10; y1[6]=9; y2[6]=6; - x1[7]=3; x2[7]=3; y1[7]=11; y2[7]=3; - x1[8]=4; x2[8]=4; y1[8]=10; y2[8]=7; - x1[9]=6; x2[9]=6; y1[9]=8; y2[9]=1; - x1[10]=11; x2[10]=0; y1[10]=0; y2[10]=0; - x1[11]=13; x2[11]=2; y1[11]=8; y2[11]=8; - x1[12]=8; x2[12]=8; y1[12]=10; y2[12]=5; - break; - case 128: - x1[0]=13; x2[0]=1; y1[0]=9; y2[0]=9; - x1[1]=1; x2[1]=1; y1[1]=8; y2[1]=6; - x1[2]=12; x2[2]=3; y1[2]=10; y2[2]=10; - x1[3]=8; x2[3]=2; y1[3]=5; y2[3]=5; - x1[4]=14; x2[4]=3; y1[4]=12; y2[4]=12; - x1[5]=1; x2[5]=1; y1[5]=10; y2[5]=7; - x1[6]=13; x2[6]=13; y1[6]=12; y2[6]=1; - x1[7]=3; x2[7]=3; y1[7]=11; y2[7]=5; - x1[8]=12; x2[8]=4; y1[8]=13; y2[8]=13; - x1[9]=3; x2[9]=3; y1[9]=14; y2[9]=1; - x1[10]=13; x2[10]=13; y1[10]=11; y2[10]=1; - x1[11]=5; x2[11]=5; y1[11]=11; y2[11]=3; - x1[12]=10; x2[12]=10; y1[12]=12; y2[12]=0; - break; - case 129: - x1[0]=13; x2[0]=3; y1[0]=10; y2[0]=10; - x1[1]=7; x2[1]=7; y1[1]=6; y2[1]=1; - x1[2]=12; x2[2]=2; y1[2]=0; y2[2]=0; - x1[3]=10; x2[3]=10; y1[3]=9; y2[3]=5; - x1[4]=11; x2[4]=11; y1[4]=6; y2[4]=1; - x1[5]=8; x2[5]=8; y1[5]=6; y2[5]=0; - x1[6]=14; x2[6]=9; y1[6]=13; y2[6]=13; - x1[7]=2; x2[7]=2; y1[7]=12; y2[7]=3; - x1[8]=5; x2[8]=3; y1[8]=8; y2[8]=8; - x1[9]=14; x2[9]=14; y1[9]=8; y2[9]=3; - x1[10]=5; x2[10]=0; y1[10]=7; y2[10]=7; - x1[11]=1; x2[11]=1; y1[11]=11; y2[11]=0; - x1[12]=3; x2[12]=1; y1[12]=8; y2[12]=8; - break; - case 130: - x1[0]=9; x2[0]=3; y1[0]=5; y2[0]=5; - x1[1]=9; x2[1]=8; y1[1]=6; y2[1]=6; - x1[2]=8; x2[2]=8; y1[2]=4; y2[2]=3; - x1[3]=14; x2[3]=14; y1[3]=14; y2[3]=12; - x1[4]=12; x2[4]=12; y1[4]=13; y2[4]=3; - x1[5]=1; x2[5]=1; y1[5]=11; y2[5]=5; - x1[6]=2; x2[6]=2; y1[6]=4; y2[6]=0; - x1[7]=0; x2[7]=0; y1[7]=5; y2[7]=1; - x1[8]=5; x2[8]=4; y1[8]=10; y2[8]=10; - x1[9]=11; x2[9]=6; y1[9]=3; y2[9]=3; - x1[10]=13; x2[10]=6; y1[10]=6; y2[10]=6; - x1[11]=3; x2[11]=0; y1[11]=13; y2[11]=13; - x1[12]=8; x2[12]=3; y1[12]=0; y2[12]=0; - break; - case 131: - x1[0]=11; x2[0]=5; y1[0]=6; y2[0]=6; - x1[1]=11; x2[1]=9; y1[1]=12; y2[1]=12; - x1[2]=7; x2[2]=7; y1[2]=1; y2[2]=0; - x1[3]=6; x2[3]=2; y1[3]=4; y2[3]=4; - x1[4]=11; x2[4]=7; y1[4]=8; y2[4]=8; - x1[5]=11; x2[5]=2; y1[5]=14; y2[5]=14; - x1[6]=14; x2[6]=14; y1[6]=8; y2[6]=5; - x1[7]=11; x2[7]=4; y1[7]=6; y2[7]=6; - x1[8]=12; x2[8]=0; y1[8]=2; y2[8]=2; - x1[9]=0; x2[9]=0; y1[9]=7; y2[9]=0; - x1[10]=12; x2[10]=0; y1[10]=5; y2[10]=5; - x1[11]=13; x2[11]=6; y1[11]=8; y2[11]=8; - x1[12]=0; x2[12]=0; y1[12]=1; y2[12]=0; - break; - case 132: - x1[0]=7; x2[0]=7; y1[0]=14; y2[0]=4; - x1[1]=14; x2[1]=1; y1[1]=14; y2[1]=14; - x1[2]=10; x2[2]=10; y1[2]=12; y2[2]=6; - x1[3]=6; x2[3]=3; y1[3]=14; y2[3]=14; - x1[4]=11; x2[4]=11; y1[4]=8; y2[4]=5; - x1[5]=12; x2[5]=7; y1[5]=8; y2[5]=8; - x1[6]=6; x2[6]=2; y1[6]=7; y2[6]=7; - x1[7]=7; x2[7]=1; y1[7]=12; y2[7]=12; - x1[8]=3; x2[8]=3; y1[8]=13; y2[8]=10; - x1[9]=8; x2[9]=8; y1[9]=7; y2[9]=0; - x1[10]=5; x2[10]=5; y1[10]=11; y2[10]=7; - x1[11]=10; x2[11]=3; y1[11]=9; y2[11]=9; - x1[12]=14; x2[12]=5; y1[12]=4; y2[12]=4; - break; - case 133: - x1[0]=9; x2[0]=4; y1[0]=6; y2[0]=6; - x1[1]=5; x2[1]=5; y1[1]=13; y2[1]=6; - x1[2]=5; x2[2]=5; y1[2]=2; y2[2]=1; - x1[3]=10; x2[3]=5; y1[3]=4; y2[3]=4; - x1[4]=7; x2[4]=2; y1[4]=0; y2[4]=0; - x1[5]=10; x2[5]=10; y1[5]=11; y2[5]=0; - x1[6]=13; x2[6]=2; y1[6]=1; y2[6]=1; - x1[7]=4; x2[7]=2; y1[7]=5; y2[7]=5; - x1[8]=8; x2[8]=8; y1[8]=14; y2[8]=4; - x1[9]=14; x2[9]=14; y1[9]=14; y2[9]=6; - x1[10]=8; x2[10]=8; y1[10]=13; y2[10]=9; - x1[11]=13; x2[11]=2; y1[11]=5; y2[11]=5; - x1[12]=10; x2[12]=10; y1[12]=12; y2[12]=1; - break; - case 134: - x1[0]=12; x2[0]=3; y1[0]=11; y2[0]=11; - x1[1]=9; x2[1]=9; y1[1]=12; y2[1]=0; - x1[2]=8; x2[2]=8; y1[2]=8; y2[2]=7; - x1[3]=14; x2[3]=12; y1[3]=8; y2[3]=8; - x1[4]=10; x2[4]=3; y1[4]=7; y2[4]=7; - x1[5]=9; x2[5]=5; y1[5]=3; y2[5]=3; - x1[6]=7; x2[6]=7; y1[6]=14; y2[6]=5; - x1[7]=11; x2[7]=8; y1[7]=12; y2[7]=12; - x1[8]=8; x2[8]=4; y1[8]=6; y2[8]=6; - x1[9]=10; x2[9]=10; y1[9]=13; y2[9]=3; - x1[10]=9; x2[10]=9; y1[10]=3; y2[10]=2; - x1[11]=2; x2[11]=1; y1[11]=4; y2[11]=4; - x1[12]=10; x2[12]=5; y1[12]=7; y2[12]=7; - break; - case 135: - x1[0]=4; x2[0]=4; y1[0]=14; y2[0]=7; - x1[1]=8; x2[1]=8; y1[1]=10; y2[1]=8; - x1[2]=5; x2[2]=5; y1[2]=6; y2[2]=3; - x1[3]=12; x2[3]=12; y1[3]=14; y2[3]=13; - x1[4]=14; x2[4]=14; y1[4]=2; y2[4]=1; - x1[5]=13; x2[5]=4; y1[5]=11; y2[5]=11; - x1[6]=5; x2[6]=2; y1[6]=5; y2[6]=5; - x1[7]=2; x2[7]=2; y1[7]=12; y2[7]=2; - x1[8]=9; x2[8]=9; y1[8]=10; y2[8]=0; - x1[9]=12; x2[9]=11; y1[9]=6; y2[9]=6; - x1[10]=13; x2[10]=3; y1[10]=3; y2[10]=3; - x1[11]=13; x2[11]=4; y1[11]=13; y2[11]=13; - x1[12]=5; x2[12]=5; y1[12]=7; y2[12]=5; - break; - case 136: - x1[0]=8; x2[0]=8; y1[0]=14; y2[0]=10; - x1[1]=11; x2[1]=11; y1[1]=7; y2[1]=4; - x1[2]=2; x2[2]=1; y1[2]=1; y2[2]=1; - x1[3]=10; x2[3]=2; y1[3]=2; y2[3]=2; - x1[4]=7; x2[4]=1; y1[4]=5; y2[4]=5; - x1[5]=9; x2[5]=6; y1[5]=0; y2[5]=0; - x1[6]=12; x2[6]=12; y1[6]=10; y2[6]=8; - x1[7]=13; x2[7]=13; y1[7]=11; y2[7]=8; - x1[8]=6; x2[8]=6; y1[8]=12; y2[8]=10; - x1[9]=4; x2[9]=4; y1[9]=10; y2[9]=0; - x1[10]=14; x2[10]=2; y1[10]=7; y2[10]=7; - x1[11]=8; x2[11]=8; y1[11]=9; y2[11]=4; - x1[12]=10; x2[12]=10; y1[12]=13; y2[12]=1; - break; - case 137: - x1[0]=14; x2[0]=14; y1[0]=11; y2[0]=3; - x1[1]=8; x2[1]=8; y1[1]=10; y2[1]=0; - x1[2]=9; x2[2]=4; y1[2]=7; y2[2]=7; - x1[3]=1; x2[3]=1; y1[3]=14; y2[3]=7; - x1[4]=10; x2[4]=2; y1[4]=6; y2[4]=6; - x1[5]=10; x2[5]=4; y1[5]=5; y2[5]=5; - x1[6]=3; x2[6]=3; y1[6]=9; y2[6]=0; - x1[7]=12; x2[7]=2; y1[7]=1; y2[7]=1; - x1[8]=7; x2[8]=7; y1[8]=8; y2[8]=5; - x1[9]=11; x2[9]=11; y1[9]=5; y2[9]=1; - x1[10]=5; x2[10]=5; y1[10]=5; y2[10]=2; - x1[11]=7; x2[11]=7; y1[11]=12; y2[11]=10; - x1[12]=13; x2[12]=13; y1[12]=5; y2[12]=3; - break; - case 138: - x1[0]=3; x2[0]=0; y1[0]=1; y2[0]=1; - x1[1]=10; x2[1]=6; y1[1]=14; y2[1]=14; - x1[2]=10; x2[2]=9; y1[2]=5; y2[2]=5; - x1[3]=10; x2[3]=10; y1[3]=7; y2[3]=0; - x1[4]=7; x2[4]=7; y1[4]=13; y2[4]=3; - x1[5]=13; x2[5]=13; y1[5]=7; y2[5]=4; - x1[6]=4; x2[6]=1; y1[6]=14; y2[6]=14; - x1[7]=9; x2[7]=1; y1[7]=13; y2[7]=13; - x1[8]=12; x2[8]=10; y1[8]=3; y2[8]=3; - x1[9]=14; x2[9]=12; y1[9]=13; y2[9]=13; - x1[10]=12; x2[10]=12; y1[10]=14; y2[10]=7; - x1[11]=10; x2[11]=10; y1[11]=5; y2[11]=3; - x1[12]=11; x2[12]=11; y1[12]=3; y2[12]=2; - break; - case 139: - x1[0]=10; x2[0]=9; y1[0]=10; y2[0]=10; - x1[1]=7; x2[1]=1; y1[1]=14; y2[1]=14; - x1[2]=11; x2[2]=3; y1[2]=0; y2[2]=0; - x1[3]=7; x2[3]=4; y1[3]=4; y2[3]=4; - x1[4]=3; x2[4]=3; y1[4]=13; y2[4]=8; - x1[5]=12; x2[5]=12; y1[5]=2; y2[5]=0; - x1[6]=8; x2[6]=5; y1[6]=8; y2[6]=8; - x1[7]=13; x2[7]=9; y1[7]=6; y2[7]=6; - x1[8]=12; x2[8]=2; y1[8]=13; y2[8]=13; - x1[9]=8; x2[9]=8; y1[9]=11; y2[9]=9; - x1[10]=8; x2[10]=4; y1[10]=12; y2[10]=12; - x1[11]=12; x2[11]=12; y1[11]=8; y2[11]=1; - x1[12]=13; x2[12]=10; y1[12]=13; y2[12]=13; - break; - case 140: - x1[0]=7; x2[0]=0; y1[0]=14; y2[0]=14; - x1[1]=7; x2[1]=5; y1[1]=1; y2[1]=1; - x1[2]=0; x2[2]=0; y1[2]=11; y2[2]=6; - x1[3]=13; x2[3]=13; y1[3]=11; y2[3]=4; - x1[4]=8; x2[4]=5; y1[4]=5; y2[4]=5; - x1[5]=8; x2[5]=8; y1[5]=10; y2[5]=4; - x1[6]=3; x2[6]=2; y1[6]=2; y2[6]=2; - x1[7]=5; x2[7]=5; y1[7]=11; y2[7]=1; - x1[8]=14; x2[8]=6; y1[8]=12; y2[8]=12; - x1[9]=9; x2[9]=9; y1[9]=7; y2[9]=3; - x1[10]=8; x2[10]=7; y1[10]=5; y2[10]=5; - x1[11]=3; x2[11]=3; y1[11]=11; y2[11]=9; - x1[12]=4; x2[12]=0; y1[12]=14; y2[12]=14; - break; - case 141: - x1[0]=6; x2[0]=5; y1[0]=2; y2[0]=2; - x1[1]=9; x2[1]=1; y1[1]=12; y2[1]=12; - x1[2]=9; x2[2]=9; y1[2]=10; y2[2]=7; - x1[3]=13; x2[3]=13; y1[3]=6; y2[3]=2; - x1[4]=11; x2[4]=10; y1[4]=9; y2[4]=9; - x1[5]=1; x2[5]=1; y1[5]=12; y2[5]=11; - x1[6]=14; x2[6]=14; y1[6]=10; y2[6]=5; - x1[7]=1; x2[7]=1; y1[7]=9; y2[7]=4; - x1[8]=5; x2[8]=5; y1[8]=10; y2[8]=4; - x1[9]=9; x2[9]=9; y1[9]=11; y2[9]=3; - x1[10]=8; x2[10]=8; y1[10]=10; y2[10]=1; - x1[11]=6; x2[11]=6; y1[11]=9; y2[11]=2; - x1[12]=8; x2[12]=8; y1[12]=7; y2[12]=2; - break; - case 142: - x1[0]=12; x2[0]=12; y1[0]=13; y2[0]=9; - x1[1]=13; x2[1]=4; y1[1]=0; y2[1]=0; - x1[2]=13; x2[2]=10; y1[2]=11; y2[2]=11; - x1[3]=14; x2[3]=5; y1[3]=7; y2[3]=7; - x1[4]=14; x2[4]=6; y1[4]=2; y2[4]=2; - x1[5]=13; x2[5]=12; y1[5]=4; y2[5]=4; - x1[6]=13; x2[6]=6; y1[6]=0; y2[6]=0; - x1[7]=3; x2[7]=3; y1[7]=13; y2[7]=5; - x1[8]=0; x2[8]=0; y1[8]=2; y2[8]=0; - x1[9]=8; x2[9]=4; y1[9]=13; y2[9]=13; - x1[10]=14; x2[10]=2; y1[10]=1; y2[10]=1; - x1[11]=7; x2[11]=7; y1[11]=11; y2[11]=0; - x1[12]=13; x2[12]=13; y1[12]=14; y2[12]=0; - break; - case 143: - x1[0]=3; x2[0]=2; y1[0]=8; y2[0]=8; - x1[1]=13; x2[1]=13; y1[1]=14; y2[1]=11; - x1[2]=1; x2[2]=0; y1[2]=12; y2[2]=12; - x1[3]=2; x2[3]=2; y1[3]=13; y2[3]=1; - x1[4]=9; x2[4]=2; y1[4]=9; y2[4]=9; - x1[5]=8; x2[5]=8; y1[5]=9; y2[5]=2; - x1[6]=0; x2[6]=0; y1[6]=14; y2[6]=6; - x1[7]=11; x2[7]=11; y1[7]=10; y2[7]=8; - x1[8]=6; x2[8]=6; y1[8]=6; y2[8]=2; - x1[9]=12; x2[9]=3; y1[9]=9; y2[9]=9; - x1[10]=11; x2[10]=11; y1[10]=6; y2[10]=3; - x1[11]=8; x2[11]=8; y1[11]=12; y2[11]=8; - x1[12]=9; x2[12]=9; y1[12]=14; y2[12]=8; - break; - case 144: - x1[0]=8; x2[0]=8; y1[0]=13; y2[0]=10; - x1[1]=5; x2[1]=5; y1[1]=9; y2[1]=4; - x1[2]=6; x2[2]=6; y1[2]=10; y2[2]=1; - x1[3]=1; x2[3]=1; y1[3]=13; y2[3]=7; - x1[4]=13; x2[4]=11; y1[4]=2; y2[4]=2; - x1[5]=14; x2[5]=4; y1[5]=11; y2[5]=11; - x1[6]=11; x2[6]=10; y1[6]=14; y2[6]=14; - x1[7]=2; x2[7]=2; y1[7]=14; y2[7]=4; - x1[8]=3; x2[8]=3; y1[8]=12; y2[8]=5; - x1[9]=9; x2[9]=9; y1[9]=9; y2[9]=4; - x1[10]=4; x2[10]=4; y1[10]=12; y2[10]=0; - x1[11]=12; x2[11]=12; y1[11]=13; y2[11]=12; - x1[12]=4; x2[12]=4; y1[12]=9; y2[12]=1; - break; - case 145: - x1[0]=9; x2[0]=4; y1[0]=8; y2[0]=8; - x1[1]=11; x2[1]=3; y1[1]=7; y2[1]=7; - x1[2]=11; x2[2]=0; y1[2]=1; y2[2]=1; - x1[3]=7; x2[3]=7; y1[3]=5; y2[3]=0; - x1[4]=6; x2[4]=1; y1[4]=6; y2[4]=6; - x1[5]=9; x2[5]=7; y1[5]=4; y2[5]=4; - x1[6]=9; x2[6]=9; y1[6]=11; y2[6]=10; - x1[7]=7; x2[7]=4; y1[7]=3; y2[7]=3; - x1[8]=3; x2[8]=3; y1[8]=11; y2[8]=6; - x1[9]=14; x2[9]=2; y1[9]=12; y2[9]=12; - x1[10]=8; x2[10]=7; y1[10]=6; y2[10]=6; - x1[11]=6; x2[11]=4; y1[11]=4; y2[11]=4; - x1[12]=10; x2[12]=10; y1[12]=10; y2[12]=7; - break; - case 146: - x1[0]=7; x2[0]=0; y1[0]=7; y2[0]=7; - x1[1]=6; x2[1]=6; y1[1]=5; y2[1]=0; - x1[2]=12; x2[2]=10; y1[2]=6; y2[2]=6; - x1[3]=10; x2[3]=10; y1[3]=14; y2[3]=4; - x1[4]=2; x2[4]=2; y1[4]=7; y2[4]=2; - x1[5]=10; x2[5]=10; y1[5]=7; y2[5]=2; - x1[6]=4; x2[6]=4; y1[6]=12; y2[6]=3; - x1[7]=4; x2[7]=0; y1[7]=6; y2[7]=6; - x1[8]=7; x2[8]=7; y1[8]=12; y2[8]=4; - x1[9]=4; x2[9]=3; y1[9]=0; y2[9]=0; - x1[10]=3; x2[10]=3; y1[10]=9; y2[10]=3; - x1[11]=9; x2[11]=4; y1[11]=11; y2[11]=11; - x1[12]=9; x2[12]=9; y1[12]=14; y2[12]=0; - break; - case 147: - x1[0]=14; x2[0]=10; y1[0]=14; y2[0]=14; - x1[1]=14; x2[1]=3; y1[1]=7; y2[1]=7; - x1[2]=14; x2[2]=14; y1[2]=8; y2[2]=1; - x1[3]=6; x2[3]=2; y1[3]=2; y2[3]=2; - x1[4]=0; x2[4]=0; y1[4]=14; y2[4]=2; - x1[5]=13; x2[5]=0; y1[5]=9; y2[5]=9; - x1[6]=0; x2[6]=0; y1[6]=5; y2[6]=2; - x1[7]=5; x2[7]=5; y1[7]=8; y2[7]=7; - x1[8]=6; x2[8]=4; y1[8]=0; y2[8]=0; - x1[9]=11; x2[9]=8; y1[9]=0; y2[9]=0; - x1[10]=0; x2[10]=0; y1[10]=12; y2[10]=0; - x1[11]=9; x2[11]=9; y1[11]=13; y2[11]=8; - x1[12]=9; x2[12]=9; y1[12]=14; y2[12]=4; - break; - case 148: - x1[0]=7; x2[0]=7; y1[0]=9; y2[0]=6; - x1[1]=8; x2[1]=6; y1[1]=14; y2[1]=14; - x1[2]=4; x2[2]=4; y1[2]=6; y2[2]=1; - x1[3]=5; x2[3]=5; y1[3]=4; y2[3]=1; - x1[4]=4; x2[4]=3; y1[4]=9; y2[4]=9; - x1[5]=1; x2[5]=1; y1[5]=14; y2[5]=2; - x1[6]=0; x2[6]=0; y1[6]=14; y2[6]=12; - x1[7]=8; x2[7]=2; y1[7]=9; y2[7]=9; - x1[8]=8; x2[8]=8; y1[8]=14; y2[8]=9; - x1[9]=12; x2[9]=11; y1[9]=3; y2[9]=3; - x1[10]=13; x2[10]=6; y1[10]=2; y2[10]=2; - x1[11]=6; x2[11]=6; y1[11]=1; y2[11]=0; - x1[12]=4; x2[12]=4; y1[12]=8; y2[12]=0; - break; - case 149: - x1[0]=8; x2[0]=8; y1[0]=12; y2[0]=0; - x1[1]=5; x2[1]=5; y1[1]=10; y2[1]=3; - x1[2]=8; x2[2]=6; y1[2]=11; y2[2]=11; - x1[3]=7; x2[3]=1; y1[3]=11; y2[3]=11; - x1[4]=11; x2[4]=0; y1[4]=9; y2[4]=9; - x1[5]=9; x2[5]=6; y1[5]=5; y2[5]=5; - x1[6]=2; x2[6]=2; y1[6]=7; y2[6]=4; - x1[7]=13; x2[7]=13; y1[7]=13; y2[7]=9; - x1[8]=10; x2[8]=6; y1[8]=12; y2[8]=12; - x1[9]=4; x2[9]=4; y1[9]=10; y2[9]=3; - x1[10]=14; x2[10]=1; y1[10]=1; y2[10]=1; - x1[11]=9; x2[11]=6; y1[11]=11; y2[11]=11; - x1[12]=10; x2[12]=3; y1[12]=2; y2[12]=2; - break; - case 150: - x1[0]=10; x2[0]=10; y1[0]=8; y2[0]=7; - x1[1]=3; x2[1]=3; y1[1]=6; y2[1]=2; - x1[2]=12; x2[2]=10; y1[2]=11; y2[2]=11; - x1[3]=7; x2[3]=7; y1[3]=5; y2[3]=4; - x1[4]=7; x2[4]=2; y1[4]=7; y2[4]=7; - x1[5]=0; x2[5]=0; y1[5]=7; y2[5]=5; - x1[6]=9; x2[6]=7; y1[6]=0; y2[6]=0; - x1[7]=5; x2[7]=5; y1[7]=12; y2[7]=1; - x1[8]=12; x2[8]=12; y1[8]=14; y2[8]=10; - x1[9]=1; x2[9]=1; y1[9]=4; y2[9]=0; - x1[10]=14; x2[10]=10; y1[10]=11; y2[10]=11; - x1[11]=11; x2[11]=11; y1[11]=11; y2[11]=3; - x1[12]=2; x2[12]=0; y1[12]=14; y2[12]=14; - break; - case 151: - x1[0]=9; x2[0]=9; y1[0]=7; y2[0]=6; - x1[1]=13; x2[1]=4; y1[1]=2; y2[1]=2; - x1[2]=10; x2[2]=4; y1[2]=4; y2[2]=4; - x1[3]=6; x2[3]=6; y1[3]=10; y2[3]=6; - x1[4]=10; x2[4]=10; y1[4]=13; y2[4]=7; - x1[5]=13; x2[5]=2; y1[5]=2; y2[5]=2; - x1[6]=14; x2[6]=14; y1[6]=14; y2[6]=5; - x1[7]=10; x2[7]=5; y1[7]=13; y2[7]=13; - x1[8]=11; x2[8]=11; y1[8]=14; y2[8]=10; - x1[9]=13; x2[9]=11; y1[9]=0; y2[9]=0; - x1[10]=14; x2[10]=14; y1[10]=3; y2[10]=1; - x1[11]=13; x2[11]=9; y1[11]=8; y2[11]=8; - x1[12]=10; x2[12]=6; y1[12]=1; y2[12]=1; - break; - case 152: - x1[0]=3; x2[0]=3; y1[0]=6; y2[0]=4; - x1[1]=3; x2[1]=3; y1[1]=11; y2[1]=8; - x1[2]=9; x2[2]=9; y1[2]=7; y2[2]=1; - x1[3]=8; x2[3]=4; y1[3]=5; y2[3]=5; - x1[4]=7; x2[4]=7; y1[4]=9; y2[4]=4; - x1[5]=11; x2[5]=6; y1[5]=7; y2[5]=7; - x1[6]=11; x2[6]=1; y1[6]=12; y2[6]=12; - x1[7]=14; x2[7]=14; y1[7]=12; y2[7]=1; - x1[8]=14; x2[8]=14; y1[8]=9; y2[8]=2; - x1[9]=10; x2[9]=3; y1[9]=12; y2[9]=12; - x1[10]=9; x2[10]=7; y1[10]=5; y2[10]=5; - x1[11]=5; x2[11]=5; y1[11]=14; y2[11]=0; - x1[12]=8; x2[12]=8; y1[12]=13; y2[12]=7; - break; - case 153: - x1[0]=10; x2[0]=10; y1[0]=13; y2[0]=8; - x1[1]=10; x2[1]=3; y1[1]=0; y2[1]=0; - x1[2]=14; x2[2]=12; y1[2]=11; y2[2]=11; - x1[3]=3; x2[3]=3; y1[3]=13; y2[3]=3; - x1[4]=14; x2[4]=8; y1[4]=13; y2[4]=13; - x1[5]=3; x2[5]=3; y1[5]=13; y2[5]=9; - x1[6]=0; x2[6]=0; y1[6]=14; y2[6]=5; - x1[7]=2; x2[7]=2; y1[7]=13; y2[7]=11; - x1[8]=13; x2[8]=2; y1[8]=14; y2[8]=14; - x1[9]=11; x2[9]=11; y1[9]=7; y2[9]=3; - x1[10]=6; x2[10]=6; y1[10]=12; y2[10]=5; - x1[11]=9; x2[11]=4; y1[11]=2; y2[11]=2; - x1[12]=14; x2[12]=13; y1[12]=11; y2[12]=11; - break; - case 154: - x1[0]=14; x2[0]=13; y1[0]=0; y2[0]=0; - x1[1]=14; x2[1]=14; y1[1]=9; y2[1]=5; - x1[2]=4; x2[2]=3; y1[2]=4; y2[2]=4; - x1[3]=11; x2[3]=11; y1[3]=13; y2[3]=0; - x1[4]=10; x2[4]=10; y1[4]=13; y2[4]=6; - x1[5]=4; x2[5]=4; y1[5]=7; y2[5]=0; - x1[6]=13; x2[6]=13; y1[6]=12; y2[6]=5; - x1[7]=1; x2[7]=1; y1[7]=5; y2[7]=3; - x1[8]=3; x2[8]=2; y1[8]=1; y2[8]=1; - x1[9]=7; x2[9]=7; y1[9]=12; y2[9]=9; - x1[10]=3; x2[10]=3; y1[10]=11; y2[10]=1; - x1[11]=4; x2[11]=4; y1[11]=8; y2[11]=2; - x1[12]=9; x2[12]=5; y1[12]=10; y2[12]=10; - break; - case 155: - x1[0]=11; x2[0]=11; y1[0]=12; y2[0]=9; - x1[1]=8; x2[1]=7; y1[1]=4; y2[1]=4; - x1[2]=11; x2[2]=11; y1[2]=12; y2[2]=3; - x1[3]=11; x2[3]=6; y1[3]=5; y2[3]=5; - x1[4]=1; x2[4]=1; y1[4]=9; y2[4]=7; - x1[5]=12; x2[5]=5; y1[5]=11; y2[5]=11; - x1[6]=6; x2[6]=5; y1[6]=12; y2[6]=12; - x1[7]=7; x2[7]=6; y1[7]=7; y2[7]=7; - x1[8]=7; x2[8]=7; y1[8]=11; y2[8]=9; - x1[9]=13; x2[9]=13; y1[9]=3; y2[9]=1; - x1[10]=11; x2[10]=6; y1[10]=9; y2[10]=9; - x1[11]=13; x2[11]=12; y1[11]=9; y2[11]=9; - x1[12]=6; x2[12]=6; y1[12]=11; y2[12]=2; - break; - case 156: - x1[0]=4; x2[0]=2; y1[0]=11; y2[0]=11; - x1[1]=11; x2[1]=11; y1[1]=12; y2[1]=10; - x1[2]=10; x2[2]=7; y1[2]=2; y2[2]=2; - x1[3]=14; x2[3]=0; y1[3]=11; y2[3]=11; - x1[4]=14; x2[4]=2; y1[4]=5; y2[4]=5; - x1[5]=7; x2[5]=2; y1[5]=8; y2[5]=8; - x1[6]=4; x2[6]=3; y1[6]=1; y2[6]=1; - x1[7]=5; x2[7]=1; y1[7]=3; y2[7]=3; - x1[8]=12; x2[8]=9; y1[8]=10; y2[8]=10; - x1[9]=6; x2[9]=5; y1[9]=0; y2[9]=0; - x1[10]=13; x2[10]=1; y1[10]=4; y2[10]=4; - x1[11]=1; x2[11]=1; y1[11]=12; y2[11]=3; - x1[12]=10; x2[12]=1; y1[12]=1; y2[12]=1; - break; - case 157: - x1[0]=8; x2[0]=8; y1[0]=3; y2[0]=2; - x1[1]=3; x2[1]=1; y1[1]=14; y2[1]=14; - x1[2]=9; x2[2]=2; y1[2]=5; y2[2]=5; - x1[3]=14; x2[3]=14; y1[3]=14; y2[3]=8; - x1[4]=6; x2[4]=6; y1[4]=6; y2[4]=0; - x1[5]=11; x2[5]=5; y1[5]=9; y2[5]=9; - x1[6]=11; x2[6]=11; y1[6]=14; y2[6]=7; - x1[7]=5; x2[7]=0; y1[7]=10; y2[7]=10; - x1[8]=10; x2[8]=10; y1[8]=13; y2[8]=9; - x1[9]=1; x2[9]=0; y1[9]=13; y2[9]=13; - x1[10]=0; x2[10]=0; y1[10]=13; y2[10]=0; - x1[11]=6; x2[11]=6; y1[11]=10; y2[11]=9; - x1[12]=2; x2[12]=2; y1[12]=8; y2[12]=5; - break; - case 158: - x1[0]=2; x2[0]=1; y1[0]=3; y2[0]=3; - x1[1]=1; x2[1]=0; y1[1]=10; y2[1]=10; - x1[2]=1; x2[2]=1; y1[2]=14; y2[2]=4; - x1[3]=14; x2[3]=7; y1[3]=9; y2[3]=9; - x1[4]=10; x2[4]=8; y1[4]=9; y2[4]=9; - x1[5]=9; x2[5]=9; y1[5]=6; y2[5]=1; - x1[6]=4; x2[6]=4; y1[6]=14; y2[6]=4; - x1[7]=10; x2[7]=1; y1[7]=13; y2[7]=13; - x1[8]=1; x2[8]=1; y1[8]=13; y2[8]=5; - x1[9]=3; x2[9]=1; y1[9]=10; y2[9]=10; - x1[10]=2; x2[10]=2; y1[10]=5; y2[10]=4; - x1[11]=3; x2[11]=3; y1[11]=3; y2[11]=0; - x1[12]=9; x2[12]=9; y1[12]=13; y2[12]=11; - break; - case 159: - x1[0]=8; x2[0]=7; y1[0]=11; y2[0]=11; - x1[1]=11; x2[1]=11; y1[1]=9; y2[1]=0; - x1[2]=13; x2[2]=9; y1[2]=12; y2[2]=12; - x1[3]=8; x2[3]=6; y1[3]=3; y2[3]=3; - x1[4]=8; x2[4]=8; y1[4]=12; y2[4]=1; - x1[5]=14; x2[5]=11; y1[5]=6; y2[5]=6; - x1[6]=2; x2[6]=2; y1[6]=4; y2[6]=3; - x1[7]=10; x2[7]=10; y1[7]=3; y2[7]=2; - x1[8]=12; x2[8]=10; y1[8]=2; y2[8]=2; - x1[9]=6; x2[9]=6; y1[9]=3; y2[9]=2; - x1[10]=0; x2[10]=0; y1[10]=12; y2[10]=7; - x1[11]=7; x2[11]=4; y1[11]=1; y2[11]=1; - x1[12]=11; x2[12]=2; y1[12]=4; y2[12]=4; - break; - case 160: - x1[0]=10; x2[0]=1; y1[0]=9; y2[0]=9; - x1[1]=4; x2[1]=4; y1[1]=2; y2[1]=0; - x1[2]=11; x2[2]=10; y1[2]=8; y2[2]=8; - x1[3]=12; x2[3]=12; y1[3]=1; y2[3]=0; - x1[4]=13; x2[4]=6; y1[4]=5; y2[4]=5; - x1[5]=12; x2[5]=12; y1[5]=13; y2[5]=0; - x1[6]=5; x2[6]=1; y1[6]=10; y2[6]=10; - x1[7]=14; x2[7]=9; y1[7]=12; y2[7]=12; - x1[8]=9; x2[8]=1; y1[8]=6; y2[8]=6; - x1[9]=11; x2[9]=11; y1[9]=11; y2[9]=5; - x1[10]=12; x2[10]=12; y1[10]=13; y2[10]=11; - x1[11]=10; x2[11]=10; y1[11]=14; y2[11]=5; - x1[12]=6; x2[12]=5; y1[12]=6; y2[12]=6; - break; - case 161: - x1[0]=14; x2[0]=13; y1[0]=3; y2[0]=3; - x1[1]=6; x2[1]=3; y1[1]=0; y2[1]=0; - x1[2]=5; x2[2]=5; y1[2]=9; y2[2]=6; - x1[3]=11; x2[3]=11; y1[3]=9; y2[3]=8; - x1[4]=7; x2[4]=7; y1[4]=11; y2[4]=7; - x1[5]=0; x2[5]=0; y1[5]=13; y2[5]=8; - x1[6]=12; x2[6]=7; y1[6]=12; y2[6]=12; - x1[7]=11; x2[7]=9; y1[7]=1; y2[7]=1; - x1[8]=14; x2[8]=13; y1[8]=7; y2[8]=7; - x1[9]=6; x2[9]=4; y1[9]=12; y2[9]=12; - x1[10]=8; x2[10]=8; y1[10]=11; y2[10]=2; - x1[11]=13; x2[11]=13; y1[11]=13; y2[11]=2; - x1[12]=10; x2[12]=10; y1[12]=11; y2[12]=4; - break; - case 162: - x1[0]=11; x2[0]=9; y1[0]=2; y2[0]=2; - x1[1]=6; x2[1]=6; y1[1]=11; y2[1]=9; - x1[2]=13; x2[2]=13; y1[2]=12; y2[2]=7; - x1[3]=0; x2[3]=0; y1[3]=14; y2[3]=9; - x1[4]=12; x2[4]=9; y1[4]=11; y2[4]=11; - x1[5]=10; x2[5]=4; y1[5]=2; y2[5]=2; - x1[6]=10; x2[6]=3; y1[6]=1; y2[6]=1; - x1[7]=6; x2[7]=6; y1[7]=13; y2[7]=10; - x1[8]=11; x2[8]=11; y1[8]=14; y2[8]=9; - x1[9]=13; x2[9]=13; y1[9]=9; y2[9]=8; - x1[10]=5; x2[10]=2; y1[10]=12; y2[10]=12; - x1[11]=12; x2[11]=6; y1[11]=14; y2[11]=14; - x1[12]=5; x2[12]=0; y1[12]=5; y2[12]=5; - break; - case 163: - x1[0]=10; x2[0]=1; y1[0]=7; y2[0]=7; - x1[1]=12; x2[1]=1; y1[1]=1; y2[1]=1; - x1[2]=7; x2[2]=7; y1[2]=11; y2[2]=5; - x1[3]=12; x2[3]=6; y1[3]=10; y2[3]=10; - x1[4]=5; x2[4]=5; y1[4]=7; y2[4]=0; - x1[5]=8; x2[5]=5; y1[5]=3; y2[5]=3; - x1[6]=5; x2[6]=5; y1[6]=6; y2[6]=1; - x1[7]=6; x2[7]=6; y1[7]=11; y2[7]=7; - x1[8]=8; x2[8]=8; y1[8]=11; y2[8]=10; - x1[9]=9; x2[9]=9; y1[9]=10; y2[9]=2; - x1[10]=11; x2[10]=11; y1[10]=12; y2[10]=4; - x1[11]=4; x2[11]=4; y1[11]=3; y2[11]=1; - x1[12]=5; x2[12]=0; y1[12]=12; y2[12]=12; - break; - case 164: - x1[0]=12; x2[0]=0; y1[0]=4; y2[0]=4; - x1[1]=8; x2[1]=6; y1[1]=12; y2[1]=12; - x1[2]=14; x2[2]=5; y1[2]=13; y2[2]=13; - x1[3]=5; x2[3]=5; y1[3]=13; y2[3]=9; - x1[4]=4; x2[4]=4; y1[4]=11; y2[4]=9; - x1[5]=14; x2[5]=14; y1[5]=10; y2[5]=1; - x1[6]=10; x2[6]=5; y1[6]=12; y2[6]=12; - x1[7]=8; x2[7]=8; y1[7]=4; y2[7]=1; - x1[8]=1; x2[8]=1; y1[8]=5; y2[8]=2; - x1[9]=11; x2[9]=8; y1[9]=8; y2[9]=8; - x1[10]=9; x2[10]=9; y1[10]=13; y2[10]=10; - x1[11]=4; x2[11]=3; y1[11]=10; y2[11]=10; - x1[12]=8; x2[12]=8; y1[12]=8; y2[12]=1; - break; - case 165: - x1[0]=11; x2[0]=0; y1[0]=5; y2[0]=5; - x1[1]=3; x2[1]=3; y1[1]=13; y2[1]=4; - x1[2]=13; x2[2]=13; y1[2]=12; y2[2]=2; - x1[3]=9; x2[3]=7; y1[3]=12; y2[3]=12; - x1[4]=11; x2[4]=9; y1[4]=10; y2[4]=10; - x1[5]=10; x2[5]=10; y1[5]=7; y2[5]=6; - x1[6]=11; x2[6]=11; y1[6]=8; y2[6]=2; - x1[7]=10; x2[7]=0; y1[7]=14; y2[7]=14; - x1[8]=9; x2[8]=9; y1[8]=14; y2[8]=9; - x1[9]=13; x2[9]=8; y1[9]=2; y2[9]=2; - x1[10]=2; x2[10]=1; y1[10]=0; y2[10]=0; - x1[11]=7; x2[11]=7; y1[11]=13; y2[11]=5; - x1[12]=7; x2[12]=7; y1[12]=6; y2[12]=5; - break; - case 166: - x1[0]=3; x2[0]=3; y1[0]=14; y2[0]=10; - x1[1]=12; x2[1]=1; y1[1]=10; y2[1]=10; - x1[2]=9; x2[2]=9; y1[2]=12; y2[2]=4; - x1[3]=11; x2[3]=3; y1[3]=1; y2[3]=1; - x1[4]=2; x2[4]=2; y1[4]=9; y2[4]=5; - x1[5]=14; x2[5]=8; y1[5]=2; y2[5]=2; - x1[6]=14; x2[6]=14; y1[6]=7; y2[6]=2; - x1[7]=8; x2[7]=8; y1[7]=2; y2[7]=1; - x1[8]=12; x2[8]=10; y1[8]=14; y2[8]=14; - x1[9]=12; x2[9]=8; y1[9]=7; y2[9]=7; - x1[10]=9; x2[10]=5; y1[10]=5; y2[10]=5; - x1[11]=12; x2[11]=11; y1[11]=14; y2[11]=14; - x1[12]=4; x2[12]=4; y1[12]=3; y2[12]=0; - break; - case 167: - x1[0]=6; x2[0]=0; y1[0]=8; y2[0]=8; - x1[1]=10; x2[1]=8; y1[1]=6; y2[1]=6; - x1[2]=9; x2[2]=4; y1[2]=12; y2[2]=12; - x1[3]=6; x2[3]=6; y1[3]=14; y2[3]=10; - x1[4]=13; x2[4]=9; y1[4]=10; y2[4]=10; - x1[5]=3; x2[5]=3; y1[5]=7; y2[5]=6; - x1[6]=14; x2[6]=11; y1[6]=10; y2[6]=10; - x1[7]=0; x2[7]=0; y1[7]=14; y2[7]=13; - x1[8]=3; x2[8]=3; y1[8]=12; y2[8]=8; - x1[9]=8; x2[9]=6; y1[9]=2; y2[9]=2; - x1[10]=6; x2[10]=2; y1[10]=5; y2[10]=5; - x1[11]=10; x2[11]=0; y1[11]=3; y2[11]=3; - x1[12]=9; x2[12]=9; y1[12]=12; y2[12]=1; - break; - case 168: - x1[0]=4; x2[0]=4; y1[0]=6; y2[0]=4; - x1[1]=11; x2[1]=11; y1[1]=14; y2[1]=3; - x1[2]=14; x2[2]=14; y1[2]=12; y2[2]=4; - x1[3]=8; x2[3]=5; y1[3]=7; y2[3]=7; - x1[4]=12; x2[4]=9; y1[4]=14; y2[4]=14; - x1[5]=2; x2[5]=2; y1[5]=11; y2[5]=5; - x1[6]=13; x2[6]=13; y1[6]=14; y2[6]=1; - x1[7]=5; x2[7]=5; y1[7]=5; y2[7]=1; - x1[8]=14; x2[8]=11; y1[8]=12; y2[8]=12; - x1[9]=6; x2[9]=6; y1[9]=10; y2[9]=0; - x1[10]=4; x2[10]=4; y1[10]=3; y2[10]=2; - x1[11]=7; x2[11]=1; y1[11]=8; y2[11]=8; - x1[12]=7; x2[12]=3; y1[12]=9; y2[12]=9; - break; - case 169: - x1[0]=11; x2[0]=1; y1[0]=11; y2[0]=11; - x1[1]=6; x2[1]=6; y1[1]=7; y2[1]=0; - x1[2]=5; x2[2]=5; y1[2]=11; y2[2]=8; - x1[3]=14; x2[3]=2; y1[3]=2; y2[3]=2; - x1[4]=7; x2[4]=7; y1[4]=11; y2[4]=4; - x1[5]=7; x2[5]=7; y1[5]=7; y2[5]=3; - x1[6]=9; x2[6]=9; y1[6]=10; y2[6]=3; - x1[7]=2; x2[7]=2; y1[7]=12; y2[7]=7; - x1[8]=12; x2[8]=2; y1[8]=8; y2[8]=8; - x1[9]=14; x2[9]=14; y1[9]=12; y2[9]=3; - x1[10]=9; x2[10]=2; y1[10]=8; y2[10]=8; - x1[11]=4; x2[11]=4; y1[11]=4; y2[11]=1; - x1[12]=8; x2[12]=0; y1[12]=1; y2[12]=1; - break; - case 170: - x1[0]=8; x2[0]=7; y1[0]=0; y2[0]=0; - x1[1]=8; x2[1]=3; y1[1]=8; y2[1]=8; - x1[2]=13; x2[2]=2; y1[2]=10; y2[2]=10; - x1[3]=12; x2[3]=5; y1[3]=7; y2[3]=7; - x1[4]=2; x2[4]=2; y1[4]=11; y2[4]=4; - x1[5]=6; x2[5]=6; y1[5]=14; y2[5]=4; - x1[6]=11; x2[6]=5; y1[6]=11; y2[6]=11; - x1[7]=9; x2[7]=6; y1[7]=2; y2[7]=2; - x1[8]=1; x2[8]=1; y1[8]=5; y2[8]=4; - x1[9]=4; x2[9]=4; y1[9]=10; y2[9]=5; - x1[10]=12; x2[10]=12; y1[10]=9; y2[10]=6; - x1[11]=8; x2[11]=8; y1[11]=1; y2[11]=0; - x1[12]=4; x2[12]=4; y1[12]=8; y2[12]=5; - break; - case 171: - x1[0]=12; x2[0]=12; y1[0]=5; y2[0]=2; - x1[1]=3; x2[1]=3; y1[1]=6; y2[1]=5; - x1[2]=14; x2[2]=14; y1[2]=13; y2[2]=12; - x1[3]=10; x2[3]=10; y1[3]=9; y2[3]=7; - x1[4]=5; x2[4]=3; y1[4]=10; y2[4]=10; - x1[5]=5; x2[5]=4; y1[5]=0; y2[5]=0; - x1[6]=8; x2[6]=8; y1[6]=13; y2[6]=12; - x1[7]=14; x2[7]=11; y1[7]=11; y2[7]=11; - x1[8]=13; x2[8]=1; y1[8]=7; y2[8]=7; - x1[9]=6; x2[9]=3; y1[9]=10; y2[9]=10; - x1[10]=3; x2[10]=3; y1[10]=14; y2[10]=5; - x1[11]=2; x2[11]=2; y1[11]=7; y2[11]=6; - x1[12]=7; x2[12]=7; y1[12]=8; y2[12]=0; - break; - case 172: - x1[0]=2; x2[0]=2; y1[0]=10; y2[0]=3; - x1[1]=9; x2[1]=6; y1[1]=13; y2[1]=13; - x1[2]=6; x2[2]=0; y1[2]=12; y2[2]=12; - x1[3]=4; x2[3]=0; y1[3]=4; y2[3]=4; - x1[4]=13; x2[4]=9; y1[4]=9; y2[4]=9; - x1[5]=2; x2[5]=0; y1[5]=3; y2[5]=3; - x1[6]=1; x2[6]=1; y1[6]=7; y2[6]=6; - x1[7]=13; x2[7]=13; y1[7]=10; y2[7]=9; - x1[8]=7; x2[8]=7; y1[8]=7; y2[8]=2; - x1[9]=6; x2[9]=3; y1[9]=1; y2[9]=1; - x1[10]=12; x2[10]=12; y1[10]=11; y2[10]=7; - x1[11]=11; x2[11]=3; y1[11]=14; y2[11]=14; - x1[12]=8; x2[12]=8; y1[12]=6; y2[12]=3; - break; - case 173: - x1[0]=12; x2[0]=12; y1[0]=12; y2[0]=1; - x1[1]=4; x2[1]=4; y1[1]=14; y2[1]=1; - x1[2]=1; x2[2]=1; y1[2]=4; y2[2]=1; - x1[3]=14; x2[3]=14; y1[3]=14; y2[3]=4; - x1[4]=10; x2[4]=6; y1[4]=0; y2[4]=0; - x1[5]=11; x2[5]=8; y1[5]=4; y2[5]=4; - x1[6]=11; x2[6]=11; y1[6]=14; y2[6]=12; - x1[7]=12; x2[7]=0; y1[7]=1; y2[7]=1; - x1[8]=3; x2[8]=2; y1[8]=5; y2[8]=5; - x1[9]=9; x2[9]=9; y1[9]=7; y2[9]=0; - x1[10]=11; x2[10]=0; y1[10]=6; y2[10]=6; - x1[11]=11; x2[11]=4; y1[11]=2; y2[11]=2; - x1[12]=8; x2[12]=8; y1[12]=14; y2[12]=6; - break; - case 174: - x1[0]=9; x2[0]=8; y1[0]=8; y2[0]=8; - x1[1]=8; x2[1]=5; y1[1]=11; y2[1]=11; - x1[2]=9; x2[2]=8; y1[2]=7; y2[2]=7; - x1[3]=5; x2[3]=5; y1[3]=8; y2[3]=6; - x1[4]=3; x2[4]=0; y1[4]=4; y2[4]=4; - x1[5]=8; x2[5]=1; y1[5]=13; y2[5]=13; - x1[6]=9; x2[6]=9; y1[6]=5; y2[6]=4; - x1[7]=10; x2[7]=10; y1[7]=11; y2[7]=10; - x1[8]=11; x2[8]=9; y1[8]=4; y2[8]=4; - x1[9]=11; x2[9]=11; y1[9]=14; y2[9]=2; - x1[10]=2; x2[10]=0; y1[10]=5; y2[10]=5; - x1[11]=0; x2[11]=0; y1[11]=7; y2[11]=2; - x1[12]=8; x2[12]=8; y1[12]=3; y2[12]=1; - break; - case 175: - x1[0]=14; x2[0]=13; y1[0]=9; y2[0]=9; - x1[1]=4; x2[1]=4; y1[1]=14; y2[1]=6; - x1[2]=9; x2[2]=9; y1[2]=9; y2[2]=5; - x1[3]=11; x2[3]=11; y1[3]=8; y2[3]=7; - x1[4]=0; x2[4]=0; y1[4]=9; y2[4]=4; - x1[5]=8; x2[5]=8; y1[5]=5; y2[5]=2; - x1[6]=8; x2[6]=6; y1[6]=10; y2[6]=10; - x1[7]=13; x2[7]=2; y1[7]=9; y2[7]=9; - x1[8]=0; x2[8]=0; y1[8]=10; y2[8]=0; - x1[9]=12; x2[9]=12; y1[9]=14; y2[9]=11; - x1[10]=5; x2[10]=5; y1[10]=13; y2[10]=7; - x1[11]=14; x2[11]=14; y1[11]=10; y2[11]=0; - x1[12]=7; x2[12]=0; y1[12]=5; y2[12]=5; - break; - case 176: - x1[0]=5; x2[0]=5; y1[0]=10; y2[0]=0; - x1[1]=13; x2[1]=5; y1[1]=0; y2[1]=0; - x1[2]=14; x2[2]=14; y1[2]=5; y2[2]=0; - x1[3]=2; x2[3]=2; y1[3]=13; y2[3]=9; - x1[4]=10; x2[4]=10; y1[4]=7; y2[4]=1; - x1[5]=11; x2[5]=11; y1[5]=13; y2[5]=8; - x1[6]=12; x2[6]=11; y1[6]=2; y2[6]=2; - x1[7]=3; x2[7]=1; y1[7]=0; y2[7]=0; - x1[8]=8; x2[8]=8; y1[8]=13; y2[8]=2; - x1[9]=14; x2[9]=6; y1[9]=8; y2[9]=8; - x1[10]=6; x2[10]=6; y1[10]=14; y2[10]=11; - x1[11]=7; x2[11]=7; y1[11]=14; y2[11]=3; - x1[12]=8; x2[12]=3; y1[12]=4; y2[12]=4; - break; - case 177: - x1[0]=2; x2[0]=0; y1[0]=9; y2[0]=9; - x1[1]=3; x2[1]=3; y1[1]=8; y2[1]=2; - x1[2]=7; x2[2]=7; y1[2]=4; y2[2]=1; - x1[3]=9; x2[3]=1; y1[3]=10; y2[3]=10; - x1[4]=11; x2[4]=3; y1[4]=2; y2[4]=2; - x1[5]=11; x2[5]=11; y1[5]=6; y2[5]=2; - x1[6]=4; x2[6]=4; y1[6]=13; y2[6]=7; - x1[7]=10; x2[7]=10; y1[7]=3; y2[7]=0; - x1[8]=10; x2[8]=2; y1[8]=3; y2[8]=3; - x1[9]=5; x2[9]=1; y1[9]=2; y2[9]=2; - x1[10]=5; x2[10]=5; y1[10]=10; y2[10]=6; - x1[11]=13; x2[11]=7; y1[11]=8; y2[11]=8; - x1[12]=12; x2[12]=6; y1[12]=8; y2[12]=8; - break; - case 178: - x1[0]=14; x2[0]=8; y1[0]=11; y2[0]=11; - x1[1]=10; x2[1]=10; y1[1]=9; y2[1]=3; - x1[2]=1; x2[2]=1; y1[2]=9; y2[2]=1; - x1[3]=13; x2[3]=13; y1[3]=14; y2[3]=9; - x1[4]=0; x2[4]=0; y1[4]=12; y2[4]=9; - x1[5]=12; x2[5]=12; y1[5]=5; y2[5]=1; - x1[6]=8; x2[6]=8; y1[6]=8; y2[6]=3; - x1[7]=0; x2[7]=0; y1[7]=4; y2[7]=0; - x1[8]=11; x2[8]=10; y1[8]=1; y2[8]=1; - x1[9]=2; x2[9]=2; y1[9]=13; y2[9]=3; - x1[10]=14; x2[10]=0; y1[10]=2; y2[10]=2; - x1[11]=9; x2[11]=3; y1[11]=4; y2[11]=4; - x1[12]=10; x2[12]=10; y1[12]=2; y2[12]=0; - break; - case 179: - x1[0]=14; x2[0]=14; y1[0]=13; y2[0]=5; - x1[1]=6; x2[1]=6; y1[1]=3; y2[1]=0; - x1[2]=6; x2[2]=6; y1[2]=13; y2[2]=5; - x1[3]=5; x2[3]=5; y1[3]=14; y2[3]=6; - x1[4]=5; x2[4]=0; y1[4]=8; y2[4]=8; - x1[5]=12; x2[5]=12; y1[5]=10; y2[5]=4; - x1[6]=2; x2[6]=2; y1[6]=5; y2[6]=0; - x1[7]=6; x2[7]=6; y1[7]=4; y2[7]=3; - x1[8]=12; x2[8]=11; y1[8]=1; y2[8]=1; - x1[9]=2; x2[9]=2; y1[9]=7; y2[9]=0; - x1[10]=6; x2[10]=6; y1[10]=9; y2[10]=0; - x1[11]=14; x2[11]=13; y1[11]=2; y2[11]=2; - x1[12]=2; x2[12]=1; y1[12]=7; y2[12]=7; - break; - case 180: - x1[0]=13; x2[0]=13; y1[0]=8; y2[0]=3; - x1[1]=3; x2[1]=3; y1[1]=14; y2[1]=6; - x1[2]=10; x2[2]=10; y1[2]=5; y2[2]=4; - x1[3]=13; x2[3]=5; y1[3]=10; y2[3]=10; - x1[4]=2; x2[4]=2; y1[4]=8; y2[4]=3; - x1[5]=10; x2[5]=6; y1[5]=10; y2[5]=10; - x1[6]=12; x2[6]=12; y1[6]=14; y2[6]=8; - x1[7]=6; x2[7]=6; y1[7]=10; y2[7]=2; - x1[8]=8; x2[8]=4; y1[8]=0; y2[8]=0; - x1[9]=10; x2[9]=8; y1[9]=2; y2[9]=2; - x1[10]=6; x2[10]=5; y1[10]=1; y2[10]=1; - x1[11]=11; x2[11]=11; y1[11]=11; y2[11]=1; - x1[12]=7; x2[12]=5; y1[12]=8; y2[12]=8; - break; - case 181: - x1[0]=13; x2[0]=13; y1[0]=11; y2[0]=7; - x1[1]=13; x2[1]=2; y1[1]=13; y2[1]=13; - x1[2]=11; x2[2]=11; y1[2]=9; y2[2]=3; - x1[3]=6; x2[3]=3; y1[3]=2; y2[3]=2; - x1[4]=9; x2[4]=4; y1[4]=9; y2[4]=9; - x1[5]=13; x2[5]=7; y1[5]=7; y2[5]=7; - x1[6]=14; x2[6]=8; y1[6]=9; y2[6]=9; - x1[7]=9; x2[7]=1; y1[7]=8; y2[7]=8; - x1[8]=11; x2[8]=11; y1[8]=12; y2[8]=8; - x1[9]=6; x2[9]=4; y1[9]=1; y2[9]=1; - x1[10]=14; x2[10]=14; y1[10]=11; y2[10]=7; - x1[11]=13; x2[11]=4; y1[11]=1; y2[11]=1; - x1[12]=5; x2[12]=5; y1[12]=12; y2[12]=0; - break; - case 182: - x1[0]=14; x2[0]=6; y1[0]=4; y2[0]=4; - x1[1]=13; x2[1]=2; y1[1]=12; y2[1]=12; - x1[2]=13; x2[2]=13; y1[2]=13; y2[2]=3; - x1[3]=14; x2[3]=0; y1[3]=0; y2[3]=0; - x1[4]=12; x2[4]=12; y1[4]=12; y2[4]=6; - x1[5]=10; x2[5]=9; y1[5]=11; y2[5]=11; - x1[6]=13; x2[6]=13; y1[6]=7; y2[6]=5; - x1[7]=0; x2[7]=0; y1[7]=8; y2[7]=6; - x1[8]=13; x2[8]=13; y1[8]=4; y2[8]=2; - x1[9]=4; x2[9]=4; y1[9]=11; y2[9]=1; - x1[10]=1; x2[10]=1; y1[10]=11; y2[10]=7; - x1[11]=13; x2[11]=6; y1[11]=13; y2[11]=13; - x1[12]=12; x2[12]=10; y1[12]=5; y2[12]=5; - break; - case 183: - x1[0]=13; x2[0]=1; y1[0]=0; y2[0]=0; - x1[1]=4; x2[1]=4; y1[1]=11; y2[1]=0; - x1[2]=14; x2[2]=5; y1[2]=3; y2[2]=3; - x1[3]=9; x2[3]=9; y1[3]=13; y2[3]=9; - x1[4]=0; x2[4]=0; y1[4]=10; y2[4]=1; - x1[5]=4; x2[5]=4; y1[5]=13; y2[5]=5; - x1[6]=12; x2[6]=0; y1[6]=9; y2[6]=9; - x1[7]=0; x2[7]=0; y1[7]=11; y2[7]=9; - x1[8]=13; x2[8]=1; y1[8]=5; y2[8]=5; - x1[9]=0; x2[9]=0; y1[9]=13; y2[9]=2; - x1[10]=12; x2[10]=10; y1[10]=0; y2[10]=0; - x1[11]=7; x2[11]=7; y1[11]=7; y2[11]=5; - x1[12]=4; x2[12]=2; y1[12]=1; y2[12]=1; - break; - case 184: - x1[0]=5; x2[0]=5; y1[0]=14; y2[0]=7; - x1[1]=6; x2[1]=1; y1[1]=4; y2[1]=4; - x1[2]=13; x2[2]=6; y1[2]=9; y2[2]=9; - x1[3]=8; x2[3]=8; y1[3]=9; y2[3]=0; - x1[4]=5; x2[4]=0; y1[4]=1; y2[4]=1; - x1[5]=6; x2[5]=5; y1[5]=11; y2[5]=11; - x1[6]=11; x2[6]=11; y1[6]=13; y2[6]=12; - x1[7]=3; x2[7]=3; y1[7]=12; y2[7]=3; - x1[8]=11; x2[8]=0; y1[8]=14; y2[8]=14; - x1[9]=14; x2[9]=1; y1[9]=2; y2[9]=2; - x1[10]=13; x2[10]=13; y1[10]=3; y2[10]=0; - x1[11]=14; x2[11]=3; y1[11]=2; y2[11]=2; - x1[12]=14; x2[12]=9; y1[12]=9; y2[12]=9; - break; - case 185: - x1[0]=10; x2[0]=5; y1[0]=2; y2[0]=2; - x1[1]=13; x2[1]=2; y1[1]=3; y2[1]=3; - x1[2]=7; x2[2]=5; y1[2]=10; y2[2]=10; - x1[3]=11; x2[3]=1; y1[3]=3; y2[3]=3; - x1[4]=12; x2[4]=6; y1[4]=5; y2[4]=5; - x1[5]=6; x2[5]=6; y1[5]=8; y2[5]=7; - x1[6]=10; x2[6]=6; y1[6]=3; y2[6]=3; - x1[7]=11; x2[7]=7; y1[7]=10; y2[7]=10; - x1[8]=3; x2[8]=1; y1[8]=5; y2[8]=5; - x1[9]=12; x2[9]=12; y1[9]=10; y2[9]=0; - x1[10]=12; x2[10]=9; y1[10]=12; y2[10]=12; - x1[11]=10; x2[11]=10; y1[11]=11; y2[11]=3; - x1[12]=8; x2[12]=8; y1[12]=9; y2[12]=1; - break; - case 186: - x1[0]=12; x2[0]=0; y1[0]=3; y2[0]=3; - x1[1]=11; x2[1]=11; y1[1]=6; y2[1]=5; - x1[2]=12; x2[2]=2; y1[2]=6; y2[2]=6; - x1[3]=5; x2[3]=1; y1[3]=8; y2[3]=8; - x1[4]=13; x2[4]=6; y1[4]=12; y2[4]=12; - x1[5]=5; x2[5]=4; y1[5]=12; y2[5]=12; - x1[6]=8; x2[6]=3; y1[6]=11; y2[6]=11; - x1[7]=14; x2[7]=7; y1[7]=1; y2[7]=1; - x1[8]=14; x2[8]=6; y1[8]=13; y2[8]=13; - x1[9]=10; x2[9]=9; y1[9]=13; y2[9]=13; - x1[10]=11; x2[10]=4; y1[10]=9; y2[10]=9; - x1[11]=7; x2[11]=5; y1[11]=14; y2[11]=14; - x1[12]=3; x2[12]=2; y1[12]=4; y2[12]=4; - break; - case 187: - x1[0]=13; x2[0]=13; y1[0]=11; y2[0]=3; - x1[1]=8; x2[1]=1; y1[1]=7; y2[1]=7; - x1[2]=6; x2[2]=0; y1[2]=7; y2[2]=7; - x1[3]=8; x2[3]=8; y1[3]=11; y2[3]=0; - x1[4]=7; x2[4]=3; y1[4]=3; y2[4]=3; - x1[5]=3; x2[5]=3; y1[5]=5; y2[5]=3; - x1[6]=11; x2[6]=11; y1[6]=14; y2[6]=5; - x1[7]=11; x2[7]=8; y1[7]=6; y2[7]=6; - x1[8]=8; x2[8]=7; y1[8]=13; y2[8]=13; - x1[9]=8; x2[9]=8; y1[9]=11; y2[9]=1; - x1[10]=8; x2[10]=2; y1[10]=3; y2[10]=3; - x1[11]=11; x2[11]=4; y1[11]=8; y2[11]=8; - x1[12]=14; x2[12]=14; y1[12]=13; y2[12]=7; - break; - case 188: - x1[0]=12; x2[0]=5; y1[0]=4; y2[0]=4; - x1[1]=6; x2[1]=6; y1[1]=11; y2[1]=6; - x1[2]=6; x2[2]=4; y1[2]=2; y2[2]=2; - x1[3]=8; x2[3]=3; y1[3]=1; y2[3]=1; - x1[4]=12; x2[4]=9; y1[4]=6; y2[4]=6; - x1[5]=13; x2[5]=0; y1[5]=4; y2[5]=4; - x1[6]=7; x2[6]=7; y1[6]=8; y2[6]=7; - x1[7]=8; x2[7]=7; y1[7]=8; y2[7]=8; - x1[8]=14; x2[8]=14; y1[8]=13; y2[8]=6; - x1[9]=8; x2[9]=8; y1[9]=10; y2[9]=7; - x1[10]=9; x2[10]=3; y1[10]=7; y2[10]=7; - x1[11]=11; x2[11]=4; y1[11]=3; y2[11]=3; - x1[12]=10; x2[12]=10; y1[12]=11; y2[12]=6; - break; - case 189: - x1[0]=7; x2[0]=7; y1[0]=3; y2[0]=1; - x1[1]=13; x2[1]=12; y1[1]=5; y2[1]=5; - x1[2]=11; x2[2]=11; y1[2]=8; y2[2]=4; - x1[3]=1; x2[3]=1; y1[3]=14; y2[3]=0; - x1[4]=10; x2[4]=0; y1[4]=13; y2[4]=13; - x1[5]=8; x2[5]=7; y1[5]=10; y2[5]=10; - x1[6]=11; x2[6]=4; y1[6]=7; y2[6]=7; - x1[7]=12; x2[7]=1; y1[7]=8; y2[7]=8; - x1[8]=12; x2[8]=5; y1[8]=10; y2[8]=10; - x1[9]=9; x2[9]=5; y1[9]=6; y2[9]=6; - x1[10]=2; x2[10]=0; y1[10]=8; y2[10]=8; - x1[11]=12; x2[11]=12; y1[11]=13; y2[11]=1; - x1[12]=6; x2[12]=6; y1[12]=8; y2[12]=5; - break; - case 190: - x1[0]=6; x2[0]=2; y1[0]=6; y2[0]=6; - x1[1]=5; x2[1]=5; y1[1]=13; y2[1]=11; - x1[2]=4; x2[2]=4; y1[2]=5; y2[2]=0; - x1[3]=12; x2[3]=8; y1[3]=4; y2[3]=4; - x1[4]=4; x2[4]=2; y1[4]=0; y2[4]=0; - x1[5]=13; x2[5]=12; y1[5]=0; y2[5]=0; - x1[6]=3; x2[6]=2; y1[6]=3; y2[6]=3; - x1[7]=3; x2[7]=2; y1[7]=10; y2[7]=10; - x1[8]=11; x2[8]=9; y1[8]=5; y2[8]=5; - x1[9]=14; x2[9]=8; y1[9]=5; y2[9]=5; - x1[10]=14; x2[10]=0; y1[10]=1; y2[10]=1; - x1[11]=10; x2[11]=10; y1[11]=7; y2[11]=4; - x1[12]=9; x2[12]=9; y1[12]=14; y2[12]=13; - break; - case 191: - x1[0]=10; x2[0]=5; y1[0]=11; y2[0]=11; - x1[1]=7; x2[1]=7; y1[1]=6; y2[1]=2; - x1[2]=1; x2[2]=1; y1[2]=10; y2[2]=6; - x1[3]=4; x2[3]=1; y1[3]=3; y2[3]=3; - x1[4]=8; x2[4]=0; y1[4]=7; y2[4]=7; - x1[5]=11; x2[5]=9; y1[5]=7; y2[5]=7; - x1[6]=6; x2[6]=6; y1[6]=14; y2[6]=9; - x1[7]=4; x2[7]=1; y1[7]=1; y2[7]=1; - x1[8]=0; x2[8]=0; y1[8]=8; y2[8]=1; - x1[9]=13; x2[9]=8; y1[9]=8; y2[9]=8; - x1[10]=9; x2[10]=9; y1[10]=6; y2[10]=2; - x1[11]=7; x2[11]=7; y1[11]=13; y2[11]=2; - x1[12]=14; x2[12]=2; y1[12]=8; y2[12]=8; - break; - case 192: - x1[0]=14; x2[0]=1; y1[0]=12; y2[0]=12; - x1[1]=6; x2[1]=0; y1[1]=10; y2[1]=10; - x1[2]=10; x2[2]=0; y1[2]=5; y2[2]=5; - x1[3]=14; x2[3]=12; y1[3]=14; y2[3]=14; - x1[4]=13; x2[4]=0; y1[4]=0; y2[4]=0; - x1[5]=3; x2[5]=3; y1[5]=11; y2[5]=4; - x1[6]=12; x2[6]=4; y1[6]=11; y2[6]=11; - x1[7]=5; x2[7]=5; y1[7]=13; y2[7]=2; - x1[8]=14; x2[8]=3; y1[8]=5; y2[8]=5; - x1[9]=12; x2[9]=12; y1[9]=10; y2[9]=9; - x1[10]=5; x2[10]=2; y1[10]=10; y2[10]=10; - x1[11]=1; x2[11]=1; y1[11]=6; y2[11]=5; - x1[12]=8; x2[12]=8; y1[12]=7; y2[12]=4; - break; - case 193: - x1[0]=6; x2[0]=3; y1[0]=4; y2[0]=4; - x1[1]=6; x2[1]=1; y1[1]=9; y2[1]=9; - x1[2]=2; x2[2]=2; y1[2]=8; y2[2]=0; - x1[3]=6; x2[3]=5; y1[3]=8; y2[3]=8; - x1[4]=2; x2[4]=2; y1[4]=6; y2[4]=3; - x1[5]=1; x2[5]=0; y1[5]=8; y2[5]=8; - x1[6]=12; x2[6]=0; y1[6]=11; y2[6]=11; - x1[7]=12; x2[7]=12; y1[7]=8; y2[7]=3; - x1[8]=10; x2[8]=10; y1[8]=12; y2[8]=10; - x1[9]=13; x2[9]=9; y1[9]=11; y2[9]=11; - x1[10]=4; x2[10]=3; y1[10]=7; y2[10]=7; - x1[11]=14; x2[11]=14; y1[11]=12; y2[11]=0; - x1[12]=7; x2[12]=7; y1[12]=12; y2[12]=6; - break; - case 194: - x1[0]=12; x2[0]=12; y1[0]=12; y2[0]=0; - x1[1]=12; x2[1]=12; y1[1]=14; y2[1]=6; - x1[2]=4; x2[2]=4; y1[2]=13; y2[2]=1; - x1[3]=4; x2[3]=4; y1[3]=11; y2[3]=2; - x1[4]=11; x2[4]=11; y1[4]=1; y2[4]=0; - x1[5]=6; x2[5]=6; y1[5]=7; y2[5]=1; - x1[6]=12; x2[6]=6; y1[6]=0; y2[6]=0; - x1[7]=5; x2[7]=5; y1[7]=7; y2[7]=6; - x1[8]=2; x2[8]=1; y1[8]=12; y2[8]=12; - x1[9]=7; x2[9]=5; y1[9]=3; y2[9]=3; - x1[10]=4; x2[10]=0; y1[10]=8; y2[10]=8; - x1[11]=10; x2[11]=3; y1[11]=13; y2[11]=13; - x1[12]=2; x2[12]=2; y1[12]=6; y2[12]=2; - break; - case 195: - x1[0]=14; x2[0]=10; y1[0]=6; y2[0]=6; - x1[1]=7; x2[1]=6; y1[1]=5; y2[1]=5; - x1[2]=4; x2[2]=3; y1[2]=3; y2[2]=3; - x1[3]=14; x2[3]=8; y1[3]=4; y2[3]=4; - x1[4]=14; x2[4]=1; y1[4]=4; y2[4]=4; - x1[5]=11; x2[5]=10; y1[5]=2; y2[5]=2; - x1[6]=7; x2[6]=7; y1[6]=13; y2[6]=8; - x1[7]=6; x2[7]=6; y1[7]=12; y2[7]=9; - x1[8]=13; x2[8]=0; y1[8]=5; y2[8]=5; - x1[9]=12; x2[9]=12; y1[9]=12; y2[9]=11; - x1[10]=12; x2[10]=12; y1[10]=12; y2[10]=7; - x1[11]=7; x2[11]=1; y1[11]=3; y2[11]=3; - x1[12]=9; x2[12]=3; y1[12]=9; y2[12]=9; - break; - case 196: - x1[0]=12; x2[0]=6; y1[0]=13; y2[0]=13; - x1[1]=13; x2[1]=8; y1[1]=4; y2[1]=4; - x1[2]=3; x2[2]=0; y1[2]=7; y2[2]=7; - x1[3]=3; x2[3]=3; y1[3]=3; y2[3]=2; - x1[4]=7; x2[4]=5; y1[4]=6; y2[4]=6; - x1[5]=11; x2[5]=6; y1[5]=12; y2[5]=12; - x1[6]=12; x2[6]=1; y1[6]=13; y2[6]=13; - x1[7]=13; x2[7]=10; y1[7]=4; y2[7]=4; - x1[8]=9; x2[8]=1; y1[8]=5; y2[8]=5; - x1[9]=12; x2[9]=3; y1[9]=1; y2[9]=1; - x1[10]=0; x2[10]=0; y1[10]=3; y2[10]=0; - x1[11]=11; x2[11]=11; y1[11]=5; y2[11]=2; - x1[12]=12; x2[12]=12; y1[12]=13; y2[12]=4; - break; - case 197: - x1[0]=2; x2[0]=0; y1[0]=6; y2[0]=6; - x1[1]=14; x2[1]=14; y1[1]=11; y2[1]=2; - x1[2]=6; x2[2]=6; y1[2]=9; y2[2]=5; - x1[3]=3; x2[3]=2; y1[3]=14; y2[3]=14; - x1[4]=12; x2[4]=12; y1[4]=14; y2[4]=4; - x1[5]=4; x2[5]=0; y1[5]=5; y2[5]=5; - x1[6]=13; x2[6]=13; y1[6]=12; y2[6]=6; - x1[7]=12; x2[7]=4; y1[7]=4; y2[7]=4; - x1[8]=0; x2[8]=0; y1[8]=10; y2[8]=5; - x1[9]=9; x2[9]=6; y1[9]=10; y2[9]=10; - x1[10]=12; x2[10]=12; y1[10]=13; y2[10]=7; - x1[11]=7; x2[11]=7; y1[11]=3; y2[11]=0; - x1[12]=6; x2[12]=0; y1[12]=1; y2[12]=1; - break; - case 198: - x1[0]=10; x2[0]=0; y1[0]=12; y2[0]=12; - x1[1]=9; x2[1]=5; y1[1]=13; y2[1]=13; - x1[2]=12; x2[2]=2; y1[2]=3; y2[2]=3; - x1[3]=12; x2[3]=4; y1[3]=7; y2[3]=7; - x1[4]=6; x2[4]=4; y1[4]=9; y2[4]=9; - x1[5]=14; x2[5]=5; y1[5]=2; y2[5]=2; - x1[6]=14; x2[6]=3; y1[6]=11; y2[6]=11; - x1[7]=10; x2[7]=10; y1[7]=12; y2[7]=7; - x1[8]=1; x2[8]=1; y1[8]=6; y2[8]=2; - x1[9]=0; x2[9]=0; y1[9]=12; y2[9]=1; - x1[10]=6; x2[10]=6; y1[10]=11; y2[10]=1; - x1[11]=1; x2[11]=1; y1[11]=13; y2[11]=6; - x1[12]=11; x2[12]=7; y1[12]=3; y2[12]=3; - break; - case 199: - x1[0]=0; x2[0]=0; y1[0]=10; y2[0]=6; - x1[1]=7; x2[1]=3; y1[1]=5; y2[1]=5; - x1[2]=3; x2[2]=0; y1[2]=6; y2[2]=6; - x1[3]=3; x2[3]=2; y1[3]=0; y2[3]=0; - x1[4]=0; x2[4]=0; y1[4]=14; y2[4]=3; - x1[5]=12; x2[5]=2; y1[5]=11; y2[5]=11; - x1[6]=10; x2[6]=6; y1[6]=6; y2[6]=6; - x1[7]=14; x2[7]=13; y1[7]=4; y2[7]=4; - x1[8]=11; x2[8]=1; y1[8]=1; y2[8]=1; - x1[9]=7; x2[9]=7; y1[9]=6; y2[9]=0; - x1[10]=13; x2[10]=11; y1[10]=5; y2[10]=5; - x1[11]=6; x2[11]=6; y1[11]=8; y2[11]=2; - x1[12]=12; x2[12]=7; y1[12]=6; y2[12]=6; - break; - case 200: - x1[0]=10; x2[0]=10; y1[0]=2; y2[0]=1; - x1[1]=7; x2[1]=2; y1[1]=10; y2[1]=10; - x1[2]=13; x2[2]=7; y1[2]=10; y2[2]=10; - x1[3]=1; x2[3]=1; y1[3]=3; y2[3]=2; - x1[4]=10; x2[4]=8; y1[4]=11; y2[4]=11; - x1[5]=13; x2[5]=6; y1[5]=10; y2[5]=10; - x1[6]=12; x2[6]=12; y1[6]=12; y2[6]=2; - x1[7]=13; x2[7]=13; y1[7]=8; y2[7]=5; - x1[8]=13; x2[8]=13; y1[8]=11; y2[8]=2; - x1[9]=14; x2[9]=11; y1[9]=1; y2[9]=1; - x1[10]=12; x2[10]=5; y1[10]=9; y2[10]=9; - x1[11]=14; x2[11]=8; y1[11]=10; y2[11]=10; - x1[12]=2; x2[12]=2; y1[12]=12; y2[12]=6; - break; - case 201: - x1[0]=13; x2[0]=7; y1[0]=12; y2[0]=12; - x1[1]=4; x2[1]=2; y1[1]=6; y2[1]=6; - x1[2]=2; x2[2]=2; y1[2]=9; y2[2]=2; - x1[3]=11; x2[3]=1; y1[3]=9; y2[3]=9; - x1[4]=12; x2[4]=12; y1[4]=13; y2[4]=5; - x1[5]=10; x2[5]=10; y1[5]=4; y2[5]=3; - x1[6]=11; x2[6]=11; y1[6]=8; y2[6]=1; - x1[7]=7; x2[7]=6; y1[7]=3; y2[7]=3; - x1[8]=4; x2[8]=1; y1[8]=13; y2[8]=13; - x1[9]=7; x2[9]=3; y1[9]=13; y2[9]=13; - x1[10]=3; x2[10]=3; y1[10]=5; y2[10]=1; - x1[11]=14; x2[11]=1; y1[11]=9; y2[11]=9; - x1[12]=7; x2[12]=2; y1[12]=13; y2[12]=13; - break; - case 202: - x1[0]=1; x2[0]=1; y1[0]=8; y2[0]=0; - x1[1]=12; x2[1]=7; y1[1]=10; y2[1]=10; - x1[2]=0; x2[2]=0; y1[2]=2; y2[2]=1; - x1[3]=2; x2[3]=2; y1[3]=14; y2[3]=12; - x1[4]=10; x2[4]=10; y1[4]=13; y2[4]=11; - x1[5]=2; x2[5]=2; y1[5]=10; y2[5]=6; - x1[6]=3; x2[6]=3; y1[6]=4; y2[6]=0; - x1[7]=13; x2[7]=7; y1[7]=3; y2[7]=3; - x1[8]=13; x2[8]=8; y1[8]=7; y2[8]=7; - x1[9]=14; x2[9]=14; y1[9]=7; y2[9]=6; - x1[10]=14; x2[10]=5; y1[10]=5; y2[10]=5; - x1[11]=6; x2[11]=1; y1[11]=13; y2[11]=13; - x1[12]=12; x2[12]=7; y1[12]=2; y2[12]=2; - break; - case 203: - x1[0]=11; x2[0]=7; y1[0]=14; y2[0]=14; - x1[1]=6; x2[1]=6; y1[1]=9; y2[1]=6; - x1[2]=12; x2[2]=9; y1[2]=9; y2[2]=9; - x1[3]=14; x2[3]=8; y1[3]=7; y2[3]=7; - x1[4]=13; x2[4]=4; y1[4]=5; y2[4]=5; - x1[5]=9; x2[5]=9; y1[5]=6; y2[5]=4; - x1[6]=10; x2[6]=7; y1[6]=5; y2[6]=5; - x1[7]=7; x2[7]=7; y1[7]=7; y2[7]=4; - x1[8]=12; x2[8]=7; y1[8]=11; y2[8]=11; - x1[9]=13; x2[9]=3; y1[9]=14; y2[9]=14; - x1[10]=12; x2[10]=4; y1[10]=2; y2[10]=2; - x1[11]=13; x2[11]=13; y1[11]=7; y2[11]=3; - x1[12]=11; x2[12]=11; y1[12]=8; y2[12]=6; - break; - case 204: - x1[0]=12; x2[0]=4; y1[0]=9; y2[0]=9; - x1[1]=11; x2[1]=11; y1[1]=4; y2[1]=1; - x1[2]=14; x2[2]=7; y1[2]=2; y2[2]=2; - x1[3]=14; x2[3]=0; y1[3]=14; y2[3]=14; - x1[4]=8; x2[4]=8; y1[4]=3; y2[4]=0; - x1[5]=6; x2[5]=6; y1[5]=12; y2[5]=0; - x1[6]=6; x2[6]=6; y1[6]=12; y2[6]=11; - x1[7]=13; x2[7]=9; y1[7]=3; y2[7]=3; - x1[8]=8; x2[8]=8; y1[8]=5; y2[8]=3; - x1[9]=1; x2[9]=1; y1[9]=7; y2[9]=2; - x1[10]=3; x2[10]=3; y1[10]=13; y2[10]=0; - x1[11]=11; x2[11]=10; y1[11]=10; y2[11]=10; - x1[12]=9; x2[12]=0; y1[12]=10; y2[12]=10; - break; - case 205: - x1[0]=14; x2[0]=7; y1[0]=6; y2[0]=6; - x1[1]=3; x2[1]=0; y1[1]=0; y2[1]=0; - x1[2]=14; x2[2]=6; y1[2]=5; y2[2]=5; - x1[3]=11; x2[3]=11; y1[3]=10; y2[3]=7; - x1[4]=12; x2[4]=3; y1[4]=7; y2[4]=7; - x1[5]=14; x2[5]=8; y1[5]=14; y2[5]=14; - x1[6]=6; x2[6]=6; y1[6]=12; y2[6]=8; - x1[7]=12; x2[7]=12; y1[7]=7; y2[7]=6; - x1[8]=11; x2[8]=3; y1[8]=9; y2[8]=9; - x1[9]=13; x2[9]=0; y1[9]=12; y2[9]=12; - x1[10]=3; x2[10]=3; y1[10]=6; y2[10]=0; - x1[11]=13; x2[11]=1; y1[11]=8; y2[11]=8; - x1[12]=7; x2[12]=5; y1[12]=0; y2[12]=0; - break; - case 206: - x1[0]=1; x2[0]=1; y1[0]=12; y2[0]=1; - x1[1]=1; x2[1]=0; y1[1]=7; y2[1]=7; - x1[2]=11; x2[2]=8; y1[2]=13; y2[2]=13; - x1[3]=2; x2[3]=1; y1[3]=5; y2[3]=5; - x1[4]=14; x2[4]=14; y1[4]=12; y2[4]=10; - x1[5]=14; x2[5]=3; y1[5]=6; y2[5]=6; - x1[6]=7; x2[6]=7; y1[6]=11; y2[6]=1; - x1[7]=9; x2[7]=5; y1[7]=14; y2[7]=14; - x1[8]=14; x2[8]=14; y1[8]=4; y2[8]=1; - x1[9]=11; x2[9]=11; y1[9]=7; y2[9]=5; - x1[10]=3; x2[10]=3; y1[10]=6; y2[10]=1; - x1[11]=0; x2[11]=0; y1[11]=9; y2[11]=6; - x1[12]=9; x2[12]=9; y1[12]=8; y2[12]=2; - break; - case 207: - x1[0]=8; x2[0]=6; y1[0]=5; y2[0]=5; - x1[1]=13; x2[1]=13; y1[1]=13; y2[1]=5; - x1[2]=1; x2[2]=1; y1[2]=12; y2[2]=7; - x1[3]=13; x2[3]=3; y1[3]=2; y2[3]=2; - x1[4]=3; x2[4]=3; y1[4]=14; y2[4]=11; - x1[5]=14; x2[5]=12; y1[5]=0; y2[5]=0; - x1[6]=2; x2[6]=2; y1[6]=3; y2[6]=2; - x1[7]=10; x2[7]=2; y1[7]=11; y2[7]=11; - x1[8]=0; x2[8]=0; y1[8]=13; y2[8]=6; - x1[9]=9; x2[9]=9; y1[9]=14; y2[9]=7; - x1[10]=2; x2[10]=2; y1[10]=12; y2[10]=10; - x1[11]=3; x2[11]=3; y1[11]=7; y2[11]=2; - x1[12]=9; x2[12]=7; y1[12]=6; y2[12]=6; - break; - case 208: - x1[0]=14; x2[0]=4; y1[0]=0; y2[0]=0; - x1[1]=14; x2[1]=12; y1[1]=3; y2[1]=3; - x1[2]=1; x2[2]=1; y1[2]=12; y2[2]=5; - x1[3]=0; x2[3]=0; y1[3]=5; y2[3]=0; - x1[4]=13; x2[4]=13; y1[4]=2; y2[4]=1; - x1[5]=4; x2[5]=4; y1[5]=10; y2[5]=2; - x1[6]=14; x2[6]=14; y1[6]=4; y2[6]=0; - x1[7]=13; x2[7]=13; y1[7]=12; y2[7]=4; - x1[8]=8; x2[8]=8; y1[8]=11; y2[8]=3; - x1[9]=4; x2[9]=0; y1[9]=1; y2[9]=1; - x1[10]=10; x2[10]=10; y1[10]=6; y2[10]=4; - x1[11]=6; x2[11]=4; y1[11]=6; y2[11]=6; - x1[12]=5; x2[12]=3; y1[12]=1; y2[12]=1; - break; - case 209: - x1[0]=11; x2[0]=4; y1[0]=11; y2[0]=11; - x1[1]=13; x2[1]=7; y1[1]=9; y2[1]=9; - x1[2]=3; x2[2]=3; y1[2]=10; y2[2]=5; - x1[3]=3; x2[3]=3; y1[3]=9; y2[3]=8; - x1[4]=0; x2[4]=0; y1[4]=9; y2[4]=5; - x1[5]=13; x2[5]=13; y1[5]=4; y2[5]=0; - x1[6]=12; x2[6]=8; y1[6]=8; y2[6]=8; - x1[7]=12; x2[7]=12; y1[7]=11; y2[7]=9; - x1[8]=1; x2[8]=1; y1[8]=14; y2[8]=13; - x1[9]=9; x2[9]=8; y1[9]=1; y2[9]=1; - x1[10]=10; x2[10]=9; y1[10]=7; y2[10]=7; - x1[11]=5; x2[11]=5; y1[11]=4; y2[11]=0; - x1[12]=5; x2[12]=4; y1[12]=5; y2[12]=5; - break; - case 210: - x1[0]=11; x2[0]=1; y1[0]=2; y2[0]=2; - x1[1]=7; x2[1]=7; y1[1]=2; y2[1]=0; - x1[2]=3; x2[2]=3; y1[2]=10; y2[2]=6; - x1[3]=7; x2[3]=7; y1[3]=12; y2[3]=3; - x1[4]=0; x2[4]=0; y1[4]=14; y2[4]=7; - x1[5]=13; x2[5]=13; y1[5]=14; y2[5]=2; - x1[6]=7; x2[6]=7; y1[6]=5; y2[6]=2; - x1[7]=13; x2[7]=3; y1[7]=11; y2[7]=11; - x1[8]=7; x2[8]=7; y1[8]=14; y2[8]=12; - x1[9]=12; x2[9]=12; y1[9]=9; y2[9]=7; - x1[10]=10; x2[10]=10; y1[10]=6; y2[10]=1; - x1[11]=4; x2[11]=4; y1[11]=12; y2[11]=4; - x1[12]=9; x2[12]=9; y1[12]=2; y2[12]=1; - break; - case 211: - x1[0]=12; x2[0]=8; y1[0]=12; y2[0]=12; - x1[1]=13; x2[1]=13; y1[1]=12; y2[1]=10; - x1[2]=7; x2[2]=3; y1[2]=8; y2[2]=8; - x1[3]=13; x2[3]=13; y1[3]=6; y2[3]=5; - x1[4]=9; x2[4]=1; y1[4]=1; y2[4]=1; - x1[5]=13; x2[5]=13; y1[5]=12; y2[5]=9; - x1[6]=11; x2[6]=11; y1[6]=9; y2[6]=7; - x1[7]=11; x2[7]=4; y1[7]=5; y2[7]=5; - x1[8]=6; x2[8]=6; y1[8]=14; y2[8]=7; - x1[9]=5; x2[9]=5; y1[9]=13; y2[9]=3; - x1[10]=6; x2[10]=5; y1[10]=5; y2[10]=5; - x1[11]=12; x2[11]=12; y1[11]=8; y2[11]=0; - x1[12]=8; x2[12]=8; y1[12]=8; y2[12]=6; - break; - case 212: - x1[0]=0; x2[0]=0; y1[0]=11; y2[0]=3; - x1[1]=13; x2[1]=2; y1[1]=7; y2[1]=7; - x1[2]=14; x2[2]=4; y1[2]=9; y2[2]=9; - x1[3]=9; x2[3]=2; y1[3]=13; y2[3]=13; - x1[4]=14; x2[4]=14; y1[4]=8; y2[4]=0; - x1[5]=11; x2[5]=6; y1[5]=4; y2[5]=4; - x1[6]=3; x2[6]=3; y1[6]=1; y2[6]=0; - x1[7]=9; x2[7]=9; y1[7]=11; y2[7]=1; - x1[8]=3; x2[8]=3; y1[8]=10; y2[8]=1; - x1[9]=14; x2[9]=1; y1[9]=6; y2[9]=6; - x1[10]=13; x2[10]=13; y1[10]=14; y2[10]=4; - x1[11]=11; x2[11]=10; y1[11]=5; y2[11]=5; - x1[12]=7; x2[12]=7; y1[12]=9; y2[12]=7; - break; - case 213: - x1[0]=2; x2[0]=2; y1[0]=10; y2[0]=8; - x1[1]=6; x2[1]=3; y1[1]=11; y2[1]=11; - x1[2]=2; x2[2]=0; y1[2]=7; y2[2]=7; - x1[3]=6; x2[3]=6; y1[3]=14; y2[3]=5; - x1[4]=14; x2[4]=10; y1[4]=5; y2[4]=5; - x1[5]=12; x2[5]=0; y1[5]=12; y2[5]=12; - x1[6]=6; x2[6]=4; y1[6]=5; y2[6]=5; - x1[7]=12; x2[7]=0; y1[7]=8; y2[7]=8; - x1[8]=10; x2[8]=10; y1[8]=11; y2[8]=7; - x1[9]=10; x2[9]=10; y1[9]=10; y2[9]=9; - x1[10]=10; x2[10]=6; y1[10]=7; y2[10]=7; - x1[11]=9; x2[11]=4; y1[11]=4; y2[11]=4; - x1[12]=5; x2[12]=4; y1[12]=2; y2[12]=2; - break; - case 214: - x1[0]=14; x2[0]=12; y1[0]=5; y2[0]=5; - x1[1]=4; x2[1]=4; y1[1]=11; y2[1]=5; - x1[2]=4; x2[2]=4; y1[2]=9; y2[2]=5; - x1[3]=11; x2[3]=11; y1[3]=7; y2[3]=0; - x1[4]=8; x2[4]=4; y1[4]=3; y2[4]=3; - x1[5]=5; x2[5]=5; y1[5]=14; y2[5]=3; - x1[6]=14; x2[6]=14; y1[6]=7; y2[6]=1; - x1[7]=7; x2[7]=0; y1[7]=12; y2[7]=12; - x1[8]=9; x2[8]=0; y1[8]=7; y2[8]=7; - x1[9]=11; x2[9]=11; y1[9]=8; y2[9]=3; - x1[10]=9; x2[10]=0; y1[10]=6; y2[10]=6; - x1[11]=12; x2[11]=1; y1[11]=14; y2[11]=14; - x1[12]=10; x2[12]=10; y1[12]=12; y2[12]=9; - break; - case 215: - x1[0]=13; x2[0]=13; y1[0]=9; y2[0]=7; - x1[1]=12; x2[1]=2; y1[1]=5; y2[1]=5; - x1[2]=6; x2[2]=3; y1[2]=5; y2[2]=5; - x1[3]=3; x2[3]=3; y1[3]=14; y2[3]=9; - x1[4]=8; x2[4]=8; y1[4]=11; y2[4]=6; - x1[5]=7; x2[5]=0; y1[5]=9; y2[5]=9; - x1[6]=9; x2[6]=9; y1[6]=14; y2[6]=10; - x1[7]=12; x2[7]=12; y1[7]=11; y2[7]=5; - x1[8]=10; x2[8]=10; y1[8]=6; y2[8]=3; - x1[9]=10; x2[9]=10; y1[9]=4; y2[9]=2; - x1[10]=5; x2[10]=1; y1[10]=0; y2[10]=0; - x1[11]=4; x2[11]=4; y1[11]=12; y2[11]=9; - x1[12]=9; x2[12]=9; y1[12]=8; y2[12]=3; - break; - case 216: - x1[0]=14; x2[0]=14; y1[0]=3; y2[0]=0; - x1[1]=10; x2[1]=2; y1[1]=13; y2[1]=13; - x1[2]=4; x2[2]=4; y1[2]=7; y2[2]=1; - x1[3]=8; x2[3]=8; y1[3]=12; y2[3]=6; - x1[4]=3; x2[4]=3; y1[4]=4; y2[4]=2; - x1[5]=8; x2[5]=8; y1[5]=8; y2[5]=5; - x1[6]=6; x2[6]=0; y1[6]=9; y2[6]=9; - x1[7]=14; x2[7]=9; y1[7]=5; y2[7]=5; - x1[8]=5; x2[8]=3; y1[8]=14; y2[8]=14; - x1[9]=6; x2[9]=6; y1[9]=13; y2[9]=9; - x1[10]=1; x2[10]=1; y1[10]=10; y2[10]=2; - x1[11]=4; x2[11]=4; y1[11]=11; y2[11]=8; - x1[12]=10; x2[12]=5; y1[12]=5; y2[12]=5; - break; - case 217: - x1[0]=12; x2[0]=7; y1[0]=5; y2[0]=5; - x1[1]=4; x2[1]=4; y1[1]=10; y2[1]=8; - x1[2]=9; x2[2]=9; y1[2]=13; y2[2]=3; - x1[3]=14; x2[3]=13; y1[3]=13; y2[3]=13; - x1[4]=0; x2[4]=0; y1[4]=3; y2[4]=2; - x1[5]=10; x2[5]=8; y1[5]=12; y2[5]=12; - x1[6]=7; x2[6]=7; y1[6]=14; y2[6]=13; - x1[7]=13; x2[7]=13; y1[7]=14; y2[7]=8; - x1[8]=13; x2[8]=4; y1[8]=7; y2[8]=7; - x1[9]=9; x2[9]=4; y1[9]=5; y2[9]=5; - x1[10]=6; x2[10]=0; y1[10]=5; y2[10]=5; - x1[11]=7; x2[11]=1; y1[11]=9; y2[11]=9; - x1[12]=0; x2[12]=0; y1[12]=13; y2[12]=7; - break; - case 218: - x1[0]=14; x2[0]=14; y1[0]=5; y2[0]=3; - x1[1]=8; x2[1]=8; y1[1]=11; y2[1]=8; - x1[2]=6; x2[2]=3; y1[2]=3; y2[2]=3; - x1[3]=14; x2[3]=13; y1[3]=10; y2[3]=10; - x1[4]=7; x2[4]=1; y1[4]=7; y2[4]=7; - x1[5]=6; x2[5]=2; y1[5]=0; y2[5]=0; - x1[6]=10; x2[6]=4; y1[6]=0; y2[6]=0; - x1[7]=13; x2[7]=3; y1[7]=8; y2[7]=8; - x1[8]=12; x2[8]=11; y1[8]=4; y2[8]=4; - x1[9]=8; x2[9]=0; y1[9]=6; y2[9]=6; - x1[10]=11; x2[10]=0; y1[10]=8; y2[10]=8; - x1[11]=10; x2[11]=10; y1[11]=9; y2[11]=2; - x1[12]=8; x2[12]=6; y1[12]=6; y2[12]=6; - break; - case 219: - x1[0]=2; x2[0]=2; y1[0]=10; y2[0]=2; - x1[1]=1; x2[1]=1; y1[1]=6; y2[1]=0; - x1[2]=5; x2[2]=5; y1[2]=13; y2[2]=1; - x1[3]=12; x2[3]=5; y1[3]=2; y2[3]=2; - x1[4]=7; x2[4]=7; y1[4]=14; y2[4]=7; - x1[5]=13; x2[5]=11; y1[5]=12; y2[5]=12; - x1[6]=8; x2[6]=5; y1[6]=4; y2[6]=4; - x1[7]=1; x2[7]=1; y1[7]=11; y2[7]=9; - x1[8]=7; x2[8]=7; y1[8]=10; y2[8]=0; - x1[9]=7; x2[9]=4; y1[9]=2; y2[9]=2; - x1[10]=4; x2[10]=4; y1[10]=11; y2[10]=7; - x1[11]=12; x2[11]=2; y1[11]=10; y2[11]=10; - x1[12]=14; x2[12]=14; y1[12]=12; y2[12]=5; - break; - case 220: - x1[0]=14; x2[0]=0; y1[0]=9; y2[0]=9; - x1[1]=13; x2[1]=10; y1[1]=12; y2[1]=12; - x1[2]=1; x2[2]=1; y1[2]=1; y2[2]=0; - x1[3]=3; x2[3]=1; y1[3]=11; y2[3]=11; - x1[4]=12; x2[4]=12; y1[4]=14; y2[4]=5; - x1[5]=5; x2[5]=3; y1[5]=0; y2[5]=0; - x1[6]=7; x2[6]=7; y1[6]=5; y2[6]=3; - x1[7]=8; x2[7]=8; y1[7]=8; y2[7]=2; - x1[8]=6; x2[8]=4; y1[8]=10; y2[8]=10; - x1[9]=11; x2[9]=11; y1[9]=13; y2[9]=3; - x1[10]=12; x2[10]=11; y1[10]=11; y2[10]=11; - x1[11]=10; x2[11]=2; y1[11]=8; y2[11]=8; - x1[12]=11; x2[12]=11; y1[12]=12; y2[12]=2; - break; - case 221: - x1[0]=10; x2[0]=3; y1[0]=3; y2[0]=3; - x1[1]=3; x2[1]=3; y1[1]=14; y2[1]=13; - x1[2]=7; x2[2]=6; y1[2]=1; y2[2]=1; - x1[3]=10; x2[3]=10; y1[3]=14; y2[3]=1; - x1[4]=10; x2[4]=8; y1[4]=10; y2[4]=10; - x1[5]=2; x2[5]=2; y1[5]=9; y2[5]=8; - x1[6]=1; x2[6]=0; y1[6]=3; y2[6]=3; - x1[7]=4; x2[7]=0; y1[7]=10; y2[7]=10; - x1[8]=12; x2[8]=4; y1[8]=10; y2[8]=10; - x1[9]=10; x2[9]=10; y1[9]=11; y2[9]=8; - x1[10]=9; x2[10]=2; y1[10]=14; y2[10]=14; - x1[11]=14; x2[11]=2; y1[11]=9; y2[11]=9; - x1[12]=11; x2[12]=6; y1[12]=0; y2[12]=0; - break; - case 222: - x1[0]=12; x2[0]=12; y1[0]=9; y2[0]=0; - x1[1]=4; x2[1]=4; y1[1]=6; y2[1]=2; - x1[2]=11; x2[2]=0; y1[2]=4; y2[2]=4; - x1[3]=11; x2[3]=11; y1[3]=12; y2[3]=0; - x1[4]=14; x2[4]=3; y1[4]=8; y2[4]=8; - x1[5]=5; x2[5]=4; y1[5]=3; y2[5]=3; - x1[6]=14; x2[6]=14; y1[6]=14; y2[6]=1; - x1[7]=10; x2[7]=10; y1[7]=6; y2[7]=0; - x1[8]=9; x2[8]=9; y1[8]=11; y2[8]=5; - x1[9]=13; x2[9]=1; y1[9]=3; y2[9]=3; - x1[10]=4; x2[10]=4; y1[10]=11; y2[10]=6; - x1[11]=12; x2[11]=4; y1[11]=6; y2[11]=6; - x1[12]=13; x2[12]=13; y1[12]=14; y2[12]=5; - break; - case 223: - x1[0]=10; x2[0]=5; y1[0]=6; y2[0]=6; - x1[1]=3; x2[1]=3; y1[1]=8; y2[1]=7; - x1[2]=13; x2[2]=13; y1[2]=10; y2[2]=7; - x1[3]=9; x2[3]=9; y1[3]=5; y2[3]=3; - x1[4]=8; x2[4]=2; y1[4]=2; y2[4]=2; - x1[5]=8; x2[5]=2; y1[5]=7; y2[5]=7; - x1[6]=10; x2[6]=10; y1[6]=10; y2[6]=1; - x1[7]=11; x2[7]=2; y1[7]=0; y2[7]=0; - x1[8]=1; x2[8]=1; y1[8]=14; y2[8]=3; - x1[9]=7; x2[9]=7; y1[9]=14; y2[9]=8; - x1[10]=3; x2[10]=3; y1[10]=14; y2[10]=12; - x1[11]=13; x2[11]=13; y1[11]=7; y2[11]=1; - x1[12]=11; x2[12]=10; y1[12]=3; y2[12]=3; - break; - case 224: - x1[0]=8; x2[0]=0; y1[0]=9; y2[0]=9; - x1[1]=3; x2[1]=3; y1[1]=8; y2[1]=0; - x1[2]=13; x2[2]=6; y1[2]=11; y2[2]=11; - x1[3]=10; x2[3]=2; y1[3]=9; y2[3]=9; - x1[4]=10; x2[4]=7; y1[4]=14; y2[4]=14; - x1[5]=14; x2[5]=5; y1[5]=6; y2[5]=6; - x1[6]=13; x2[6]=8; y1[6]=14; y2[6]=14; - x1[7]=0; x2[7]=0; y1[7]=8; y2[7]=0; - x1[8]=13; x2[8]=9; y1[8]=13; y2[8]=13; - x1[9]=14; x2[9]=14; y1[9]=14; y2[9]=11; - x1[10]=12; x2[10]=4; y1[10]=14; y2[10]=14; - x1[11]=14; x2[11]=3; y1[11]=10; y2[11]=10; - x1[12]=14; x2[12]=12; y1[12]=10; y2[12]=10; - break; - case 225: - x1[0]=5; x2[0]=5; y1[0]=10; y2[0]=2; - x1[1]=9; x2[1]=9; y1[1]=4; y2[1]=0; - x1[2]=12; x2[2]=10; y1[2]=4; y2[2]=4; - x1[3]=9; x2[3]=9; y1[3]=9; y2[3]=6; - x1[4]=13; x2[4]=0; y1[4]=3; y2[4]=3; - x1[5]=8; x2[5]=0; y1[5]=10; y2[5]=10; - x1[6]=5; x2[6]=4; y1[6]=4; y2[6]=4; - x1[7]=14; x2[7]=8; y1[7]=0; y2[7]=0; - x1[8]=3; x2[8]=1; y1[8]=7; y2[8]=7; - x1[9]=9; x2[9]=9; y1[9]=12; y2[9]=8; - x1[10]=11; x2[10]=8; y1[10]=7; y2[10]=7; - x1[11]=9; x2[11]=8; y1[11]=0; y2[11]=0; - x1[12]=6; x2[12]=5; y1[12]=10; y2[12]=10; - break; - case 226: - x1[0]=14; x2[0]=4; y1[0]=10; y2[0]=10; - x1[1]=7; x2[1]=7; y1[1]=14; y2[1]=6; - x1[2]=14; x2[2]=9; y1[2]=8; y2[2]=8; - x1[3]=1; x2[3]=1; y1[3]=13; y2[3]=11; - x1[4]=8; x2[4]=8; y1[4]=4; y2[4]=2; - x1[5]=11; x2[5]=8; y1[5]=9; y2[5]=9; - x1[6]=4; x2[6]=2; y1[6]=3; y2[6]=3; - x1[7]=14; x2[7]=6; y1[7]=1; y2[7]=1; - x1[8]=7; x2[8]=7; y1[8]=7; y2[8]=1; - x1[9]=10; x2[9]=10; y1[9]=10; y2[9]=4; - x1[10]=5; x2[10]=4; y1[10]=13; y2[10]=13; - x1[11]=7; x2[11]=7; y1[11]=13; y2[11]=4; - x1[12]=13; x2[12]=9; y1[12]=2; y2[12]=2; - break; - case 227: - x1[0]=13; x2[0]=9; y1[0]=4; y2[0]=4; - x1[1]=4; x2[1]=3; y1[1]=6; y2[1]=6; - x1[2]=12; x2[2]=12; y1[2]=10; y2[2]=1; - x1[3]=9; x2[3]=3; y1[3]=13; y2[3]=13; - x1[4]=10; x2[4]=10; y1[4]=6; y2[4]=2; - x1[5]=9; x2[5]=9; y1[5]=5; y2[5]=0; - x1[6]=14; x2[6]=0; y1[6]=5; y2[6]=5; - x1[7]=14; x2[7]=11; y1[7]=7; y2[7]=7; - x1[8]=11; x2[8]=11; y1[8]=10; y2[8]=3; - x1[9]=14; x2[9]=14; y1[9]=5; y2[9]=1; - x1[10]=7; x2[10]=7; y1[10]=9; y2[10]=1; - x1[11]=6; x2[11]=4; y1[11]=3; y2[11]=3; - x1[12]=10; x2[12]=10; y1[12]=14; y2[12]=11; - break; - case 228: - x1[0]=14; x2[0]=1; y1[0]=3; y2[0]=3; - x1[1]=11; x2[1]=11; y1[1]=11; y2[1]=10; - x1[2]=8; x2[2]=1; y1[2]=0; y2[2]=0; - x1[3]=11; x2[3]=7; y1[3]=12; y2[3]=12; - x1[4]=1; x2[4]=1; y1[4]=13; y2[4]=4; - x1[5]=5; x2[5]=5; y1[5]=8; y2[5]=0; - x1[6]=13; x2[6]=5; y1[6]=3; y2[6]=3; - x1[7]=14; x2[7]=11; y1[7]=3; y2[7]=3; - x1[8]=4; x2[8]=0; y1[8]=7; y2[8]=7; - x1[9]=14; x2[9]=14; y1[9]=12; y2[9]=11; - x1[10]=10; x2[10]=10; y1[10]=8; y2[10]=0; - x1[11]=13; x2[11]=13; y1[11]=11; y2[11]=10; - x1[12]=10; x2[12]=10; y1[12]=13; y2[12]=12; - break; - case 229: - x1[0]=11; x2[0]=9; y1[0]=11; y2[0]=11; - x1[1]=9; x2[1]=3; y1[1]=0; y2[1]=0; - x1[2]=11; x2[2]=5; y1[2]=12; y2[2]=12; - x1[3]=8; x2[3]=6; y1[3]=0; y2[3]=0; - x1[4]=3; x2[4]=3; y1[4]=6; y2[4]=3; - x1[5]=11; x2[5]=11; y1[5]=9; y2[5]=4; - x1[6]=8; x2[6]=6; y1[6]=13; y2[6]=13; - x1[7]=14; x2[7]=2; y1[7]=10; y2[7]=10; - x1[8]=6; x2[8]=3; y1[8]=6; y2[8]=6; - x1[9]=14; x2[9]=11; y1[9]=14; y2[9]=14; - x1[10]=2; x2[10]=2; y1[10]=11; y2[10]=8; - x1[11]=1; x2[11]=1; y1[11]=14; y2[11]=10; - x1[12]=9; x2[12]=9; y1[12]=12; y2[12]=2; - break; - case 230: - x1[0]=12; x2[0]=0; y1[0]=6; y2[0]=6; - x1[1]=11; x2[1]=10; y1[1]=0; y2[1]=0; - x1[2]=10; x2[2]=10; y1[2]=8; y2[2]=6; - x1[3]=2; x2[3]=1; y1[3]=2; y2[3]=2; - x1[4]=3; x2[4]=3; y1[4]=14; y2[4]=7; - x1[5]=14; x2[5]=2; y1[5]=0; y2[5]=0; - x1[6]=11; x2[6]=3; y1[6]=12; y2[6]=12; - x1[7]=5; x2[7]=5; y1[7]=14; y2[7]=5; - x1[8]=10; x2[8]=10; y1[8]=9; y2[8]=0; - x1[9]=11; x2[9]=11; y1[9]=13; y2[9]=1; - x1[10]=1; x2[10]=1; y1[10]=12; y2[10]=0; - x1[11]=5; x2[11]=4; y1[11]=14; y2[11]=14; - x1[12]=2; x2[12]=2; y1[12]=14; y2[12]=11; - break; - case 231: - x1[0]=9; x2[0]=9; y1[0]=14; y2[0]=6; - x1[1]=6; x2[1]=6; y1[1]=10; y2[1]=8; - x1[2]=6; x2[2]=2; y1[2]=8; y2[2]=8; - x1[3]=7; x2[3]=5; y1[3]=13; y2[3]=13; - x1[4]=13; x2[4]=3; y1[4]=6; y2[4]=6; - x1[5]=7; x2[5]=7; y1[5]=2; y2[5]=1; - x1[6]=8; x2[6]=8; y1[6]=14; y2[6]=11; - x1[7]=11; x2[7]=11; y1[7]=11; y2[7]=0; - x1[8]=8; x2[8]=7; y1[8]=2; y2[8]=2; - x1[9]=9; x2[9]=1; y1[9]=4; y2[9]=4; - x1[10]=2; x2[10]=2; y1[10]=13; y2[10]=8; - x1[11]=10; x2[11]=0; y1[11]=0; y2[11]=0; - x1[12]=3; x2[12]=3; y1[12]=10; y2[12]=3; - break; - case 232: - x1[0]=13; x2[0]=4; y1[0]=12; y2[0]=12; - x1[1]=4; x2[1]=0; y1[1]=0; y2[1]=0; - x1[2]=0; x2[2]=0; y1[2]=12; y2[2]=6; - x1[3]=7; x2[3]=5; y1[3]=12; y2[3]=12; - x1[4]=4; x2[4]=4; y1[4]=10; y2[4]=6; - x1[5]=14; x2[5]=14; y1[5]=4; y2[5]=2; - x1[6]=10; x2[6]=2; y1[6]=10; y2[6]=10; - x1[7]=6; x2[7]=6; y1[7]=7; y2[7]=3; - x1[8]=13; x2[8]=13; y1[8]=9; y2[8]=6; - x1[9]=5; x2[9]=3; y1[9]=2; y2[9]=2; - x1[10]=13; x2[10]=13; y1[10]=10; y2[10]=0; - x1[11]=10; x2[11]=6; y1[11]=8; y2[11]=8; - x1[12]=10; x2[12]=3; y1[12]=11; y2[12]=11; - break; - case 233: - x1[0]=14; x2[0]=5; y1[0]=0; y2[0]=0; - x1[1]=2; x2[1]=2; y1[1]=14; y2[1]=0; - x1[2]=6; x2[2]=6; y1[2]=12; y2[2]=2; - x1[3]=11; x2[3]=9; y1[3]=0; y2[3]=0; - x1[4]=3; x2[4]=0; y1[4]=10; y2[4]=10; - x1[5]=10; x2[5]=10; y1[5]=10; y2[5]=0; - x1[6]=12; x2[6]=12; y1[6]=7; y2[6]=3; - x1[7]=9; x2[7]=9; y1[7]=13; y2[7]=4; - x1[8]=5; x2[8]=5; y1[8]=11; y2[8]=2; - x1[9]=0; x2[9]=0; y1[9]=12; y2[9]=3; - x1[10]=9; x2[10]=2; y1[10]=12; y2[10]=12; - x1[11]=1; x2[11]=1; y1[11]=9; y2[11]=0; - x1[12]=1; x2[12]=1; y1[12]=14; y2[12]=9; - break; - case 234: - x1[0]=10; x2[0]=10; y1[0]=14; y2[0]=10; - x1[1]=5; x2[1]=5; y1[1]=14; y2[1]=8; - x1[2]=4; x2[2]=4; y1[2]=11; y2[2]=10; - x1[3]=7; x2[3]=7; y1[3]=4; y2[3]=2; - x1[4]=12; x2[4]=11; y1[4]=13; y2[4]=13; - x1[5]=13; x2[5]=9; y1[5]=7; y2[5]=7; - x1[6]=1; x2[6]=1; y1[6]=10; y2[6]=9; - x1[7]=6; x2[7]=5; y1[7]=7; y2[7]=7; - x1[8]=5; x2[8]=5; y1[8]=8; y2[8]=4; - x1[9]=11; x2[9]=5; y1[9]=4; y2[9]=4; - x1[10]=13; x2[10]=13; y1[10]=2; y2[10]=0; - x1[11]=7; x2[11]=1; y1[11]=4; y2[11]=4; - x1[12]=10; x2[12]=1; y1[12]=5; y2[12]=5; - break; - case 235: - x1[0]=3; x2[0]=3; y1[0]=7; y2[0]=1; - x1[1]=12; x2[1]=8; y1[1]=6; y2[1]=6; - x1[2]=11; x2[2]=8; y1[2]=11; y2[2]=11; - x1[3]=10; x2[3]=9; y1[3]=12; y2[3]=12; - x1[4]=9; x2[4]=9; y1[4]=12; y2[4]=5; - x1[5]=14; x2[5]=6; y1[5]=6; y2[5]=6; - x1[6]=4; x2[6]=4; y1[6]=6; y2[6]=3; - x1[7]=5; x2[7]=5; y1[7]=12; y2[7]=5; - x1[8]=14; x2[8]=14; y1[8]=8; y2[8]=6; - x1[9]=10; x2[9]=7; y1[9]=9; y2[9]=9; - x1[10]=3; x2[10]=3; y1[10]=11; y2[10]=10; - x1[11]=14; x2[11]=9; y1[11]=0; y2[11]=0; - x1[12]=1; x2[12]=1; y1[12]=11; y2[12]=3; - break; - case 236: - x1[0]=13; x2[0]=11; y1[0]=14; y2[0]=14; - x1[1]=3; x2[1]=1; y1[1]=12; y2[1]=12; - x1[2]=14; x2[2]=2; y1[2]=14; y2[2]=14; - x1[3]=3; x2[3]=3; y1[3]=8; y2[3]=6; - x1[4]=12; x2[4]=6; y1[4]=12; y2[4]=12; - x1[5]=14; x2[5]=0; y1[5]=6; y2[5]=6; - x1[6]=14; x2[6]=9; y1[6]=3; y2[6]=3; - x1[7]=2; x2[7]=0; y1[7]=0; y2[7]=0; - x1[8]=8; x2[8]=3; y1[8]=7; y2[8]=7; - x1[9]=3; x2[9]=3; y1[9]=14; y2[9]=3; - x1[10]=11; x2[10]=11; y1[10]=14; y2[10]=1; - x1[11]=7; x2[11]=7; y1[11]=9; y2[11]=8; - x1[12]=7; x2[12]=7; y1[12]=13; y2[12]=12; - break; - case 237: - x1[0]=14; x2[0]=3; y1[0]=14; y2[0]=14; - x1[1]=3; x2[1]=3; y1[1]=9; y2[1]=6; - x1[2]=14; x2[2]=9; y1[2]=7; y2[2]=7; - x1[3]=4; x2[3]=3; y1[3]=2; y2[3]=2; - x1[4]=10; x2[4]=10; y1[4]=11; y2[4]=2; - x1[5]=14; x2[5]=4; y1[5]=13; y2[5]=13; - x1[6]=3; x2[6]=3; y1[6]=14; y2[6]=2; - x1[7]=4; x2[7]=4; y1[7]=8; y2[7]=1; - x1[8]=14; x2[8]=2; y1[8]=6; y2[8]=6; - x1[9]=14; x2[9]=10; y1[9]=13; y2[9]=13; - x1[10]=5; x2[10]=5; y1[10]=6; y2[10]=2; - x1[11]=6; x2[11]=1; y1[11]=11; y2[11]=11; - x1[12]=12; x2[12]=0; y1[12]=14; y2[12]=14; - break; - case 238: - x1[0]=14; x2[0]=4; y1[0]=12; y2[0]=12; - x1[1]=6; x2[1]=6; y1[1]=14; y2[1]=13; - x1[2]=11; x2[2]=3; y1[2]=8; y2[2]=8; - x1[3]=5; x2[3]=5; y1[3]=10; y2[3]=1; - x1[4]=11; x2[4]=1; y1[4]=5; y2[4]=5; - x1[5]=7; x2[5]=4; y1[5]=0; y2[5]=0; - x1[6]=10; x2[6]=9; y1[6]=3; y2[6]=3; - x1[7]=12; x2[7]=12; y1[7]=8; y2[7]=6; - x1[8]=11; x2[8]=1; y1[8]=6; y2[8]=6; - x1[9]=13; x2[9]=5; y1[9]=8; y2[9]=8; - x1[10]=1; x2[10]=1; y1[10]=12; y2[10]=8; - x1[11]=0; x2[11]=0; y1[11]=8; y2[11]=4; - x1[12]=8; x2[12]=8; y1[12]=9; y2[12]=8; - break; - case 239: - x1[0]=8; x2[0]=4; y1[0]=14; y2[0]=14; - x1[1]=5; x2[1]=5; y1[1]=13; y2[1]=10; - x1[2]=9; x2[2]=0; y1[2]=4; y2[2]=4; - x1[3]=10; x2[3]=7; y1[3]=11; y2[3]=11; - x1[4]=11; x2[4]=11; y1[4]=9; y2[4]=6; - x1[5]=14; x2[5]=1; y1[5]=5; y2[5]=5; - x1[6]=12; x2[6]=4; y1[6]=8; y2[6]=8; - x1[7]=0; x2[7]=0; y1[7]=11; y2[7]=4; - x1[8]=1; x2[8]=1; y1[8]=10; y2[8]=4; - x1[9]=12; x2[9]=7; y1[9]=4; y2[9]=4; - x1[10]=10; x2[10]=9; y1[10]=9; y2[10]=9; - x1[11]=12; x2[11]=7; y1[11]=3; y2[11]=3; - x1[12]=2; x2[12]=2; y1[12]=10; y2[12]=7; - break; - case 240: - x1[0]=6; x2[0]=6; y1[0]=13; y2[0]=8; - x1[1]=2; x2[1]=2; y1[1]=6; y2[1]=0; - x1[2]=6; x2[2]=6; y1[2]=12; y2[2]=4; - x1[3]=0; x2[3]=0; y1[3]=11; y2[3]=7; - x1[4]=4; x2[4]=4; y1[4]=6; y2[4]=0; - x1[5]=3; x2[5]=2; y1[5]=11; y2[5]=11; - x1[6]=8; x2[6]=0; y1[6]=11; y2[6]=11; - x1[7]=7; x2[7]=7; y1[7]=14; y2[7]=10; - x1[8]=12; x2[8]=12; y1[8]=5; y2[8]=0; - x1[9]=14; x2[9]=14; y1[9]=14; y2[9]=2; - x1[10]=2; x2[10]=2; y1[10]=13; y2[10]=5; - x1[11]=12; x2[11]=12; y1[11]=14; y2[11]=1; - x1[12]=14; x2[12]=0; y1[12]=13; y2[12]=13; - break; - case 241: - x1[0]=11; x2[0]=6; y1[0]=8; y2[0]=8; - x1[1]=12; x2[1]=12; y1[1]=12; y2[1]=3; - x1[2]=11; x2[2]=6; y1[2]=6; y2[2]=6; - x1[3]=14; x2[3]=7; y1[3]=0; y2[3]=0; - x1[4]=7; x2[4]=7; y1[4]=11; y2[4]=2; - x1[5]=6; x2[5]=6; y1[5]=11; y2[5]=0; - x1[6]=12; x2[6]=1; y1[6]=4; y2[6]=4; - x1[7]=11; x2[7]=1; y1[7]=13; y2[7]=13; - x1[8]=5; x2[8]=3; y1[8]=7; y2[8]=7; - x1[9]=14; x2[9]=14; y1[9]=9; y2[9]=8; - x1[10]=14; x2[10]=12; y1[10]=1; y2[10]=1; - x1[11]=8; x2[11]=2; y1[11]=6; y2[11]=6; - x1[12]=6; x2[12]=6; y1[12]=12; y2[12]=7; - break; - case 242: - x1[0]=7; x2[0]=7; y1[0]=10; y2[0]=5; - x1[1]=13; x2[1]=13; y1[1]=8; y2[1]=2; - x1[2]=9; x2[2]=8; y1[2]=11; y2[2]=11; - x1[3]=8; x2[3]=8; y1[3]=7; y2[3]=5; - x1[4]=2; x2[4]=2; y1[4]=9; y2[4]=0; - x1[5]=1; x2[5]=0; y1[5]=9; y2[5]=9; - x1[6]=11; x2[6]=2; y1[6]=6; y2[6]=6; - x1[7]=3; x2[7]=0; y1[7]=12; y2[7]=12; - x1[8]=13; x2[8]=7; y1[8]=4; y2[8]=4; - x1[9]=13; x2[9]=13; y1[9]=6; y2[9]=3; - x1[10]=11; x2[10]=1; y1[10]=10; y2[10]=10; - x1[11]=13; x2[11]=13; y1[11]=9; y2[11]=0; - x1[12]=3; x2[12]=3; y1[12]=10; y2[12]=0; - break; - } -}}} diff --git a/modules/tracking/src/tld_tracker.cpp b/modules/tracking/src/tld_tracker.cpp new file mode 100644 index 000000000..42e92f5f7 --- /dev/null +++ b/modules/tracking/src/tld_tracker.cpp @@ -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 +#include +#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 model_in):model(model_in), params_(params){} + ~TLDDetector(){} + static void generateScanGrid(int rows, int cols, Size initBox, std::vector& res, bool withScaling = false); + struct LabeledPatch + { + Rect2d rect; + bool isObject, shouldBeIntegrated; + }; + bool detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector& patches); +protected: + friend class MyMouseCallbackDEBUG; + Ptr model; + void computeIntegralImages(const Mat& img, Mat_& intImgP, Mat_& intImgP2){ integral(img, intImgP, intImgP2, CV_64F); } + inline bool patchVariance(Mat_& intImgP, Mat_& intImgP2, double originalVariance, Point pt, Size size); + TrackerTLD::Params params_; +}; + +template +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 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_& patch); + double Sc(const Mat_& patch); + void integrateRelabeled(Mat& img, Mat& imgBlurred, const std::vector& patches); + void integrateAdditional(const std::vector >& eForModel, const std::vector >& 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_& example, bool positive); + void modelEstimationImpl( const std::vector& /*responses*/ ){} + void modelUpdateImpl(){} + Rect2d boundingBox_; + double originalVariance_; + std::vector > positiveExamples, negativeExamples; + std::vector timeStampsPositive, timeStampsNegative; + RNG rng; + std::vector 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 >& examplesForModel, std::vector >& 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 >& examplesForModel, std::vector >& 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; + Ptr trackerProxy; + Ptr detector; +}; + +} + +TrackerTLD::Params::Params(){} + +void TrackerTLD::Params::read(const cv::FileNode& /*fn*/){} + +void TrackerTLD::Params::write(cv::FileStorage& /*fs*/) const {} + +Ptr TrackerTLD::createTracker(const TrackerTLD::Params ¶meters) +{ + return Ptr(new tld::TrackerTLDImpl(parameters)); +} + +namespace tld +{ + +TrackerTLDImpl::TrackerTLDImpl(const TrackerTLD::Params ¶meters) : + params( parameters ) +{ + isInit = false; + trackerProxy = Ptr > + (new TrackerProxyImpl()); +} + +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(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(new TrackerTLDModel(params, image_gray, myBoundingBox, data->getMinSize())); + detector = Ptr(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(model)); + data->frameNum++; + Mat_ standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE); + std::vector detectorResults; + //best overlap around 92% + + std::vector candidates; + std::vector 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::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 > 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 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_ 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_ 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 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_ 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& 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& patches) +{ + TrackerTLDModel* tldModel = ((TrackerTLDModel*)static_cast(model)); + Size initSize = tldModel->getMinSize(); + patches.clear(); + + Mat resized_img, blurred_img; + Mat_ 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_ 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(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 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_& intImgP, Mat_& 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_& 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_& 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& patches) +{ + Mat_ 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 >& eForModel, const std::vector >& 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 >& examplesForModel, std::vector >& examplesForEnsemble) +{ + examplesForModel.clear(); examplesForEnsemble.clear(); + examplesForModel.reserve(100); examplesForEnsemble.reserve(100); + + std::vector 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_ 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(detector_->model)); + Size initSize = tldModel->getMinSize(); + Mat_ 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_ 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(dy * j, dx * i))))); + fprintf(stderr, "ensembleClassifier = %s\n", + (!(tldModel->ensembleClassifierNum(&blurred_img.at(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_& example, bool positive) +{ + std::vector >* proxyV; + int* proxyN; + std::vector* 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 */ diff --git a/modules/tracking/src/tld_tracker.hpp b/modules/tracking/src/tld_tracker.hpp new file mode 100644 index 000000000..39f983244 --- /dev/null +++ b/modules/tracking/src/tld_tracker.hpp @@ -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 +#include + +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_& standardPatch); +std::string type2str(const Mat& mat); +void drawWithRects(const Mat& img, std::vector& blackOnes, Rect2d whiteOne = Rect2d(-1.0, -1.0, -1.0, -1.0)); +void drawWithRects(const Mat& img, std::vector& blackOnes, std::vector& whiteOnes, String fileName = ""); + +//aux functions and variables +template 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_& samples); +/** Specialization of resample() for rectangles without retation for better performance and simplicity.*/ +void resample(const Mat& img, const Rect2d& r2, Mat_& 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_& patch1, const Mat_& patch2); +void getClosestN(std::vector& scanGrid, Rect2d bBox, int n, std::vector& res); +double scaleAndBlur(const Mat& originalImg, int scale, Mat& scaledImg, Mat& blurredImg, Size GaussBlurKernelSize, double scaleStep); +int getMedian(const std::vector& values, int size = -1); + +class TLDEnsembleClassifier +{ +public: + static int makeClassifiers(Size size, int measurePerClassifier, int gridSize, std::vector& classifiers); + void integrate(const Mat_& 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& meas, int beg, int end); + static void stepPrefSuff(std::vector & arr, int pos, int len, int gridSize); + int code(const uchar* data, int rowstep) const; + int codeFast(const uchar* data) const; + std::vector posAndNeg; + std::vector measurements; + std::vector 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(){} +}; + +}} diff --git a/modules/tracking/src/tld_utils.cpp b/modules/tracking/src/tld_utils.cpp new file mode 100644 index 000000000..df9c9b709 --- /dev/null +++ b/modules/tracking/src/tld_utils.cpp @@ -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 +#include +#include +#include +#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& 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& blackOnes, std::vector& 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(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_& 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& scanGrid, Rect2d bBox, int n, std::vector& res) +{ + if( n >= (int)scanGrid.size() ) + { + res.assign(scanGrid.begin(), scanGrid.end()); + return; + } + std::vector 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(i, j); + p2 += img.at(i, j) * img.at(i, j); + } + } + p /= (img.cols * img.rows); + p2 /= (img.cols * img.rows); + return p2 - p * p; +} + +double NCC(const Mat_& patch1, const Mat_& 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& values, int size) +{ + if( size == -1 ) + size = (int)values.size(); + std::vector 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_& samples) +{ + Mat_ 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_ 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_& samples) +{ + Mat_ 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& 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& 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_& 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& classifiers) +{ + + std::vector 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(); +} + +}} diff --git a/modules/tracking/src/trackerTLD.cpp b/modules/tracking/src/trackerTLD.cpp deleted file mode 100644 index e9e8e90e3..000000000 --- a/modules/tracking/src/trackerTLD.cpp +++ /dev/null @@ -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 -#include -#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,Ptrmodel_in):model(model_in),params_(params){} - ~TLDDetector(){} - static void generateScanGrid(int rows,int cols,Size initBox,std::vector& res,bool withScaling=false); - bool detect(const Mat& img,const Mat& imgBlurred,Rect2d& res,std::vector& rect,std::vector& isObject, - std::vector& shouldBeIntegrated); -protected: - friend class MyMouseCallbackDEBUG; - Ptr model; - void computeIntegralImages(const Mat& img,Mat_& intImgP,Mat_& intImgP2){integral(img,intImgP,intImgP2,CV_64F);} - inline bool patchVariance(Mat_& intImgP,Mat_& 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 >& examplesForModel,std::vector >& 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 >& examplesForModel,std::vector >& examplesForEnsemble){ - examplesForModel.clear();examplesForEnsemble.clear();return 0;} -protected: - Mat img_; - Rect2d resultBox_; - const TLDDetector* detector_; - TrackerTLD::Params params_; -}; - -template -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 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* getClassifiers(){return &classifiers;} - double Sr(const Mat_ patch); - double Sc(const Mat_ patch); - void integrateRelabeled(Mat& img,Mat& imgBlurred,const std::vector& box,const std::vector& isPositive, - const std::vector& alsoIntoModel); - void integrateAdditional(const std::vector >& eForModel,const std::vector >& 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_& example,bool positive); - void modelEstimationImpl( const std::vector& /*responses*/ ){} - void modelUpdateImpl(){} - Rect2d boundingBox_; - double originalVariance_; - std::vector > positiveExamples,negativeExamples; - std::vector timeStampsPositive,timeStampsNegative; - RNG rng; - std::vector 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; - Ptr trackerProxy; - Ptr detector; -}; - - -TrackerTLD::Params::Params(){ -} - -void TrackerTLD::Params::read( const cv::FileNode& /*fn*/ ){ -} - -void TrackerTLD::Params::write( cv::FileStorage& /*fs*/ ) const{ -} - -Ptr TrackerTLD::createTracker(const TrackerTLD::Params ¶meters){ - return Ptr(new TrackerTLDImpl(parameters)); -} - -TrackerTLDImpl::TrackerTLDImpl( const TrackerTLD::Params ¶meters) : - params( parameters ){ - isInit = false; - trackerProxy=Ptr >( - new TrackerProxyImpl()); -} - -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(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(new TrackerTLDModel(params,image_gray,myBoundingBox,data->getMinSize())); - detector=Ptr(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(model)); - data->frameNum++; - Mat_ standardPatch(15,15); - std::vector detectorResults; - std::vector isObject,shouldBeIntegrated; - //best overlap around 92% - - Rect2d tmpCandid=boundingBox; - std::vector candidates; - std::vector 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::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 > 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 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_ blurredPatch(minSize); - for(int i=0,howMany=TLDEnsembleClassifier::getMaxOrdinal();i 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 indices; - indices.reserve(NEG_EXAMPLES_IN_INIT_MODEL); - while(negativeExamples.size() 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& 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;hinitBox.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& rect,std::vector& isObject, - std::vector& shouldBeIntegrated){ - TrackerTLDModel* tldModel=((TrackerTLDModel*)static_cast(model)); - Size initSize=tldModel->getMinSize(); - rect.clear(); - isObject.clear(); - shouldBeIntegrated.clear(); - - Mat resized_img,blurred_img; - Mat_ 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_ intImgP,intImgP2; - computeIntegralImages(resized_img,intImgP,intImgP2); - - for(int i=0,imax=cvFloor((0.0+resized_img.cols-initSize.width)/dx);i(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 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 scanGrid; - generateScanGrid(img.rows,img.cols,initSize,scanGrid); - std::vector results; - Mat_ 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::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_& intImgP,Mat_& 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(model)); - std::vector* 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_ 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_ 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& box,const std::vector& isPositive, - const std::vector& alsoIntoModel){ - Mat_ 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 >& eForModel,const std::vector >& 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 >& examplesForModel,std::vector >& examplesForEnsemble){ - examplesForModel.clear();examplesForEnsemble.clear(); - examplesForModel.reserve(100);examplesForEnsemble.reserve(100); - - std::vector 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_ 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(detector_->model)); - Size initSize=tldModel->getMinSize(); - Mat_ 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_ 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(dy*j,dx*i),(int)blurred_img.step[0])))); - fprintf(stderr,"ensembleClassifier=%s\n", - (detector_->ensembleClassifier(&blurred_img.at(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_& example,bool positive){ - std::vector >* proxyV; - unsigned int* proxyN; - std::vector* proxyT; - if(positive){ - proxyV=&positiveExamples; - proxyN=&timeStampPositiveNext; - proxyT=&timeStampsPositive; - }else{ - proxyV=&negativeExamples; - proxyN=&timeStampNegativeNext; - proxyT=&timeStampsNegative; - } - if(proxyV->size()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 */ diff --git a/modules/ximgproc/CMakeLists.txt b/modules/ximgproc/CMakeLists.txt new file mode 100644 index 000000000..6a6c87b07 --- /dev/null +++ b/modules/ximgproc/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/modules/ximgproc/doc/edge_aware_filters.rst b/modules/ximgproc/doc/edge_aware_filters.rst new file mode 100644 index 000000000..9f822f908 --- /dev/null +++ b/modules/ximgproc/doc/edge_aware_filters.rst @@ -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 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 `_. + +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 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 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 `__. + + +.. [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 `__. + + +.. [Kaiming10] Kaiming He et. al., "Guided Image Filtering," ECCV 2010, pp. 1 - 14. + + The paper is available `online `__. + + +.. [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 `__. + + +.. [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 `__. diff --git a/modules/ximgproc/doc/ximgproc.rst b/modules/ximgproc/doc/ximgproc.rst new file mode 100644 index 000000000..8852e3aca --- /dev/null +++ b/modules/ximgproc/doc/ximgproc.rst @@ -0,0 +1,10 @@ +******************************************** +ximgproc. Extended image processing module. +******************************************** + +.. highlight:: cpp + +.. toctree:: + :maxdepth: 2 + + edge_aware_filters diff --git a/modules/ximgproc/include/opencv2/ximgproc.hpp b/modules/ximgproc/include/opencv2/ximgproc.hpp new file mode 100644 index 000000000..8a8d2534e --- /dev/null +++ b/modules/ximgproc/include/opencv2/ximgproc.hpp @@ -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 \ No newline at end of file diff --git a/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp new file mode 100644 index 000000000..2375446bf --- /dev/null +++ b/modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp @@ -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 + +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 DTFilterPtr; + +/*Fabric function for DT filters*/ +CV_EXPORTS +Ptr 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 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 create(); +}; + +//Fabric function for AM filter algorithm +CV_EXPORTS Ptr 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 diff --git a/modules/ximgproc/perf/perf_adaptive_manifold.cpp b/modules/ximgproc/perf/perf_adaptive_manifold.cpp new file mode 100644 index 000000000..a45a4395b --- /dev/null +++ b/modules/ximgproc/perf/perf_adaptive_manifold.cpp @@ -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 AMPerfTestParam; +typedef TestBaseWithParam 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); +} + +} \ No newline at end of file diff --git a/modules/ximgproc/perf/perf_domain_transform.cpp b/modules/ximgproc/perf/perf_domain_transform.cpp new file mode 100644 index 000000000..b680ee289 --- /dev/null +++ b/modules/ximgproc/perf/perf_domain_transform.cpp @@ -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 DTTestParams; + +typedef TestBaseWithParam 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); +} + +} \ No newline at end of file diff --git a/modules/ximgproc/perf/perf_guided_filter.cpp b/modules/ximgproc/perf/perf_guided_filter.cpp new file mode 100644 index 000000000..e018d53d3 --- /dev/null +++ b/modules/ximgproc/perf/perf_guided_filter.cpp @@ -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 GFParams; + +typedef TestBaseWithParam 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); +} + +} \ No newline at end of file diff --git a/modules/ximgproc/perf/perf_main.cpp b/modules/ximgproc/perf/perf_main.cpp new file mode 100644 index 000000000..cd025b579 --- /dev/null +++ b/modules/ximgproc/perf/perf_main.cpp @@ -0,0 +1,3 @@ +#include "perf_precomp.hpp" + +CV_PERF_TEST_MAIN(edgefilter) diff --git a/modules/ximgproc/perf/perf_precomp.hpp b/modules/ximgproc/perf/perf_precomp.hpp new file mode 100644 index 000000000..88b1684fd --- /dev/null +++ b/modules/ximgproc/perf/perf_precomp.hpp @@ -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 +#include +#include +#include + +#endif diff --git a/modules/ximgproc/perf/pref_joint_bilateral_filter.cpp b/modules/ximgproc/perf/pref_joint_bilateral_filter.cpp new file mode 100644 index 000000000..16f28ce13 --- /dev/null +++ b/modules/ximgproc/perf/pref_joint_bilateral_filter.cpp @@ -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 JBFTestParam; +typedef TestBaseWithParam 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); +} +} \ No newline at end of file diff --git a/modules/ximgproc/samples/CMakeLists.txt b/modules/ximgproc/samples/CMakeLists.txt new file mode 100644 index 000000000..aa6d6a6da --- /dev/null +++ b/modules/ximgproc/samples/CMakeLists.txt @@ -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}) diff --git a/modules/ximgproc/samples/live_demo.cpp b/modules/ximgproc/samples/live_demo.cpp new file mode 100644 index 000000000..d46646877 --- /dev/null +++ b/modules/ximgproc/samples/live_demo.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +using namespace cv; +using namespace cv::ximgproc; + +#include +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; +} diff --git a/modules/ximgproc/src/adaptive_manifold_filter_n.cpp b/modules/ximgproc/src/adaptive_manifold_filter_n.cpp new file mode 100644 index 000000000..bd0c598b0 --- /dev/null +++ b/modules/ximgproc/src/adaptive_manifold_filter_n.cpp @@ -0,0 +1,774 @@ +#include "precomp.hpp" +#include "edgeaware_filters_common.hpp" +#include +#include +#include + +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(ceil(Hs * Lr))); +} + +static void splitChannels(InputArrayOfArrays src, vector& 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 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 jointCn; + vector srcCn; + + vector etaFull; + + vector sum_w_ki_Psi_blur_; + Mat sum_w_ki_Psi_blur_0_; + + Mat w_k; + Mat Psi_splat_0_small; + vector 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& srcv = *static_cast< vector* >(src.getObj()); + vector& dstv = *static_cast< vector* >(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& srcv = *static_cast< vector* >(src.getObj()); + vector& dstv = *static_cast< vector* >(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& eta, Mat1b& cluster, int treeLevel); + + void gatherResult(InputArray src_, OutputArray dst_); + + void compute_w_k(vector& etak, Mat& dst, float sigma, int curTreeLevel); + + void computeClusters(Mat1b& cluster, Mat1b& cluster_minus, Mat1b& cluster_plus); + + void computeEta(Mat& teta, Mat1b& cluster, vector& etaDst); + + + static void h_filter(const Mat1f& src, Mat& dst, float sigma); + + static void RFFilterPass(vector& joint, vector& Psi_splat, Mat& Psi_splat_0, vector& Psi_splat_dst, Mat& Psi_splat_0_dst, float ss, float sr); + + static void computeDTHor(vector& srcCn, Mat& dst, float ss, float sr); + + static void computeDTVer(vector& 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(srcSize.height/2, srcSize.width/2); + const uint64 baseCoef = numeric_limits::max() / 0xFFFF; + rnd.state = static_cast(baseCoef*seedCoef); + + Mat1b cluster0(srcSize, 0xFF); + vector 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 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& 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& 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 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(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(y); + float* dst_prev_row = dst.ptr(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(y); + float* dst_prev_row = dst.ptr(y+1); + + rf_vert_row_pass(dst_cur_row, dst_prev_row, a, src.cols); + } +} + +void AdaptiveManifoldFilterN::compute_w_k(vector& 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(i); + + for (int cn = 0; cn < jointCnNum; cn++) + { + float *eta_kCnRow = etak[cn].ptr(i); + float *jointCnRow = jointCn[cn].ptr(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(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& 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(i); + + for (int cn = 0; cn < cnNum; cn++) + { + float *curCnRow = srcCn[cn].ptr(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& 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(i); + + for (int cn = 0; cn < cnNum; cn++) + { + float *srcRow1 = srcCn[cn].ptr(i); + float *srcRow2 = srcCn[cn].ptr(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& joint, vector& Psi_splat, Mat& Psi_splat_0, vector& 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 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 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& 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::create() +{ + return Ptr(new AdaptiveManifoldFilterN()); +} + +CV_EXPORTS_W +Ptr createAMFilter(double sigma_s, double sigma_r, bool adjust_outliers) +{ + Ptr 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 amf = createAMFilter(sigma_s, sigma_r, adjust_outliers); + amf->filter(src, dst, joint); +} + +} +} \ No newline at end of file diff --git a/modules/ximgproc/src/domain_transform.cpp b/modules/ximgproc/src/domain_transform.cpp new file mode 100644 index 000000000..6433ead05 --- /dev/null +++ b/modules/ximgproc/src/domain_transform.cpp @@ -0,0 +1,24 @@ +#include "precomp.hpp" +#include "dtfilter_cpu.hpp" + +namespace cv +{ +namespace ximgproc +{ + +CV_EXPORTS_W +Ptr createDTFilter(InputArray guide, double sigmaSpatial, double sigmaColor, int mode, int numIters) +{ + return Ptr(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 dtf = DTFilterCPU::create(guide, sigmaSpatial, sigmaColor, mode, numIters); + dtf->setSingleFilterCall(true); + dtf->filter(src, dst); +} + +} +} \ No newline at end of file diff --git a/modules/ximgproc/src/dtfilter_cpu.cpp b/modules/ximgproc/src/dtfilter_cpu.cpp new file mode 100644 index 000000000..34fc5f02b --- /dev/null +++ b/modules/ximgproc/src/dtfilter_cpu.cpp @@ -0,0 +1,177 @@ +#include "precomp.hpp" +#include "dtfilter_cpu.hpp" + +namespace cv +{ +namespace ximgproc +{ + +typedef Vec Vec1b; +typedef Vec Vec1f; + +Ptr DTFilterCPU::create(InputArray guide, double sigmaSpatial, double sigmaColor, int mode, int numIters) +{ + Ptr dtf(new DTFilterCPU()); + dtf->init(guide, sigmaSpatial, sigmaColor, mode, numIters); + return dtf; +} + +Ptr 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 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_(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_(src, dst, dDepth); + if (depth == CV_32F) + filter_(src, dst, dDepth); + } + else if (cn == 2) + { + if (depth == CV_8U) + filter_(src, dst, dDepth); + if (depth == CV_32F) + filter_(src, dst, dDepth); + } + else if (cn == 3) + { + if (depth == CV_8U) + filter_(src, dst, dDepth); + if (depth == CV_32F) + filter_(src, dst, dDepth); + } + else if (cn == 4) + { + if (depth == CV_8U) + filter_(src, dst, dDepth); + if (depth == CV_32F) + filter_(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); +} + +} +} \ No newline at end of file diff --git a/modules/ximgproc/src/dtfilter_cpu.hpp b/modules/ximgproc/src/dtfilter_cpu.hpp new file mode 100644 index 000000000..57712c775 --- /dev/null +++ b/modules/ximgproc/src/dtfilter_cpu.hpp @@ -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 create(InputArray guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3); + + static Ptr 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 + static DTFilterCPU* create_p_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3); + + template + static DTFilterCPU create_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3); + + template + void init_(Mat& guide, double sigmaSpatial, double sigmaColor, int mode = DTF_NC, int numIters = 3); + + template + void filter_(const Mat& src, Mat& dst, int dDepth = -1); + +protected: /*Typedefs declarations*/ + + typedef float IDistType; + typedef Vec IDistVec; + + typedef float DistType; + typedef Vec 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 + inline IDistType getTransformedDistance(const GuideVec &l, const GuideVec &r) + { + return (IDistType)(1.0f + sigmaSpatial / sigmaColor * norm1(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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + static void integrateSparseRow(const SrcVec *src, const float *dist, SrcWorkVec *dst, int cols); + + template + 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 + inline static T norm1(const cv::Vec& v1, const cv::Vec& 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 +void domainTransformFilter( const Mat_& guide, + const Mat_& source, + Mat& dst, + double sigmaSpatial, double sigmaColor, + int mode = DTF_NC, int numPasses = 3 + ); + +template +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 \ No newline at end of file diff --git a/modules/ximgproc/src/dtfilter_cpu.inl.hpp b/modules/ximgproc/src/dtfilter_cpu.inl.hpp new file mode 100644 index 000000000..684cefd8c --- /dev/null +++ b/modules/ximgproc/src/dtfilter_cpu.inl.hpp @@ -0,0 +1,588 @@ +#ifndef __OPENCV_DTFILTER_INL_HPP__ +#define __OPENCV_DTFILTER_INL_HPP__ +#include "precomp.hpp" +#include "edgeaware_filters_common.hpp" +#include + +namespace cv +{ +namespace ximgproc +{ + +using namespace cv::ximgproc::intrinsics; + +#define NC_USE_INTEGRAL_SRC +//#undef NC_USE_INTEGRAL_SRC + +template +DTFilterCPU DTFilterCPU::create_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode, int numIters) +{ + DTFilterCPU dtf; + dtf.init_(guide, sigmaSpatial, sigmaColor, mode, numIters); + return dtf; +} + +template +DTFilterCPU* DTFilterCPU::create_p_(const Mat& guide, double sigmaSpatial, double sigmaColor, int mode, int numIters) +{ + DTFilterCPU* dtf = new DTFilterCPU(); + dtf->init_(guide, sigmaSpatial, sigmaColor, mode, numIters); + return dtf; +} + +template +void DTFilterCPU::init_(Mat& guide, double sigmaSpatial_, double sigmaColor_, int mode_, int numIters_) +{ + CV_Assert(guide.type() == cv::DataType::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 horBody(*this, guide, idistHor); + parallel_for_(horBody.getRange(), horBody); + } + { + Mat guideT = guide.t(); + ComputeIDTHor_ParBody horBody(*this, guideT, idistVert); + parallel_for_(horBody.getRange(), horBody); + } + } + else if (mode == DTF_IC) + { + { + ComputeDTandIDTHor_ParBody horBody(*this, guide, distHor, idistHor); + parallel_for_(horBody.getRange(), horBody); + } + { + Mat guideT = guide.t(); + ComputeDTandIDTHor_ParBody horBody(*this, guideT, distVert, idistVert); + parallel_for_(horBody.getRange(), horBody); + } + } + else if (mode == DTF_RF) + { + ComputeA0DTHor_ParBody horBody(*this, guide); + ComputeA0DTVert_ParBody vertBody(*this, guide); + parallel_for_(horBody.getRange(), horBody); + parallel_for_(vertBody.getRange(), vertBody); + } + else + { + CV_Error(Error::StsBadFlag, "Incorrect DT filter mode"); + } +} + +template +void DTFilterCPU::filter_(const Mat& src, Mat& dst, int dDepth) +{ + typedef typename DataType >::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 horParBody(res, idistHor, resT); + FilterNC_horPass 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(src, res, resT); + + FilterIC_horPass horParBody(res, idistHor, distHor, resT); + FilterIC_horPass 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 horParBody(res, a0dHor, iter); + FilterRF_vertPass vertParBody(res, a0dVert, iter); + parallel_for_(horParBody.getRange(), horParBody); + parallel_for_(vertParBody.getRange(), vertParBody); + } + } + + if (!useDstAsRes) + { + res.convertTo(dst, dDepth); + } +} + +template +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 +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 +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(i); + line[0] = line[1]; + line[ri] = line[ri - 1]; + } + + WorkVec *topLine = dst.ptr(0); + WorkVec *bottomLine = dst.ptr(dst.rows - 1); + ri = dstOutT.cols - 1; + for (int i = 0; i < src.cols; i++) + { + line = dstOutT.ptr(i); + line[0] = topLine[i]; + line[ri] = bottomLine[i]; + } +} + + +template +DTFilterCPU::FilterNC_horPass::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 +void DTFilterCPU::FilterNC_horPass::operator()(const Range& range) const +{ + #ifdef NC_USE_INTEGRAL_SRC + std::vector isrcBuf(src.cols + 1); + WorkVec *isrcLine = &isrcBuf[0]; + #endif + + for (int i = range.start; i < range.end; i++) + { + const WorkVec *srcLine = src.ptr(i); + IDistType *idistLine = idist.ptr(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(j, i) = sum / (float)(rightBound + 1 - leftBound); + } + } +} + +template +DTFilterCPU::FilterIC_horPass::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 +void DTFilterCPU::FilterIC_horPass::operator()(const Range& range) const +{ + #ifdef CV_GET_NUM_THREAD_WORKS_PROPERLY + WorkVec *isrcLine = const_cast( isrcBuf.ptr(cv::getThreadNum()) ); + #else + WorkVec *isrcLine = const_cast( isrcBuf.ptr(range.start) ); + #endif + + for (int i = range.start; i < range.end; i++) + { + WorkVec *srcLine = src.ptr(i); + DistType *distLine = dist.ptr(i); + IDistType *idistLine = idist.ptr(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(j, i) = (sumL + sumC + sumR) / (2.0f * radius); + } + } +} + + +template +DTFilterCPU::FilterRF_horPass::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 +void DTFilterCPU::FilterRF_horPass::operator()(const Range& range) const +{ + for (int i = range.start; i < range.end; i++) + { + WorkVec *dstLine = res.ptr(i); + DistType *adLine = alphaD.ptr(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 +DTFilterCPU::FilterRF_vertPass::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 +void DTFilterCPU::FilterRF_vertPass::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(i); + WorkVec *prevRow = res.ptr(i - 1); + DistType *adRow = alphaD.ptr(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(i + 1); + WorkVec *curRow = res.ptr(i); + DistType *adRow = alphaD.ptr(i); + + for (int j = rcols.start; j < rcols.end; j++) + { + curRow[j] += adRow[j] * (prevRow[j] - curRow[j]); + } + } +} + +template +DTFilterCPU::ComputeIDTHor_ParBody::ComputeIDTHor_ParBody(DTFilterCPU& dtf_, Mat& guide_, Mat& dst_) +: dtf(dtf_), guide(guide_), dst(dst_) +{ + dst.create(guide.rows, guide.cols + 1, IDistVec::type); +} + +template +void DTFilterCPU::ComputeIDTHor_ParBody::operator()(const Range& range) const +{ + for (int i = range.start; i < range.end; i++) + { + const GuideVec *guideLine = guide.ptr(i); + IDistType *idistLine = dst.ptr(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::max(); + } +} + +template +DTFilterCPU::ComputeDTandIDTHor_ParBody::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 +void DTFilterCPU::ComputeDTandIDTHor_ParBody::operator()(const Range& range) const +{ + for (int i = range.start; i < range.end; i++) + { + const GuideVec *guideLine = guide.ptr(i); + DistType *distLine = dist.ptr(i); + IDistType *idistLine = idist.ptr(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 +DTFilterCPU::ComputeA0DTHor_ParBody::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 +void DTFilterCPU::ComputeA0DTHor_ParBody::operator()(const Range& range) const +{ + for (int i = range.start; i < range.end; i++) + { + const GuideVec *guideLine = guide.ptr(i); + DistType *dstLine = dtf.a0distHor.ptr(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 +DTFilterCPU::ComputeA0DTHor_ParBody::~ComputeA0DTHor_ParBody() +{ + cv::exp(dtf.a0distHor, dtf.a0distHor); +} + +template +DTFilterCPU::ComputeA0DTVert_ParBody::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 +void DTFilterCPU::ComputeA0DTVert_ParBody::operator()(const Range& range) const +{ + for (int i = range.start; i < range.end; i++) + { + DistType *dstLine = dtf.a0distVert.ptr(i); + GuideVec *guideRow1 = guide.ptr(i); + GuideVec *guideRow2 = guide.ptr(i+1); + + for (int j = 0; j < guide.cols; j++) + { + DistType d = (DistType)dtf.getTransformedDistance(guideRow1[j], guideRow2[j]); + dstLine[j] = lna*d; + } + } +} + +template +DTFilterCPU::ComputeA0DTVert_ParBody::~ComputeA0DTVert_ParBody() +{ + cv::exp(dtf.a0distVert, dtf.a0distVert); +} + + +template +void domainTransformFilter( const Mat_& guide, + const Mat_& source, + Mat& dst, + double sigmaSpatial, double sigmaColor, + int mode, int numPasses + ) +{ + DTFilterCPU *dtf = DTFilterCPU::create_p_(guide, sigmaSpatial, sigmaColor, mode, numPasses); + dtf->filter_(source, dst); + delete dtf; +} + +template +void domainTransformFilter( const Mat& guide, + const Mat& source, + Mat& dst, + double sigmaSpatial, double sigmaColor, + int mode, int numPasses + ) +{ + DTFilterCPU *dtf = DTFilterCPU::create_p_(guide, sigmaSpatial, sigmaColor, mode, numPasses); + dtf->filter_(source, dst); + delete dtf; +} + +} +} +#endif \ No newline at end of file diff --git a/modules/ximgproc/src/edgeaware_filters_common.cpp b/modules/ximgproc/src/edgeaware_filters_common.cpp new file mode 100644 index 000000000..8fcbc60bf --- /dev/null +++ b/modules/ximgproc/src/edgeaware_filters_common.cpp @@ -0,0 +1,515 @@ +#include "precomp.hpp" +#include "edgeaware_filters_common.hpp" +#include "dtfilter_cpu.hpp" + +#include +#include +#include +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 createDTFilterRF(InputArray adistHor, InputArray adistVert, double sigmaSpatial, double sigmaColor, int numIters) +{ + return Ptr(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& srcv = *static_cast*>(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& srcv = *static_cast*>(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& srcv = *static_cast*>(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& srcv = *static_cast*>(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 \ No newline at end of file diff --git a/modules/ximgproc/src/edgeaware_filters_common.hpp b/modules/ximgproc/src/edgeaware_filters_common.hpp new file mode 100644 index 000000000..7e6f2b6f7 --- /dev/null +++ b/modules/ximgproc/src/edgeaware_filters_common.hpp @@ -0,0 +1,61 @@ +#ifndef __EDGEAWAREFILTERS_COMMON_HPP__ +#define __EDGEAWAREFILTERS_COMMON_HPP__ +#ifdef __cplusplus + +namespace cv +{ +namespace ximgproc +{ + +Ptr 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 \ No newline at end of file diff --git a/modules/ximgproc/src/guided_filter.cpp b/modules/ximgproc/src/guided_filter.cpp new file mode 100644 index 000000000..7c620b4a3 --- /dev/null +++ b/modules/ximgproc/src/guided_filter.cpp @@ -0,0 +1,755 @@ +#include "precomp.hpp" +#include "edgeaware_filters_common.hpp" +#include + +#ifdef _MSC_VER +# pragma warning(disable: 4512) +#endif + +namespace cv +{ +namespace ximgproc +{ + +using std::vector; +using namespace cv::ximgproc::intrinsics; + +template +struct SymArray2D +{ + vector 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 +static void splitFirstNChannels(InputArrayOfArrays src, vector& 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 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 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 guideCn; + vector guideCnMean; + + SymArray2D covarsInv; + + int gCnNum; + +protected: + + GuidedFilterImpl() {} + + void init(InputArray guide, int radius, double eps); + + void computeCovGuide(SymArray2D& covars); + + void computeCovGuideAndSrc(vector& srcCn, vector& srcCnMean, vector >& 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 src; + mutable vector dst; + TransformFunc func; + + GFTransform_ParBody(GuidedFilterImpl& gf_, vector& srcv, vector& dstv, TransformFunc func_); + GFTransform_ParBody(GuidedFilterImpl& gf_, vector >& srcvv, vector >& dstvv, TransformFunc func_); + + void operator () (const Range& range) const; + + Range getRange() const + { + return Range(0, (int)src.size()); + } + }; + + template + void parConvertToWorkType(V &src, V &dst) + { + GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::convertToWorkType); + parallel_for_(pb.getRange(), pb); + } + + template + 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 &covars; + + MulChannelsGuide_ParBody(GuidedFilterImpl& gf_, SymArray2D& covars_) + : gf(gf_), covars(covars_) {} + + void operator () (const Range& range) const; + }; + + struct ComputeCovGuideFromChannelsMul_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + SymArray2D &covars; + + ComputeCovGuideFromChannelsMul_ParBody(GuidedFilterImpl& gf_, SymArray2D& covars_) + : gf(gf_), covars(covars_) {} + + void operator () (const Range& range) const; + }; + + struct ComputeCovGuideInv_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + SymArray2D &covars; + + ComputeCovGuideInv_ParBody(GuidedFilterImpl& gf_, SymArray2D& covars_); + + void operator () (const Range& range) const; + }; + + + struct MulChannelsGuideAndSrc_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + vector > &cov; + vector &srcCn; + + MulChannelsGuideAndSrc_ParBody(GuidedFilterImpl& gf_, vector& srcCn_, vector >& cov_) + : gf(gf_), cov(cov_), srcCn(srcCn_) {} + + void operator () (const Range& range) const; + }; + + struct ComputeCovFromSrcChannelsMul_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + vector > &cov; + vector &srcCnMean; + + ComputeCovFromSrcChannelsMul_ParBody(GuidedFilterImpl& gf_, vector& srcCnMean_, vector >& cov_) + : gf(gf_), cov(cov_), srcCnMean(srcCnMean_) {} + + void operator () (const Range& range) const; + }; + + struct ComputeAlpha_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + vector > α + vector > &covSrc; + + ComputeAlpha_ParBody(GuidedFilterImpl& gf_, vector >& alpha_, vector >& covSrc_) + : gf(gf_), alpha(alpha_), covSrc(covSrc_) {} + + void operator () (const Range& range) const; + }; + + struct ComputeBeta_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + vector > α + vector &srcCnMean; + vector β + + ComputeBeta_ParBody(GuidedFilterImpl& gf_, vector >& alpha_, vector& srcCnMean_, vector& beta_) + : gf(gf_), alpha(alpha_), srcCnMean(srcCnMean_), beta(beta_) {} + + void operator () (const Range& range) const; + }; + + struct ApplyTransform_ParBody : public ParallelLoopBody + { + GuidedFilterImpl &gf; + vector > α + vector β + + ApplyTransform_ParBody(GuidedFilterImpl& gf_, vector >& alpha_, vector& 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(i); + guide2 = gf.guideCn[c2].ptr(i); + cov = covars(c1, c2).ptr(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(i); + guide2 = gf.guideCnMean[c2].ptr(i); + cov = covars(c1, c2).ptr(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& 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 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(i); + + float *a00 = covars((k + 1) % 3, (l + 1) % 3).ptr(i); + float *a01 = covars((k + 1) % 3, (l + 2) % 3).ptr(i); + float *a10 = covars((k + 2) % 3, (l + 1) % 3).ptr(i); + float *a11 = covars((k + 2) % 3, (l + 2) % 3).ptr(i); + + det_2x2(dst, a00, a01, a10, a11, gf.w); + } + + for (int k = 0; k < 3; k++) + { + register float *a = covars(k, 0).ptr(i); + register float *ac = gf.covarsInv(k, 0).ptr(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(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(i); + float *a10 = gf.covarsInv(1, 0).ptr(i); + float *a11 = gf.covarsInv(1, 1).ptr(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(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(i); + + for (int gi = start; gi != end; gi += step) + { + float *guideLine = gf.guideCn[gi].ptr(i); + float *dstLine = cov[si][gi].ptr(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(i); + + for (int gi = start; gi != end; gi += step) + { + float *guideMeanLine = gf.guideCnMean[gi].ptr(i); + float *covLine = cov[si][gi].ptr(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(i); + for (int k = 0; k < gf.gCnNum; k++) + { + y = covSrc[si][k].ptr(i); + A = gf.covarsInv(gi, k).ptr(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(i); + + float *betaDst, *g, *a; + for (int si = 0; si < srcCnNum; si++) + { + betaDst = beta[si].ptr(i); + for (int gi = 0; gi < gf.gCnNum; gi++) + { + a = alpha[si][gi].ptr(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(i); + + float *betaDst, *g, *a; + for (int si = 0; si < srcCnNum; si++) + { + betaDst = beta[si].ptr(i); + for (int gi = 0; gi < gf.gCnNum; gi++) + { + a = alpha[si][gi].ptr(i); + g = _g[gi]; + + add_mul(betaDst, a, g, gf.w); + } + } + } +} + +GuidedFilterImpl::GFTransform_ParBody::GFTransform_ParBody(GuidedFilterImpl& gf_, vector& srcv, vector& 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 >& srcvv, vector >& 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::create(InputArray guide, int radius, double eps) +{ + GuidedFilterImpl *gf = new GuidedFilterImpl(); + gf->init(guide, radius, eps); + return Ptr(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 covars; + computeCovGuide(covars); + runParBody(ComputeCovGuideInv_ParBody(*this, covars)); + covars.release(); +} + +void GuidedFilterImpl::computeCovGuide(SymArray2D& 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 srcCn(srcCnNum); + vector& srcCnMean = srcCn; + split(src, srcCn); + + if (src.depth() != CV_32F) + { + parConvertToWorkType(srcCn, srcCn); + } + + vector > covSrcGuide(srcCnNum); + computeCovGuideAndSrc(srcCn, srcCnMean, covSrcGuide); + + vector > 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& 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& srcCn, vector& srcCnMean, vector >& 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 createGuidedFilter(InputArray guide, int radius, double eps) +{ + return Ptr(GuidedFilterImpl::create(guide, radius, eps)); +} + +CV_EXPORTS_W +void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth) +{ + Ptr gf = createGuidedFilter(guide, radius, eps); + gf->filter(src, dst, dDepth); +} + +} +} \ No newline at end of file diff --git a/modules/ximgproc/src/joint_bilateral_filter.cpp b/modules/ximgproc/src/joint_bilateral_filter.cpp new file mode 100644 index 000000000..013012c62 --- /dev/null +++ b/modules/ximgproc/src/joint_bilateral_filter.cpp @@ -0,0 +1,366 @@ +#include "precomp.hpp" +#include +#include +using namespace std; + +#ifdef _MSC_VER +# pragma warning(disable: 4512) +#endif + +namespace cv +{ +namespace ximgproc +{ + +typedef Vec Vec1f; +typedef Vec 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 +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(i) + j; + SrcVec *srcCenterPixPtr = src.ptr(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(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(srcCenterPixPtr + spaceOfs[k]); + for (int cn = 0; cn < SrcVec::channels; cn++) + srcSum[cn] += weight*srcPix[cn]; + wSum += weight; + } + + dst.at(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 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 spaceWeightsv(d*d); + vector 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(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT)); + } + if (src.type() == CV_32FC3) + { + parallel_for_(range, JointBilateralFilter_32f(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT)); + } + } + + if (joint.type() == CV_32FC3) + { + if (src.type() == CV_32FC1) + { + parallel_for_(range, JointBilateralFilter_32f(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT)); + } + if (src.type() == CV_32FC3) + { + parallel_for_(range, JointBilateralFilter_32f(jointTemp, srcTemp, dst, radius, maxk, scaleIndex, spaceOfsJoint, spaceWeights, expLUT)); + } + } +} + +template +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 JointVeci; + typedef Vec 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(i) + j; + SrcVec *srcCenterPixPtr = src.ptr(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(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(srcCenterPixPtr + spaceOfs[k]); + for (int cn = 0; cn < SrcVec::channels; cn++) + srcSum[cn] += weight*srcPix[cn]; + wSum += weight; + } + + dst.at(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 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 spaceWeightsv(d*d); + vector 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(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); + } + if (src.type() == CV_8UC3) + { + parallel_for_(range, JointBilateralFilter_8u(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); + } + } + + if (joint.type() == CV_8UC3) + { + if (src.type() == CV_8UC1) + { + parallel_for_(range, JointBilateralFilter_8u(jointTemp, srcTemp, dst, radius, maxk, spaceOfsJoint, spaceWeights, expLUT)); + } + if (src.type() == CV_8UC3) + { + parallel_for_(range, JointBilateralFilter_8u(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"); + } +} + +} +} \ No newline at end of file diff --git a/modules/ximgproc/src/precomp.hpp b/modules/ximgproc/src/precomp.hpp new file mode 100644 index 000000000..ea07f5e79 --- /dev/null +++ b/modules/ximgproc/src/precomp.hpp @@ -0,0 +1,15 @@ +#ifndef _OPENCV_EDGEFILTER_PRECOMP_HPP_ +#define _OPENCV_EDGEFILTER_PRECOMP_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#endif \ No newline at end of file diff --git a/modules/ximgproc/test/test_adaptive_manifold.cpp b/modules/ximgproc/test/test_adaptive_manifold.cpp new file mode 100644 index 000000000..9173f3e79 --- /dev/null +++ b/modules/ximgproc/test/test_adaptive_manifold.cpp @@ -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 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 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 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 amf = createAMFilter(sigma_s, sigma_r, true); + amf->setBool("use_RNG", false); + amf->filter(srcImg, res, srcImg); + amf->collectGarbage(); + + checkSimilarity(res, refRes); + } +} + +typedef tuple AMRefTestParams; +typedef TestWithParam AdaptiveManifoldRefImplTest; + +Ptr 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 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") +)); + +} \ No newline at end of file diff --git a/modules/ximgproc/test/test_adaptive_manifold_ref_impl.cpp b/modules/ximgproc/test/test_adaptive_manifold_ref_impl.cpp new file mode 100644 index 000000000..85031ca30 --- /dev/null +++ b/modules/ximgproc/test/test_adaptive_manifold_ref_impl.cpp @@ -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 +#include + +namespace +{ + + using std::numeric_limits; + using namespace cv; + using namespace cv::ximgproc; + + struct Buf + { + Mat_ eta_1; + Mat_ cluster_1; + + Mat_ tilde_dst; + Mat_ alpha; + Mat_ diff; + Mat_ dst; + + Mat_ V; + + Mat_ dIcdx; + Mat_ dIcdy; + Mat_ dIdx; + Mat_ dIdy; + Mat_ dHdx; + Mat_ dVdy; + + Mat_ t; + + Mat_ theta_masked; + Mat_ mul; + Mat_ numerator; + Mat_ denominator; + Mat_ numerator_filtered; + Mat_ denominator_filtered; + + Mat_ X; + Mat_ eta_k_small; + Mat_ eta_k_big; + Mat_ X_squared; + Mat_ pixel_dist_to_manifold_squared; + Mat_ gaussian_distance_weights; + Mat_ Psi_splat; + Mat_ Psi_splat_joined; + Mat_ Psi_splat_joined_resized; + Mat_ blurred_projected_values; + Mat_ w_ki_Psi_blur; + Mat_ w_ki_Psi_blur_0; + Mat_ w_ki_Psi_blur_resized; + Mat_ w_ki_Psi_blur_0_resized; + Mat_ rand_vec; + Mat_ v1; + Mat_ Nx_v1_mult; + Mat_ theta; + + std::vector > eta_minus; + std::vector > cluster_minus; + std::vector > eta_plus; + std::vector > 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_& eta_k, const Mat_& cluster_k, int current_tree_level); + + Buf buf_; + + Mat_ src_f_; + Mat_ src_joint_f_; + + Mat_ sum_w_ki_Psi_blur_; + Mat_ sum_w_ki_Psi_blur_0_; + + Mat_ 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(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((delta2 - minstep) / m.step + 1), m.rows); + wholeSize.width = std::max(static_cast((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 + inline void ensureSizeIsEnough(int rows, int cols, Mat_& 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((delta2 - minstep) / m.step + 1), m.rows); + wholeSize.width = std::max(static_cast((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 + inline void ensureSizeIsEnough(Size size, Mat_& m) + { + ensureSizeIsEnough(size.height, size.width, m); + } + + template + void h_filter(const Mat_& src, Mat_& 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 + void rdivide(const Mat_& a, const Mat_& b, Mat_& 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::epsilon()) + dst_row[x] = a_row[x] * (1.0f / b_row[x]); + } + } + } + + template + void times(const Mat_& a, const Mat_& b, Mat_& 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::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::max() / 0xFFFF; + rng_.state = static_cast(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(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(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_& src, Mat_& 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_& src, Mat_& 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_& a, const Mat_& b, Mat_& 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_& src, Mat_& 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_& src, Mat_& 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_& I, const Mat_& DH, const Mat_& DV, Mat_& 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_& src, const Mat_& src_joint, Mat_& 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_& src, Mat_& dst1, Mat_& 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_& X, const Mat_& mask, Mat_& dst, int num_pca_iterations, const Mat_& 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_& src_joint_f, const Mat_& theta, const Mat_& cluster, Mat_& 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(buf.mul.cols * (1.0 / df)), saturate_cast(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_& eta_k, const Mat_& 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(eta_k.cols * (1.0 / df)), saturate_cast(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_& 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(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_& 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_ 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_ dot(src_joint_f_.rows, src_joint_f_.cols, (float*) buf_.Nx_v1_mult.data); + + Mat_& 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_& 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_& 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_& 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 createAMFilterRefImpl(double sigma_s, double sigma_r, bool adjust_outliers) +{ + Ptr amf(new AdaptiveManifoldFilterRefImpl()); + + amf->set("sigma_s", sigma_s); + amf->set("sigma_r", sigma_r); + amf->set("adjust_outliers", adjust_outliers); + + return amf; +} + +} \ No newline at end of file diff --git a/modules/ximgproc/test/test_domain_transform.cpp b/modules/ximgproc/test/test_domain_transform.cpp new file mode 100644 index 000000000..4a266f751 --- /dev/null +++ b/modules/ximgproc/test/test_domain_transform.cpp @@ -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 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 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 +Mat getChessMat1px(Size sz, double whiteIntensity = 255) +{ + typedef typename DataType::channel_type SrcType; + + Mat dst(sz, DataType::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(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(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); +} + +} \ No newline at end of file diff --git a/modules/ximgproc/test/test_guided_filter.cpp b/modules/ximgproc/test/test_guided_filter.cpp new file mode 100644 index 000000000..1b33a19e0 --- /dev/null +++ b/modules/ximgproc/test/test_guided_filter.cpp @@ -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 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 GFParams; +typedef TestWithParam 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 gf = createGuidedFilter(guide, radius, eps); + gf->filter(src, res); + + cv::setNumThreads(1); + Mat resRef; + Ptr 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") +)); + +} \ No newline at end of file diff --git a/modules/ximgproc/test/test_joint_bilateral_filter.cpp b/modules/ximgproc/test/test_joint_bilateral_filter.cpp new file mode 100644 index 000000000..863e8483d --- /dev/null +++ b/modules/ximgproc/test/test_joint_bilateral_filter.cpp @@ -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 Vec1f; +typedef Vec Vec1b; + +#ifndef SQR +#define SQR(a) ((a)*(a)) +#endif + +template +float normL1Sqr(const Vec& a, const Vec& 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 +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 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_ dst = dst_.getMat(); + Mat_ jointExt; + Mat_ 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_(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 JBFTestParam; +typedef TestWithParam 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 BFTestParam; +typedef TestWithParam 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) + ) +); + +} \ No newline at end of file diff --git a/modules/ximgproc/test/test_main.cpp b/modules/ximgproc/test/test_main.cpp new file mode 100644 index 000000000..17ee2225b --- /dev/null +++ b/modules/ximgproc/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("") \ No newline at end of file diff --git a/modules/ximgproc/test/test_precomp.hpp b/modules/ximgproc/test/test_precomp.hpp new file mode 100644 index 000000000..1d9a8ab52 --- /dev/null +++ b/modules/ximgproc/test/test_precomp.hpp @@ -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 +#include +#include +#include +#include +#include +#include + +#endif \ No newline at end of file diff --git a/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss15_sr0.3_outliers_ref.png b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss15_sr0.3_outliers_ref.png new file mode 100644 index 000000000..09b6378f1 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss15_sr0.3_outliers_ref.png differ diff --git a/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss30_sr0.1_ref.png b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss30_sr0.1_ref.png new file mode 100644 index 000000000..da8f762bb Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss30_sr0.1_ref.png differ diff --git a/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss50_sr0.3_ref.png b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss50_sr0.3_ref.png new file mode 100644 index 000000000..23be3f4f3 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss50_sr0.3_ref.png differ diff --git a/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss50_sr0.5_outliers_ref.png b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss50_sr0.5_outliers_ref.png new file mode 100644 index 000000000..94cbf8fe2 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss50_sr0.5_outliers_ref.png differ diff --git a/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss5_sr0.1_outliers_ref.png b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss5_sr0.1_outliers_ref.png new file mode 100644 index 000000000..d35ad979b Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss5_sr0.1_outliers_ref.png differ diff --git a/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss5_sr0.3_ref.png b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss5_sr0.3_ref.png new file mode 100644 index 000000000..59830f0e6 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/amf/kodim23_amf_ss5_sr0.3_ref.png differ diff --git a/modules/ximgproc/testdata/edgefilter/dt/authors_statue_IC_ss30_sc0.2.png b/modules/ximgproc/testdata/edgefilter/dt/authors_statue_IC_ss30_sc0.2.png new file mode 100644 index 000000000..adae53725 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/dt/authors_statue_IC_ss30_sc0.2.png differ diff --git a/modules/ximgproc/testdata/edgefilter/dt/authors_statue_NC_ss30_sc0.2.png b/modules/ximgproc/testdata/edgefilter/dt/authors_statue_NC_ss30_sc0.2.png new file mode 100644 index 000000000..7a14144c3 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/dt/authors_statue_NC_ss30_sc0.2.png differ diff --git a/modules/ximgproc/testdata/edgefilter/dt/authors_statue_RF_ss30_sc0.2.png b/modules/ximgproc/testdata/edgefilter/dt/authors_statue_RF_ss30_sc0.2.png new file mode 100644 index 000000000..4d71882a3 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/dt/authors_statue_RF_ss30_sc0.2.png differ diff --git a/modules/ximgproc/testdata/edgefilter/kodim23.png b/modules/ximgproc/testdata/edgefilter/kodim23.png new file mode 100644 index 000000000..ed0297ea6 Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/kodim23.png differ diff --git a/modules/ximgproc/testdata/edgefilter/statue.png b/modules/ximgproc/testdata/edgefilter/statue.png new file mode 100644 index 000000000..b348cfd7d Binary files /dev/null and b/modules/ximgproc/testdata/edgefilter/statue.png differ