25 # include "Base/Common/W32Compat.hh"
29 #include "CornerDetectorGradient.hh"
30 #include <Base/Image/ImageConvert.hh>
33 #include <Base/Image/ImageIO.hh>
38 #include <Base/Debug/TimeMeasure.hh>
41 #include <Base/Common/BIASpragma.hh>
51 template <
class StorageType,
class CalculationType>
63 template <
class StorageType,
class CalculationType>
69 template <
class StorageType,
class CalculationType>
75 _gauss.Filter(image, fim);
80 if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)){
90 if (
typeid(StorageType)==
typeid(CalculationType)) {
97 int sres=this->_st.CalcStructureTensor(*kim, this->_sgxx, this->_sgxy,
99 if (
typeid(StorageType)!=
typeid(CalculationType))
delete kim;
101 else if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)) {
107 if (this->_sgxx.GetWidth()>this->_Cornerness.GetWidth() ||
108 this->_sgxx.GetHeight()>this->_Cornerness.GetHeight()){
109 _AllocInternalMem(this->_sgxx.GetWidth(), this->_sgxx.GetHeight());
112 int mres = _ComputeCornerness(this->_Cornerness);
114 if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)) {
119 this->_ZeroFeatureMap();
121 if (p.size()>0) this->_FillFeatureMap(p);
122 int eres = this->_EnforceMinimumDistance(p, quality);
125 if (_DoRefinement) _RefineCorners(p, quality);
132 return (sres==0 && mres==0 && eres==0)?(0):-1;
135 template <
class StorageType,
class CalculationType>
138 const double noise)
const
140 const unsigned int num = p.size();
142 const double square_noise = noise * noise;
145 for (
unsigned int i = 0; i < num; i++){
146 x = (int)rint(p[i][0]);
147 y = (int)rint(p[i][1]);
149 if (!(x > 0 && y >= 0 &&
150 x < (
int)_sgxx.GetWidth() && y < (int)_sgxx.GetHeight() &&
151 x < (int)_sgxy.GetWidth() && y < (int)_sgxy.GetHeight() &&
152 x < (int)_sgyy.GetWidth() && y < (int)_sgyy.GetHeight())) {
153 BIASERR(
"Invalid feature point position given (" << x
154 <<
", " << y <<
"), setting covariance to max.!");
156 cov[i][0][0] = cov[i][1][1] = DBL_MAX;
157 cov[i][1][0] = cov[i][0][1] = 0.0;
161 c[0][0] = _sgxx.GetImageDataArray()[y][x];
162 c[1][1] = _sgyy.GetImageDataArray()[y][x];
163 c[1][0] = c[0][1] = _sgxy.GetImageDataArray()[y][x];
166 BIASERR(
"Inversion of covariance matrix " << c <<
" failed (result of "
167 "Invert = " << ires <<
"), setting covariance matrix to max.!");
169 cov[i][0][0] = cov[i][1][1] = DBL_MAX;
170 cov[i][1][0] = cov[i][0][1] = 0.0;
172 cov[i] *= square_noise;
177 template <
class StorageType,
class CalculationType>
181 std::vector<HomgPoint2D>& p,std::vector<QUAL>& quality,
190 if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)){
204 cerr <<
"allocating took "<<timer2.
GetRealTime()<<
" us\n";
212 sres=this->_st.CalcStructureTensor(gradx, grady, this->_sgxx,
213 this->_sgxy, this->_sgyy);
216 if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)){
225 cerr <<
"CalcStructureTensor took "<<timer2.
GetRealTime()<<
" us\n";
230 if (this->_sgxx.GetWidth()>this->_Cornerness.GetWidth() ||
231 this->_sgxx.GetHeight()>this->_Cornerness.GetHeight()){
232 _AllocInternalMem(this->_sgxx.GetWidth(), this->_sgxx.GetHeight());
234 int mres = _ComputeCornerness(this->_Cornerness);
237 if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)){
245 cerr <<
"_ComputeCornerness took "<<timer2.
GetRealTime()<<
" us\n";
250 this->_ZeroFeatureMap();
251 if (p.size()>0) this->_FillFeatureMap(p);
252 int eres = this->_EnforceMinimumDistance(p, quality);
256 cerr <<
"EnforceMinimumDistance took "<<timer2.
GetRealTime()<<
" us\n";
262 if (_DoRefinement) _RefineCorners(p, quality);
269 return (sres==0 && mres==0 && eres==0)?(0):-1;
272 template <
class StorageType,
class CalculationType>
283 if ((ires=this->_st.CalcStructureTensor(gradx, grady, this->_sgxx,
284 this->_sgxy, this->_sgyy))!=0){
285 BIASERR(
"error calculating structure tensor");
294 meres = _ComputeCornerness(cornerness);
296 return (ires==0 && meres==0)?0:-1;
299 template <
class StorageType,
class CalculationType>
302 std::vector<HomgPoint2D>& p, std::vector<QUAL>& quality)
305 _ExtractLocalMaxima(cornerness);
308 this->_ZeroFeatureMap();
309 if (p.size()>0) this->_FillFeatureMap(p);
310 int res = this->_EnforceMinimumDistance(p, quality);
313 if (_DoRefinement) _RefineCorners(p, quality);
319 template <
class StorageType,
class CalculationType>
323 std::vector<HomgPoint2D> &p,
324 std::vector<QUAL> &quality)
327 BIASASSERT( p.size() == quality.size() );
331 const double mincorn = (double)this->_MinCornerness;
334 this->_FeatList.clear();
335 this->_FeatList.reserve(region.size());
340 for (i = 0; i < region.size(); i++) {
341 const int x = region[i][0];
342 const int y = region[i][1];
343 const double corn = imagedatacorn[y][x];
345 if ( corn > mincorn ) {
350 this->_FeatList.push_back(fp);
355 this->_ZeroFeatureMap();
357 this->_FillFeatureMap(p);
359 int res = this->_EnforceMinimumDistance(p, quality);
362 if (_DoRefinement) _RefineCorners(p, quality);
371 template <
class StorageType,
class CalculationType>
383 sort(_FeatList.begin(), _FeatList.end());
388 cerr <<
"Quicksort of "<<_FeatList.size()<<
" took "<<timer.
GetRealTime()<<
" us\n";
397 int minx, maxx, miny, maxy, mx, my;
399 register double mineig;
401 const int md=_MinDistance-1;
402 const int width=_Cornerness.GetWidth();
403 const int height=_Cornerness.GetHeight();
404 const int mwidth=width-md;
405 const int mheight=height-md;
410 if (md>0) num=width*height/(md*md);
417 vector<class Feat>::iterator it;
418 for (it=_FeatList.begin(); it!=_FeatList.end(); it++)
421 if (_NumberFeaturesSoFar >= this->_MaxNum && this->_MaxNum > 0)
426 if (!_FeatureMap[y][x]) {
428 miny=(y>md)?(y-md):(0);
429 maxy=(y>=mheight)?(height-1):(y+md);
430 minx=(x>md)?(x-md):(0);
431 maxx=(x>=mwidth)?(width-1):(x+md);
432 for (my=miny; my<=maxy; my++){
433 for (mx=minx; mx<=maxx; mx++){
434 _FeatureMap[my][mx]=
true;
438 hp.
Set((
double)(x), (
double)(y), 1.0);
440 BIASASSERT(!BIAS_ISNAN(hp[0]) && !BIAS_ISINF(hp[0]));
441 BIASASSERT(!BIAS_ISNAN(hp[1]) && !BIAS_ISINF(hp[1]));
442 if (this->DebugLevelIsSet(D_CD_FEATURES) && points.size()<15)
443 cerr << points.size() <<
" : ("<<x<<
", "<<y<<
" ) qual "<<mineig<<endl;
445 points.push_back(hp);
446 qual.push_back((QUAL)mineig);
447 _NumberFeaturesSoFar++;
452 for (
unsigned i=0; i<points.size(); i++){
453 BIASASSERT(!BIAS_ISNAN(points[i][0]) && !BIAS_ISINF(points[i][0]));
454 BIASASSERT(!BIAS_ISNAN(points[i][1]) && !BIAS_ISINF(points[i][1]));
460 cerr <<
"EnforceMinimumDistance without Quicksort took "
467 template <
class StorageType,
class CalculationType>
470 double &x,
double &y, QUAL& NewCornerness) {
471 CalculationType **cornerness = _Cornerness.GetImageDataArray();
473 const register CalculationType& DR_P11 = cornerness[row-1][col-1];
474 const register CalculationType& DR_P12 = cornerness[row-1][col ];
475 const register CalculationType& DR_P13 = cornerness[row-1][col+1];
476 const register CalculationType& DR_P21 = cornerness[row ][col-1];
477 const register CalculationType& DR_P22 = cornerness[row ][col ];
478 const register CalculationType& DR_P23 = cornerness[row ][col+1];
479 const register CalculationType& DR_P31 = cornerness[row+1][col-1];
480 const register CalculationType& DR_P32 = cornerness[row+1][col ];
481 const register CalculationType& DR_P33 = cornerness[row+1][col+1];
482 NewCornerness = (float)DR_P22;
496 b = (-DR_P11 + DR_P13 - DR_P21 +
497 DR_P23 - DR_P31 + DR_P33)/6.0f;
502 c = (-DR_P11 - DR_P12 - DR_P13 +
503 DR_P31 + DR_P32 + DR_P33)/6.0f;
508 d = (DR_P11 + DR_P13 +
511 -2.0f*(DR_P12+DR_P22+DR_P32))/3.0f;
516 e = (DR_P11 - DR_P31 -
517 DR_P13 + DR_P33)/4.0f;
522 f = ( DR_P11 + DR_P12 + DR_P13
523 -2.0f*(DR_P21+DR_P22+DR_P23)
524 +DR_P31 + DR_P32 + DR_P33)/3.0f;
536 float det = d*f - e*e;
538 float off_x = (c*e - b*f) / det;
539 float off_y = (b*e - c*d) / det;
542 if (fabs (off_x) > 1.0 || fabs (off_y) > 1.0)
553 float a = (- DR_P11 +2.0f *DR_P12 - DR_P13
554 +2.0f*DR_P21 +5.0f *DR_P22 +2.0f*DR_P23
555 - DR_P31 +2.0f *DR_P32 - DR_P33)/9.0f;
557 NewCornerness = float( a + b*off_x + c*off_y + 0.5*d*off_x*off_x
558 + e*off_x*off_y + 0.5*f*off_y*off_y );
564 else if (det < 0.0) {
575 template <
class StorageType,
class CalculationType>
578 BIASERR(
"Make instances of derived class, dont call this !!!");
583 template <
class StorageType,
class CalculationType>
587 vector<HomgPoint2D>::iterator pit;
588 const int md=_MinDistance-1;
589 const int width=_Cornerness.GetWidth();
590 const int height=_Cornerness.GetHeight();
591 const int mwidth=width-md;
592 const int mheight=height-md;
593 int minx, maxx, miny, maxy, mx, my, x, y;
594 for (pit=p.begin(); pit!=p.end(); pit++){
595 if (!pit->IsAtInfinity()){
596 _NumberFeaturesSoFar++;
597 x=(int)rint((*pit)[0]);
598 y=(int)rint((*pit)[1]);
600 miny=(y>md)?(y-md):(0);
601 maxy=(y>=mheight)?(height-1):(y+md);
602 minx=(x>md)?(x-md):(0);
603 maxx=(x>=mwidth)?(width-1):(x+md);
604 for (my=miny; my<=maxy; my++){
605 for (mx=minx; mx<=maxx; mx++){
606 _FeatureMap[my][mx]=
true;
613 template <
class StorageType,
class CalculationType>
617 if (!_Cornerness.IsEmpty()) _Cornerness.Release();
619 if (_FeatureMap!=NULL) {
620 delete[] _FeatureMap[0]; _FeatureMap[0]=NULL;
621 delete[] _FeatureMap; _FeatureMap=NULL;
625 template <
class StorageType,
class CalculationType>
629 _DeleteInternalMem();
630 int size=width*height;
631 _Cornerness.Init(width, height, 1);
635 _FeatureMap =
new bool*[height];
636 _FeatureMap[0] =
new bool[size];
637 for (
int i=1; i<height; i++) {
638 _FeatureMap[i]=_FeatureMap[i-1]+width;
640 _FeatList.reserve(size);
643 template <
class StorageType,
class CalculationType>
649 _gauss.Filter(image, fim);
651 if (this->DebugLevelIsSet(D_CD_WRITE_DEBUG_IM)){
662 if (
typeid(StorageType)==
typeid(CalculationType)) {
669 int sres=this->_st.CalcStructureTensor(*kim, this->_sgxx, this->_sgxy,
671 if (
typeid(StorageType)!=
typeid(CalculationType))
delete kim;
673 if (this->_sgxx.GetWidth()>this->_Cornerness.GetWidth() ||
674 this->_sgxx.GetHeight()>this->_Cornerness.GetHeight()){
675 _AllocInternalMem(this->_sgxx.GetWidth(), this->_sgxx.GetHeight());
679 int mres = _ComputeCornerness(cornerness);
680 return (sres==0 && mres==0)?0:-1;
685 template <
class StorageType,
class CalculationType>
691 if ((ires=this->_st.CalcStructureTensorValid(gradx, grady,
692 this->_sgxx, this->_sgxy,
694 BIASERR(
"error calculatimng structure tensor");
700 meres = _ComputeCornerness(cornerness);
702 return (ires==0 && meres==0)?0:-1;
706 template <
class StorageType,
class CalculationType>
717 const int minx = tlx;
718 const int miny = tly;
719 const int maxx = brx;
720 const int maxy = bry;
722 register int x, y, lx, ly;
724 register const double me = (double)this->_MinCornerness;
726 this->_FeatList.clear();
727 this->_FeatList.reserve((brx-tlx)*(bry-tly));
734 for (y=miny; y<maxy; y++){
735 for (x=minx; x<maxx; x++){
738 const int hws_local_max = 1;
739 bool is_local_maximum =
true;
740 for (ly=y-hws_local_max; ly<=y+hws_local_max && is_local_maximum; ly++){
741 if (ly<miny || ly>=maxy)
continue;
742 for (lx=x-hws_local_max; lx<=x+hws_local_max && is_local_maximum; lx++){
743 if (lx<minx || lx>=maxx)
continue;
744 if (lx==x && ly==y)
continue;
745 if (ev <= ime[ly][lx])
746 is_local_maximum =
false;
749 if (is_local_maximum){
752 fp.val=(ev>(double)INT_MAX)?(INT_MAX):((
int)ev);
753 this->_FeatList.push_back(fp);
762 for (y=miny; y<maxy; y++){
763 for (x=minx; x<maxx; x++){
765 if (ev>me && roi->
IsInROI(x,y)){
766 const int hws_local_max = 1;
767 bool is_local_maximum =
true;
768 for (ly=y-hws_local_max; ly<=y+hws_local_max && is_local_maximum; ly++){
769 if (ly<miny || ly>=maxy)
continue;
770 for (lx=x-hws_local_max; lx<=x+hws_local_max && is_local_maximum; lx++){
771 if (lx<minx || lx>=maxx)
continue;
772 if (lx==x && ly==y)
continue;
773 if (!roi->
IsInROI(lx,ly))
continue;
774 if (ev <= ime[ly][lx])
775 is_local_maximum =
false;
778 if (is_local_maximum){
781 fp.val=(ev>(double)INT_MAX)?(INT_MAX):((
int)ev);
782 this->_FeatList.push_back(fp);
789 BIASERR(
"Unknown ROI type " << roi->
GetROIType() <<
" found!");
803 #ifdef BUILD_IMAGE_INT
807 #ifdef BUILD_IMAGE_CHAR
811 #ifdef BUILD_IMAGE_SHORT
814 #ifdef BUILD_IMAGE_USHORT
815 #ifdef BUILD_IMAGE_INT
821 #ifdef BUILD_IMAGE_UINT
824 #ifdef BUILD_IMAGE_DOUBLE
void Release()
reimplemented from ImageBase
bool _RefineCornerPosition(int col, int row, double &x, double &y, QUAL &SubPixelCornerness)
fit quadratic surface around (row;col) to find the maximum cornerness subpixel position and save it i...
int _MinDistance
minimal distance between points
int Invert(Matrix2x2< T > &result) const
analyticaly inverts matrix
class for handling different region of interest (ROI) representations...
enum ERoiType GetROIType() const
is the mask image valid?
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
void GetCorners(unsigned &UpperLeftX, unsigned &UpperLeftY, unsigned &LowerRightX, unsigned &LowerRightY) const
Return the region of interest, by saving the coordinates within the variables defined by the paramete...
bool _DoRefinement
tells whether subpixel refinement should be done
bool IsEmpty() const
check if ImageData_ points to allocated image buffer or not
virtual int _ComputeCornerness(Image< CalculationType > &Cornerness)
the main function which should be overwritten in derived classes, no implementation in this class ...
void _ExtractLocalMaxima(const Image< CalculationType > &Cornerness)
extracts the local maxima checking a 3x3 region into _FeatList
virtual int DetectFromCornerness(const Image< CalculationType > &cornerness, std::vector< HomgPoint2D > &p, std::vector< QUAL > &quality)
detect corners avoiding double calculation of cornerness image
unsigned GetWidth() const
width capacity of roi (image width)
virtual parent class for API definition of all (future) filters
double _MinCornerness
threshold to accept a feature
virtual ~CornerDetectorGradient()
unsigned int GetWidth() const
void GetCov_(const std::vector< HomgPoint2D > &p, std::vector< Matrix2x2< double > > &cov, const double noise=5) const
returns the inverse structure tensor, multiplied by noise^2
bool ** _FeatureMap
array used to enforce a min distance / neighbor suppression
void _AllocInternalMem(const int width, const int height)
allocates _Cornerness , _sgxx, _sgxy and _sgyy
ROI * GetROI()
Returns a pointer to the roi object.
static int ConvertST(const BIAS::ImageBase &source, BIAS::ImageBase &dest, ImageBase::EStorageType targetST)
Function to convert the storage type of images e.g.
bool IsInROI(const double &x, const double &y) const
ROI check if pixel position is inside the ROI.
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
unsigned int GetHeight() const
The image template class for specific storage types.
void _FillFeatureMap(std::vector< HomgPoint2D > &p)
fills feature map with every point from p which isnt at infinity
bool SamePixelAndChannelCount(const ImageBase &Image) const
checks if data area has same "size" as Image of other type
static int Save(const std::string &filename, const ImageBase &img, const enum TFileFormat FileFormat=FF_auto, const bool sync=BIAS_DEFAULT_SYNC, const int c_jpeg_quality=BIAS_DEFAULT_IMAGE_QUALITY, const bool forceNewID=BIAS_DEFAULT_FORCENEWID, const bool &writeMetaData=true)
Export image as file using extrnal libs.
double GetRealTime() const
return real time (=wall time clock) in usec JW For Win32: real-time is measured differently from user...
void Init(unsigned int Width, unsigned int Height, unsigned int channels=1, enum EStorageType storageType=ST_unsignedchar, const bool interleaved=true)
calls Init from ImageBase storageType is ignored, just dummy argument
purly virtual interface defining class for corner detectors
enum EStorageType GetStorageType() const
base class for all gradient based corner detectors
unsigned GetHeight() const
height capacity of roi (image height)
int _EnforceMinimumDistance(std::vector< HomgPoint2D > &p, std::vector< QUAL > &quality)
selects features sorted by quality from _FeatList such that they have a min distance as given in the ...
void Set(const HOMGPOINT2D_TYPE &x, const HOMGPOINT2D_TYPE &y)
set elementwise with given 2 euclidean scalar values.
int _CalcCornerness(const Image< StorageType > &src, Image< CalculationType > &cornerness)
calculates the cornerness using the src image
class TimeMeasure contains functions for timing real time and cpu time.
virtual int Cornerness(const Image< CalculationType > &gradx, const Image< CalculationType > &grady, Image< CalculationType > &cornerness)
calculates the cornerness using the gradients gx and gy
void _DeleteInternalMem()
frees _Cornerness, _sgxx, _sgxy and _sgyy
virtual int Detect(const Image< StorageType > &image, std::vector< HomgPoint2D > &p, std::vector< QUAL > &quality, std::vector< Matrix2x2< double > > *cov=NULL)
detect corners in a grey image
const StorageType ** GetImageDataArray() const
overloaded GetImageDataArray() from ImageBase