examples/surfNaive/surf.cpp

00001 /*
00002  *      Copyright (C) 2007. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00021 
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <iostream>
00026 #include <QDebug>
00027 #include <QVector>
00028 #include <QMap>
00029 
00030 #include <qvcore/qvapplication.h>
00031 #include <qvcameras/qvmplayercamera.h>
00032 #include <qvgui/qvgui.h>
00033 #include <qvip/qvipp/qvipp.h>
00034 #include <qvip/qvpolyline.h>
00035 #include <qvdta/qvdta.h>
00036 
00037 #ifndef DOXYGEN_IGNORE_THIS
00038 
00039 
00040 
00041 QList<QPoint> MaxIPP( QVImage<sFloat> &image1, QVImage<sFloat> &image2, QVImage<sFloat> &image3, float threshold)  // blockSize(3x3x3) = 3
00042 { 
00043   
00044   QList<QPoint> listMax;
00045   float max1, min1, aux; int xmax, ymax;
00046   float max2, min2;
00047   float max3, min3;
00048   float max, min;
00049   QRect image1ROI=image1.getROI();
00050   QRect image2ROI=image2.getROI();
00051   QRect image3ROI=image3.getROI();
00052 
00053   int rows=image3.getRows(), cols=image3.getCols();
00054   QVImage<sFloat> imageMax1(cols,rows), imageMax2(cols,rows), imageMax3(cols,rows);
00055   int beginx=image3.getROI().x(), beginy=image3.getROI().y();
00056   int endx=image3.getROI().width()+beginx, endy=image3.getROI().height()+beginy;
00057   imageMax1.setMarginROI(beginx);
00058   imageMax2.setMarginROI(beginx);
00059   imageMax3.setMarginROI(beginx);
00060 
00061   image1.setMarginROI(beginx);
00062   imageMax1.setMarginROI(beginx);
00063   FilterMax(image1,imageMax1,3,3,QPoint(beginx+1,beginy+1));
00064   image2.setMarginROI(beginx);
00065   imageMax2.setMarginROI(beginx);
00066   FilterMax(image2,imageMax2,3,3,QPoint(beginx+1,beginy+1));
00067   imageMax3.setMarginROI(beginx);
00068   FilterMax(image3,imageMax3,3,3,QPoint(beginx+1,beginy+1));
00069 
00070   // dejamos los ROI como estaban
00071   image1.setROI(image1ROI);
00072   image2.setROI(image2ROI);
00073   image3.setROI(image3ROI);
00074 
00075 
00076   // aqui hay que examinar los tres maximos para encontrar el maximo vertical y marcarlo en imageMax2
00077 
00078   QVIMAGE_INIT_READ(sFloat,imageMax1);
00079   QVIMAGE_INIT_WRITE(sFloat,imageMax2)
00080   QVIMAGE_INIT_READ(sFloat,imageMax3);
00081   QVIMAGE_INIT_READ(sFloat,image2);
00082 
00083   QMap<sFloat,QPoint> listMap;
00084   sFloat faux, fmaxvalue=-1e30, fminvalue=1e30;
00085   for(int i=beginx; i<endx; ++i)
00086   {  for(int j=beginy; j<endy; ++j)
00087      { faux = QVIMAGE_PIXEL(imageMax2,i,j,0);
00088        if ((faux == QVIMAGE_PIXEL(image2,i,j,0)) && (faux > QVIMAGE_PIXEL(imageMax1,i,j,0)) && (faux > QVIMAGE_PIXEL(imageMax3,i,j,0)))
00089          {  listMap.insertMulti(faux,QPoint(i,j));
00090          }
00091      }
00092   }
00093  QList<QPoint> valuesList = listMap.values();
00094  QList<QPoint> listFiltered;
00095  int miThreshold = threshold * valuesList.size();  // threshold es el % de elementos a mostrar
00096  for(int i=valuesList.size()-miThreshold; i<valuesList.size(); ++i)
00097    listFiltered.append(valuesList[i]);
00098  return(listFiltered);
00099 }
00100 
00101 
00102 
00103 float applyDxx(const QVImage<sFloat> &integralImage, int x, int y, int octave, int scale) 
00104         { 
00105         // las octavas comienzan en 0 y el incremento se multiplica por 2^octava antes de usarse
00106         // dim = dim + scale*(6 << octave)
00107         int w = 5 + 4*octave + scale*(4 << octave);  // ancho de la máscara de 9x9 (5x3) e incremento de ese ancho en cada escala (4 pixels)
00108         int h = 3 + 2*octave + scale*(2 << octave);  // alto de la máscara de 9x9 (5x3) e incremento de ese alto en cada escala (2 pixels)
00109 
00110         float a,b,c,d,e,f,g,hh;
00111         QVIMAGE_INIT_READ(sFloat,integralImage);
00112         a = QVIMAGE_PIXEL(integralImage,x-h/2-h,  y-w/2,0);
00113         b = QVIMAGE_PIXEL(integralImage,x-h/2,    y-w/2,0);
00114         c = QVIMAGE_PIXEL(integralImage,x+h/2+1,  y-w/2,0);
00115         d = QVIMAGE_PIXEL(integralImage,x+h+h/2+1,    y-w/2,0);
00116         e = QVIMAGE_PIXEL(integralImage,x-h/2-h,  y+w/2+1,0);
00117         f = QVIMAGE_PIXEL(integralImage,x-h/2,  y+w/2+1,0);
00118         g = QVIMAGE_PIXEL(integralImage,x+h/2+1,y+w/2+1,0);
00119         hh = QVIMAGE_PIXEL(integralImage,x+h/2+h+1,y+w/2+1,0);
00120 
00121         return((a+hh-d-e+3*(f+c-b-g))/(w*h));
00122         }
00123 
00124 float applyDyy(const QVImage<sFloat> &integralImage, int x, int y, int octave, int scale) 
00125         { 
00126         // las octavas comienzan en 0 y el incremento se multiplica por 2^octava antes de usarse
00127         // dim = dim + scale*(6 << octave)
00128         int w = 5 + 4*octave + scale*(4 << octave);  // ancho de la máscara de 9x9 (5x3) e incremento de ese ancho en cada escala (4 pixels)
00129         int h = 3 + 2*octave + scale*(2 << octave);  // alto de la máscara de 9x9 (5x3) e incremento de ese alto en cada escala (2 pixels)
00130         float a,b,c,d,e,f,g,hh;
00131         QVIMAGE_INIT_READ(sFloat,integralImage);
00132         a = QVIMAGE_PIXEL(integralImage,x-w/2,y-h-h/2,0);
00133         c = QVIMAGE_PIXEL(integralImage,x-w/2,y-h/2,0);
00134         e = QVIMAGE_PIXEL(integralImage,x-w/2,y+h/2+1,0);
00135         g = QVIMAGE_PIXEL(integralImage,x-w/2,y+h+h/2+1,0);
00136         b = QVIMAGE_PIXEL(integralImage,x+w/2+1,y-h-h/2,0);
00137         d = QVIMAGE_PIXEL(integralImage,x+w/2+1,y-h/2,0);
00138         f = QVIMAGE_PIXEL(integralImage,x+w/2+1,y+h/2+1,0);
00139         hh = QVIMAGE_PIXEL(integralImage,x+w/2+1,y+h+h/2+1,0);
00140 
00141         return((a-b+hh-g+3*(d+e-c-f))/(w*h));
00142 
00143         }
00144 
00145 float applyDxy(const QVImage<sFloat> &integralImage, int x, int y, int octave, int scale) 
00146         { 
00147         // las octavas comienzan en 0 y el incremento se multiplica por 2^octava antes de usarse
00148         // dim = dim + scale*(6 << octave)
00149         int w = 3 + 2*octave + scale*(2 << octave);  // ancho de la máscara de 9x9 (5x3) e incremento de ese ancho en cada escala (4 pixels)
00150         int h = 3 + 2*octave + scale*(2 << octave);  // alto de la máscara de 9x9 (5x3) e incremento de ese alto en cada escala (2 pixels)
00151         float a,b,c,d,e,f,g,hh,i,j,k,l,m,n,o,p;
00152         QVIMAGE_INIT_READ(sFloat,integralImage);
00153         a = QVIMAGE_PIXEL(integralImage,x-w,y-h,0);
00154         c = QVIMAGE_PIXEL(integralImage,x-w,y,0);
00155         i = QVIMAGE_PIXEL(integralImage,x-w,y+1,0);
00156         k = QVIMAGE_PIXEL(integralImage,x-w,y+h+1,0);
00157         b = QVIMAGE_PIXEL(integralImage,x,y-h,0);
00158         d = QVIMAGE_PIXEL(integralImage,x,y,0);
00159         j = QVIMAGE_PIXEL(integralImage,x,y+1,0);
00160         l = QVIMAGE_PIXEL(integralImage,x,y+h+1,0);
00161         e = QVIMAGE_PIXEL(integralImage,x+1,y-h,0);
00162         g = QVIMAGE_PIXEL(integralImage,x+1,y,0);
00163         m = QVIMAGE_PIXEL(integralImage,x+1,y+1,0);
00164         o = QVIMAGE_PIXEL(integralImage,x+1,y+h+1,0);
00165         f = QVIMAGE_PIXEL(integralImage,x+w+1,y-h,0);
00166         hh = QVIMAGE_PIXEL(integralImage,x+w+1,y,0);
00167         n = QVIMAGE_PIXEL(integralImage,x+w+1,y+1,0);
00168         p = QVIMAGE_PIXEL(integralImage,x+w+1,y+h+1,0);
00169 
00170         return(((a+d-b-c)+(m+p-n-o)-(e+hh-f-g)-(i+l-j-k))/(w*h));
00171         }
00172 
00173 
00174 
00175 void detHessian(const QVImage<sFloat> &integral, QVImage<sFloat> &hessian, int octave, int scale)
00176 {  // OJO. Se aplica la máscara dando saltos dependiendo de la octava en la que estemos. step=2^octava.
00177         QVIMAGE_INIT_WRITE(sFloat,hessian);
00178         sFloat det;
00179         int cols = integral.getCols(), rows = integral.getRows();
00180         int sizeMask=9 + 6*octave + scale*(6 << octave);
00181         int step= 1 << octave;
00182         int colsHessian = cols/step, rowsHessian = rows/step;
00183         int marginHessian = ((sizeMask)/2)/step+1; // Obtenemos un margen para el ROI de la imagen del hessiano.
00184         int marginImage = marginHessian*step;  // Obtenemos un margen para el ROI de la imagen integral.
00185         for(int j=marginImage,jh=marginHessian; j<rows-marginImage;j+=step,++jh) //j=j+(1<<octave))
00186                 for(int i=marginImage,ih=marginHessian; i<cols-marginImage;i+=step,++ih) // i=i+(1<<octave))
00187                         { const float Dxy_apply=applyDxy(integral,i,j,octave,scale);
00188                         det = ( applyDxx(integral,i,j,octave,scale)
00189                                 *
00190                                 applyDyy(integral,i,j,octave,scale)
00191                                 -0.81*Dxy_apply*Dxy_apply
00192                                 );
00193                         det = (det < 0.0) ? 0.0 : det;
00194                         QVIMAGE_PIXEL(hessian,ih,jh,0) = det;
00195                         }
00196         hessian.setMarginROI(marginHessian);
00197         integral.setMarginROI(marginImage);
00198 }
00199 
00200 
00201 QList<QVPolyline> addBlobs(QList<QVPolyline> listBlobs, QList<QPoint> listPoints, int octave, int scale)
00202 { int radius=4+3*octave+scale*(3<<octave);
00203   int sizeMask=9 + 6*octave + scale*(6 << octave);
00204   int step= 1 << octave;
00205 
00206   for(QList<QPoint>::const_iterator i=listPoints.constBegin(); i!=listPoints.constEnd(); ++i)
00207         listBlobs.append(QVPolyline::ellipse(10,i->x()*step,i->y()*step,radius,radius,0));
00208   return(listBlobs);
00209 }
00210 
00211 QList<QVPolyline> getBlobs(QVImage<sFloat> &integralFloat, float THRESHOLD)
00212 {       int cols=integralFloat.getCols(), rows=integralFloat.getRows();
00213         QVImage<sFloat> approxHessian0[4]={QVImage<sFloat>(cols,rows),QVImage<sFloat>(cols,rows),
00214                                         QVImage<sFloat>(cols,rows),QVImage<sFloat>(cols,rows)};
00215         QVImage<sFloat> approxHessian1[4]={QVImage<sFloat>(cols/2,rows/2),QVImage<sFloat>(cols/2,rows/2),
00216                                         QVImage<sFloat>(cols/2,rows/2),QVImage<sFloat>(cols/2,rows/2)};
00217         QVImage<sFloat> approxHessian2[4]={QVImage<sFloat>(cols/4,rows/4),QVImage<sFloat>(cols/4,rows/4),
00218                                         QVImage<sFloat>(cols/4,rows/4),QVImage<sFloat>(cols/4,rows/4)};
00219         QList<QVPolyline> listBlobs;
00220 
00221         int oct=0;
00222         for(int sca=0; sca<4; ++sca)
00223           detHessian(integralFloat,approxHessian0[sca],oct,sca);
00224         listBlobs=addBlobs(listBlobs,MaxIPP(approxHessian0[0],approxHessian0[1],approxHessian0[2], THRESHOLD),oct,1);
00225         listBlobs=addBlobs(listBlobs,MaxIPP(approxHessian0[1],approxHessian0[2],approxHessian0[3], THRESHOLD),oct,2);
00226 
00227         oct=1;
00228         Resize(approxHessian0[1], approxHessian1[0],IPPI_INTER_NN);
00229         detHessian(integralFloat,approxHessian1[1],oct,1);
00230         detHessian(integralFloat,approxHessian1[2],oct,2);
00231         detHessian(integralFloat,approxHessian1[3],oct,3);
00232         listBlobs=addBlobs(listBlobs,MaxIPP(approxHessian1[0],approxHessian1[1],approxHessian1[2], THRESHOLD),oct,1);
00233         listBlobs=addBlobs(listBlobs,MaxIPP(approxHessian1[1],approxHessian1[2],approxHessian1[3], THRESHOLD),oct,2);
00234 
00235         oct=2;
00236         Resize(approxHessian1[1], approxHessian2[0],IPPI_INTER_NN);
00237         detHessian(integralFloat,approxHessian2[1],oct,1);
00238         detHessian(integralFloat,approxHessian2[2],oct,2);
00239         detHessian(integralFloat,approxHessian2[3],oct,3);
00240         listBlobs=addBlobs(listBlobs,MaxIPP(approxHessian2[0],approxHessian2[1],approxHessian2[2], THRESHOLD),oct,1);
00241         listBlobs=addBlobs(listBlobs,MaxIPP(approxHessian2[1],approxHessian2[2],approxHessian2[3], THRESHOLD),oct,2);
00242 
00243 
00244         return(listBlobs);
00245 }
00246 
00247 
00248 
00249 class MyWorker: public QVWorker
00250         {
00251         public:
00252                 MyWorker(QString name): QVWorker(name)
00253                         { 
00254                         addProperty<double>("Threshold", inputFlag, 1.0, "Threshold",0.01,1.0);
00255                         addProperty<bool>("Percentage",inputFlag,true);
00256                         addProperty< QVImage<uChar> >("Input image", inputFlag|outputFlag);
00257                         addProperty< QVImage<uChar> >("Output image 1", outputFlag);
00258                         addProperty< QList<QVPolyline> >("BlobsMax1",outputFlag);
00259                         }
00260 
00261                 void iterate()
00262                         {
00263                         sFloat THRESHOLD = getPropertyValue<sFloat>("Threshold");
00264                         QVImage<uChar> input0 = getPropertyValue< QVImage<uChar> >("Input image");
00265                         uInt rows = input0.getRows()*2, cols = input0.getCols()*2;
00266 
00267                         QVImage<uChar> input(cols, rows);
00268                         Resize(input0, input,IPPI_INTER_NN); // duplicamos el tamaño de la imagen
00269 
00270                         timeFlag("double size");
00271 
00272                         QVImage<sFloat> imageGaussian(cols,rows);  // aplicamos filtro gaussiano
00273                         FilterGauss(input,imageGaussian,3);
00274                         timeFlag("gaussian");
00275 
00276                         ++cols; ++rows;
00277                         QVImage<sFloat> integralFloat(cols,rows);  // Calculamos la integral
00278                         Integral(imageGaussian,integralFloat,0);
00279 
00280                         timeFlag("integral");
00281 
00282 
00283                         QList<QVPolyline> listBlobs1=getBlobs(integralFloat,THRESHOLD);
00284 
00285                         timeFlag("hessiano");
00286 
00287 
00288                         setPropertyValue< QVImage<uChar> >("Output image 1", imageGaussian);
00289                         setPropertyValue< QList<QVPolyline > >("BlobsMax1",listBlobs1);
00290                         timeFlag("shows");
00291 
00292                         }
00293         };
00294 
00295 int main(int argc, char *argv[])
00296         {   ippSetNumThreads(1);
00297         QVApplication app(argc, argv,
00298                 "Example program for QVision library. It is an implementation of the SURF algorithm."
00299                 );
00300 
00301         QVMPlayerCamera camera1("Video 1");
00302         MyWorker worker("Surf Worker");
00303 
00304         camera1.link(&worker,"Input image");
00305 
00306         QVGUI interface;
00307 
00308 
00309         QVImageCanvas imageCanvas("Output image 1");
00310         imageCanvas.linkProperty(worker,"Output image 1");
00311         imageCanvas.linkProperty(worker,"BlobsMax1", Qt::green);
00312 
00313         return app.exec();
00314         }
00315 
00316 #endif

Generated on Thu Jul 17 17:23:27 2008 for QVision by  doxygen 1.5.3