From 81f98a0a858a060220dc40dfd7d3e1326b46ff2f Mon Sep 17 00:00:00 2001 From: Jordan Cheney Date: Tue, 17 Feb 2015 15:46:17 -0500 Subject: [PATCH] Finished reorg. Comments and fixes to follow --- openbr/plugins/classification/adaboost.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/ebif.cpp | 2 +- openbr/plugins/classification/forest.cpp | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/ipc2013.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/lda.cpp | 657 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/liblinear.cpp | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/mlp.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/nt4.cpp | 460 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/pp4.cpp | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/pp5.cpp | 596 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/svm.cpp | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/classification/turk.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/cluster/collectnn.cpp | 2 +- openbr/plugins/cluster/kmeans.cpp | 2 +- openbr/plugins/cluster/knn.cpp | 2 +- openbr/plugins/cluster/lognn.cpp | 2 +- openbr/plugins/cluster/randomcentroids.cpp | 2 +- openbr/plugins/cmake/eigen3.cmake | 9 +++++++++ openbr/plugins/cmake/ipc2013.cmake | 9 +++++++++ openbr/plugins/cmake/jni.cmake | 12 ++++++++++++ openbr/plugins/cmake/liblinear.cmake | 8 ++++++++ openbr/plugins/cmake/likely.cmake | 10 ++++++++++ openbr/plugins/cmake/mongoose.cmake | 8 ++++++++ openbr/plugins/cmake/network.cmake | 12 ++++++++++++ openbr/plugins/cmake/nt4.cmake | 9 +++++++++ openbr/plugins/cmake/pp4.cmake | 10 ++++++++++ openbr/plugins/cmake/pp5.cmake | 16 ++++++++++++++++ openbr/plugins/cmake/show.cmake | 3 +++ openbr/plugins/cmake/stasm4.cmake | 17 +++++++++++++++++ openbr/plugins/core/algorithms.cpp | 2 +- openbr/plugins/core/attributealgorithms.cpp | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/cache.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/contract.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/crossvalidate.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/discard.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/core/discardtemplates.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/core/distributetemplate.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/downsample.cpp | 119 ----------------------------------------------------------------------------------------------------------------------- openbr/plugins/core/downsampletraining.cpp | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/event.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/core/expand.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/first.cpp | 31 +++++++++++++++++++++++++++++++ openbr/plugins/core/fork.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/fte.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/gallerycompare.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/identity.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/core/independent.cpp | 2 +- openbr/plugins/core/jni.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/likely.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/loadstore.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/pipe.cpp | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/processwrapper.cpp | 661 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/progresscounter.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/registrar.cpp | 25 +++++++++++++++++++++++++ openbr/plugins/core/remove.cpp | 31 +++++++++++++++++++++++++++++++ openbr/plugins/core/rest.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/core/schrodinger.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/singleton.cpp | 2 +- openbr/plugins/core/stream.cpp | 1365 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/detection/cascade.cpp | 440 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/detection/eyes.cpp | 198 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/detection/keypointdetector.cpp | 54 ------------------------------------------------------ openbr/plugins/distance/L2.cpp | 2 +- openbr/plugins/distance/attribute.cpp | 2 +- openbr/plugins/distance/bayesianquantization.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/byteL1.cpp | 2 +- openbr/plugins/distance/crossvalidate.cpp | 28 ++++++++++++++++++++++++++++ openbr/plugins/distance/default.cpp | 2 +- openbr/plugins/distance/dist.cpp | 2 +- openbr/plugins/distance/filter.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/fuse.cpp | 2 +- openbr/plugins/distance/gallerycompare.cpp | 64 ---------------------------------------------------------------- openbr/plugins/distance/halfbyteL1.cpp | 2 +- openbr/plugins/distance/heatmap.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/identical.cpp | 2 +- openbr/plugins/distance/keypointmatcher.cpp | 2 +- openbr/plugins/distance/l1.cpp | 2 +- openbr/plugins/distance/matchprobability.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/metadata.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/neglogplusone.cpp | 2 +- openbr/plugins/distance/online.cpp | 2 +- openbr/plugins/distance/pipe.cpp | 2 +- openbr/plugins/distance/reject.cpp | 38 ++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/sum.cpp | 2 +- openbr/plugins/distance/turk.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/unit.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/zscore.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/eigen3.cmake | 7 ------- openbr/plugins/eigen3.cpp | 657 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/format/binary.cpp | 2 +- openbr/plugins/format/csv.cpp | 2 +- openbr/plugins/format/ebts.cpp | 2 +- openbr/plugins/format/lffs.cpp | 2 +- openbr/plugins/format/lmat.cpp | 37 +++++++++++++++++++++++++++++++++++++ openbr/plugins/format/mat.cpp | 2 +- openbr/plugins/format/mtx.cpp | 2 +- openbr/plugins/format/null.cpp | 2 +- openbr/plugins/format/post.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/raw.cpp | 2 +- openbr/plugins/format/scores.cpp | 2 +- openbr/plugins/format/url.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/video.cpp | 2 +- openbr/plugins/format/xml.cpp | 2 +- openbr/plugins/gallery.cpp | 2075 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/gallery/arff.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/binary.cpp | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/crawl.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/csv.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/db.cpp | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/default.cpp | 37 +++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/empty.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/fddb.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/flat.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/galleryutil.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/google.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/landmarks.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/lmat.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/matrix.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/mem.cpp | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/post.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/seq.cpp | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/stat.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/template.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/turk.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/txt.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/vbb.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/video.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gallery/xml.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui.cmake | 3 --- openbr/plugins/gui.cpp | 1096 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/gui/adjacentoverlay.cpp | 2 +- openbr/plugins/gui/draw.cpp | 2 +- openbr/plugins/gui/drawdelaunay.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/drawgridlines.cpp | 2 +- openbr/plugins/gui/drawopticalflow.cpp | 2 +- openbr/plugins/gui/drawpropertiespoint.cpp | 2 +- openbr/plugins/gui/drawpropertypoint.cpp | 2 +- openbr/plugins/gui/drawsegmentation.cpp | 2 +- openbr/plugins/gui/show.cpp | 1098 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/hash.cpp | 95 ----------------------------------------------------------------------------------------------- openbr/plugins/imgproc/abs.cpp | 2 +- openbr/plugins/imgproc/absdiff.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/affine.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/and.cpp | 28 ++++++++++++++++++++++++++++ openbr/plugins/imgproc/applymask.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/imgproc/bayesianquantization.cpp | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/binarize.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/blend.cpp | 2 +- openbr/plugins/imgproc/blur.cpp | 2 +- openbr/plugins/imgproc/byrow.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/imgproc/cat.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/catcols.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/catrows.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/center.cpp | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/contrasteq.cpp | 2 +- openbr/plugins/imgproc/crop.cpp | 2 +- openbr/plugins/imgproc/cropblack.cpp | 2 +- openbr/plugins/imgproc/cropsquare.cpp | 2 +- openbr/plugins/imgproc/cryptographichash.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/csdn.cpp | 2 +- openbr/plugins/imgproc/cvtcolor.cpp | 2 +- openbr/plugins/imgproc/cvtfloat.cpp | 2 +- openbr/plugins/imgproc/cvtuchar.cpp | 2 +- openbr/plugins/imgproc/denoising.cpp | 2 +- openbr/plugins/imgproc/discardalpha.cpp | 2 +- openbr/plugins/imgproc/div.cpp | 2 +- openbr/plugins/imgproc/dog.cpp | 2 +- openbr/plugins/imgproc/downsample.cpp | 38 ++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/dup.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/ensurechannels.cpp | 2 +- openbr/plugins/imgproc/equalizehist.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/flip.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/flood.cpp | 2 +- openbr/plugins/imgproc/gabor.cpp | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/gamma.cpp | 2 +- openbr/plugins/imgproc/gradient.cpp | 2 +- openbr/plugins/imgproc/gradientmask.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/group.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/hist.cpp | 2 +- openbr/plugins/imgproc/histbin.cpp | 2 +- openbr/plugins/imgproc/histeqquantization.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/hog.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/inpaint.cpp | 2 +- openbr/plugins/imgproc/integral.cpp | 2 +- openbr/plugins/imgproc/integralhist.cpp | 2 +- openbr/plugins/imgproc/integralsampler.cpp | 2 +- openbr/plugins/imgproc/kernelhash.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/keypointdescriptor.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/largestconvexarea.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/lbp.cpp | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/limitsize.cpp | 2 +- openbr/plugins/imgproc/ltp.cpp | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/madd.cpp | 2 +- openbr/plugins/imgproc/mask.cpp | 33 +++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/matstats.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/mean.cpp | 2 +- openbr/plugins/imgproc/meanfill.cpp | 2 +- openbr/plugins/imgproc/merge.cpp | 31 +++++++++++++++++++++++++++++++ openbr/plugins/imgproc/morph.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/multiscale.cpp | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/normalize.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/origlinearregression.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/pack.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/pow.cpp | 2 +- openbr/plugins/imgproc/productquantization.cpp | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/quantize.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rank.cpp | 2 +- openbr/plugins/imgproc/rectregions.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/recursiveintegralsampler.cpp | 2 +- openbr/plugins/imgproc/redlinearregression.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/reshape.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/resize.cpp | 2 +- openbr/plugins/imgproc/revertaffine.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rg.cpp | 2 +- openbr/plugins/imgproc/rndpoint.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rndregion.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rndrotate.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rndsample.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rndsubspace.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/roi.cpp | 2 +- openbr/plugins/imgproc/roifrompoints.cpp | 2 +- openbr/plugins/imgproc/rootnorm.cpp | 34 ++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rowwisemeancenter.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/scale.cpp | 2 +- openbr/plugins/imgproc/sift.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/skinmask.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/slidingwindow.cpp | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/splitchannels.cpp | 2 +- openbr/plugins/imgproc/subdivide.cpp | 2 +- openbr/plugins/imgproc/subtract.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/transpose.cpp | 20 ++++++++++++++++++++ openbr/plugins/imgproc/watershedsegmentation.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/decode.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/io/download.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/galleryoutput.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/incrementaloutput.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/open.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/out.cpp | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/print.cpp | 37 +++++++++++++++++++++++++++++++++++++ openbr/plugins/io/read.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/readlandmarks.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/io/write.cpp | 2 +- openbr/plugins/io/youtubefacesdb.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/ipc2013.cmake | 8 -------- openbr/plugins/ipc2013.cpp | 51 --------------------------------------------------- openbr/plugins/jni.cmake | 12 ------------ openbr/plugins/jni.cpp | 104 -------------------------------------------------------------------------------------------------------- openbr/plugins/landmarks.cpp | 434 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/lbp.cpp | 128 -------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/liblinear.cmake | 7 ------- openbr/plugins/liblinear.cpp | 206 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/likely.cmake | 7 ------- openbr/plugins/likely.cpp | 124 ---------------------------------------------------------------------------------------------------------------------------- openbr/plugins/ltp.cpp | 119 ----------------------------------------------------------------------------------------------------------------------- openbr/plugins/mask.cpp | 206 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/meta.cpp | 849 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/metadata/anonymizelandmarks.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/as.cpp | 28 ++++++++++++++++++++++++++++ openbr/plugins/metadata/cascade.cpp | 440 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/check.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/clearpoints.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/metadata/consolidatedetections.cpp | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/croprect.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/delaunay.cpp | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/expandrect.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/extractmetadata.cpp | 32 ++++++++++++++++++++++++++++++++ openbr/plugins/metadata/eyes.cpp | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/facefromeyes.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/fileexclusion.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/filterdupemetadata.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/grid.cpp | 2 +- openbr/plugins/metadata/groundtruth.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/hogpersondetector.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/ifmetadata.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/metadata/imposteruniquenessmeasure.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/json.cpp | 31 +++++++++++++++++++++++++++++++ openbr/plugins/metadata/keepmetadata.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/metadata/keypointdetector.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/keytorect.cpp | 2 +- openbr/plugins/metadata/mongoose.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/name.cpp | 28 ++++++++++++++++++++++++++++ openbr/plugins/metadata/namelandmarks.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/normalizepoints.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/pointdisplacement.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/pointstomatrix.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/procrustes.cpp | 3 ++- openbr/plugins/metadata/rectfrompoints.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/rectstotemplates.cpp | 38 ++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/regexproperty.cpp | 38 ++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/removemetadata.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/metadata/removetemplates.cpp | 34 ++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/rename.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/renamefirst.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/reorderpoints.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/restoremat.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/savemat.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/metadata/selectpoints.cpp | 34 ++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/setmetadata.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/metadata/setpointsinrect.cpp | 37 +++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/stasm4.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/stopwatch.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/misc.cpp | 1013 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/mongoose.cmake | 6 ------ openbr/plugins/mongoose.cpp | 69 --------------------------------------------------------------------- openbr/plugins/motion.cpp | 64 ---------------------------------------------------------------- openbr/plugins/nn.cpp | 140 -------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/normalize.cpp | 316 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/nt4.cmake | 8 -------- openbr/plugins/nt4.cpp | 460 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/output.cpp | 658 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/output/best.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/csv.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/output/default.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/output/empty.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/eval.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/heat.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/hist.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/melt.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/mtx.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/null.cpp | 25 +++++++++++++++++++++++++ openbr/plugins/output/rank.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/rr.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/tail.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/output/txt.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/plugins.cmake | 65 ++++++++++++++++++++++++++++++++++------------------------------- openbr/plugins/pp4.cmake | 9 --------- openbr/plugins/pp4.cpp | 369 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/pp5.cmake | 15 --------------- openbr/plugins/pp5.cpp | 596 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/process.cpp | 661 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/qtnetwork.cmake | 8 -------- openbr/plugins/qtnetwork.cpp | 207 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/quality.cpp | 449 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/quantize.cpp | 498 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/quantize2.cpp | 122 -------------------------------------------------------------------------------------------------------------------------- openbr/plugins/random.cpp | 276 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/reduce.cpp | 152 -------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/regions.cpp | 483 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/register.cpp | 224 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/representation/hogdescriptor.cpp | 39 --------------------------------------- openbr/plugins/representation/keypointdescriptor.cpp | 50 -------------------------------------------------- openbr/plugins/representation/siftdescriptor.cpp | 60 ------------------------------------------------------------ openbr/plugins/segmentation.cpp | 53 ----------------------------------------------------- openbr/plugins/slidingwindow.cpp | 529 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/stasm4.cmake | 15 --------------- openbr/plugins/stasm4.cpp | 203 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/stream.cpp | 1364 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/svm.cpp | 264 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/synthetic.cpp | 103 ------------------------------------------------------------------------------------------------------- openbr/plugins/template.cpp | 248 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/time.cpp | 89 ----------------------------------------------------------------------------------------- openbr/plugins/tree.cpp | 362 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/turk.cpp | 307 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/validate.cpp | 271 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/video/aggregate.cpp | 2 +- openbr/plugins/video/drop.cpp | 2 +- openbr/plugins/video/opticalflow.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/wavelet.cpp | 204 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/youtube.cpp | 50 -------------------------------------------------- 359 files changed, 19138 insertions(+), 18194 deletions(-) create mode 100644 openbr/plugins/classification/adaboost.cpp create mode 100644 openbr/plugins/classification/forest.cpp create mode 100644 openbr/plugins/classification/ipc2013.cpp create mode 100644 openbr/plugins/classification/lda.cpp create mode 100644 openbr/plugins/classification/liblinear.cpp create mode 100644 openbr/plugins/classification/mlp.cpp create mode 100644 openbr/plugins/classification/nt4.cpp create mode 100644 openbr/plugins/classification/pp4.cpp create mode 100644 openbr/plugins/classification/pp5.cpp create mode 100644 openbr/plugins/classification/svm.cpp create mode 100644 openbr/plugins/classification/turk.cpp create mode 100644 openbr/plugins/cmake/eigen3.cmake create mode 100644 openbr/plugins/cmake/ipc2013.cmake create mode 100644 openbr/plugins/cmake/jni.cmake create mode 100644 openbr/plugins/cmake/liblinear.cmake create mode 100644 openbr/plugins/cmake/likely.cmake create mode 100644 openbr/plugins/cmake/mongoose.cmake create mode 100644 openbr/plugins/cmake/network.cmake create mode 100644 openbr/plugins/cmake/nt4.cmake create mode 100644 openbr/plugins/cmake/pp4.cmake create mode 100644 openbr/plugins/cmake/pp5.cmake create mode 100644 openbr/plugins/cmake/show.cmake create mode 100644 openbr/plugins/cmake/stasm4.cmake create mode 100644 openbr/plugins/core/attributealgorithms.cpp create mode 100644 openbr/plugins/core/cache.cpp create mode 100644 openbr/plugins/core/contract.cpp create mode 100644 openbr/plugins/core/crossvalidate.cpp create mode 100644 openbr/plugins/core/discard.cpp create mode 100644 openbr/plugins/core/discardtemplates.cpp create mode 100644 openbr/plugins/core/distributetemplate.cpp delete mode 100644 openbr/plugins/core/downsample.cpp create mode 100644 openbr/plugins/core/downsampletraining.cpp create mode 100644 openbr/plugins/core/event.cpp create mode 100644 openbr/plugins/core/expand.cpp create mode 100644 openbr/plugins/core/first.cpp create mode 100644 openbr/plugins/core/fork.cpp create mode 100644 openbr/plugins/core/fte.cpp create mode 100644 openbr/plugins/core/gallerycompare.cpp create mode 100644 openbr/plugins/core/identity.cpp create mode 100644 openbr/plugins/core/jni.cpp create mode 100644 openbr/plugins/core/likely.cpp create mode 100644 openbr/plugins/core/loadstore.cpp create mode 100644 openbr/plugins/core/pipe.cpp create mode 100644 openbr/plugins/core/processwrapper.cpp create mode 100644 openbr/plugins/core/progresscounter.cpp create mode 100644 openbr/plugins/core/registrar.cpp create mode 100644 openbr/plugins/core/remove.cpp create mode 100644 openbr/plugins/core/rest.cpp create mode 100644 openbr/plugins/core/schrodinger.cpp create mode 100644 openbr/plugins/core/stream.cpp delete mode 100644 openbr/plugins/detection/cascade.cpp delete mode 100644 openbr/plugins/detection/eyes.cpp delete mode 100644 openbr/plugins/detection/keypointdetector.cpp create mode 100644 openbr/plugins/distance/bayesianquantization.cpp create mode 100644 openbr/plugins/distance/crossvalidate.cpp create mode 100644 openbr/plugins/distance/filter.cpp delete mode 100644 openbr/plugins/distance/gallerycompare.cpp create mode 100644 openbr/plugins/distance/heatmap.cpp create mode 100644 openbr/plugins/distance/matchprobability.cpp create mode 100644 openbr/plugins/distance/metadata.cpp create mode 100644 openbr/plugins/distance/reject.cpp create mode 100644 openbr/plugins/distance/turk.cpp create mode 100644 openbr/plugins/distance/unit.cpp create mode 100644 openbr/plugins/distance/zscore.cpp delete mode 100644 openbr/plugins/eigen3.cmake delete mode 100644 openbr/plugins/eigen3.cpp create mode 100644 openbr/plugins/format/lmat.cpp create mode 100644 openbr/plugins/format/post.cpp create mode 100644 openbr/plugins/format/url.cpp delete mode 100644 openbr/plugins/gallery.cpp create mode 100644 openbr/plugins/gallery/arff.cpp create mode 100644 openbr/plugins/gallery/binary.cpp create mode 100644 openbr/plugins/gallery/crawl.cpp create mode 100644 openbr/plugins/gallery/csv.cpp create mode 100644 openbr/plugins/gallery/db.cpp create mode 100644 openbr/plugins/gallery/default.cpp create mode 100644 openbr/plugins/gallery/empty.cpp create mode 100644 openbr/plugins/gallery/fddb.cpp create mode 100644 openbr/plugins/gallery/flat.cpp create mode 100644 openbr/plugins/gallery/galleryutil.cpp create mode 100644 openbr/plugins/gallery/google.cpp create mode 100644 openbr/plugins/gallery/landmarks.cpp create mode 100644 openbr/plugins/gallery/lmat.cpp create mode 100644 openbr/plugins/gallery/matrix.cpp create mode 100644 openbr/plugins/gallery/mem.cpp create mode 100644 openbr/plugins/gallery/post.cpp create mode 100644 openbr/plugins/gallery/seq.cpp create mode 100644 openbr/plugins/gallery/stat.cpp create mode 100644 openbr/plugins/gallery/template.cpp create mode 100644 openbr/plugins/gallery/turk.cpp create mode 100644 openbr/plugins/gallery/txt.cpp create mode 100644 openbr/plugins/gallery/vbb.cpp create mode 100644 openbr/plugins/gallery/video.cpp create mode 100644 openbr/plugins/gallery/xml.cpp delete mode 100644 openbr/plugins/gui.cmake delete mode 100644 openbr/plugins/gui.cpp create mode 100644 openbr/plugins/gui/drawdelaunay.cpp create mode 100644 openbr/plugins/gui/show.cpp delete mode 100644 openbr/plugins/hash.cpp create mode 100644 openbr/plugins/imgproc/absdiff.cpp create mode 100644 openbr/plugins/imgproc/affine.cpp create mode 100644 openbr/plugins/imgproc/and.cpp create mode 100644 openbr/plugins/imgproc/applymask.cpp create mode 100644 openbr/plugins/imgproc/bayesianquantization.cpp create mode 100644 openbr/plugins/imgproc/binarize.cpp create mode 100644 openbr/plugins/imgproc/byrow.cpp create mode 100644 openbr/plugins/imgproc/cat.cpp create mode 100644 openbr/plugins/imgproc/catcols.cpp create mode 100644 openbr/plugins/imgproc/catrows.cpp create mode 100644 openbr/plugins/imgproc/center.cpp create mode 100644 openbr/plugins/imgproc/cryptographichash.cpp create mode 100644 openbr/plugins/imgproc/downsample.cpp create mode 100644 openbr/plugins/imgproc/dup.cpp create mode 100644 openbr/plugins/imgproc/equalizehist.cpp create mode 100644 openbr/plugins/imgproc/flip.cpp create mode 100644 openbr/plugins/imgproc/gabor.cpp create mode 100644 openbr/plugins/imgproc/gradientmask.cpp create mode 100644 openbr/plugins/imgproc/group.cpp create mode 100644 openbr/plugins/imgproc/histeqquantization.cpp create mode 100644 openbr/plugins/imgproc/hog.cpp create mode 100644 openbr/plugins/imgproc/kernelhash.cpp create mode 100644 openbr/plugins/imgproc/keypointdescriptor.cpp create mode 100644 openbr/plugins/imgproc/largestconvexarea.cpp create mode 100644 openbr/plugins/imgproc/lbp.cpp create mode 100644 openbr/plugins/imgproc/ltp.cpp create mode 100644 openbr/plugins/imgproc/mask.cpp create mode 100644 openbr/plugins/imgproc/matstats.cpp create mode 100644 openbr/plugins/imgproc/merge.cpp create mode 100644 openbr/plugins/imgproc/morph.cpp create mode 100644 openbr/plugins/imgproc/multiscale.cpp create mode 100644 openbr/plugins/imgproc/normalize.cpp create mode 100644 openbr/plugins/imgproc/origlinearregression.cpp create mode 100644 openbr/plugins/imgproc/pack.cpp create mode 100644 openbr/plugins/imgproc/productquantization.cpp create mode 100644 openbr/plugins/imgproc/quantize.cpp create mode 100644 openbr/plugins/imgproc/rectregions.cpp create mode 100644 openbr/plugins/imgproc/redlinearregression.cpp create mode 100644 openbr/plugins/imgproc/reshape.cpp create mode 100644 openbr/plugins/imgproc/revertaffine.cpp create mode 100644 openbr/plugins/imgproc/rndpoint.cpp create mode 100644 openbr/plugins/imgproc/rndregion.cpp create mode 100644 openbr/plugins/imgproc/rndrotate.cpp create mode 100644 openbr/plugins/imgproc/rndsample.cpp create mode 100644 openbr/plugins/imgproc/rndsubspace.cpp create mode 100644 openbr/plugins/imgproc/rootnorm.cpp create mode 100644 openbr/plugins/imgproc/rowwisemeancenter.cpp create mode 100644 openbr/plugins/imgproc/sift.cpp create mode 100644 openbr/plugins/imgproc/skinmask.cpp create mode 100644 openbr/plugins/imgproc/slidingwindow.cpp create mode 100644 openbr/plugins/imgproc/subtract.cpp create mode 100644 openbr/plugins/imgproc/transpose.cpp create mode 100644 openbr/plugins/imgproc/watershedsegmentation.cpp create mode 100644 openbr/plugins/io/decode.cpp create mode 100644 openbr/plugins/io/download.cpp create mode 100644 openbr/plugins/io/galleryoutput.cpp create mode 100644 openbr/plugins/io/incrementaloutput.cpp create mode 100644 openbr/plugins/io/open.cpp create mode 100644 openbr/plugins/io/out.cpp create mode 100644 openbr/plugins/io/print.cpp create mode 100644 openbr/plugins/io/read.cpp create mode 100644 openbr/plugins/io/readlandmarks.cpp create mode 100644 openbr/plugins/io/youtubefacesdb.cpp delete mode 100644 openbr/plugins/ipc2013.cmake delete mode 100644 openbr/plugins/ipc2013.cpp delete mode 100644 openbr/plugins/jni.cmake delete mode 100644 openbr/plugins/jni.cpp delete mode 100644 openbr/plugins/landmarks.cpp delete mode 100644 openbr/plugins/lbp.cpp delete mode 100644 openbr/plugins/liblinear.cmake delete mode 100644 openbr/plugins/liblinear.cpp delete mode 100644 openbr/plugins/likely.cmake delete mode 100644 openbr/plugins/likely.cpp delete mode 100644 openbr/plugins/ltp.cpp delete mode 100644 openbr/plugins/mask.cpp delete mode 100644 openbr/plugins/meta.cpp create mode 100644 openbr/plugins/metadata/anonymizelandmarks.cpp create mode 100644 openbr/plugins/metadata/as.cpp create mode 100644 openbr/plugins/metadata/cascade.cpp create mode 100644 openbr/plugins/metadata/check.cpp create mode 100644 openbr/plugins/metadata/clearpoints.cpp create mode 100644 openbr/plugins/metadata/consolidatedetections.cpp create mode 100644 openbr/plugins/metadata/croprect.cpp create mode 100644 openbr/plugins/metadata/delaunay.cpp create mode 100644 openbr/plugins/metadata/expandrect.cpp create mode 100644 openbr/plugins/metadata/extractmetadata.cpp create mode 100644 openbr/plugins/metadata/eyes.cpp create mode 100644 openbr/plugins/metadata/facefromeyes.cpp create mode 100644 openbr/plugins/metadata/fileexclusion.cpp create mode 100644 openbr/plugins/metadata/filterdupemetadata.cpp create mode 100644 openbr/plugins/metadata/groundtruth.cpp create mode 100644 openbr/plugins/metadata/hogpersondetector.cpp create mode 100644 openbr/plugins/metadata/ifmetadata.cpp create mode 100644 openbr/plugins/metadata/imposteruniquenessmeasure.cpp create mode 100644 openbr/plugins/metadata/json.cpp create mode 100644 openbr/plugins/metadata/keepmetadata.cpp create mode 100644 openbr/plugins/metadata/keypointdetector.cpp create mode 100644 openbr/plugins/metadata/mongoose.cpp create mode 100644 openbr/plugins/metadata/name.cpp create mode 100644 openbr/plugins/metadata/namelandmarks.cpp create mode 100644 openbr/plugins/metadata/normalizepoints.cpp create mode 100644 openbr/plugins/metadata/pointdisplacement.cpp create mode 100644 openbr/plugins/metadata/pointstomatrix.cpp create mode 100644 openbr/plugins/metadata/rectfrompoints.cpp create mode 100644 openbr/plugins/metadata/rectstotemplates.cpp create mode 100644 openbr/plugins/metadata/regexproperty.cpp create mode 100644 openbr/plugins/metadata/removemetadata.cpp create mode 100644 openbr/plugins/metadata/removetemplates.cpp create mode 100644 openbr/plugins/metadata/rename.cpp create mode 100644 openbr/plugins/metadata/renamefirst.cpp create mode 100644 openbr/plugins/metadata/reorderpoints.cpp create mode 100644 openbr/plugins/metadata/restoremat.cpp create mode 100644 openbr/plugins/metadata/savemat.cpp create mode 100644 openbr/plugins/metadata/selectpoints.cpp create mode 100644 openbr/plugins/metadata/setmetadata.cpp create mode 100644 openbr/plugins/metadata/setpointsinrect.cpp create mode 100644 openbr/plugins/metadata/stasm4.cpp create mode 100644 openbr/plugins/metadata/stopwatch.cpp delete mode 100644 openbr/plugins/misc.cpp delete mode 100644 openbr/plugins/mongoose.cmake delete mode 100644 openbr/plugins/mongoose.cpp delete mode 100644 openbr/plugins/motion.cpp delete mode 100644 openbr/plugins/nn.cpp delete mode 100644 openbr/plugins/normalize.cpp delete mode 100644 openbr/plugins/nt4.cmake delete mode 100644 openbr/plugins/nt4.cpp delete mode 100644 openbr/plugins/output.cpp create mode 100644 openbr/plugins/output/best.cpp create mode 100644 openbr/plugins/output/csv.cpp create mode 100644 openbr/plugins/output/default.cpp create mode 100644 openbr/plugins/output/empty.cpp create mode 100644 openbr/plugins/output/eval.cpp create mode 100644 openbr/plugins/output/heat.cpp create mode 100644 openbr/plugins/output/hist.cpp create mode 100644 openbr/plugins/output/melt.cpp create mode 100644 openbr/plugins/output/mtx.cpp create mode 100644 openbr/plugins/output/null.cpp create mode 100644 openbr/plugins/output/rank.cpp create mode 100644 openbr/plugins/output/rr.cpp create mode 100644 openbr/plugins/output/tail.cpp create mode 100644 openbr/plugins/output/txt.cpp delete mode 100644 openbr/plugins/pp4.cmake delete mode 100644 openbr/plugins/pp4.cpp delete mode 100644 openbr/plugins/pp5.cmake delete mode 100644 openbr/plugins/pp5.cpp delete mode 100644 openbr/plugins/process.cpp delete mode 100644 openbr/plugins/qtnetwork.cmake delete mode 100644 openbr/plugins/qtnetwork.cpp delete mode 100644 openbr/plugins/quality.cpp delete mode 100644 openbr/plugins/quantize.cpp delete mode 100644 openbr/plugins/quantize2.cpp delete mode 100644 openbr/plugins/random.cpp delete mode 100644 openbr/plugins/reduce.cpp delete mode 100644 openbr/plugins/regions.cpp delete mode 100644 openbr/plugins/register.cpp delete mode 100644 openbr/plugins/representation/hogdescriptor.cpp delete mode 100644 openbr/plugins/representation/keypointdescriptor.cpp delete mode 100644 openbr/plugins/representation/siftdescriptor.cpp delete mode 100644 openbr/plugins/segmentation.cpp delete mode 100644 openbr/plugins/slidingwindow.cpp delete mode 100644 openbr/plugins/stasm4.cmake delete mode 100644 openbr/plugins/stasm4.cpp delete mode 100644 openbr/plugins/stream.cpp delete mode 100644 openbr/plugins/svm.cpp delete mode 100644 openbr/plugins/synthetic.cpp delete mode 100644 openbr/plugins/template.cpp delete mode 100644 openbr/plugins/time.cpp delete mode 100644 openbr/plugins/tree.cpp delete mode 100644 openbr/plugins/turk.cpp delete mode 100644 openbr/plugins/validate.cpp create mode 100644 openbr/plugins/video/opticalflow.cpp delete mode 100644 openbr/plugins/wavelet.cpp delete mode 100644 openbr/plugins/youtube.cpp diff --git a/openbr/plugins/classification/adaboost.cpp b/openbr/plugins/classification/adaboost.cpp new file mode 100644 index 0000000..35173e9 --- /dev/null +++ b/openbr/plugins/classification/adaboost.cpp @@ -0,0 +1,118 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV's Ada Boost framework + * \author Scott Klum \cite sklum + * \brief http://docs.opencv.org/modules/ml/doc/boosting.html + */ +class AdaBoostTransform : public Transform +{ + Q_OBJECT + Q_ENUMS(Type) + Q_ENUMS(SplitCriteria) + + Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) + Q_PROPERTY(SplitCriteria splitCriteria READ get_splitCriteria WRITE set_splitCriteria RESET reset_splitCriteria STORED false) + Q_PROPERTY(int weakCount READ get_weakCount WRITE set_weakCount RESET reset_weakCount STORED false) + Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false) + Q_PROPERTY(int folds READ get_folds WRITE set_folds RESET reset_folds STORED false) + Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) + Q_PROPERTY(bool returnConfidence READ get_returnConfidence WRITE set_returnConfidence RESET reset_returnConfidence STORED false) + Q_PROPERTY(bool overwriteMat READ get_overwriteMat WRITE set_overwriteMat RESET reset_overwriteMat STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) + +public: + enum Type { Discrete = CvBoost::DISCRETE, + Real = CvBoost::REAL, + Logit = CvBoost::LOGIT, + Gentle = CvBoost::GENTLE}; + + enum SplitCriteria { Default = CvBoost::DEFAULT, + Gini = CvBoost::GINI, + Misclass = CvBoost::MISCLASS, + Sqerr = CvBoost::SQERR}; + +private: + BR_PROPERTY(Type, type, Real) + BR_PROPERTY(SplitCriteria, splitCriteria, Default) + BR_PROPERTY(int, weakCount, 100) + BR_PROPERTY(float, trimRate, .95) + BR_PROPERTY(int, folds, 0) + BR_PROPERTY(int, maxDepth, 1) + BR_PROPERTY(bool, returnConfidence, true) + BR_PROPERTY(bool, overwriteMat, true) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QString, outputVariable, "") + + CvBoost boost; + + void train(const TemplateList &data) + { + Mat samples = OpenCVUtils::toMat(data.data()); + Mat labels = OpenCVUtils::toMat(File::get(data, inputVariable)); + + Mat types = Mat(samples.cols + 1, 1, CV_8U); + types.setTo(Scalar(CV_VAR_NUMERICAL)); + types.at(samples.cols, 0) = CV_VAR_CATEGORICAL; + + CvBoostParams params; + params.boost_type = type; + params.split_criteria = splitCriteria; + params.weak_count = weakCount; + params.weight_trim_rate = trimRate; + params.cv_folds = folds; + params.max_depth = maxDepth; + + boost.train( samples, CV_ROW_SAMPLE, labels, Mat(), Mat(), types, Mat(), + params); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + float response; + if (returnConfidence) { + response = boost.predict(src.m().reshape(1,1),Mat(),Range::all(),false,true)/weakCount; + } else { + response = boost.predict(src.m().reshape(1,1)); + } + + if (overwriteMat) { + dst.m() = Mat(1, 1, CV_32F); + dst.m().at(0, 0) = response; + } else { + dst.file.set(outputVariable, response); + } + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(boost,stream); + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(boost,stream); + } + + + void init() + { + if (outputVariable.isEmpty()) + outputVariable = inputVariable; + } +}; + +BR_REGISTER(Transform, AdaBoostTransform) + +} // namespace br + +#include "classification/adaboost.moc" diff --git a/openbr/plugins/classification/ebif.cpp b/openbr/plugins/classification/ebif.cpp index ca9f184..765c9f1 100644 --- a/openbr/plugins/classification/ebif.cpp +++ b/openbr/plugins/classification/ebif.cpp @@ -128,4 +128,4 @@ BR_REGISTER(Transform, EBIFTransform) } // namespace br -#include "ebif.moc" +#include "classification/ebif.moc" diff --git a/openbr/plugins/classification/forest.cpp b/openbr/plugins/classification/forest.cpp new file mode 100644 index 0000000..cd50743 --- /dev/null +++ b/openbr/plugins/classification/forest.cpp @@ -0,0 +1,254 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV's random trees framework + * \author Scott Klum \cite sklum + * \brief http://docs.opencv.org/modules/ml/doc/random_trees.html + */ +class ForestTransform : public Transform +{ + Q_OBJECT + + void train(const TemplateList &data) + { + trainForest(data); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + + float response; + if (classification && returnConfidence) { + // Fuzzy class label + response = forest.predict_prob(src.m().reshape(1,1)); + } else { + response = forest.predict(src.m().reshape(1,1)); + } + + if (overwriteMat) { + dst.m() = Mat(1, 1, CV_32F); + dst.m().at(0, 0) = response; + } else { + dst.file.set(outputVariable, response); + } + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(forest,stream); + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(forest,stream); + } + + void init() + { + if (outputVariable.isEmpty()) + outputVariable = inputVariable; + } + +protected: + Q_ENUMS(TerminationCriteria) + Q_PROPERTY(bool classification READ get_classification WRITE set_classification RESET reset_classification STORED false) + Q_PROPERTY(float splitPercentage READ get_splitPercentage WRITE set_splitPercentage RESET reset_splitPercentage STORED false) + Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) + Q_PROPERTY(int maxTrees READ get_maxTrees WRITE set_maxTrees RESET reset_maxTrees STORED false) + Q_PROPERTY(float forestAccuracy READ get_forestAccuracy WRITE set_forestAccuracy RESET reset_forestAccuracy STORED false) + Q_PROPERTY(bool returnConfidence READ get_returnConfidence WRITE set_returnConfidence RESET reset_returnConfidence STORED false) + Q_PROPERTY(bool overwriteMat READ get_overwriteMat WRITE set_overwriteMat RESET reset_overwriteMat STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) + Q_PROPERTY(bool weight READ get_weight WRITE set_weight RESET reset_weight STORED false) + Q_PROPERTY(TerminationCriteria termCrit READ get_termCrit WRITE set_termCrit RESET reset_termCrit STORED false) + +public: + enum TerminationCriteria { Iter = CV_TERMCRIT_ITER, + EPS = CV_TERMCRIT_EPS, + Both = CV_TERMCRIT_EPS | CV_TERMCRIT_ITER}; + +protected: + BR_PROPERTY(bool, classification, true) + BR_PROPERTY(float, splitPercentage, .01) + BR_PROPERTY(int, maxDepth, std::numeric_limits::max()) + BR_PROPERTY(int, maxTrees, 10) + BR_PROPERTY(float, forestAccuracy, .1) + BR_PROPERTY(bool, returnConfidence, true) + BR_PROPERTY(bool, overwriteMat, true) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QString, outputVariable, "") + BR_PROPERTY(bool, weight, false) + BR_PROPERTY(TerminationCriteria, termCrit, Iter) + + CvRTrees forest; + + void trainForest(const TemplateList &data) + { + Mat samples = OpenCVUtils::toMat(data.data()); + Mat labels = OpenCVUtils::toMat(File::get(data, inputVariable)); + + Mat types = Mat(samples.cols + 1, 1, CV_8U); + types.setTo(Scalar(CV_VAR_NUMERICAL)); + + if (classification) { + types.at(samples.cols, 0) = CV_VAR_CATEGORICAL; + } else { + types.at(samples.cols, 0) = CV_VAR_NUMERICAL; + } + + bool usePrior = classification && weight; + float priors[2]; + if (usePrior) { + int nonZero = countNonZero(labels); + priors[0] = 1; + priors[1] = (float)(samples.rows-nonZero)/nonZero; + } + + int minSamplesForSplit = data.size()*splitPercentage; + forest.train( samples, CV_ROW_SAMPLE, labels, Mat(), Mat(), types, Mat(), + CvRTParams(maxDepth, + minSamplesForSplit, + 0, + false, + 2, + usePrior ? priors : 0, + false, + 0, + maxTrees, + forestAccuracy, + termCrit)); + + if (Globals->verbose) { + qDebug() << "Number of trees:" << forest.get_tree_count(); + + if (classification) { + QTime timer; + timer.start(); + int correctClassification = 0; + float regressionError = 0; + for (int i=0; i(i,0)) { + correctClassification++; + } + regressionError += fabs(prediction-labels.at(i,0)); + } + + qDebug("Time to classify %d samples: %d ms\n \ + Classification Accuracy: %f\n \ + MAE: %f\n \ + Sample dimensionality: %d", + samples.rows,timer.elapsed(),(float)correctClassification/samples.rows,regressionError/samples.rows,samples.cols); + } + } + } +}; + +BR_REGISTER(Transform, ForestTransform) + +/*! + * \ingroup transforms + * \brief Wraps OpenCV's random trees framework to induce features + * \author Scott Klum \cite sklum + * \brief https://lirias.kuleuven.be/bitstream/123456789/316661/1/icdm11-camready.pdf + */ +class ForestInductionTransform : public ForestTransform +{ + Q_OBJECT + Q_PROPERTY(bool useRegressionValue READ get_useRegressionValue WRITE set_useRegressionValue RESET reset_useRegressionValue STORED false) + BR_PROPERTY(bool, useRegressionValue, false) + + int totalSize; + QList< QList > nodes; + + void fillNodes() + { + for (int i=0; i()); + const CvDTreeNode* node = forest.get_tree(i)->get_root(); + + // traverse the tree and save all the nodes in depth-first order + for(;;) + { + CvDTreeNode* parent; + for(;;) + { + if( !node->left ) + break; + node = node->left; + } + + nodes.last().append(node); + + for( parent = node->parent; parent && parent->right == node; + node = parent, parent = parent->parent ) + ; + + if( !parent ) + break; + + node = parent->right; + } + + totalSize += nodes.last().size(); + } + } + + void train(const TemplateList &data) + { + trainForest(data); + if (!useRegressionValue) fillNodes(); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + + Mat responses; + + if (useRegressionValue) { + responses = Mat::zeros(forest.get_tree_count(),1,CV_32F); + for (int i=0; i(i,0) = forest.get_tree(i)->predict(src.m().reshape(1,1))->value; + } + } else { + responses = Mat::zeros(totalSize,1,CV_32F); + int offset = 0; + for (int i=0; ipredict(src.m().reshape(1,1))); + responses.at(offset+index,0) = 1; + offset += nodes[i].size(); + } + } + + dst.m() = responses; + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(forest,stream); + if (!useRegressionValue) fillNodes(); + + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(forest,stream); + } +}; + +BR_REGISTER(Transform, ForestInductionTransform) + +} // namespace br + +#include "classification/forest.moc" diff --git a/openbr/plugins/classification/ipc2013.cpp b/openbr/plugins/classification/ipc2013.cpp new file mode 100644 index 0000000..d16958c --- /dev/null +++ b/openbr/plugins/classification/ipc2013.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include + +using namespace br; + +static PXCSession *pxcSession = NULL; +static PXCAccelerator *pxcAccelerator = NULL; + +/*! + * \ingroup initializers + * \brief Initializes Intel Perceptual Computing SDK 2013 + * \author Josh Klontz \cite jklontz + */ +class IPC2013Initializer : public Initializer +{ + void initialize() const + { + PXCSession_Create(&pxcSession); + pxcSession->CreateAccelerator(&pxcAccelerator); + } +}; + +BR_REGISTER(Initializer, IPC2013Initializer) + +/*! + * \ingroup transforms + * \brief Intel Perceptual Computing SDK 2013 Face Recognition + * \author Josh Klontz \cite jklontz + */ +class IPC2013FaceRecognitionTransfrom : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + PXCImage::ImageInfo pxcImageInfo; + pxcImageInfo.width = src.m().cols; + pxcImageInfo.height = src.m().rows; + pxcImageInfo.format = PXCImage::COLOR_FORMAT_RGB24; + + //PXCImage *pxcImage; + //pxcAccelerator->CreateImage(&pxcImageInfo, 0, src.m().data, &pxcImage); + } +}; + +BR_REGISTER(Transform, IPC2013FaceRecognitionTransfrom) + +#include "ipc2013.moc" diff --git a/openbr/plugins/classification/lda.cpp b/openbr/plugins/classification/lda.cpp new file mode 100644 index 0000000..390f266 --- /dev/null +++ b/openbr/plugins/classification/lda.cpp @@ -0,0 +1,657 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright 2012 The MITRE Corporation * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +#include + +#include +#include +#include + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Initialize Eigen + * http://eigen.tuxfamily.org/dox/TopicMultiThreading.html + * \author Scott Klum \cite sklum + */ +class EigenInitializer : public Initializer +{ + Q_OBJECT + + void initialize() const + { + Eigen::initParallel(); + } +}; + +BR_REGISTER(Initializer, EigenInitializer) + +/*! + * \ingroup transforms + * \brief Projects input into learned Principal Component Analysis subspace. + * \author Brendan Klare \cite bklare + * \author Josh Klontz \cite jklontz + */ +class PCATransform : public Transform +{ + Q_OBJECT + friend class DFFSTransform; + friend class LDATransform; + +protected: + Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false) + Q_PROPERTY(int drop READ get_drop WRITE set_drop RESET reset_drop STORED false) + Q_PROPERTY(bool whiten READ get_whiten WRITE set_whiten RESET reset_whiten STORED false) + + /*! + * keep < 0: All eigenvalues are retained. + * keep = 0: No PCA performed, eigenvectors form an identity matrix. + * 0 < keep < 1: Fraction of the variance to retain. + * keep >= 1: Number of leading eigenvectors to retain. + */ + BR_PROPERTY(float, keep, 0.95) + BR_PROPERTY(int, drop, 0) + BR_PROPERTY(bool, whiten, false) + + Eigen::VectorXf mean, eVals; + Eigen::MatrixXf eVecs; + + int originalRows; + +public: + PCATransform() : keep(0.95), drop(0), whiten(false) {} + +private: + double residualReconstructionError(const Template &src) const + { + Template proj; + project(src, proj); + + Eigen::Map srcMap(src.m().ptr(), src.m().rows*src.m().cols); + Eigen::Map projMap(proj.m().ptr(), keep); + + return (srcMap - mean).squaredNorm() - projMap.squaredNorm(); + } + + void train(const TemplateList &trainingSet) + { + if (trainingSet.first().m().type() != CV_32FC1) + qFatal("Requires single channel 32-bit floating point matrices."); + + originalRows = trainingSet.first().m().rows; + int dimsIn = trainingSet.first().m().rows * trainingSet.first().m().cols; + const int instances = trainingSet.size(); + + // Map into 64-bit Eigen matrix + Eigen::MatrixXd data(dimsIn, instances); + for (int i=0; i(trainingSet[i].m().ptr(), dimsIn, 1).cast(); + + trainCore(data); + } + + void project(const Template &src, Template &dst) const + { + dst = cv::Mat(1, keep, CV_32FC1); + + // Map Eigen into OpenCV + Eigen::Map inMap(src.m().ptr(), src.m().rows*src.m().cols, 1); + Eigen::Map outMap(dst.m().ptr(), keep, 1); + + // Do projection + outMap = eVecs.transpose() * (inMap - mean); + } + + void store(QDataStream &stream) const + { + stream << keep << drop << whiten << originalRows << mean << eVals << eVecs; + } + + void load(QDataStream &stream) + { + stream >> keep >> drop >> whiten >> originalRows >> mean >> eVals >> eVecs; + } + +protected: + void trainCore(Eigen::MatrixXd data) + { + int dimsIn = data.rows(); + int instances = data.cols(); + const bool dominantEigenEstimation = (dimsIn > instances); + + Eigen::MatrixXd allEVals, allEVecs; + if (keep != 0) { + // Compute and remove mean + mean = Eigen::VectorXf(dimsIn); + for (int i=0; i eSolver(cov); + allEVals = eSolver.eigenvalues(); + allEVecs = eSolver.eigenvectors(); + if (dominantEigenEstimation) allEVecs = data * allEVecs; + } else { + // Null case + mean = Eigen::VectorXf::Zero(dimsIn); + allEVecs = Eigen::MatrixXd::Identity(dimsIn, dimsIn); + allEVals = Eigen::VectorXd::Ones(dimsIn); + } + + if (keep <= 0) { + keep = dimsIn - drop; + } else if (keep < 1) { + // Keep eigenvectors that retain a certain energy percentage. + const double totalEnergy = allEVals.sum(); + if (totalEnergy == 0) { + keep = 0; + } else { + double currentEnergy = 0; + int i=0; + while ((currentEnergy / totalEnergy < keep) && (i < allEVals.rows())) { + currentEnergy += allEVals(allEVals.rows()-(i+1)); + i++; + } + keep = i - drop; + } + } else { + if (keep + drop > allEVals.rows()) { + qWarning("Insufficient samples, needed at least %d but only got %d.", (int)keep + drop, (int)allEVals.rows()); + keep = allEVals.rows() - drop; + } + } + + // Keep highest energy vectors + eVals = Eigen::VectorXf((int)keep, 1); + eVecs = Eigen::MatrixXf(allEVecs.rows(), (int)keep); + for (int i=0; i() / allEVecs.col(index).norm(); + if (whiten) eVecs.col(i) /= sqrt(eVals(i)); + } + + // Debug output + if (Globals->verbose) qDebug() << "PCA Training:\n\tDimsIn =" << dimsIn << "\n\tKeep =" << keep; + } + + void writeEigenVectors(const Eigen::MatrixXd &allEVals, const Eigen::MatrixXd &allEVecs) const + { + const int originalCols = mean.rows() / originalRows; + + { // Write out mean image + cv::Mat out(originalRows, originalCols, CV_32FC1); + Eigen::Map outMap(out.ptr(), mean.rows(), 1); + outMap = mean.col(0); + // OpenCVUtils::saveImage(out, Globals->Debug+"/PCA/eigenVectors/mean.png"); + } + + // Write out sample eigen vectors (16 highest, 8 lowest), filename = eigenvalue. + for (int k=0; k<(int)allEVals.size(); k++) { + if ((k < 8) || (k >= (int)allEVals.size()-16)) { + cv::Mat out(originalRows, originalCols, CV_64FC1); + Eigen::Map outMap(out.ptr(), mean.rows(), 1); + outMap = allEVecs.col(k); + // OpenCVUtils::saveImage(out, Globals->Debug+"/PCA/eigenVectors/"+QString::number(allEVals(k),'f',0)+".png"); + } + } + } +}; + +BR_REGISTER(Transform, PCATransform) + +/*! + * \ingroup transforms + * \brief PCA on each row. + * \author Josh Klontz \cite jklontz + */ +class RowWisePCATransform : public PCATransform +{ + Q_OBJECT + + void train(const TemplateList &trainingSet) + { + if (trainingSet.first().m().type() != CV_32FC1) + qFatal("Requires single channel 32-bit floating point matrices."); + + originalRows = trainingSet.first().m().rows; + const int dimsIn = trainingSet.first().m().cols; + int instances = 0; + foreach (const Template &t, trainingSet) + instances += t.m().rows; + + // Map into 64-bit Eigen matrix + Eigen::MatrixXd data(dimsIn, instances); + int index = 0; + foreach (const Template &t, trainingSet) + for (int i=0; i(t.m().ptr(i), dimsIn, 1).cast(); + + PCATransform::trainCore(data); + } + + void project(const Template &src, Template &dst) const + { + dst = cv::Mat(src.m().rows, keep, CV_32FC1); + + for (int i=0; i inMap(src.m().ptr(i), src.m().cols, 1); + Eigen::Map outMap(dst.m().ptr(i), keep, 1); + outMap = eVecs.transpose() * (inMap - mean); + } + } +}; + +BR_REGISTER(Transform, RowWisePCATransform) + +/*! + * \ingroup transforms + * \brief Computes Distance From Feature Space (DFFS) \cite moghaddam97. + * \author Josh Klontz \cite jklontz + */ +class DFFSTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false) + BR_PROPERTY(float, keep, 0.95) + + PCATransform pca; + Transform *cvtFloat; + + void init() + { + pca.keep = keep; + cvtFloat = make("CvtFloat"); + } + + void train(const TemplateList &data) + { + pca.train((*cvtFloat)(data)); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src)))); + } + + void store(QDataStream &stream) const + { + pca.store(stream); + } + + void load(QDataStream &stream) + { + pca.load(stream); + } +}; + +BR_REGISTER(Transform, DFFSTransform) + +/*! + * \ingroup transforms + * \brief Projects input into learned Linear Discriminant Analysis subspace. + * \author Brendan Klare \cite bklare + * \author Josh Klontz \cite jklontz + */ +class LDATransform : public Transform +{ + friend class SparseLDATransform; + + Q_OBJECT + Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false) + Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false) + Q_PROPERTY(int directLDA READ get_directLDA WRITE set_directLDA RESET reset_directLDA STORED false) + Q_PROPERTY(float directDrop READ get_directDrop WRITE set_directDrop RESET reset_directDrop STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(bool isBinary READ get_isBinary WRITE set_isBinary RESET reset_isBinary STORED false) + Q_PROPERTY(bool normalize READ get_normalize WRITE set_normalize RESET reset_normalize STORED false) + BR_PROPERTY(float, pcaKeep, 0.98) + BR_PROPERTY(bool, pcaWhiten, false) + BR_PROPERTY(int, directLDA, 0) + BR_PROPERTY(float, directDrop, 0.1) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(bool, isBinary, false) + BR_PROPERTY(bool, normalize, true) + + int dimsOut; + Eigen::VectorXf mean; + Eigen::MatrixXf projection; + float stdDev; + + void train(const TemplateList &_trainingSet) + { + // creates "Label" + TemplateList trainingSet = TemplateList::relabel(_trainingSet, inputVariable, isBinary); + int instances = trainingSet.size(); + + // Perform PCA dimensionality reduction + PCATransform pca; + pca.keep = pcaKeep; + pca.whiten = pcaWhiten; + pca.train(trainingSet); + mean = pca.mean; + + TemplateList ldaTrainingSet; + static_cast(&pca)->project(trainingSet, ldaTrainingSet); + + int dimsIn = ldaTrainingSet.first().m().rows * ldaTrainingSet.first().m().cols; + + // OpenBR ensures that class values range from 0 to numClasses-1. + // Label exists because we created it earlier with relabel + QList classes = File::get(trainingSet, "Label"); + QMap classCounts = trainingSet.countValues("Label"); + const int numClasses = classCounts.size(); + + // Map Eigen into OpenCV + Eigen::MatrixXd data = Eigen::MatrixXd(dimsIn, instances); + for (int i=0; i(ldaTrainingSet[i].m().ptr(), dimsIn, 1).cast(); + + // Removing class means + Eigen::MatrixXd classMeans = Eigen::MatrixXd::Zero(dimsIn, numClasses); + for (int i=0; i instances - numClasses) { + // Here, we are replacing the eigenvalue of the null space + // eigenvectors with the eigenvalue (divided by 2) of the + // smallest eigenvector from the row space eigenvector. + // This allows us to scale these null-space vectors (otherwise + // it is a divide by zero. + double null_eig = space1.eVals(instances - numClasses - 1) / 2; + for (int i = instances - numClasses; i < dimsIn; i++) + space1.eVals(i) = null_eig; + } + + // Drop the first few leading eigenvectors in the within-class space + QList eVal_list; eVal_list.reserve(dimsIn); + float fmax = -1; + for (int i=0; i dSum = Common::CumSum(eVal_list); + int drop_idx; + for (drop_idx = 0; drop_idx= directDrop) + break; + + drop_idx++; + space1.keep = dimsIn - drop_idx; + + Eigen::MatrixXf new_vecs = Eigen::MatrixXf(space1.eVecs.rows(), (int)space1.keep); + Eigen::MatrixXf new_vals = Eigen::MatrixXf((int)space1.keep, 1); + + for (int i = 0; i < space1.keep; i++) { + new_vecs.col(i) = space1.eVecs.col(i + drop_idx); + new_vals(i) = space1.eVals(i + drop_idx); + } + + space1.eVecs = new_vecs; + space1.eVals = new_vals; + + // We will call this "agressive" whitening. Really, it is not whitening + // anymore. Instead, we are further scaling the small eigenvalues and the + // null space eigenvalues (to increase their impact). + for (int i=0; i() * classMeans; + + // The rank of the between-class scatter matrix is bound by numClasses - 1 + // because each class is a vector used to compute the covariance, + // but one degree of freedom is lost removing the global mean. + int dim2 = std::min((int)space1.keep, numClasses-1); + PCATransform space2; + space2.keep = dim2; + space2.trainCore(data2); + + // Compute final projection matrix + projection = ((space2.eVecs.transpose() * space1.eVecs.transpose()) * pca.eVecs.transpose()).transpose(); + dimsOut = dim2; + + stdDev = 1; // default initialize + if (isBinary) { + assert(dimsOut == 1); + float posVal = 0; + float negVal = 0; + Eigen::MatrixXf results(trainingSet.size(),1); + for (int i = 0; i < trainingSet.size(); i++) { + Template t; + project(trainingSet[i],t); + //Note: the positive class is assumed to be 0 b/c it will + // typically be the first gallery template in the TemplateList structure + if (classes[i] == 0) + posVal += t.m().at(0,0); + else if (classes[i] == 1) + negVal += t.m().at(0,0); + else + qFatal("Binary mode only supports two class problems."); + results(i) = t.m().at(0,0); //used for normalization + } + posVal /= classCounts[0]; + negVal /= classCounts[1]; + + if (posVal < negVal) { + //Ensure positive value is supposed to be > 0 after projection + Eigen::MatrixXf invert = Eigen::MatrixXf::Ones(dimsIn,1); + invert *= -1; + projection = invert.transpose() * projection; + } + + if (normalize) + stdDev = sqrt(results.array().square().sum() / trainingSet.size()); + } + } + + void project(const Template &src, Template &dst) const + { + dst = cv::Mat(1, dimsOut, CV_32FC1); + + // Map Eigen into OpenCV + Eigen::Map inMap((float*)src.m().ptr(), src.m().rows*src.m().cols, 1); + Eigen::Map outMap(dst.m().ptr(), dimsOut, 1); + + // Do projection + outMap = projection.transpose() * (inMap - mean); + if (normalize && isBinary) + dst.m().at(0,0) = dst.m().at(0,0) / stdDev; + } + + void store(QDataStream &stream) const + { + stream << pcaKeep; + stream << directLDA; + stream << directDrop; + stream << dimsOut; + stream << mean; + stream << projection; + if (normalize && isBinary) + stream << stdDev; + } + + void load(QDataStream &stream) + { + stream >> pcaKeep; + stream >> directLDA; + stream >> directDrop; + stream >> dimsOut; + stream >> mean; + stream >> projection; + if (normalize && isBinary) + stream >> stdDev; + } +}; + +BR_REGISTER(Transform, LDATransform) + +/*! + * \ingroup transforms + * \brief Projects input into learned Linear Discriminant Analysis subspace + * learned on a sparse subset of features with the highest weight + * in the original LDA algorithm. + * \author Brendan Klare \cite bklare + */ +class SparseLDATransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(float varThreshold READ get_varThreshold WRITE set_varThreshold RESET reset_varThreshold STORED false) + Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false) + Q_PROPERTY(bool normalize READ get_normalize WRITE set_normalize RESET reset_normalize STORED false) + BR_PROPERTY(float, varThreshold, 1.5) + BR_PROPERTY(float, pcaKeep, 0.98) + BR_PROPERTY(bool, normalize, true) + + LDATransform ldaSparse; + int dimsOut; + QList selections; + + Eigen::VectorXf mean; + + void init() + { + ldaSparse.init(); + ldaSparse.pcaKeep = pcaKeep; + ldaSparse.inputVariable = "Label"; + ldaSparse.isBinary = true; + ldaSparse.normalize = true; + } + + void train(const TemplateList &_trainingSet) + { + + LDATransform ldaOrig; + ldaOrig.init(); + ldaOrig.inputVariable = "Label"; + ldaOrig.pcaKeep = pcaKeep; + ldaOrig.isBinary = true; + ldaOrig.normalize = true; + + ldaOrig.train(_trainingSet); + + //Only works on binary class problems for now + assert(ldaOrig.projection.cols() == 1); + float ldaStd = EigenUtils::stddev(ldaOrig.projection); + for (int i = 0; i < ldaOrig.projection.rows(); i++) + if (abs(ldaOrig.projection(i)) > varThreshold * ldaStd) + selections.append(i); + + TemplateList newSet; + for (int i = 0; i < _trainingSet.size(); i++) { + cv::Mat x(_trainingSet[i]); + cv::Mat y = cv::Mat(selections.size(), 1, CV_32FC1); + int idx = 0; + int cnt = 0; + for (int j = 0; j < x.rows; j++) + for (int k = 0; k < x.cols; k++, cnt++) + if (selections.contains(cnt)) + y.at(idx++,0) = x.at(j, k); + newSet.append(Template(_trainingSet[i].file, y)); + } + ldaSparse.train(newSet); + dimsOut = ldaSparse.dimsOut; + } + + void project(const Template &src, Template &dst) const + { + Eigen::Map inMap((float*)src.m().ptr(), src.m().rows*src.m().cols, 1); + Eigen::Map outMap(dst.m().ptr(), dimsOut, 1); + + int d = selections.size(); + cv::Mat inSelect(d,1,CV_32F); + for (int i = 0; i < d; i++) + inSelect.at(i) = src.m().at(selections[i]); + ldaSparse.project(Template(src.file, inSelect), dst); + } + + void store(QDataStream &stream) const + { + stream << pcaKeep; + stream << ldaSparse; + stream << dimsOut; + stream << selections; + } + + void load(QDataStream &stream) + { + stream >> pcaKeep; + stream >> ldaSparse; + stream >> dimsOut; + stream >> selections; + } +}; + +BR_REGISTER(Transform, SparseLDATransform) + +} // namespace br + +#include "classification/lda.moc" diff --git a/openbr/plugins/classification/liblinear.cpp b/openbr/plugins/classification/liblinear.cpp new file mode 100644 index 0000000..31b2137 --- /dev/null +++ b/openbr/plugins/classification/liblinear.cpp @@ -0,0 +1,206 @@ +#include +#include +#include + +#include +#include + +#include + +using namespace cv; + +namespace br +{ + +static void storeModel(const model &m, QDataStream &stream) +{ + // Create local file + QTemporaryFile tempFile; + tempFile.open(); + tempFile.close(); + + // Save MLP to local file + save_model(qPrintable(tempFile.fileName()),&m); + + // Copy local file contents to stream + tempFile.open(); + QByteArray data = tempFile.readAll(); + tempFile.close(); + stream << data; +} + +static void loadModel(model &m, QDataStream &stream) +{ + // Copy local file contents from stream + QByteArray data; + stream >> data; + + // Create local file + QTemporaryFile tempFile(QDir::tempPath()+"/model"); + tempFile.open(); + tempFile.write(data); + tempFile.close(); + + // Load MLP from local file + m = *load_model(qPrintable(tempFile.fileName())); +} + +class Linear : public Transform +{ + Q_OBJECT + Q_ENUMS(Solver) + Q_PROPERTY(Solver solver READ get_solver WRITE set_solver RESET reset_solver STORED false) + Q_PROPERTY(float C READ get_C WRITE set_C RESET reset_C STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) + Q_PROPERTY(bool returnDFVal READ get_returnDFVal WRITE set_returnDFVal RESET reset_returnDFVal STORED false) + Q_PROPERTY(bool overwriteMat READ get_overwriteMat WRITE set_overwriteMat RESET reset_overwriteMat STORED false) + Q_PROPERTY(bool weight READ get_weight WRITE set_weight RESET reset_weight STORED false) + +public: + enum Solver { L2R_LR = ::L2R_LR, + L2R_L2LOSS_SVC_DUAL = ::L2R_L2LOSS_SVC_DUAL, + L2R_L2LOSS_SVC = ::L2R_L2LOSS_SVC, + L2R_L1LOSS_SVC_DUAL = ::L2R_L1LOSS_SVC_DUAL, + MCSVM_CS = ::MCSVM_CS, + L1R_L2LOSS_SVC = ::L1R_L2LOSS_SVC, + L1R_LR = ::L1R_LR, + L2R_LR_DUAL = ::L2R_LR_DUAL, + L2R_L2LOSS_SVR = ::L2R_L2LOSS_SVR, + L2R_L2LOSS_SVR_DUAL = ::L2R_L2LOSS_SVR_DUAL, + L2R_L1LOSS_SVR_DUAL = ::L2R_L1LOSS_SVR_DUAL }; + +private: + BR_PROPERTY(Solver, solver, L2R_L2LOSS_SVC_DUAL) + BR_PROPERTY(float, C, 1) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QString, outputVariable, "") + BR_PROPERTY(bool, returnDFVal, false) + BR_PROPERTY(bool, overwriteMat, true) + BR_PROPERTY(bool, weight, false) + + model m; + + void train(const TemplateList &data) + { + Mat samples = OpenCVUtils::toMat(data.data()); + Mat labels = OpenCVUtils::toMat(File::get(data, inputVariable)); + + problem prob; + prob.n = samples.cols; + prob.l = samples.rows; + prob.bias = -1; + prob.y = new double[prob.l]; + + for (int i=0; i(i,0); + + // Allocate enough memory for l feature_nodes pointers + prob.x = new feature_node*[prob.l]; + feature_node *x_space = new feature_node[(prob.n+1)*prob.l]; + + int k = 0; + for (int i=0; i(i,j); + k++; + } + x_space[k++].index = -1; + } + + parameter param; + + // TODO: Support grid search + param.C = C; + param.p = 1; + param.eps = FLT_EPSILON; + param.solver_type = solver; + + if (weight) { + param.nr_weight = 2; + param.weight_label = new int[2]; + param.weight = new double[2]; + param.weight_label[0] = 0; + param.weight_label[1] = 1; + int nonZero = countNonZero(labels); + param.weight[0] = 1; + param.weight[1] = (double)(prob.l-nonZero)/nonZero; + qDebug() << param.weight[0] << param.weight[1]; + } else { + param.nr_weight = 0; + param.weight_label = NULL; + param.weight = NULL; + } + + m = *train_svm(&prob, ¶m); + + delete[] param.weight; + delete[] param.weight_label; + delete[] prob.y; + delete[] prob.x; + delete[] x_space; + } + + void project(const Template &src, Template &dst) const + { + dst = src; + + Mat sample = src.m().reshape(1,1); + feature_node *x_space = new feature_node[sample.cols+1]; + + for (int j=0; j(0,j); + } + x_space[sample.cols].index = -1; + + float prediction; + double prob_estimates[m.nr_class]; + + if (solver == L2R_L2LOSS_SVR || + solver == L2R_L1LOSS_SVR_DUAL || + solver == L2R_L2LOSS_SVR_DUAL || + solver == L2R_L2LOSS_SVC_DUAL || + solver == L2R_L2LOSS_SVC || + solver == L2R_L1LOSS_SVC_DUAL || + solver == MCSVM_CS || + solver == L1R_L2LOSS_SVC) + { + prediction = predict_values(&m,x_space,prob_estimates); + if (returnDFVal) prediction = prob_estimates[0]; + } else if (solver == L2R_LR || + solver == L2R_LR_DUAL || + solver == L1R_LR) + { + prediction = predict_probability(&m,x_space,prob_estimates); + if (returnDFVal) prediction = prob_estimates[0]; + } + + if (overwriteMat) { + dst.m() = Mat(1, 1, CV_32F); + dst.m().at(0, 0) = prediction; + } else { + dst.file.set(outputVariable,prediction); + } + + delete[] x_space; + } + + void store(QDataStream &stream) const + { + storeModel(m,stream); + } + + void load(QDataStream &stream) + { + loadModel(m,stream); + } +}; + +BR_REGISTER(Transform, Linear) + +} // namespace br + +#include "liblinear.moc" diff --git a/openbr/plugins/classification/mlp.cpp b/openbr/plugins/classification/mlp.cpp new file mode 100644 index 0000000..5cf4ce4 --- /dev/null +++ b/openbr/plugins/classification/mlp.cpp @@ -0,0 +1,102 @@ +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV's multi-layer perceptron framework + * \author Scott Klum \cite sklum + * \brief http://docs.opencv.org/modules/ml/doc/neural_networks.html + */ +class MLPTransform : public MetaTransform +{ + Q_OBJECT + + Q_ENUMS(Kernel) + Q_PROPERTY(Kernel kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) + Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) + Q_PROPERTY(float beta READ get_beta WRITE set_beta RESET reset_beta STORED false) + Q_PROPERTY(QStringList inputVariables READ get_inputVariables WRITE set_inputVariables RESET reset_inputVariables STORED false) + Q_PROPERTY(QStringList outputVariables READ get_outputVariables WRITE set_outputVariables RESET reset_outputVariables STORED false) + Q_PROPERTY(QList neuronsPerLayer READ get_neuronsPerLayer WRITE set_neuronsPerLayer RESET reset_neuronsPerLayer STORED false) + +public: + + enum Kernel { Identity = CvANN_MLP::IDENTITY, + Sigmoid = CvANN_MLP::SIGMOID_SYM, + Gaussian = CvANN_MLP::GAUSSIAN}; + +private: + BR_PROPERTY(Kernel, kernel, Sigmoid) + BR_PROPERTY(float, alpha, 1) + BR_PROPERTY(float, beta, 1) + BR_PROPERTY(QStringList, inputVariables, QStringList()) + BR_PROPERTY(QStringList, outputVariables, QStringList()) + BR_PROPERTY(QList, neuronsPerLayer, QList() << 1 << 1) + + CvANN_MLP mlp; + + void init() + { + if (kernel == Gaussian) + qWarning("The OpenCV documentation warns that the Gaussian kernel, \"is not completely supported at the moment\""); + + Mat layers = Mat(neuronsPerLayer.size(), 1, CV_32SC1); + for (int i=0; i(data, inputVariables.at(i))); + + mlp.train(_data,labels,Mat()); + + if (Globals->verbose) + for (int i=0; i(0,i)); + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(mlp, stream); + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(mlp, stream); + } +}; + +BR_REGISTER(Transform, MLPTransform) + +} // namespace br + +#include "classification/mlp.moc" diff --git a/openbr/plugins/classification/nt4.cpp b/openbr/plugins/classification/nt4.cpp new file mode 100644 index 0000000..c0b1cb7 --- /dev/null +++ b/openbr/plugins/classification/nt4.cpp @@ -0,0 +1,460 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "openbr_internal.h" + +//IRIS +#include +#include +#include +#include +#include +#include + +#include "core/resource.h" +#include "core/opencvutils.h" + +using namespace cv; +using namespace br; + +/*! + * \ingroup initializers + * \brief Initialize Neurotech SDK 4 + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + */ +class NT4Initializer : public Initializer +{ + Q_OBJECT + + static void manageLicenses(bool obtain) + { + const NChar *components = { N_T("Biometrics.FaceExtraction,Biometrics.FaceMatching,Biometrics.IrisExtraction,Biometrics.IrisMatching") }; + if (obtain) { + NBool available; + if (!Globals->contains("NT4_SERVER_IP")) Globals->set("NT4_SERVER_IP", "128.29.70.34"); + NResult result = NLicenseObtainComponents(N_T(qPrintable(Globals->property("NT4_SERVER_IP").toString())), N_T("5000"), components, &available); + if (NFailed(result)) qWarning("NLicenseObtainComponents() failed, result=%i.", result); + if (!available) qWarning("NT4 components not available."); + } else /* release */ { + NResult result = NLicenseReleaseComponents(components); + if (NFailed(result)) qWarning("NLicenseReleaseComponents() failed, result=%i.", result); + } + } + + void initialize() const + { + NCoreOnStart(); + Globals->abbreviations.insert("NT4Face", "Open+NT4DetectFace!NT4EnrollFace:NT4Compare"); + Globals->abbreviations.insert("NT4Iris", "Open+NT4EnrollIris:NT4Compare"); + manageLicenses(true); + } + + void finalize() const + { + manageLicenses(false); + NCoreOnExitEx(false); + } +}; + +BR_REGISTER(Initializer, NT4Initializer) + +/*! + * \brief Neurotech context + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + */ +struct NT4Context +{ + HNLExtractor extractor; /*!< Face extractor. */ + HNEExtractor irisExtractor; /*!< Iris extractor. */ + HNMatcher matcher; /*!< Template matcher. */ + + NT4Context() + { + NResult result; + + // Face + // Create extractor + result = NleCreate(&extractor); + if (NFailed(result)) qFatal("NleCreate() failed, result=%i.", result); + + NBool detectAllFeaturePoints = true; + NObjectSetParameter(extractor, NLEP_DETECT_ALL_FEATURE_POINTS, &detectAllFeaturePoints); + + NDouble faceConfidence = 1; + NObjectSetParameter(extractor, NLEP_FACE_CONFIDENCE_THRESHOLD, &faceConfidence); + + NByte faceQuality = 1; + NObjectSetParameter(extractor, NLEP_FACE_QUALITY_THRESHOLD, &faceQuality); + + NByte favorLargestFace = 0; + NObjectSetParameter(extractor, NLEP_FAVOR_LARGEST_FACE, &favorLargestFace); + + NByte useLivenessCheck = 0; + NObjectSetParameter(extractor, NLEP_USE_LIVENESS_CHECK, &useLivenessCheck); + + NleTemplateSize templSize = nletsLarge; + NObjectSetParameter(extractor, NLEP_TEMPLATE_SIZE, &templSize); + + // Iris + // Create extractor + + result = NeeCreate(&irisExtractor); + if (NFailed(result)) qFatal("NeeCreate() failed, result=%i.", result); + + NInt boundaryPointCount = 32; + NObjectSetParameter(irisExtractor, NEE_BOUNDARY_POINT_COUNT, &boundaryPointCount); + + NBool interlace = false; + NObjectSetParameter(irisExtractor, NEEP_DEINTERLACE, &interlace); + + + NInt innerBoundaryFrom = 40; + NObjectSetParameter(irisExtractor, NEEP_INNER_BOUNDARY_FROM, &innerBoundaryFrom); + + NInt innerBoundaryTo = 160; + NObjectSetParameter(irisExtractor, NEEP_INNER_BOUNDARY_TO, &innerBoundaryTo); + + NInt outerBoundaryFrom = 140; + NObjectSetParameter(irisExtractor, NEEP_OUTER_BOUNDARY_FROM, &outerBoundaryFrom); + + NInt outerBoundaryTo = 255; + NObjectSetParameter(irisExtractor, NEEP_OUTER_BOUNDARY_TO, &outerBoundaryTo); + + + // Face + // Create matcher + result = NMCreate(&matcher); + if (NFailed(result)) qFatal("NMCreate() failed, result=%i.",result); + + NInt matchingThreshold = 0; + NObjectSetParameter(matcher, NMP_MATCHING_THRESHOLD, &matchingThreshold); + } + + ~NT4Context() + { + NObjectFree(extractor); + NObjectFree(irisExtractor); + NObjectFree(matcher); + } + + /*! + * \brief to NT image + */ + static void toImage(const Mat &src, HNGrayscaleImage *grayscaleImage) + { + Mat gray; + OpenCVUtils::cvtGray(src, gray); + assert(gray.isContinuous()); + + HNImage image; + NResult result; + result = NImageCreateFromDataEx(npfGrayscale, gray.cols, gray.rows, 0, gray.cols, gray.data, gray.rows*gray.cols, 0, &image); + if (NFailed(result)) qFatal("NT4Context::toImage NImageCreateFromDataEx() failed, result=%i.", result); + + result = NImageToGrayscale(image, grayscaleImage); + if (NFailed(result)) qFatal("NT4Context::toImage NImageToGrayscale() failed, result=%i.", result); + NObjectFree(image); + } + + /*! + * \brief to OpenCV matrix + */ + static Mat toMat(const HNLTemplate &templ) + { + NSizeType bufferSize; + NLTemplateGetSize(templ, 0, &bufferSize); + + Mat buffer(1, bufferSize, CV_8UC1); + NLTemplateSaveToMemory(templ, buffer.data, bufferSize, 0, &bufferSize); + + return buffer; + } + + /*! + * \brief extract metadata + */ + static File toMetadata(const NleDetectionDetails &detectionDetails) + { + File metadata; + + metadata.insert("NT4_FaceAvailable", detectionDetails.FaceAvailable); + metadata.insert("NT4_Face_Rectangle_X", detectionDetails.Face.Rectangle.X); + metadata.insert("NT4_Face_Rectangle_Y", detectionDetails.Face.Rectangle.Y); + metadata.insert("NT4_Face_Rectangle_Width", detectionDetails.Face.Rectangle.Width); + metadata.insert("NT4_Face_Rectangle_Height", detectionDetails.Face.Rectangle.Height); + metadata.insert("NT4_Face_Rotation_Roll", detectionDetails.Face.Rotation.Roll); + metadata.insert("NT4_Face_Rotation_Pitch", detectionDetails.Face.Rotation.Pitch); + metadata.insert("NT4_Face_Rotation_Yaw", detectionDetails.Face.Rotation.Yaw); + metadata.insert("NT4_Face_Confidence", detectionDetails.Face.Confidence); + + metadata.insert("NT4_RightEyeCenter_X", detectionDetails.RightEyeCenter.X); + metadata.insert("NT4_RightEyeCenter_Y", detectionDetails.RightEyeCenter.Y); + metadata.insert("NT4_RightEyeCenter_Code", detectionDetails.RightEyeCenter.Code); + metadata.insert("NT4_RightEyeCenter_Confidence", detectionDetails.RightEyeCenter.Confidence); + + metadata.insert("NT4_LeftEyeCenter_X", detectionDetails.LeftEyeCenter.X); + metadata.insert("NT4_LeftEyeCenter_Y", detectionDetails.LeftEyeCenter.Y); + metadata.insert("NT4_LeftEyeCenter_Code", detectionDetails.LeftEyeCenter.Code); + metadata.insert("NT4_LeftEyeCenter_Confidence", detectionDetails.LeftEyeCenter.Confidence); + + metadata.insert("NT4_MouthCenter_X", detectionDetails.MouthCenter.X); + metadata.insert("NT4_MouthCenter_Y", detectionDetails.MouthCenter.Y); + metadata.insert("NT4_MouthCenter_Code", detectionDetails.MouthCenter.Code); + metadata.insert("NT4_MouthCenter_Confidence", detectionDetails.MouthCenter.Confidence); + + metadata.insert("NT4_NoseTip_X", detectionDetails.NoseTip.X); + metadata.insert("NT4_NoseTip_Y", detectionDetails.NoseTip.Y); + metadata.insert("NT4_NoseTip_Code", detectionDetails.NoseTip.Code); + metadata.insert("NT4_NoseTip_Confidence", detectionDetails.NoseTip.Confidence); + + return metadata; + } + + /*! + * \brief Initialize from metadata + */ + static NleDetectionDetails fromMetadata(const File &metadata) + { + NleDetectionDetails detectionDetails; + + detectionDetails.FaceAvailable = metadata.value("NT4_FaceAvailable").toBool(); + detectionDetails.Face.Rectangle.X = metadata.value("NT4_Face_Rectangle_X").toInt(); + detectionDetails.Face.Rectangle.Y = metadata.value("NT4_Face_Rectangle_Y").toInt(); + detectionDetails.Face.Rectangle.Width = metadata.value("NT4_Face_Rectangle_Width").toInt(); + detectionDetails.Face.Rectangle.Height = metadata.value("NT4_Face_Rectangle_Height").toInt(); + detectionDetails.Face.Rotation.Roll = metadata.value("NT4_Face_Rotation_Roll").toInt(); + detectionDetails.Face.Rotation.Pitch = metadata.value("NT4_Face_Rotation_Pitch").toInt(); + detectionDetails.Face.Rotation.Yaw = metadata.value("NT4_Face_Rotation_Yaw").toInt(); + detectionDetails.Face.Confidence = metadata.value("NT4_Face_Confidence").toDouble(); + + detectionDetails.RightEyeCenter.X = metadata.value("NT4_RightEyeCenter_X").toInt(); + detectionDetails.RightEyeCenter.Y = metadata.value("NT4_RightEyeCenter_Y").toInt(); + detectionDetails.RightEyeCenter.Code = metadata.value("NT4_RightEyeCenter_Code").toInt(); + detectionDetails.RightEyeCenter.Confidence = metadata.value("NT4_RightEyeCenter_Confidence").toDouble(); + + detectionDetails.LeftEyeCenter.X = metadata.value("NT4_LeftEyeCenter_X").toInt(); + detectionDetails.LeftEyeCenter.Y = metadata.value("NT4_LeftEyeCenter_Y").toInt(); + detectionDetails.LeftEyeCenter.Code = metadata.value("NT4_LeftEyeCenter_Code").toInt(); + detectionDetails.LeftEyeCenter.Confidence = metadata.value("NT4_LeftEyeCenter_Confidence").toDouble(); + + detectionDetails.MouthCenter.X = metadata.value("NT4_MouthCenter_X").toInt(); + detectionDetails.MouthCenter.Y = metadata.value("NT4_MouthCenter_Y").toInt(); + detectionDetails.MouthCenter.Code = metadata.value("NT4_MouthCenter_Code").toInt(); + detectionDetails.MouthCenter.Confidence = metadata.value("NT4_MouthCenter_Confidence").toDouble(); + + detectionDetails.NoseTip.X = metadata.value("NT4_NoseTip_X").toInt(); + detectionDetails.NoseTip.Y = metadata.value("NT4_NoseTip_Y").toInt(); + detectionDetails.NoseTip.Code = metadata.value("NT4_NoseTip_Code").toInt(); + detectionDetails.NoseTip.Confidence = metadata.value("NT4_NoseTip_Confidence").toDouble(); + + return detectionDetails; + } +}; + +/*! + * \ingroup transforms + * \brief Neurotech face detection + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + */ +class NT4DetectFace : public UntrainableTransform +{ + Q_OBJECT + + Resource contexts; + +public: + NT4DetectFace() : UntrainableTransform(true) {} + +private: + void project(const Template &src, Template &dst) const + { + HNGrayscaleImage grayscaleImage; + NT4Context::toImage(src, &grayscaleImage); + + NT4Context *context = contexts.acquire(); + + NInt faceCount; + NleFace *faces; + NResult result = NleDetectFaces(context->extractor, grayscaleImage, &faceCount, &faces); + if (NFailed(result)) qFatal("NT4DetectFace::project NleDetectFaces() failed, result=%i.", result); + for (int i=0; iextractor, grayscaleImage, &faces[i], &detectionDetails); + if (NFailed(result)) qFatal("NT4DetectFace::project NleDetectFacialFeatures() failed, result=%i.", result); + + dst.file.append(NT4Context::toMetadata(detectionDetails)); + dst += src; + //if (!Globals.EnrollAll) break; + } + + contexts.release(context); + NObjectFree(grayscaleImage); + + //if (!Globals.EnrollAll && dst.isEmpty()) dst = Mat(); + if (dst.isEmpty()) dst = Mat(); + } +}; + +BR_REGISTER(Transform, NT4DetectFace) + +/*! + * \ingroup transforms + * \brief Enroll face in Neurotech SDK 4 + * \author Josh Klontz \cite jklontz + */ +class NT4EnrollFace : public UntrainableTransform +{ + Q_OBJECT + + Resource contexts; + +public: + NT4EnrollFace() : UntrainableTransform(true) {} + +private: + void project(const Template &src, Template &dst) const + { + if (!src.m().data) { + dst = Mat(); + return; + } + + HNGrayscaleImage grayscaleImage; + NT4Context::toImage(src, &grayscaleImage); + + NT4Context *context = contexts.acquire(); + + NleDetectionDetails detectionDetails = NT4Context::fromMetadata(src.file); + NleExtractionStatus extractionStatus; + HNLTemplate templ; + + NResult result = NleExtract(context->extractor, grayscaleImage, &detectionDetails, &extractionStatus, &templ); + contexts.release(context); + + if (NFailed(result) || (extractionStatus != nleesTemplateCreated)) + dst = Mat(); + else + dst = NT4Context::toMat(templ); + + NObjectFree(templ); + NObjectFree(grayscaleImage); + } +}; + +BR_REGISTER(Transform, NT4EnrollFace) + +/*! + * \ingroup transforms + * \brief Enroll iris in Neurotech SDK 4 + * \author E. Taborsky \cite mmtaborsky + */ +class NT4EnrollIris : public UntrainableTransform +{ + Q_OBJECT + + Resource contexts; + +public: + NT4EnrollIris() : UntrainableTransform(true) {} + +private: + void project(const Template &src, Template &dst) const + { + HNGrayscaleImage grayscaleImage; + NT4Context::toImage(src, &grayscaleImage); + + NeeSegmentationDetails segmentationDetails; + NeeExtractionStatus extractionStatus; + HNERecord hRecord; + + NResult result = NERecordCreate((NUShort)src.m().cols,(NUShort)src.m().rows, 0, &hRecord); // This seems wrong... + assert(!NFailed(result)); + + NT4Context *context = contexts.acquire(); + + + result = NeeExtract(context->irisExtractor, grayscaleImage, nepUnknown, &segmentationDetails, &extractionStatus, &hRecord); + + if (!(segmentationDetails.OuterBoundaryAvailable)){ + qDebug("NT4EnrollIris::project Outer Boundary not available"); + } + + if (NFailed(result)) qFatal("NT4EnrollIris::project NeeExtract() failed, result=%i.", result); + else if (extractionStatus == neeesTemplateCreated){ + NSizeType bufferSize; + NERecordGetSize(hRecord, 0, &bufferSize); + + Mat buffer(1, bufferSize, CV_8UC1); + NERecordSaveToMemory(hRecord, buffer.data, bufferSize, 0, &bufferSize); + + dst = Template(src.file, buffer); + } + + contexts.release(context); + NObjectFree(grayscaleImage); + + //if (!Globals.EnrollAll && dst.isEmpty()) dst.append(Template(src.file, Mat())); + if (dst.isEmpty()) dst.append(Template(src.file, Mat())); + } +}; + +BR_REGISTER(Transform, NT4EnrollIris) + +/*! + * \ingroup distances + * \brief Compare templates with Neurotech SDK 4 + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + */ +class NT4Compare : public Distance +{ + Q_OBJECT + + Resource contexts; + + float compare(const br::Template &a, const br::Template &b) const + { + NT4Context *context = contexts.acquire(); + + NResult result; + + const Mat &srcA = a; + if (srcA.data) { + result = NMIdentifyStartEx(context->matcher, srcA.data, srcA.rows*srcA.cols, NULL); + if (NFailed(result)) qFatal("NT4Compare::compare NMIdentifyStart() failed, result=%i.", result); + } + + const Mat &srcB = b; + float score = -std::numeric_limits::max(); + if (srcA.data && srcB.data) { + NInt pScore; + result = NMIdentifyNextEx(context->matcher, srcB.data, srcB.rows*srcB.cols, NULL, &pScore); + if (NFailed(result)) qFatal("NT4Compare::compare NMIdentifyNext() failed, result=%i.",result); + score = float(pScore); + } + + if (srcA.data) { + result = NMIdentifyEnd(context->matcher); + if (NFailed(result)) qFatal("NT4Compare::compare NMIdentifyEnd() failed, result=%i.", result); + } + + contexts.release(context); + return score; + } +}; + +BR_REGISTER(Distance, NT4Compare) + +#include "classification/nt4.moc" diff --git a/openbr/plugins/classification/pp4.cpp b/openbr/plugins/classification/pp4.cpp new file mode 100644 index 0000000..69bac4b --- /dev/null +++ b/openbr/plugins/classification/pp4.cpp @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "openbr_internal.h" +#include "openbr/core/resource.h" + +#define TRY(CC) \ +{ \ + if ((CC) != PPR_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_error_message(CC)); \ +} + +#define TRY_VIDEO(CC) \ +{ \ + if ((CC) != PPR_VIDEO_IO_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_video_io_error_message(CC)); \ +} + +#define TRY_RAW_IMAGE(CC) \ +{ \ + if ((CC) != PPR_RAW_IMAGE_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_raw_image_error_message(CC)); \ +} + +using namespace br; + +/*! + * \ingroup initializers + * \brief Initialize PittPatt 4 + * \author Josh Klontz \cite jklontz + * \warning Needs a maintainer. + */ +class PP4Initializer : public Initializer +{ + Q_OBJECT + + void initialize() const + { + Globals->abbreviations.insert("PP4", "Open+PP4Enroll:PP4Compare"); + } + + void finalize() const + { + ppr_finalize_sdk(); + } +}; + +BR_REGISTER(Initializer, PP4Initializer) + +/*! + * \brief PittPatt 4 context + * \author Josh Klontz \cite jklontz + * \warning Needs a maintainer. + */ +struct PP4Context +{ + ppr_context_type context; + + PP4Context() + { + context = ppr_get_context(); + TRY(ppr_enable_recognition(context)) + TRY(ppr_set_license(context, my_license_id, my_license_key)) + TRY(ppr_set_models_path(context, qPrintable(Globals->sdkPath + "/models/pp4"))) + TRY(ppr_set_num_recognition_threads(context, QThreadPool::globalInstance()->maxThreadCount())) + TRY(ppr_set_num_detection_threads(context, 1)) + TRY(ppr_set_detection_precision(context, PPR_FINE_PRECISION)) + TRY(ppr_set_landmark_detector_type(context, PPR_DUAL_MULTI_POSE_LANDMARK_DETECTOR, PPR_AUTOMATIC_LANDMARKS)) + TRY(ppr_set_min_size(context, 4)) + TRY(ppr_set_frontal_yaw_constraint(context, PPR_FRONTAL_YAW_CONSTRAINT_PERMISSIVE)) + TRY(ppr_set_template_extraction_type(context, PPR_EXTRACT_DOUBLE)) + TRY(ppr_initialize_context(context)) + } + + ~PP4Context() + { + TRY(ppr_release_context(context)) + } + + static void createRawImage(const cv::Mat &src, ppr_raw_image_type &dst) + { + ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_BGR24); + assert((src.type() == CV_8UC3) && src.isContinuous()); + memcpy(dst.data, src.data, 3*src.rows*src.cols); + } + + void createMat(const ppr_template_type &src, cv::Mat &dst) const + { + ppr_flat_template_type flat_template; + TRY(ppr_flatten_template(context,src,&flat_template)) + dst = cv::Mat(1, flat_template.num_bytes, CV_8UC1, flat_template.data).clone(); + ppr_free_flat_template(flat_template); + } + + void createTemplate(const cv::Mat &src, ppr_template_type *dst) const + { + ppr_flat_template_type flat_template; + flat_template.num_bytes = src.cols; + flat_template.data = src.data; + TRY(ppr_unflatten_template(context, flat_template, dst)) + } + + static QString toString(const ppr_landmark_category_type &category) + { + switch (category) { + case PPR_LANDMARK_LEFT_EYE: + return "Left_Eye"; + case PPR_LANDMARK_RIGHT_EYE: + return "Right_Eye"; + case PPR_LANDMARK_NOSE_BASE: + return "Nose_Base"; + case PPR_LANDMARK_NOSE_BRIDGE: + return "Nose_Bridge"; + case PPR_LANDMARK_NOSE_TIP: + return "Nose_Tip"; + case PPR_LANDMARK_NOSE_TOP: + return "Nose_Top"; + case PPR_LANDMARK_EYE_NOSE: + return "Eye_Nose"; + case PPR_LANDMARK_MOUTH: + return "Mouth"; + } + + return "Unknown"; + } + + static QMap toMetadata(const ppr_object_type &object) + { + QMap metadata; + + metadata.insert("FrontalFace", QRectF(object.position.x - object.dimensions.width/2, + object.position.y - object.dimensions.height/2, + object.dimensions.width, + object.dimensions.height)); + metadata.insert("Confidence", object.confidence); + metadata.insert("PP4_Object_X", object.position.x - object.dimensions.width/2); + metadata.insert("PP4_Object_Y", object.position.y - object.dimensions.height/2); + metadata.insert("PP4_Object_Width", object.dimensions.width); + metadata.insert("PP4_Object_Height", object.dimensions.height); + metadata.insert("PP4_Object_Roll", object.rotation.roll); + metadata.insert("PP4_Object_Pitch", object.rotation.pitch); + metadata.insert("PP4_Object_Yaw", object.rotation.yaw); + metadata.insert("PP4_Object_Precision", object.rotation.precision); + metadata.insert("PP4_Object_ModelID", object.model_id); + metadata.insert("PP4_Object_NumLandmarks", object.num_landmarks); + metadata.insert("PP4_Object_Size", object.size); + + QList categories; + categories << PPR_LANDMARK_RIGHT_EYE + << PPR_LANDMARK_LEFT_EYE + << PPR_LANDMARK_NOSE_BASE + << PPR_LANDMARK_NOSE_BRIDGE + << PPR_LANDMARK_NOSE_TIP + << PPR_LANDMARK_NOSE_TOP + << PPR_LANDMARK_EYE_NOSE + << PPR_LANDMARK_MOUTH; + + for (int i=0; i contexts; + + void project(const Template &src, Template &dst) const + { + if (Globals->enrollAll) + qFatal("single template project doesn't support enrollAll"); + + TemplateList srcList; + srcList.append(src); + TemplateList dstList; + project(srcList, dstList); + dst = dstList.first(); + } + + void project(const TemplateList &srcList, TemplateList &dstList) const + { + if (srcList.empty()) + return; + + PP4Context *context = contexts.acquire(); + + foreach(const Template &src, srcList) { + if (!src.isEmpty()) { + ppr_raw_image_type raw_image; + PP4Context::createRawImage(src, raw_image); + ppr_image_type image; + TRY(ppr_create_image(raw_image, &image)) + ppr_object_list_type object_list; + TRY(ppr_detect_objects(context->context, image, &object_list)) + + QList objects; + if (Globals->enrollAll) objects = getAllObjects(object_list); + else objects = getBestObject(context, object_list); + + foreach (const ppr_object_type &object, objects) { + ppr_object_suitability_type suitability; + TRY(ppr_is_object_suitable_for_recognition(context->context, object, &suitability)) + if (suitability != PPR_OBJECT_SUITABLE_FOR_RECOGNITION && !detectOnly) continue; + + cv::Mat m; + if (detectOnly) + m = src; + else { + ppr_template_type curr_template; + TRY(ppr_extract_template_from_object(context->context, image, object, &curr_template)) + context->createMat(curr_template, m); + } + + Template dst; + dst.file = src.file; + + dst.file.append(PP4Context::toMetadata(object)); + dst += m; + dstList.append(dst); + + if (!Globals->enrollAll) + break; + } + + ppr_free_object_list(object_list); + ppr_free_image(image); + ppr_raw_image_free(raw_image); + } + + if (!Globals->enrollAll && dstList.empty()) { + dstList.append(Template(src.file, detectOnly ? src.m() : cv::Mat())); + dstList.last().file.fte = true; + } + } + + contexts.release(context); + } + +private: + QList getBestObject(PP4Context *context, ppr_object_list_type object_list) const + { + int best_index = -1; + float best_confidence = 0; + for (int i=0; icontext, object, &suitability)) + if (suitability != PPR_OBJECT_SUITABLE_FOR_RECOGNITION) continue; + if ((object.confidence > best_confidence) || + (best_index == -1)) { + best_confidence = object.confidence; + best_index = i; + } + } + + QList objects; + if (best_index != -1) objects.append(object_list.objects[best_index]); + return objects; + } + + QList getAllObjects(ppr_object_list_type object_list) const + { + QList objects; + for (int i=0; i target_template_ids, query_template_ids; + enroll(target, &target_gallery, target_template_ids); + enroll(query, &query_gallery, query_template_ids); + + ppr_similarity_matrix_type similarity_matrix; + TRY(ppr_compare_galleries(context, query_gallery, target_gallery, &similarity_matrix)) + + for (int i=0; i::max(); + if ((query_template_id != -1) && (target_template_id != -1)) { + TRY(ppr_get_similarity_matrix_element(context, similarity_matrix, query_template_id, target_template_id, &score)) + } + output->setRelative(score, i, j); + } + } + + ppr_free_similarity_matrix(similarity_matrix); + ppr_free_gallery(target_gallery); + ppr_free_gallery(query_gallery); + } + + void enroll(const TemplateList &templates, ppr_gallery_type *gallery, QList &template_ids) const + { + foreach (const Template &t, templates) { + if (t.m().data) { + ppr_template_type u; + createTemplate(t.m(), &u); + int template_id; + TRY(ppr_copy_template_to_gallery(context, gallery, u, &template_id)) + template_ids.append(template_id); + ppr_free_template(u); + } else { + template_ids.append(-1); + } + } + } +}; + +BR_REGISTER(Distance, PP4Compare) + +#include "plugins/pp4.moc" diff --git a/openbr/plugins/classification/pp5.cpp b/openbr/plugins/classification/pp5.cpp new file mode 100644 index 0000000..5627e8d --- /dev/null +++ b/openbr/plugins/classification/pp5.cpp @@ -0,0 +1,596 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "openbr_internal.h" +#include "openbr/core/resource.h" + +#define TRY(CC) \ +{ \ + if ((CC) != PPR_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_error_message(CC)); \ +} + +#define TRY_VIDEO(CC) \ +{ \ + if ((CC) != PPR_VIDEO_IO_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_video_io_error_message(CC)); \ +} + +#define TRY_RAW_IMAGE(CC) \ +{ \ + if ((CC) != PPR_RAW_IMAGE_SUCCESS) qFatal("%d error (%s, %d): %s.", CC, __FILE__, __LINE__, ppr_raw_image_error_message(CC)); \ +} + +using namespace br; + +/*! + * \ingroup initializers + * \brief Initialize PP5 + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + * \warning PittPatt 5.x.x is known to NOT work with MinGW-w64 due to a segfault in ppr_initialize_sdk. + */ +class PP5Initializer : public Initializer +{ + Q_OBJECT + + void initialize() const + { + TRY(ppr_initialize_sdk(qPrintable(Globals->sdkPath + "/share/openbr/models/pp5/"), my_license_id, my_license_key)) + Globals->abbreviations.insert("PP5","Open+Expand+PP5Enroll!PP5Gallery"); + Globals->abbreviations.insert("PP5Register", "PP5Enroll(true,true,0.02,5,Extended)+RenameFirst([eyeL,PP5_Landmark0_Right_Eye],Affine_0)+RenameFirst([eyeR,PP5_Landmark1_Left_Eye],Affine_1)"); + Globals->abbreviations.insert("PP5CropFace", "Open+PP5Enroll(true)+RenameFirst([eyeL,PP5_Landmark0_Right_Eye],Affine_0)+RenameFirst([eyeR,PP5_Landmark1_Left_Eye],Affine_1)+Affine(128,128,0.25,0.35)+Cvt(Gray)"); + } + + void finalize() const + { + ppr_finalize_sdk(); + } +}; + +BR_REGISTER(Initializer, PP5Initializer) + +/*! + * \brief PP5 context + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + */ +struct PP5Context +{ + ppr_context_type context; + + PP5Context(bool detectOnly = false, float adaptiveMinSize = 0.01f, int minSize = 4, ppr_landmark_range_type landmarkRange = PPR_LANDMARK_RANGE_COMPREHENSIVE, int searchPruningAggressiveness = 0) + { + ppr_settings_type default_settings = ppr_get_default_settings(); + + default_settings.detection.adaptive_max_size = 1.f; + default_settings.detection.adaptive_min_size = adaptiveMinSize; + default_settings.detection.detect_best_face_only = !Globals->enrollAll; + default_settings.detection.enable = 1; + default_settings.detection.min_size = minSize; + default_settings.detection.search_pruning_aggressiveness = searchPruningAggressiveness; + default_settings.detection.use_serial_face_detection = 1; + + default_settings.landmarks.enable = 1; + default_settings.landmarks.landmark_range = landmarkRange; + default_settings.landmarks.manually_detect_landmarks = 0; + + default_settings.recognition.automatically_extract_templates = !detectOnly; + default_settings.recognition.enable_comparison = !detectOnly; + default_settings.recognition.enable_extraction = !detectOnly; + default_settings.recognition.num_comparison_threads = 1; + default_settings.recognition.recognizer = PPR_RECOGNIZER_MULTI_POSE; + TRY(ppr_initialize_context(default_settings, &context)) + } + + ~PP5Context() + { + TRY(ppr_finalize_context(context)) + } + + static void createRawImage(const cv::Mat &src, ppr_raw_image_type &dst) + { + if (!src.isContinuous()) qFatal("PP5Context::createRawImage requires continuous data."); + else if (src.channels() == 3) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_BGR24); + else if (src.channels() == 1) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_GRAY8); + else qFatal("PP5Context::createRawImage invalid channel count."); + memcpy(dst.data, src.data, src.channels()*src.rows*src.cols); + } + + void createMat(const ppr_face_type &src, cv::Mat &dst) const + { + ppr_flat_data_type flat_data; + TRY(ppr_flatten_face(context,src,&flat_data)) + dst = cv::Mat(1, flat_data.length, CV_8UC1, flat_data.data).clone(); + ppr_free_flat_data(flat_data); + } + + void createFace(const cv::Mat &src, ppr_face_type *dst) const + { + ppr_flat_data_type flat_data; + flat_data.length = src.cols; + flat_data.data = src.data; + TRY(ppr_unflatten_face(context, flat_data, dst)) + } + + static QString toString(const ppr_landmark_category_type &category) + { + switch (category) { + case PPR_LANDMARK_CATEGORY_LEFT_EYE: + return "Left_Eye"; + case PPR_LANDMARK_CATEGORY_RIGHT_EYE: + return "Right_Eye"; + case PPR_LANDMARK_CATEGORY_NOSE_BASE: + return "Nose_Base"; + case PPR_LANDMARK_CATEGORY_NOSE_BRIDGE: + return "Nose_Bridge"; + case PPR_LANDMARK_CATEGORY_EYE_NOSE: + return "Eye_Nose"; + case PPR_LANDMARK_CATEGORY_LEFT_UPPER_CHEEK: + return "Left_Upper_Cheek"; + case PPR_LANDMARK_CATEGORY_LEFT_LOWER_CHEEK: + return "Left_Lower_Cheek"; + case PPR_LANDMARK_CATEGORY_RIGHT_UPPER_CHEEK: + return "Right_Upper_Cheek"; + case PPR_LANDMARK_CATEGORY_RIGHT_LOWER_CHEEK: + return "Right_Lower_Cheek"; + case PPR_NUM_LANDMARK_CATEGORIES: + return "Num_Landmark_Categories"; + } + + return "Unknown"; + } + + static QMap toMetadata(const ppr_face_type &face) + { + QMap metadata; + + ppr_face_attributes_type face_attributes; + ppr_get_face_attributes(face, &face_attributes); + metadata.insert("FrontalFace", QRectF(face_attributes.position.x - face_attributes.dimensions.width/2, + face_attributes.position.y - face_attributes.dimensions.height/2, + face_attributes.dimensions.width, + face_attributes.dimensions.height)); + metadata.insert("Confidence", face_attributes.confidence); + metadata.insert("PP5_Face_Roll", face_attributes.rotation.roll); + metadata.insert("PP5_Face_Pitch", face_attributes.rotation.pitch); + metadata.insert("PP5_Face_Yaw", face_attributes.rotation.yaw); + metadata.insert("PP5_Face_HasThumbnail", face_attributes.has_thumbnail); + metadata.insert("PP5_Face_NumLandmarks", face_attributes.num_landmarks); + metadata.insert("PP5_Face_Size", face_attributes.size); + metadata.insert("PP5_TrackingInfo_ConfidenceLevel", face_attributes.tracking_info.confidence_level); + metadata.insert("PP5_TrackingInfo_FrameNumber", face_attributes.tracking_info.frame_number); + metadata.insert("PP5_TrackingInfo_TrackID", face_attributes.tracking_info.track_id); + + ppr_landmark_list_type landmark_list; + TRY(ppr_get_face_landmarks(face, &landmark_list)) + + QList categories; + categories << PPR_LANDMARK_CATEGORY_RIGHT_EYE + << PPR_LANDMARK_CATEGORY_LEFT_EYE + << PPR_LANDMARK_CATEGORY_NOSE_BASE + << PPR_LANDMARK_CATEGORY_NOSE_BRIDGE + << PPR_LANDMARK_CATEGORY_EYE_NOSE + << PPR_LANDMARK_CATEGORY_LEFT_UPPER_CHEEK + << PPR_LANDMARK_CATEGORY_LEFT_LOWER_CHEEK + << PPR_LANDMARK_CATEGORY_RIGHT_UPPER_CHEEK + << PPR_LANDMARK_CATEGORY_RIGHT_LOWER_CHEEK; + for (int i=0; i::quiet_NaN(), std::numeric_limits::quiet_NaN())); + } + } + + ppr_free_landmark_list(landmark_list); + + return metadata; + } + + void compareNative(ppr_gallery_type target, const QList &targetIDs, ppr_gallery_type query, const QList &queryIDs, Output *output) const + { + ppr_similarity_matrix_type simmat; + TRY(ppr_compare_galleries(context, query, target, &simmat)) + for (int i=0; i::max(); + if ((query_subject_id != -1) && (target_subject_id != -1)) { + TRY(ppr_get_subject_similarity_score(context, simmat, query_subject_id, target_subject_id, &score)) + } + output->setRelative(score, i, j); + } + } + ppr_free_similarity_matrix(simmat); + } + + void enroll(const TemplateList &templates, ppr_gallery_type *gallery, QList &subject_ids) const + { + int subject_id = 0, face_id = 0; + foreach (const Template &src, templates) { + if (!src.empty() && src.m().data) { + foreach (const cv::Mat &m, src) { + ppr_face_type face; + createFace(m, &face); + TRY(ppr_add_face(context, gallery, face, subject_id, face_id)) + face_id++; + ppr_free_face(face); + } + subject_ids.append(subject_id); + subject_id++; + } else { + subject_ids.append(-1); + } + } + } + +}; + +/*! + * \ingroup transforms + * \brief Enroll faces in PP5 + * + * See PittPatt documentation for the relationship between minSize and pixel IPD. + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + */ +class PP5EnrollTransform : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(bool detectOnly READ get_detectOnly WRITE set_detectOnly RESET reset_detectOnly STORED false) + Q_PROPERTY(bool requireLandmarks READ get_requireLandmarks WRITE set_requireLandmarks RESET reset_requireLandmarks STORED false) + Q_PROPERTY(float adaptiveMinSize READ get_adaptiveMinSize WRITE set_adaptiveMinSize RESET reset_adaptiveMinSize STORED false) + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) + Q_PROPERTY(LandmarkRange landmarkRange READ get_landmarkRange WRITE set_landmarkRange RESET reset_landmarkRange STORED false) + Q_PROPERTY(int searchPruningAggressiveness READ get_searchPruningAggressiveness WRITE set_searchPruningAggressiveness RESET reset_searchPruningAggressiveness STORED false) + +public: + enum LandmarkRange + { + Frontal = PPR_LANDMARK_RANGE_FRONTAL, + Extended = PPR_LANDMARK_RANGE_EXTENDED, + Full = PPR_LANDMARK_RANGE_FULL, + Comprehensive = PPR_LANDMARK_RANGE_COMPREHENSIVE + }; + Q_ENUMS(LandmarkRange) + +private: + BR_PROPERTY(bool, detectOnly, false) + BR_PROPERTY(bool, requireLandmarks, false) + BR_PROPERTY(float, adaptiveMinSize, 0.01f) + BR_PROPERTY(int, minSize, 4) + BR_PROPERTY(LandmarkRange, landmarkRange, Comprehensive) + BR_PROPERTY(int, searchPruningAggressiveness, 0) + + Resource contexts; + + struct PP5ContextMaker : public ResourceMaker + { + PP5ContextMaker(PP5EnrollTransform *pp5EnrollTransform) + : pp5EnrollTransform(pp5EnrollTransform) {} + + private: + PP5EnrollTransform *pp5EnrollTransform; + + PP5Context *make() const + { + return new PP5Context(pp5EnrollTransform->detectOnly, + pp5EnrollTransform->adaptiveMinSize, + pp5EnrollTransform->minSize, + (ppr_landmark_range_type) pp5EnrollTransform->landmarkRange, + pp5EnrollTransform->searchPruningAggressiveness); + } + }; + + void init() + { + contexts.setResourceMaker(new PP5ContextMaker(this)); + } + + void project(const Template &src, Template &dst) const + { + if (Globals->enrollAll) + qFatal("single template project doesn't support enrollAll"); + + TemplateList srcList; + srcList.append(src); + TemplateList dstList; + project(srcList, dstList); + dst = dstList.first(); + } + + void project(const TemplateList &srcList, TemplateList &dstList) const + { + // Nothing to do here + if (srcList.empty()) + return; + + PP5Context *context = contexts.acquire(); + + foreach (const Template &src, srcList) { + bool foundFace = false; + if (!src.isEmpty()) { + ppr_raw_image_type raw_image; + PP5Context::createRawImage(src, raw_image); + ppr_image_type image; + TRY(ppr_create_image(raw_image, &image)) + ppr_face_list_type face_list; + TRY(ppr_detect_faces(context->context, image, &face_list)) + + for (int i=0; icontext, face, &extractable)) + if (!extractable) + continue; + } + foundFace = true; + + cv::Mat m; + if (detectOnly) { + m = src; + } else { + TRY(ppr_extract_face_template(context->context, image, &face)) + context->createMat(face, m); + } + Template dst; + dst.file = src.file; + + dst.file.append(PP5Context::toMetadata(face)); + if (requireLandmarks) { + QPointF right = dst.file.get("PP5_Landmark0_Right_Eye"); + QPointF left = dst.file.get("PP5_Landmark1_Left_Eye"); + QPointF nose = dst.file.get("PP5_Landmark2_Nose_Base"); + // a number not equaling itself means it's NaN + // there should be no NaNs for the 3 special landmarks + if (dst.file.get("PP5_Face_NumLandmarks") < 3 || + right.x() != right.x() || right.y() != right.y() || + left.x() != left.x() || left.y() != left.y() || + nose.x() != nose.x() || nose.y() != nose.y()) + { + dst.file.fte = true; + } + } + dst += m; + dstList.append(dst); + + // Found a face, nothing else to do (if we aren't trying to find multiple faces). + if (!Globals->enrollAll) + break; + } + + ppr_free_face_list(face_list); + ppr_free_image(image); + ppr_raw_image_free(raw_image); + } + + // No faces were detected when we were expecting one, output something with FTE set. + if (!foundFace && !Globals->enrollAll) { + dstList.append(Template(src.file, detectOnly ? src.m() : cv::Mat())); + dstList.last().file.fte = true; + } + } + + contexts.release(context); + } +}; + +BR_REGISTER(Transform, PP5EnrollTransform) + + +/*! + * \ingroup distances + * \brief Compare templates with PP5 + * \author Josh Klontz \cite jklontz + * \author E. Taborsky \cite mmtaborsky + * \note PP5 distance is known to be asymmetric + */ +class PP5CompareDistance : public UntrainableDistance + , public PP5Context +{ + Q_OBJECT + + struct NativeGallery + { + FileList files; + QList subjectIDs; + ppr_gallery_type gallery; + }; + + mutable QMap cache; + mutable QMutex cacheLock; + + ~PP5CompareDistance() + { + foreach (const NativeGallery &gallery, cache.values()) + ppr_free_gallery(gallery.gallery); + } + + float compare(const cv::Mat &target, const cv::Mat &query) const + { + return compare(Template(target), Template(query)); + } + + float compare(const Template &target, const Template &query) const + { + TemplateList targetList; + targetList.append(target); + TemplateList queryList; + queryList.append(query); + MatrixOutput *score = MatrixOutput::make(targetList.files(), queryList.files()); + compare(targetList, queryList, score); + return score->data.at(0); + } + + void compare(const TemplateList &target, const TemplateList &query, Output *output) const + { + ppr_gallery_type target_gallery, query_gallery; + ppr_create_gallery(context, &target_gallery); + ppr_create_gallery(context, &query_gallery); + QList target_subject_ids, query_subject_ids; + enroll(target, &target_gallery, target_subject_ids); + enroll(query, &query_gallery, query_subject_ids); + compareNative(target_gallery, target_subject_ids, query_gallery, query_subject_ids, output); + ppr_free_gallery(target_gallery); + ppr_free_gallery(query_gallery); + } + + NativeGallery cacheRetain(const File &gallery) const + { + QMutexLocker locker(&cacheLock); + NativeGallery nativeGallery; + if (cache.contains(gallery.name)) { + nativeGallery = cache[gallery.name]; + } else { + ppr_create_gallery(context, &nativeGallery.gallery); + TemplateList templates = TemplateList::fromGallery(gallery); + enroll(templates, &nativeGallery.gallery, nativeGallery.subjectIDs); + nativeGallery.files = templates.files(); + if (gallery.get("retain")) + cache.insert(gallery.name, nativeGallery); + } + return nativeGallery; + } + + void cacheRelease(const File &gallery, const NativeGallery &nativeGallery) const + { + QMutexLocker locker(&cacheLock); + if (cache.contains(gallery.name)) { + if (gallery.get("release")) { + cache.remove(gallery.name); + ppr_free_gallery(nativeGallery.gallery); + } + } else { + ppr_free_gallery(nativeGallery.gallery); + } + } + + bool compare(const File &targetGallery, const File &queryGallery, const File &output) const + { + if (!targetGallery.get("native") || !queryGallery.get("native")) + return false; + + NativeGallery nativeTarget = cacheRetain(targetGallery); + NativeGallery nativeQuery = cacheRetain(queryGallery); + + QScopedPointer o(Output::make(output, nativeTarget.files, nativeQuery.files)); + o->setBlock(0, 0); + compareNative(nativeTarget.gallery, nativeTarget.subjectIDs, nativeQuery.gallery, nativeQuery.subjectIDs, o.data()); + + cacheRelease(targetGallery, nativeTarget); + cacheRelease(queryGallery, nativeQuery); + return true; + } +}; + +BR_REGISTER(Distance, PP5CompareDistance) + +class PP5GalleryTransform: public UntrainableMetaTransform + , public PP5Context +{ + Q_OBJECT + Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) + BR_PROPERTY(QString, galleryName, "") + + ppr_gallery_type target; + QList targetIDs; + TemplateList gallery; + + void project(const Template &src, Template &dst) const + { + TemplateList temp, output; + temp.append(src); + project(temp, output); + if (!output.empty()) + dst = output[0]; + } + + void project(const TemplateList &src, TemplateList &dst) const + { + dst.clear(); + QList queryIDs; + + ppr_gallery_type query; + ppr_create_gallery(context, &query); + enroll(src,&query, queryIDs); + + ppr_similarity_matrix_type simmat; + + TRY(ppr_compare_galleries(context, query, target, &simmat)) + + for (int i=0; i::max(); + if ((query_subject_id != -1) && (target_subject_id != -1)) { + TRY(ppr_get_subject_similarity_score(context, simmat, query_subject_id, target_subject_id, &score)) + } + dst[i].m().at(0,j) = score; + } + } + + ppr_free_similarity_matrix(simmat); + ppr_free_gallery(query); + } + + void init() + { + if (!galleryName.isEmpty() || !gallery.isEmpty()) { + // set up the gallery + ppr_create_gallery(context, &target); + if (gallery.isEmpty() ) + gallery = TemplateList::fromGallery(galleryName); + enroll(gallery, &target, targetIDs); + } + } + + void train(const TemplateList &data) + { + gallery = data; + } + + void store(QDataStream &stream) const + { + br::Object::store(stream); + stream << gallery; + } + + void load(QDataStream &stream) + { + br::Object::load(stream); + stream >> gallery; + init(); + } + +}; + +BR_REGISTER(Transform, PP5GalleryTransform) + +#include "classification/pp5.moc" diff --git a/openbr/plugins/classification/svm.cpp b/openbr/plugins/classification/svm.cpp new file mode 100644 index 0000000..e6f42d1 --- /dev/null +++ b/openbr/plugins/classification/svm.cpp @@ -0,0 +1,264 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright 2012 The MITRE Corporation * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +static void trainSVM(SVM &svm, Mat data, Mat lab, int kernel, int type, float C, float gamma, int folds, bool balanceFolds, int termCriteria) +{ + if (data.type() != CV_32FC1) + qFatal("Expected single channel floating point training data."); + + CvSVMParams params; + params.kernel_type = kernel; + params.svm_type = type; + params.p = 0.1; + params.nu = 0.5; + params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, termCriteria, FLT_EPSILON); + + if ((C == -1) || ((gamma == -1) && (kernel == CvSVM::RBF))) { + try { + svm.train_auto(data, lab, Mat(), Mat(), params, folds, + CvSVM::get_default_grid(CvSVM::C), + CvSVM::get_default_grid(CvSVM::GAMMA), + CvSVM::get_default_grid(CvSVM::P), + CvSVM::get_default_grid(CvSVM::NU), + CvSVM::get_default_grid(CvSVM::COEF), + CvSVM::get_default_grid(CvSVM::DEGREE), + balanceFolds); + } catch (...) { + qWarning("Some classes do not contain sufficient examples or are not discriminative enough for accurate SVM classification."); + svm.train(data, lab, Mat(), Mat(), params); + } + } else { + params.C = C; + params.gamma = gamma; + svm.train(data, lab, Mat(), Mat(), params); + } + + CvSVMParams p = svm.get_params(); + qDebug("SVM C = %f Gamma = %f Support Vectors = %d", p.C, p.gamma, svm.get_support_vector_count()); +} + +/*! + * \ingroup transforms + * \brief C. Burges. "A tutorial on support vector machines for pattern recognition," + * \author Josh Klontz \cite jklontz + * Knowledge Discovery and Data Mining 2(2), 1998. + */ +class SVMTransform : public Transform +{ + Q_OBJECT + Q_ENUMS(Kernel) + Q_ENUMS(Type) + Q_PROPERTY(Kernel kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) + Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) + Q_PROPERTY(float C READ get_C WRITE set_C RESET reset_C STORED false) + Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) + Q_PROPERTY(bool returnDFVal READ get_returnDFVal WRITE set_returnDFVal RESET reset_returnDFVal STORED false) + Q_PROPERTY(int termCriteria READ get_termCriteria WRITE set_termCriteria RESET reset_termCriteria STORED false) + Q_PROPERTY(int folds READ get_folds WRITE set_folds RESET reset_folds STORED false) + Q_PROPERTY(bool balanceFolds READ get_balanceFolds WRITE set_balanceFolds RESET reset_balanceFolds STORED false) + +public: + enum Kernel { Linear = CvSVM::LINEAR, + Poly = CvSVM::POLY, + RBF = CvSVM::RBF, + Sigmoid = CvSVM::SIGMOID }; + + enum Type { C_SVC = CvSVM::C_SVC, + NU_SVC = CvSVM::NU_SVC, + ONE_CLASS = CvSVM::ONE_CLASS, + EPS_SVR = CvSVM::EPS_SVR, + NU_SVR = CvSVM::NU_SVR}; + +private: + BR_PROPERTY(Kernel, kernel, Linear) + BR_PROPERTY(Type, type, C_SVC) + BR_PROPERTY(float, C, -1) + BR_PROPERTY(float, gamma, -1) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QString, outputVariable, "") + BR_PROPERTY(bool, returnDFVal, false) + BR_PROPERTY(int, termCriteria, 1000) + BR_PROPERTY(int, folds, 5) + BR_PROPERTY(bool, balanceFolds, false) + + SVM svm; + QHash labelMap; + QHash reverseLookup; + + void train(const TemplateList &_data) + { + Mat data = OpenCVUtils::toMat(_data.data()); + Mat lab; + // If we are doing regression, the input variable should have float + // values + if (type == EPS_SVR || type == NU_SVR) { + lab = OpenCVUtils::toMat(File::get(_data, inputVariable)); + } + // If we are doing classification, we should be dealing with discrete + // values. Map them and store the mapping data + else { + QList dataLabels = _data.indexProperty(inputVariable, labelMap, reverseLookup); + lab = OpenCVUtils::toMat(dataLabels); + } + + trainSVM(svm, data, lab, kernel, type, C, gamma, folds, balanceFolds, termCriteria); + } + + void project(const Template &src, Template &dst) const + { + if (returnDFVal && reverseLookup.size() > 2) + qFatal("Decision function for multiclass classification not implemented."); + + dst = src; + float prediction = svm.predict(src.m().reshape(1, 1), returnDFVal); + if (returnDFVal) { + dst.m() = Mat(1, 1, CV_32F); + dst.m().at(0, 0) = prediction; + // positive values ==> first class + // negative values ==> second class + if (type != EPS_SVR && type != NU_SVR) + prediction = prediction > 0 ? 0 : 1; + } + if (type == EPS_SVR || type == NU_SVR) { + dst.file.set(outputVariable, prediction); + dst.m() = Mat(1, 1, CV_32F); + dst.m().at(0, 0) = prediction; + + } else + dst.file.set(outputVariable, reverseLookup[prediction]); + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(svm, stream); + stream << labelMap << reverseLookup; + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(svm, stream); + stream >> labelMap >> reverseLookup; + } + + void init() + { + if (outputVariable.isEmpty()) + outputVariable = inputVariable; + } +}; + +BR_REGISTER(Transform, SVMTransform) + +/*! + * \ingroup Distances + * \brief SVM Regression on template absolute differences. + * \author Josh Klontz + */ +class SVMDistance : public Distance +{ + Q_OBJECT + Q_ENUMS(Kernel) + Q_ENUMS(Type) + Q_PROPERTY(Kernel kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) + Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(int termCriteria READ get_termCriteria WRITE set_termCriteria RESET reset_termCriteria STORED false) + Q_PROPERTY(int folds READ get_folds WRITE set_folds RESET reset_folds STORED false) + Q_PROPERTY(bool balanceFolds READ get_balanceFolds WRITE set_balanceFolds RESET reset_balanceFolds STORED false) + +public: + enum Kernel { Linear = CvSVM::LINEAR, + Poly = CvSVM::POLY, + RBF = CvSVM::RBF, + Sigmoid = CvSVM::SIGMOID }; + + enum Type { C_SVC = CvSVM::C_SVC, + NU_SVC = CvSVM::NU_SVC, + ONE_CLASS = CvSVM::ONE_CLASS, + EPS_SVR = CvSVM::EPS_SVR, + NU_SVR = CvSVM::NU_SVR}; + +private: + BR_PROPERTY(Kernel, kernel, Linear) + BR_PROPERTY(Type, type, EPS_SVR) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(int, termCriteria, 1000) + BR_PROPERTY(int, folds, 5) + BR_PROPERTY(bool, balanceFolds, false) + + SVM svm; + + void train(const TemplateList &src) + { + const Mat data = OpenCVUtils::toMat(src.data()); + const QList lab = src.indexProperty(inputVariable); + + const int instances = data.rows * (data.rows+1) / 2; + Mat deltaData(instances, data.cols, data.type()); + Mat deltaLab(instances, 1, CV_32FC1); + int index = 0; + for (int i=0; i(index, 0) = (match ? 1 : 0); + index++; + } + deltaData = deltaData.rowRange(0, index); + deltaLab = deltaLab.rowRange(0, index); + + trainSVM(svm, deltaData, deltaLab, kernel, type, -1, -1, folds, balanceFolds, termCriteria); + } + + float compare(const Mat &a, const Mat &b) const + { + Mat delta; + absdiff(a, b, delta); + return svm.predict(delta.reshape(1, 1)); + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(svm, stream); + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(svm, stream); + } +}; + +BR_REGISTER(Distance, SVMDistance) + +} // namespace br + +#include "classification/svm.moc" diff --git a/openbr/plugins/classification/turk.cpp b/openbr/plugins/classification/turk.cpp new file mode 100644 index 0000000..ecaace7 --- /dev/null +++ b/openbr/plugins/classification/turk.cpp @@ -0,0 +1,56 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Convenience class for training turk attribute regressors + * \author Josh Klontz \cite jklontz + */ +class TurkClassifierTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) + Q_PROPERTY(QStringList values READ get_values WRITE set_values RESET reset_values STORED false) + Q_PROPERTY(bool isMeta READ get_isMeta WRITE set_isMeta RESET reset_isMeta STORED false) + BR_PROPERTY(QString, key, QString()) + BR_PROPERTY(QStringList, values, QStringList()) + BR_PROPERTY(bool, isMeta, false) + + Transform *child; + + void init() + { + QStringList classifiers; + foreach (const QString &value, values) + classifiers.append(QString("(SVM(RBF,EPS_SVR,returnDFVal=true,inputVariable=%1,outputVariable=predicted_%1)%2)").arg(key + "_" + value, isMeta ? QString("+Average+SaveMat(predicted_%1)").arg(value) : QString())); + child = Transform::make(classifiers.join("/") + (classifiers.size() > 1 ? "+Cat" : "")); + } + + void train(const QList &data) + { + child->train(data); + } + + void project(const Template &src, Template &dst) const + { + child->project(src, dst); + } + + void store(QDataStream &stream) const + { + child->store(stream); + } + + void load(QDataStream &stream) + { + child->load(stream); + } +}; + +BR_REGISTER(Transform, TurkClassifierTransform) + +} // namespace br + +#include "classification/turk.moc" diff --git a/openbr/plugins/cluster/collectnn.cpp b/openbr/plugins/cluster/collectnn.cpp index 74f2eff..00d1764 100644 --- a/openbr/plugins/cluster/collectnn.cpp +++ b/openbr/plugins/cluster/collectnn.cpp @@ -39,4 +39,4 @@ BR_REGISTER(Transform, CollectNNTransform) } // namespace br -#include "collectnn.moc" +#include "cluster/collectnn.moc" diff --git a/openbr/plugins/cluster/kmeans.cpp b/openbr/plugins/cluster/kmeans.cpp index 4f30c31..475a30d 100644 --- a/openbr/plugins/cluster/kmeans.cpp +++ b/openbr/plugins/cluster/kmeans.cpp @@ -62,4 +62,4 @@ BR_REGISTER(Transform, KMeansTransform) } // namespace br -#include "kmeans.moc" +#include "cluster/kmeans.moc" diff --git a/openbr/plugins/cluster/knn.cpp b/openbr/plugins/cluster/knn.cpp index 60fed0f..c1030e5 100644 --- a/openbr/plugins/cluster/knn.cpp +++ b/openbr/plugins/cluster/knn.cpp @@ -81,4 +81,4 @@ BR_REGISTER(Transform, KNNTransform) } // namespace br -#include "knn.moc" +#include "cluster/knn.moc" diff --git a/openbr/plugins/cluster/lognn.cpp b/openbr/plugins/cluster/lognn.cpp index 0cdab6e..db2ba40 100644 --- a/openbr/plugins/cluster/lognn.cpp +++ b/openbr/plugins/cluster/lognn.cpp @@ -62,4 +62,4 @@ BR_REGISTER(Transform, LogNNTransform) } // namespace br -#include "lognn.moc" +#include "cluster/lognn.moc" diff --git a/openbr/plugins/cluster/randomcentroids.cpp b/openbr/plugins/cluster/randomcentroids.cpp index d9b8c9d..c7a7016 100644 --- a/openbr/plugins/cluster/randomcentroids.cpp +++ b/openbr/plugins/cluster/randomcentroids.cpp @@ -65,4 +65,4 @@ BR_REGISTER(Transform, RandomCentroidsTransform) } //namespace br -#include "randomcentroids.moc" +#include "cluster/randomcentroids.moc" diff --git a/openbr/plugins/cmake/eigen3.cmake b/openbr/plugins/cmake/eigen3.cmake new file mode 100644 index 0000000..95c018c --- /dev/null +++ b/openbr/plugins/cmake/eigen3.cmake @@ -0,0 +1,9 @@ +set(BR_WITH_EIGEN3 ON CACHE BOOL "Build Eigen3 plugins") + +if(${BR_WITH_EIGEN3}) + find_package(Eigen3 REQUIRED) + install(FILES ${EIGEN3_LICENSE} RENAME Eigen3 DESTINATION share/openbr/licenses) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/lda.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/revertaffine.cpp) +endif() diff --git a/openbr/plugins/cmake/ipc2013.cmake b/openbr/plugins/cmake/ipc2013.cmake new file mode 100644 index 0000000..617ac4d --- /dev/null +++ b/openbr/plugins/cmake/ipc2013.cmake @@ -0,0 +1,9 @@ +set(BR_WITH_IPC2013 OFF CACHE BOOL "Build with Intel Perceptual Computing SDK 2013") + +if(${BR_WITH_IPC2013}) + find_package(IPC2013 REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${IPC2013_LIBS}) + install(DIRECTORY ${IPC2013_DIR}/bin/x64/ DESTINATION bin) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/ipc2013.cpp) +endif() diff --git a/openbr/plugins/cmake/jni.cmake b/openbr/plugins/cmake/jni.cmake new file mode 100644 index 0000000..41cc5ba --- /dev/null +++ b/openbr/plugins/cmake/jni.cmake @@ -0,0 +1,12 @@ +set(BR_WITH_JAVA OFF CACHE BOOL "Use Java Code") + +if (${BR_WITH_JAVA}) + find_package(JNI REQUIRED) + find_package(JAVA REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${JNI_LIBRARIES}) + + include_directories(${JAVA_INCLUDE_PATH}) + include_directories(${JAVA_INCLUDE_PATH2}) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/core/jni.cpp) +endif() diff --git a/openbr/plugins/cmake/liblinear.cmake b/openbr/plugins/cmake/liblinear.cmake new file mode 100644 index 0000000..431a64c --- /dev/null +++ b/openbr/plugins/cmake/liblinear.cmake @@ -0,0 +1,8 @@ +set(BR_WITH_LIBLINEAR OFF CACHE BOOL "Build with LibLinear") + +if(${BR_WITH_LIBLINEAR}) + find_package(LibLinear REQUIRED) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${LibLinear_SRC}) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/liblinear.cpp) +endif() diff --git a/openbr/plugins/cmake/likely.cmake b/openbr/plugins/cmake/likely.cmake new file mode 100644 index 0000000..ba894d6 --- /dev/null +++ b/openbr/plugins/cmake/likely.cmake @@ -0,0 +1,10 @@ +set(BR_WITH_LIKELY OFF CACHE BOOL "Build with Likely") + +if(${BR_WITH_LIKELY}) + find_package(Likely REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Likely_LIBS}) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/core/likely.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/format/lmat.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/gallery/lmat.cpp) +endif() diff --git a/openbr/plugins/cmake/mongoose.cmake b/openbr/plugins/cmake/mongoose.cmake new file mode 100644 index 0000000..729365a --- /dev/null +++ b/openbr/plugins/cmake/mongoose.cmake @@ -0,0 +1,8 @@ +set(BR_WITH_MONGOOSE OFF CACHE BOOL "Build with Mongoose") +if(${BR_WITH_MONGOOSE}) + find_package(Mongoose) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${MONGOOSE_SRC}) + install(FILES ${MONGOOSE_LICENSE} RENAME mongoose DESTINATION share/openbr/licenses) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/mongoose.cpp) +endif() diff --git a/openbr/plugins/cmake/network.cmake b/openbr/plugins/cmake/network.cmake new file mode 100644 index 0000000..46899ee --- /dev/null +++ b/openbr/plugins/cmake/network.cmake @@ -0,0 +1,12 @@ +set(BR_WITH_QTNETWORK ON CACHE BOOL "Build with QtNetwork") +if(${BR_WITH_QTNETWORK}) + find_package(Qt5Network) + find_package(HttpParser) + set(QT_DEPENDENCIES ${QT_DEPENDENCIES} Network) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${HTTPPARSER_SRC}) + install(FILES ${HTTPPARSER_LICENSE} RENAME http-parser DESTINATION share/openbr/licenses) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/format/urlformat.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/format/postformat.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/gallery/postgallery.cpp) +endif() diff --git a/openbr/plugins/cmake/nt4.cmake b/openbr/plugins/cmake/nt4.cmake new file mode 100644 index 0000000..bdba899 --- /dev/null +++ b/openbr/plugins/cmake/nt4.cmake @@ -0,0 +1,9 @@ +set(BR_WITH_NT4 OFF CACHE BOOL "Build with Neurotec Biometric 4") + +if(${BR_WITH_NT4}) + find_package(NT4 REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${NT4_LIBS}) + install(DIRECTORY ${NT4_DIR_LIB}/ DESTINATION lib) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/nt4.cpp) +endif() diff --git a/openbr/plugins/cmake/pp4.cmake b/openbr/plugins/cmake/pp4.cmake new file mode 100644 index 0000000..95743a7 --- /dev/null +++ b/openbr/plugins/cmake/pp4.cmake @@ -0,0 +1,10 @@ +set(BR_WITH_PP4 OFF CACHE BOOL "Build with PittPatt 4") + +if(${BR_WITH_PP4}) + find_package(PP4 REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${PP4_LIBS}) + install(DIRECTORY ${PP4_DIR}/lib/ DESTINATION lib) + install(DIRECTORY ${PP4_DIR}/models/ DESTINATION models/pp4) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/pp4.cpp) +endif() diff --git a/openbr/plugins/cmake/pp5.cmake b/openbr/plugins/cmake/pp5.cmake new file mode 100644 index 0000000..2df45b6 --- /dev/null +++ b/openbr/plugins/cmake/pp5.cmake @@ -0,0 +1,16 @@ +set(BR_WITH_PP5 OFF CACHE BOOL "Build with PittPatt 5") + +if(${BR_WITH_PP5}) + find_package(PP5 REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${PP5_LIBS}) + + if(WIN32) + install(DIRECTORY ${PP5_DIR}/lib/ DESTINATION bin) + else() + install(DIRECTORY ${PP5_DIR}/lib/ DESTINATION lib) + endif() + + install(DIRECTORY ${PP5_DIR}/models/ DESTINATION share/openbr/models/pp5) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/pp5.cpp) +endif() diff --git a/openbr/plugins/cmake/show.cmake b/openbr/plugins/cmake/show.cmake new file mode 100644 index 0000000..54e924a --- /dev/null +++ b/openbr/plugins/cmake/show.cmake @@ -0,0 +1,3 @@ +if(${BR_EMBEDDED}) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/gui/show.cpp) +endif() diff --git a/openbr/plugins/cmake/stasm4.cmake b/openbr/plugins/cmake/stasm4.cmake new file mode 100644 index 0000000..fa52d2b --- /dev/null +++ b/openbr/plugins/cmake/stasm4.cmake @@ -0,0 +1,17 @@ +set(BR_WITH_STASM4 OFF CACHE BOOL "Build with Stasm") + +if(${BR_WITH_STASM4}) + find_package(Stasm4 REQUIRED) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Stasm4_LIBS}) + + if(WIN32) + install(DIRECTORY ${Stasm_DIR}/build/ DESTINATION bin) + else() + install(DIRECTORY ${Stasm_DIR}/build/ DESTINATION lib) + endif() + + install(DIRECTORY ${Stasm_DIR}/data/ DESTINATION share/openbr/models/stasm) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/stasm4.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/revertaffine.cpp) +endif() diff --git a/openbr/plugins/core/algorithms.cpp b/openbr/plugins/core/algorithms.cpp index 2293e65..9d61dd1 100644 --- a/openbr/plugins/core/algorithms.cpp +++ b/openbr/plugins/core/algorithms.cpp @@ -108,4 +108,4 @@ BR_REGISTER(Initializer, AlgorithmsInitializer) } // namespace br -#include "algorithms.moc" +#include "core/algorithms.moc" diff --git a/openbr/plugins/core/attributealgorithms.cpp b/openbr/plugins/core/attributealgorithms.cpp new file mode 100644 index 0000000..7915e31 --- /dev/null +++ b/openbr/plugins/core/attributealgorithms.cpp @@ -0,0 +1,137 @@ +#include + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Initializes global abbreviations with implemented algorithms for attributes + * \author Babatunde Ogunfemi \cite baba1472 + */ +class AttributeAlgorithmsInitializer : public Initializer +{ + Q_OBJECT + + void initialize() const + { + // Constants + QString BASE="Open+PP5Register+Rename(PP5_Landmark0_Right_Eye,Affine_0)+Rename(PP5_Landmark1_Left_Eye,Affine_1)+Affine(192,240,.345,.475)+Cvt(Gray)+Stasm(false,true,[(66.24,114),(125.76,114)])"; + QString SUBSPACE ="Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,4,4)+Hist(59)+Cat+PCA(0.95)"; + + QString NOSE="RectFromStasmNoseWithBridge+ROI+Resize(36,24)+" + SUBSPACE; + QString MOUTH="RectFromStasmMouth+ROI+Resize(24,36)+" + SUBSPACE; + QString EYES="RectFromStasmEyes+ROI+Resize(24,36)+" + SUBSPACE; + QString HAIR="RectFromStasmHair+ROI+Resize(24,36)+" + SUBSPACE; + QString BROW="RectFromStasmBrow+ROI+Resize(24,36)+" + SUBSPACE; + QString JAW="RectFromStasmJaw+ROI+Resize(36,36)+" + SUBSPACE; + QString FACE = "Crop(24,30,144,190)+Resize(36,36)+" + SUBSPACE; + + // All Attributes + Globals->abbreviations.insert("AllAttributes", "AttributeBrow/AttributeMouth/AttributeEyes/AttributeFace/AttributeHair/AttributeNose/AttributeJaw"); + Globals->abbreviations.insert("AllAttributesMatching", "(AttributeBrow)/(AttributeMouth)/(AttributeEyes)/(AttributeFace)/(AttributeHair)/(AttributeNose)/(AttributeJaw):AttributeMatch"); + + //Individual Attributes + Globals->abbreviations.insert("AttributeBrow", "(" + BASE+ "+" + BROW + "+" + "TurkClassifier(eyebrowposition,[closebrows,highbrows],3)/" + "TurkClassifier(unibrow,[unibrow],3)/" + "TurkClassifier(eyebroworientation,[eyebrowsdown,eyebrowsuptodown],3)/" + "TurkClassifier(thickeyebrows,[thickeyebrows,lighteyebrows],3))"); + Globals->abbreviations.insert("AttributeMouth", "(" + BASE + "+" + MOUTH + "+" + "TurkClassifier(smiling,[smiling],3)/" + "TurkClassifier(lipthickness,[cherry,big,small],3)/" + "TurkClassifier(mouthbite,[underbite,overbite],3)/" + "TurkClassifier(mouthopen,[closed,noteeth,halfteeth,allteeth],3)/" + "TurkClassifier(mouthwidth,[small,wide],3)/" + "TurkClassifier(mustache,[nomustache,linemustache,lightmustache,normalmustache,down],3)/" + "TurkClassifier(mouthasymmetry,[asymmetrical],3))"); + Globals->abbreviations.insert("AttributeEyes", "(" + BASE + "+" + EYES + "+ " + "TurkClassifier(eyeseparation,[close,wide],3)/" + "TurkClassifier(eyeslant,[slant2,slant1,wild],3)/" + "TurkClassifier(benteyes,[bent])/" + "TurkClassifier(eyecolor,[darkeyes,lighteyes],3)/" + "TurkClassifier(baggyeyes,[baggy],3)/" + "TurkClassifier(almondeyes,[almond],3)/" + "TurkClassifier(buriedeyes,[buriedeyes],3)/" + "TurkClassifier(sleepyeyes,[sleepy],3)/" + "TurkClassifier(lineeyes,[line],3)/" + "TurkClassifier(roundeyes,[round],3)/" + "TurkClassifier(sharpeyes,[sharp],3)/" + "TurkClassifier(smalleyes,[smalleyes],3)/" + "TurkClassifier(glasses,[glasses],3)/" + "TurkClassifier(eyelashvisibility,[feweyelashes],3))"); + Globals->abbreviations.insert("AttributeFace", "(" + BASE + "+" + FACE + "+" + "TurkClassifier(gender,[male],3)/" + "TurkClassifier(faceshape,[round,triangular,rectangular],3)/" + "TurkClassifier(cheekdensity,[puffy,in,normal],3)/" + "TurkClassifier(facemarks,[scars,moles,normal],3)/" + "TurkClassifier(facelength,[long],3)/" + "TurkClassifier(nosetoeyedist,[short,long],3)/" + "TurkClassifier(nosetomouthdist,[long,small],3))"); + Globals->abbreviations.insert("AttributeHair", "(" + BASE + "+" + HAIR + "+" + "TurkClassifier(foreheadwrinkles,[wrinkled],3)/" + "TurkClassifier(foreheadsize,[smallforehead,largeforehead],3)/" + "TurkClassifier(haircolor,[darkhair,lighthair,greyhair],3)/" + "TurkClassifier(hairdensity,[thick,bald,thin,halfbald],3)/" + "TurkClassifier(widowspeak,[widowspeak],3)/" + "TurkClassifier(hairstyle,[curlyhair],3))"); + Globals->abbreviations.insert("AttributeNose", "(" + BASE + "+" + NOSE + "+" + "TurkClassifier(noseorientation,[upnose,downnose],3)/" + "TurkClassifier(nosewidth,[small,thick],3)/" + "TurkClassifier(nosesize,[smallnose,bignose],3)/" + "TurkClassifier(brokennose,[broken],3))"); + Globals->abbreviations.insert("AttributeJaw", "(" + BASE + "+" + JAW + "+" + "TurkClassifier(beard,[nobeard,bigbeard,lightbeard,goatee,linebeard,normalbeard,lincolnbeard],3)/" + "TurkClassifier(chinsize,[shortchin,longchin],3))"); + Globals->abbreviations.insert("AttributeMatch", "Fuse([" + "Turk(eyebrowposition,[closebrows,highbrows],3)," + "Turk(unibrow,[unibrow],3)," + "Turk(eyebroworientation,[eyebrowsdown,eyebrowsuptodown],3)," + "Turk(thickeyebrows,[thickeyebrows,lighteyebrows],3)," + "Turk(smiling,[smiling],3)," + "Turk(lipthickness,[cherry,big,small],3)," + "Turk(mouthbite,[underbite,overbite],3)," + "Turk(mouthopen,[closed,noteeth,halfteeth,allteeth],3)," + "Turk(mouthwidth,[small,wide],3)," + "Turk(mustache,[nomustache,linemustache,lightmustache,normalmustache,down],3)," + "Turk(mouthasymmetry,[asymmetrical],3)," + "Turk(eyeseparation,[close,wide],3)," + "Turk(eyeslant,[slant2,slant1,wild],3)," + "Turk(benteyes,[bent],3)," + "Turk(eyecolor,[darkeyes,lighteyes],3)," + "Turk(baggyeyes,[baggy],3)," + "Turk(almondeyes,[almond],3)," + "Turk(buriedeyes,[buriedeyes],3)," + "Turk(sleepyeyes,[sleepy],3)," + "Turk(lineeyes,[line],3)," + "Turk(roundeyes,[round],3)," + "Turk(sharpeyes,[sharp],3)," + "Turk(smalleyes,[smalleyes],3)," + "Turk(glasses,[glasses],3)," + "Turk(eyelashvisibility,[feweyelashes],3)," + "Turk(gender,[male],3)," + "Turk(faceshape,[round,triangular,rectangular],3)," + "Turk(cheekdensity,[puffy,in,normal],3)," + "Turk(facemarks,[scars,moles,normal],3)," + "Turk(facelength,[long],3)," + "Turk(nosetoeyedist,[short,long],3)," + "Turk(nosetomouthdist,[long,small],3)," + "Turk(foreheadwrinkles,[wrinkled],3)," + "Turk(foreheadsize,[smallforehead,largeforehead],3)," + "Turk(haircolor,[darkhair,lighthair,greyhair],3)," + "Turk(hairdensity,[thick,bald,thin,halfbald],3)," + "Turk(widowspeak,[widowspeak],3)," + "Turk(hairstyle,[curlyhair],3)," + "Turk(noseorientation,[upnose,downnose],3)," + "Turk(nosewidth,[small,thick],3)," + "Turk(nosesize,[smallnose,bignose],3)," + "Turk(brokennose,[broken],3)," + "Turk(beard,[nobeard,bigbeard,lightbeard,goatee,linebeard,normalbeard,lincolnbeard],3)," + "Turk(chinsize,[shortchin,longchin],3)])"); + } +}; + +BR_REGISTER(Initializer, AttributeAlgorithmsInitializer) + +} // namespace br + +#include "core/attributealgorithms.moc" diff --git a/openbr/plugins/core/cache.cpp b/openbr/plugins/core/cache.cpp new file mode 100644 index 0000000..fa07aad --- /dev/null +++ b/openbr/plugins/core/cache.cpp @@ -0,0 +1,79 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Caches br::Transform::project() results. + * \author Josh Klontz \cite jklontz + */ +class CacheTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(br::Transform*, transform, NULL) + + static QHash cache; + static QMutex cacheLock; + +public: + ~CacheTransform() + { + if (cache.isEmpty()) return; + + // Write to cache + QFile file("Cache"); + if (!file.open(QFile::WriteOnly)) + qFatal("Unable to open %s for writing.", qPrintable(file.fileName())); + QDataStream stream(&file); + stream << cache; + file.close(); + } + +private: + void init() + { + if (!transform) return; + + trainable = transform->trainable; + if (!cache.isEmpty()) return; + + // Read from cache + QFile file("Cache"); + if (file.exists()) { + if (!file.open(QFile::ReadOnly)) + qFatal("Unable to open %s for reading.", qPrintable(file.fileName())); + QDataStream stream(&file); + stream >> cache; + file.close(); + } + } + + void train(const QList &data) + { + transform->train(data); + } + + void project(const Template &src, Template &dst) const + { + const QString &file = src.file; + if (cache.contains(file)) { + dst = cache[file]; + } else { + transform->project(src, dst); + cacheLock.lock(); + cache[file] = dst; + cacheLock.unlock(); + } + } +}; + +QHash CacheTransform::cache; +QMutex CacheTransform::cacheLock; + +BR_REGISTER(Transform, CacheTransform) + +} // namespace br + +#include "core/cache.moc" diff --git a/openbr/plugins/core/contract.cpp b/openbr/plugins/core/contract.cpp new file mode 100644 index 0000000..d23fe57 --- /dev/null +++ b/openbr/plugins/core/contract.cpp @@ -0,0 +1,45 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief It's like the opposite of ExpandTransform, but not really + * \author Charles Otto \cite caotto + * + * Given a set of templatelists as input, concatenate them onto a single Template + */ +class ContractTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + virtual void project(const TemplateList &src, TemplateList &dst) const + { + if (src.empty()) return; + Template out; + + foreach (const Template &t, src) { + out.merge(t); + } + out.file.clearRects(); + foreach (const Template &t, src) { + if (!t.file.rects().empty()) + out.file.appendRects(t.file.rects()); + } + dst.clear(); + dst.append(out); + } + + virtual void project(const Template &src, Template &dst) const + { + qFatal("this has gone bad"); + (void) src; (void) dst; + } +}; + +BR_REGISTER(Transform, ContractTransform) + +} // namespace br + +#include "core/contract.moc" diff --git a/openbr/plugins/core/crossvalidate.cpp b/openbr/plugins/core/crossvalidate.cpp new file mode 100644 index 0000000..d0d9066 --- /dev/null +++ b/openbr/plugins/core/crossvalidate.cpp @@ -0,0 +1,138 @@ +#include + +#include +#include + +namespace br +{ + +static void _train(Transform *transform, TemplateList data) // think data has to be a copy -cao +{ + transform->train(data); +} + +/*! + * \ingroup transforms + * \brief Cross validate a trainable transform. + * \author Josh Klontz \cite jklontz + * \author Scott Klum \cite sklum + * \note To use an extended gallery, add an allPartitions="true" flag to the gallery sigset for those images that should be compared + * against for all testing partitions. + */ +class CrossValidateTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + Q_PROPERTY(bool leaveOneImageOut READ get_leaveOneImageOut WRITE set_leaveOneImageOut RESET reset_leaveOneImageOut STORED false) + BR_PROPERTY(QString, description, "Identity") + BR_PROPERTY(bool, leaveOneImageOut, false) + + // numPartitions copies of transform specified by description. + QList transforms; + + // Treating this transform as a leaf (in terms of update training scheme), the child transform + // of this transform will lose any structure present in the training QList, which + // is generally incorrect behavior. + void train(const TemplateList &data) + { + int numPartitions = 0; + QList partitions; partitions.reserve(data.size()); + foreach (const File &file, data.files()) { + partitions.append(file.get("Partition", 0)); + numPartitions = std::max(numPartitions, partitions.last()+1); + } + + while (transforms.size() < numPartitions) + transforms.append(make(description)); + + if (numPartitions < 2) { + transforms.first()->train(data); + return; + } + + QFutureSynchronizer futures; + for (int i=0; i partitionsBuffer = partitions; + TemplateList partitionedData = data; + int j = partitionedData.size()-1; + while (j>=0) { + // Remove all templates belonging to partition i + // if leaveOneImageOut is true, + // and i is greater than the number of images for a particular subject + // even if the partitions are different + if (leaveOneImageOut) { + const QString label = partitionedData.at(j).file.get("Label"); + QList subjectIndices = partitionedData.find("Label",label); + QList removed; + // Remove target only data + for (int k=subjectIndices.size()-1; k>=0; k--) + if (partitionedData[subjectIndices[k]].file.getBool("targetOnly")) { + removed.append(subjectIndices[k]); + subjectIndices.removeAt(k); + } + // Remove template that was repeated to make the testOnly template + if (subjectIndices.size() > 1 && subjectIndices.size() <= i) { + removed.append(subjectIndices[i%subjectIndices.size()]); + } else if (partitionsBuffer[j] == i) { + removed.append(j); + } + + if (!removed.empty()) { + typedef QPair Pair; + foreach (Pair pair, Common::Sort(removed,true)) { + partitionedData.removeAt(pair.first); partitionsBuffer.removeAt(pair.first); j--; + } + } else { + j--; + } + } else if (partitions[j] == i) { + // Remove data, it's designated for testing + partitionedData.removeAt(j); + j--; + } else j--; + } + // Train on the remaining templates + futures.addFuture(QtConcurrent::run(_train, transforms[i], partitionedData)); + } + futures.waitForFinished(); + } + + void project(const Template &src, Template &dst) const + { + // Remember, the partition should never be -1 + // since it is assumed that the allPartitions + // flag is only used during comparison + // (i.e. only used when making a mask) + + // If we want to duplicate templates but use the same training data + // for all partitions (i.e. transforms.size() == 1), we need to + // restrict the partition + + int partition = src.file.get("Partition", 0); + partition = (partition >= transforms.size()) ? 0 : partition; + transforms[partition]->project(src, dst); + } + + void store(QDataStream &stream) const + { + stream << transforms.size(); + foreach (Transform *transform, transforms) + transform->store(stream); + } + + void load(QDataStream &stream) + { + int numTransforms; + stream >> numTransforms; + while (transforms.size() < numTransforms) + transforms.append(make(description)); + foreach (Transform *transform, transforms) + transform->load(stream); + } +}; + +BR_REGISTER(Transform, CrossValidateTransform) + +} // namespace br + +#include "core/crossvalidate.moc" diff --git a/openbr/plugins/core/discard.cpp b/openbr/plugins/core/discard.cpp new file mode 100644 index 0000000..56c62a8 --- /dev/null +++ b/openbr/plugins/core/discard.cpp @@ -0,0 +1,26 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes all template's matrices. + * \see IdentityTransform FirstTransform RestTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class DiscardTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst.file = src.file; + } +}; + +BR_REGISTER(Transform, DiscardTransform) + +} // namespace br + +#include "core/discard.moc" diff --git a/openbr/plugins/core/discardtemplates.cpp b/openbr/plugins/core/discardtemplates.cpp new file mode 100644 index 0000000..cf3d7bc --- /dev/null +++ b/openbr/plugins/core/discardtemplates.cpp @@ -0,0 +1,26 @@ +#include + +namespace br +{ + +class DiscardTemplatesTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + (void) src; (void) dst; + qFatal("Incorrect project called on DiscardTemplatesTransform"); + } + void project(const TemplateList &src, TemplateList &dst) const + { + (void) src; + dst.clear(); + } +}; + +BR_REGISTER(Transform, DiscardTemplatesTransform) + +} // namespace br + +#include "core/discardtemplates.moc" diff --git a/openbr/plugins/core/distributetemplate.cpp b/openbr/plugins/core/distributetemplate.cpp new file mode 100644 index 0000000..53298f1 --- /dev/null +++ b/openbr/plugins/core/distributetemplate.cpp @@ -0,0 +1,126 @@ +#include + +#include + +namespace br +{ + +static void _projectList(const Transform *transform, const TemplateList *src, TemplateList *dst) +{ + transform->project(*src, *dst); +} + +class DistributeTemplateTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(br::Transform*, transform, NULL) + +public: + + Transform *smartCopy(bool &newTransform) + { + if (!transform->timeVarying()) { + newTransform = false; + return this; + } + newTransform = true; + + DistributeTemplateTransform *output = new DistributeTemplateTransform; + bool newChild = false; + output->transform = transform->smartCopy(newChild); + if (newChild) + output->transform->setParent(output); + + return output; + } + + void train(const QList &data) + { + if (!transform->trainable) { + qWarning("Attempted to train untrainable transform, nothing will happen."); + return; + } + + QList separated; + foreach (const TemplateList &list, data) { + foreach (const Template &t, list) { + separated.append(TemplateList()); + separated.last().append(t); + } + } + + transform->train(separated); + } + + void project(const Template &src, Template &dst) const + { + TemplateList input; + input.append(src); + TemplateList output; + project(input, output); + + if (output.size() != 1) qFatal("output contains more than 1 template"); + else dst = output[0]; + } + + // For each input template, form a single element TemplateList, push all those + // lists through transform, and form dst by concatenating the results. + // Process the single elemnt templates in parallel if parallelism is enabled. + void project(const TemplateList &src, TemplateList &dst) const + { + // Pre-allocate output for each template + QList output_buffer; + output_buffer.reserve(src.size()); + + // Can't declare this local to the loop because it would go out of scope + QList input_buffer; + input_buffer.reserve(src.size()); + + QFutureSynchronizer futures; + + for (int i =0; i < src.size();i++) { + input_buffer.append(TemplateList()); + output_buffer.append(TemplateList()); + } + QList > temp; + temp.reserve(src.size()); + for (int i=0; iparallelism > 1) temp.append(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i])); + else _projectList(transform, &input_buffer[i], &output_buffer[i]); + } + // We add the futures in reverse order, since in Qt 5.1 at least the + // waiting thread will wait on them in the order added (which for uniform priority + // threads is the order of execution), and we want the waiting thread to go in the opposite order + // so that it can steal runnables and do something besides wait. + for (int i = temp.size() - 1; i >= 0; i--) { + futures.addFuture(temp[i]); + } + + futures.waitForFinished(); + + for (int i=0; iproject(src, dst); + return; + } + + void init() + { + if (!transform) + return; + + trainable = transform->trainable; + } + +}; +BR_REGISTER(Transform, DistributeTemplateTransform) + +} // namespace br + +#include "core/distributetemplate.moc" diff --git a/openbr/plugins/core/downsample.cpp b/openbr/plugins/core/downsample.cpp deleted file mode 100644 index b1cc62f..0000000 --- a/openbr/plugins/core/downsample.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include - -namespace br -{ - -static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString &inputVariable, const QStringList &gallery, const QStringList &subjects) -{ - // Return early when no downsampling is required - if ((classes == std::numeric_limits::max()) && - (instances == std::numeric_limits::max()) && - (fraction >= 1) && - (gallery.isEmpty()) && - (subjects.isEmpty())) - return templates; - - const bool atLeast = instances < 0; - instances = abs(instances); - - QList allLabels = File::get(templates, inputVariable); - - QList uniqueLabels = allLabels.toSet().toList(); - qSort(uniqueLabels); - - QMap counts = templates.countValues(inputVariable, instances != std::numeric_limits::max()); - - if ((instances != std::numeric_limits::max()) && (classes != std::numeric_limits::max())) - foreach (const QString &label, counts.keys()) - if (counts[label] < instances) - counts.remove(label); - - uniqueLabels = counts.keys(); - if ((classes != std::numeric_limits::max()) && (uniqueLabels.size() < classes)) - qWarning("Downsample requested %d classes but only %d are available.", classes, uniqueLabels.size()); - - QList selectedLabels = uniqueLabels; - if (classes < uniqueLabels.size()) { - std::random_shuffle(selectedLabels.begin(), selectedLabels.end()); - selectedLabels = selectedLabels.mid(0, classes); - } - - TemplateList downsample; - for (int i=0; i indices; - for (int j=0; j("FTE", false)) && (!templates.value(j).file.get("PossibleFTE", false))) - indices.append(j); - - std::random_shuffle(indices.begin(), indices.end()); - const int max = atLeast ? indices.size() : std::min(indices.size(), instances); - for (int j=0; j=0; i--) - if (!gallery.contains(downsample[i].file.get("Gallery"))) - downsample.removeAt(i); - - if (!subjects.isEmpty()) - for (int i=downsample.size()-1; i>=0; i--) - if (subjects.contains(downsample[i].file.get(inputVariable))) - downsample.removeAt(i); - - return downsample; -} - -class DownsampleTrainingTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED true) - Q_PROPERTY(int classes READ get_classes WRITE set_classes RESET reset_classes STORED false) - Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false) - Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false) - Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) - Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false) - Q_PROPERTY(QStringList subjects READ get_subjects WRITE set_subjects RESET reset_subjects STORED false) - BR_PROPERTY(br::Transform*, transform, NULL) - BR_PROPERTY(int, classes, std::numeric_limits::max()) - BR_PROPERTY(int, instances, std::numeric_limits::max()) - BR_PROPERTY(float, fraction, 1) - BR_PROPERTY(QString, inputVariable, "Label") - BR_PROPERTY(QStringList, gallery, QStringList()) - BR_PROPERTY(QStringList, subjects, QStringList()) - - - Transform *simplify(bool &newTForm) - { - Transform *res = transform->simplify(newTForm); - return res; - } - - void project(const Template &src, Template &dst) const - { - transform->project(src,dst); - } - - - void train(const TemplateList &data) - { - if (!transform || !transform->trainable) - return; - - TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery, subjects); - - transform->train(downsampled); - } -}; - -BR_REGISTER(Transform, DownsampleTrainingTransform) - -} // namespace br - -#include "downsample.moc" diff --git a/openbr/plugins/core/downsampletraining.cpp b/openbr/plugins/core/downsampletraining.cpp new file mode 100644 index 0000000..8f3d38c --- /dev/null +++ b/openbr/plugins/core/downsampletraining.cpp @@ -0,0 +1,119 @@ +#include + +namespace br +{ + +static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString &inputVariable, const QStringList &gallery, const QStringList &subjects) +{ + // Return early when no downsampling is required + if ((classes == std::numeric_limits::max()) && + (instances == std::numeric_limits::max()) && + (fraction >= 1) && + (gallery.isEmpty()) && + (subjects.isEmpty())) + return templates; + + const bool atLeast = instances < 0; + instances = abs(instances); + + QList allLabels = File::get(templates, inputVariable); + + QList uniqueLabels = allLabels.toSet().toList(); + qSort(uniqueLabels); + + QMap counts = templates.countValues(inputVariable, instances != std::numeric_limits::max()); + + if ((instances != std::numeric_limits::max()) && (classes != std::numeric_limits::max())) + foreach (const QString &label, counts.keys()) + if (counts[label] < instances) + counts.remove(label); + + uniqueLabels = counts.keys(); + if ((classes != std::numeric_limits::max()) && (uniqueLabels.size() < classes)) + qWarning("Downsample requested %d classes but only %d are available.", classes, uniqueLabels.size()); + + QList selectedLabels = uniqueLabels; + if (classes < uniqueLabels.size()) { + std::random_shuffle(selectedLabels.begin(), selectedLabels.end()); + selectedLabels = selectedLabels.mid(0, classes); + } + + TemplateList downsample; + for (int i=0; i indices; + for (int j=0; j("FTE", false)) && (!templates.value(j).file.get("PossibleFTE", false))) + indices.append(j); + + std::random_shuffle(indices.begin(), indices.end()); + const int max = atLeast ? indices.size() : std::min(indices.size(), instances); + for (int j=0; j=0; i--) + if (!gallery.contains(downsample[i].file.get("Gallery"))) + downsample.removeAt(i); + + if (!subjects.isEmpty()) + for (int i=downsample.size()-1; i>=0; i--) + if (subjects.contains(downsample[i].file.get(inputVariable))) + downsample.removeAt(i); + + return downsample; +} + +class DownsampleTrainingTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED true) + Q_PROPERTY(int classes READ get_classes WRITE set_classes RESET reset_classes STORED false) + Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false) + Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false) + Q_PROPERTY(QStringList subjects READ get_subjects WRITE set_subjects RESET reset_subjects STORED false) + BR_PROPERTY(br::Transform*, transform, NULL) + BR_PROPERTY(int, classes, std::numeric_limits::max()) + BR_PROPERTY(int, instances, std::numeric_limits::max()) + BR_PROPERTY(float, fraction, 1) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QStringList, gallery, QStringList()) + BR_PROPERTY(QStringList, subjects, QStringList()) + + + Transform *simplify(bool &newTForm) + { + Transform *res = transform->simplify(newTForm); + return res; + } + + void project(const Template &src, Template &dst) const + { + transform->project(src,dst); + } + + + void train(const TemplateList &data) + { + if (!transform || !transform->trainable) + return; + + TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery, subjects); + + transform->train(downsampled); + } +}; + +BR_REGISTER(Transform, DownsampleTrainingTransform) + +} // namespace br + +#include "core/downsampletraining.moc" diff --git a/openbr/plugins/core/event.cpp b/openbr/plugins/core/event.cpp new file mode 100644 index 0000000..b17209d --- /dev/null +++ b/openbr/plugins/core/event.cpp @@ -0,0 +1,30 @@ +#include + +namespace br +{ + +class EventTransform : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString eventName READ get_eventName WRITE set_eventName RESET reset_eventName STORED false) + BR_PROPERTY(QString, eventName, "") + + TemplateEvent event; + + void project(const Template &src, Template &dst) const + { + dst = src; + event.pulseSignal(dst); + } + + TemplateEvent *getEvent(const QString &name) + { + return name == eventName ? &event : NULL; + } +}; + +BR_REGISTER(Transform, EventTransform) + +} // namespace br + +#include "core/event.moc" diff --git a/openbr/plugins/core/expand.cpp b/openbr/plugins/core/expand.cpp new file mode 100644 index 0000000..6fdd8af --- /dev/null +++ b/openbr/plugins/core/expand.cpp @@ -0,0 +1,62 @@ +#include + +namespace br +{ + +static TemplateList Expanded(const TemplateList &templates) +{ + TemplateList expanded; + foreach (const Template &t, templates) { + const bool enrollAll = t.file.get("enrollAll"); + if (t.isEmpty()) { + if (!enrollAll) + expanded.append(t); + continue; + } + + const QList points = t.file.points(); + const QList rects = t.file.rects(); + if (points.size() % t.size() != 0) qFatal("Uneven point count."); + if (rects.size() % t.size() != 0) qFatal("Uneven rect count."); + const int pointStep = points.size() / t.size(); + const int rectStep = rects.size() / t.size(); + + for (int i=0; i + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes all but the first matrix from the template. + * \see IdentityTransform DiscardTransform RestTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class FirstTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + // AggregateFrames will leave the Template empty + // if it hasn't filled up the buffer + // so we gotta anticipate an empty Template + if (src.empty()) return; + dst.file = src.file; + dst = src.m(); + } +}; + +BR_REGISTER(Transform, FirstTransform) + +} // namespace br + +#include "core/first.moc" diff --git a/openbr/plugins/core/fork.cpp b/openbr/plugins/core/fork.cpp new file mode 100644 index 0000000..867cb7c --- /dev/null +++ b/openbr/plugins/core/fork.cpp @@ -0,0 +1,127 @@ +#include + +#include + +namespace br +{ + +static void _train(Transform *transform, const QList *data) +{ + transform->train(*data); +} + +/*! + * \ingroup transforms + * \brief Transforms in parallel. + * \author Josh Klontz \cite jklontz + * + * The source br::Template is seperately given to each transform and the results are appended together. + * + * \see PipeTransform + */ +class ForkTransform : public CompositeTransform +{ + Q_OBJECT + + void train(const QList &data) + { + if (!trainable) return; + QFutureSynchronizer futures; + for (int i=0; iprojectUpdate(src, res); + dst.merge(res); + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + break; + } + } + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst.reserve(src.size()); + for (int i=0; iprojectUpdate(src, m); + if (m.size() != dst.size()) qFatal("TemplateList is of an unexpected size."); + for (int i=0; ifinalize(last_set); + if (last_set.empty()) + continue; + + if (output.empty()) output = last_set; + else + { + // is the number of templates received from this transform consistent with the number + // received previously? If not we can't do anything coherent here. + if (last_set.size() != output.size()) + qFatal("mismatched template list sizes in ForkTransform"); + for (int j = 0; j < output.size(); j++) { + output[j].append(last_set[j]); + } + } + } + } + +protected: + + // Apply each transform to src, concatenate the results + void _project(const Template &src, Template &dst) const + { + foreach (const Transform *f, transforms) { + try { + dst.merge((*f)(src)); + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + break; + } + } + } + + void _project(const TemplateList &src, TemplateList &dst) const + { + dst.reserve(src.size()); + for (int i=0; iproject(src, m); + if (m.size() != dst.size()) qFatal("TemplateList is of an unexpected size."); + for (int i=0; i +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Flags images that failed to enroll based on the specified transform. + * \author Josh Klontz \cite jklontz + */ +class FTETransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + Q_PROPERTY(float min READ get_min WRITE set_min RESET reset_min) + Q_PROPERTY(float max READ get_max WRITE set_max RESET reset_max) + BR_PROPERTY(br::Transform*, transform, NULL) + BR_PROPERTY(float, min, -std::numeric_limits::max()) + BR_PROPERTY(float, max, std::numeric_limits::max()) + + void train(const TemplateList &data) + { + transform->train(data); + + TemplateList projectedData; + transform->project(data, projectedData); + + QList vals; + foreach (const Template &t, projectedData) { + if (!t.file.contains(transform->objectName())) + qFatal("Matrix metadata missing key %s.", qPrintable(transform->objectName())); + vals.append(t.file.get(transform->objectName())); + } + float q1, q3; + Common::Median(vals, &q1, &q3); + min = q1 - 1.5 * (q3 - q1); + max = q3 + 1.5 * (q3 - q1); + } + + void project(const Template &src, Template &dst) const + { + Template projectedSrc; + transform->project(src, projectedSrc); + const float val = projectedSrc.file.get(transform->objectName()); + + dst = src; + dst.file.set(transform->objectName(), val); + dst.file.set("PossibleFTE", (val < min) || (val > max)); + } +}; + +BR_REGISTER(Transform, FTETransform) + +} // namespace br + +#include "core/fte.moc" diff --git a/openbr/plugins/core/gallerycompare.cpp b/openbr/plugins/core/gallerycompare.cpp new file mode 100644 index 0000000..76a76da --- /dev/null +++ b/openbr/plugins/core/gallerycompare.cpp @@ -0,0 +1,64 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Compare each template to a fixed gallery (with name = galleryName), using the specified distance. + * dst will contain a 1 by n vector of scores. + * \author Charles Otto \cite caotto + */ +class GalleryCompareTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true) + Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) + BR_PROPERTY(br::Distance*, distance, NULL) + BR_PROPERTY(QString, galleryName, "") + + TemplateList gallery; + + void project(const Template &src, Template &dst) const + { + dst = src; + if (gallery.isEmpty()) + return; + + QList line = distance->compare(gallery, src); + dst.m() = OpenCVUtils::toMat(line, 1); + } + + void init() + { + if (!galleryName.isEmpty()) + gallery = TemplateList::fromGallery(galleryName); + } + + void train(const TemplateList &data) + { + gallery = data; + } + + void store(QDataStream &stream) const + { + br::Object::store(stream); + stream << gallery; + } + + void load(QDataStream &stream) + { + br::Object::load(stream); + stream >> gallery; + } + +public: + GalleryCompareTransform() : Transform(false, true) {} +}; + +BR_REGISTER(Transform, GalleryCompareTransform) + +} // namespace br + +#include "core/gallerycompare.moc" diff --git a/openbr/plugins/core/identity.cpp b/openbr/plugins/core/identity.cpp new file mode 100644 index 0000000..dccca5c --- /dev/null +++ b/openbr/plugins/core/identity.cpp @@ -0,0 +1,26 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief A no-op transform. + * \see DiscardTransform FirstTransform RestTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class IdentityTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst = src; + } +}; + +BR_REGISTER(Transform, IdentityTransform) + +} // namespace br + +#include "core/identity.moc" diff --git a/openbr/plugins/core/independent.cpp b/openbr/plugins/core/independent.cpp index 41a0054..59136a2 100644 --- a/openbr/plugins/core/independent.cpp +++ b/openbr/plugins/core/independent.cpp @@ -210,4 +210,4 @@ BR_REGISTER(Transform, IndependentTransform) } // namespace br -#include "independent.moc" +#include "core/independent.moc" diff --git a/openbr/plugins/core/jni.cpp b/openbr/plugins/core/jni.cpp new file mode 100644 index 0000000..1b84bb9 --- /dev/null +++ b/openbr/plugins/core/jni.cpp @@ -0,0 +1,104 @@ +//Need to include location of jvm.dll (jdk version) and its parent directory in the environment variables + +#include +#include +#include +#include + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Initialize JNI + * \author Jordan Cheney \cite jcheney + */ +class JNIInitializer : public Initializer +{ + Q_OBJECT + public: + static JavaVM* jvm; + static JavaVMInitArgs vm_args; + + void initialize() const + { + JNIEnv *env; + JavaVMOption options[1]; + + //Location of Java files + QByteArray classpath = QString("-Djava.class.path=").append(Globals->sdkPath).append(QString("/share/openbr/Java/jniLibraries/")).toLocal8Bit(); + char *charClasspath = classpath.data(); + + options[0].optionString = charClasspath; + vm_args.version = JNI_VERSION_1_6; + vm_args.nOptions = 1; + vm_args.options = options; + vm_args.ignoreUnrecognized = JNI_FALSE; + + JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); + + Globals->abbreviations.insert("JNIHelloWorld","Open+JNI(HelloWorld)"); + } + + void finalize() const + { + jvm->DestroyJavaVM(); + } +}; + +JavaVM *JNIInitializer::jvm; +JavaVMInitArgs JNIInitializer::vm_args; + +BR_REGISTER(Initializer, JNIInitializer) + +/*! + * \ingroup transforms + * \brief Execute Java code from OpenBR using the JNI + * \author Jordan Cheney \cite jcheney + */ + +class JNITransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString className READ get_className WRITE set_className RESET reset_className STORED false) + BR_PROPERTY(QString, className, "") + + void project(const Template &src, Template &dst) const + { + (void)dst; //Eliminates a compiler warning. + + JNIEnv *env; + + //Attach current thread to the thread of the JavaVM and access env + JNIInitializer::jvm->AttachCurrentThreadAsDaemon((void**)&env, NULL); + if (JNIInitializer::jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + qFatal("Failed to initialize JNI environment"); + } + + //Convert QString to const char* + QByteArray tmpClass = className.toLocal8Bit(); + const char* charClassName = tmpClass.constData(); + + //Call java method + jclass cls = env->FindClass(charClassName); + if (cls == NULL) { qFatal("Class not found"); } + jmethodID mid = env->GetStaticMethodID(cls, "project", "(Ljava/lang/String;)V"); + if (mid == NULL) { qFatal("MethodID not found"); } + + QByteArray tmp = src.file.name.toLocal8Bit(); + const char* fileName = tmp.constData(); + + //Convert char* to java compatible string + jstring jfileName = env->NewStringUTF(fileName); + + env->CallStaticObjectMethod(cls, mid, jfileName); + + JNIInitializer::jvm->DetachCurrentThread(); + } +}; + +BR_REGISTER(Transform, JNITransform) + +} // namespace br + +#include "jni.moc" diff --git a/openbr/plugins/core/likely.cpp b/openbr/plugins/core/likely.cpp new file mode 100644 index 0000000..400b7e7 --- /dev/null +++ b/openbr/plugins/core/likely.cpp @@ -0,0 +1,58 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Generic interface to Likely JIT compiler + * + * www.liblikely.org + * \author Josh Klontz \cite jklontz + */ +class LikelyTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString kernel READ get_kernel WRITE set_kernel RESET reset_kernel STORED false) + BR_PROPERTY(QString, kernel, "") + + likely_const_env env; + void *function; + + ~LikelyTransform() + { + likely_release_env(env); + } + + void init() + { + likely_release_env(env); + const likely_ast ast = likely_lex_and_parse(qPrintable(kernel), likely_file_lisp); + const likely_const_env parent = likely_standard(NULL); + env = likely_eval(ast, parent, NULL, NULL); + likely_release_env(parent); + likely_release_ast(ast); + function = likely_compile(env->expr, NULL, 0); + } + + void project(const Template &src, Template &dst) const + { + const likely_const_mat srcl = likelyFromOpenCVMat(src); + const likely_const_mat dstl = reinterpret_cast(function)(srcl); + dst = likelyToOpenCVMat(dstl); + likely_release_mat(dstl); + likely_release_mat(srcl); + } + +public: + LikelyTransform() + { + env = NULL; + } +}; + +BR_REGISTER(Transform, LikelyTransform) + +} // namespace br + +#include "core/likely.moc" diff --git a/openbr/plugins/core/loadstore.cpp b/openbr/plugins/core/loadstore.cpp new file mode 100644 index 0000000..08f34fa --- /dev/null +++ b/openbr/plugins/core/loadstore.cpp @@ -0,0 +1,147 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Caches transform training. + * \author Josh Klontz \cite jklontz + */ +class LoadStoreTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString transformString READ get_transformString WRITE set_transformString RESET reset_transformString STORED false) + Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) + BR_PROPERTY(QString, transformString, "Identity") + BR_PROPERTY(QString, fileName, QString()) + +public: + Transform *transform; + + LoadStoreTransform() : transform(NULL) {} + + QString description(bool expanded = false) const + { + if (expanded) { + QString res = transform->description(expanded); + return res; + } + return br::Object::description(expanded); + } + + Transform *simplify(bool &newTForm) + { + Transform *res = transform->simplify(newTForm); + return res; + } + + QList getChildren() const + { + QList rval; + rval.append(transform); + return rval; + } +private: + + void init() + { + if (transform != NULL) return; + if (fileName.isEmpty()) fileName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString); + + if (!tryLoad()) + transform = make(transformString); + else + trainable = false; + } + + bool timeVarying() const + { + return transform->timeVarying(); + } + + void train(const QList &data) + { + if (QFileInfo(getFileName()).exists()) + return; + + transform->train(data); + + qDebug("Storing %s", qPrintable(fileName)); + QtUtils::BlockCompression compressedOut; + QFile fout(fileName); + QtUtils::touchDir(fout); + compressedOut.setBasis(&fout); + + QDataStream stream(&compressedOut); + QString desc = transform->description(); + + if (!compressedOut.open(QFile::WriteOnly)) + qFatal("Failed to open %s for writing.", qPrintable(file)); + + stream << desc; + transform->store(stream); + compressedOut.close(); + } + + void project(const Template &src, Template &dst) const + { + transform->project(src, dst); + } + + void project(const TemplateList &src, TemplateList &dst) const + { + transform->project(src, dst); + } + + void projectUpdate(const Template &src, Template &dst) + { + transform->projectUpdate(src, dst); + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + transform->projectUpdate(src, dst); + } + + void finalize(TemplateList &output) + { + transform->finalize(output); + } + + QString getFileName() const + { + if (QFileInfo(fileName).exists()) return fileName; + const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + fileName; + return QFileInfo(file).exists() ? file : QString(); + } + + bool tryLoad() + { + const QString file = getFileName(); + if (file.isEmpty()) return false; + + qDebug("Loading %s", qPrintable(file)); + QFile fin(file); + QtUtils::BlockCompression reader(&fin); + if (!reader.open(QIODevice::ReadOnly)) { + if (QFileInfo(file).exists()) qFatal("Unable to open %s for reading. Check file permissions.", qPrintable(file)); + else qFatal("Unable to open %s for reading. File does not exist.", qPrintable(file)); + } + + QDataStream stream(&reader); + stream >> transformString; + + transform = Transform::make(transformString); + transform->load(stream); + + return true; + } +}; + +BR_REGISTER(Transform, LoadStoreTransform) + +} // namespace br + +#include "core/loadstore.moc" diff --git a/openbr/plugins/core/pipe.cpp b/openbr/plugins/core/pipe.cpp new file mode 100644 index 0000000..77938ac --- /dev/null +++ b/openbr/plugins/core/pipe.cpp @@ -0,0 +1,205 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup Transforms + * \brief Transforms in series. + * \author Josh Klontz \cite jklontz + * + * The source br::Template is given to the first transform and the resulting br::Template is passed to the next transform, etc. + * + * \see ExpandTransform + * \see ForkTransform + */ +class PipeTransform : public CompositeTransform +{ + Q_OBJECT + + void _projectPartial(TemplateList *srcdst, int startIndex, int stopIndex) + { + TemplateList ftes; + for (int i=startIndex; iproject(*srcdst, res); + + splitFTEs(res, ftes); + *srcdst = res; + } + } + + void train(const QList &data) + { + if (!trainable) return; + + QList dataLines(data); + + int i = 0; + while (i < transforms.size()) { + // Conditional statement covers likely case that first transform is untrainable + if (transforms[i]->trainable) { + qDebug() << "Training " << transforms[i]->description() << "\n..."; + transforms[i]->train(dataLines); + } + + // if the transform is time varying, we can't project it in parallel + if (transforms[i]->timeVarying()) { + qDebug() << "Projecting " << transforms[i]->description() << "\n..."; + for (int j=0; j < dataLines.size();j++) { + TemplateList junk; + splitFTEs(dataLines[j], junk); + + transforms[i]->projectUpdate(dataLines[j], dataLines[j]); + } + + // advance i since we already projected for this stage. + i++; + + // the next stage might be trainable, so continue to evaluate it. + continue; + } + + // We project through any subsequent untrainable transforms at once + // as a memory optimization in case any of these intermediate + // transforms allocate a lot of memory (like OpenTransform) + // then we don't want all the training templates to be processed + // by that transform at once if we can avoid it. + int nextTrainableTransform = i+1; + while ((nextTrainableTransform < transforms.size()) && + !transforms[nextTrainableTransform]->trainable && + !transforms[nextTrainableTransform]->timeVarying()) + nextTrainableTransform++; + + // No more trainable transforms? Don't need any more projects then + if (nextTrainableTransform == transforms.size()) + break; + + fprintf(stderr, "Projecting %s", qPrintable(transforms[i]->description())); + for (int j=i+1; j < nextTrainableTransform; j++) + fprintf(stderr,"+%s", qPrintable(transforms[j]->description())); + fprintf(stderr, "\n...\n"); + fflush(stderr); + + QFutureSynchronizer futures; + for (int j=0; j < dataLines.size(); j++) + futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, &dataLines[j], i, nextTrainableTransform)); + futures.waitForFinished(); + + i = nextTrainableTransform; + } + } + + void projectUpdate(const Template &src, Template &dst) + { + dst = src; + foreach (Transform *f, transforms) { + try { + f->projectUpdate(dst); + if (dst.file.fte) + break; + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + break; + } + } + } + + // For time varying transforms, parallel execution over individual templates + // won't work. + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + TemplateList ftes; + dst = src; + foreach (Transform *f, transforms) { + TemplateList res; + f->projectUpdate(dst, res); + splitFTEs(res, ftes); + dst = res; + } + dst.append(ftes); + } + + virtual void finalize(TemplateList &output) + { + output.clear(); + // For each transform, + for (int i = 0; i < transforms.size(); i++) + { + + // Collect any final templates + TemplateList last_set; + transforms[i]->finalize(last_set); + if (last_set.empty()) + continue; + // Push any templates received through the remaining transforms in the sequence + for (int j = (i+1); j < transforms.size();j++) + { + transforms[j]->projectUpdate(last_set); + } + // append the result to the output set + output.append(last_set); + } + } + + void init() + { + QList flattened; + for (int i=0;i < transforms.size(); i++) + { + PipeTransform *probe = dynamic_cast (transforms[i]); + if (!probe) { + flattened.append(transforms[i]); + continue; + } + for (int j=0; j < probe->transforms.size(); j++) + flattened.append(probe->transforms[j]); + } + transforms = flattened; + + CompositeTransform::init(); + } + +protected: + // Template list project -- process templates in parallel through Transform::project + // or if parallelism is disabled, handle them sequentially + void _project(const TemplateList &src, TemplateList &dst) const + { + TemplateList ftes; + dst = src; + foreach (const Transform *f, transforms) { + TemplateList res; + f->project(dst, res); + splitFTEs(res, ftes); + dst = res; + } + dst.append(ftes); + } + + // Single template const project, pass the template through each sub-transform, one after the other + virtual void _project(const Template &src, Template &dst) const + { + dst = src; + foreach (const Transform *f, transforms) { + try { + dst >> *f; + if (dst.file.fte) + break; + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + } + } + } +}; + +BR_REGISTER(Transform, PipeTransform) + +} // namespace br + +#include "core/pipe.moc" diff --git a/openbr/plugins/core/processwrapper.cpp b/openbr/plugins/core/processwrapper.cpp new file mode 100644 index 0000000..70a674b --- /dev/null +++ b/openbr/plugins/core/processwrapper.cpp @@ -0,0 +1,661 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +class CommunicationManager : public QObject +{ + Q_OBJECT +public: + int timeout_ms; + QThread *basis; + CommunicationManager() + { + timeout_ms = 30000; + + basis = new QThread; + moveToThread(basis); + server.moveToThread(basis); + outbound.moveToThread(basis); + + // signals for our sever + connect(&server, SIGNAL(newConnection()), this, SLOT(receivedConnection() )); + connect(this, SIGNAL(pulseStartServer(QString)), this, SLOT(startServerInternal(QString)), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(pulseOutboundConnect(QString)), this, SLOT(startConnectInternal(QString) ), Qt::BlockingQueuedConnection); + + + // internals, cause work to be done by the main thread because reasons. + connect(this, SIGNAL(pulseSignal()), this, SLOT(sendSignalInternal()), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(pulseReadSignal() ), this, SLOT(readSignalInternal()), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(pulseReadSerialized() ), this, SLOT(readSerializedInternal()), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(pulseSendSerialized() ), this, SLOT(sendSerializedInternal() ), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(pulseShutdown() ), this, SLOT(shutdownInternal() ), Qt::BlockingQueuedConnection); + + // signals for our outbound connection + connect(&outbound, SIGNAL(connected() ), this, SLOT(outboundConnected() )); + connect(&outbound, SIGNAL(disconnected() ), this, SLOT(outboundDisconnected() )); + + connect(&outbound, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(outboundConnectionError(QLocalSocket::LocalSocketError) ) ); + connect(&outbound, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), this, SLOT(outboundStateChanged(QLocalSocket::LocalSocketState) ) ); + + inbound = NULL; + basis->start(); + } + + ~CommunicationManager() + { + + } + + void shutDownThread() + { + basis->quit(); + basis->wait(); + delete basis; + } + + enum SignalType + { + INPUT_AVAILABLE, + OUTPUT_AVAILABLE, + SHOULD_END + }; + + +public slots: + // matching server signals + void receivedConnection() + { + inbound = server.nextPendingConnection(); + connect(inbound, SIGNAL(disconnected() ), this, SLOT(inboundDisconnected() )); + connect(inbound, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(inboundConnectionError(QLocalSocket::LocalSocketError) ) ); + connect(inbound, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), this, SLOT(inboundStateChanged(QLocalSocket::LocalSocketState) ) ); + + receivedWait.wakeAll(); + } + + // matching outbound socket signals + + // Oh boy. + void outboundConnected() + { + outboundWait.wakeAll(); + } + + // Oh no! + void outboundDisconnected() + { + //qDebug() << key << " outbound socket has disconnected"; + } + + // informative. + void outboundConnectionError(QLocalSocket::LocalSocketError socketError) + { + (void) socketError; + //qDebug() << key << " outbound socket error " << socketError; + } + + void outboundStateChanged(QLocalSocket::LocalSocketState socketState) + { + (void) socketState; + //qDebug() << key << " outbound socket state changed to " << socketState; + } + + // matching inbound socket signals + void inboundDisconnected() + { + //qDebug() << key << " inbound socket has disconnected"; + } + + void inboundConnectionError(QLocalSocket::LocalSocketError socketError) + { + (void) socketError; + //qDebug() << key << " inbound socket error " << socketError; + } + + void inboundStateChanged(QLocalSocket::LocalSocketState socketState) + { + (void) socketState; + //qDebug() << key << " inbound socket state changed to " << socketState; + } + + void startServerInternal(QString serverName) + { + if (!server.isListening()) { + bool listen_res = server.listen(serverName); + if (!listen_res) + qDebug() << key << " Failed to start server at " << serverName; + } + } + + void sendSignalInternal() + { + SignalType signal = sendType; + qint64 signal_wrote = outbound.write((char *) &signal, sizeof(signal)); + + if (signal_wrote != sizeof(signal)) + qDebug() << key << " inconsistent signal size"; + + while (outbound.bytesToWrite() > 0) { + bool written = outbound.waitForBytesWritten(timeout_ms); + + if (!written && (!outbound.isValid() || outbound.state() == QLocalSocket::UnconnectedState)) { + qDebug() << key << " failed to wait for bytes written in signal size"; + return; + } + } + } + + void readSignalInternal() + { + while (inbound->bytesAvailable() < qint64(sizeof(readSignal)) ) { + bool size_ready = inbound->waitForReadyRead(timeout_ms); + + // wait timed out, now what? + if (!size_ready && (!inbound->isValid() || inbound->state() == QLocalSocket::UnconnectedState)) { + readSignal = SHOULD_END; + server.close(); + outbound.abort(); + inbound->abort(); + return; + } + } + + qint64 signalBytesRead = inbound->read((char *) &readSignal, sizeof(readSignal)); + if (signalBytesRead != sizeof(readSignal)) + qDebug("Inconsistent signal size read!"); + + if (readSignal == SHOULD_END) + { + server.close(); + outbound.abort(); + inbound->abort(); + } + } + + void sendSerializedInternal() + { + qint64 serializedSize = writeArray.size(); + qint64 size_wrote = outbound.write((char *) &serializedSize, sizeof(serializedSize)); + if (size_wrote != sizeof(serializedSize)) { + qDebug() << key << "inconsistent size sent in send data!"; + return; + } + + while (outbound.bytesToWrite() > 0) { + bool written = outbound.waitForBytesWritten(timeout_ms); + + if (!written && (!outbound.isValid() || outbound.state() == QLocalSocket::UnconnectedState)) { + qDebug() << key << " wait for bytes failed!"; + return; + } + } + + qint64 data_wrote = outbound.write(writeArray.data(), serializedSize); + if (data_wrote != serializedSize) + qDebug() << key << " inconsistent data written!"; + + while (outbound.bytesToWrite() > 0) { + bool write_res = outbound.waitForBytesWritten(); + if (!write_res && (!outbound.isValid() || outbound.state() == QLocalSocket::UnconnectedState)) { + qDebug() << key << " wait for bytes failed!"; + return; + } + } + + return; + + } + + void readSerializedInternal() + { + qint64 bufferSize; + while (inbound->bytesAvailable() < qint64(sizeof(bufferSize))) { + bool size_ready = inbound->waitForReadyRead(timeout_ms); + if (!size_ready && (!inbound->isValid() || inbound->state() == QLocalSocket::UnconnectedState)) { + qDebug() << key << " Failed to received object size in read data!"; + qDebug() << key << "inbound status: " << inbound->state() << " error: " << inbound->errorString(); + return; + } + } + + qint64 sizeBytesRead = inbound->read((char *) &bufferSize, sizeof(bufferSize)); + if (sizeBytesRead != sizeof(bufferSize)) { + qDebug("failed to read size of buffer!"); + return; + } + + // allocate the input buffer + readArray.resize(bufferSize); + + // read the data, we may get it in serveral bursts + qint64 arrayPosition = 0; + while (arrayPosition < bufferSize) { + while (!inbound->bytesAvailable()) { + bool ready_res = inbound->waitForReadyRead(timeout_ms); + + if (!ready_res && (!inbound->isValid() || inbound->state() == QLocalSocket::UnconnectedState)) { + qDebug() << key << "failed to wait for data!"; + return; + } + } + + // how many bytes do we still need? + qint64 bytes_remaining = bufferSize - arrayPosition; + arrayPosition += inbound->read(readArray.data()+arrayPosition, qMin(inbound->bytesAvailable(), bytes_remaining)); + } + if (arrayPosition != bufferSize) + qDebug() << key << "Read wrong size object!"; + } + + void shutdownInternal() + { + outbound.abort(); + inbound->abort(); + server.close(); + } + + + void startConnectInternal(QString remoteName) + { + outbound.connectToServer(remoteName); + } + + +signals: + void pulseStartServer(QString serverName); + void pulseSignal(); + void pulseReadSignal(); + void pulseReadSerialized(); + void pulseSendSerialized(); + void pulseShutdown(); + void pulseOutboundConnect(QString serverName); + +public: + QByteArray readArray; + QByteArray writeArray; + + SignalType readSignal; + QMutex receivedLock; + QWaitCondition receivedWait; + + QMutex outboundLock; + QWaitCondition outboundWait; + + QString key; + QString serverName; + QString remoteName; + + QLocalSocket *inbound; + QLocalSocket outbound; + QLocalServer server; + + + void waitForInbound() + { + QMutexLocker locker(&receivedLock); + while (!inbound || inbound->state() != QLocalSocket::ConnectedState) { + bool res = receivedWait.wait(&receivedLock,30*1000); + if (!res) + qDebug() << key << " " << QThread::currentThread() << " waiting timed out, server thread is " << server.thread() << " base thread " << basis; + } + } + + void connectToRemote(const QString &remoteName) + { + emit pulseOutboundConnect(remoteName); + + QMutexLocker locker(&outboundLock); + while (outbound.state() != QLocalSocket::ConnectedState) { + outboundWait.wait(&outboundLock,30*1000); + + } + } + + SignalType getSignal() + { + emit pulseReadSignal(); + return readSignal; + } + + + template + bool readData(T &input) + { + emit pulseReadSerialized(); + QDataStream deserializer(readArray); + deserializer >> input; + return true; + } + + Transform *readTForm() + { + emit pulseReadSerialized(); + + QByteArray data = readArray; + QDataStream deserializer(data); + Transform *res = Transform::deserialize(deserializer); + sendSignal(CommunicationManager::OUTPUT_AVAILABLE); + return res; + } + + template + bool sendData(const T &output) + { + QBuffer buffer; + buffer.open(QBuffer::ReadWrite); + + QDataStream serializer(&buffer); + serializer << output; + writeArray = buffer.data(); + emit pulseSendSerialized(); + return true; + } + + SignalType sendType; + void sendSignal(SignalType signal) + { + sendType = signal; + if (QThread::currentThread() == this->thread() ) + this->sendSignalInternal(); + else + emit pulseSignal(); + } + + void startServer(QString server) + { + emit pulseStartServer(server); + } + + void shutdown() + { + emit pulseShutdown(); + } + + +}; + +class EnrollmentWorker : public QObject +{ + Q_OBJECT +public: + CommunicationManager *comm; + QString name; + + ~EnrollmentWorker() + { + delete transform; + + comm->shutDownThread(); + delete comm; + } + + br::Transform *transform; + +public: + void connections(const QString &baseName) + { + comm = new CommunicationManager(); + name = baseName; + comm->key = "worker_"+baseName.mid(1,5); + comm->startServer(baseName+"_worker"); + comm->connectToRemote(baseName+"_master"); + + comm->waitForInbound(); + + transform = comm->readTForm(); + } + + void workerLoop() + { + QString sign = "worker " + name; + CommunicationManager::SignalType signal; + forever { + signal= comm->getSignal(); + + if (signal == CommunicationManager::SHOULD_END) { + break; + } + TemplateList inList; + TemplateList outList; + + comm->readData(inList); + transform->projectUpdate(inList,outList); + comm->sendData(outList); + } + comm->shutdown(); + } +}; + +void WorkerProcess::mainLoop() +{ + processInterface = new EnrollmentWorker(); + processInterface->transform = NULL; + processInterface->connections(baseName); + processInterface->workerLoop(); + delete processInterface; +} + +class ProcessInterface : public QObject +{ + Q_OBJECT +public: + QThread *basis; + + ~ProcessInterface() + { + basis->quit(); + basis->wait(); + delete basis; + } + + ProcessInterface() + { + basis = new QThread(); + + moveToThread(basis); + workerProcess.moveToThread(basis); + + connect(this, SIGNAL(pulseEnd()), this, SLOT(endProcessInternal()), Qt::BlockingQueuedConnection); + connect(this, SIGNAL(pulseStart(QStringList)), this, SLOT(startProcessInternal(QStringList)), Qt::BlockingQueuedConnection); + + basis->start(); + } + + QProcess workerProcess; + void endProcess() + { + emit pulseEnd(); + } + + void startProcess(QStringList arguments) + { + emit pulseStart(arguments); + } + +signals: + void pulseEnd(); + void pulseStart(QStringList); + +protected slots: + void endProcessInternal() + { + workerProcess.waitForFinished(-1); + } + + void startProcessInternal(QStringList arguments) + { + workerProcess.setProcessChannelMode(QProcess::ForwardedChannels); + workerProcess.start("br", arguments); + workerProcess.waitForStarted(-1); + } +}; + +struct ProcessData +{ + CommunicationManager comm; + ProcessInterface proc; + bool initialized; + ProcessData() + { + initialized = false; + } + + ~ProcessData() + { + if (initialized) { + comm.sendSignal(CommunicationManager::SHOULD_END); + proc.endProcess(); + comm.shutdown(); + comm.shutDownThread(); + } + } +}; + + +/*! + * \ingroup transforms + * \brief Interface to a separate process + * \author Charles Otto \cite caotto + */ +class ProcessWrapperTransform : public WrapperTransform +{ + Q_OBJECT + Q_PROPERTY(int concurrentCount READ get_concurrentCount WRITE set_concurrentCount RESET reset_concurrentCount STORED false) + BR_PROPERTY(int, concurrentCount, 2) + + QString baseKey; + + Resource processes; + + Transform *smartCopy(bool &newTransform) + { + newTransform = false; + return this; + } + + // make clang shut up? what a great compiler + using WrapperTransform::project; + using WrapperTransform::train; + + void project(const TemplateList &src, TemplateList &dst) const + { + if (src.empty()) + return; + + ProcessData *data = processes.acquire(); + if (!data->initialized) + activateProcess(data); + + CommunicationManager *localComm = &(data->comm); + + localComm->sendSignal(CommunicationManager::INPUT_AVAILABLE); + localComm->sendData(src); + + localComm->readData(dst); + processes.release(data); + } + + void train(const TemplateList &data) + { + (void) data; + } + + void init() + { + processActive = false; + serialized.clear(); + if (transform) { + QDataStream out(&serialized, QFile::WriteOnly); + transform->serialize(out); + tcount = Globals->parallelism; + counter.acquire(counter.available()); + counter.release(this->concurrentCount); + } + } + + static QSemaphore counter; + mutable int tcount; + mutable QByteArray serialized; + void transmitTForm(CommunicationManager *localComm) const + { + if (serialized.isEmpty() ) + qFatal("Trying to transmit empty transform!"); + + counter.acquire(1); + static QMutex transmission; + QMutexLocker lock(&transmission); + tcount--; + + localComm->writeArray = serialized; + if (tcount == 0) + serialized.clear(); + lock.unlock(); + + emit localComm->pulseSendSerialized(); + localComm->getSignal(); + counter.release(1); + } + + void activateProcess(ProcessData *data) const + { + data->initialized = true; + // generate a uuid for our local servers + QUuid id = QUuid::createUuid(); + QString baseKey = id.toString(); + + QStringList argumentList; + // We serialize and transmit the transform directly, so algorithm doesn't matter. + argumentList.append("-quiet"); + argumentList.append("-algorithm"); + argumentList.append("Identity"); + if (!Globals->path.isEmpty()) { + argumentList.append("-path"); + argumentList.append(Globals->path); + } + argumentList.append("-parallelism"); + argumentList.append(QString::number(0)); + argumentList.append("-slave"); + argumentList.append(baseKey); + + data->comm.key = "master_"+baseKey.mid(1,5); + + data->comm.startServer(baseKey+"_master"); + + data->proc.startProcess(argumentList); + data->comm.waitForInbound(); + data->comm.connectToRemote(baseKey+"_worker"); + + transmitTForm(&(data->comm)); + } + + bool timeVarying() const + { + return false; + } + +public: + bool processActive; + ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; } +}; +QSemaphore ProcessWrapperTransform::counter; + +BR_REGISTER(Transform, ProcessWrapperTransform) + +} + +#include "core/processwrapper.moc" diff --git a/openbr/plugins/core/progresscounter.cpp b/openbr/plugins/core/progresscounter.cpp new file mode 100644 index 0000000..fadbcde --- /dev/null +++ b/openbr/plugins/core/progresscounter.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include + +namespace br +{ + +class ProgressCounterTransform : public TimeVaryingTransform +{ + Q_OBJECT + + Q_PROPERTY(qint64 totalProgress READ get_totalProgress WRITE set_totalProgress RESET reset_totalProgress STORED false) + BR_PROPERTY(qint64, totalProgress, 1) + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst = src; + + qint64 elapsed = timer.elapsed(); + int last_frame = -2; + if (!dst.empty()) { + for (int i=0;i < dst.size();i++) { + int frame = dst[i].file.get("FrameNumber", -1); + if (frame == last_frame && frame != -1) + continue; + + // Use 1 as the starting index for progress output + Globals->currentProgress = dst[i].file.get("progress",0)+1; + dst[i].file.remove("progress"); + last_frame = frame; + + Globals->currentStep++; + } + } + + // updated every second + if (elapsed > 1000) { + Globals->printStatus(); + timer.start(); + } + + return; + } + + void train(const TemplateList& data) + { + (void) data; + } + + void finalize(TemplateList &data) + { + (void) data; + float p = br_progress(); + qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep); + timer.start(); + Globals->startTime.start(); + Globals->currentStep = 0; + Globals->currentProgress = 0; + Globals->totalSteps = totalProgress; + } + + void init() + { + timer.start(); + Globals->startTime.start(); + Globals->currentProgress = 0; + Globals->currentStep = 0; + Globals->totalSteps = totalProgress; + } + +public: + ProgressCounterTransform() : TimeVaryingTransform(false,false) {} + QElapsedTimer timer; +}; + +BR_REGISTER(Transform, ProgressCounterTransform) + +} // namespace br + +#include "core/progresscounter.moc" diff --git a/openbr/plugins/core/registrar.cpp b/openbr/plugins/core/registrar.cpp new file mode 100644 index 0000000..8879c8b --- /dev/null +++ b/openbr/plugins/core/registrar.cpp @@ -0,0 +1,25 @@ +#include + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Register custom objects with Qt meta object system. + * \author Charles Otto \cite caotto + */ +class Registrar : public Initializer +{ + Q_OBJECT + + void initialize() const + { + qRegisterMetaType(); + } +}; + +BR_REGISTER(Initializer, Registrar) + +} // namespace br + +#include "core/registrar.moc" diff --git a/openbr/plugins/core/remove.cpp b/openbr/plugins/core/remove.cpp new file mode 100644 index 0000000..7105d4d --- /dev/null +++ b/openbr/plugins/core/remove.cpp @@ -0,0 +1,31 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes the matrix from the template at the specified index. + * \author Josh Klontz \cite jklontz + * \see IdentityTransform DiscardTransform FirstTransform RestTransform + */ +//! [example_transform] +class RemoveTransform : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(int index READ get_index WRITE set_index RESET reset_index STORED false) + BR_PROPERTY(int, index, 0) + + void project(const Template &src, Template &dst) const + { + dst = src; + dst.removeAt(index); + } +}; + +BR_REGISTER(Transform, RemoveTransform) +//! [example_transform] + +} // namespace br + +#include "core/remove.moc" diff --git a/openbr/plugins/core/rest.cpp b/openbr/plugins/core/rest.cpp new file mode 100644 index 0000000..49c87aa --- /dev/null +++ b/openbr/plugins/core/rest.cpp @@ -0,0 +1,27 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes the first matrix from the template. + * \see IdentityTransform DiscardTransform FirstTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class RestTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst = src; + dst.removeFirst(); + } +}; + +BR_REGISTER(Transform, RestTransform) + +} // namespace br + +#include "core/rest.moc" diff --git a/openbr/plugins/core/schrodinger.cpp b/openbr/plugins/core/schrodinger.cpp new file mode 100644 index 0000000..1fbcfd6 --- /dev/null +++ b/openbr/plugins/core/schrodinger.cpp @@ -0,0 +1,45 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Generates two templates, one of which is passed through a transform and the other + * is not. No cats were harmed in the making of this transform. + * \author Scott Klum \cite sklum + */ +class SchrodingerTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(br::Transform*, transform, NULL) + +public: + void train(const TemplateList &data) + { + transform->train(data); + } + + void project(const TemplateList &src, TemplateList &dst) const + { + foreach(const Template &t, src) { + dst.append(t); + Template u; + transform->project(t,u); + dst.append(u); + } + } + + void project(const Template &src, Template &dst) const { + TemplateList temp; + project(TemplateList() << src, temp); + if (!temp.isEmpty()) dst = temp.first(); + } + +}; +BR_REGISTER(Transform, SchrodingerTransform) + +} // namespace br + +#include "core/schrodinger.moc" diff --git a/openbr/plugins/core/singleton.cpp b/openbr/plugins/core/singleton.cpp index df64659..1a4ede8 100644 --- a/openbr/plugins/core/singleton.cpp +++ b/openbr/plugins/core/singleton.cpp @@ -70,4 +70,4 @@ BR_REGISTER(Transform, SingletonTransform) } // namespace br -#include "singleton.moc" +#include "core/singleton.moc" diff --git a/openbr/plugins/core/stream.cpp b/openbr/plugins/core/stream.cpp new file mode 100644 index 0000000..dda116b --- /dev/null +++ b/openbr/plugins/core/stream.cpp @@ -0,0 +1,1365 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +namespace br +{ + +class FrameData +{ +public: + int sequenceNumber; + TemplateList data; +}; + +// A buffer shared between adjacent processing stages in a stream +class SharedBuffer +{ +public: + SharedBuffer() {} + virtual ~SharedBuffer() {} + + virtual void addItem(FrameData *input)=0; + virtual void reset()=0; + + virtual FrameData *tryGetItem()=0; + virtual int size()=0; +}; + +// for n - 1 boundaries, multiple threads call addItem, the frames are +// sequenced based on FrameData::sequence_number, and calls to getItem +// receive them in that order +class SequencingBuffer : public SharedBuffer +{ +public: + SequencingBuffer() + { + next_target = 0; + } + + void addItem(FrameData *input) + { + QMutexLocker bufferLock(&bufferGuard); + + buffer.insert(input->sequenceNumber, input); + } + + FrameData *tryGetItem() + { + QMutexLocker bufferLock(&bufferGuard); + + if (buffer.empty() || buffer.begin().key() != this->next_target) { + return NULL; + } + + QMap::Iterator result = buffer.begin(); + + if (next_target != result.value()->sequenceNumber) { + qFatal("mismatched targets!"); + } + + next_target = next_target + 1; + + FrameData *output = result.value(); + buffer.erase(result); + return output; + } + + virtual int size() + { + QMutexLocker lock(&bufferGuard); + return buffer.size(); + } + virtual void reset() + { + if (size() != 0) + qDebug("Sequencing buffer has non-zero size during reset!"); + + QMutexLocker lock(&bufferGuard); + next_target = 0; + } + + +private: + QMutex bufferGuard; + int next_target; + + QMap buffer; +}; + +// For 1 - 1 boundaries, a double buffering scheme +// Producer/consumer read/write from separate buffers, and switch if their +// buffer runs out/overflows. Synchronization is handled by a read/write lock +// threads are "reading" if they are adding to/removing from their individual +// buffer, and writing if they access or swap with the other buffer. +class DoubleBuffer : public SharedBuffer +{ +public: + DoubleBuffer() + { + inputBuffer = &buffer1; + outputBuffer = &buffer2; + } + + int size() + { + QReadLocker readLock(&bufferGuard); + return inputBuffer->size() + outputBuffer->size(); + } + + // called from the producer thread + void addItem(FrameData *input) + { + QReadLocker readLock(&bufferGuard); + inputBuffer->append(input); + } + + FrameData *tryGetItem() + { + QReadLocker readLock(&bufferGuard); + + // There is something for us to get + if (!outputBuffer->empty()) { + FrameData *output = outputBuffer->first(); + outputBuffer->removeFirst(); + return output; + } + + // Outputbuffer is empty, try to swap with the input buffer, we need a + // write lock to do that. + readLock.unlock(); + QWriteLocker writeLock(&bufferGuard); + + // Nothing on the input buffer either? + if (inputBuffer->empty()) { + return NULL; + } + + // input buffer is non-empty, so swap the buffers + std::swap(inputBuffer, outputBuffer); + + // Return a frame + FrameData *output = outputBuffer->first(); + outputBuffer->removeFirst(); + return output; + } + + virtual void reset() + { + if (this->size() != 0) + qDebug("Shared buffer has non-zero size during reset!"); + } + + +private: + // The read-write lock. The thread adding to this buffer can add + // to the current input buffer if it has a read lock. The thread + // removing from this buffer can remove things from the current + // output buffer if it has a read lock, or swap the buffers if it + // has a write lock. + QReadWriteLock bufferGuard; + + // The buffer that is currently being added to + QList * inputBuffer; + // The buffer that is currently being removed from + QList * outputBuffer; + + // The buffers pointed at by inputBuffer/outputBuffer + QList buffer1; + QList buffer2; +}; + +// Given a template as input, open the file contained as a gallery, and return templates one at a time on +// calls to getNextTemplate +struct StreamGallery +{ + bool open(Template &input) + { + // Create a gallery + gallery = QSharedPointer(Gallery::make(input.file)); + // Failed to open the gallery? + if (gallery.isNull()) { + qDebug("Failed to create gallery!"); + galleryOk = false; + return false; + } + + // Set up state variables for future reads + galleryOk = true; + gallery->readBlockSize = 100; + nextIdx = 0; + lastBlock = false; + return galleryOk; + } + + bool isOpen() { return galleryOk; } + + void close() + { + galleryOk = false; + currentData.clear(); + nextIdx = 0; + lastBlock = true; + } + + bool getNextTemplate(Template &output) + { + // If we still have data available, we return one of those + if ((nextIdx >= currentData.size()) && !lastBlock) { + currentData = gallery->readBlock(&lastBlock); + nextIdx = 0; + } + + if (nextIdx >= currentData.size()) { + galleryOk = false; + return false; + } + + // Return the indicated template, and advance the index + output = currentData[nextIdx++]; + return true; + } + +protected: + + QSharedPointer gallery; + bool galleryOk; + bool lastBlock; + + TemplateList currentData; + int nextIdx; +}; + +// Interface for sequentially getting data from some data source. +// Given a TemplateList, return single template frames sequentially by applying a TemplateProcessor +// to each individual template. +class DataSource +{ +public: + DataSource(int maxFrames=500) + { + // The sequence number of the last frame + final_frame = -1; + for (int i=0; i < maxFrames;i++) + { + allFrames.addItem(new FrameData()); + } + } + + virtual ~DataSource() + { + while (true) + { + FrameData *frame = allFrames.tryGetItem(); + if (frame == NULL) + break; + delete frame; + } + } + + void close() + { + frameSource.close(); + } + + int size() + { + return this->templates.size(); + } + + bool open(const TemplateList &input) + { + // Set up variables specific to us + current_template_idx = 0; + templates = input; + + is_broken = false; + allReturned = false; + + // The last frame isn't initialized yet + final_frame = -1; + // Start our sequence numbers from the input index + next_sequence_number = 0; + + // Actually open the data source + bool open_res = openNextTemplate(); + + // We couldn't open the data source + if (!open_res) { + is_broken = true; + return false; + } + + return true; + } + + // non-blocking version of getFrame + // Returns a NULL FrameData if too many frames are out, or the + // data source is broken. Sets last_frame to true iff the FrameData + // returned is the last valid frame, and the data source is now broken. + FrameData *tryGetFrame(bool &last_frame) + { + last_frame = false; + + if (is_broken) { + return NULL; + } + + // Try to get a FrameData from the pool, if we can't it means too many + // frames are already out, and we will return NULL to indicate failure + FrameData *aFrame = allFrames.tryGetItem(); + if (aFrame == NULL) + return NULL; + + // Try to actually read a frame, if this returns false the data source is broken + bool res = getNextFrame(*aFrame); + + // The datasource broke, update final_frame + if (!res) + { + QMutexLocker lock(&last_frame_update); + final_frame = aFrame->sequenceNumber; + aFrame->data().clear(); + } + + // If this is the last frame, say so + if (aFrame->sequenceNumber == final_frame) { + last_frame = true; + is_broken = true; + } + + return aFrame; + } + + // Return a frame to the pool, returns true if the frame returned was the last + // frame issued, false otherwise + bool returnFrame(FrameData *inputFrame) + { + int frameNumber = inputFrame->sequenceNumber; + + inputFrame->data.clear(); + inputFrame->sequenceNumber = -1; + allFrames.addItem(inputFrame); + + bool rval = false; + + QMutexLocker lock(&last_frame_update); + + if (frameNumber == final_frame) { + // We just received the last frame, better pulse + allReturned = true; + rval = true; + } + + return rval; + } + + void wake() + { + lastReturned.wakeAll(); + } + + bool waitLast() + { + QMutexLocker lock(&last_frame_update); + + while (!allReturned) + { + // This would be a safer wait if we used a timeout, but + // theoretically that should never matter. + lastReturned.wait(&last_frame_update); + } + return true; + } + +protected: + + bool openNextTemplate() + { + if (this->current_template_idx >= this->templates.size()) + return false; + + bool open_res = false; + while (!open_res) + { + frameSource.close(); + + Template curr = this->templates[current_template_idx]; + + open_res = frameSource.open(curr); + if (!open_res) + { + current_template_idx++; + if (current_template_idx >= this->templates.size()) + return false; + } + } + return true; + } + + bool getNextFrame(FrameData &output) + { + bool got_frame = false; + + Template aTemplate; + + while (!got_frame) + { + got_frame = frameSource.getNextTemplate(aTemplate); + + // OK we got a frame + if (got_frame) { + // set the sequence number and tempalte of this frame + output.sequenceNumber = next_sequence_number; + output.data.append(aTemplate); + // set the frame number in the template's metadata + output.data.last().file.set("FrameNumber", output.sequenceNumber); + next_sequence_number++; + return true; + } + + // advance to the next tempalte in our list + this->current_template_idx++; + bool open_res = this->openNextTemplate(); + + // couldn't get the next template? nothing to do, otherwise we try to read + // a frame at the top of this loop. + if (!open_res) { + output.sequenceNumber = next_sequence_number; + return false; + } + } + + return false; + } + + // Index of the template in the templatelist we are currently reading from + int current_template_idx; + + // list of templates we are workign from + TemplateList templates; + + // processor for the current template + StreamGallery frameSource; + + int next_sequence_number; + int final_frame; + bool is_broken; + bool allReturned; + + DoubleBuffer allFrames; + + QWaitCondition lastReturned; + QMutex last_frame_update; +}; + +class ProcessingStage; + +class BasicLoop : public QRunnable, public QFutureInterface +{ +public: + BasicLoop() + { + this->reportStarted(); + } + + void run(); + + QList * stages; + int start_idx; + FrameData *startItem; +}; + +class ProcessingStage +{ +public: + friend class DirectStreamTransform; +public: + ProcessingStage(int nThreads = 1) + { + thread_count = nThreads; + } + virtual ~ProcessingStage() {} + + virtual FrameData* run(FrameData *input, bool &should_continue, bool &final)=0; + + virtual bool tryAcquireNextStage(FrameData *& input, bool &final)=0; + + int stage_id; + + virtual void reset()=0; + + virtual void status()=0; + +protected: + int thread_count; + + SharedBuffer *inputBuffer; + ProcessingStage *nextStage; + QList * stages; + QThreadPool *threads; + Transform *transform; + +}; + +class MultiThreadStage : public ProcessingStage +{ +public: + MultiThreadStage(int _input) : ProcessingStage(_input) {} + + // Not much to worry about here, we will project the input + // and try to continue to the next stage. + FrameData *run(FrameData *input, bool &should_continue, bool &final) + { + if (input == NULL) { + qFatal("null input to multi-thread stage"); + } + + TemplateList ftes; + splitFTEs(input->data, ftes); + TemplateList res; + transform->project(input->data, res); + input->data = res; + input->data.append(ftes); + + should_continue = nextStage->tryAcquireNextStage(input, final); + + return input; + } + + // Called from a different thread than run. Nothing to worry about + // we offer no restrictions on when loops may enter this stage. + virtual bool tryAcquireNextStage(FrameData *& input, bool &final) + { + (void) input; + final = false; + return true; + } + + void reset() + { + // nothing to do. + } + void status() { + qDebug("multi thread stage %d, nothing to worry about", this->stage_id); + } +}; + +class SingleThreadStage : public ProcessingStage +{ +public: + SingleThreadStage(bool input_variance) : ProcessingStage(1) + { + currentStatus = STOPPING; + next_target = 0; + // If the previous stage is single-threaded, queued inputs + // are stored in a double buffer + if (input_variance) { + this->inputBuffer = new DoubleBuffer(); + } + // If it's multi-threaded we need to put the inputs back in order + // before we can use them, so we use a sequencing buffer. + else { + this->inputBuffer = new SequencingBuffer(); + } + } + + ~SingleThreadStage() + { + delete inputBuffer; + } + + void reset() + { + QWriteLocker writeLock(&statusLock); + currentStatus = STOPPING; + next_target = 0; + inputBuffer->reset(); + } + + + int next_target; + enum Status + { + STARTING, + STOPPING + }; + QReadWriteLock statusLock; + Status currentStatus; + + FrameData *run(FrameData *input, bool &should_continue, bool &final) + { + if (input == NULL) + qFatal("NULL input to stage %d", this->stage_id); + + if (input->sequenceNumber != next_target) + qFatal("out of order frames for stage %d, got %d expected %d", this->stage_id, input->sequenceNumber, this->next_target); + + next_target = input->sequenceNumber + 1; + + TemplateList ftes; + splitFTEs(input->data, ftes); + TemplateList res; + transform->projectUpdate(input->data, res); + input->data = res; + input->data.append(ftes); + + should_continue = nextStage->tryAcquireNextStage(input,final); + + if (final) + return input; + + // Is there anything on our input buffer? If so we should start a thread with that. + QWriteLocker lock(&statusLock); + FrameData *newItem = inputBuffer->tryGetItem(); + if (!newItem) + { + this->currentStatus = STOPPING; + } + lock.unlock(); + + if (newItem) + startThread(newItem); + + return input; + } + + void startThread(br::FrameData *newItem) + { + BasicLoop *next = new BasicLoop(); + next->stages = stages; + next->start_idx = this->stage_id; + next->startItem = newItem; + + // We start threads with priority equal to their stage id + // This is intended to ensure progression, we do queued late stage + // jobs before queued early stage jobs, and so tend to finish frames + // rather than go stage by stage. In Qt 5.1, priorities are priorities + // so we use the stage_id directly. + this->threads->start(next, stage_id); + } + + + // Calledfrom a different thread than run. + bool tryAcquireNextStage(FrameData *& input, bool &final) + { + final = false; + inputBuffer->addItem(input); + + QReadLocker lock(&statusLock); + // Thread is already running, we should just return + if (currentStatus == STARTING) + { + return false; + } + // Have to change to a write lock to modify currentStatus + lock.unlock(); + + QWriteLocker writeLock(&statusLock); + // But someone else might have started a thread in the meantime + if (currentStatus == STARTING) + { + return false; + } + // Ok we might start a thread, as long as we can get something back + // from the input buffer + input = inputBuffer->tryGetItem(); + + if (!input) + return false; + + currentStatus = STARTING; + + return true; + } + + void status() { + qDebug("single thread stage %d, status starting? %d, next %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->inputBuffer->size()); + } + +}; + +class EndStage : public SingleThreadStage +{ +public: + EndStage(bool input_variance) : SingleThreadStage(input_variance) {} + + ~EndStage() {} + + // Calledfrom a different thread than run. + bool tryAcquireNextStage(FrameData *& input, bool &final) + { + for (int i=0; i < input->data.size(); i++) { + if (input->data[i].file.fte) { + input->data[i].file.fte = false; + input->data[i].file.set("FTE",QVariant::fromValue(true)); + } + else + input->data[i].file.set("FTE",QVariant::fromValue(false)); + } + + return SingleThreadStage::tryAcquireNextStage(input, final); + } + + void status() { + qDebug("end stage %d, status starting? %d, next %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->inputBuffer->size()); + } + +}; + +// Semi-functional, doesn't do anything productive outside of stream::train +class CollectSets : public TimeVaryingTransform +{ + Q_OBJECT +public: + CollectSets() : TimeVaryingTransform(false, false) {} + + QList sets; + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + (void) dst; + sets.append(src); + } + + void train(const TemplateList &data) + { + (void) data; + } + +}; + +class CollectOutputTransform : public TimeVaryingTransform +{ + Q_OBJECT +public: + CollectOutputTransform() : TimeVaryingTransform(false, false) {} + + TemplateList data; + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + (void) dst; + data.append(src); + } + + void finalize(TemplateList &output) + { + output = data; + data.clear(); + } + void train(const TemplateList &data) + { + (void) data; + } +}; +BR_REGISTER(Transform, CollectOutputTransform) + +// This stage reads new frames from the data source. +class ReadStage : public SingleThreadStage +{ +public: + ReadStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ } + + DataSource dataSource; + + void reset() + { + dataSource.close(); + SingleThreadStage::reset(); + } + + FrameData *run(FrameData *input, bool &should_continue, bool &final) + { + if (input == NULL) + qFatal("NULL frame in input stage"); + + // Can we enter the next stage? + should_continue = nextStage->tryAcquireNextStage(input, final); + + // Try to get a frame from the datasource, we keep working on + // the frame we have, but we will queue another job for the next + // frame if a frame is currently available. + QWriteLocker lock(&statusLock); + bool last_frame = false; + FrameData *newFrame = dataSource.tryGetFrame(last_frame); + + // Were we able to get a frame? + if (newFrame) startThread(newFrame); + // If not this stage will enter a stopped state. + else { + currentStatus = STOPPING; + } + + lock.unlock(); + + return input; + } + + // The last stage, trying to access the first stage + bool tryAcquireNextStage(FrameData *& input, bool &final) + { + // Return the frame, was it the last one? + final = dataSource.returnFrame(input); + input = NULL; + + // OK we won't continue. + if (final) { + return false; + } + + QReadLocker lock(&statusLock); + // If the first stage is already active we will just end. + if (currentStatus == STARTING) + { + return false; + } + + // Otherwise we will try to continue, but to do so we have to + // escalate the lock, and sadly there is no way to do so without + // releasing the read-mode lock, and getting a new write-mode lock. + lock.unlock(); + + QWriteLocker writeLock(&statusLock); + // currentStatus might have changed in the gap between releasing the read + // lock and getting the write lock. + if (currentStatus == STARTING) + { + return false; + } + + bool last_frame = false; + // Try to get a frame from the data source, if we get one we will + // continue to the first stage. + input = dataSource.tryGetFrame(last_frame); + + if (!input) { + return false; + } + + currentStatus = STARTING; + + return true; + } + + void status() { + qDebug("Read stage %d, status starting? %d, next frame %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->dataSource.size()); + } +}; + +void BasicLoop::run() +{ + int current_idx = start_idx; + FrameData *target_item = startItem; + bool should_continue = true; + bool the_end = false; + forever + { + target_item = stages->at(current_idx)->run(target_item, should_continue, the_end); + if (!should_continue) { + break; + } + current_idx++; + current_idx = current_idx % stages->size(); + } + if (the_end) { + dynamic_cast (stages->at(0))->dataSource.wake(); + } + + this->reportFinished(); + +} + +class DirectStreamTransform : public CompositeTransform +{ + Q_OBJECT + +public: + Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) + Q_PROPERTY(br::Transform* endPoint READ get_endPoint WRITE set_endPoint RESET reset_endPoint STORED true) + BR_PROPERTY(int, activeFrames, 100) + BR_PROPERTY(br::Transform*, endPoint, make("CollectOutput")) + + friend class StreamTransfrom; + + void subProject(QList &data, int end_idx) + { + if (end_idx == 0) + return; + + CollectSets collector; + + // Set transforms to the start, up to end_idx + QList backup = this->transforms; + transforms = backup.mid(0,end_idx); + // We use collector to retain the project structure at the end of the + // truncated stream. + transforms.append(&collector); + + // Reinitialize, we now act as a shorter stream. + init(); + + QList output; + for (int i=0; i < data.size(); i++) { + TemplateList res; + TemplateList ftes; + projectUpdate(data[i], res); + data[i] = res; + splitFTEs(data[i], ftes); + output.append(collector.sets); + collector.sets.clear(); + } + data = output; + transforms = backup; + } + + void train(const QList &data) + { + if (!trainable) { + qWarning("Attempted to train untrainable transform, nothing will happen."); + return; + } + QList separated; + foreach (const TemplateList &list, data) { + foreach(const Template &t, list) { + separated.append(TemplateList()); + separated.last().append(t); + } + } + + for (int i=0; i < transforms.size(); i++) { + // OK we have a trainable transform, we need to get input data for it. + if (transforms[i]->trainable) { + QList copy = separated; + // Project from the start to the trainable stage. + subProject(copy,i); + + transforms[i]->train(copy); + } + } + // Re-initialize because subProject probably messed us up. + init(); + } + + bool timeVarying() const { return true; } + + void project(const Template &src, Template &dst) const + { + TemplateList in; + in.append(src); + TemplateList out; + CompositeTransform::project(in,out); + if (!out.isEmpty()) dst = out.first(); + if (out.size() > 1) + qDebug("Returning first output template only"); + } + + void projectUpdate(const Template &src, Template &dst) + { + TemplateList in; + in.append(src); + TemplateList out; + projectUpdate(in,out); + if (!out.isEmpty()) dst = out.first(); + if (out.size() > 1) + qDebug("Returning first output template only"); + } + + + virtual void finalize(TemplateList &output) + { + (void) output; + // Nothing in particular to do here, stream calls finalize + // on all child transforms as part of projectUpdate + } + + // start processing, consider all templates in src a continuous + // 'video' + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst = src; + if (src.empty()) + return; + + bool res = readStage->dataSource.open(src); + if (!res) { + qDebug("stream failed to open %s", qPrintable(dst[0].file.name)); + return; + } + + // Start the first thread in the stream. + QWriteLocker lock(&readStage->statusLock); + readStage->currentStatus = SingleThreadStage::STARTING; + + // We have to get a frame before starting the thread + bool last_frame = false; + FrameData *firstFrame = readStage->dataSource.tryGetFrame(last_frame); + if (firstFrame == NULL) + qFatal("Failed to read first frame of video"); + + readStage->startThread(firstFrame); + lock.unlock(); + + // Wait for the stream to process the last frame available from + // the data source. + readStage->dataSource.waitLast(); + + // Now that there are no more incoming frames, call finalize + // on each transform in turn to collect any last templates + // they wish to issue. + TemplateList final_output; + + // Push finalize through the stages + for (int i=0; i < this->transforms.size(); i++) + { + TemplateList output_set; + transforms[i]->finalize(output_set); + if (output_set.empty()) + continue; + + for (int j=i+1; j < transforms.size();j++) + { + transforms[j]->projectUpdate(output_set); + } + final_output.append(output_set); + } + endPoint->projectUpdate(final_output); + + // Clear dst, since we set it to src so that the datasource could open it + dst.clear(); + + // dst is set to all output received by the final stage, along + // with anything output via the calls to finalize. + TemplateList output; + endPoint->finalize(output); + dst.append(output); + + foreach (ProcessingStage *stage, processingStages) + stage->reset(); + } + + + // Create and link stages + void init() + { + if (transforms.isEmpty()) return; + + for (int i=0; i < processingStages.size();i++) + delete processingStages[i]; + processingStages.clear(); + + // call CompositeTransform::init so that trainable is set + // correctly. + CompositeTransform::init(); + + // We share a thread pool across streams attached to the same + // parent tranform, retrieve or create a thread pool based + // on our parent transform. + QMutexLocker poolLock(&poolsAccess); + QHash::Iterator it; + if (!pools.contains(this->parent())) { + it = pools.insert(this->parent(), new QThreadPool()); + it.value()->setMaxThreadCount(Globals->parallelism); + } + else it = pools.find(this->parent()); + threads = it.value(); + poolLock.unlock(); + + // Are our children time varying or not? This decides whether + // we run them in single threaded or multi threaded stages + stage_variance.clear(); + stage_variance.reserve(transforms.size()); + foreach (const br::Transform *transform, transforms) { + stage_variance.append(transform->timeVarying()); + } + + // Additionally, we have a separate stage responsible for reading + // frames from the data source + readStage = new ReadStage(activeFrames); + + processingStages.push_back(readStage); + readStage->stage_id = 0; + readStage->stages = &this->processingStages; + readStage->threads = this->threads; + + // Initialize and link a processing stage for each of our child + // transforms. + int next_stage_id = 1; + bool prev_stage_variance = true; + for (int i =0; i < transforms.size(); i++) + { + if (stage_variance[i]) + // Whether or not the previous stage is multi-threaded controls + // the type of input buffer we need in a single threaded stage. + processingStages.append(new SingleThreadStage(prev_stage_variance)); + else + processingStages.append(new MultiThreadStage(Globals->parallelism)); + + processingStages.last()->stage_id = next_stage_id++; + + // link nextStage pointers, the stage we just appeneded is i+1 since + // the read stage was added before this loop + processingStages[i]->nextStage = processingStages[i+1]; + + processingStages.last()->stages = &this->processingStages; + processingStages.last()->threads = this->threads; + + processingStages.last()->transform = transforms[i]; + prev_stage_variance = stage_variance[i]; + } + + // We also have the last stage, which just puts the output of the + // previous stages on a template list. + collectionStage = new EndStage(prev_stage_variance); + collectionStage->transform = this->endPoint; + + + processingStages.append(collectionStage); + collectionStage->stage_id = next_stage_id; + collectionStage->stages = &this->processingStages; + collectionStage->threads = this->threads; + + // the last transform stage points to collection stage + processingStages[processingStages.size() - 2]->nextStage = collectionStage; + + // And the collection stage points to the read stage, because this is + // a ring buffer. + collectionStage->nextStage = readStage; + } + + ~DirectStreamTransform() + { + // Delete all the stages + for (int i = 0; i < processingStages.size(); i++) { +// TODO: Are we releasing memory which is already freed? + delete processingStages[i]; + } + processingStages.clear(); + } + +protected: + QList stage_variance; + + ReadStage *readStage; + SingleThreadStage *collectionStage; + + QList processingStages; + + // This is a map from parent transforms (of Streams) to thread pools. Rather + // than starting threads on the global thread pool, Stream uses separate thread pools + // keyed on their parent transform. This is necessary because stream's project starts + // threads, then enters an indefinite wait for them to finish. Since we are starting + // threads using thread pools, threads themselves are a limited resource. Therefore, + // the type of hold and wait done by stream project can lead to deadlock unless + // resources are ordered in such a way that a circular wait will not occur. The points + // of this hash is to introduce a resource ordering (on threads) that mirrors the structure + // of the algorithm. So, as long as the structure of the algorithm is a DAG, the wait done + // by stream project will not be circular, since every thread in stream project is waiting + // for threads at a lower level to do the work. + // This issue doesn't come up in distribute, since a thread waiting on a QFutureSynchronizer + // will steal work from those jobs, so in that sense distribute isn't doing a hold and wait. + // Waiting for a QFutureSynchronzier isn't really possible here since stream runs an indeteriminate + // number of jobs. + static QHash pools; + static QMutex poolsAccess; + QThreadPool *threads; + + void _project(const Template &src, Template &dst) const + { + (void) src; (void) dst; + qFatal("nope"); + } + void _project(const TemplateList &src, TemplateList &dst) const + { + (void) src; (void) dst; + qFatal("nope"); + } +}; + +QHash DirectStreamTransform::pools; +QMutex DirectStreamTransform::poolsAccess; + +BR_REGISTER(Transform, DirectStreamTransform) + +class StreamTransform : public WrapperTransform +{ + Q_OBJECT + +public: + StreamTransform() : WrapperTransform(false) + { + } + + Q_PROPERTY(br::Transform* endPoint READ get_endPoint WRITE set_endPoint RESET reset_endPoint STORED true) + Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) + + BR_PROPERTY(int, activeFrames, 100) + BR_PROPERTY(br::Transform*, endPoint, make("CollectOutput")) + + bool timeVarying() const { return true; } + + void project(const Template &src, Template &dst) const + { + basis->project(src,dst); + } + + void projectUpdate(const Template &src, Template &dst) + { + basis->projectUpdate(src,dst); + } + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + basis->projectUpdate(src,dst); + } + + void train(const QList &data) + { + basis->train(data); + } + + virtual void finalize(TemplateList &output) + { + (void) output; + // Nothing in particular to do here, stream calls finalize + // on all child transforms as part of projectUpdate + } + + // reinterpret transform, set up the actual stream. We can only reinterpret pipes + void init() + { + if (!transform) + return; + + trainable = transform->trainable; + + basis = QSharedPointer((DirectStreamTransform *) Transform::make("DirectStream",this)); + basis->transforms.clear(); + basis->activeFrames = this->activeFrames; + basis->endPoint = this->endPoint; + + // We need at least a CompositeTransform * to acess transform's children. + CompositeTransform *downcast = dynamic_cast (transform); + + // If this isn't even a composite transform, or it's not a pipe, just set up + // basis with 1 stage. + if (!downcast || QString(transform->metaObject()->className()) != "br::PipeTransform") + { + basis->transforms.append(transform); + basis->init(); + return; + } + if (downcast->transforms.empty()) + { + qWarning("Trying to set up empty stream"); + basis->init(); + return; + } + + // OK now we will regroup downcast's children + QList > sets; + sets.append(QList ()); + sets.last().append(downcast->transforms[0]); + if (downcast->transforms[0]->timeVarying()) + sets.append(QList ()); + + for (int i=1;i < downcast->transforms.size(); i++) { + // If this is time varying it becomse its own stage + if (downcast->transforms[i]->timeVarying()) { + // If a set was already active, we add another one + if (!sets.last().empty()) { + sets.append(QList()); + } + // add the item + sets.last().append(downcast->transforms[i]); + // Add another set to indicate separation. + sets.append(QList()); + } + // otherwise, we can combine non time-varying stages + else { + sets.last().append(downcast->transforms[i]); + } + + } + if (sets.last().empty()) + sets.removeLast(); + + QList transform_set; + transform_set.reserve(sets.size()); + for (int i=0; i < sets.size(); i++) { + // If this is a single transform set, we add that to the list + if (sets[i].size() == 1 ) { + transform_set.append(sets[i].at(0)); + } + //otherwise we build a pipe + else { + CompositeTransform *pipe = dynamic_cast(Transform::make("Pipe([])", this)); + pipe->transforms = sets[i]; + pipe->init(); + transform_set.append(pipe); + } + } + + basis->transforms = transform_set; + basis->init(); + } + + Transform *smartCopy(bool &newTransform) + { + // We just want the DirectStream to begin with, so just return a copy of that. + DirectStreamTransform *res = (DirectStreamTransform *) basis->smartCopy(newTransform); + res->activeFrames = this->activeFrames; + return res; + } + + // can't use general setPropertyRecursive due to special handling of basis + bool setPropertyRecursive(const QString &name, QVariant value) + { + if (br::Object::setExistingProperty(name, value)) + return true; + + for (int i=0; i < basis->transforms.size();i++) { + if (basis->transforms[i]->setPropertyRecursive(name, value)) { + basis->init(); + return true; + } + } + + if (basis->endPoint->setPropertyRecursive(name, value)) { + basis->endPoint->init(); + return true; + } + + return false; + } + + // Stream acts as a shallow interface to DirectStream, so it's fine to remove ourselves here + Transform *simplify(bool &newTransform) + { + return basis->simplify(newTransform); + } + +private: + QSharedPointer basis; +}; + +BR_REGISTER(Transform, StreamTransform) + +} // namespace br + +#include "core/stream.moc" + diff --git a/openbr/plugins/detection/cascade.cpp b/openbr/plugins/detection/cascade.cpp deleted file mode 100644 index f523cff..0000000 --- a/openbr/plugins/detection/cascade.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright 2012 The MITRE Corporation * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include -#include -#include "openbr/core/opencvutils.h" -#include "openbr/core/resource.h" -#include "openbr/core/qtutils.h" -#include -#include - -using namespace cv; - -struct TrainParams -{ - QString data; // REQUIRED: Filepath to store trained classifier - QString vec; // REQUIRED: Filepath to store vector of positive samples, default "vector" - QString img; // Filepath to source object image. Either this or info is REQUIRED - QString info; // Description file of source images. Either this or img is REQUIRED - QString bg; // REQUIRED: Filepath to background list file - int num; // Number of samples to generate - int bgcolor; // Background color supplied image (via img) - int bgthresh; // Threshold to determine bgcolor match - bool inv; // Invert colors - bool randinv; // Randomly invert colors - int maxidev; // Max intensity deviation of foreground pixels - double maxxangle; // Maximum rotation angle (X) - double maxyangle; // Maximum rotation angle (Y) - double maxzangle; // Maximum rotation angle (Z) - bool show; // Show generated samples - int w; // REQUIRED: Sample width - int h; // REQUIRED: Sample height - int numPos; // Number of positive samples - int numNeg; // Number of negative samples - int numStages; // Number of stages - int precalcValBufSize; // Precalculated val buffer size in Mb - int precalcIdxBufSize; // Precalculated index buffer size in Mb - bool baseFormatSave; // Save in old format - QString stageType; // Stage type (BOOST) - QString featureType; // Feature type (HAAR, LBP) - QString bt; // Boosted classifier type (DAB, RAB, LB, GAB) - double minHitRate; // Minimal hit rate per stage - double maxFalseAlarmRate; // Max false alarm rate per stage - double weightTrimRate; // Weight for trimming - int maxDepth; // Max weak tree depth - int maxWeakCount; // Max weak tree count per stage - QString mode; // Haar feature mode (BASIC, CORE, ALL) - - TrainParams() - { - num = -1; - maxidev = -1; - maxxangle = -1; - maxyangle = -1; - maxzangle = -1; - w = -1; - h = -1; - numPos = -1; - numNeg = -1; - numStages = -1; - precalcValBufSize = -1; - precalcIdxBufSize = -1; - minHitRate = -1; - maxFalseAlarmRate = -1; - weightTrimRate = -1; - maxDepth = -1; - maxWeakCount = -1; - inv = false; - randinv = false; - show = false; - baseFormatSave = false; - vec = "vector.vec"; - bgcolor = -1; - bgthresh = -1; - } -}; - -static QStringList buildTrainingArgs(const TrainParams ¶ms) -{ - QStringList args; - if (params.data != "") args << "-data" << params.data; - else qFatal("Must specify storage location for cascade"); - if (params.vec != "") args << "-vec" << params.vec; - else qFatal("Must specify location of positive vector"); - if (params.bg != "") args << "-bg" << params.bg; - else qFatal("Must specify negative images"); - if (params.numPos >= 0) args << "-numPos" << QString::number(params.numPos); - if (params.numNeg >= 0) args << "-numNeg" << QString::number(params.numNeg); - if (params.numStages >= 0) args << "-numStages" << QString::number(params.numStages); - if (params.precalcValBufSize >= 0) args << "-precalcValBufSize" << QString::number(params.precalcValBufSize); - if (params.precalcIdxBufSize >= 0) args << "-precalcIdxBufSize" << QString::number(params.precalcIdxBufSize); - if (params.baseFormatSave) args << "-baseFormatSave"; - if (params.stageType != "") args << "-stageType" << params.stageType; - if (params.featureType != "") args << "-featureType" << params.featureType; - if (params.w >= 0) args << "-w" << QString::number(params.w); - else qFatal("Must specify width"); - if (params.h >= 0) args << "-h" << QString::number(params.h); - else qFatal("Must specify height"); - if (params.bt != "") args << "-bt" << params.bt; - if (params.minHitRate >= 0) args << "-minHitRate" << QString::number(params.minHitRate); - if (params.maxFalseAlarmRate >= 0) args << "-maxFalseAlarmRate" << QString::number(params.maxFalseAlarmRate); - if (params.weightTrimRate >= 0) args << "-weightTrimRate" << QString::number(params.weightTrimRate); - if (params.maxDepth >= 0) args << "-maxDepth" << QString::number(params.maxDepth); - if (params.maxWeakCount >= 0) args << "-maxWeakCount" << QString::number(params.maxWeakCount); - if (params.mode != "") args << "-mode" << params.mode; - return args; -} - -static QStringList buildSampleArgs(const TrainParams ¶ms) -{ - QStringList args; - if (params.vec != "") args << "-vec" << params.vec; - else qFatal("Must specify location of positive vector"); - if (params.img != "") args << "-img" << params.img; - else if (params.info != "") args << "-info" << params.info; - else qFatal("Must specify positive images"); - if (params.bg != "") args << "-bg" << params.bg; - if (params.num > 0) args << "-num" << QString::number(params.num); - if (params.bgcolor >=0 ) args << "-bgcolor" << QString::number(params.bgcolor); - if (params.bgthresh >= 0) args << "-bgthresh" << QString::number(params.bgthresh); - if (params.maxidev >= 0) args << "-maxidev" << QString::number(params.maxidev); - if (params.maxxangle >= 0) args << "-maxxangle" << QString::number(params.maxxangle); - if (params.maxyangle >= 0) args << "-maxyangle" << QString::number(params.maxyangle); - if (params.maxzangle >= 0) args << "-maxzangle" << QString::number(params.maxzangle); - if (params.w >= 0) args << "-w" << QString::number(params.w); - if (params.h >= 0) args << "-h" << QString::number(params.h); - if (params.show) args << "-show"; - if (params.inv) args << "-inv"; - if (params.randinv) args << "-randinv"; - return args; -} - -static void genSamples(const TrainParams ¶ms) -{ - const QStringList cmdArgs = buildSampleArgs(params); - QProcess::execute("opencv_createsamples",cmdArgs); -} - -static void trainCascade(const TrainParams ¶ms) -{ - const QStringList cmdArgs = buildTrainingArgs(params); - QProcess::execute("opencv_traincascade", cmdArgs); -} - -namespace br -{ - -class CascadeResourceMaker : public ResourceMaker -{ - QString file; - -public: - CascadeResourceMaker(const QString &model) - { - file = Globals->sdkPath + "/share/openbr/models/"; - if (model == "Ear") file += "haarcascades/haarcascade_ear.xml"; - else if (model == "Eye") file += "haarcascades/haarcascade_eye_tree_eyeglasses.xml"; - else if (model == "FrontalFace") file += "haarcascades/haarcascade_frontalface_alt2.xml"; - else if (model == "ProfileFace") file += "haarcascades/haarcascade_profileface.xml"; - else { - // Create a directory for trainable cascades - file += "openbrcascades/"+model+"/cascade.xml"; - QFile touchFile(file); - QtUtils::touchDir(touchFile); - } - } - -private: - CascadeClassifier *make() const - { - CascadeClassifier *cascade = new CascadeClassifier(); - if (!cascade->load(file.toStdString())) - qFatal("Failed to load: %s", qPrintable(file)); - return cascade; - } -}; - -/*! - * \ingroup transforms - * \brief Wraps OpenCV cascade classifier - * \author Josh Klontz \cite jklontz - * \author David Crouse \cite dgcrouse - */ -class CascadeTransform : public MetaTransform -{ - Q_OBJECT - Q_PROPERTY(QString model READ get_model WRITE set_model RESET reset_model STORED false) - Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) - Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) - Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false) - - // Training parameters - Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false) - Q_PROPERTY(int w READ get_w WRITE set_w RESET reset_w STORED false) - Q_PROPERTY(int h READ get_h WRITE set_h RESET reset_h STORED false) - Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false) - Q_PROPERTY(int numNeg READ get_numNeg WRITE set_numNeg RESET reset_numNeg STORED false) - Q_PROPERTY(int precalcValBufSize READ get_precalcValBufSize WRITE set_precalcValBufSize RESET reset_precalcValBufSize STORED false) - Q_PROPERTY(int precalcIdxBufSize READ get_precalcIdxBufSize WRITE set_precalcIdxBufSize RESET reset_precalcIdxBufSize STORED false) - Q_PROPERTY(double minHitRate READ get_minHitRate WRITE set_minHitRate RESET reset_minHitRate STORED false) - Q_PROPERTY(double maxFalseAlarmRate READ get_maxFalseAlarmRate WRITE set_maxFalseAlarmRate RESET reset_maxFalseAlarmRate STORED false) - Q_PROPERTY(double weightTrimRate READ get_weightTrimRate WRITE set_weightTrimRate RESET reset_weightTrimRate STORED false) - Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) - Q_PROPERTY(int maxWeakCount READ get_maxWeakCount WRITE set_maxWeakCount RESET reset_maxWeakCount STORED false) - Q_PROPERTY(QString stageType READ get_stageType WRITE set_stageType RESET reset_stageType STORED false) - Q_PROPERTY(QString featureType READ get_featureType WRITE set_featureType RESET reset_featureType STORED false) - Q_PROPERTY(QString bt READ get_bt WRITE set_bt RESET reset_bt STORED false) - Q_PROPERTY(QString mode READ get_mode WRITE set_mode RESET reset_mode STORED false) - Q_PROPERTY(bool show READ get_show WRITE set_show RESET reset_show STORED false) - Q_PROPERTY(bool baseFormatSave READ get_baseFormatSave WRITE set_baseFormatSave RESET reset_baseFormatSave STORED false) - - BR_PROPERTY(QString, model, "FrontalFace") - BR_PROPERTY(int, minSize, 64) - BR_PROPERTY(int, minNeighbors, 5) - BR_PROPERTY(bool, ROCMode, false) - - // Training parameters - Default values provided trigger OpenCV defaults - BR_PROPERTY(int, numStages, -1) - BR_PROPERTY(int, w, -1) - BR_PROPERTY(int, h, -1) - BR_PROPERTY(int, numPos, -1) - BR_PROPERTY(int, numNeg, -1) - BR_PROPERTY(int, precalcValBufSize, -1) - BR_PROPERTY(int, precalcIdxBufSize, -1) - BR_PROPERTY(double, minHitRate, -1) - BR_PROPERTY(double, maxFalseAlarmRate, -1) - BR_PROPERTY(double, weightTrimRate, -1) - BR_PROPERTY(int, maxDepth, -1) - BR_PROPERTY(int, maxWeakCount, -1) - BR_PROPERTY(QString, stageType, "") - BR_PROPERTY(QString, featureType, "") - BR_PROPERTY(QString, bt, "") - BR_PROPERTY(QString, mode, "") - BR_PROPERTY(bool, show, false) - BR_PROPERTY(bool, baseFormatSave, false) - - Resource cascadeResource; - - void init() - { - cascadeResource.setResourceMaker(new CascadeResourceMaker(model)); - if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace") - this->trainable = false; - } - - // Train transform - void train(const TemplateList& data) - { - // Don't train if we're using OpenCV's prebuilt cascades - if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace") - return; - - // Open positive and negative list temporary files - QTemporaryFile posFile; - QTemporaryFile negFile; - - posFile.open(); - negFile.open(); - - QTextStream posStream(&posFile); - QTextStream negStream(&negFile); - - TrainParams params; - - // Fill in from params (param defaults are same as struct defaults, so no checks are needed) - params.numStages = numStages; - params.w = w; - params.h = h; - params.numPos = numPos; - params.numNeg = numNeg; - params.precalcValBufSize = precalcValBufSize; - params.precalcIdxBufSize = precalcIdxBufSize; - params.minHitRate = minHitRate; - params.maxFalseAlarmRate = maxFalseAlarmRate; - params.weightTrimRate = weightTrimRate; - params.maxDepth = maxDepth; - params.maxWeakCount = maxWeakCount; - params.stageType = stageType; - params.featureType = featureType; - params.bt = bt; - params.mode = mode; - params.show = show; - params.baseFormatSave = baseFormatSave; - if (params.w < 0) params.w = minSize; - if (params.h < 0) params.h = minSize; - - int posCount = 0; - int negCount = 0; - - bool buildPos = false; // If true, build positive vector from single image - - const FileList files = data.files(); - - for (int i = 0; i < files.length(); i++) { - File f = files[i]; - if (f.contains("training-set")) { - QString tset = f.get("training-set",QString()).toLower(); - - // Negative samples - if (tset == "neg") { - negStream << f.path() << QDir::separator() << f.fileName() << endl; - negCount++; - // Positive samples for crop/rescale - } else if (tset == "pos") { - QString buffer = ""; - - // Extract rectangles - QList rects = f.rects(); - for (int j = 0; j < rects.size(); j++) { - QRectF r = rects[j]; - buffer += " " + QString::number(r.x()) + " " + QString::number(r.y()) + " " + QString::number(r.width()) + " "+ QString::number(r.height()); - posCount++; - } - - posStream << f.path() << QDir::separator() << f.fileName() << " " << f.rects().length() << " " << buffer << endl; - - // Single positive sample for background removal and overlay on negatives - } else if (tset == "pos-base") { - buildPos = true; - params.img = f.path() + QDir::separator() + f.fileName(); - - // Parse settings (unique to this one tag) - if (f.contains("num")) params.num = f.get("num"); - if (f.contains("bgcolor")) params.bgcolor = f.get("bgcolor"); - if (f.contains("bgthresh")) params.bgthresh =f.get("bgthresh"); - if (f.contains("inv")) params.inv = f.get("inv",false); - if (f.contains("randinv")) params.randinv = f.get("randinv",false); - if (f.contains("maxidev")) params.maxidev = f.get("maxidev"); - if (f.contains("maxxangle")) params.maxxangle = f.get("maxxangle"); - if (f.contains("maxyangle")) params.maxyangle = f.get("maxyangle"); - if (f.contains("maxzangle")) params.maxzangle = f.get("maxzangle"); - } - } - } - - posFile.close(); - negFile.close(); - - // Fill in remaining params conditionally - if (buildPos) { - if (params.numPos < 0) { - if (params.num > 0) params.numPos = params.num*.95; - else params.numPos = 950; - } - } else { - params.info = posFile.fileName(); - if (params.numPos < 0) params.numPos = posCount*.95; - } - - if (params.num < 0) params.num = posCount; - if (params.numNeg < 0) params.numNeg = negCount*10; - - params.bg = negFile.fileName(); - params.data = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model + "/cascade.xml"; - - genSamples(params); - trainCascade(params); - } - - void project(const Template &src, Template &dst) const - { - TemplateList temp; - project(TemplateList() << src, temp); - if (!temp.isEmpty()) dst = temp.first(); - } - - void project(const TemplateList &src, TemplateList &dst) const - { - CascadeClassifier *cascade = cascadeResource.acquire(); - foreach (const Template &t, src) { - const bool enrollAll = t.file.getBool("enrollAll"); - - // Mirror the behavior of ExpandTransform in the special case - // of an empty template. - if (t.empty() && !enrollAll) { - dst.append(t); - continue; - } - - for (int i=0; i rects; - std::vector rejectLevels; - std::vector levelWeights; - if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, (enrollAll ? 0 : CASCADE_FIND_BIGGEST_OBJECT) | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true); - else cascade->detectMultiScale(m, rects, 1.2, minNeighbors, enrollAll ? 0 : CASCADE_FIND_BIGGEST_OBJECT, Size(minSize, minSize)); - - if (!enrollAll && rects.empty()) - rects.push_back(Rect(0, 0, m.cols, m.rows)); - - for (size_t j=0; j j) - u.file.set("Confidence", rejectLevels[j]*levelWeights[j]); - else - u.file.set("Confidence", 1); - const QRectF rect = OpenCVUtils::fromRect(rects[j]); - u.file.appendRect(rect); - u.file.set(model, rect); - dst.append(u); - } - } - } - - cascadeResource.release(cascade); - } - - // TODO: Remove this code when ready to break binary compatibility - void store(QDataStream &stream) const - { - int size = 1; - stream << size; - } - - void load(QDataStream &stream) - { - int size; - stream >> size; - } -}; - -BR_REGISTER(Transform, CascadeTransform) - -} // namespace br - -#include "cascade.moc" diff --git a/openbr/plugins/detection/eyes.cpp b/openbr/plugins/detection/eyes.cpp deleted file mode 100644 index ae00219..0000000 --- a/openbr/plugins/detection/eyes.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* -# PyVision License -# -# Copyright (c) 2006-2008 David S. Bolme -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. 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. -# -# 3. Neither name of copyright holders 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 REGENTS 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. -*/ - -#include -#include - -#include -#include - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Bolme, D.S.; Draper, B.A.; Beveridge, J.R.; - * "Average of Synthetic Exact Filters," - * Computer Vision and Pattern Recognition, 2009. CVPR 2009. - * IEEE Conference on , vol., no., pp.2105-2112, 20-25 June 2009 - * \author David Bolme - * \author Josh Klontz \cite jklontz - */ -class ASEFEyesTransform : public UntrainableTransform -{ - Q_OBJECT - - Mat left_filter_dft, right_filter_dft, lut; - Rect left_rect, right_rect; - int width, height; - -public: - ASEFEyesTransform() - { - QFile file; - QByteArray line, lf, rf, magic_number; - QList words; - int r, c; - Scalar t1, t2; - - // Open the eye locator model - file.setFileName(Globals->sdkPath + "/share/openbr/models/EyeLocatorASEF128x128.fel"); - if (!file.open(QFile::ReadOnly)) qFatal("Failed to open %s for reading.", qPrintable(file.fileName())); - - // Check the first line - if (file.readLine().simplified() != "CFEL") qFatal("Invalid header."); - - // Read past the comment and copyright. - file.readLine(); - file.readLine(); - - // Get the width and the height - words = file.readLine().simplified().split(' '); - r = words[0].toInt(); - c = words[1].toInt(); - - // Read in the left bounding rectangle - words = file.readLine().simplified().split(' '); - left_rect = Rect(words[0].toInt(), words[1].toInt(), words[2].toInt(), words[3].toInt()); - - // Read in the right bounding rectangle - words = file.readLine().simplified().split(' '); - right_rect = Rect(words[0].toInt(), words[1].toInt(), words[2].toInt(), words[3].toInt()); - - // Read the magic number - magic_number = file.readLine().simplified(); - - // Read in the filter data - lf = file.read(4*r*c); - rf = file.read(4*r*c); - file.close(); - - // Test the magic number and byteswap if necessary. - if (magic_number == "ABCD") { - // Do nothing - } else if (magic_number == "DCBA") { - // Reverse the endianness - // No swapping needed, not sure why - } else { - qFatal("Invalid Magic Number"); - } - - // Create the left and right filters - Mat left_mat = Mat(r, c, CV_32F, lf.data()); - Mat right_mat = Mat(r, c, CV_32F, rf.data()); - - Mat left_filter; - meanStdDev(left_mat, t1, t2); - left_mat.convertTo(left_filter, -1, 1.0/t2[0], -t1[0]*1.0/t2[0]); - - Mat right_filter; - meanStdDev(right_mat, t1, t2); - right_mat.convertTo(right_filter, -1, 1.0/t2[0], -t1[0]*1.0/t2[0]); - - // Check the input to this function - height = left_filter.rows; - width = left_filter.cols; - assert((left_filter.rows == right_filter.rows) && - (left_filter.cols == right_filter.cols) && - (left_filter.channels() == 1) && - (right_filter.channels() == 1)); - - // Create the arrays needed for the computation - left_filter_dft = Mat(r, c, CV_32F); - right_filter_dft = Mat(r, c, CV_32F); - - // Compute the filters in the Fourier domain - dft(left_filter, left_filter_dft, CV_DXT_FORWARD); - dft(right_filter, right_filter_dft, CV_DXT_FORWARD); - - // Create the look up table for the log transform - lut = Mat(256, 1, CV_32F); - for (int i=0; i<256; i++) lut.at(i, 0) = std::log((float)i+1); - } - -private: - void project(const Template &src, Template &dst) const - { - Rect roi = OpenCVUtils::toRect(src.file.rects().first()); - - Mat gray; - OpenCVUtils::cvtGray(src.m()(roi), gray); - - Mat image_tile; - // (r,c) == (128, 128) EyeLocatorASEF128x128.fel - resize(gray, image_tile, Size(height, width)); - - // _preprocess - Mat image; - LUT(image_tile, lut, image); - - // correlate - Mat left_corr, right_corr; - dft(image, image, CV_DXT_FORWARD); - mulSpectrums(image, left_filter_dft, left_corr, 0, true); - mulSpectrums(image, right_filter_dft, right_corr, 0, true); - dft(left_corr, left_corr, CV_DXT_INV_SCALE); - dft(right_corr, right_corr, CV_DXT_INV_SCALE); - - // locateEyes - double minVal, maxVal; - Point minLoc, maxLoc; - - // left_rect == (23, 35) (32, 32) EyeLocatorASEF128x128.fel - minMaxLoc(left_corr(left_rect), &minVal, &maxVal, &minLoc, &maxLoc); - float first_eye_x = (left_rect.x + maxLoc.x)*gray.cols/width+roi.x; - float first_eye_y = (left_rect.y + maxLoc.y)*gray.rows/height+roi.y; - - // right_rect == (71, 32) (32, 32) EyeLocatorASEF128x128.fel - minMaxLoc(right_corr(right_rect), &minVal, &maxVal, &minLoc, &maxLoc); - float second_eye_x = (right_rect.x + maxLoc.x)*gray.cols/width+roi.x; - float second_eye_y = (right_rect.y + maxLoc.y)*gray.rows/height+roi.y; - - dst.m() = src.m(); - dst.file.appendPoint(QPointF(first_eye_x, first_eye_y)); - dst.file.appendPoint(QPointF(second_eye_x, second_eye_y)); - dst.file.set("First_Eye", QPointF(first_eye_x, first_eye_y)); - dst.file.set("Second_Eye", QPointF(second_eye_x, second_eye_y)); - } -}; - -BR_REGISTER(Transform, ASEFEyesTransform) - -} // namespace br - -#include "eyes.moc" diff --git a/openbr/plugins/detection/keypointdetector.cpp b/openbr/plugins/detection/keypointdetector.cpp deleted file mode 100644 index 0c59d9e..0000000 --- a/openbr/plugins/detection/keypointdetector.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -#include -#include - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Wraps OpenCV Key Point Detector - * \author Josh Klontz \cite jklontz - */ -class KeyPointDetectorTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString detector READ get_detector WRITE set_detector RESET reset_detector STORED false) - BR_PROPERTY(QString, detector, "SIFT") - - Ptr featureDetector; - - void init() - { - featureDetector = FeatureDetector::create(detector.toStdString()); - if (featureDetector.empty()) - qFatal("Failed to create KeyPointDetector: %s", qPrintable(detector)); - } - - void project(const Template &src, Template &dst) const - { - dst = src; - - std::vector keyPoints; - try { - featureDetector->detect(src, keyPoints); - } catch (...) { - qWarning("Key point detection failed for file %s", qPrintable(src.file.name)); - dst.file.fte = true; - } - - QList rects; - foreach (const KeyPoint &keyPoint, keyPoints) - rects.append(Rect(keyPoint.pt.x-keyPoint.size/2, keyPoint.pt.y-keyPoint.size/2, keyPoint.size, keyPoint.size)); - dst.file.setRects(OpenCVUtils::fromRects(rects)); - } -}; - -BR_REGISTER(Transform, KeyPointDetectorTransform) - -} // namespace br - -#include "keypointdetector.moc" diff --git a/openbr/plugins/distance/L2.cpp b/openbr/plugins/distance/L2.cpp index 3543894..aca49a0 100644 --- a/openbr/plugins/distance/L2.cpp +++ b/openbr/plugins/distance/L2.cpp @@ -27,4 +27,4 @@ BR_REGISTER(Distance, L2Distance) } // namespace br -#include "L2.moc" +#include "distance/L2.moc" diff --git a/openbr/plugins/distance/attribute.cpp b/openbr/plugins/distance/attribute.cpp index 17446b2..cdf4568 100644 --- a/openbr/plugins/distance/attribute.cpp +++ b/openbr/plugins/distance/attribute.cpp @@ -31,4 +31,4 @@ BR_REGISTER(Distance, AttributeDistance) } // namespace br -#include "attribute.moc" +#include "distance/attribute.moc" diff --git a/openbr/plugins/distance/bayesianquantization.cpp b/openbr/plugins/distance/bayesianquantization.cpp new file mode 100644 index 0000000..0fe58e4 --- /dev/null +++ b/openbr/plugins/distance/bayesianquantization.cpp @@ -0,0 +1,88 @@ +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Bayesian quantization distance + * \author Josh Klontz \cite jklontz + */ +class BayesianQuantizationDistance : public Distance +{ + Q_OBJECT + + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + BR_PROPERTY(QString, inputVariable, "Label") + + QVector loglikelihoods; + + static void computeLogLikelihood(const Mat &data, const QList &labels, float *loglikelihood) + { + const QList vals = OpenCVUtils::matrixToVector(data); + if (vals.size() != labels.size()) + qFatal("Logic error."); + + QVector genuines(256, 0), impostors(256,0); + for (int i=0; i 1) || (src.first().m().type() != CV_8UC1)) + qFatal("Expected sigle matrix templates of type CV_8UC1!"); + + const Mat data = OpenCVUtils::toMat(src.data()); + const QList templateLabels = src.indexProperty(inputVariable); + loglikelihoods = QVector(data.cols*256, 0); + + QFutureSynchronizer futures; + for (int i=0; i> loglikelihoods; + } +}; + +BR_REGISTER(Distance, BayesianQuantizationDistance) + +} // namespace br + +#include "distance/bayesianquantization.moc" diff --git a/openbr/plugins/distance/byteL1.cpp b/openbr/plugins/distance/byteL1.cpp index 4457999..ea377ac 100644 --- a/openbr/plugins/distance/byteL1.cpp +++ b/openbr/plugins/distance/byteL1.cpp @@ -23,4 +23,4 @@ BR_REGISTER(Distance, ByteL1Distance) } // namespace br -#include "byteL1.moc" +#include "distance/byteL1.moc" diff --git a/openbr/plugins/distance/crossvalidate.cpp b/openbr/plugins/distance/crossvalidate.cpp new file mode 100644 index 0000000..54a8f17 --- /dev/null +++ b/openbr/plugins/distance/crossvalidate.cpp @@ -0,0 +1,28 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Cross validate a distance metric. + * \author Josh Klontz \cite jklontz + */ +class CrossValidateDistance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Template &a, const Template &b) const + { + static const QString key("Partition"); // More efficient to preallocate this + const int partitionA = a.file.get(key, 0); + const int partitionB = b.file.get(key, 0); + return (partitionA != partitionB) ? -std::numeric_limits::max() : 0; + } +}; + +BR_REGISTER(Distance, CrossValidateDistance) + +} // namespace br + +#include "distance/crossvalidate.moc" diff --git a/openbr/plugins/distance/default.cpp b/openbr/plugins/distance/default.cpp index 1409145..7c9c4d5 100644 --- a/openbr/plugins/distance/default.cpp +++ b/openbr/plugins/distance/default.cpp @@ -28,4 +28,4 @@ BR_REGISTER(Distance, DefaultDistance) } // namespace br -#include "default.moc" +#include "distance/default.moc" diff --git a/openbr/plugins/distance/dist.cpp b/openbr/plugins/distance/dist.cpp index 1f36afc..2fb8887 100644 --- a/openbr/plugins/distance/dist.cpp +++ b/openbr/plugins/distance/dist.cpp @@ -101,4 +101,4 @@ BR_REGISTER(Distance, DistDistance) } // namespace br -#include "dist.moc" +#include "distance/dist.moc" diff --git a/openbr/plugins/distance/filter.cpp b/openbr/plugins/distance/filter.cpp new file mode 100644 index 0000000..b752493 --- /dev/null +++ b/openbr/plugins/distance/filter.cpp @@ -0,0 +1,39 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Checks target metadata against filters. + * \author Josh Klontz \cite jklontz + */ +class FilterDistance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Template &a, const Template &b) const + { + (void) b; // Query template isn't checked + foreach (const QString &key, Globals->filters.keys()) { + bool keep = false; + const QString metadata = a.file.get(key, ""); + if (Globals->filters[key].isEmpty()) continue; + if (metadata.isEmpty()) return -std::numeric_limits::max(); + foreach (const QString &value, Globals->filters[key]) { + if (metadata == value) { + keep = true; + break; + } + } + if (!keep) return -std::numeric_limits::max(); + } + return 0; + } +}; + +BR_REGISTER(Distance, FilterDistance) + +} // namespace br + +#include "distance/filter.moc" diff --git a/openbr/plugins/distance/fuse.cpp b/openbr/plugins/distance/fuse.cpp index 782fe58..d517db3 100644 --- a/openbr/plugins/distance/fuse.cpp +++ b/openbr/plugins/distance/fuse.cpp @@ -98,4 +98,4 @@ BR_REGISTER(Distance, FuseDistance) } // namespace br -#include "fuse.moc" +#include "distance/fuse.moc" diff --git a/openbr/plugins/distance/gallerycompare.cpp b/openbr/plugins/distance/gallerycompare.cpp deleted file mode 100644 index d4aba79..0000000 --- a/openbr/plugins/distance/gallerycompare.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Compare each template to a fixed gallery (with name = galleryName), using the specified distance. - * dst will contain a 1 by n vector of scores. - * \author Charles Otto \cite caotto - */ -class GalleryCompareTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true) - Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) - BR_PROPERTY(br::Distance*, distance, NULL) - BR_PROPERTY(QString, galleryName, "") - - TemplateList gallery; - - void project(const Template &src, Template &dst) const - { - dst = src; - if (gallery.isEmpty()) - return; - - QList line = distance->compare(gallery, src); - dst.m() = OpenCVUtils::toMat(line, 1); - } - - void init() - { - if (!galleryName.isEmpty()) - gallery = TemplateList::fromGallery(galleryName); - } - - void train(const TemplateList &data) - { - gallery = data; - } - - void store(QDataStream &stream) const - { - br::Object::store(stream); - stream << gallery; - } - - void load(QDataStream &stream) - { - br::Object::load(stream); - stream >> gallery; - } - -public: - GalleryCompareTransform() : Transform(false, true) {} -}; - -BR_REGISTER(Transform, GalleryCompareTransform) - -} // namespace br - -#include "gallerycompare.moc" diff --git a/openbr/plugins/distance/halfbyteL1.cpp b/openbr/plugins/distance/halfbyteL1.cpp index 6f89ebb..70ef1a4 100644 --- a/openbr/plugins/distance/halfbyteL1.cpp +++ b/openbr/plugins/distance/halfbyteL1.cpp @@ -26,4 +26,4 @@ BR_REGISTER(Distance, HalfByteL1Distance) } // namespace br -#include "halfbyteL1.moc" +#include "distance/halfbyteL1.moc" diff --git a/openbr/plugins/distance/heatmap.cpp b/openbr/plugins/distance/heatmap.cpp new file mode 100644 index 0000000..351c04a --- /dev/null +++ b/openbr/plugins/distance/heatmap.cpp @@ -0,0 +1,84 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief 1v1 heat map comparison + * \author Scott Klum \cite sklum + */ +class HeatMapDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + Q_PROPERTY(int step READ get_step WRITE set_step RESET reset_step STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + BR_PROPERTY(QString, description, "IdenticalDistance") + BR_PROPERTY(int, step, 1) + BR_PROPERTY(QString, inputVariable, "Label") + + QList distances; + + void train(const TemplateList &src) + { + QList patches; + + // Split src into list of TemplateLists of corresponding patches across all Templates + for (int i=0; itrain(patches[i]); + } + + float compare(const cv::Mat &target, const cv::Mat &query) const + { + (void) target; + (void) query; + qFatal("Heatmap Distance not compatible with Template to Template comparison."); + + return 0; + } + + void compare(const TemplateList &target, const TemplateList &query, Output *output) const + { + for (int i=0; isetRelative(distances[j]->compare(target[i][j],query[i][j]), j, 0); + } + } + + void store(QDataStream &stream) const + { + stream << distances.size(); + foreach (Distance *distance, distances) + distance->store(stream); + } + + void load(QDataStream &stream) + { + int numDistances; + stream >> numDistances; + while (distances.size() < numDistances) + distances.append(make(description)); + foreach (Distance *distance, distances) + distance->load(stream); + } +}; + +BR_REGISTER(Distance, HeatMapDistance) + +} // namespace br + +#include "distance/heatmap.moc" diff --git a/openbr/plugins/distance/identical.cpp b/openbr/plugins/distance/identical.cpp index ec36f24..aae6cd6 100644 --- a/openbr/plugins/distance/identical.cpp +++ b/openbr/plugins/distance/identical.cpp @@ -28,4 +28,4 @@ BR_REGISTER(Distance, IdenticalDistance) } // namespace br -#include "identical.moc" +#include "distance/identical.moc" diff --git a/openbr/plugins/distance/keypointmatcher.cpp b/openbr/plugins/distance/keypointmatcher.cpp index f6a720e..bceb45b 100644 --- a/openbr/plugins/distance/keypointmatcher.cpp +++ b/openbr/plugins/distance/keypointmatcher.cpp @@ -55,4 +55,4 @@ BR_REGISTER(Distance, KeyPointMatcherDistance) } // namespace br -#include "keypointmatcher.moc" +#include "distance/keypointmatcher.moc" diff --git a/openbr/plugins/distance/l1.cpp b/openbr/plugins/distance/l1.cpp index 1fa2183..dae988f 100644 --- a/openbr/plugins/distance/l1.cpp +++ b/openbr/plugins/distance/l1.cpp @@ -27,4 +27,4 @@ BR_REGISTER(Distance, L1Distance) } // namespace br -#include "L1.moc" +#include "distance/L1.moc" diff --git a/openbr/plugins/distance/matchprobability.cpp b/openbr/plugins/distance/matchprobability.cpp new file mode 100644 index 0000000..8bc6f63 --- /dev/null +++ b/openbr/plugins/distance/matchprobability.cpp @@ -0,0 +1,179 @@ +#include + +#include +#include + +namespace br +{ + +float KDEPointer(const QList *scores, double x, double h) +{ + return Common::KernelDensityEstimation(*scores, x, h); +} + +/* Kernel Density Estimator */ +struct KDE +{ + float min, max; + double mean, stddev; + QList bins; + + KDE() : min(0), max(1), mean(0), stddev(1) {} + + KDE(const QList &scores, bool trainKDE) + { + Common::MinMax(scores, &min, &max); + Common::MeanStdDev(scores, &mean, &stddev); + + if (!trainKDE) + return; + + double h = Common::KernelDensityBandwidth(scores); + const int size = 255; + bins.reserve(size); + + QFutureSynchronizer futures; + + for (int i=0; i < size; i++) + futures.addFuture(QtConcurrent::run(KDEPointer, &scores, min + (max-min)*i/(size-1), h)); + futures.waitForFinished(); + + foreach(const QFuture & future, futures.futures()) + bins.append(future.result()); + } + + float operator()(float score, bool gaussian = true) const + { + if (gaussian) return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((score-mean)/stddev, 2)); + if (bins.empty()) + return -std::numeric_limits::max(); + + if (score <= min) return bins.first(); + if (score >= max) return bins.last(); + const float x = (score-min)/(max-min)*bins.size(); + const float y1 = bins[floor(x)]; + const float y2 = bins[ceil(x)]; + return y1 + (y2-y1)*(x-floor(x)); + } +}; + +QDataStream &operator<<(QDataStream &stream, const KDE &kde) +{ + return stream << kde.min << kde.max << kde.mean << kde.stddev << kde.bins; +} + +QDataStream &operator>>(QDataStream &stream, KDE &kde) +{ + return stream >> kde.min >> kde.max >> kde.mean >> kde.stddev >> kde.bins; +} + +/* Match Probability */ +struct MP +{ + KDE genuine, impostor; + MP() {} + MP(const QList &genuineScores, const QList &impostorScores, bool trainKDE) + : genuine(genuineScores, trainKDE), impostor(impostorScores, trainKDE) {} + float operator()(float score, bool gaussian = true) const + { + const float g = genuine(score, gaussian); + const float s = g / (impostor(score, gaussian) + g); + return s; + } +}; + +QDataStream &operator<<(QDataStream &stream, const MP &nmp) +{ + return stream << nmp.genuine << nmp.impostor; +} + +QDataStream &operator>>(QDataStream &stream, MP &nmp) +{ + return stream >> nmp.genuine >> nmp.impostor; +} + +/*! + * \ingroup distances + * \brief Match Probability \cite klare12 + * \author Josh Klontz \cite jklontz + */ +class MatchProbabilityDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(bool gaussian READ get_gaussian WRITE set_gaussian RESET reset_gaussian STORED false) + Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + + MP mp; + + void train(const TemplateList &src) + { + distance->train(src); + + const QList labels = src.indexProperty(inputVariable); + QScopedPointer matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); + distance->compare(src, src, matrixOutput.data()); + + QList genuineScores, impostorScores; + genuineScores.reserve(labels.size()); + impostorScores.reserve(labels.size()*labels.size()); + for (int i=0; idata.at(i, j); + if (score == -std::numeric_limits::max()) continue; + if (crossModality && src[i].file.get("MODALITY") == src[j].file.get("MODALITY")) continue; + if (labels[i] == labels[j]) genuineScores.append(score); + else impostorScores.append(score); + } + } + + mp = MP(genuineScores, impostorScores, !gaussian); + } + + float compare(const Template &target, const Template &query) const + { + return normalize(distance->compare(target, query)); + } + + float compare(const cv::Mat &target, const cv::Mat &query) const + { + return normalize(distance->compare(target, query)); + } + + float compare(const uchar *a, const uchar *b, size_t size) const + { + return normalize(distance->compare(a, b, size)); + } + + float normalize(float score) const + { + if (score == -std::numeric_limits::max()) return score; + if (!Globals->scoreNormalization) return -log(score+1); + return mp(score, gaussian); + } + + void store(QDataStream &stream) const + { + distance->store(stream); + stream << mp; + } + + void load(QDataStream &stream) + { + distance->load(stream); + stream >> mp; + } + +protected: + BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) + BR_PROPERTY(bool, gaussian, true) + BR_PROPERTY(bool, crossModality, false) + BR_PROPERTY(QString, inputVariable, "Label") +}; + +BR_REGISTER(Distance, MatchProbabilityDistance) + +} // namespace br + +#include "distance/matchprobability.moc" diff --git a/openbr/plugins/distance/metadata.cpp b/openbr/plugins/distance/metadata.cpp new file mode 100644 index 0000000..2a49568 --- /dev/null +++ b/openbr/plugins/distance/metadata.cpp @@ -0,0 +1,60 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Checks target metadata against query metadata. + * \author Scott Klum \cite sklum + */ +class MetadataDistance : public UntrainableDistance +{ + Q_OBJECT + + Q_PROPERTY(QStringList filters READ get_filters WRITE set_filters RESET reset_filters STORED false) + BR_PROPERTY(QStringList, filters, QStringList()) + + float compare(const Template &a, const Template &b) const + { + foreach (const QString &key, filters) { + QString aValue = a.file.get(key, QString()); + QString bValue = b.file.get(key, QString()); + + // The query value may be a range. Let's check. + if (bValue.isEmpty()) bValue = QtUtils::toString(b.file.get(key, QPointF())); + + if (aValue.isEmpty() || bValue.isEmpty()) continue; + + bool keep = false; + bool ok; + + QPointF range = QtUtils::toPoint(bValue,&ok); + + if (ok) /* Range */ { + int value = range.x(); + int upperBound = range.y(); + + while (value <= upperBound) { + if (aValue == QString::number(value)) { + keep = true; + break; + } + value++; + } + } + else if (aValue == bValue) keep = true; + + if (!keep) return -std::numeric_limits::max(); + } + return 0; + } +}; + + +BR_REGISTER(Distance, MetadataDistance) + +} // namespace br + +#include "distance/metadata.moc" diff --git a/openbr/plugins/distance/neglogplusone.cpp b/openbr/plugins/distance/neglogplusone.cpp index ded2dfc..3c180eb 100644 --- a/openbr/plugins/distance/neglogplusone.cpp +++ b/openbr/plugins/distance/neglogplusone.cpp @@ -39,4 +39,4 @@ BR_REGISTER(Distance, NegativeLogPlusOneDistance) } // namespace br -#include "neglogplusone.moc" +#include "distance/neglogplusone.moc" diff --git a/openbr/plugins/distance/online.cpp b/openbr/plugins/distance/online.cpp index 1880e68..19a6ab7 100644 --- a/openbr/plugins/distance/online.cpp +++ b/openbr/plugins/distance/online.cpp @@ -32,4 +32,4 @@ BR_REGISTER(Distance, OnlineDistance) } // namespace br -#include "online.moc" +#include "distance/online.moc" diff --git a/openbr/plugins/distance/pipe.cpp b/openbr/plugins/distance/pipe.cpp index a6cc9c0..de55d05 100644 --- a/openbr/plugins/distance/pipe.cpp +++ b/openbr/plugins/distance/pipe.cpp @@ -44,4 +44,4 @@ BR_REGISTER(Distance, PipeDistance) } // namespace br -#include "pipe.moc" +#include "distance/pipe.moc" diff --git a/openbr/plugins/distance/reject.cpp b/openbr/plugins/distance/reject.cpp new file mode 100644 index 0000000..e1aec36 --- /dev/null +++ b/openbr/plugins/distance/reject.cpp @@ -0,0 +1,38 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Sets distance to -FLOAT_MAX if a target template has/doesn't have a key. + * \author Scott Klum \cite sklum + */ +class RejectDistance : public UntrainableDistance +{ + Q_OBJECT + + Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) + BR_PROPERTY(QStringList, keys, QStringList()) + Q_PROPERTY(bool rejectIfContains READ get_rejectIfContains WRITE set_rejectIfContains RESET reset_rejectIfContains STORED false) + BR_PROPERTY(bool, rejectIfContains, false) + + float compare(const Template &a, const Template &b) const + { + // We don't look at the query + (void) b; + + foreach (const QString &key, keys) + if ((rejectIfContains && a.file.contains(key)) || (!rejectIfContains && !a.file.contains(key))) + return -std::numeric_limits::max(); + + return 0; + } +}; + + +BR_REGISTER(Distance, RejectDistance) + +} // namespace br + +#include "distance/reject.moc" diff --git a/openbr/plugins/distance/sum.cpp b/openbr/plugins/distance/sum.cpp index 6ab6b38..4e219c4 100644 --- a/openbr/plugins/distance/sum.cpp +++ b/openbr/plugins/distance/sum.cpp @@ -43,4 +43,4 @@ BR_REGISTER(Distance, SumDistance) } // namespace br -#include "sum.moc" +#include "distance/sum.moc" diff --git a/openbr/plugins/distance/turk.cpp b/openbr/plugins/distance/turk.cpp new file mode 100644 index 0000000..43063cb --- /dev/null +++ b/openbr/plugins/distance/turk.cpp @@ -0,0 +1,49 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Unmaps Turk HITs to be compared against query mats + * \author Scott Klum \cite sklum + */ +class TurkDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key) + Q_PROPERTY(QStringList values READ get_values WRITE set_values RESET reset_values STORED false) + BR_PROPERTY(QString, key, QString()) + BR_PROPERTY(QStringList, values, QStringList()) + + bool targetHuman; + bool queryMachine; + + void init() + { + targetHuman = Globals->property("TurkTargetHuman").toBool(); + queryMachine = Globals->property("TurkQueryMachine").toBool(); + } + + cv::Mat getValues(const Template &t) const + { + QList result; + foreach (const QString &value, values) + result.append(t.file.get(key + "_" + value)); + return OpenCVUtils::toMat(result, 1); + } + + float compare(const Template &target, const Template &query) const + { + const cv::Mat a = targetHuman ? getValues(target) : target.m(); + const cv::Mat b = queryMachine ? query.m() : getValues(query); + return -norm(a, b, cv::NORM_L1); + } +}; + +BR_REGISTER(Distance, TurkDistance) + +} // namespace br + +#include "distance/turk.moc" diff --git a/openbr/plugins/distance/unit.cpp b/openbr/plugins/distance/unit.cpp new file mode 100644 index 0000000..7919841 --- /dev/null +++ b/openbr/plugins/distance/unit.cpp @@ -0,0 +1,71 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Linear normalizes of a distance so the mean impostor score is 0 and the mean genuine score is 1. + * \author Josh Klontz \cite jklontz + */ +class UnitDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance) + Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a) + Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) + BR_PROPERTY(float, a, 1) + BR_PROPERTY(float, b, 0) + BR_PROPERTY(QString, inputVariable, "Label") + + void train(const TemplateList &templates) + { + const TemplateList samples = templates.mid(0, 2000); + const QList sampleLabels = samples.indexProperty(inputVariable); + QScopedPointer matrixOutput(MatrixOutput::make(FileList(samples.size()), FileList(samples.size()))); + Distance::compare(samples, samples, matrixOutput.data()); + + double genuineAccumulator, impostorAccumulator; + int genuineCount, impostorCount; + genuineAccumulator = impostorAccumulator = genuineCount = impostorCount = 0; + + for (int i=0; idata.at(i, j); + if (sampleLabels[i] == sampleLabels[j]) { + genuineAccumulator += val; + genuineCount++; + } else { + impostorAccumulator += val; + impostorCount++; + } + } + } + + if (genuineCount == 0) { qWarning("No genuine matches."); return; } + if (impostorCount == 0) { qWarning("No impostor matches."); return; } + + double genuineMean = genuineAccumulator / genuineCount; + double impostorMean = impostorAccumulator / impostorCount; + + if (genuineMean == impostorMean) { qWarning("Genuines and impostors are indistinguishable."); return; } + + a = 1.0/(genuineMean-impostorMean); + b = impostorMean; + + qDebug("a = %f, b = %f", a, b); + } + + float compare(const Template &target, const Template &query) const + { + return a * (distance->compare(target, query) - b); + } +}; + +BR_REGISTER(Distance, UnitDistance) + +} // namespace br + +#include "distance/unit.moc" diff --git a/openbr/plugins/distance/zscore.cpp b/openbr/plugins/distance/zscore.cpp new file mode 100644 index 0000000..bbb52e5 --- /dev/null +++ b/openbr/plugins/distance/zscore.cpp @@ -0,0 +1,68 @@ +#include +#include + +namespace br +{ + +class ZScoreDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) + BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) + BR_PROPERTY(bool, crossModality, false) + + float min, max; + double mean, stddev; + + void train(const TemplateList &src) + { + distance->train(src); + + QScopedPointer matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); + distance->compare(src, src, matrixOutput.data()); + + QList scores; + scores.reserve(src.size()*src.size()); + for (int i=0; idata.at(i, j); + if (score == -std::numeric_limits::max()) continue; + if (crossModality && src[i].file.get("MODALITY") == src[j].file.get("MODALITY")) continue; + scores.append(score); + } + } + + Common::MinMax(scores, &min, &max); + Common::MeanStdDev(scores, &mean, &stddev); + + if (stddev == 0) qFatal("Stddev is 0."); + } + + float compare(const Template &target, const Template &query) const + { + float score = distance->compare(target,query); + if (score == -std::numeric_limits::max()) score = (min - mean) / stddev; + else if (score == std::numeric_limits::max()) score = (max - mean) / stddev; + else score = (score - mean) / stddev; + return score; + } + + void store(QDataStream &stream) const + { + distance->store(stream); + stream << min << max << mean << stddev; + } + + void load(QDataStream &stream) + { + distance->load(stream); + stream >> min >> max >> mean >> stddev; + } +}; + +BR_REGISTER(Distance, ZScoreDistance) + +} // namespace br + +#include "distance/zscore.moc" diff --git a/openbr/plugins/eigen3.cmake b/openbr/plugins/eigen3.cmake deleted file mode 100644 index ed6a3e2..0000000 --- a/openbr/plugins/eigen3.cmake +++ /dev/null @@ -1,7 +0,0 @@ -set(BR_WITH_EIGEN3 ON CACHE BOOL "Build Eigen3 plugins") - -if(${BR_WITH_EIGEN3}) - find_package(Eigen3 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/eigen3.cpp) - install(FILES ${EIGEN3_LICENSE} RENAME Eigen3 DESTINATION share/openbr/licenses) -endif() diff --git a/openbr/plugins/eigen3.cpp b/openbr/plugins/eigen3.cpp deleted file mode 100644 index 97bd90f..0000000 --- a/openbr/plugins/eigen3.cpp +++ /dev/null @@ -1,657 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright 2012 The MITRE Corporation * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include - -#include "openbr_internal.h" - -#include "openbr/core/common.h" -#include "openbr/core/eigenutils.h" -#include "openbr/core/opencvutils.h" - -namespace br -{ - -/*! - * \ingroup initializers - * \brief Initialize Eigen - * http://eigen.tuxfamily.org/dox/TopicMultiThreading.html - * \author Scott Klum \cite sklum - */ -class EigenInitializer : public Initializer -{ - Q_OBJECT - - void initialize() const - { - Eigen::initParallel(); - } -}; - -BR_REGISTER(Initializer, EigenInitializer) - -/*! - * \ingroup transforms - * \brief Projects input into learned Principal Component Analysis subspace. - * \author Brendan Klare \cite bklare - * \author Josh Klontz \cite jklontz - */ -class PCATransform : public Transform -{ - Q_OBJECT - friend class DFFSTransform; - friend class LDATransform; - -protected: - Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false) - Q_PROPERTY(int drop READ get_drop WRITE set_drop RESET reset_drop STORED false) - Q_PROPERTY(bool whiten READ get_whiten WRITE set_whiten RESET reset_whiten STORED false) - - /*! - * keep < 0: All eigenvalues are retained. - * keep = 0: No PCA performed, eigenvectors form an identity matrix. - * 0 < keep < 1: Fraction of the variance to retain. - * keep >= 1: Number of leading eigenvectors to retain. - */ - BR_PROPERTY(float, keep, 0.95) - BR_PROPERTY(int, drop, 0) - BR_PROPERTY(bool, whiten, false) - - Eigen::VectorXf mean, eVals; - Eigen::MatrixXf eVecs; - - int originalRows; - -public: - PCATransform() : keep(0.95), drop(0), whiten(false) {} - -private: - double residualReconstructionError(const Template &src) const - { - Template proj; - project(src, proj); - - Eigen::Map srcMap(src.m().ptr(), src.m().rows*src.m().cols); - Eigen::Map projMap(proj.m().ptr(), keep); - - return (srcMap - mean).squaredNorm() - projMap.squaredNorm(); - } - - void train(const TemplateList &trainingSet) - { - if (trainingSet.first().m().type() != CV_32FC1) - qFatal("Requires single channel 32-bit floating point matrices."); - - originalRows = trainingSet.first().m().rows; - int dimsIn = trainingSet.first().m().rows * trainingSet.first().m().cols; - const int instances = trainingSet.size(); - - // Map into 64-bit Eigen matrix - Eigen::MatrixXd data(dimsIn, instances); - for (int i=0; i(trainingSet[i].m().ptr(), dimsIn, 1).cast(); - - trainCore(data); - } - - void project(const Template &src, Template &dst) const - { - dst = cv::Mat(1, keep, CV_32FC1); - - // Map Eigen into OpenCV - Eigen::Map inMap(src.m().ptr(), src.m().rows*src.m().cols, 1); - Eigen::Map outMap(dst.m().ptr(), keep, 1); - - // Do projection - outMap = eVecs.transpose() * (inMap - mean); - } - - void store(QDataStream &stream) const - { - stream << keep << drop << whiten << originalRows << mean << eVals << eVecs; - } - - void load(QDataStream &stream) - { - stream >> keep >> drop >> whiten >> originalRows >> mean >> eVals >> eVecs; - } - -protected: - void trainCore(Eigen::MatrixXd data) - { - int dimsIn = data.rows(); - int instances = data.cols(); - const bool dominantEigenEstimation = (dimsIn > instances); - - Eigen::MatrixXd allEVals, allEVecs; - if (keep != 0) { - // Compute and remove mean - mean = Eigen::VectorXf(dimsIn); - for (int i=0; i eSolver(cov); - allEVals = eSolver.eigenvalues(); - allEVecs = eSolver.eigenvectors(); - if (dominantEigenEstimation) allEVecs = data * allEVecs; - } else { - // Null case - mean = Eigen::VectorXf::Zero(dimsIn); - allEVecs = Eigen::MatrixXd::Identity(dimsIn, dimsIn); - allEVals = Eigen::VectorXd::Ones(dimsIn); - } - - if (keep <= 0) { - keep = dimsIn - drop; - } else if (keep < 1) { - // Keep eigenvectors that retain a certain energy percentage. - const double totalEnergy = allEVals.sum(); - if (totalEnergy == 0) { - keep = 0; - } else { - double currentEnergy = 0; - int i=0; - while ((currentEnergy / totalEnergy < keep) && (i < allEVals.rows())) { - currentEnergy += allEVals(allEVals.rows()-(i+1)); - i++; - } - keep = i - drop; - } - } else { - if (keep + drop > allEVals.rows()) { - qWarning("Insufficient samples, needed at least %d but only got %d.", (int)keep + drop, (int)allEVals.rows()); - keep = allEVals.rows() - drop; - } - } - - // Keep highest energy vectors - eVals = Eigen::VectorXf((int)keep, 1); - eVecs = Eigen::MatrixXf(allEVecs.rows(), (int)keep); - for (int i=0; i() / allEVecs.col(index).norm(); - if (whiten) eVecs.col(i) /= sqrt(eVals(i)); - } - - // Debug output - if (Globals->verbose) qDebug() << "PCA Training:\n\tDimsIn =" << dimsIn << "\n\tKeep =" << keep; - } - - void writeEigenVectors(const Eigen::MatrixXd &allEVals, const Eigen::MatrixXd &allEVecs) const - { - const int originalCols = mean.rows() / originalRows; - - { // Write out mean image - cv::Mat out(originalRows, originalCols, CV_32FC1); - Eigen::Map outMap(out.ptr(), mean.rows(), 1); - outMap = mean.col(0); - // OpenCVUtils::saveImage(out, Globals->Debug+"/PCA/eigenVectors/mean.png"); - } - - // Write out sample eigen vectors (16 highest, 8 lowest), filename = eigenvalue. - for (int k=0; k<(int)allEVals.size(); k++) { - if ((k < 8) || (k >= (int)allEVals.size()-16)) { - cv::Mat out(originalRows, originalCols, CV_64FC1); - Eigen::Map outMap(out.ptr(), mean.rows(), 1); - outMap = allEVecs.col(k); - // OpenCVUtils::saveImage(out, Globals->Debug+"/PCA/eigenVectors/"+QString::number(allEVals(k),'f',0)+".png"); - } - } - } -}; - -BR_REGISTER(Transform, PCATransform) - -/*! - * \ingroup transforms - * \brief PCA on each row. - * \author Josh Klontz \cite jklontz - */ -class RowWisePCATransform : public PCATransform -{ - Q_OBJECT - - void train(const TemplateList &trainingSet) - { - if (trainingSet.first().m().type() != CV_32FC1) - qFatal("Requires single channel 32-bit floating point matrices."); - - originalRows = trainingSet.first().m().rows; - const int dimsIn = trainingSet.first().m().cols; - int instances = 0; - foreach (const Template &t, trainingSet) - instances += t.m().rows; - - // Map into 64-bit Eigen matrix - Eigen::MatrixXd data(dimsIn, instances); - int index = 0; - foreach (const Template &t, trainingSet) - for (int i=0; i(t.m().ptr(i), dimsIn, 1).cast(); - - PCATransform::trainCore(data); - } - - void project(const Template &src, Template &dst) const - { - dst = cv::Mat(src.m().rows, keep, CV_32FC1); - - for (int i=0; i inMap(src.m().ptr(i), src.m().cols, 1); - Eigen::Map outMap(dst.m().ptr(i), keep, 1); - outMap = eVecs.transpose() * (inMap - mean); - } - } -}; - -BR_REGISTER(Transform, RowWisePCATransform) - -/*! - * \ingroup transforms - * \brief Computes Distance From Feature Space (DFFS) \cite moghaddam97. - * \author Josh Klontz \cite jklontz - */ -class DFFSTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false) - BR_PROPERTY(float, keep, 0.95) - - PCATransform pca; - Transform *cvtFloat; - - void init() - { - pca.keep = keep; - cvtFloat = make("CvtFloat"); - } - - void train(const TemplateList &data) - { - pca.train((*cvtFloat)(data)); - } - - void project(const Template &src, Template &dst) const - { - dst = src; - dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src)))); - } - - void store(QDataStream &stream) const - { - pca.store(stream); - } - - void load(QDataStream &stream) - { - pca.load(stream); - } -}; - -BR_REGISTER(Transform, DFFSTransform) - -/*! - * \ingroup transforms - * \brief Projects input into learned Linear Discriminant Analysis subspace. - * \author Brendan Klare \cite bklare - * \author Josh Klontz \cite jklontz - */ -class LDATransform : public Transform -{ - friend class SparseLDATransform; - - Q_OBJECT - Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false) - Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false) - Q_PROPERTY(int directLDA READ get_directLDA WRITE set_directLDA RESET reset_directLDA STORED false) - Q_PROPERTY(float directDrop READ get_directDrop WRITE set_directDrop RESET reset_directDrop STORED false) - Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) - Q_PROPERTY(bool isBinary READ get_isBinary WRITE set_isBinary RESET reset_isBinary STORED false) - Q_PROPERTY(bool normalize READ get_normalize WRITE set_normalize RESET reset_normalize STORED false) - BR_PROPERTY(float, pcaKeep, 0.98) - BR_PROPERTY(bool, pcaWhiten, false) - BR_PROPERTY(int, directLDA, 0) - BR_PROPERTY(float, directDrop, 0.1) - BR_PROPERTY(QString, inputVariable, "Label") - BR_PROPERTY(bool, isBinary, false) - BR_PROPERTY(bool, normalize, true) - - int dimsOut; - Eigen::VectorXf mean; - Eigen::MatrixXf projection; - float stdDev; - - void train(const TemplateList &_trainingSet) - { - // creates "Label" - TemplateList trainingSet = TemplateList::relabel(_trainingSet, inputVariable, isBinary); - int instances = trainingSet.size(); - - // Perform PCA dimensionality reduction - PCATransform pca; - pca.keep = pcaKeep; - pca.whiten = pcaWhiten; - pca.train(trainingSet); - mean = pca.mean; - - TemplateList ldaTrainingSet; - static_cast(&pca)->project(trainingSet, ldaTrainingSet); - - int dimsIn = ldaTrainingSet.first().m().rows * ldaTrainingSet.first().m().cols; - - // OpenBR ensures that class values range from 0 to numClasses-1. - // Label exists because we created it earlier with relabel - QList classes = File::get(trainingSet, "Label"); - QMap classCounts = trainingSet.countValues("Label"); - const int numClasses = classCounts.size(); - - // Map Eigen into OpenCV - Eigen::MatrixXd data = Eigen::MatrixXd(dimsIn, instances); - for (int i=0; i(ldaTrainingSet[i].m().ptr(), dimsIn, 1).cast(); - - // Removing class means - Eigen::MatrixXd classMeans = Eigen::MatrixXd::Zero(dimsIn, numClasses); - for (int i=0; i instances - numClasses) { - // Here, we are replacing the eigenvalue of the null space - // eigenvectors with the eigenvalue (divided by 2) of the - // smallest eigenvector from the row space eigenvector. - // This allows us to scale these null-space vectors (otherwise - // it is a divide by zero. - double null_eig = space1.eVals(instances - numClasses - 1) / 2; - for (int i = instances - numClasses; i < dimsIn; i++) - space1.eVals(i) = null_eig; - } - - // Drop the first few leading eigenvectors in the within-class space - QList eVal_list; eVal_list.reserve(dimsIn); - float fmax = -1; - for (int i=0; i dSum = Common::CumSum(eVal_list); - int drop_idx; - for (drop_idx = 0; drop_idx= directDrop) - break; - - drop_idx++; - space1.keep = dimsIn - drop_idx; - - Eigen::MatrixXf new_vecs = Eigen::MatrixXf(space1.eVecs.rows(), (int)space1.keep); - Eigen::MatrixXf new_vals = Eigen::MatrixXf((int)space1.keep, 1); - - for (int i = 0; i < space1.keep; i++) { - new_vecs.col(i) = space1.eVecs.col(i + drop_idx); - new_vals(i) = space1.eVals(i + drop_idx); - } - - space1.eVecs = new_vecs; - space1.eVals = new_vals; - - // We will call this "agressive" whitening. Really, it is not whitening - // anymore. Instead, we are further scaling the small eigenvalues and the - // null space eigenvalues (to increase their impact). - for (int i=0; i() * classMeans; - - // The rank of the between-class scatter matrix is bound by numClasses - 1 - // because each class is a vector used to compute the covariance, - // but one degree of freedom is lost removing the global mean. - int dim2 = std::min((int)space1.keep, numClasses-1); - PCATransform space2; - space2.keep = dim2; - space2.trainCore(data2); - - // Compute final projection matrix - projection = ((space2.eVecs.transpose() * space1.eVecs.transpose()) * pca.eVecs.transpose()).transpose(); - dimsOut = dim2; - - stdDev = 1; // default initialize - if (isBinary) { - assert(dimsOut == 1); - float posVal = 0; - float negVal = 0; - Eigen::MatrixXf results(trainingSet.size(),1); - for (int i = 0; i < trainingSet.size(); i++) { - Template t; - project(trainingSet[i],t); - //Note: the positive class is assumed to be 0 b/c it will - // typically be the first gallery template in the TemplateList structure - if (classes[i] == 0) - posVal += t.m().at(0,0); - else if (classes[i] == 1) - negVal += t.m().at(0,0); - else - qFatal("Binary mode only supports two class problems."); - results(i) = t.m().at(0,0); //used for normalization - } - posVal /= classCounts[0]; - negVal /= classCounts[1]; - - if (posVal < negVal) { - //Ensure positive value is supposed to be > 0 after projection - Eigen::MatrixXf invert = Eigen::MatrixXf::Ones(dimsIn,1); - invert *= -1; - projection = invert.transpose() * projection; - } - - if (normalize) - stdDev = sqrt(results.array().square().sum() / trainingSet.size()); - } - } - - void project(const Template &src, Template &dst) const - { - dst = cv::Mat(1, dimsOut, CV_32FC1); - - // Map Eigen into OpenCV - Eigen::Map inMap((float*)src.m().ptr(), src.m().rows*src.m().cols, 1); - Eigen::Map outMap(dst.m().ptr(), dimsOut, 1); - - // Do projection - outMap = projection.transpose() * (inMap - mean); - if (normalize && isBinary) - dst.m().at(0,0) = dst.m().at(0,0) / stdDev; - } - - void store(QDataStream &stream) const - { - stream << pcaKeep; - stream << directLDA; - stream << directDrop; - stream << dimsOut; - stream << mean; - stream << projection; - if (normalize && isBinary) - stream << stdDev; - } - - void load(QDataStream &stream) - { - stream >> pcaKeep; - stream >> directLDA; - stream >> directDrop; - stream >> dimsOut; - stream >> mean; - stream >> projection; - if (normalize && isBinary) - stream >> stdDev; - } -}; - -BR_REGISTER(Transform, LDATransform) - -/*! - * \ingroup transforms - * \brief Projects input into learned Linear Discriminant Analysis subspace - * learned on a sparse subset of features with the highest weight - * in the original LDA algorithm. - * \author Brendan Klare \cite bklare - */ -class SparseLDATransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(float varThreshold READ get_varThreshold WRITE set_varThreshold RESET reset_varThreshold STORED false) - Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false) - Q_PROPERTY(bool normalize READ get_normalize WRITE set_normalize RESET reset_normalize STORED false) - BR_PROPERTY(float, varThreshold, 1.5) - BR_PROPERTY(float, pcaKeep, 0.98) - BR_PROPERTY(bool, normalize, true) - - LDATransform ldaSparse; - int dimsOut; - QList selections; - - Eigen::VectorXf mean; - - void init() - { - ldaSparse.init(); - ldaSparse.pcaKeep = pcaKeep; - ldaSparse.inputVariable = "Label"; - ldaSparse.isBinary = true; - ldaSparse.normalize = true; - } - - void train(const TemplateList &_trainingSet) - { - - LDATransform ldaOrig; - ldaOrig.init(); - ldaOrig.inputVariable = "Label"; - ldaOrig.pcaKeep = pcaKeep; - ldaOrig.isBinary = true; - ldaOrig.normalize = true; - - ldaOrig.train(_trainingSet); - - //Only works on binary class problems for now - assert(ldaOrig.projection.cols() == 1); - float ldaStd = EigenUtils::stddev(ldaOrig.projection); - for (int i = 0; i < ldaOrig.projection.rows(); i++) - if (abs(ldaOrig.projection(i)) > varThreshold * ldaStd) - selections.append(i); - - TemplateList newSet; - for (int i = 0; i < _trainingSet.size(); i++) { - cv::Mat x(_trainingSet[i]); - cv::Mat y = cv::Mat(selections.size(), 1, CV_32FC1); - int idx = 0; - int cnt = 0; - for (int j = 0; j < x.rows; j++) - for (int k = 0; k < x.cols; k++, cnt++) - if (selections.contains(cnt)) - y.at(idx++,0) = x.at(j, k); - newSet.append(Template(_trainingSet[i].file, y)); - } - ldaSparse.train(newSet); - dimsOut = ldaSparse.dimsOut; - } - - void project(const Template &src, Template &dst) const - { - Eigen::Map inMap((float*)src.m().ptr(), src.m().rows*src.m().cols, 1); - Eigen::Map outMap(dst.m().ptr(), dimsOut, 1); - - int d = selections.size(); - cv::Mat inSelect(d,1,CV_32F); - for (int i = 0; i < d; i++) - inSelect.at(i) = src.m().at(selections[i]); - ldaSparse.project(Template(src.file, inSelect), dst); - } - - void store(QDataStream &stream) const - { - stream << pcaKeep; - stream << ldaSparse; - stream << dimsOut; - stream << selections; - } - - void load(QDataStream &stream) - { - stream >> pcaKeep; - stream >> ldaSparse; - stream >> dimsOut; - stream >> selections; - } -}; - -BR_REGISTER(Transform, SparseLDATransform) - -} // namespace br - -#include "eigen3.moc" diff --git a/openbr/plugins/format/binary.cpp b/openbr/plugins/format/binary.cpp index 75e8fe0..1a82d57 100644 --- a/openbr/plugins/format/binary.cpp +++ b/openbr/plugins/format/binary.cpp @@ -67,4 +67,4 @@ BR_REGISTER(Format, binaryFormat) } // namespace br -#include "binary.moc" +#include "format/binary.moc" diff --git a/openbr/plugins/format/csv.cpp b/openbr/plugins/format/csv.cpp index ccede30..51d0e2f 100644 --- a/openbr/plugins/format/csv.cpp +++ b/openbr/plugins/format/csv.cpp @@ -70,4 +70,4 @@ BR_REGISTER(Format, csvFormat) } // namespace br -#include "csv.moc" +#include "format/csv.moc" diff --git a/openbr/plugins/format/ebts.cpp b/openbr/plugins/format/ebts.cpp index 97046f6..4a28fca 100644 --- a/openbr/plugins/format/ebts.cpp +++ b/openbr/plugins/format/ebts.cpp @@ -196,4 +196,4 @@ BR_REGISTER(Format, ebtsFormat) } // namespace br -#include "ebts.moc" +#include "format/ebts.moc" diff --git a/openbr/plugins/format/lffs.cpp b/openbr/plugins/format/lffs.cpp index b1da86e..2fc880c 100644 --- a/openbr/plugins/format/lffs.cpp +++ b/openbr/plugins/format/lffs.cpp @@ -33,4 +33,4 @@ BR_REGISTER(Format, lffsFormat) } // namespace br -#include "lffs.moc" +#include "format/lffs.moc" diff --git a/openbr/plugins/format/lmat.cpp b/openbr/plugins/format/lmat.cpp new file mode 100644 index 0000000..7bb4e62 --- /dev/null +++ b/openbr/plugins/format/lmat.cpp @@ -0,0 +1,37 @@ +#include + +namespace br +{ + +/*! + * \ingroup formats + * \brief Likely matrix format + * + * www.liblikely.org + * \author Josh Klontz \cite jklontz + */ +class lmatFormat : public Format +{ + Q_OBJECT + + Template read() const + { + const likely_const_mat m = likely_read(qPrintable(file.name), likely_file_guess); + const Template result(likelyToOpenCVMat(m)); + likely_release_mat(m); + return result; + } + + void write(const Template &t) const + { + const likely_const_mat m = likelyFromOpenCVMat(t); + likely_write(m, qPrintable(file.name)); + likely_release_mat(m); + } +}; + +BR_REGISTER(Format, lmatFormat) + +} // namespace br + +#include "format/lmat.moc" diff --git a/openbr/plugins/format/mat.cpp b/openbr/plugins/format/mat.cpp index 6e6e411..17a821f 100644 --- a/openbr/plugins/format/mat.cpp +++ b/openbr/plugins/format/mat.cpp @@ -224,4 +224,4 @@ BR_REGISTER(Format, matFormat) } // namespace br -#include "mat.moc" +#include "format/mat.moc" diff --git a/openbr/plugins/format/mtx.cpp b/openbr/plugins/format/mtx.cpp index 0ae94af..1aa25cf 100644 --- a/openbr/plugins/format/mtx.cpp +++ b/openbr/plugins/format/mtx.cpp @@ -44,4 +44,4 @@ BR_REGISTER(Format, maskFormat) } // namespace br -#include "mtx.moc" +#include "format/mtx.moc" diff --git a/openbr/plugins/format/null.cpp b/openbr/plugins/format/null.cpp index b5cbd38..a86382c 100644 --- a/openbr/plugins/format/null.cpp +++ b/openbr/plugins/format/null.cpp @@ -27,4 +27,4 @@ BR_REGISTER(Format, nullFormat) } // namespace br -#include "null.moc" +#include "format/null.moc" diff --git a/openbr/plugins/format/post.cpp b/openbr/plugins/format/post.cpp new file mode 100644 index 0000000..92d8cd1 --- /dev/null +++ b/openbr/plugins/format/post.cpp @@ -0,0 +1,90 @@ +#include +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Handle POST requests + * \author Josh Klontz \cite jklontz + */ +class postFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t(file); + + // Read from socket + QTcpSocket *socket = new QTcpSocket(); + socket->setSocketDescriptor(file.get("socketDescriptor")); + socket->write("HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "Hello World!\r\n"); + socket->waitForBytesWritten(); + socket->waitForReadyRead(); + QByteArray data = socket->readAll(); + socket->close(); + delete socket; + + qDebug() << data; + + // Parse data + http_parser_settings settings; + settings.on_body = bodyCallback; + settings.on_headers_complete = NULL; + settings.on_header_field = NULL; + settings.on_header_value = NULL; + settings.on_message_begin = NULL; + settings.on_message_complete = NULL; + settings.on_status_complete = NULL; + settings.on_url = NULL; + + { + QByteArray body; + http_parser parser; + http_parser_init(&parser, HTTP_REQUEST); + parser.data = &body; + http_parser_execute(&parser, &settings, data.data(), data.size()); + data = body; + } + + data.prepend("HTTP/1.1 200 OK"); + QByteArray body; + { // Image data is two layers deep + http_parser parser; + http_parser_init(&parser, HTTP_BOTH); + parser.data = &body; + http_parser_execute(&parser, &settings, data.data(), data.size()); + } + + t.append(imdecode(Mat(1, body.size(), CV_8UC1, body.data()), 1)); + return t; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported!"); + } + + static int bodyCallback(http_parser *parser, const char *at, size_t length) + { + QByteArray *byteArray = (QByteArray*)parser->data; + *byteArray = QByteArray(at, length); + return 0; + } +}; + +BR_REGISTER(Format, postFormat) + +} // namespace br + +#include "format/post.moc" diff --git a/openbr/plugins/format/raw.cpp b/openbr/plugins/format/raw.cpp index 3cf868f..431c6c1 100644 --- a/openbr/plugins/format/raw.cpp +++ b/openbr/plugins/format/raw.cpp @@ -67,4 +67,4 @@ BR_REGISTER(Format, rawFormat) } // namespace br -#include "raw.moc" +#include "format/raw.moc" diff --git a/openbr/plugins/format/scores.cpp b/openbr/plugins/format/scores.cpp index 453dffa..495ad2b 100644 --- a/openbr/plugins/format/scores.cpp +++ b/openbr/plugins/format/scores.cpp @@ -62,4 +62,4 @@ BR_REGISTER(Format, scoresFormat) } // namespace br -#include "scores.moc" +#include "format/scores.moc" diff --git a/openbr/plugins/format/url.cpp b/openbr/plugins/format/url.cpp new file mode 100644 index 0000000..54f60a0 --- /dev/null +++ b/openbr/plugins/format/url.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads image files from the web. + * \author Josh Klontz \cite jklontz + */ +class urlFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t; + + QNetworkAccessManager networkAccessManager; + QNetworkRequest request(QString(file.name).remove(".url")); + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + QNetworkReply *reply = networkAccessManager.get(request); + + while (!reply->isFinished()) QCoreApplication::processEvents(); + if (reply->error()) qWarning("%s (%s)", qPrintable(reply->errorString()), qPrintable(QString::number(reply->error()))); + + QByteArray data = reply->readAll(); + delete reply; + + Mat m = imdecode(Mat(1, data.size(), CV_8UC1, data.data()), 1); + if (m.data) t.append(m); + + return t; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported."); + } +}; + +BR_REGISTER(Format, urlFormat) + +} // namespace br + +#include "format/url.moc" diff --git a/openbr/plugins/format/video.cpp b/openbr/plugins/format/video.cpp index 0a1eaf8..78ed5c8 100644 --- a/openbr/plugins/format/video.cpp +++ b/openbr/plugins/format/video.cpp @@ -144,4 +144,4 @@ BR_REGISTER(Format, DefaultFormat) } // namespace br -#include "video.moc" +#include "format/video.moc" diff --git a/openbr/plugins/format/xml.cpp b/openbr/plugins/format/xml.cpp index 8cf3dc2..c9ca39f 100644 --- a/openbr/plugins/format/xml.cpp +++ b/openbr/plugins/format/xml.cpp @@ -99,4 +99,4 @@ BR_REGISTER(Format, xmlFormat) } // namespace br -#include "xml.moc" +#include "format/xml.moc" diff --git a/openbr/plugins/gallery.cpp b/openbr/plugins/gallery.cpp deleted file mode 100644 index 5036aec..0000000 --- a/openbr/plugins/gallery.cpp +++ /dev/null @@ -1,2075 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright 2012 The MITRE Corporation * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include -#include -#ifndef BR_EMBEDDED -#include -#include -#include -#include -#include -#include -#include -#endif // BR_EMBEDDED -#include -#include "openbr_internal.h" - -#include "openbr/universal_template.h" -#include "openbr/core/bee.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/qtutils.h" - -#include - -#ifdef CVMATIO -#include "MatlabIO.hpp" -#include "MatlabIOContainer.hpp" -#endif - -#ifdef _WIN32 -#include -#include -#endif // _WIN32 - -namespace br -{ - -/*! - * \ingroup galleries - * \brief Weka ARFF file format. - * \author Josh Klontz \cite jklontz - * http://weka.wikispaces.com/ARFF+%28stable+version%29 - */ -class arffGallery : public Gallery -{ - Q_OBJECT - QFile arffFile; - - TemplateList readBlock(bool *done) - { - (void) done; - qFatal("Not implemented."); - return TemplateList(); - } - - void write(const Template &t) - { - if (!arffFile.isOpen()) { - arffFile.setFileName(file.name); - arffFile.open(QFile::WriteOnly); - arffFile.write("% OpenBR templates\n" - "@RELATION OpenBR\n" - "\n"); - - const int dimensions = t.m().rows * t.m().cols; - for (int i=0; i("Label") + "'\n")); - } - - void init() - { - // - } -}; - -BR_REGISTER(Gallery, arffGallery) - -class BinaryGallery : public Gallery -{ - Q_OBJECT - - void init() - { - const QString baseName = file.baseName(); - - if (baseName == "stdin") { -#ifdef _WIN32 - if(_setmode(_fileno(stdin), _O_BINARY) == -1) - qFatal("Failed to set stdin to binary mode!"); -#endif // _WIN32 - - gallery.open(stdin, QFile::ReadOnly); - } else if (baseName == "stdout") { -#ifdef _WIN32 - if(_setmode(_fileno(stdout), _O_BINARY) == -1) - qFatal("Failed to set stdout to binary mode!"); -#endif // _WIN32 - - gallery.open(stdout, QFile::WriteOnly); - } else if (baseName == "stderr") { -#ifdef _WIN32 - if(_setmode(_fileno(stderr), _O_BINARY) == -1) - qFatal("Failed to set stderr to binary mode!"); -#endif // _WIN32 - - gallery.open(stderr, QFile::WriteOnly); - } else { - // Defer opening the file, in the general case we don't know if we - // need read or write mode yet - return; - } - stream.setDevice(&gallery); - } - - void readOpen() - { - if (!gallery.isOpen()) { - gallery.setFileName(file); - if (!gallery.exists()) - qFatal("File %s does not exist", qPrintable(gallery.fileName())); - - QFile::OpenMode mode = QFile::ReadOnly; - if (!gallery.open(mode)) - qFatal("Can't open gallery: %s for reading", qPrintable(gallery.fileName())); - stream.setDevice(&gallery); - } - } - - void writeOpen() - { - if (!gallery.isOpen()) { - gallery.setFileName(file); - - // Do we remove the pre-existing gallery? - if (file.get("remove")) - gallery.remove(); - QtUtils::touchDir(gallery); - QFile::OpenMode mode = QFile::WriteOnly; - - // Do we append? - if (file.get("append")) - mode |= QFile::Append; - - if (!gallery.open(mode)) - qFatal("Can't open gallery: %s for writing", qPrintable(gallery.fileName())); - stream.setDevice(&gallery); - } - } - - TemplateList readBlock(bool *done) - { - readOpen(); - if (gallery.atEnd()) - gallery.seek(0); - - TemplateList templates; - while ((templates.size() < readBlockSize) && !gallery.atEnd()) { - const Template t = readTemplate(); - if (!t.isEmpty() || !t.file.isNull()) { - templates.append(t); - templates.last().file.set("progress", position()); - } - - // Special case for pipes where we want to process data as soon as it is available - if (gallery.isSequential()) - break; - } - - *done = gallery.atEnd(); - return templates; - } - - void write(const Template &t) - { - writeOpen(); - writeTemplate(t); - if (gallery.isSequential()) - gallery.flush(); - } - -protected: - QFile gallery; - QDataStream stream; - - qint64 totalSize() - { - readOpen(); - return gallery.size(); - } - - qint64 position() - { - return gallery.pos(); - } - - virtual Template readTemplate() = 0; - virtual void writeTemplate(const Template &t) = 0; -}; - -/*! - * \ingroup galleries - * \brief A binary gallery. - * - * Designed to be a literal translation of templates to disk. - * Compatible with TemplateList::fromBuffer. - * \author Josh Klontz \cite jklontz - */ -class galGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - Template t; - stream >> t; - return t; - } - - void writeTemplate(const Template &t) - { - if (t.isEmpty() && t.file.isNull()) - return; - else if (t.file.fte) - stream << Template(t.file); // only write metadata for failure to enroll - else - stream << t; - } -}; - -BR_REGISTER(Gallery, galGallery) - -/*! - * \ingroup galleries - * \brief A contiguous array of br_universal_template. - * \author Josh Klontz \cite jklontz - */ -class utGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - Template t; - br_universal_template ut; - if (gallery.read((char*)&ut, sizeof(br_universal_template)) == sizeof(br_universal_template)) { - QByteArray data(ut.urlSize + ut.fvSize, Qt::Uninitialized); - char *dst = data.data(); - qint64 bytesNeeded = ut.urlSize + ut.fvSize; - while (bytesNeeded > 0) { - qint64 bytesRead = gallery.read(dst, bytesNeeded); - if (bytesRead <= 0) { - qDebug() << gallery.errorString(); - qFatal("Unexepected EOF while reading universal template data, needed: %d more of: %d bytes.", int(bytesNeeded), int(ut.urlSize + ut.fvSize)); - } - bytesNeeded -= bytesRead; - dst += bytesRead; - } - - t.file.set("ImageID", QVariant(QByteArray((const char*)ut.imageID, 16).toHex())); - t.file.set("AlgorithmID", ut.algorithmID); - t.file.set("URL", QString(data.data())); - char *dataStart = data.data() + ut.urlSize; - uint32_t dataSize = ut.fvSize; - if ((ut.algorithmID <= -1) && (ut.algorithmID >= -3)) { - t.file.set("FrontalFace", QRectF(ut.x, ut.y, ut.width, ut.height)); - uint32_t *rightEyeX = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - uint32_t *rightEyeY = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - uint32_t *leftEyeX = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - uint32_t *leftEyeY = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - dataSize -= sizeof(uint32_t)*4; - t.file.set("First_Eye", QPointF(*rightEyeX, *rightEyeY)); - t.file.set("Second_Eye", QPointF(*leftEyeX, *leftEyeY)); - } else { - t.file.set("X", ut.x); - t.file.set("Y", ut.y); - t.file.set("Width", ut.width); - t.file.set("Height", ut.height); - } - t.file.set("Label", ut.label); - t.append(cv::Mat(1, dataSize, CV_8UC1, dataStart).clone() /* We don't want a shallow copy! */); - } else { - if (!gallery.atEnd()) - qFatal("Failed to read universal template header!"); - } - return t; - } - - void writeTemplate(const Template &t) - { - const QByteArray imageID = QByteArray::fromHex(t.file.get("ImageID", QByteArray(32, '0'))); - if (imageID.size() != 16) - qFatal("Expected 16-byte ImageID, got: %d bytes.", imageID.size()); - - const int32_t algorithmID = (t.isEmpty() || t.file.fte) ? 0 : t.file.get("AlgorithmID"); - const QByteArray url = t.file.get("URL", t.file.name).toLatin1(); - - uint32_t x = 0, y = 0, width = 0, height = 0; - QByteArray header; - if ((algorithmID <= -1) && (algorithmID >= -3)) { - const QRectF frontalFace = t.file.get("FrontalFace"); - x = frontalFace.x(); - y = frontalFace.y(); - width = frontalFace.width(); - height = frontalFace.height(); - - const QPointF firstEye = t.file.get("First_Eye"); - const QPointF secondEye = t.file.get("Second_Eye"); - const uint32_t rightEyeX = firstEye.x(); - const uint32_t rightEyeY = firstEye.y(); - const uint32_t leftEyeX = secondEye.x(); - const uint32_t leftEyeY = secondEye.y(); - - header.append((const char*)&rightEyeX, sizeof(uint32_t)); - header.append((const char*)&rightEyeY, sizeof(uint32_t)); - header.append((const char*)&leftEyeX , sizeof(uint32_t)); - header.append((const char*)&leftEyeY , sizeof(uint32_t)); - } else { - x = t.file.get("X", 0); - y = t.file.get("Y", 0); - width = t.file.get("Width", 0); - height = t.file.get("Height", 0); - } - const uint32_t label = t.file.get("Label", 0); - - gallery.write(imageID); - gallery.write((const char*) &algorithmID, sizeof(int32_t)); - gallery.write((const char*) &x , sizeof(uint32_t)); - gallery.write((const char*) &y , sizeof(uint32_t)); - gallery.write((const char*) &width , sizeof(uint32_t)); - gallery.write((const char*) &height , sizeof(uint32_t)); - gallery.write((const char*) &label , sizeof(uint32_t)); - - const uint32_t urlSize = url.size() + 1; - gallery.write((const char*) &urlSize, sizeof(uint32_t)); - - const uint32_t signatureSize = (algorithmID == 0) ? 0 : t.m().rows * t.m().cols * t.m().elemSize(); - const uint32_t fvSize = header.size() + signatureSize; - gallery.write((const char*) &fvSize, sizeof(uint32_t)); - - gallery.write((const char*) url.data(), urlSize); - if (algorithmID != 0) { - gallery.write(header); - gallery.write((const char*) t.m().data, signatureSize); - } - } -}; - -BR_REGISTER(Gallery, utGallery) - -/*! - * \ingroup galleries - * \brief Newline-separated URLs. - * \author Josh Klontz \cite jklontz - */ -class urlGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - Template t; - const QString url = QString::fromLocal8Bit(gallery.readLine()).simplified(); - if (!url.isEmpty()) - t.file.set("URL", url); - return t; - } - - void writeTemplate(const Template &t) - { - const QString url = t.file.get("URL", t.file.name); - if (!url.isEmpty()) { - gallery.write(qPrintable(url)); - gallery.write("\n"); - } - } -}; - -BR_REGISTER(Gallery, urlGallery) - -/*! - * \ingroup galleries - * \brief Newline-separated JSON objects. - * \author Josh Klontz \cite jklontz - */ -class jsonGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - QJsonParseError error; - const QByteArray line = gallery.readLine().simplified(); - if (line.isEmpty()) - return Template(); - File file = QJsonDocument::fromJson(line, &error).object().toVariantMap(); - if (error.error != QJsonParseError::NoError) { - qWarning("Couldn't parse: %s\n", line.data()); - qFatal("%s\n", qPrintable(error.errorString())); - } - return file; - } - - void writeTemplate(const Template &t) - { - const QByteArray json = QJsonDocument(QJsonObject::fromVariantMap(t.file.localMetadata())).toJson().replace('\n', ""); - if (!json.isEmpty()) { - gallery.write(json); - gallery.write("\n"); - } - } -}; - -BR_REGISTER(Gallery, jsonGallery) - -/*! - * \ingroup galleries - * \brief Reads/writes templates to/from folders. - * \author Josh Klontz \cite jklontz - * \param regexp An optional regular expression to match against the files extension. - */ -class EmptyGallery : public Gallery -{ - Q_OBJECT - Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) - BR_PROPERTY(QString, regexp, QString()) - - qint64 gallerySize; - - void init() - { - QDir dir(file.name); - QtUtils::touchDir(dir); - gallerySize = dir.count(); - } - - TemplateList readBlock(bool *done) - { - TemplateList templates; - *done = true; - - // Enrolling a null file is used as an idiom to initialize an algorithm - if (file.isNull()) return templates; - - // Add immediate subfolders - QDir dir(file); - QList< QFuture > futures; - foreach (const QString &folder, QtUtils::naturalSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) { - const QDir subdir = dir.absoluteFilePath(folder); - futures.append(QtConcurrent::run(&EmptyGallery::getTemplates, subdir)); - } - foreach (const QFuture &future, futures) - templates.append(future.result()); - - // Add root folder - foreach (const QString &fileName, QtUtils::getFiles(file.name, false)) - templates.append(File(fileName, dir.dirName())); - - if (!regexp.isEmpty()) { - QRegExp re(regexp); - re.setPatternSyntax(QRegExp::Wildcard); - for (int i=templates.size()-1; i>=0; i--) { - if (!re.exactMatch(templates[i].file.fileName())) { - templates.removeAt(i); - } - } - } - - for (int i = 0; i < templates.size(); i++) templates[i].file.set("progress", i); - - return templates; - } - - void write(const Template &t) - { - static QMutex diskLock; - - // Enrolling a null file is used as an idiom to initialize an algorithm - if (file.name.isEmpty()) return; - - const QString newFormat = file.get("newFormat",QString()); - QString destination = file.name + "/" + (file.getBool("preservePath") ? t.file.path()+"/" : QString()); - destination += (newFormat.isEmpty() ? t.file.fileName() : t.file.baseName()+newFormat); - - QMutexLocker diskLocker(&diskLock); // Windows prefers to crash when writing to disk in parallel - if (t.isNull()) { - QtUtils::copyFile(t.file.resolved(), destination); - } else { - QScopedPointer format(Factory::make(destination)); - format->write(t); - } - } - - qint64 totalSize() - { - return gallerySize; - } - - static TemplateList getTemplates(const QDir &dir) - { - const QStringList files = QtUtils::getFiles(dir, true); - TemplateList templates; templates.reserve(files.size()); - foreach (const QString &file, files) - templates.append(File(file, dir.dirName())); - return templates; - } -}; - -BR_REGISTER(Gallery, EmptyGallery) - -/*! - * \ingroup galleries - * \brief Crawl a root location for image files. - * \author Josh Klontz \cite jklontz - */ -class crawlGallery : public Gallery -{ - Q_OBJECT - Q_PROPERTY(bool autoRoot READ get_autoRoot WRITE set_autoRoot RESET reset_autoRoot STORED false) - Q_PROPERTY(int depth READ get_depth WRITE set_depth RESET reset_depth STORED false) - Q_PROPERTY(bool depthFirst READ get_depthFirst WRITE set_depthFirst RESET reset_depthFirst STORED false) - Q_PROPERTY(int images READ get_images WRITE set_images RESET reset_images STORED false) - Q_PROPERTY(bool json READ get_json WRITE set_json RESET reset_json STORED false) - Q_PROPERTY(int timeLimit READ get_timeLimit WRITE set_timeLimit RESET reset_timeLimit STORED false) - BR_PROPERTY(bool, autoRoot, false) - BR_PROPERTY(int, depth, INT_MAX) - BR_PROPERTY(bool, depthFirst, false) - BR_PROPERTY(int, images, INT_MAX) - BR_PROPERTY(bool, json, false) - BR_PROPERTY(int, timeLimit, INT_MAX) - - QTime elapsed; - TemplateList templates; - - void crawl(QFileInfo url, int currentDepth = 0) - { - if ((templates.size() >= images) || (currentDepth >= depth) || (elapsed.elapsed()/1000 >= timeLimit)) - return; - - if (url.filePath().startsWith("file://")) - url = QFileInfo(url.filePath().mid(7)); - - if (url.isDir()) { - const QDir dir(url.absoluteFilePath()); - const QFileInfoList files = dir.entryInfoList(QDir::Files); - const QFileInfoList subdirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QFileInfo &first, depthFirst ? subdirs : files) - crawl(first, currentDepth + 1); - foreach (const QFileInfo &second, depthFirst ? files : subdirs) - crawl(second, currentDepth + 1); - } else if (url.isFile()) { - const QString suffix = url.suffix(); - if ((suffix == "bmp") || (suffix == "jpg") || (suffix == "jpeg") || (suffix == "png") || (suffix == "tiff")) { - File f; - if (json) f.set("URL", "file://"+url.canonicalFilePath()); - else f.name = "file://"+url.canonicalFilePath(); - templates.append(f); - } - } - } - - void init() - { - elapsed.start(); - const QString root = file.name.mid(0, file.name.size()-6); // Remove .crawl suffix"; - if (!root.isEmpty()) { - crawl(root); - } else { - if (autoRoot) { - foreach (const QString &path, QStandardPaths::standardLocations(QStandardPaths::HomeLocation)) - crawl(path); - } else { - QFile file; - file.open(stdin, QFile::ReadOnly); - while (!file.atEnd()) { - const QString url = QString::fromLocal8Bit(file.readLine()).simplified(); - if (!url.isEmpty()) - crawl(url); - } - } - } - } - - TemplateList readBlock(bool *done) - { - *done = true; - return templates; - } - - void write(const Template &) - { - qFatal("Not supported"); - } -}; - -BR_REGISTER(Gallery, crawlGallery) - -/*! - * \ingroup galleries - * \brief Treats the gallery as a br::Format. - * \author Josh Klontz \cite jklontz - */ -class DefaultGallery : public Gallery -{ - Q_OBJECT - - TemplateList readBlock(bool *done) - { - *done = true; - return TemplateList() << file; - } - - void write(const Template &t) - { - QScopedPointer format(Factory::make(file)); - format->write(t); - } - - qint64 totalSize() - { - return 1; - } -}; - -BR_REGISTER(Gallery, DefaultGallery) - -/*! - * \ingroup galleries - * \brief Combine all templates into one large matrix and process it as a br::Format - * \author Josh Klontz \cite jklontz - */ -class matrixGallery : public Gallery -{ - Q_OBJECT - Q_PROPERTY(const QString extension READ get_extension WRITE set_extension RESET reset_extension STORED false) - BR_PROPERTY(QString, extension, "mtx") - - TemplateList templates; - - ~matrixGallery() - { - if (templates.isEmpty()) - return; - - QScopedPointer format(Factory::make(getFormat())); - format->write(Template(file, OpenCVUtils::toMat(templates.data()))); - } - - File getFormat() const - { - return file.name.left(file.name.size() - file.suffix().size()) + extension; - } - - TemplateList readBlock(bool *done) - { - *done = true; - return TemplateList() << getFormat(); - } - - void write(const Template &t) - { - templates.append(t); - } -}; - -BR_REGISTER(Gallery, matrixGallery) - -/*! - * \ingroup initializers - * \brief Initialization support for memGallery. - * \author Josh Klontz \cite jklontz - */ -class MemoryGalleries : public Initializer -{ - Q_OBJECT - - void initialize() const {} - - void finalize() const - { - galleries.clear(); - } - -public: - static QHash galleries; /*!< TODO */ -}; - -QHash MemoryGalleries::galleries; - -BR_REGISTER(Initializer, MemoryGalleries) - -/*! - * \ingroup galleries - * \brief A gallery held in memory. - * \author Josh Klontz \cite jklontz - */ -class memGallery : public Gallery -{ - Q_OBJECT - int block; - qint64 gallerySize; - - void init() - { - block = 0; - File galleryFile = file.name.mid(0, file.name.size()-4); - if ((galleryFile.suffix() == "gal") && galleryFile.exists() && !MemoryGalleries::galleries.contains(file)) { - QSharedPointer gallery(Factory::make(galleryFile)); - MemoryGalleries::galleries[file] = gallery->read(); - gallerySize = MemoryGalleries::galleries[file].size(); - } - } - - TemplateList readBlock(bool *done) - { - TemplateList templates = MemoryGalleries::galleries[file].mid(block*readBlockSize, readBlockSize); - for (qint64 i = 0; i < templates.size();i++) { - templates[i].file.set("progress", i + block * readBlockSize); - } - - *done = (templates.size() < readBlockSize); - block = *done ? 0 : block+1; - return templates; - } - - void write(const Template &t) - { - MemoryGalleries::galleries[file].append(t); - } - - qint64 totalSize() - { - return gallerySize; - } - - qint64 position() - { - return block * readBlockSize; - } - -}; - -BR_REGISTER(Gallery, memGallery) - -FileList FileList::fromGallery(const File &rFile, bool cache) -{ - File file = rFile; - file.remove("append"); - - File targetMeta = file; - targetMeta.name = targetMeta.path() + targetMeta.baseName() + "_meta" + targetMeta.hash() + ".mem"; - - FileList fileData; - - // Did we already read the data? - if (MemoryGalleries::galleries.contains(targetMeta)) - { - return MemoryGalleries::galleries[targetMeta].files(); - } - - TemplateList templates; - // OK we read the data in some form, does the gallery type containing matrices? - if ((QStringList() << "gal" << "mem" << "template" << "ut").contains(file.suffix())) { - // Retrieve it block by block, dropping matrices from read templates. - QScopedPointer gallery(Gallery::make(file)); - gallery->set_readBlockSize(10); - bool done = false; - while (!done) - { - TemplateList tList = gallery->readBlock(&done); - for (int i=0; i < tList.size();i++) - { - tList[i].clear(); - templates.append(tList[i].file); - } - } - } - else { - // this is a gallery format that doesn't include matrices, so we can just read it - QScopedPointer gallery(Gallery::make(file)); - templates= gallery->read(); - } - - if (cache) - { - QScopedPointer memOutput(Gallery::make(targetMeta)); - memOutput->writeBlock(templates); - } - fileData = templates.files(); - return fileData; -} - -/*! - * \ingroup galleries - * \brief Treats each line as a file. - * \author Josh Klontz \cite jklontz - * - * Columns should be comma separated with first row containing headers. - * The first column in the file should be the path to the file to enroll. - * Other columns will be treated as file metadata. - * - * \see txtGallery - */ -class csvGallery : public FileGallery -{ - Q_OBJECT - Q_PROPERTY(int fileIndex READ get_fileIndex WRITE set_fileIndex RESET reset_fileIndex) - BR_PROPERTY(int, fileIndex, 0) - - FileList files; - QStringList headers; - - ~csvGallery() - { - f.close(); - - if (files.isEmpty()) return; - - QMap samples; - foreach (const File &file, files) - foreach (const QString &key, file.localKeys()) - if (!samples.contains(key)) - samples.insert(key, file.value(key)); - - // Don't create columns in the CSV for these special fields - samples.remove("Points"); - samples.remove("Rects"); - - QStringList lines; - lines.reserve(files.size()+1); - - QMap columnCounts; - - { // Make header - QStringList words; - words.append("File"); - foreach (const QString &key, samples.keys()) { - int count = 0; - words.append(getCSVElement(key, samples[key], true, count)); - columnCounts.insert(key, count); - } - lines.append(words.join(",")); - } - - // Make table - foreach (const File &file, files) { - QStringList words; - words.append(file.name); - foreach (const QString &key, samples.keys()) { - int count = columnCounts[key]; - words.append(getCSVElement(key, file.value(key), false, count)); - } - lines.append(words.join(",")); - } - - QtUtils::writeFile(file, lines); - } - - TemplateList readBlock(bool *done) - { - readOpen(); - *done = false; - TemplateList templates; - if (!file.exists()) { - *done = true; - return templates; - } - QRegExp regexp("\\s*,\\s*"); - - if (f.pos() == 0) - { - // read a line - QByteArray lineBytes = f.readLine(); - QString line = QString::fromLocal8Bit(lineBytes).trimmed(); - headers = line.split(regexp); - } - - for (qint64 i = 0; i < this->readBlockSize && !f.atEnd(); i++){ - QByteArray lineBytes = f.readLine(); - QString line = QString::fromLocal8Bit(lineBytes).trimmed(); - - QStringList words = line.split(regexp); - if (words.size() != headers.size()) continue; - File fi; - for (int j=0; j()) { - if (header) return key; - else { - if (columnCount != 1) - qFatal("Inconsistent datatype for key %s, csv file cannot be generated", qPrintable(key)); - return value.value(); - } - } else if (value.canConvert()) { - const QPointF point = value.value(); - if (header) { - columnCount = 2; - return key+"_X,"+key+"_Y"; - } - else { - if (columnCount != 2) - qFatal("Inconsistent datatype for key %s, csv file cannot be generated", qPrintable(key)); - - return QString::number(point.x())+","+QString::number(point.y()); - } - } else if (value.canConvert()) { - const QRectF rect = value.value(); - if (header) { - columnCount = 4; - return key+"_X,"+key+"_Y,"+key+"_Width,"+key+"_Height"; - } - else { - if (columnCount != 4) - qFatal("Inconsistent datatype for key %s, csv file cannot be generated", qPrintable(key)); - - return QString::number(rect.x())+","+QString::number(rect.y())+","+QString::number(rect.width())+","+QString::number(rect.height()); - } - } else { - if (header) return key; - else { - QString output = QString::number(std::numeric_limits::quiet_NaN()); - for (int i = 1; i < columnCount; i++) - output += "," + QString::number(std::numeric_limits::quiet_NaN()); - return output; - } - } - } -}; - -BR_REGISTER(Gallery, csvGallery) - -/*! - * \ingroup galleries - * \brief Treats each line as a file. - * \author Josh Klontz \cite jklontz - * - * The entire line is treated as the file path. An optional label may be specified using a space ' ' separator: - * -\verbatim - - -... - -\endverbatim - * or -\verbatim -