00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00035 #ifndef DOXYGEN_IGNORE_THIS // Do not generate any doxygen documentation from here on.
00036 
00037 
00038 
00039 
00040 #include <QVApplication>
00041 
00042 
00043 #include <QVVideoReaderBlock>
00044 
00045 
00046 #include <QVDefaultGUI>
00047 #include <QVImageCanvas>
00048 #include <QVFunctionPlot>
00049 
00050 
00051 #include <qvipp.h>
00052 #include <qvip.h>
00053 #include <qvmatrixalgebra.h>
00054 
00055 
00056 
00057 
00058 typedef QMap<sFloat,QPointF> TMapFloatPoints;
00059 Q_DECLARE_METATYPE(TMapFloatPoints);
00060 typedef QList<QLineF> TListLines;
00061 Q_DECLARE_METATYPE(TListLines);
00062 
00063 
00064 
00065 class ResponseImageCanvas : public QVImageCanvas {
00066     public:
00067         ResponseImageCanvas(QString name) : QVImageCanvas(name) {
00068             
00069             addProperty<QMap<sFloat,QPointF> >("Max points", inputFlag);
00070         } ;
00071         void custom_viewer() {
00072             
00073             QMap<sFloat,QPointF> maxPoints = getPropertyValue< QMap<sFloat,QPointF> >("Max points");
00074 
00075             
00076             getQVPainter()->setPen(Qt::yellow);
00077             QMap<sFloat, QPointF>::iterator i = maxPoints.begin();
00078             int order = 1;
00079             while (i != maxPoints.end()) {
00080                 QPointF point = i.value() + QPointF(0.5,0.5);
00081                 sFloat response = i.key();
00082                 getQVPainter()->drawLine(point+QPointF(-2,0),point+QPointF(+2,0));
00083                 getQVPainter()->drawLine(point+QPointF(0,-2),point+QPointF(0,+2));
00084                 getQVPainter()->drawTextUnscaled(point, QString("%1(%2)").arg(order).arg(-response));
00085                 i++;
00086                 order++;
00087             }
00088         };
00089 };
00090 
00091 class CannyImageCanvas : public QVImageCanvas {
00092     public:
00093         CannyImageCanvas(QString name) : QVImageCanvas(name) {
00094             addProperty<int>("lr", inputFlag);
00095             addProperty< QList<QLineF> >("Regression lines", inputFlag);
00096         } ;
00097         void custom_viewer(){
00098             QVPolyline poly = getPropertyValue<QVPolyline>("poly select");
00099             QList<QLineF> regLines = getPropertyValue< QList<QLineF> >("Regression lines");
00100             const int lr = getPropertyValue<int>("lr");
00101             getQVPainter()->setPen(Qt::green);
00102             foreach(QPoint point,poly)
00103                 getQVPainter()->drawRect(point.x()-lr,point.y()-lr,2*lr+1,2*lr+1);
00104             getQVPainter()->setPen(Qt::red);
00105             foreach(QLineF line,regLines)
00106                 getQVPainter()->drawLine(line.translated(0.5,0.5));
00107         };
00108 };
00109 
00110 
00111 
00112 
00113 
00114 
00115 
00116 QMap<sFloat,QPointF>  findMaximalPoints(const QVImage<sFloat> &image, const double threshold, const int windowRadius)
00117 {
00118     QVImage<sFloat> maxImage;
00119     FilterMax(image, maxImage,
00120               QSize(2*windowRadius+1,2*windowRadius+1), 
00121               QPoint(0,0), 
00122               image.getROI().topLeft() + QPoint(windowRadius, windowRadius) 
00123               );
00124 
00125     const QRect ROI = maxImage.getROI();
00126     const int maxStep = maxImage.getStep() / sizeof(sFloat),
00127               imageStep = image.getStep() / sizeof(sFloat);
00128 
00129     QMap<sFloat, QPointF> sortedPoints;
00130 
00131     sFloat *actualPtr = (sFloat *) image.getReadData() + (imageStep + 1) * windowRadius;
00132     sFloat *maxPtr = (sFloat *) maxImage.getReadData() + (maxStep + 1) * windowRadius;
00133 
00134     for(int j = 0; j < ROI.height(); j++, actualPtr += imageStep, maxPtr += maxStep)
00135         for(int i = 0; i < ROI.width(); i++)
00136             if ( (actualPtr[i] >= threshold) and (maxPtr[i] == actualPtr[i]) )
00137                 
00138                 sortedPoints.insertMulti(-actualPtr[i], QPointF(i+ROI.x(), j+ROI.y()));
00139 
00140     return sortedPoints;
00141 }
00142 
00143 QVImage<uChar> getCannyImage(const QVImage<uChar> imageGray, QRect CannyROI, const double thrLow, const double thrHigh)
00144 {
00145     uInt cols = imageGray.getCols(), rows = imageGray.getRows();
00146     QVImage<sFloat> imageFloat(imageGray),dX(cols,rows), dY(cols,rows), dXNeg(cols,rows);
00147     QVImage<uChar> imageCanny(cols,rows), buffer;
00148 
00149     QVImage<uChar> imageDest(imageGray);
00150     imageFloat.setROI(CannyROI);
00151     imageCanny.setROI(CannyROI);
00152     FilterSobelHorizMask(imageFloat, dY, ippMskSize3x3);
00153     FilterSobelVertMask(imageFloat, dX, ippMskSize3x3);
00154     MulC(dX, -1, dXNeg);
00155     CannyGetSize(imageCanny, buffer);
00156     Canny(dXNeg, dY, imageCanny, qMin(thrLow,thrHigh) , thrHigh, buffer);
00157     Copy(imageCanny, imageDest, QPoint(CannyROI.x(),CannyROI.y()));
00158     imageDest.resetROI();
00159 
00160     return imageDest;
00161 }
00162 
00163 QList<QLineF> getRegressionLines(const QVImage<uChar> &imageCanny, const QRect &CannyROI,
00164                                  const QVPolyline &CannySelPoints, int lr)
00165 {
00166     QList<QLineF> regLines;
00167     for(int k=0;k<CannySelPoints.size();k++) {
00168         QPointF mean(0,0);
00169         QList<QPointF> listPoints;
00170         QPointF point = CannySelPoints[k];
00171         for(int j=point.y()-lr;j<=point.y()+lr;j++)
00172             for(int i=point.x()-lr;i<=point.x()+lr;i++)
00173                 if(CannyROI.contains(i,j))
00174                     if(imageCanny(i,j) == 255) {
00175                         listPoints.append(QPointF(i,j));
00176                         mean += QPointF(i,j);
00177                     }
00178         const int n = listPoints.size();
00179         if(n >= 2) { 
00180             mean /= n;
00181             QVMatrix M(n,2); 
00182             for(int k=0;k<n;k++) {
00183                 M(k,0) = listPoints[k].x() - mean.x();
00184                 M(k,1) = listPoints[k].y() - mean.y();
00185             }
00186             QVMatrix covMat = M.transpose()*M / n, Q;
00187             QVVector lambda;
00188             eigenDecomposition(covMat,lambda,Q);
00189             QVVector mainEigenvector = Q.getCol(0);
00190             QPointF pointEV(mainEigenvector(0),mainEigenvector(1));
00191             regLines.append(QLineF(mean+lr*pointEV,mean-lr*pointEV));
00192         }
00193     }
00194     return regLines;
00195 }
00196 
00197 
00198 
00199 
00200 
00201 
00202 
00203 
00204 
00205 
00206 class MyProcessingBlock: public QVProcessingBlock {
00207     private:
00208         QVIndexedStringList filterList; 
00209 
00210     public:
00211         MyProcessingBlock(QString name = QString()): QVProcessingBlock(name) { 
00212             
00213             
00214             addProperty<bool>("box", inputFlag, false, "Apply box filter");
00215             addProperty<int>("boxrows", inputFlag, 3, "Row box size", 1, 5);
00216             addProperty<int>("boxcols", inputFlag, 3, "Col box size", 1, 5);
00217             addProperty<double>("thhi", inputFlag, 150, "High threshold for Canny operator", 50, 1000);
00218             addProperty<double>("thlo", inputFlag, 50, "Low threshold for Canny operator", 10, 500);
00219             addProperty<double>("thMax", inputFlag, 50, "Max threshold for Hessian/Harris", 1.0, 255.0);
00220             addProperty<int>("lr", inputFlag|outputFlag, 5, "Radius for Canny regression lines", 2, 20);
00221 
00222             
00223             filterList.append("None");
00224             filterList.append("Hessian");
00225             filterList.append("Harris");
00226             
00227             addProperty<QVIndexedStringList>("Filter", inputFlag, filterList, "Apply corner response filter");
00228 
00229             
00230             addProperty<QRect>("CannyROI", inputFlag);
00231             addProperty<QVPolyline>("CannySelPoints", inputFlag);
00232 
00233             
00234             addProperty< QVImage<uChar,3> >("Input RGB image", inputFlag|outputFlag);
00235             addProperty< QVImage<uChar,1> >("Output image byte", outputFlag);
00236             addProperty< QVImage<sFloat,1> >("Output image float", outputFlag);
00237 
00238             
00239             addProperty< QMap<sFloat,QPointF> >("Max points", outputFlag);
00240             addProperty< QList<QLineF> >("Regression lines", outputFlag);
00241             addProperty< QList<double> >("R histogram", outputFlag);
00242             addProperty< QList<double> >("G histogram", outputFlag);
00243             addProperty< QList<double> >("B histogram", outputFlag);
00244 
00245             
00246             addTrigger("Snapshot");
00247         }
00248 
00249         
00250         void processTrigger(const QString triggerName) {
00251             if(triggerName == "Snapshot") {
00252                 QVImage<uchar,3> imageRGB = getPropertyValue< QVImage<uChar,3> >("Input RGB image");
00253                 QImage qimage = imageRGB;
00254                 qimage.save("snapshot.jpg");
00255             } 
00256         }
00257 
00258         
00259         void iterate() {
00260             
00261             QVImage<uchar,3> imageRGB = getPropertyValue< QVImage<uChar,3> >("Input RGB image");
00262 
00263             
00264             QVImage<uchar,1> imageGray = imageRGB;
00265 
00266             
00267             const QVIndexedStringList selectedFilter = getPropertyValue<QVIndexedStringList>("Filter");
00268             const bool applyBox = getPropertyValue<bool>("box");
00269             const int boxRows = getPropertyValue<int>("boxrows");
00270             const int boxCols = getPropertyValue<int>("boxcols");
00271             const double thrLow = getPropertyValue<double>("thhi");
00272             const double thrHigh = getPropertyValue<double>("thlo");
00273             const double thMax = getPropertyValue<double>("thMax");
00274             const int lr = getPropertyValue<int>("lr");
00275 
00276             
00277             QRect CannyROI = getPropertyValue<QRect>("CannyROI");
00278             QVPolyline CannySelPoints = getPropertyValue<QVPolyline>("CannySelPoints");
00279 
00280             
00281             timeFlag("Read input parameters");
00282 
00283             
00284             uInt cols = imageGray.getCols(), rows = imageGray.getRows();
00285 
00286             
00287             bool computeCanny = true;
00288             if (CannyROI == QRect()) {
00289                 
00290                 CannyROI = imageGray.getROI();
00291             } else if(CannyROI.height()>=5 and CannyROI.width()>=5) {
00292                 
00293                 if(CannyROI.bottom() == static_cast<int>(rows)-1)
00294                     CannyROI.setBottom(CannyROI.bottom()-1); 
00295             } else {
00296                 
00297                 computeCanny = false;
00298             }
00299 
00300             
00301             QVImage<uChar> imageCanny;
00302             if(computeCanny)
00303                 imageCanny = getCannyImage(imageGray,CannyROI,thrLow,thrHigh);
00304             else
00305                 imageCanny = imageGray;
00306 
00307             timeFlag("Canny operator");
00308 
00309             
00310             QList<QLineF> regLines = getRegressionLines(imageCanny,CannyROI,CannySelPoints,lr);
00311 
00312             timeFlag("Regression lines");
00313 
00314             
00315             if(applyBox) {
00316                 
00317                 QVImage<uChar> imageGray2(cols,rows);
00318                 FilterBox(imageGray, imageGray2, QSize(boxCols,boxRows),
00319                            QPoint(0,0), QPoint(boxCols/2,boxRows/2));
00320                 imageGray = imageGray2;
00321                 
00322                 imageGray.resetROI();
00323             }
00324             timeFlag("Box filter");
00325 
00326             
00327             QVImage<sFloat> imageFloat(cols,rows);
00328             int choice = selectedFilter.getIndex();
00329             switch (choice) {
00330                 case 0: { 
00331                     imageFloat = imageGray;
00332                     break;
00333                 }
00334                 case 1: { 
00335                     FilterHessianCornerResponseImage(imageGray, imageFloat,  QPoint(2,2));
00336                     break;
00337                 }
00338                 case 2: { 
00339                     FilterHarrisCornerResponseImage(imageGray, imageFloat);
00340                     
00341                     break;
00342                 }
00343             }
00344             timeFlag("Corner response filter");
00345 
00346             
00347             imageFloat.resetROI();
00348 
00349             
00350             FilterNormalize(imageFloat, imageFloat);
00351 
00352             timeFlag("Normalization");
00353 
00354             QMap<sFloat,QPointF> maxPoints;
00355             if(choice != 0) {
00356                 const int radius = 3;
00357                 maxPoints = findMaximalPoints(imageFloat, thMax, radius);
00358             }
00359             timeFlag("Get maximum response points");
00360 
00361             
00362             QVImage<uChar> selected;
00363             Copy(imageRGB,0,selected);
00364             const QVector<int> RHistogramInt = HistogramRange(selected);
00365             Copy(imageRGB,1,selected);
00366             const QVector<int> GHistogramInt = HistogramRange(selected);
00367             Copy(imageRGB,2,selected);
00368             const QVector<int> BHistogramInt = HistogramRange(selected);
00369 
00370             QList<double> RHistogram,GHistogram,BHistogram;
00371             foreach(int val,RHistogramInt)
00372                 RHistogram.append(val);
00373             foreach(int val,GHistogramInt)
00374                 GHistogram.append(val);
00375             foreach(int val,BHistogramInt)
00376                 BHistogram.append(val);
00377 
00378             timeFlag("Obtain RGB histograms");
00379 
00380             
00381             setPropertyValue< QVImage<uchar,1> >("Output image byte", imageCanny);
00382             setPropertyValue< QVImage<sFloat,1> >("Output image float", imageFloat);
00383             setPropertyValue< QMap<sFloat,QPointF> >("Max points", maxPoints);
00384             setPropertyValue< QList<QLineF> >("Regression lines", regLines);
00385             setPropertyValue< QList<double> >("R histogram", RHistogram);
00386             setPropertyValue< QList<double> >("G histogram", GHistogram);
00387             setPropertyValue< QList<double> >("B histogram", BHistogram);
00388 
00389             timeFlag("Write output parameters");
00390             }
00391         };
00392 
00393 
00394 
00395 
00396 
00397 
00398 
00399 
00400 int main(int argc, char *argv[]) {
00401     
00402     QVApplication app(argc, argv, "Example program for QVision library.");
00403 
00404     
00405     QVVideoReaderBlock camera("Video");
00406 
00407     
00408     MyProcessingBlock filterBlock("Image filter block");
00409 
00410     
00411     QVDefaultGUI interface;
00412     QVImageCanvas inputImage("Input RGB image");
00413     CannyImageCanvas outputImageByte("Output image byte");
00414     ResponseImageCanvas outputImageFloat("Output image float");
00415     QVFunctionPlot RGBHistogramPlot("RGB histogram");
00416 
00417     
00418     camera.linkProperty(&filterBlock,"Input RGB image");
00419 
00420     filterBlock.linkProperty("Input RGB image", inputImage);
00421     filterBlock.linkProperty("Output image byte", outputImageByte);
00422     filterBlock.linkProperty("Regression lines", outputImageByte, "Regression lines");
00423     filterBlock.linkProperty("Output image float", outputImageFloat);
00424     filterBlock.linkProperty("Max points", outputImageFloat,"Max points");
00425     filterBlock.linkProperty("lr",outputImageByte,"lr");
00426     filterBlock.linkProperty("R histogram",RGBHistogramPlot);
00427     filterBlock.linkProperty("G histogram",RGBHistogramPlot);
00428     filterBlock.linkProperty("B histogram",RGBHistogramPlot);
00429     outputImageByte.linkSelectedRectangle(filterBlock,"CannyROI");
00430     outputImageByte.linkSelectedPolyline(filterBlock,"CannySelPoints");
00431 
00432     
00433     return app.exec();
00434 }
00435 
00436 #endif
00437 
00438