From b34029cc89b22994e6464b16479488ee7a775525 Mon Sep 17 00:00:00 2001 From: Jordan Cheney Date: Fri, 13 Feb 2015 16:51:49 -0500 Subject: [PATCH] Incremental progress for plugins reorg --- openbr/plugins/algorithms.cpp | 111 --------------------------------------------------------------------------------------------------------------- openbr/plugins/cascade.cpp | 440 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/classification/ebif.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/cluster.cpp | 298 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/cluster/collectnn.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/cluster/kmeans.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/cluster/knn.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/cluster/lognn.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/cluster/randomcentroids.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/algorithms.cpp | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/downsample.cpp | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/independent.cpp | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/core/singleton.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/crop.cpp | 299 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/cvt.cpp | 292 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/denoising.cpp | 50 -------------------------------------------------- openbr/plugins/detection/cascade.cpp | 440 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/detection/eyes.cpp | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/detection/keypointdetector.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance.cpp | 512 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/distance/L2.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/distance/attribute.cpp | 34 ++++++++++++++++++++++++++++++++++ openbr/plugins/distance/byteL1.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/distance/default.cpp | 31 +++++++++++++++++++++++++++++++ openbr/plugins/distance/dist.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/fuse.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/gallerycompare.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/halfbyteL1.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/distance/identical.cpp | 31 +++++++++++++++++++++++++++++++ openbr/plugins/distance/keypointmatcher.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/l1.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/distance/neglogplusone.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/online.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/distance/pipe.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/distance/sum.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/draw.cpp | 700 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/ebif.cpp | 131 ----------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/eigen3.cpp | 40 ---------------------------------------- openbr/plugins/eyes.cpp | 197 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/fill.cpp | 126 ------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/filter.cpp | 258 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/format.cpp | 975 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/format/binary.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/csv.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/ebts.cpp | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/lffs.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/format/mat.cpp | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/mtx.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/null.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/format/raw.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/scores.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/video.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/format/xml.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/frames.cpp | 88 ---------------------------------------------------------------------------------------- openbr/plugins/gui.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/adjacentoverlay.cpp | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/draw.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/drawgridlines.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/drawopticalflow.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/drawpropertiespoint.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/drawpropertypoint.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/gui/drawsegmentation.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/hist.cpp | 195 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/imgproc/abs.cpp | 25 +++++++++++++++++++++++++ openbr/plugins/imgproc/blend.cpp | 30 ++++++++++++++++++++++++++++++ openbr/plugins/imgproc/blur.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/contrasteq.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/crop.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/cropblack.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/cropsquare.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/imgproc/csdn.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/cvtcolor.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/cvtfloat.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/cvtuchar.cpp | 26 ++++++++++++++++++++++++++ openbr/plugins/imgproc/denoising.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/discardalpha.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/div.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/imgproc/dog.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/ensurechannels.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/flood.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/gamma.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/gradient.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/hist.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/histbin.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/inpaint.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/integral.cpp | 27 +++++++++++++++++++++++++++ openbr/plugins/imgproc/integralhist.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/integralsampler.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/limitsize.cpp | 37 +++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/madd.cpp | 29 +++++++++++++++++++++++++++++ openbr/plugins/imgproc/mean.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/meanfill.cpp | 28 ++++++++++++++++++++++++++++ openbr/plugins/imgproc/pow.cpp | 32 ++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rank.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/recursiveintegralsampler.cpp | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/resize.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/rg.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/roi.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/roifrompoints.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/scale.cpp | 32 ++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/splitchannels.cpp | 32 ++++++++++++++++++++++++++++++++ openbr/plugins/imgproc/subdivide.cpp | 33 +++++++++++++++++++++++++++++++++ openbr/plugins/independent.cpp | 390 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/integral.cpp | 374 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- openbr/plugins/io/write.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/keypoint.cpp | 258 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ openbr/plugins/landmarks.cpp | 24 ++++++++++++++++++++++++ openbr/plugins/lbp.cpp | 70 ---------------------------------------------------------------------- openbr/plugins/metadata/grid.cpp | 36 ++++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/keytorect.cpp | 35 +++++++++++++++++++++++++++++++++++ openbr/plugins/metadata/procrustes.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/motion.cpp | 42 ------------------------------------------ openbr/plugins/plugins.cmake | 22 +++++++++++++++++++++- openbr/plugins/quantize.cpp | 50 -------------------------------------------------- openbr/plugins/random.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++------------------- openbr/plugins/regions.cpp | 11 ++++++++--- openbr/plugins/representation/hogdescriptor.cpp | 39 +++++++++++++++++++++++++++++++++++++++ openbr/plugins/representation/keypointdescriptor.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/representation/siftdescriptor.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/sentence.cpp | 93 --------------------------------------------------------------------------------------------- openbr/plugins/stasm4.cmake | 2 +- openbr/plugins/video/aggregate.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbr/plugins/video/drop.cpp | 35 +++++++++++++++++++++++++++++++++++ 123 files changed, 6450 insertions(+), 6013 deletions(-) delete mode 100644 openbr/plugins/algorithms.cpp delete mode 100644 openbr/plugins/cascade.cpp create mode 100644 openbr/plugins/classification/ebif.cpp delete mode 100644 openbr/plugins/cluster.cpp create mode 100644 openbr/plugins/cluster/collectnn.cpp create mode 100644 openbr/plugins/cluster/kmeans.cpp create mode 100644 openbr/plugins/cluster/knn.cpp create mode 100644 openbr/plugins/cluster/lognn.cpp create mode 100644 openbr/plugins/cluster/randomcentroids.cpp create mode 100644 openbr/plugins/core/algorithms.cpp create mode 100644 openbr/plugins/core/downsample.cpp create mode 100644 openbr/plugins/core/independent.cpp create mode 100644 openbr/plugins/core/singleton.cpp delete mode 100644 openbr/plugins/crop.cpp delete mode 100644 openbr/plugins/cvt.cpp delete mode 100644 openbr/plugins/denoising.cpp create mode 100644 openbr/plugins/detection/cascade.cpp create mode 100644 openbr/plugins/detection/eyes.cpp create mode 100644 openbr/plugins/detection/keypointdetector.cpp delete mode 100644 openbr/plugins/distance.cpp create mode 100644 openbr/plugins/distance/L2.cpp create mode 100644 openbr/plugins/distance/attribute.cpp create mode 100644 openbr/plugins/distance/byteL1.cpp create mode 100644 openbr/plugins/distance/default.cpp create mode 100644 openbr/plugins/distance/dist.cpp create mode 100644 openbr/plugins/distance/fuse.cpp create mode 100644 openbr/plugins/distance/gallerycompare.cpp create mode 100644 openbr/plugins/distance/halfbyteL1.cpp create mode 100644 openbr/plugins/distance/identical.cpp create mode 100644 openbr/plugins/distance/keypointmatcher.cpp create mode 100644 openbr/plugins/distance/l1.cpp create mode 100644 openbr/plugins/distance/neglogplusone.cpp create mode 100644 openbr/plugins/distance/online.cpp create mode 100644 openbr/plugins/distance/pipe.cpp create mode 100644 openbr/plugins/distance/sum.cpp delete mode 100644 openbr/plugins/draw.cpp delete mode 100644 openbr/plugins/ebif.cpp delete mode 100644 openbr/plugins/eyes.cpp delete mode 100644 openbr/plugins/fill.cpp delete mode 100644 openbr/plugins/filter.cpp delete mode 100644 openbr/plugins/format.cpp create mode 100644 openbr/plugins/format/binary.cpp create mode 100644 openbr/plugins/format/csv.cpp create mode 100644 openbr/plugins/format/ebts.cpp create mode 100644 openbr/plugins/format/lffs.cpp create mode 100644 openbr/plugins/format/mat.cpp create mode 100644 openbr/plugins/format/mtx.cpp create mode 100644 openbr/plugins/format/null.cpp create mode 100644 openbr/plugins/format/raw.cpp create mode 100644 openbr/plugins/format/scores.cpp create mode 100644 openbr/plugins/format/video.cpp create mode 100644 openbr/plugins/format/xml.cpp delete mode 100644 openbr/plugins/frames.cpp create mode 100644 openbr/plugins/gui/adjacentoverlay.cpp create mode 100644 openbr/plugins/gui/draw.cpp create mode 100644 openbr/plugins/gui/drawgridlines.cpp create mode 100644 openbr/plugins/gui/drawopticalflow.cpp create mode 100644 openbr/plugins/gui/drawpropertiespoint.cpp create mode 100644 openbr/plugins/gui/drawpropertypoint.cpp create mode 100644 openbr/plugins/gui/drawsegmentation.cpp delete mode 100644 openbr/plugins/hist.cpp create mode 100644 openbr/plugins/imgproc/abs.cpp create mode 100644 openbr/plugins/imgproc/blend.cpp create mode 100644 openbr/plugins/imgproc/blur.cpp create mode 100644 openbr/plugins/imgproc/contrasteq.cpp create mode 100644 openbr/plugins/imgproc/crop.cpp create mode 100644 openbr/plugins/imgproc/cropblack.cpp create mode 100644 openbr/plugins/imgproc/cropsquare.cpp create mode 100644 openbr/plugins/imgproc/csdn.cpp create mode 100644 openbr/plugins/imgproc/cvtcolor.cpp create mode 100644 openbr/plugins/imgproc/cvtfloat.cpp create mode 100644 openbr/plugins/imgproc/cvtuchar.cpp create mode 100644 openbr/plugins/imgproc/denoising.cpp create mode 100644 openbr/plugins/imgproc/discardalpha.cpp create mode 100644 openbr/plugins/imgproc/div.cpp create mode 100644 openbr/plugins/imgproc/dog.cpp create mode 100644 openbr/plugins/imgproc/ensurechannels.cpp create mode 100644 openbr/plugins/imgproc/flood.cpp create mode 100644 openbr/plugins/imgproc/gamma.cpp create mode 100644 openbr/plugins/imgproc/gradient.cpp create mode 100644 openbr/plugins/imgproc/hist.cpp create mode 100644 openbr/plugins/imgproc/histbin.cpp create mode 100644 openbr/plugins/imgproc/inpaint.cpp create mode 100644 openbr/plugins/imgproc/integral.cpp create mode 100644 openbr/plugins/imgproc/integralhist.cpp create mode 100644 openbr/plugins/imgproc/integralsampler.cpp create mode 100644 openbr/plugins/imgproc/limitsize.cpp create mode 100644 openbr/plugins/imgproc/madd.cpp create mode 100644 openbr/plugins/imgproc/mean.cpp create mode 100644 openbr/plugins/imgproc/meanfill.cpp create mode 100644 openbr/plugins/imgproc/pow.cpp create mode 100644 openbr/plugins/imgproc/rank.cpp create mode 100644 openbr/plugins/imgproc/recursiveintegralsampler.cpp create mode 100644 openbr/plugins/imgproc/resize.cpp create mode 100644 openbr/plugins/imgproc/rg.cpp create mode 100644 openbr/plugins/imgproc/roi.cpp create mode 100644 openbr/plugins/imgproc/roifrompoints.cpp create mode 100644 openbr/plugins/imgproc/scale.cpp create mode 100644 openbr/plugins/imgproc/splitchannels.cpp create mode 100644 openbr/plugins/imgproc/subdivide.cpp delete mode 100644 openbr/plugins/independent.cpp delete mode 100644 openbr/plugins/integral.cpp create mode 100644 openbr/plugins/io/write.cpp delete mode 100644 openbr/plugins/keypoint.cpp create mode 100644 openbr/plugins/metadata/grid.cpp create mode 100644 openbr/plugins/metadata/keytorect.cpp create mode 100644 openbr/plugins/metadata/procrustes.cpp create mode 100644 openbr/plugins/representation/hogdescriptor.cpp create mode 100644 openbr/plugins/representation/keypointdescriptor.cpp create mode 100644 openbr/plugins/representation/siftdescriptor.cpp delete mode 100644 openbr/plugins/sentence.cpp create mode 100644 openbr/plugins/video/aggregate.cpp create mode 100644 openbr/plugins/video/drop.cpp diff --git a/openbr/plugins/algorithms.cpp b/openbr/plugins/algorithms.cpp deleted file mode 100644 index 88ecaea..0000000 --- a/openbr/plugins/algorithms.cpp +++ /dev/null @@ -1,111 +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 "openbr_internal.h" - -namespace br -{ - -/*! - * \ingroup initializers - * \brief Initializes global abbreviations with implemented algorithms - * \author Josh Klontz \cite jklontz - */ -class AlgorithmsInitializer : public Initializer -{ - Q_OBJECT - - void initialize() const - { - // Face - Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration++++SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); - Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+++Discard"); - Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+FaceClassificationRegistration+Expand+++Discard"); - Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); - Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); - Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); - Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)"); - Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection+Contract+First+Show+Discard"); - Globals->abbreviations.insert("DownloadFaceRecognition", "Download+Open+ROI+Cvt(Gray)+Cascade(FrontalFace)+FaceRecognitionRegistration++++SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); - Globals->abbreviations.insert("OpenBR", "FaceRecognition"); - Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); - Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); - Globals->abbreviations.insert("FaceRecognition2", "{PP5Register+Affine(128,128,0.25,0.35)+Cvt(Gray)}+(Gradient+Bin(0,360,9,true))/(Blur(1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2,true)+Bin(0,10,10,true))+Merge+Integral+RecursiveIntegralSampler(4,2,8,LDA(.98)+Normalize(L1))+Cat+PCA(768)+Normalize(L1)+Quantize:UCharL1"); - Globals->abbreviations.insert("CropFace", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.25,0.35)"); - Globals->abbreviations.insert("4SF", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.33,0.45)+(Grid(10,10)+SIFTDescriptor(12)+ByRow)/(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))+PCA(0.95)+Cat+Normalize(L2)+Dup(12)+RndSubspace(0.05,1)+LDA(0.98)+Cat+PCA(0.95)+Normalize(L1)+Quantize:NegativeLogPlusOne(ByteL1)"); - - // Video - Globals->abbreviations.insert("DisplayVideo", "FPSLimit(30)+Show(false,[FrameNumber])+Discard"); - Globals->abbreviations.insert("PerFrameDetection", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard"); - Globals->abbreviations.insert("AgeGenderDemo", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+FaceClassificationRegistration++/+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard"); - Globals->abbreviations.insert("ShowOpticalFlowField", "SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard"); - Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard"); - Globals->abbreviations.insert("ShowMotionSegmentation", "DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard"); - - Globals->abbreviations.insert("HOGVideo", "Stream(DropFrames(5)+Cvt(Gray)+Grid(5,5)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); - Globals->abbreviations.insert("HOFVideo", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)"); - Globals->abbreviations.insert("HOGHOFVideo", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+(OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)/(First+Cvt(Gray)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)+CatCols)+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); - - // Generic Image Processing - Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); - Globals->abbreviations.insert("SURF", "Open+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); - Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); - Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); - Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)+Expand+EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); - Globals->abbreviations.insert("ImageSimilarity", "Open+EnsureChannels(3)+Resize(256,256)+SplitChannels+RectRegions(64,64,64,64)+Hist(256,0,8)+Cat:NegativeLogPlusOne(L2)"); - Globals->abbreviations.insert("ImageClassification", "Open+CropSquare+LimitSize(256)+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+RecursiveIntegralSampler(4,2,8,Singleton(KMeans(256)))+Cat+CvtFloat+Hist(256)+KNN(5,Dist(L1),false,5)+Rename(KNN,Subject)"); - Globals->abbreviations.insert("TanTriggs", "Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); - - // Hash - Globals->abbreviations.insert("FileName", "Name+Identity:Identical"); - Globals->abbreviations.insert("MD5", "Open+CryptographicHash(Md5):Identical"); - Globals->abbreviations.insert("SHA1", "Open+CryptographicHash(Sha1):Identical"); - - // Miscellaneous - Globals->abbreviations.insert("Display", "Open+Identity+Show+Discard"); - Globals->abbreviations.insert("RegisterAffine", "Open+Affine(256,256,0.37,0.45)"); - Globals->abbreviations.insert("ContrastEnhanced", "Open+Affine(256,256,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); - Globals->abbreviations.insert("ColoredLBP", "Open+Affine(128,128,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+ColoredU2"); - Globals->abbreviations.insert("FlipiBug","Flip+ReorderPoints([ 0, 1, 2, 3, 4, 5, 6, 7, 17,18,19,20,21,31,32,36,37,38,39,40,41,48,49,50,55,56,60,61,65], \ - [16,15,14,13,12,11,10, 9, 26,25,24,23,22,35,34,45,44,43,42,47,46,54,53,52,59,58,64,63,67])"); - Globals->abbreviations.insert("FlipiBugNoJaw","Flip+ReorderPoints([0,1,2,3,4,14,15,19,20,21,22,23,24,31,32,33,38,39,43,44,48], \ - [9,8,7,6,5,18,17,28,27,26,25,30,29,37,36,35,42,41,47,46,50])"); - Globals->abbreviations.insert("FlipMUCT","Flip+ReorderPoints([ 0, 1, 2, 3, 4,5,6,21,22,23,24,25,26,27,28,29,30,31,37,38,39,40,46,48,49,50,65,60,59,58,68,69,70,71], \ - [14,13,12,11,10,9,8,15,16,17,18,19,20,32,33,34,35,36,45,44,43,42,47,54,53,52,63,62,55,56,72,73,74,75])"); - - // Transforms - Globals->abbreviations.insert("FaceDetection", "Open+Cvt(Gray)+Cascade(FrontalFace)"); - Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))"); - Globals->abbreviations.insert("DenseHOG", "Gradient+RectRegions(8,8,6,6)+Bin(0,360,8)+Hist(8)"); - Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); - Globals->abbreviations.insert("DenseSIFT2", "(Grid(5,5)+SIFTDescriptor(12)+ByRow)"); - Globals->abbreviations.insert("FaceRecognitionRegistration", "ASEFEyes+Affine(88,88,0.25,0.35)"); - Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)"); - Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))"); - Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)"); - Globals->abbreviations.insert("FaceClassificationRegistration", "ASEFEyes+Affine(56,72,0.33,0.45)"); - Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)"); - Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)"); - Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)"); - Globals->abbreviations.insert("UCharL1", "Unit(ByteL1)"); - } -}; - -BR_REGISTER(Initializer, AlgorithmsInitializer) - -} // namespace br - -#include "algorithms.moc" diff --git a/openbr/plugins/cascade.cpp b/openbr/plugins/cascade.cpp deleted file mode 100644 index a0b282a..0000000 --- a/openbr/plugins/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 "openbr_internal.h" -#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/classification/ebif.cpp b/openbr/plugins/classification/ebif.cpp new file mode 100644 index 0000000..ca9f184 --- /dev/null +++ b/openbr/plugins/classification/ebif.cpp @@ -0,0 +1,131 @@ +#include + +#include +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Face Recognition Using Early Biologically Inspired Features + * Min Li (IBM China Research Lab, China), Nalini Ratha (IBM Watson Research Center, + * USA), Weihong Qian (IBM China Research Lab, China), Shenghua Bao (IBM China + * Research Lab, China), Zhong Su (IBM China Research Lab, China) + * \author Josh Klontz \cite jklontz + */ + +class EBIFTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int N READ get_N WRITE set_N RESET reset_N STORED false) // scales + Q_PROPERTY(int M READ get_M WRITE set_M RESET reset_M STORED false) // orientations + BR_PROPERTY(int, N, 6) + BR_PROPERTY(int, M, 9) + + QList orientations; + + void init() + { + for (int m=0; m > features; + foreach (Transform *orientation, orientations) { + // Compute the reponse wavelet response + Template response; + orientation->project(scales, response); + + // Pool for each two adjacent features + QList orientedFeatures; + for (int i=0; i localFeatures; localFeatures.reserve(2*M); + for (int m=0; m pool(const Mat &bottom, const Mat &top) const + { + QList features; + for (int i=0; i<=top.rows-3; i+=3) { + for (int j=0; j<=top.cols-3; j+=3) { + QList vals; vals.reserve(3*3 + 4*4); + + // Top values + for (int k=0; k<3; k++) { + const float *data = top.ptr(i+k, j); + for (int l=0; l<3; l++) + vals.append(data[l]); + } + + // Bottom values + for (int k=0; k<4; k++) { + const float *data = bottom.ptr(4*i/3+k, 4*j/3); + for (int l=0; l<4; l++) + vals.append(data[l]); + } + + double mean, stddev; + Common::MeanStdDev(vals, &mean, &stddev); + features.append(mean); + features.append(stddev); + } + } + + return features; + } +}; + +BR_REGISTER(Transform, EBIFTransform) + +} // namespace br + +#include "ebif.moc" diff --git a/openbr/plugins/cluster.cpp b/openbr/plugins/cluster.cpp deleted file mode 100644 index ab95f27..0000000 --- a/openbr/plugins/cluster.cpp +++ /dev/null @@ -1,298 +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/opencvutils.h" -#include - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Wraps OpenCV kmeans and flann. - * \author Josh Klontz \cite jklontz - */ -class KMeansTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) - Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) - BR_PROPERTY(int, kTrain, 256) - BR_PROPERTY(int, kSearch, 1) - - Mat centers; - mutable QScopedPointer index; - mutable QMutex mutex; - - void reindex() - { - index.reset(new flann::Index(centers, flann::LinearIndexParams())); - } - - void train(const TemplateList &data) - { - Mat bestLabels; - const double compactness = kmeans(OpenCVUtils::toMatByRow(data.data()), kTrain, bestLabels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, centers); - qDebug("KMeans compactness = %f", compactness); - reindex(); - } - - void project(const Template &src, Template &dst) const - { - QMutexLocker locker(&mutex); - Mat dists, indicies; - index->knnSearch(src, indicies, dists, kSearch); - dst = indicies.reshape(1, 1); - } - - void load(QDataStream &stream) - { - stream >> centers; - reindex(); - } - - void store(QDataStream &stream) const - { - stream << centers; - } -}; - -BR_REGISTER(Transform, KMeansTransform) - -/*! - * \ingroup transforms - * \brief K nearest neighbors classifier. - * \author Josh Klontz \cite jklontz - */ -class KNNTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(int k READ get_k WRITE set_k RESET reset_k STORED false) - Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) - Q_PROPERTY(bool weighted READ get_weighted WRITE set_weighted RESET reset_weighted STORED false) - Q_PROPERTY(int numSubjects READ get_numSubjects WRITE set_numSubjects RESET reset_numSubjects 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(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) - BR_PROPERTY(int, k, 1) - BR_PROPERTY(br::Distance*, distance, NULL) - BR_PROPERTY(bool, weighted, false) - BR_PROPERTY(int, numSubjects, 1) - BR_PROPERTY(QString, inputVariable, "Label") - BR_PROPERTY(QString, outputVariable, "KNN") - BR_PROPERTY(QString, galleryName, "") - - TemplateList gallery; - - void train(const TemplateList &data) - { - distance->train(data); - gallery = data; - } - - void project(const Template &src, Template &dst) const - { - QList< QPair > sortedScores = Common::Sort(distance->compare(gallery, src), true); - - QStringList subjects; - for (int i=0; i votes; - const int max = (k < 1) ? sortedScores.size() : std::min(k, sortedScores.size()); - for (int j=0; j(inputVariable)] += (weighted ? sortedScores[j].first : 1); - subjects.append(votes.keys()[votes.values().indexOf(Common::Max(votes.values()))]); - - // Remove subject from consideration - if (subjects.size() < numSubjects) - for (int j=sortedScores.size()-1; j>=0; j--) - if (gallery[sortedScores[j].second].file.get(inputVariable) == subjects.last()) - sortedScores.removeAt(j); - } - - dst.file.set(outputVariable, subjects.size() > 1 ? "[" + subjects.join(",") + "]" : subjects.first()); - dst.file.set("Nearest", gallery[sortedScores[0].second].file.name); - } - - void store(QDataStream &stream) const - { - stream << gallery; - } - - void load(QDataStream &stream) - { - stream >> gallery; - } - - void init() - { - if (!galleryName.isEmpty()) - gallery = TemplateList::fromGallery(galleryName); - } -}; - -BR_REGISTER(Transform, KNNTransform) - -/*! - * \ingroup transforms - * \brief Chooses k random points to be centroids. - * \author Austin Blanton \cite imaus10 - * \see KMeansTransform - */ -class RandomCentroidsTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) - Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) - BR_PROPERTY(int, kTrain, 256) - BR_PROPERTY(int, kSearch, 1) - - Mat centers; - mutable QScopedPointer index; - mutable QMutex mutex; - - void reindex() - { - index.reset(new flann::Index(centers, flann::LinearIndexParams())); - } - - void train(const TemplateList &data) - { - Mat flat = OpenCVUtils::toMatByRow(data.data()); - QList sample = Common::RandSample(kTrain, flat.rows, 0, true); - foreach (const int &idx, sample) - centers.push_back(flat.row(idx)); - reindex(); - } - - void project(const Template &src, Template &dst) const - { - QMutexLocker locker(&mutex); - Mat dists, indicies; - index->knnSearch(src, indicies, dists, kSearch); - dst = indicies.reshape(1, 1); - } - - void load(QDataStream &stream) - { - stream >> centers; - reindex(); - } - - void store(QDataStream &stream) const - { - stream << centers; - } -}; - -BR_REGISTER(Transform, RandomCentroidsTransform) - -class RegInitializer : public Initializer -{ - Q_OBJECT - - void initialize() const - { - qRegisterMetaType(); - } -}; -BR_REGISTER(Initializer, RegInitializer) - -class CollectNNTransform : public UntrainableMetaTransform -{ - Q_OBJECT - - Q_PROPERTY(int keep READ get_keep WRITE set_keep RESET reset_keep STORED false) - BR_PROPERTY(int, keep, 20) - - void project(const Template &src, Template &dst) const - { - dst.file = src.file; - dst.clear(); - dst.m() = cv::Mat(); - Neighbors neighbors; - for (int i=0; i < src.m().cols;i++) { - // skip self compares - if (i == src.file.get("FrameNumber")) - continue; - neighbors.append(Neighbor(i, src.m().at(0,i))); - } - int actuallyKeep = std::min(keep, neighbors.size()); - std::partial_sort(neighbors.begin(), neighbors.begin()+actuallyKeep, neighbors.end(), compareNeighbors); - - Neighbors selected = neighbors.mid(0, actuallyKeep); - dst.file.set("neighbors", QVariant::fromValue(selected)); - } -}; -BR_REGISTER(Transform, CollectNNTransform) - -class LogNNTransform : public TimeVaryingTransform -{ - Q_OBJECT - - Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) - BR_PROPERTY(QString, fileName, "") - - std::fstream fout; - - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - - if (!dst.file.contains("neighbors")) { - fout << std::endl; - return; - } - - Neighbors neighbors = dst.file.get("neighbors"); - if (neighbors.isEmpty() ) { - fout << std::endl; - return; - } - - QString aLine; - aLine.append(QString::number(neighbors[0].first)+":"+QString::number(neighbors[0].second)); - for (int i=1; i < neighbors.size();i++) - aLine.append(","+QString::number(neighbors[i].first)+":"+QString::number(neighbors[i].second)); - - fout << qPrintable(aLine) << std::endl; - } - - void init() - { - if (!fileName.isEmpty()) - fout.open(qPrintable(fileName), std::ios_base::out); - } - - void finalize(TemplateList &output) - { - (void) output; - fout.close(); - } - -public: - LogNNTransform() : TimeVaryingTransform(false, false) {} -}; -BR_REGISTER(Transform, LogNNTransform) - -} // namespace br - -#include "cluster.moc" diff --git a/openbr/plugins/cluster/collectnn.cpp b/openbr/plugins/cluster/collectnn.cpp new file mode 100644 index 0000000..74f2eff --- /dev/null +++ b/openbr/plugins/cluster/collectnn.cpp @@ -0,0 +1,42 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Collect nearest neighbors and append them to metadata. + * \author Charles Otto \cite caotto + */ +class CollectNNTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + Q_PROPERTY(int keep READ get_keep WRITE set_keep RESET reset_keep STORED false) + BR_PROPERTY(int, keep, 20) + + void project(const Template &src, Template &dst) const + { + dst.file = src.file; + dst.clear(); + dst.m() = cv::Mat(); + Neighbors neighbors; + for (int i=0; i < src.m().cols;i++) { + // skip self compares + if (i == src.file.get("FrameNumber")) + continue; + neighbors.append(Neighbor(i, src.m().at(0,i))); + } + int actuallyKeep = std::min(keep, neighbors.size()); + std::partial_sort(neighbors.begin(), neighbors.begin()+actuallyKeep, neighbors.end(), compareNeighbors); + + Neighbors selected = neighbors.mid(0, actuallyKeep); + dst.file.set("neighbors", QVariant::fromValue(selected)); + } +}; + +BR_REGISTER(Transform, CollectNNTransform) + +} // namespace br + +#include "collectnn.moc" diff --git a/openbr/plugins/cluster/kmeans.cpp b/openbr/plugins/cluster/kmeans.cpp new file mode 100644 index 0000000..4f30c31 --- /dev/null +++ b/openbr/plugins/cluster/kmeans.cpp @@ -0,0 +1,65 @@ +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV kmeans and flann. + * \author Josh Klontz \cite jklontz + */ +class KMeansTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) + Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) + BR_PROPERTY(int, kTrain, 256) + BR_PROPERTY(int, kSearch, 1) + + Mat centers; + mutable QScopedPointer index; + mutable QMutex mutex; + + void reindex() + { + index.reset(new flann::Index(centers, flann::LinearIndexParams())); + } + + void train(const TemplateList &data) + { + Mat bestLabels; + const double compactness = kmeans(OpenCVUtils::toMatByRow(data.data()), kTrain, bestLabels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, centers); + qDebug("KMeans compactness = %f", compactness); + reindex(); + } + + void project(const Template &src, Template &dst) const + { + QMutexLocker locker(&mutex); + Mat dists, indicies; + index->knnSearch(src, indicies, dists, kSearch); + dst = indicies.reshape(1, 1); + } + + void load(QDataStream &stream) + { + stream >> centers; + reindex(); + } + + void store(QDataStream &stream) const + { + stream << centers; + } +}; + +BR_REGISTER(Transform, KMeansTransform) + +} // namespace br + +#include "kmeans.moc" diff --git a/openbr/plugins/cluster/knn.cpp b/openbr/plugins/cluster/knn.cpp new file mode 100644 index 0000000..60fed0f --- /dev/null +++ b/openbr/plugins/cluster/knn.cpp @@ -0,0 +1,84 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief K nearest neighbors classifier. + * \author Josh Klontz \cite jklontz + */ +class KNNTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(int k READ get_k WRITE set_k RESET reset_k STORED false) + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(bool weighted READ get_weighted WRITE set_weighted RESET reset_weighted STORED false) + Q_PROPERTY(int numSubjects READ get_numSubjects WRITE set_numSubjects RESET reset_numSubjects 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(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) + BR_PROPERTY(int, k, 1) + BR_PROPERTY(br::Distance*, distance, NULL) + BR_PROPERTY(bool, weighted, false) + BR_PROPERTY(int, numSubjects, 1) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QString, outputVariable, "KNN") + BR_PROPERTY(QString, galleryName, "") + + TemplateList gallery; + + void train(const TemplateList &data) + { + distance->train(data); + gallery = data; + } + + void project(const Template &src, Template &dst) const + { + QList< QPair > sortedScores = Common::Sort(distance->compare(gallery, src), true); + + QStringList subjects; + for (int i=0; i votes; + const int max = (k < 1) ? sortedScores.size() : std::min(k, sortedScores.size()); + for (int j=0; j(inputVariable)] += (weighted ? sortedScores[j].first : 1); + subjects.append(votes.keys()[votes.values().indexOf(Common::Max(votes.values()))]); + + // Remove subject from consideration + if (subjects.size() < numSubjects) + for (int j=sortedScores.size()-1; j>=0; j--) + if (gallery[sortedScores[j].second].file.get(inputVariable) == subjects.last()) + sortedScores.removeAt(j); + } + + dst.file.set(outputVariable, subjects.size() > 1 ? "[" + subjects.join(",") + "]" : subjects.first()); + dst.file.set("Nearest", gallery[sortedScores[0].second].file.name); + } + + void store(QDataStream &stream) const + { + stream << gallery; + } + + void load(QDataStream &stream) + { + stream >> gallery; + } + + void init() + { + if (!galleryName.isEmpty()) + gallery = TemplateList::fromGallery(galleryName); + } +}; + +BR_REGISTER(Transform, KNNTransform) + +} // namespace br + +#include "knn.moc" diff --git a/openbr/plugins/cluster/lognn.cpp b/openbr/plugins/cluster/lognn.cpp new file mode 100644 index 0000000..0cdab6e --- /dev/null +++ b/openbr/plugins/cluster/lognn.cpp @@ -0,0 +1,65 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Log nearest neighbors to specified file. + * \author Charles Otto \cite caotto + */ +class LogNNTransform : public TimeVaryingTransform +{ + Q_OBJECT + + Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) + BR_PROPERTY(QString, fileName, "") + + std::fstream fout; + + void projectUpdate(const Template &src, Template &dst) + { + dst = src; + + if (!dst.file.contains("neighbors")) { + fout << std::endl; + return; + } + + Neighbors neighbors = dst.file.get("neighbors"); + if (neighbors.isEmpty() ) { + fout << std::endl; + return; + } + + QString aLine; + aLine.append(QString::number(neighbors[0].first)+":"+QString::number(neighbors[0].second)); + for (int i=1; i < neighbors.size();i++) + aLine.append(","+QString::number(neighbors[i].first)+":"+QString::number(neighbors[i].second)); + + fout << qPrintable(aLine) << std::endl; + } + + void init() + { + if (!fileName.isEmpty()) + fout.open(qPrintable(fileName), std::ios_base::out); + } + + void finalize(TemplateList &output) + { + (void) output; + fout.close(); + } + +public: + LogNNTransform() : TimeVaryingTransform(false, false) {} +}; + +BR_REGISTER(Transform, LogNNTransform) + +} // namespace br + +#include "lognn.moc" diff --git a/openbr/plugins/cluster/randomcentroids.cpp b/openbr/plugins/cluster/randomcentroids.cpp new file mode 100644 index 0000000..d9b8c9d --- /dev/null +++ b/openbr/plugins/cluster/randomcentroids.cpp @@ -0,0 +1,68 @@ +#include + +#include +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Chooses k random points to be centroids. + * \author Austin Blanton \cite imaus10 + * \see KMeansTransform + */ +class RandomCentroidsTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) + Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) + BR_PROPERTY(int, kTrain, 256) + BR_PROPERTY(int, kSearch, 1) + + Mat centers; + mutable QScopedPointer index; + mutable QMutex mutex; + + void reindex() + { + index.reset(new flann::Index(centers, flann::LinearIndexParams())); + } + + void train(const TemplateList &data) + { + Mat flat = OpenCVUtils::toMatByRow(data.data()); + QList sample = Common::RandSample(kTrain, flat.rows, 0, true); + foreach (const int &idx, sample) + centers.push_back(flat.row(idx)); + reindex(); + } + + void project(const Template &src, Template &dst) const + { + QMutexLocker locker(&mutex); + Mat dists, indicies; + index->knnSearch(src, indicies, dists, kSearch); + dst = indicies.reshape(1, 1); + } + + void load(QDataStream &stream) + { + stream >> centers; + reindex(); + } + + void store(QDataStream &stream) const + { + stream << centers; + } +}; + +BR_REGISTER(Transform, RandomCentroidsTransform) + +} //namespace br + +#include "randomcentroids.moc" diff --git a/openbr/plugins/core/algorithms.cpp b/openbr/plugins/core/algorithms.cpp new file mode 100644 index 0000000..2293e65 --- /dev/null +++ b/openbr/plugins/core/algorithms.cpp @@ -0,0 +1,111 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Initializes global abbreviations with implemented algorithms + * \author Josh Klontz \cite jklontz + */ +class AlgorithmsInitializer : public Initializer +{ + Q_OBJECT + + void initialize() const + { + // Face + Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration++++SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); + Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+++Discard"); + Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+FaceClassificationRegistration+Expand+++Discard"); + Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard"); + Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); + Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); + Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)"); + Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection+Contract+First+Show+Discard"); + Globals->abbreviations.insert("DownloadFaceRecognition", "Download+Open+ROI+Cvt(Gray)+Cascade(FrontalFace)+FaceRecognitionRegistration++++SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); + Globals->abbreviations.insert("OpenBR", "FaceRecognition"); + Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); + Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); + Globals->abbreviations.insert("FaceRecognition2", "{PP5Register+Affine(128,128,0.25,0.35)+Cvt(Gray)}+(Gradient+Bin(0,360,9,true))/(Blur(1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2,true)+Bin(0,10,10,true))+Merge+Integral+RecursiveIntegralSampler(4,2,8,LDA(.98)+Normalize(L1))+Cat+PCA(768)+Normalize(L1)+Quantize:UCharL1"); + Globals->abbreviations.insert("CropFace", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.25,0.35)"); + Globals->abbreviations.insert("4SF", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.33,0.45)+(Grid(10,10)+SIFTDescriptor(12)+ByRow)/(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))+PCA(0.95)+Cat+Normalize(L2)+Dup(12)+RndSubspace(0.05,1)+LDA(0.98)+Cat+PCA(0.95)+Normalize(L1)+Quantize:NegativeLogPlusOne(ByteL1)"); + + // Video + Globals->abbreviations.insert("DisplayVideo", "FPSLimit(30)+Show(false,[FrameNumber])+Discard"); + Globals->abbreviations.insert("PerFrameDetection", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard"); + Globals->abbreviations.insert("AgeGenderDemo", "SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+FaceClassificationRegistration++/+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard"); + Globals->abbreviations.insert("ShowOpticalFlowField", "SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard"); + Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard"); + Globals->abbreviations.insert("ShowMotionSegmentation", "DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard"); + + Globals->abbreviations.insert("HOGVideo", "Stream(DropFrames(5)+Cvt(Gray)+Grid(5,5)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); + Globals->abbreviations.insert("HOFVideo", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)"); + Globals->abbreviations.insert("HOGHOFVideo", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+(OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)/(First+Cvt(Gray)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat+Contract)+CatCols)+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); + + // Generic Image Processing + Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); + Globals->abbreviations.insert("SURF", "Open+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); + Globals->abbreviations.insert("SmallSIFT", "Open+LimitSize(512)+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); + Globals->abbreviations.insert("SmallSURF", "Open+LimitSize(512)+KeyPointDetector(SURF)+KeyPointDescriptor(SURF):KeyPointMatcher(BruteForce)"); + Globals->abbreviations.insert("ColorHist", "Open+LimitSize(512)+Expand+EnsureChannels(3)+SplitChannels+Hist(256,0,8)+Cat+Normalize(L1):L2"); + Globals->abbreviations.insert("ImageSimilarity", "Open+EnsureChannels(3)+Resize(256,256)+SplitChannels+RectRegions(64,64,64,64)+Hist(256,0,8)+Cat:NegativeLogPlusOne(L2)"); + Globals->abbreviations.insert("ImageClassification", "Open+CropSquare+LimitSize(256)+Cvt(Gray)+Gradient+Bin(0,360,9,true)+Merge+Integral+RecursiveIntegralSampler(4,2,8,Singleton(KMeans(256)))+Cat+CvtFloat+Hist(256)+KNN(5,Dist(L1),false,5)+Rename(KNN,Subject)"); + Globals->abbreviations.insert("TanTriggs", "Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); + + // Hash + Globals->abbreviations.insert("FileName", "Name+Identity:Identical"); + Globals->abbreviations.insert("MD5", "Open+CryptographicHash(Md5):Identical"); + Globals->abbreviations.insert("SHA1", "Open+CryptographicHash(Sha1):Identical"); + + // Miscellaneous + Globals->abbreviations.insert("Display", "Open+Identity+Show+Discard"); + Globals->abbreviations.insert("RegisterAffine", "Open+Affine(256,256,0.37,0.45)"); + Globals->abbreviations.insert("ContrastEnhanced", "Open+Affine(256,256,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)"); + Globals->abbreviations.insert("ColoredLBP", "Open+Affine(128,128,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+ColoredU2"); + Globals->abbreviations.insert("FlipiBug","Flip+ReorderPoints([ 0, 1, 2, 3, 4, 5, 6, 7, 17,18,19,20,21,31,32,36,37,38,39,40,41,48,49,50,55,56,60,61,65], \ + [16,15,14,13,12,11,10, 9, 26,25,24,23,22,35,34,45,44,43,42,47,46,54,53,52,59,58,64,63,67])"); + Globals->abbreviations.insert("FlipiBugNoJaw","Flip+ReorderPoints([0,1,2,3,4,14,15,19,20,21,22,23,24,31,32,33,38,39,43,44,48], \ + [9,8,7,6,5,18,17,28,27,26,25,30,29,37,36,35,42,41,47,46,50])"); + Globals->abbreviations.insert("FlipMUCT","Flip+ReorderPoints([ 0, 1, 2, 3, 4,5,6,21,22,23,24,25,26,27,28,29,30,31,37,38,39,40,46,48,49,50,65,60,59,58,68,69,70,71], \ + [14,13,12,11,10,9,8,15,16,17,18,19,20,32,33,34,35,36,45,44,43,42,47,54,53,52,63,62,55,56,72,73,74,75])"); + + // Transforms + Globals->abbreviations.insert("FaceDetection", "Open+Cvt(Gray)+Cascade(FrontalFace)"); + Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))"); + Globals->abbreviations.insert("DenseHOG", "Gradient+RectRegions(8,8,6,6)+Bin(0,360,8)+Hist(8)"); + Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); + Globals->abbreviations.insert("DenseSIFT2", "(Grid(5,5)+SIFTDescriptor(12)+ByRow)"); + Globals->abbreviations.insert("FaceRecognitionRegistration", "ASEFEyes+Affine(88,88,0.25,0.35)"); + Globals->abbreviations.insert("FaceRecognitionExtraction", "(Mask+DenseSIFT/DenseLBP+DownsampleTraining(PCA(0.95),instances=1)+Normalize(L2)+Cat)"); + Globals->abbreviations.insert("FaceRecognitionEmbedding", "(Dup(12)+RndSubspace(0.05,1)+DownsampleTraining(LDA(0.98),instances=-2)+Cat+DownsampleTraining(PCA(768),instances=1))"); + Globals->abbreviations.insert("FaceRecognitionQuantization", "(Normalize(L1)+Quantize)"); + Globals->abbreviations.insert("FaceClassificationRegistration", "ASEFEyes+Affine(56,72,0.33,0.45)"); + Globals->abbreviations.insert("FaceClassificationExtraction", "((Grid(7,7)+SIFTDescriptor(8)+ByRow)/DenseLBP+DownsampleTraining(PCA(0.95),instances=-1, inputVariable=Gender)+Cat)"); + Globals->abbreviations.insert("AgeRegressor", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Age)+DownsampleTraining(SVM(RBF,EPS_SVR,inputVariable=Age),instances=100, inputVariable=Age)"); + Globals->abbreviations.insert("GenderClassifier", "DownsampleTraining(Center(Range),instances=-1, inputVariable=Gender)+DownsampleTraining(SVM(RBF,C_SVC,inputVariable=Gender),instances=4000, inputVariable=Gender)"); + Globals->abbreviations.insert("UCharL1", "Unit(ByteL1)"); + } +}; + +BR_REGISTER(Initializer, AlgorithmsInitializer) + +} // namespace br + +#include "algorithms.moc" diff --git a/openbr/plugins/core/downsample.cpp b/openbr/plugins/core/downsample.cpp new file mode 100644 index 0000000..b1cc62f --- /dev/null +++ b/openbr/plugins/core/downsample.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 "downsample.moc" diff --git a/openbr/plugins/core/independent.cpp b/openbr/plugins/core/independent.cpp new file mode 100644 index 0000000..41a0054 --- /dev/null +++ b/openbr/plugins/core/independent.cpp @@ -0,0 +1,213 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Clones the transform so that it can be applied independently. + * \author Josh Klontz \cite jklontz + * \em Independent transforms expect single-matrix templates. + */ +class IndependentTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED false) + BR_PROPERTY(br::Transform*, transform, NULL) + + QList transforms; + + QString description(bool expanded) const + { + return transform->description(expanded); + } + + // can't use general setPropertyRecursive because of transforms oddness + bool setPropertyRecursive(const QString &name, QVariant value) + { + if (br::Object::setExistingProperty(name, value)) + return true; + + if (!transform->setPropertyRecursive(name, value)) + return false; + + for (int i=0;i < transforms.size();i++) + transforms[i]->setPropertyRecursive(name, value); + + return true; + } + + Transform *simplify(bool &newTransform) + { + newTransform = false; + bool newChild = false; + Transform *temp = transform->simplify(newChild); + if (temp == transform) { + return this; + } + IndependentTransform* indep = new IndependentTransform(); + indep->transform = temp; + + IndependentTransform *test = dynamic_cast (temp); + if (test) { + // child was independent? this changes things... + indep->transform = test->transform; + for (int i=0; i < transforms.size(); i++) { + bool newThing = false; + IndependentTransform *probe = dynamic_cast (transforms[i]->simplify(newThing)); + indep->transforms.append(probe->transform); + if (newThing) + probe->setParent(indep); + } + indep->file = indep->transform->file; + indep->trainable = indep->transform->trainable; + indep->setObjectName(indep->transform->objectName()); + + return indep; + } + + if (newChild) + indep->transform->setParent(indep); + + for (int i=0; i < transforms.size();i++) { + bool subTform = false; + indep->transforms.append(transforms[i]->simplify(subTform)); + if (subTform) + indep->transforms[i]->setParent(indep); + } + + indep->file = indep->transform->file; + indep->trainable = indep->transform->trainable; + indep->setObjectName(indep->transform->objectName()); + + return indep; + } + + void init() + { + transforms.clear(); + if (transform == NULL) + return; + + transform->setParent(this); + transforms.append(transform); + file = transform->file; + trainable = transform->trainable; + setObjectName(transform->objectName()); + } + + Transform *clone() const + { + IndependentTransform *independentTransform = new IndependentTransform(); + independentTransform->transform = transform->clone(); + independentTransform->init(); + return independentTransform; + } + + bool timeVarying() const { return transform->timeVarying(); } + + static void _train(Transform *transform, const TemplateList *data) + { + transform->train(*data); + } + + void train(const TemplateList &data) + { + // Don't bother if the transform is untrainable + if (!trainable) return; + + QList templatesList; + foreach (const Template &t, data) { + if ((templatesList.size() != t.size()) && !templatesList.isEmpty()) + qWarning("Independent::train (%s) template %s of size %d differs from expected size %d.", qPrintable(objectName()), qPrintable(t.file.name), t.size(), templatesList.size()); + while (templatesList.size() < t.size()) + templatesList.append(TemplateList()); + for (int i=0; iclone()); + + QFutureSynchronizer futures; + for (int i=0; i mats; + for (int i=0; iproject(Template(src.file, src[i]), dst); + mats.append(dst); + dst.clear(); + } + dst.append(mats); + } + + void projectUpdate(const Template &src, Template &dst) + { + dst.file = src.file; + QList mats; + for (int i=0; iprojectUpdate(Template(src.file, src[i]), dst); + mats.append(dst); + dst.clear(); + } + dst.append(mats); + } + + void finalize(TemplateList &out) + { + if (transforms.empty()) + return; + + transforms[0]->finalize(out); + for (int i=1; i < transforms.size(); i++) { + TemplateList temp; + transforms[i]->finalize(temp); + + for (int j=0; j < out.size(); j++) + out[j].append(temp[j]); + } + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst.reserve(src.size()); + foreach (const Template &t, src) { + dst.append(Template()); + projectUpdate(t, dst.last()); + } + } + + void store(QDataStream &stream) const + { + const int size = transforms.size(); + stream << size; + for (int i=0; istore(stream); + } + + void load(QDataStream &stream) + { + int size; + stream >> size; + while (transforms.size() < size) + transforms.append(transform->clone()); + for (int i=0; iload(stream); + } +}; + +BR_REGISTER(Transform, IndependentTransform) + +} // namespace br + +#include "independent.moc" diff --git a/openbr/plugins/core/singleton.cpp b/openbr/plugins/core/singleton.cpp new file mode 100644 index 0000000..df64659 --- /dev/null +++ b/openbr/plugins/core/singleton.cpp @@ -0,0 +1,73 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief A globally shared transform. + * \author Josh Klontz \cite jklontz + */ +class SingletonTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + BR_PROPERTY(QString, description, "Identity") + + static QMutex mutex; + static QHash transforms; + static QHash trainingReferenceCounts; + static QHash trainingData; + + Transform *transform; + + void init() + { + QMutexLocker locker(&mutex); + if (!transforms.contains(description)) { + transforms.insert(description, make(description)); + trainingReferenceCounts.insert(description, 0); + } + + transform = transforms[description]; + trainingReferenceCounts[description]++; + } + + void train(const TemplateList &data) + { + QMutexLocker locker(&mutex); + trainingData[description].append(data); + trainingReferenceCounts[description]--; + if (trainingReferenceCounts[description] > 0) return; + transform->train(trainingData[description]); + trainingData[description].clear(); + } + + void project(const Template &src, Template &dst) const + { + transform->project(src, dst); + } + + void store(QDataStream &stream) const + { + if (transform->parent() == this) + transform->store(stream); + } + + void load(QDataStream &stream) + { + if (transform->parent() == this) + transform->load(stream); + } +}; + +QMutex SingletonTransform::mutex; +QHash SingletonTransform::transforms; +QHash SingletonTransform::trainingReferenceCounts; +QHash SingletonTransform::trainingData; + +BR_REGISTER(Transform, SingletonTransform) + +} // namespace br + +#include "singleton.moc" diff --git a/openbr/plugins/crop.cpp b/openbr/plugins/crop.cpp deleted file mode 100644 index 01b5167..0000000 --- a/openbr/plugins/crop.cpp +++ /dev/null @@ -1,299 +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/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Crops about the specified region of interest. - * \author Josh Klontz \cite jklontz - */ -class CropTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int x READ get_x WRITE set_x RESET reset_x STORED false) - Q_PROPERTY(int y READ get_y WRITE set_y RESET reset_y STORED false) - Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) - Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) - BR_PROPERTY(int, x, 0) - BR_PROPERTY(int, y, 0) - BR_PROPERTY(int, width, -1) - BR_PROPERTY(int, height, -1) - - void project(const Template &src, Template &dst) const - { - dst = Mat(src, Rect(x, y, width < 1 ? src.m().cols-x-abs(width) : width, height < 1 ? src.m().rows-y-abs(height) : height)); - } -}; - -BR_REGISTER(Transform, CropTransform) - -/*! - * \ingroup transforms - * \brief Crops the rectangular regions of interest. - * \author Josh Klontz \cite jklontz - */ -class ROITransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) - BR_PROPERTY(QString, propName, "") - - void project(const Template &src, Template &dst) const - { - if (!propName.isEmpty()) { - QRectF rect = src.file.get(propName); - dst += src.m()(OpenCVUtils::toRect(rect)); - } else if (!src.file.rects().empty()) { - foreach (const QRectF &rect, src.file.rects()) - dst += src.m()(OpenCVUtils::toRect(rect)); - } else if (src.file.contains(QStringList() << "X" << "Y" << "Width" << "Height")) { - dst += src.m()(Rect(src.file.get("X"), - src.file.get("Y"), - src.file.get("Width"), - src.file.get("Height"))); - } else { - dst = src; - if (Globals->verbose) - qWarning("No rects present in file."); - } - } -}; - -BR_REGISTER(Transform, ROITransform) - -/*! - * \ingroup transforms - * \brief Crops the rectangular regions of interest from given points and sizes. - * \author Austin Blanton \cite imaus10 - */ -class ROIFromPtsTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) - Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) - BR_PROPERTY(int, width, 1) - BR_PROPERTY(int, height, 1) - - void project(const Template &src, Template &dst) const - { - foreach (const QPointF &pt, src.file.points()) { - int x = pt.x() - (width/2); - int y = pt.y() - (height/2); - dst += src.m()(Rect(x, y, width, height)); - } - } -}; - -BR_REGISTER(Transform, ROIFromPtsTransform) - -/*! - * \ingroup transforms - * \brief Resize the template - * \author Josh Klontz \cite jklontz - * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement. - * \param preserveAspect If true, the image will be sized per specification, but - * a border will be applied to preserve aspect ratio. - */ -class ResizeTransform : public UntrainableTransform -{ - Q_OBJECT - Q_ENUMS(Method) - -public: - /*!< */ - enum Method { Near = INTER_NEAREST, - Area = INTER_AREA, - Bilin = INTER_LINEAR, - Cubic = INTER_CUBIC, - Lanczo = INTER_LANCZOS4}; - -private: - Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) - Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) - Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) - Q_PROPERTY(bool preserveAspect READ get_preserveAspect WRITE set_preserveAspect RESET reset_preserveAspect STORED false) - BR_PROPERTY(int, rows, -1) - BR_PROPERTY(int, columns, -1) - BR_PROPERTY(Method, method, Bilin) - BR_PROPERTY(bool, preserveAspect, false) - - void project(const Template &src, Template &dst) const - { - if (!preserveAspect) - resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method); - else { - float inRatio = (float) src.m().rows / src.m().cols; - float outRatio = (float) rows / columns; - dst = Mat::zeros(rows, columns, src.m().type()); - if (outRatio > inRatio) { - float heightAR = src.m().rows * inRatio / outRatio; - Mat buffer; - resize(src, buffer, Size(columns, heightAR), 0, 0, method); - buffer.copyTo(dst.m()(Rect(0, (rows - heightAR) / 2, columns, heightAR))); - } else { - float widthAR = src.m().cols / inRatio * outRatio; - Mat buffer; - resize(src, buffer, Size(widthAR, rows), 0, 0, method); - buffer.copyTo(dst.m()(Rect((columns - widthAR) / 2, 0, widthAR, rows))); - } - } - } -}; - -BR_REGISTER(Transform, ResizeTransform) - -/*! - * \ingroup transforms - * \brief Limit the size of the template - * \author Josh Klontz \cite jklontz - */ -class LimitSizeTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int max READ get_max WRITE set_max RESET reset_max STORED false) - BR_PROPERTY(int, max, -1) - - void project(const Template &src, Template &dst) const - { - const Mat &m = src; - if (m.rows > m.cols) - if (m.rows > max) resize(m, dst, Size(std::max(1, m.cols * max / m.rows), max)); - else dst = m; - else - if (m.cols > max) resize(m, dst, Size(max, std::max(1, m.rows * max / m.cols))); - else dst = m; - } -}; - -BR_REGISTER(Transform, LimitSizeTransform) - -/*! - * \ingroup transforms - * \brief Enforce a multiple of \em n columns. - * \author Josh Klontz \cite jklontz - */ -class DivTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) - BR_PROPERTY(int, n, 1) - - void project(const Template &src, Template &dst) const - { - dst = Mat(src, Rect(0,0,n*(src.m().cols/n),src.m().rows)); - } -}; - -BR_REGISTER(Transform, DivTransform) - -/*! - * \ingroup transforms - * \brief Crop out black borders - * \author Josh Klontz \cite jklontz - */ -class CropBlackTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - Mat gray; - OpenCVUtils::cvtGray(src, gray); - - int xStart = 0; - while (xStart < gray.cols) { - if (mean(gray.col(xStart))[0] >= 1) break; - xStart++; - } - - int xEnd = gray.cols - 1; - while (xEnd >= 0) { - if (mean(gray.col(xEnd))[0] >= 1) break; - xEnd--; - } - - int yStart = 0; - while (yStart < gray.rows) { - if (mean(gray.col(yStart))[0] >= 1) break; - yStart++; - } - - int yEnd = gray.rows - 1; - while (yEnd >= 0) { - if (mean(gray.col(yEnd))[0] >= 1) break; - yEnd--; - } - - dst = src.m()(Rect(xStart, yStart, xEnd-xStart, yEnd-yStart)); - } -}; - -BR_REGISTER(Transform, CropBlackTransform) - -/*! - * \ingroup transforms - * \brief Divide the matrix into 4 smaller matricies of equal size. - * \author Josh Klontz \cite jklontz - */ -class SubdivideTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - const Mat &m = src; - const int subrows = m.rows/2; - const int subcolumns = m.cols/2; - dst.append(Mat(m,Rect(0, 0, subcolumns, subrows)).clone()); - dst.append(Mat(m,Rect(subcolumns, 0, subcolumns, subrows)).clone()); - dst.append(Mat(m,Rect(0, subrows, subcolumns, subrows)).clone()); - dst.append(Mat(m,Rect(subcolumns, subrows, subcolumns, subrows)).clone()); - } -}; - -BR_REGISTER(Transform, SubdivideTransform) - -/*! - * \ingroup transforms - * \brief Trim the image so the width and the height are the same size. - * \author Josh Klontz \cite jklontz - */ -class CropSquareTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - const Mat &m = src; - const int newSize = min(m.rows, m.cols); - dst = Mat(m, Rect((m.cols-newSize)/2, (m.rows-newSize)/2, newSize, newSize)); - } -}; - -BR_REGISTER(Transform, CropSquareTransform) - -} // namespace br - -#include "crop.moc" diff --git a/openbr/plugins/cvt.cpp b/openbr/plugins/cvt.cpp deleted file mode 100644 index 362ca0c..0000000 --- a/openbr/plugins/cvt.cpp +++ /dev/null @@ -1,292 +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_internal.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Colorspace conversion. - * \author Josh Klontz \cite jklontz - */ -class CvtTransform : public UntrainableTransform -{ - Q_OBJECT - Q_ENUMS(ColorSpace) - Q_PROPERTY(ColorSpace colorSpace READ get_colorSpace WRITE set_colorSpace RESET reset_colorSpace STORED false) - Q_PROPERTY(int channel READ get_channel WRITE set_channel RESET reset_channel STORED false) - -public: - enum ColorSpace { Gray = CV_BGR2GRAY, - RGBGray = CV_RGB2GRAY, - HLS = CV_BGR2HLS, - HSV = CV_BGR2HSV, - Lab = CV_BGR2Lab, - Luv = CV_BGR2Luv, - RGB = CV_BGR2RGB, - XYZ = CV_BGR2XYZ, - YCrCb = CV_BGR2YCrCb, - Color = CV_GRAY2BGR }; - -private: - BR_PROPERTY(ColorSpace, colorSpace, Gray) - BR_PROPERTY(int, channel, -1) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() > 1 || colorSpace == CV_GRAY2BGR) cvtColor(src, dst, colorSpace); - else dst = src; - - if (channel != -1) { - std::vector mv; - split(dst, mv); - dst = mv[channel % (int)mv.size()]; - } - } -}; - -BR_REGISTER(Transform, CvtTransform) - -/*! - * \ingroup transforms - * \brief Convert to floating point format. - * \author Josh Klontz \cite jklontz - */ -class CvtFloatTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - src.m().convertTo(dst, CV_32F); - } -}; - -BR_REGISTER(Transform, CvtFloatTransform) - -/*! - * \ingroup transforms - * \brief Convert to uchar format - * \author Josh Klontz \cite jklontz - */ -class CvtUCharTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - OpenCVUtils::cvtUChar(src, dst); - } -}; - -BR_REGISTER(Transform, CvtUCharTransform) - -/*! - * \ingroup transforms - * \brief Scales using the given factor - * \author Scott Klum \cite sklum - */ -class ScaleTransform : public UntrainableTransform -{ - Q_OBJECT - - Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) - BR_PROPERTY(float, scaleFactor, 1.) - - void project(const Template &src, Template &dst) const - { - resize(src, dst, Size(src.m().cols*scaleFactor,src.m().rows*scaleFactor)); - } -}; - -BR_REGISTER(Transform, ScaleTransform) - -/*! - * \ingroup transforms - * \brief Split a multi-channel matrix into several single-channel matrices. - * \author Josh Klontz \cite jklontz - */ -class SplitChannelsTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - std::vector mv; - split(src, mv); - foreach (const Mat &m, mv) - dst += m; - } -}; - -BR_REGISTER(Transform, SplitChannelsTransform) - -/*! - * \ingroup transforms - * \brief Enforce the matrix has a certain number of channels by adding or removing channels. - * \author Josh Klontz \cite jklontz - */ -class EnsureChannelsTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) - BR_PROPERTY(int, n, 1) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() == n) { - dst = src; - } else { - std::vector mv; - split(src, mv); - - // Add extra channels - while ((int)mv.size() < n) { - for (int i=0; i n) - mv.pop_back(); - - merge(mv, dst); - } - } -}; - -BR_REGISTER(Transform, EnsureChannelsTransform) - -/*! - * \ingroup transforms - * \brief Drop the alpha channel (if exists). - * \author Austin Blanton \cite imaus10 - */ -class DiscardAlphaTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() > 4 || src.m().channels() == 2) { - dst.file.fte = true; - return; - } - - dst = src; - if (src.m().channels() == 4) { - std::vector mv; - split(src, mv); - mv.pop_back(); - merge(mv, dst); - } - } -}; - -BR_REGISTER(Transform, DiscardAlphaTransform) - -/*! - * \ingroup transforms - * \brief Normalized RG color space. - * \author Josh Klontz \cite jklontz - */ -class RGTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - if (src.m().type() != CV_8UC3) - qFatal("Expected CV_8UC3 images."); - - const Mat &m = src.m(); - Mat R(m.size(), CV_8UC1); // R / (R+G+B) - Mat G(m.size(), CV_8UC1); // G / (R+G+B) - - for (int i=0; i(i,j); - const int b = v[0]; - const int g = v[1]; - const int r = v[2]; - const int sum = b + g + r; - if (sum > 0) { - R.at(i, j) = saturate_cast(255.0*r/(r+g+b)); - G.at(i, j) = saturate_cast(255.0*g/(r+g+b)); - } else { - R.at(i, j) = 0; - G.at(i, j) = 0; - } - } - - dst.append(R); - dst.append(G); - } -}; - -BR_REGISTER(Transform, RGTransform) - -/*! - * \ingroup transforms - * \brief dst = a*src+b - * \author Josh Klontz \cite jklontz - */ -class MAddTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(double a READ get_a WRITE set_a RESET reset_a STORED false) - Q_PROPERTY(double b READ get_b WRITE set_b RESET reset_b STORED false) - BR_PROPERTY(double, a, 1) - BR_PROPERTY(double, b, 0) - - void project(const Template &src, Template &dst) const - { - src.m().convertTo(dst.m(), src.m().depth(), a, b); - } -}; - -BR_REGISTER(Transform, MAddTransform) - -/*! - * \ingroup transforms - * \brief Computes the absolute value of each element. - * \author Josh Klontz \cite jklontz - */ -class AbsTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - dst = abs(src); - } -}; - -BR_REGISTER(Transform, AbsTransform) - -} // namespace br - -#include "cvt.moc" diff --git a/openbr/plugins/denoising.cpp b/openbr/plugins/denoising.cpp deleted file mode 100644 index 03ac646..0000000 --- a/openbr/plugins/denoising.cpp +++ /dev/null @@ -1,50 +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" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Wraps OpenCV Non-Local Means Denoising - * \author Josh Klontz \cite jklontz - */ -class NLMeansDenoisingTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float h READ get_h WRITE set_h RESET reset_h STORED false) - Q_PROPERTY(int templateWindowSize READ get_templateWindowSize WRITE set_templateWindowSize RESET reset_templateWindowSize STORED false) - Q_PROPERTY(int searchWindowSize READ get_searchWindowSize WRITE set_searchWindowSize RESET reset_searchWindowSize STORED false) - BR_PROPERTY(float, h, 3) - BR_PROPERTY(int, templateWindowSize, 7) - BR_PROPERTY(int, searchWindowSize, 21) - - void project(const Template &src, Template &dst) const - { - fastNlMeansDenoising(src, dst, h, templateWindowSize, searchWindowSize); - } -}; - -BR_REGISTER(Transform, NLMeansDenoisingTransform) - -} // namespace br - -#include "denoising.moc" diff --git a/openbr/plugins/detection/cascade.cpp b/openbr/plugins/detection/cascade.cpp new file mode 100644 index 0000000..f523cff --- /dev/null +++ b/openbr/plugins/detection/cascade.cpp @@ -0,0 +1,440 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 new file mode 100644 index 0000000..ae00219 --- /dev/null +++ b/openbr/plugins/detection/eyes.cpp @@ -0,0 +1,198 @@ +/* +# 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 new file mode 100644 index 0000000..0c59d9e --- /dev/null +++ b/openbr/plugins/detection/keypointdetector.cpp @@ -0,0 +1,54 @@ +#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.cpp b/openbr/plugins/distance.cpp deleted file mode 100644 index 4fd7014..0000000 --- a/openbr/plugins/distance.cpp +++ /dev/null @@ -1,512 +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 -#include -#include -#include "openbr_internal.h" - -#include "openbr/core/distance_sse.h" -#include "openbr/core/qtutils.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup distances - * \brief Standard distance metrics - * \author Josh Klontz \cite jklontz - */ -class DistDistance : public UntrainableDistance -{ - Q_OBJECT - Q_ENUMS(Metric) - Q_PROPERTY(Metric metric READ get_metric WRITE set_metric RESET reset_metric STORED false) - Q_PROPERTY(bool negLogPlusOne READ get_negLogPlusOne WRITE set_negLogPlusOne RESET reset_negLogPlusOne STORED false) - -public: - /*!< */ - enum Metric { Correlation, - ChiSquared, - Intersection, - Bhattacharyya, - INF, - L1, - L2, - Cosine, - Dot}; - -private: - BR_PROPERTY(Metric, metric, L2) - BR_PROPERTY(bool, negLogPlusOne, true) - - float compare(const Mat &a, const Mat &b) const - { - if ((a.size != b.size) || - (a.type() != b.type())) - return -std::numeric_limits::max(); - -// TODO: this max value is never returned based on the switch / default - float result = std::numeric_limits::max(); - switch (metric) { - case Correlation: - return compareHist(a, b, CV_COMP_CORREL); - case ChiSquared: - result = compareHist(a, b, CV_COMP_CHISQR); - break; - case Intersection: - result = compareHist(a, b, CV_COMP_INTERSECT); - break; - case Bhattacharyya: - result = compareHist(a, b, CV_COMP_BHATTACHARYYA); - break; - case INF: - result = norm(a, b, NORM_INF); - break; - case L1: - result = norm(a, b, NORM_L1); - break; - case L2: - result = norm(a, b, NORM_L2); - break; - case Cosine: - return cosine(a, b); - case Dot: - return a.dot(b); - default: - qFatal("Invalid metric"); - } - - if (result != result) - qFatal("NaN result."); - - return negLogPlusOne ? -log(result+1) : result; - } - - static float cosine(const Mat &a, const Mat &b) - { - float dot = 0; - float magA = 0; - float magB = 0; - - for (int row=0; row(row,col); - const float query = b.at(row,col); - dot += target * query; - magA += target * target; - magB += query * query; - } - } - - return dot / (sqrt(magA)*sqrt(magB)); - } -}; - -BR_REGISTER(Distance, DistDistance) - -/*! - * \ingroup distances - * \brief DistDistance wrapper. - * \author Josh Klontz \cite jklontz - */ -class DefaultDistance : public UntrainableDistance -{ - Q_OBJECT - Distance *distance; - - void init() - { - distance = Distance::make("Dist("+file.suffix()+")"); - } - - float compare(const cv::Mat &a, const cv::Mat &b) const - { - return distance->compare(a, b); - } -}; - -BR_REGISTER(Distance, DefaultDistance) - -/*! - * \ingroup distances - * \brief Distances in series. - * \author Josh Klontz \cite jklontz - * - * The templates are compared using each br::Distance in order. - * If the result of the comparison with any given distance is -FLOAT_MAX then this result is returned early. - * Otherwise the returned result is the value of comparing the templates using the last br::Distance. - */ -class PipeDistance : public Distance -{ - Q_OBJECT - Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) - BR_PROPERTY(QList, distances, QList()) - - void train(const TemplateList &data) - { - QFutureSynchronizer futures; - foreach (br::Distance *distance, distances) - futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); - futures.waitForFinished(); - } - - float compare(const Template &a, const Template &b) const - { - float result = -std::numeric_limits::max(); - foreach (br::Distance *distance, distances) { - result = distance->compare(a, b); - if (result == -std::numeric_limits::max()) - return result; - } - return result; - } -}; - -BR_REGISTER(Distance, PipeDistance) - -/*! - * \ingroup distances - * \brief Fuses similarity scores across multiple matrices of compared templates - * \author Scott Klum \cite sklum - * \note Operation: Mean, sum, min, max are supported. - */ -class FuseDistance : public Distance -{ - Q_OBJECT - Q_ENUMS(Operation) - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) - Q_PROPERTY(Operation operation READ get_operation WRITE set_operation RESET reset_operation STORED false) - Q_PROPERTY(QList weights READ get_weights WRITE set_weights RESET reset_weights STORED false) - - QList distances; - -public: - /*!< */ - enum Operation {Mean, Sum, Max, Min}; - -private: - BR_PROPERTY(QString, description, "L2") - BR_PROPERTY(Operation, operation, Mean) - BR_PROPERTY(QList, weights, QList()) - - void train(const TemplateList &src) - { - // Partition the templates by matrix - QList split; - for (int i=0; i partitionedSrc = src.partition(split); - - while (distances.size() < partitionedSrc.size()) - distances.append(make(description)); - - // Train on each of the partitions - for (int i=0; itrain(partitionedSrc[i]); - } - - float compare(const Template &a, const Template &b) const - { - if (a.size() != b.size()) qFatal("Comparison size mismatch"); - - QList scores; - for (int i=0; icompare(Template(a.file, a[i]),Template(b.file, b[i]))); - } - - switch (operation) { - case Mean: - return std::accumulate(scores.begin(),scores.end(),0.0)/(float)scores.size(); - break; - case Sum: - return std::accumulate(scores.begin(),scores.end(),0.0); - break; - case Min: - return *std::min_element(scores.begin(),scores.end()); - break; - case Max: - return *std::max_element(scores.begin(),scores.end()); - break; - default: - qFatal("Invalid operation."); - } - return 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, FuseDistance) - -/*! - * \ingroup distances - * \brief Fast 8-bit L1 distance - * \author Josh Klontz \cite jklontz - */ -class ByteL1Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const unsigned char *a, const unsigned char *b, size_t size) const - { - return l1(a, b, size); - } -}; - -BR_REGISTER(Distance, ByteL1Distance) - -/*! - * \ingroup distances - * \brief Fast 4-bit L1 distance - * \author Josh Klontz \cite jklontz - */ -class HalfByteL1Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const Mat &a, const Mat &b) const - { - return packed_l1(a.data, b.data, a.total()); - } -}; - -BR_REGISTER(Distance, HalfByteL1Distance) - -/*! - * \ingroup distances - * \brief Returns -log(distance(a,b)+1) - * \author Josh Klontz \cite jklontz - */ -class NegativeLogPlusOneDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) - BR_PROPERTY(br::Distance*, distance, NULL) - - void train(const TemplateList &src) - { - distance->train(src); - } - - float compare(const Template &a, const Template &b) const - { - return -log(distance->compare(a,b)+1); - } - - void store(QDataStream &stream) const - { - distance->store(stream); - } - - void load(QDataStream &stream) - { - distance->load(stream); - } -}; - -BR_REGISTER(Distance, NegativeLogPlusOneDistance) - -/*! - * \ingroup distances - * \brief Returns \c true if the templates are identical, \c false otherwise. - * \author Josh Klontz \cite jklontz - */ -class IdenticalDistance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const Mat &a, const Mat &b) const - { - const size_t size = a.total() * a.elemSize(); - if (size != b.total() * b.elemSize()) return 0; - for (size_t i=0; i scoreHash; - mutable QMutex mutex; - - float compare(const Template &target, const Template &query) const - { - float currentScore = distance->compare(target, query); - - QMutexLocker mutexLocker(&mutex); - return scoreHash[target.file.name] = (1.0- alpha) * scoreHash[target.file.name] + alpha * currentScore; - } -}; - -BR_REGISTER(Distance, OnlineDistance) - -/*! - * \ingroup distances - * \brief Attenuation function based distance from attributes - * \author Scott Klum \cite sklum - */ -class AttributeDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(QString attribute READ get_attribute WRITE set_attribute RESET reset_attribute STORED false) - BR_PROPERTY(QString, attribute, QString()) - - float compare(const Template &target, const Template &query) const - { - float queryValue = query.file.get(attribute); - float targetValue = target.file.get(attribute); - - // TODO: Set this magic number to something meaningful - float stddev = 1; - - if (queryValue == targetValue) return 1; - else return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((targetValue-queryValue)/stddev, 2)); - } -}; - -BR_REGISTER(Distance, AttributeDistance) - -/*! - * \ingroup distances - * \brief Sum match scores across multiple distances - * \author Scott Klum \cite sklum - */ -class SumDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) - BR_PROPERTY(QList, distances, QList()) - - void train(const TemplateList &data) - { - QFutureSynchronizer futures; - foreach (br::Distance *distance, distances) - futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); - futures.waitForFinished(); - } - - float compare(const Template &target, const Template &query) const - { - float result = 0; - - foreach (br::Distance *distance, distances) { - result += distance->compare(target, query); - - if (result == -std::numeric_limits::max()) - return result; - } - - return result; - } -}; - -BR_REGISTER(Distance, SumDistance) - -/*! - * \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 "distance.moc" diff --git a/openbr/plugins/distance/L2.cpp b/openbr/plugins/distance/L2.cpp new file mode 100644 index 0000000..3543894 --- /dev/null +++ b/openbr/plugins/distance/L2.cpp @@ -0,0 +1,30 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief L2 distance computed using eigen. + * \author Josh Klontz \cite jklontz + */ +class L2Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const cv::Mat &a, const cv::Mat &b) const + { + const int size = a.rows * a.cols; + Eigen::Map aMap((float*)a.data, size); + Eigen::Map bMap((float*)b.data, size); + return (aMap-bMap).squaredNorm(); + } +}; + +BR_REGISTER(Distance, L2Distance) + +} // namespace br + +#include "L2.moc" diff --git a/openbr/plugins/distance/attribute.cpp b/openbr/plugins/distance/attribute.cpp new file mode 100644 index 0000000..17446b2 --- /dev/null +++ b/openbr/plugins/distance/attribute.cpp @@ -0,0 +1,34 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Attenuation function based distance from attributes + * \author Scott Klum \cite sklum + */ +class AttributeDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QString attribute READ get_attribute WRITE set_attribute RESET reset_attribute STORED false) + BR_PROPERTY(QString, attribute, QString()) + + float compare(const Template &target, const Template &query) const + { + float queryValue = query.file.get(attribute); + float targetValue = target.file.get(attribute); + + // TODO: Set this magic number to something meaningful + float stddev = 1; + + if (queryValue == targetValue) return 1; + else return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((targetValue-queryValue)/stddev, 2)); + } +}; + +BR_REGISTER(Distance, AttributeDistance) + +} // namespace br + +#include "attribute.moc" diff --git a/openbr/plugins/distance/byteL1.cpp b/openbr/plugins/distance/byteL1.cpp new file mode 100644 index 0000000..4457999 --- /dev/null +++ b/openbr/plugins/distance/byteL1.cpp @@ -0,0 +1,26 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Fast 8-bit L1 distance + * \author Josh Klontz \cite jklontz + */ +class ByteL1Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const unsigned char *a, const unsigned char *b, size_t size) const + { + return l1(a, b, size); + } +}; + +BR_REGISTER(Distance, ByteL1Distance) + +} // namespace br + +#include "byteL1.moc" diff --git a/openbr/plugins/distance/default.cpp b/openbr/plugins/distance/default.cpp new file mode 100644 index 0000000..1409145 --- /dev/null +++ b/openbr/plugins/distance/default.cpp @@ -0,0 +1,31 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief DistDistance wrapper. + * \author Josh Klontz \cite jklontz + */ +class DefaultDistance : public UntrainableDistance +{ + Q_OBJECT + Distance *distance; + + void init() + { + distance = Distance::make("Dist("+file.suffix()+")"); + } + + float compare(const cv::Mat &a, const cv::Mat &b) const + { + return distance->compare(a, b); + } +}; + +BR_REGISTER(Distance, DefaultDistance) + +} // namespace br + +#include "default.moc" diff --git a/openbr/plugins/distance/dist.cpp b/openbr/plugins/distance/dist.cpp new file mode 100644 index 0000000..1f36afc --- /dev/null +++ b/openbr/plugins/distance/dist.cpp @@ -0,0 +1,104 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Standard distance metrics + * \author Josh Klontz \cite jklontz + */ +class DistDistance : public UntrainableDistance +{ + Q_OBJECT + Q_ENUMS(Metric) + Q_PROPERTY(Metric metric READ get_metric WRITE set_metric RESET reset_metric STORED false) + Q_PROPERTY(bool negLogPlusOne READ get_negLogPlusOne WRITE set_negLogPlusOne RESET reset_negLogPlusOne STORED false) + +public: + /*!< */ + enum Metric { Correlation, + ChiSquared, + Intersection, + Bhattacharyya, + INF, + L1, + L2, + Cosine, + Dot}; + +private: + BR_PROPERTY(Metric, metric, L2) + BR_PROPERTY(bool, negLogPlusOne, true) + + float compare(const Mat &a, const Mat &b) const + { + if ((a.size != b.size) || + (a.type() != b.type())) + return -std::numeric_limits::max(); + +// TODO: this max value is never returned based on the switch / default + float result = std::numeric_limits::max(); + switch (metric) { + case Correlation: + return compareHist(a, b, CV_COMP_CORREL); + case ChiSquared: + result = compareHist(a, b, CV_COMP_CHISQR); + break; + case Intersection: + result = compareHist(a, b, CV_COMP_INTERSECT); + break; + case Bhattacharyya: + result = compareHist(a, b, CV_COMP_BHATTACHARYYA); + break; + case INF: + result = norm(a, b, NORM_INF); + break; + case L1: + result = norm(a, b, NORM_L1); + break; + case L2: + result = norm(a, b, NORM_L2); + break; + case Cosine: + return cosine(a, b); + case Dot: + return a.dot(b); + default: + qFatal("Invalid metric"); + } + + if (result != result) + qFatal("NaN result."); + + return negLogPlusOne ? -log(result+1) : result; + } + + static float cosine(const Mat &a, const Mat &b) + { + float dot = 0; + float magA = 0; + float magB = 0; + + for (int row=0; row(row,col); + const float query = b.at(row,col); + dot += target * query; + magA += target * target; + magB += query * query; + } + } + + return dot / (sqrt(magA)*sqrt(magB)); + } +}; + +BR_REGISTER(Distance, DistDistance) + +} // namespace br + +#include "dist.moc" diff --git a/openbr/plugins/distance/fuse.cpp b/openbr/plugins/distance/fuse.cpp new file mode 100644 index 0000000..782fe58 --- /dev/null +++ b/openbr/plugins/distance/fuse.cpp @@ -0,0 +1,101 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Fuses similarity scores across multiple matrices of compared templates + * \author Scott Klum \cite sklum + * \note Operation: Mean, sum, min, max are supported. + */ +class FuseDistance : public Distance +{ + Q_OBJECT + Q_ENUMS(Operation) + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + Q_PROPERTY(Operation operation READ get_operation WRITE set_operation RESET reset_operation STORED false) + Q_PROPERTY(QList weights READ get_weights WRITE set_weights RESET reset_weights STORED false) + + QList distances; + +public: + /*!< */ + enum Operation {Mean, Sum, Max, Min}; + +private: + BR_PROPERTY(QString, description, "L2") + BR_PROPERTY(Operation, operation, Mean) + BR_PROPERTY(QList, weights, QList()) + + void train(const TemplateList &src) + { + // Partition the templates by matrix + QList split; + for (int i=0; i partitionedSrc = src.partition(split); + + while (distances.size() < partitionedSrc.size()) + distances.append(make(description)); + + // Train on each of the partitions + for (int i=0; itrain(partitionedSrc[i]); + } + + float compare(const Template &a, const Template &b) const + { + if (a.size() != b.size()) qFatal("Comparison size mismatch"); + + QList scores; + for (int i=0; icompare(Template(a.file, a[i]),Template(b.file, b[i]))); + } + + switch (operation) { + case Mean: + return std::accumulate(scores.begin(),scores.end(),0.0)/(float)scores.size(); + break; + case Sum: + return std::accumulate(scores.begin(),scores.end(),0.0); + break; + case Min: + return *std::min_element(scores.begin(),scores.end()); + break; + case Max: + return *std::max_element(scores.begin(),scores.end()); + break; + default: + qFatal("Invalid operation."); + } + return 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, FuseDistance) + +} // namespace br + +#include "fuse.moc" diff --git a/openbr/plugins/distance/gallerycompare.cpp b/openbr/plugins/distance/gallerycompare.cpp new file mode 100644 index 0000000..d4aba79 --- /dev/null +++ b/openbr/plugins/distance/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 "gallerycompare.moc" diff --git a/openbr/plugins/distance/halfbyteL1.cpp b/openbr/plugins/distance/halfbyteL1.cpp new file mode 100644 index 0000000..6f89ebb --- /dev/null +++ b/openbr/plugins/distance/halfbyteL1.cpp @@ -0,0 +1,29 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Fast 4-bit L1 distance + * \author Josh Klontz \cite jklontz + */ +class HalfByteL1Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Mat &a, const Mat &b) const + { + return packed_l1(a.data, b.data, a.total()); + } +}; + +BR_REGISTER(Distance, HalfByteL1Distance) + + +} // namespace br + +#include "halfbyteL1.moc" diff --git a/openbr/plugins/distance/identical.cpp b/openbr/plugins/distance/identical.cpp new file mode 100644 index 0000000..ec36f24 --- /dev/null +++ b/openbr/plugins/distance/identical.cpp @@ -0,0 +1,31 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Returns \c true if the templates are identical, \c false otherwise. + * \author Josh Klontz \cite jklontz + */ +class IdenticalDistance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Mat &a, const Mat &b) const + { + const size_t size = a.total() * a.elemSize(); + if (size != b.total() * b.elemSize()) return 0; + for (size_t i=0; i + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV Key Point Matcher + * \author Josh Klontz \cite jklontz + */ +class KeyPointMatcherDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QString matcher READ get_matcher WRITE set_matcher RESET reset_matcher STORED false) + Q_PROPERTY(float maxRatio READ get_maxRatio WRITE set_maxRatio RESET reset_maxRatio STORED false) + BR_PROPERTY(QString, matcher, "BruteForce") + BR_PROPERTY(float, maxRatio, 0.8) + + Ptr descriptorMatcher; + + void init() + { + descriptorMatcher = DescriptorMatcher::create(matcher.toStdString()); + if (descriptorMatcher.empty()) + qFatal("Failed to create DescriptorMatcher: %s", qPrintable(matcher)); + } + + float compare(const Mat &a, const Mat &b) const + { + if ((a.rows < 2) || (b.rows < 2)) return 0; + + std::vector< std::vector > matches; + if (a.rows < b.rows) descriptorMatcher->knnMatch(a, b, matches, 2); + else descriptorMatcher->knnMatch(b, a, matches, 2); + + QList distances; + foreach (const std::vector &match, matches) { + if (match[0].distance / match[1].distance > maxRatio) continue; + distances.append(match[0].distance); + } + qSort(distances); + + float similarity = 0; + for (int i=0; i + +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief L1 distance computed using eigen. + * \author Josh Klontz \cite jklontz + */ +class L1Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const cv::Mat &a, const cv::Mat &b) const + { + const int size = a.rows * a.cols; + Eigen::Map aMap((float*)a.data, size); + Eigen::Map bMap((float*)b.data, size); + return (aMap-bMap).cwiseAbs().sum(); + } +}; + +BR_REGISTER(Distance, L1Distance) + +} // namespace br + +#include "L1.moc" diff --git a/openbr/plugins/distance/neglogplusone.cpp b/openbr/plugins/distance/neglogplusone.cpp new file mode 100644 index 0000000..ded2dfc --- /dev/null +++ b/openbr/plugins/distance/neglogplusone.cpp @@ -0,0 +1,42 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Returns -log(distance(a,b)+1) + * \author Josh Klontz \cite jklontz + */ +class NegativeLogPlusOneDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + BR_PROPERTY(br::Distance*, distance, NULL) + + void train(const TemplateList &src) + { + distance->train(src); + } + + float compare(const Template &a, const Template &b) const + { + return -log(distance->compare(a,b)+1); + } + + void store(QDataStream &stream) const + { + distance->store(stream); + } + + void load(QDataStream &stream) + { + distance->load(stream); + } +}; + +BR_REGISTER(Distance, NegativeLogPlusOneDistance) + +} // namespace br + +#include "neglogplusone.moc" diff --git a/openbr/plugins/distance/online.cpp b/openbr/plugins/distance/online.cpp new file mode 100644 index 0000000..1880e68 --- /dev/null +++ b/openbr/plugins/distance/online.cpp @@ -0,0 +1,35 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Online distance metric to attenuate match scores across multiple frames + * \author Brendan klare \cite bklare + */ +class OnlineDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) + BR_PROPERTY(br::Distance*, distance, NULL) + BR_PROPERTY(float, alpha, 0.1f) + + mutable QHash scoreHash; + mutable QMutex mutex; + + float compare(const Template &target, const Template &query) const + { + float currentScore = distance->compare(target, query); + + QMutexLocker mutexLocker(&mutex); + return scoreHash[target.file.name] = (1.0- alpha) * scoreHash[target.file.name] + alpha * currentScore; + } +}; + +BR_REGISTER(Distance, OnlineDistance) + +} // namespace br + +#include "online.moc" diff --git a/openbr/plugins/distance/pipe.cpp b/openbr/plugins/distance/pipe.cpp new file mode 100644 index 0000000..a6cc9c0 --- /dev/null +++ b/openbr/plugins/distance/pipe.cpp @@ -0,0 +1,47 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Distances in series. + * \author Josh Klontz \cite jklontz + * + * The templates are compared using each br::Distance in order. + * If the result of the comparison with any given distance is -FLOAT_MAX then this result is returned early. + * Otherwise the returned result is the value of comparing the templates using the last br::Distance. + */ +class PipeDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) + BR_PROPERTY(QList, distances, QList()) + + void train(const TemplateList &data) + { + QFutureSynchronizer futures; + foreach (br::Distance *distance, distances) + futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); + futures.waitForFinished(); + } + + float compare(const Template &a, const Template &b) const + { + float result = -std::numeric_limits::max(); + foreach (br::Distance *distance, distances) { + result = distance->compare(a, b); + if (result == -std::numeric_limits::max()) + return result; + } + return result; + } +}; + +BR_REGISTER(Distance, PipeDistance) + +} // namespace br + +#include "pipe.moc" diff --git a/openbr/plugins/distance/sum.cpp b/openbr/plugins/distance/sum.cpp new file mode 100644 index 0000000..6ab6b38 --- /dev/null +++ b/openbr/plugins/distance/sum.cpp @@ -0,0 +1,46 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Sum match scores across multiple distances + * \author Scott Klum \cite sklum + */ +class SumDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) + BR_PROPERTY(QList, distances, QList()) + + void train(const TemplateList &data) + { + QFutureSynchronizer futures; + foreach (br::Distance *distance, distances) + futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); + futures.waitForFinished(); + } + + float compare(const Template &target, const Template &query) const + { + float result = 0; + + foreach (br::Distance *distance, distances) { + result += distance->compare(target, query); + + if (result == -std::numeric_limits::max()) + return result; + } + + return result; + } +}; + +BR_REGISTER(Distance, SumDistance) + +} // namespace br + +#include "sum.moc" diff --git a/openbr/plugins/draw.cpp b/openbr/plugins/draw.cpp deleted file mode 100644 index 3dc6a9b..0000000 --- a/openbr/plugins/draw.cpp +++ /dev/null @@ -1,700 +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 -#include -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Renders metadata onto the image. - * - * The inPlace argument controls whether or not the image is cloned before the metadata is drawn. - * - * \author Josh Klontz \cite jklontz - */ -class DrawTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(bool verbose READ get_verbose WRITE set_verbose RESET reset_verbose STORED false) - Q_PROPERTY(bool points READ get_points WRITE set_points RESET reset_points STORED false) - Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) - Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) - Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) - Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) - Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false) - BR_PROPERTY(bool, verbose, false) - BR_PROPERTY(bool, points, true) - BR_PROPERTY(bool, rects, true) - BR_PROPERTY(bool, inPlace, false) - BR_PROPERTY(int, lineThickness, 1) - BR_PROPERTY(bool, named, true) - BR_PROPERTY(bool, location, true) - - void project(const Template &src, Template &dst) const - { - const Scalar color(0,255,0); - const Scalar verboseColor(255, 255, 0); - dst.m() = inPlace ? src.m() : src.m().clone(); - - if (points) { - const QList pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points()); - for (int i=0; i opener; - void project(const Template &src, Template &dst) const - { - dst = src; - - if (imgName.isEmpty() || targetName.isEmpty() || !dst.file.contains(imgName) || !dst.file.contains(targetName)) - return; - - QVariant temp = src.file.value(imgName); - cv::Mat im; - // is this a filename? - if (temp.canConvert()) { - QString im_name = temp.toString(); - Template temp_im; - opener->project(File(im_name), temp_im); - im = temp_im.m(); - } - // a cv::Mat ? - else if (temp.canConvert()) - im = src.file.get(imgName); - else - qDebug() << "Unrecognized property type " << imgName << "for" << src.file.name; - - // Location of detected face in source image - QRectF target_location = src.file.get(targetName); - - // match width with target region - qreal target_width = target_location.width(); - qreal current_width = im.cols; - qreal current_height = im.rows; - - qreal aspect_ratio = current_height / current_width; - qreal target_height = target_width * aspect_ratio; - - cv::resize(im, im, cv::Size(target_width, target_height)); - - // ROI used to maybe crop the matched image - cv::Rect clip_roi; - clip_roi.x = 0; - clip_roi.y = 0; - clip_roi.width = im.cols; - clip_roi.height= im.rows <= dst.m().rows ? im.rows : dst.m().rows; - - int half_width = src.m().cols / 2; - int out_x = 0; - - // place in the source image we will copy the matched image to. - cv::Rect target_roi; - bool left_side = false; - int width_adjust = 0; - // Place left - if (target_location.center().rx() > half_width) { - out_x = target_location.left() - im.cols; - if (out_x < 0) { - width_adjust = abs(out_x); - out_x = 0; - } - left_side = true; - } - // place right - else { - out_x = target_location.right(); - int high = out_x + im.cols; - if (high >= src.m().cols) { - width_adjust = abs(high - src.m().cols + 1); - } - } - - cv::Mat outIm; - if (width_adjust) - { - outIm.create(dst.m().rows, dst.m().cols + width_adjust, CV_8UC3); - memset(outIm.data, 127, outIm.rows * outIm.cols * outIm.channels()); - - Rect temp; - - if (left_side) - temp = Rect(abs(width_adjust), 0, dst.m().cols, dst.m().rows); - - else - temp = Rect(0, 0, dst.m().cols, dst.m().rows); - - dst.m().copyTo(outIm(temp)); - - } - else - outIm = dst.m(); - - if (clip_roi.height + target_location.top() >= outIm.rows) - { - clip_roi.height -= abs(outIm.rows - (clip_roi.height + target_location.top() )); - } - if (clip_roi.x + clip_roi.width >= im.cols) { - clip_roi.width -= abs(im.cols - (clip_roi.x + clip_roi.width + 1)); - if (clip_roi.width < 0) - clip_roi.width = 1; - } - - if (clip_roi.y + clip_roi.height >= im.rows) { - clip_roi.height -= abs(im.rows - (clip_roi.y + clip_roi.height + 1)); - } - if (clip_roi.x < 0) - clip_roi.x = 0; - if (clip_roi.y < 0) - clip_roi.y = 0; - - if (clip_roi.height < 0) - clip_roi.height = 0; - - if (clip_roi.width < 0) - clip_roi.width = 0; - - - if (clip_roi.y + clip_roi.height >= im.rows) - { - qDebug() << "Bad clip y" << clip_roi.y + clip_roi.height << im.rows; - } - if (clip_roi.x + clip_roi.width >= im.cols) - { - qDebug() << "Bad clip x" << clip_roi.x + clip_roi.width << im.cols; - } - - if (clip_roi.y < 0 || clip_roi.height < 0) - { - qDebug() << "bad clip y, low" << clip_roi.y << clip_roi.height; - qFatal("die"); - } - if (clip_roi.x < 0 || clip_roi.width < 0) - { - qDebug() << "bad clip x, low" << clip_roi.x << clip_roi.width; - qFatal("die"); - } - - target_roi.x = out_x; - target_roi.width = clip_roi.width; - target_roi.y = target_location.top(); - target_roi.height = clip_roi.height; - - - im = im(clip_roi); - - if (target_roi.x < 0 || target_roi.x >= outIm.cols) - { - qDebug() << "Bad xdim in targetROI!" << target_roi.x << " out im x: " << outIm.cols; - qFatal("die"); - } - - if (target_roi.x + target_roi.width < 0 || (target_roi.x + target_roi.width) >= outIm.cols) - { - qDebug() << "Bad xdim in targetROI!" << target_roi.x + target_roi.width; - qFatal("die"); - } - - if (target_roi.y < 0 || target_roi.y >= outIm.rows) - { - qDebug() << "Bad ydim in targetROI!" << target_roi.y; - qFatal("die"); - } - - if ((target_roi.y + target_roi.height) < 0 || (target_roi.y + target_roi.height) > outIm.rows) - { - qDebug() << "Bad ydim in targetROI!" << target_roi.y + target_roi.height; - qDebug() << "target_roi.y: " << target_roi.y << " height: " << target_roi.height; - qFatal("die"); - } - - - std::vector channels; - cv::split(outIm, channels); - - std::vector patch_channels; - cv::split(im, patch_channels); - - for (size_t i=0; i < channels.size(); i++) - { - cv::addWeighted(channels[i](target_roi), 0, patch_channels[i % patch_channels.size()], 1, 0,channels[i](target_roi)); - } - cv::merge(channels, outIm); - dst.m() = outIm; - - } - - void init() - { - opener = QSharedPointer(br::Transform::make("Cache(Open)", NULL)); - } - -}; - -BR_REGISTER(Transform, AdjacentOverlayTransform) - -/*! - * \ingroup transforms - * \brief Draw a line representing the direction and magnitude of optical flow at the specified points. - * \author Austin Blanton \cite imaus10 - */ -class DrawOpticalFlow : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString original READ get_original WRITE set_original RESET reset_original STORED false) - BR_PROPERTY(QString, original, "original") - - void project(const Template &src, Template &dst) const - { - const Scalar color(0,255,0); - Mat flow = src.m(); - dst = src; - if (!dst.file.contains(original)) qFatal("The original img must be saved in the metadata with SaveMat."); - dst.m() = dst.file.get(original); - dst.file.remove(original); - foreach (const Point2f &pt, OpenCVUtils::toPoints(dst.file.points())) { - Point2f dxy = flow.at(pt.y, pt.x); - Point2f newPt(pt.x+dxy.x, pt.y+dxy.y); - line(dst, pt, newPt, color); - } - } -}; -BR_REGISTER(Transform, DrawOpticalFlow) - -/*! - * \ingroup transforms - * \brief Fill in the segmentations or draw a line between intersecting segments. - * \author Austin Blanton \cite imaus10 - */ -class DrawSegmentation : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(bool fillSegment READ get_fillSegment WRITE set_fillSegment RESET reset_fillSegment STORED false) - BR_PROPERTY(bool, fillSegment, true) - - void project(const Template &src, Template &dst) const - { - if (!src.file.contains("SegmentsMask") || !src.file.contains("NumSegments")) qFatal("Must supply a Contours object in the metadata to drawContours."); - Mat segments = src.file.get("SegmentsMask"); - int numSegments = src.file.get("NumSegments"); - - dst.file = src.file; - Mat drawn = fillSegment ? Mat(segments.size(), CV_8UC3, Scalar::all(0)) : src.m(); - - for (int i=1; i > contours; - Scalar color(0,255,0); - findContours(mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); - drawContours(drawn, contours, -1, color); - } - } - - dst.m() = drawn; - } -}; -BR_REGISTER(Transform, DrawSegmentation) - -/*! - * \ingroup transforms - * \brief Write all mats to disk as images. - * \author Brendan Klare \cite bklare - */ -class WriteImageTransform : public TimeVaryingTransform -{ - Q_OBJECT - Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false) - Q_PROPERTY(QString imageName READ get_imageName WRITE set_imageName RESET reset_imageName STORED false) - Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false) - BR_PROPERTY(QString, outputDirectory, "Temp") - BR_PROPERTY(QString, imageName, "image") - BR_PROPERTY(QString, imgExtension, "jpg") - - int cnt; - - void init() { - cnt = 0; - if (! QDir(outputDirectory).exists()) - QDir().mkdir(outputDirectory); - } - - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - OpenCVUtils::saveImage(dst.m(), QString("%1/%2_%3.%4").arg(outputDirectory).arg(imageName).arg(cnt++, 5, 10, QChar('0')).arg(imgExtension)); - } - -}; -BR_REGISTER(Transform, WriteImageTransform) - - -/** - * @brief The MeanImageTransform class computes the average template/image - * and save the result as an encoded image. - */ -class MeanImageTransform : public TimeVaryingTransform -{ - Q_OBJECT - - Q_PROPERTY(QString imgname READ get_imgname WRITE set_imgname RESET reset_imgname STORED false) - Q_PROPERTY(QString ext READ get_ext WRITE set_ext RESET reset_ext STORED false) - - BR_PROPERTY(QString, imgname, "average") - BR_PROPERTY(QString, ext, "jpg") - - Mat average; - int cnt; - - void init() - { - cnt = 0; - } - - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - if (cnt == 0) { - if (src.m().channels() == 1) - average = Mat::zeros(dst.m().size(),CV_64FC1); - else if (src.m().channels() == 3) - average = Mat::zeros(dst.m().size(),CV_64FC3); - else - qFatal("Unsupported number of channels"); - } - - Mat temp; - if (src.m().channels() == 1) { - src.m().convertTo(temp, CV_64FC1); - average += temp; - } else if (src.m().channels() == 3) { - src.m().convertTo(temp, CV_64FC3); - average += temp; - } else - qFatal("Unsupported number of channels"); - - cnt++; - } - - virtual void finalize(TemplateList &output) - { - average /= float(cnt); - imwrite(QString("%1.%2").arg(imgname).arg(ext).toStdString(), average); - output = TemplateList(); - } - - -public: - MeanImageTransform() : TimeVaryingTransform(false, false) {} -}; - -BR_REGISTER(Transform, MeanImageTransform) - - -// TODO: re-implement EditTransform using Qt -#if 0 -/*! - * \ingroup transforms - * \brief Remove landmarks. - * \author Josh Klontz \cite jklontz - */ -class EditTransform : public UntrainableTransform -{ - Q_OBJECT - - Transform *draw; - static Template currentTemplate; - static QMutex currentTemplateLock; - - void init() - { - draw = make("Draw"); - Globals->setProperty("parallelism", "0"); // Can only work in single threaded mode - } - - void project(const Template &src, Template &dst) const - { - dst = src; - - if (Globals->parallelism) { - qWarning("Edit::project() only works in single threaded mode."); - return; - } - - currentTemplateLock.lock(); - currentTemplate = src; - OpenCVUtils::showImage(src, "Edit", false); - setMouseCallback("Edit", mouseCallback, (void*)this); - mouseEvent(0, 0, 0, 0); - waitKey(-1); - dst = currentTemplate; - currentTemplateLock.unlock(); - } - - static void mouseCallback(int event, int x, int y, int flags, void *userdata) - { - ((const EditTransform*)userdata)->mouseEvent(event, x, y, flags); - } - - void mouseEvent(int event, int x, int y, int flags) const - { - (void) event; - if (flags) { - QList rects = currentTemplate.file.rects(); - for (int i=rects.size()-1; i>=0; i--) - if (rects[i].contains(x,y)) - rects.removeAt(i); - currentTemplate.file.setRects(rects); - } - - Template temp; - draw->project(currentTemplate, temp); - OpenCVUtils::showImage(temp, "Edit", false); - } -}; - -Template EditTransform::currentTemplate; -QMutex EditTransform::currentTemplateLock; - -BR_REGISTER(Transform, EditTransform) -#endif - -} // namespace br - -#include "draw.moc" diff --git a/openbr/plugins/ebif.cpp b/openbr/plugins/ebif.cpp deleted file mode 100644 index 718a3b9..0000000 --- a/openbr/plugins/ebif.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include - -#include "openbr_internal.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Face Recognition Using Early Biologically Inspired Features - * Min Li (IBM China Research Lab, China), Nalini Ratha (IBM Watson Research Center, - * USA), Weihong Qian (IBM China Research Lab, China), Shenghua Bao (IBM China - * Research Lab, China), Zhong Su (IBM China Research Lab, China) - * \author Josh Klontz \cite jklontz - */ - -class EBIFTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int N READ get_N WRITE set_N RESET reset_N STORED false) // scales - Q_PROPERTY(int M READ get_M WRITE set_M RESET reset_M STORED false) // orientations - BR_PROPERTY(int, N, 6) - BR_PROPERTY(int, M, 9) - - QList orientations; - - void init() - { - for (int m=0; m > features; - foreach (Transform *orientation, orientations) { - // Compute the reponse wavelet response - Template response; - orientation->project(scales, response); - - // Pool for each two adjacent features - QList orientedFeatures; - for (int i=0; i localFeatures; localFeatures.reserve(2*M); - for (int m=0; m pool(const Mat &bottom, const Mat &top) const - { - QList features; - for (int i=0; i<=top.rows-3; i+=3) { - for (int j=0; j<=top.cols-3; j+=3) { - QList vals; vals.reserve(3*3 + 4*4); - - // Top values - for (int k=0; k<3; k++) { - const float *data = top.ptr(i+k, j); - for (int l=0; l<3; l++) - vals.append(data[l]); - } - - // Bottom values - for (int k=0; k<4; k++) { - const float *data = bottom.ptr(4*i/3+k, 4*j/3); - for (int l=0; l<4; l++) - vals.append(data[l]); - } - - double mean, stddev; - Common::MeanStdDev(vals, &mean, &stddev); - features.append(mean); - features.append(stddev); - } - } - - return features; - } -}; - -BR_REGISTER(Transform, EBIFTransform) - -} // namespace br - -#include "ebif.moc" diff --git a/openbr/plugins/eigen3.cpp b/openbr/plugins/eigen3.cpp index dfe4ba8..97bd90f 100644 --- a/openbr/plugins/eigen3.cpp +++ b/openbr/plugins/eigen3.cpp @@ -652,46 +652,6 @@ class SparseLDATransform : public Transform BR_REGISTER(Transform, SparseLDATransform) -/*! - * \ingroup distances - * \brief L1 distance computed using eigen. - * \author Josh Klontz \cite jklontz - */ -class L1Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const cv::Mat &a, const cv::Mat &b) const - { - const int size = a.rows * a.cols; - Eigen::Map aMap((float*)a.data, size); - Eigen::Map bMap((float*)b.data, size); - return (aMap-bMap).cwiseAbs().sum(); - } -}; - -BR_REGISTER(Distance, L1Distance) - -/*! - * \ingroup distances - * \brief L2 distance computed using eigen. - * \author Josh Klontz \cite jklontz - */ -class L2Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const cv::Mat &a, const cv::Mat &b) const - { - const int size = a.rows * a.cols; - Eigen::Map aMap((float*)a.data, size); - Eigen::Map bMap((float*)b.data, size); - return (aMap-bMap).squaredNorm(); - } -}; - -BR_REGISTER(Distance, L2Distance) - } // namespace br #include "eigen3.moc" diff --git a/openbr/plugins/eyes.cpp b/openbr/plugins/eyes.cpp deleted file mode 100644 index 5d3929d..0000000 --- a/openbr/plugins/eyes.cpp +++ /dev/null @@ -1,197 +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 "openbr_internal.h" -#include "openbr/core/opencvutils.h" - -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/fill.cpp b/openbr/plugins/fill.cpp deleted file mode 100644 index 1614337..0000000 --- a/openbr/plugins/fill.cpp +++ /dev/null @@ -1,126 +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" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Wraps OpenCV inpainting - * \author Josh Klontz \cite jklontz - */ -class InpaintTransform : public UntrainableTransform -{ - Q_OBJECT - Q_ENUMS(Method) - Q_PROPERTY(int radius READ get_radius WRITE set_radius RESET reset_radius STORED false) - Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) - -public: - /*!< */ - enum Method { NavierStokes = INPAINT_NS, - Telea = INPAINT_TELEA }; - -private: - BR_PROPERTY(int, radius, 1) - BR_PROPERTY(Method, method, NavierStokes) - Transform *cvtGray; - - void init() - { - cvtGray = make("Cvt(Gray)"); - } - - void project(const Template &src, Template &dst) const - { - inpaint(src, (*cvtGray)(src)<5, dst, radius, method); - } -}; - -BR_REGISTER(Transform, InpaintTransform) - -/*! - * \ingroup transforms - * \brief Fill 0 pixels with the mean of non-0 pixels. - * \author Josh Klontz \cite jklontz - */ -class MeanFillTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - dst = src.m().clone(); - dst.m().setTo(mean(dst, dst.m()!=0), dst.m()==0); - } -}; - -BR_REGISTER(Transform, MeanFillTransform) - -/*! - * \ingroup transforms - * \brief Fill black pixels with the specified color. - * \author Josh Klontz \cite jklontz - */ -class FloodTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int r READ get_r WRITE set_r RESET reset_r STORED false) - Q_PROPERTY(int g READ get_g WRITE set_g RESET reset_g STORED false) - Q_PROPERTY(int b READ get_b WRITE set_b RESET reset_b STORED false) - Q_PROPERTY(bool all READ get_all WRITE set_all RESET reset_all STORED false) - BR_PROPERTY(int, r, 0) - BR_PROPERTY(int, g, 0) - BR_PROPERTY(int, b, 0) - BR_PROPERTY(bool, all, false) - - void project(const Template &src, Template &dst) const - { - dst = src.m().clone(); - dst.m().setTo(Scalar(r, g, b), all ? Mat() : dst.m()==0); - } -}; - -BR_REGISTER(Transform, FloodTransform) - -/*! - * \ingroup transforms - * \brief Alpha-blend two matrices - * \author Josh Klontz \cite jklontz - */ -class BlendTransform : public UntrainableMetaTransform -{ - Q_OBJECT - Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) - BR_PROPERTY(float, alpha, 0.5) - - void project(const Template &src, Template &dst) const - { - if (src.size() != 2) qFatal("Expected two source matrices."); - addWeighted(src[0], alpha, src[1], 1-alpha, 0, dst); - } -}; - -BR_REGISTER(Transform, BlendTransform) - -} // namespace br - -#include "fill.moc" diff --git a/openbr/plugins/filter.cpp b/openbr/plugins/filter.cpp deleted file mode 100644 index 48fbe75..0000000 --- a/openbr/plugins/filter.cpp +++ /dev/null @@ -1,258 +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/tanh_sse.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Gamma correction - * \author Josh Klontz \cite jklontz - */ -class GammaTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false) - BR_PROPERTY(float, gamma, 0.2) - - Mat lut; - - void init() - { - lut.create(256, 1, CV_32FC1); - if (gamma == 0) for (int i=0; i<256; i++) lut.at(i,0) = log((float)i); - else for (int i=0; i<256; i++) lut.at(i,0) = pow(i, gamma); - } - - void project(const Template &src, Template &dst) const - { - if (src.m().depth() == CV_8U) LUT(src, lut, dst); - else pow(src, gamma, dst); - } -}; - -BR_REGISTER(Transform, GammaTransform) - -/*! - * \ingroup transforms - * \brief Gaussian blur - * \author Josh Klontz \cite jklontz - */ -class BlurTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) - Q_PROPERTY(bool ROI READ get_ROI WRITE set_ROI RESET reset_ROI STORED false) - BR_PROPERTY(float, sigma, 1) - BR_PROPERTY(bool, ROI, false) - - void project(const Template &src, Template &dst) const - { - if (!ROI) GaussianBlur(src, dst, Size(0,0), sigma); - else { - dst.m() = src.m(); - foreach (const QRectF &rect, src.file.rects()) { - Rect region(rect.x(), rect.y(), rect.width(), rect.height()); - Mat input = dst.m(); - Mat output = input.clone(); - GaussianBlur(input(region), output(region), Size(0,0), sigma); - dst.m() = output; - } - } - } -}; - -BR_REGISTER(Transform, BlurTransform) - -/*! - * \ingroup transforms - * \brief Difference of gaussians - * \author Josh Klontz \cite jklontz - */ -class DoGTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float sigma0 READ get_sigma0 WRITE set_sigma0 RESET reset_sigma0 STORED false) - Q_PROPERTY(float sigma1 READ get_sigma1 WRITE set_sigma1 RESET reset_sigma1 STORED false) - BR_PROPERTY(float, sigma0, 1) - BR_PROPERTY(float, sigma1, 2) - - Size ksize0, ksize1; - - static Size getKernelSize(double sigma) - { - // Inverts OpenCV's conversion from kernel size to sigma: - // sigma = ((ksize-1)*0.5 - 1)*0.3 + 0.8 - // See documentation for cv::getGaussianKernel() - int ksize = ((sigma - 0.8) / 0.3 + 1) * 2 + 1; - if (ksize % 2 == 0) ksize++; - return Size(ksize, ksize); - } - - void init() - { - ksize0 = getKernelSize(sigma0); - ksize1 = getKernelSize(sigma1); - } - - void project(const Template &src, Template &dst) const - { - Mat g0, g1; - GaussianBlur(src, g0, ksize0, 0); - GaussianBlur(src, g1, ksize1, 0); - subtract(g0, g1, dst); - } -}; - -BR_REGISTER(Transform, DoGTransform) - -/*! - * \ingroup transforms - * \brief Meyers, E.; Wolf, L. - * “Using biologically inspired features for face processing,” - * Int. Journal of Computer Vision, vol. 76, no. 1, pp 93–104, 2008. - * \author Scott Klum \cite sklum - */ - -class CSDNTransform : public UntrainableTransform -{ - Q_OBJECT - - Q_PROPERTY(float s READ get_s WRITE set_s RESET reset_s STORED false) - BR_PROPERTY(int, s, 16) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() != 1) qFatal("Expected single channel source matrix."); - - const int nRows = src.m().rows; - const int nCols = src.m().cols; - - Mat m; - src.m().convertTo(m, CV_32FC1); - - const int surround = s/2; - - for ( int i = 0; i < nRows; i++ ) { - for ( int j = 0; j < nCols; j++ ) { - int width = min( j+surround, nCols ) - max( 0, j-surround ); - int height = min( i+surround, nRows ) - max( 0, i-surround ); - - Rect_ ROI(max(0, j-surround), max(0, i-surround), width, height); - - Scalar_ avg = mean(m(ROI)); - - m.at(i,j) = m.at(i,j) - avg[0]; - } - } - - dst = m; - - } -}; - -BR_REGISTER(Transform, CSDNTransform) - -/*! - * \ingroup transforms - * \brief Xiaoyang Tan; Triggs, B.; - * "Enhanced Local Texture Feature Sets for Face Recognition Under Difficult Lighting Conditions," - * Image Processing, IEEE Transactions on , vol.19, no.6, pp.1635-1650, June 2010 - * \author Josh Klontz \cite jklontz - */ -class ContrastEqTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a STORED false) - Q_PROPERTY(float t READ get_t WRITE set_t RESET reset_t STORED false) - BR_PROPERTY(float, a, 1) - BR_PROPERTY(float, t, 0.1) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() != 1) qFatal("Expected single channel source matrix."); - - // Stage 1 - Mat stage1; - { - Mat abs_dst; - absdiff(src, Scalar(0), abs_dst); - Mat pow_dst; - pow(abs_dst, a, pow_dst); - float denominator = pow((float)mean(pow_dst)[0], 1.f/a); - src.m().convertTo(stage1, CV_32F, 1/denominator); - } - - // Stage 2 - Mat stage2; - { - Mat abs_dst; - absdiff(stage1, Scalar(0), abs_dst); - Mat min_dst; - min(abs_dst, t, min_dst); - Mat pow_dst; - pow(min_dst, a, pow_dst); - float denominator = pow((float)mean(pow_dst)[0], 1.f/a); - stage1.convertTo(stage2, CV_32F, 1/denominator); - } - - // Hyperbolic tangent - const int nRows = src.m().rows; - const int nCols = src.m().cols; - const float* p = (const float*)stage2.ptr(); - Mat m(nRows, nCols, CV_32FC1); - for (int i=0; i(i, j) = fast_tanh(p[i*nCols+j]); - // TODO: m.at(i, j) = t * fast_tanh(p[i*nCols+j] / t); - - dst = m; - } -}; - -BR_REGISTER(Transform, ContrastEqTransform) - -/*! - * \ingroup transforms - * \brief Raise each element to the specified power. - * \author Josh Klontz \cite jklontz - */ -class PowTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float power READ get_power WRITE set_power RESET reset_power STORED false) - Q_PROPERTY(bool preserveSign READ get_preserveSign WRITE set_preserveSign RESET reset_preserveSign STORED false) - BR_PROPERTY(float, power, 2) - BR_PROPERTY(bool, preserveSign, false) - - void project(const Template &src, Template &dst) const - { - pow(preserveSign ? abs(src) : src.m(), power, dst); - if (preserveSign) subtract(Scalar::all(0), dst, dst, src.m() < 0); - } -}; - -BR_REGISTER(Transform, PowTransform) - -} // namespace br - -#include "filter.moc" diff --git a/openbr/plugins/format.cpp b/openbr/plugins/format.cpp deleted file mode 100644 index 48eed7d..0000000 --- a/openbr/plugins/format.cpp +++ /dev/null @@ -1,975 +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 -#ifndef BR_EMBEDDED -#include -#endif // BR_EMBEDDED -#include -#include - -#include -#include -#include "openbr_internal.h" - -#include "openbr/core/bee.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/qtutils.h" - -using namespace cv; - -namespace br -{ - - /*! - * \ingroup formats - * \brief Read all frames of a video using OpenCV - * \author Charles Otto \cite caotto - */ -class videoFormat : public Format -{ - Q_OBJECT - -public: - Template read() const - { - if (!file.exists() ) - return Template(); - - VideoCapture videoSource(file.name.toStdString()); - videoSource.open(file.name.toStdString() ); - - - Template frames; - if (!videoSource.isOpened()) { - qWarning("video file open failed"); - return frames; - } - - bool open = true; - while (open) { - cv::Mat frame; - open = videoSource.read(frame); - if (!open) break; - - frames.append(cv::Mat()); - frames.back() = frame.clone(); - } - - return frames; - } - - void write(const Template &t) const - { - int fourcc = OpenCVUtils::getFourcc(); - VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size()); - - // Did we successfully open the output file? - if (!videoSink.isOpened() ) qFatal("Failed to open output file"); - - for (Template::const_iterator it = t.begin(); it!= t.end(); ++it) { - videoSink << *it; - } - } -}; - -BR_REGISTER(Format, videoFormat) - -/*! - * \ingroup formats - * \brief A simple binary matrix format. - * \author Josh Klontz \cite jklontz - * First 4 bytes indicate the number of rows. - * Second 4 bytes indicate the number of columns. - * The rest of the bytes are 32-bit floating data elements in row-major order. - */ -class binFormat : public Format -{ - Q_OBJECT - Q_PROPERTY(bool raw READ get_raw WRITE set_raw RESET reset_raw STORED false) - BR_PROPERTY(bool, raw, false) - - Template read() const - { - QByteArray data; - QtUtils::readFile(file, data); - if (raw) { - return Template(file, Mat(1, data.size(), CV_8UC1, data.data()).clone()); - } else { - return Template(file, Mat(((quint32*)data.data())[0], - ((quint32*)data.data())[1], - CV_32FC1, - data.data()+8).clone()); - } - } - - void write(const Template &t) const - { - QFile f(file); - QtUtils::touchDir(f); - if (!f.open(QFile::WriteOnly)) - qFatal("Failed to open %s for writing.", qPrintable(file)); - - Mat m; - if (!raw) { - if (t.m().type() != CV_32FC1) - t.m().convertTo(m, CV_32F); - else m = t.m(); - - if (m.channels() != 1) qFatal("Only supports single channel matrices."); - - f.write((const char *) &m.rows, 4); - f.write((const char *) &m.cols, 4); - } - else m = t.m(); - - qint64 rowSize = m.cols * sizeof(float); - for (int i=0; i < m.rows; i++) - { - f.write((const char *) m.row(i).data, rowSize); - } - f.close(); - } -}; - -BR_REGISTER(Format, binFormat) - -/*! - * \ingroup formats - * \brief Reads a comma separated value file. - * \author Josh Klontz \cite jklontz - */ -class csvFormat : public Format -{ - Q_OBJECT - - Template read() const - { - QFile f(file.name); - f.open(QFile::ReadOnly); - QStringList lines(QString(f.readAll()).split(QRegularExpression("[\n|\r\n|\r]"), QString::SkipEmptyParts)); - f.close(); - - bool isUChar = true; - QList< QList > valsList; - foreach (const QString &line, lines) { - QList vals; - foreach (const QString &word, line.split(QRegExp(" *, *"), QString::SkipEmptyParts)) { - bool ok; - const float val = word.toFloat(&ok); - vals.append(val); - isUChar = isUChar && (val == float(uchar(val))); - } - if (!vals.isEmpty()) - valsList.append(vals); - } - - Mat m(valsList.size(), valsList[0].size(), CV_32FC1); - for (int i=0; i(i,j) = valsList[i][j]; - - if (isUChar) m.convertTo(m, CV_8U); - return Template(m); - } - - void write(const Template &t) const - { - const Mat &m = t.m(); - if (t.size() != 1) qFatal("Only supports single matrix templates."); - if (m.channels() != 1) qFatal("Only supports single channel matrices."); - - QStringList lines; lines.reserve(m.rows); - for (int r=0; r::names().contains("url")) { - File urlFile = file; - urlFile.name.append(".url"); - QScopedPointer url(Factory::make(urlFile)); - t = url->read(); - } - } else { - Mat m = imread(file.resolved().toStdString()); - if (m.data) { - t.append(m); - } else { - videoFormat videoReader; - videoReader.file = file; - t = videoReader.read(); - } - } - - return t; - } - - void write(const Template &t) const - { - if (t.size() > 1) { - videoFormat videoWriter; - videoWriter.file = file; - videoWriter.write(t); - } else if (t.size() == 1) { - QtUtils::touchDir(QDir(file.path())); - imwrite(file.name.toStdString(), t); - } - } -}; - -BR_REGISTER(Format, DefaultFormat) - -/*! - * \ingroup formats - * \brief Reads a NIST LFFS file. - * \author Josh Klontz \cite jklontz - */ -class lffsFormat : public Format -{ - Q_OBJECT - - Template read() const - { - QByteArray byteArray; - QtUtils::readFile(file.name, byteArray); - return Mat(1, byteArray.size(), CV_8UC1, byteArray.data()).clone(); - } - - void write(const Template &t) const - { - QByteArray byteArray((const char*)t.m().data, t.m().total()*t.m().elemSize()); - QtUtils::writeFile(file.name, byteArray); - } -}; - -BR_REGISTER(Format, lffsFormat) - -/*! - * \ingroup formats - * \brief Reads a NIST BEE similarity matrix. - * \author Josh Klontz \cite jklontz - */ -class mtxFormat : public Format -{ - Q_OBJECT - - Template read() const - { - QString target, query; - Template result = BEE::readMatrix(file, &target, &query); - result.file.set("Target", target); - result.file.set("Query", query); - return result; - } - - void write(const Template &t) const - { - BEE::writeMatrix(t, file); - } -}; - -BR_REGISTER(Format, mtxFormat) - -/*! - * \ingroup formats - * \brief Reads a NIST BEE mask matrix. - * \author Josh Klontz \cite jklontz - */ -class maskFormat : public mtxFormat -{ - Q_OBJECT -}; - -BR_REGISTER(Format, maskFormat) - -/*! - * \ingroup formats - * \brief MATLAB .mat format. - * \author Josh Klontz \cite jklontz - * http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf - * \note matFormat is known not to work with compressed matrices - */ -class matFormat : public Format -{ - Q_OBJECT - - struct Element - { - // It is always best to cast integers to a Qt integer type, such as qint16 or quint32, when reading and writing. - // This ensures that you always know exactly what size integers you are reading and writing, no matter what the - // underlying platform and architecture the application happens to be running on. - // http://qt-project.org/doc/qt-4.8/datastreamformat.html - quint32 type, bytes; - QByteArray data; - Element() : type(0), bytes(0) {} - Element(QDataStream &stream) - : type(0), bytes(0) - { - // Read first 4 bytes into type (32 bit integer), - // specifying the type of data used - if (stream.readRawData((char*)&type, 4) != 4) - qFatal("Unexpected end of file."); - - if (type >= 1 << 16) { - // Small data format - bytes = type; - type = type & 0x0000FFFF; - bytes = bytes >> 16; - } else { - // Regular format - // Read 4 bytes into bytes (32 bit integer), - // specifying the size of the element - if (stream.readRawData((char*)&bytes, 4) != 4) - qFatal("Unexpected end of file."); - } - - // Set the size of data to bytes - data.resize(bytes); - - // Read bytes amount of data from the file into data - if (int(bytes) != stream.readRawData(data.data(), bytes)) - qFatal("Unexpected end of file."); - - // Alignment - int skipBytes = (bytes < 4) ? (4 - bytes) : (8 - bytes%8)%8; - if (skipBytes != 0) stream.skipRawData(skipBytes); - } - }; - - Template read() const - { - QByteArray byteArray; - QtUtils::readFile(file, byteArray); - QDataStream f(byteArray); - - { // Check header - QByteArray header(128, 0); - f.readRawData(header.data(), 128); - if (!header.startsWith("MATLAB 5.0 MAT-file")) - qFatal("Invalid MAT header."); - } - - Template t(file); - - while (!f.atEnd()) { - Element element(f); - - // miCOMPRESSED - if (element.type == 15) { - // Prepend the number of bytes to element.data - element.data.prepend((char*)&element.bytes, 4); // Qt zlib wrapper requires this to preallocate the buffer - QDataStream uncompressed(qUncompress(element.data)); - element = Element(uncompressed); - } - - // miMATRIX - if (element.type == 14) { - QDataStream matrix(element.data); - qint32 rows = 0, columns = 0; - int matrixType = 0; - QByteArray matrixData; - while (!matrix.atEnd()) { - Element subelement(matrix); - if (subelement.type == 5) { // Dimensions array - if (subelement.bytes == 8) { - rows = ((qint32*)subelement.data.data())[0]; - columns = ((qint32*)subelement.data.data())[1]; - } else { - qWarning("matFormat::read can only handle 2D arrays."); - } - } else if (subelement.type == 7) { //miSINGLE - matrixType = CV_32FC1; - matrixData = subelement.data; - } else if (subelement.type == 9) { //miDOUBLE - matrixType = CV_64FC1; - matrixData = subelement.data; - } - } - - if ((rows > 0) && (columns > 0) && (matrixType != 0) && !matrixData.isEmpty()) { - Mat transposed; - transpose(Mat(columns, rows, matrixType, matrixData.data()), transposed); - t.append(transposed); - } - } - } - - return t; - } - - void write(const Template &t) const - { - QByteArray data; - QDataStream stream(&data, QFile::WriteOnly); - - { // Header - QByteArray header = "MATLAB 5.0 MAT-file; Made with OpenBR | www.openbiometrics.org\n"; - QByteArray buffer(116-header.size(), 0); - stream.writeRawData(header.data(), header.size()); - stream.writeRawData(buffer.data(), buffer.size()); - quint64 subsystem = 0; - quint16 version = 0x0100; - const char *endianness = "IM"; - stream.writeRawData((const char*)&subsystem, 8); - stream.writeRawData((const char*)&version, 2); - stream.writeRawData(endianness, 2); - } - - for (int i=0; i > imageSizes; // QHash > - - Template read() const - { - QString path = file.path(); - if (!imageSizes.contains(path)) { - static QMutex mutex; - QMutexLocker locker(&mutex); - - if (!imageSizes.contains(path)) { - const QString imageSize = path+"/ImageSize.txt"; - QStringList lines; - if (QFileInfo(imageSize).exists()) { - lines = QtUtils::readLines(imageSize); - lines.removeFirst(); // Remove header - } - - QHash sizes; - QRegExp whiteSpace("\\s+"); - foreach (const QString &line, lines) { - QStringList words = line.split(whiteSpace); - if (words.size() != 3) continue; - sizes.insert(words[0], QSize(words[2].toInt(), words[1].toInt())); - } - - imageSizes.insert(path, sizes); - } - } - - QByteArray data; - QtUtils::readFile(file, data); - - QSize size = imageSizes[path][file.baseName()]; - if (!size.isValid()) size = QSize(800,768); - if (data.size() != size.width() * size.height()) - qFatal("Expected %d*%d bytes, got %d.", size.height(), size.width(), data.size()); - return Template(file, Mat(size.height(), size.width(), CV_8UC1, data.data()).clone()); - } - - void write(const Template &t) const - { - QtUtils::writeFile(file, QByteArray().setRawData((const char*)t.m().data, t.m().total() * t.m().elemSize())); - } -}; - -QHash > rawFormat::imageSizes; - -BR_REGISTER(Format, rawFormat) - -/*! - * \ingroup formats - * \brief Retrieves an image from a webcam. - * \author Josh Klontz \cite jklontz - */ -class webcamFormat : public Format -{ - Q_OBJECT - - Template read() const - { - static QScopedPointer videoCapture; - - if (videoCapture.isNull()) - videoCapture.reset(new VideoCapture(0)); - - Mat m; - videoCapture->read(m); - return Template(m); - } - - void write(const Template &t) const - { - (void) t; - qFatal("Not supported."); - } -}; - -BR_REGISTER(Format, webcamFormat) - -/*! - * \ingroup formats - * \brief Decodes images from Base64 xml - * \author Scott Klum \cite sklum - * \author Josh Klontz \cite jklontz - */ -class xmlFormat : public Format -{ - Q_OBJECT - - Template read() const - { - Template t; - -#ifndef BR_EMBEDDED - QString fileName = file.get("path") + file.name; - - QDomDocument doc(fileName); - QFile f(fileName); - - if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); - if (!doc.setContent(&f)) qWarning("Unable to parse %s.", qPrintable(file.flat())); - f.close(); - - QDomElement docElem = doc.documentElement(); - QDomNode subject = docElem.firstChild(); - while (!subject.isNull()) { - QDomNode fileNode = subject.firstChild(); - - while (!fileNode.isNull()) { - QDomElement e = fileNode.toElement(); - - if (e.tagName() == "FORMAL_IMG") { - QByteArray byteArray = QByteArray::fromBase64(qPrintable(e.text())); - Mat m = imdecode(Mat(3, byteArray.size(), CV_8UC3, byteArray.data()), CV_LOAD_IMAGE_COLOR); - if (!m.data) qWarning("xmlFormat::read failed to decode image data."); - t.append(m); - } else if ((e.tagName() == "RELEASE_IMG") || - (e.tagName() == "PREBOOK_IMG") || - (e.tagName() == "LPROFILE") || - (e.tagName() == "RPROFILE")) { - // Ignore these other image fields for now - } else { - t.file.set(e.tagName(), e.text()); - } - - fileNode = fileNode.nextSibling(); - } - subject = subject.nextSibling(); - } - - // Calculate age - if (t.file.contains("DOB")) { - const QDate dob = QDate::fromString(t.file.get("DOB").left(10), "yyyy-MM-dd"); - const QDate current = QDate::currentDate(); - int age = current.year() - dob.year(); - if (current.month() < dob.month()) age--; - t.file.set("Age", age); - } -#endif // BR_EMBEDDED - - return t; - } - - void write(const Template &t) const - { - QStringList lines; - lines.append(""); - lines.append(""); - lines.append("\t"); - foreach (const QString &key, t.file.localKeys()) { - if ((key == "Index") || (key == "Label")) continue; - lines.append("\t\t<"+key+">"+QtUtils::toString(t.file.value(key))+""); - } - std::vector data; - imencode(".jpg",t.m(),data); - QByteArray byteArray = QByteArray::fromRawData((const char*)data.data(), data.size()); - lines.append("\t\t"+byteArray.toBase64()+""); - lines.append("\t"); - lines.append(""); - QtUtils::writeFile(file, lines); - } -}; - -BR_REGISTER(Format, xmlFormat) - -/*! - * \ingroup formats - * \brief Reads in scores or ground truth from a text table. - * \author Josh Klontz \cite jklontz - * - * Example of the format: - * \code - * 2.2531514 FALSE 99990377 99990164 - * 2.2549822 TRUE 99990101 99990101 - * \endcode - */ -class scoresFormat : public Format -{ - Q_OBJECT - Q_PROPERTY(int column READ get_column WRITE set_column RESET reset_column STORED false) - Q_PROPERTY(bool groundTruth READ get_groundTruth WRITE set_groundTruth RESET reset_groundTruth STORED false) - Q_PROPERTY(QString delimiter READ get_delimiter WRITE set_delimiter RESET reset_delimiter STORED false) - BR_PROPERTY(int, column, 0) - BR_PROPERTY(bool, groundTruth, false) - BR_PROPERTY(QString, delimiter, "\t") - - Template read() const - { - QFile f(file.name); - if (!f.open(QFile::ReadOnly | QFile::Text)) - qFatal("Failed to open %s for reading.", qPrintable(f.fileName())); - QList values; - while (!f.atEnd()) { - const QStringList words = QString(f.readLine()).split(delimiter); - if (words.size() <= column) qFatal("Expected file to have at least %d columns.", column+1); - const QString &word = words[column]; - bool ok; - float value = word.toFloat(&ok); - if (!ok) value = (QtUtils::toBool(word) ? BEE::Match : BEE::NonMatch); - values.append(value); - } - if (values.size() == 1) - qWarning("Only one value read, double check file line endings."); - Mat result = OpenCVUtils::toMat(values); - if (groundTruth) result.convertTo(result, CV_8U); - return result; - } - - void write(const Template &t) const - { - (void) t; - qFatal("Not implemented."); - } -}; - -BR_REGISTER(Format, scoresFormat) - -/*! - * \ingroup formats - * \brief Reads FBI EBTS transactions. - * \author Scott Klum \cite sklum - * https://www.fbibiospecs.org/ebts.html - */ -class ebtsFormat : public Format -{ - Q_OBJECT - - struct Field { - int type; - QList data; - }; - - struct Record { - int type; - quint32 bytes; - int position; // Starting position of record - - QHash > fields; - }; - - quint32 recordBytes(const QByteArray &byteArray, const float recordType, int from) const - { - bool ok; - quint32 size; - - if (recordType == 4 || recordType == 7) { - // read first four bytes - ok = true; - size = qFromBigEndian((const uchar*)byteArray.mid(from,4).constData()); - } else { - int index = byteArray.indexOf(QChar(0x1D), from); - size = byteArray.mid(from, index-from).split(':').last().toInt(&ok); - } - - return ok ? size : -1; - } - - void parseRecord(const QByteArray &byteArray, Record &record) const - { - if (record.type == 4 || record.type == 7) { - // Just a binary blob - // Read everything after the first four bytes - // Not current supported - } else { - // Continue reading fields until we get all the data - unsigned int position = record.position; - while (position < record.position + record.bytes) { - int index = byteArray.indexOf(QChar(0x1D), position); - Field field = parseField(byteArray.mid(position, index-position),QChar(0x1F)); - if (field.type == 999 ) { - // Data begin after the field identifier and the colon - int dataBegin = byteArray.indexOf(':', position)+1; - field.data.clear(); - field.data.append(byteArray.mid(dataBegin, record.bytes-(dataBegin-record.position))); - - // Data fields are always last in the record - record.fields.insert(field.type,field.data); - break; - } - // Advance the position accounting for the separator - position += index-position+1; - record.fields.insert(field.type,field.data); - } - } - } - - Field parseField(const QByteArray &byteArray, const QChar &sep) const - { - bool ok; - Field f; - - QList data = byteArray.split(':'); - - f.type = data.first().split('.').last().toInt(&ok); - f.data = data.last().split(sep.toLatin1()); - - return f; - } - - Template read() const - { - QByteArray byteArray; - QtUtils::readFile(file, byteArray); - - Template t; - - Mat m; - - QList records; - - // Read the type one record (every EBTS file will have one of these) - Record r1; - r1.type = 1; - r1.position = 0; - r1.bytes = recordBytes(byteArray,r1.type,r1.position); - - // The fields in a type 1 record are strictly defined - QList data = byteArray.mid(r1.position,r1.bytes).split(QChar(0x1D).toLatin1()); - foreach (const QByteArray &datum, data) { - Field f = parseField(datum,QChar(0x1F)); - r1.fields.insert(f.type,f.data); - } - - records.append(r1); - - // Read the type two record (every EBTS file will have one of these) - Record r2; - r2.type = 2; - r2.position = r1.bytes; - r2.bytes = recordBytes(byteArray,r2.type,r2.position); - - // The fields in a type 2 record are strictly defined - data = byteArray.mid(r2.position,r2.bytes).split(QChar(0x1D).toLatin1()); - foreach (const QByteArray &datum, data) { - Field f = parseField(datum,QChar(0x1F)); - r2.fields.insert(f.type,f.data); - } - - // Demographics - if (r2.fields.contains(18)) { - QString name = r2.fields.value(18).first(); - QStringList names = name.split(','); - t.file.set("FIRSTNAME", names.at(1)); - t.file.set("LASTNAME", names.at(0)); - } - - if (r2.fields.contains(22)) t.file.set("DOB", r2.fields.value(22).first().toInt()); - if (r2.fields.contains(24)) t.file.set("GENDER", QString(r2.fields.value(24).first())); - if (r2.fields.contains(25)) t.file.set("RACE", QString(r2.fields.value(25).first())); - - if (t.file.contains("DOB")) { - const QDate dob = QDate::fromString(t.file.get("DOB"), "yyyyMMdd"); - const QDate current = QDate::currentDate(); - int age = current.year() - dob.year(); - if (current.month() < dob.month()) age--; - t.file.set("Age", age); - } - - records.append(r2); - - // The third field of the first record contains informations about all the remaining records in the transaction - // We don't care about the first two and the final items - QList recordTypes = r1.fields.value(3); - for (int i=2; i frontalIdxs; - int position = r1.bytes + r2.bytes; - for (int i=2; i +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief A simple binary matrix format. + * \author Josh Klontz \cite jklontz + * First 4 bytes indicate the number of rows. + * Second 4 bytes indicate the number of columns. + * The rest of the bytes are 32-bit floating data elements in row-major order. + */ +class binaryFormat : public Format +{ + Q_OBJECT + Q_PROPERTY(bool raw READ get_raw WRITE set_raw RESET reset_raw STORED false) + BR_PROPERTY(bool, raw, false) + + Template read() const + { + QByteArray data; + QtUtils::readFile(file, data); + if (raw) { + return Template(file, Mat(1, data.size(), CV_8UC1, data.data()).clone()); + } else { + return Template(file, Mat(((quint32*)data.data())[0], + ((quint32*)data.data())[1], + CV_32FC1, + data.data()+8).clone()); + } + } + + void write(const Template &t) const + { + QFile f(file); + QtUtils::touchDir(f); + if (!f.open(QFile::WriteOnly)) + qFatal("Failed to open %s for writing.", qPrintable(file)); + + Mat m; + if (!raw) { + if (t.m().type() != CV_32FC1) + t.m().convertTo(m, CV_32F); + else m = t.m(); + + if (m.channels() != 1) qFatal("Only supports single channel matrices."); + + f.write((const char *) &m.rows, 4); + f.write((const char *) &m.cols, 4); + } + else m = t.m(); + + qint64 rowSize = m.cols * sizeof(float); + for (int i=0; i < m.rows; i++) + { + f.write((const char *) m.row(i).data, rowSize); + } + f.close(); + } +}; + +BR_REGISTER(Format, binaryFormat) + +} // namespace br + +#include "binary.moc" diff --git a/openbr/plugins/format/csv.cpp b/openbr/plugins/format/csv.cpp new file mode 100644 index 0000000..ccede30 --- /dev/null +++ b/openbr/plugins/format/csv.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads a comma separated value file. + * \author Josh Klontz \cite jklontz + */ +class csvFormat : public Format +{ + Q_OBJECT + + Template read() const + { + QFile f(file.name); + f.open(QFile::ReadOnly); + QStringList lines(QString(f.readAll()).split(QRegularExpression("[\n|\r\n|\r]"), QString::SkipEmptyParts)); + f.close(); + + bool isUChar = true; + QList< QList > valsList; + foreach (const QString &line, lines) { + QList vals; + foreach (const QString &word, line.split(QRegExp(" *, *"), QString::SkipEmptyParts)) { + bool ok; + const float val = word.toFloat(&ok); + vals.append(val); + isUChar = isUChar && (val == float(uchar(val))); + } + if (!vals.isEmpty()) + valsList.append(vals); + } + + Mat m(valsList.size(), valsList[0].size(), CV_32FC1); + for (int i=0; i(i,j) = valsList[i][j]; + + if (isUChar) m.convertTo(m, CV_8U); + return Template(m); + } + + void write(const Template &t) const + { + const Mat &m = t.m(); + if (t.size() != 1) qFatal("Only supports single matrix templates."); + if (m.channels() != 1) qFatal("Only supports single channel matrices."); + + QStringList lines; lines.reserve(m.rows); + for (int r=0; r +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads FBI EBTS transactions. + * \author Scott Klum \cite sklum + * https://www.fbibiospecs.org/ebts.html + */ +class ebtsFormat : public Format +{ + Q_OBJECT + + struct Field { + int type; + QList data; + }; + + struct Record { + int type; + quint32 bytes; + int position; // Starting position of record + + QHash > fields; + }; + + quint32 recordBytes(const QByteArray &byteArray, const float recordType, int from) const + { + bool ok; + quint32 size; + + if (recordType == 4 || recordType == 7) { + // read first four bytes + ok = true; + size = qFromBigEndian((const uchar*)byteArray.mid(from,4).constData()); + } else { + int index = byteArray.indexOf(QChar(0x1D), from); + size = byteArray.mid(from, index-from).split(':').last().toInt(&ok); + } + + return ok ? size : -1; + } + + void parseRecord(const QByteArray &byteArray, Record &record) const + { + if (record.type == 4 || record.type == 7) { + // Just a binary blob + // Read everything after the first four bytes + // Not current supported + } else { + // Continue reading fields until we get all the data + unsigned int position = record.position; + while (position < record.position + record.bytes) { + int index = byteArray.indexOf(QChar(0x1D), position); + Field field = parseField(byteArray.mid(position, index-position),QChar(0x1F)); + if (field.type == 999 ) { + // Data begin after the field identifier and the colon + int dataBegin = byteArray.indexOf(':', position)+1; + field.data.clear(); + field.data.append(byteArray.mid(dataBegin, record.bytes-(dataBegin-record.position))); + + // Data fields are always last in the record + record.fields.insert(field.type,field.data); + break; + } + // Advance the position accounting for the separator + position += index-position+1; + record.fields.insert(field.type,field.data); + } + } + } + + Field parseField(const QByteArray &byteArray, const QChar &sep) const + { + bool ok; + Field f; + + QList data = byteArray.split(':'); + + f.type = data.first().split('.').last().toInt(&ok); + f.data = data.last().split(sep.toLatin1()); + + return f; + } + + Template read() const + { + QByteArray byteArray; + QtUtils::readFile(file, byteArray); + + Template t; + + Mat m; + + QList records; + + // Read the type one record (every EBTS file will have one of these) + Record r1; + r1.type = 1; + r1.position = 0; + r1.bytes = recordBytes(byteArray,r1.type,r1.position); + + // The fields in a type 1 record are strictly defined + QList data = byteArray.mid(r1.position,r1.bytes).split(QChar(0x1D).toLatin1()); + foreach (const QByteArray &datum, data) { + Field f = parseField(datum,QChar(0x1F)); + r1.fields.insert(f.type,f.data); + } + + records.append(r1); + + // Read the type two record (every EBTS file will have one of these) + Record r2; + r2.type = 2; + r2.position = r1.bytes; + r2.bytes = recordBytes(byteArray,r2.type,r2.position); + + // The fields in a type 2 record are strictly defined + data = byteArray.mid(r2.position,r2.bytes).split(QChar(0x1D).toLatin1()); + foreach (const QByteArray &datum, data) { + Field f = parseField(datum,QChar(0x1F)); + r2.fields.insert(f.type,f.data); + } + + // Demographics + if (r2.fields.contains(18)) { + QString name = r2.fields.value(18).first(); + QStringList names = name.split(','); + t.file.set("FIRSTNAME", names.at(1)); + t.file.set("LASTNAME", names.at(0)); + } + + if (r2.fields.contains(22)) t.file.set("DOB", r2.fields.value(22).first().toInt()); + if (r2.fields.contains(24)) t.file.set("GENDER", QString(r2.fields.value(24).first())); + if (r2.fields.contains(25)) t.file.set("RACE", QString(r2.fields.value(25).first())); + + if (t.file.contains("DOB")) { + const QDate dob = QDate::fromString(t.file.get("DOB"), "yyyyMMdd"); + const QDate current = QDate::currentDate(); + int age = current.year() - dob.year(); + if (current.month() < dob.month()) age--; + t.file.set("Age", age); + } + + records.append(r2); + + // The third field of the first record contains informations about all the remaining records in the transaction + // We don't care about the first two and the final items + QList recordTypes = r1.fields.value(3); + for (int i=2; i frontalIdxs; + int position = r1.bytes + r2.bytes; + for (int i=2; i +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads a NIST LFFS file. + * \author Josh Klontz \cite jklontz + */ +class lffsFormat : public Format +{ + Q_OBJECT + + Template read() const + { + QByteArray byteArray; + QtUtils::readFile(file.name, byteArray); + return Mat(1, byteArray.size(), CV_8UC1, byteArray.data()).clone(); + } + + void write(const Template &t) const + { + QByteArray byteArray((const char*)t.m().data, t.m().total()*t.m().elemSize()); + QtUtils::writeFile(file.name, byteArray); + } +}; + +BR_REGISTER(Format, lffsFormat) + +} // namespace br + +#include "lffs.moc" diff --git a/openbr/plugins/format/mat.cpp b/openbr/plugins/format/mat.cpp new file mode 100644 index 0000000..6e6e411 --- /dev/null +++ b/openbr/plugins/format/mat.cpp @@ -0,0 +1,227 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief MATLAB .mat format. + * \author Josh Klontz \cite jklontz + * http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf + * \note matFormat is known not to work with compressed matrices + */ +class matFormat : public Format +{ + Q_OBJECT + + struct Element + { + // It is always best to cast integers to a Qt integer type, such as qint16 or quint32, when reading and writing. + // This ensures that you always know exactly what size integers you are reading and writing, no matter what the + // underlying platform and architecture the application happens to be running on. + // http://qt-project.org/doc/qt-4.8/datastreamformat.html + quint32 type, bytes; + QByteArray data; + Element() : type(0), bytes(0) {} + Element(QDataStream &stream) + : type(0), bytes(0) + { + // Read first 4 bytes into type (32 bit integer), + // specifying the type of data used + if (stream.readRawData((char*)&type, 4) != 4) + qFatal("Unexpected end of file."); + + if (type >= 1 << 16) { + // Small data format + bytes = type; + type = type & 0x0000FFFF; + bytes = bytes >> 16; + } else { + // Regular format + // Read 4 bytes into bytes (32 bit integer), + // specifying the size of the element + if (stream.readRawData((char*)&bytes, 4) != 4) + qFatal("Unexpected end of file."); + } + + // Set the size of data to bytes + data.resize(bytes); + + // Read bytes amount of data from the file into data + if (int(bytes) != stream.readRawData(data.data(), bytes)) + qFatal("Unexpected end of file."); + + // Alignment + int skipBytes = (bytes < 4) ? (4 - bytes) : (8 - bytes%8)%8; + if (skipBytes != 0) stream.skipRawData(skipBytes); + } + }; + + Template read() const + { + QByteArray byteArray; + QtUtils::readFile(file, byteArray); + QDataStream f(byteArray); + + { // Check header + QByteArray header(128, 0); + f.readRawData(header.data(), 128); + if (!header.startsWith("MATLAB 5.0 MAT-file")) + qFatal("Invalid MAT header."); + } + + Template t(file); + + while (!f.atEnd()) { + Element element(f); + + // miCOMPRESSED + if (element.type == 15) { + // Prepend the number of bytes to element.data + element.data.prepend((char*)&element.bytes, 4); // Qt zlib wrapper requires this to preallocate the buffer + QDataStream uncompressed(qUncompress(element.data)); + element = Element(uncompressed); + } + + // miMATRIX + if (element.type == 14) { + QDataStream matrix(element.data); + qint32 rows = 0, columns = 0; + int matrixType = 0; + QByteArray matrixData; + while (!matrix.atEnd()) { + Element subelement(matrix); + if (subelement.type == 5) { // Dimensions array + if (subelement.bytes == 8) { + rows = ((qint32*)subelement.data.data())[0]; + columns = ((qint32*)subelement.data.data())[1]; + } else { + qWarning("matFormat::read can only handle 2D arrays."); + } + } else if (subelement.type == 7) { //miSINGLE + matrixType = CV_32FC1; + matrixData = subelement.data; + } else if (subelement.type == 9) { //miDOUBLE + matrixType = CV_64FC1; + matrixData = subelement.data; + } + } + + if ((rows > 0) && (columns > 0) && (matrixType != 0) && !matrixData.isEmpty()) { + Mat transposed; + transpose(Mat(columns, rows, matrixType, matrixData.data()), transposed); + t.append(transposed); + } + } + } + + return t; + } + + void write(const Template &t) const + { + QByteArray data; + QDataStream stream(&data, QFile::WriteOnly); + + { // Header + QByteArray header = "MATLAB 5.0 MAT-file; Made with OpenBR | www.openbiometrics.org\n"; + QByteArray buffer(116-header.size(), 0); + stream.writeRawData(header.data(), header.size()); + stream.writeRawData(buffer.data(), buffer.size()); + quint64 subsystem = 0; + quint16 version = 0x0100; + const char *endianness = "IM"; + stream.writeRawData((const char*)&subsystem, 8); + stream.writeRawData((const char*)&version, 2); + stream.writeRawData(endianness, 2); + } + + for (int i=0; i +#include + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads a NIST BEE similarity matrix. + * \author Josh Klontz \cite jklontz + */ +class mtxFormat : public Format +{ + Q_OBJECT + + Template read() const + { + QString target, query; + Template result = BEE::readMatrix(file, &target, &query); + result.file.set("Target", target); + result.file.set("Query", query); + return result; + } + + void write(const Template &t) const + { + BEE::writeMatrix(t, file); + } +}; + +BR_REGISTER(Format, mtxFormat) + +/*! + * \ingroup formats + * \brief Reads a NIST BEE mask matrix. + * \author Josh Klontz \cite jklontz + */ +class maskFormat : public mtxFormat +{ + Q_OBJECT +}; + +BR_REGISTER(Format, maskFormat) + +} // namespace br + +#include "mtx.moc" diff --git a/openbr/plugins/format/null.cpp b/openbr/plugins/format/null.cpp new file mode 100644 index 0000000..b5cbd38 --- /dev/null +++ b/openbr/plugins/format/null.cpp @@ -0,0 +1,30 @@ +#include + +namespace br +{ + +/*! + * \ingroup formats + * \brief Returns an empty matrix. + * \author Josh Klontz \cite jklontz + */ +class nullFormat : public Format +{ + Q_OBJECT + + Template read() const + { + return Template(file, cv::Mat()); + } + + void write(const Template &t) const + { + (void)t; + } +}; + +BR_REGISTER(Format, nullFormat) + +} // namespace br + +#include "null.moc" diff --git a/openbr/plugins/format/raw.cpp b/openbr/plugins/format/raw.cpp new file mode 100644 index 0000000..3cf868f --- /dev/null +++ b/openbr/plugins/format/raw.cpp @@ -0,0 +1,70 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief RAW format + * + * http://www.nist.gov/srd/nistsd27.cfm + * \author Josh Klontz \cite jklontz + */ +class rawFormat : public Format +{ + Q_OBJECT + static QHash > imageSizes; // QHash > + + Template read() const + { + QString path = file.path(); + if (!imageSizes.contains(path)) { + static QMutex mutex; + QMutexLocker locker(&mutex); + + if (!imageSizes.contains(path)) { + const QString imageSize = path+"/ImageSize.txt"; + QStringList lines; + if (QFileInfo(imageSize).exists()) { + lines = QtUtils::readLines(imageSize); + lines.removeFirst(); // Remove header + } + + QHash sizes; + QRegExp whiteSpace("\\s+"); + foreach (const QString &line, lines) { + QStringList words = line.split(whiteSpace); + if (words.size() != 3) continue; + sizes.insert(words[0], QSize(words[2].toInt(), words[1].toInt())); + } + + imageSizes.insert(path, sizes); + } + } + + QByteArray data; + QtUtils::readFile(file, data); + + QSize size = imageSizes[path][file.baseName()]; + if (!size.isValid()) size = QSize(800,768); + if (data.size() != size.width() * size.height()) + qFatal("Expected %d*%d bytes, got %d.", size.height(), size.width(), data.size()); + return Template(file, Mat(size.height(), size.width(), CV_8UC1, data.data()).clone()); + } + + void write(const Template &t) const + { + QtUtils::writeFile(file, QByteArray().setRawData((const char*)t.m().data, t.m().total() * t.m().elemSize())); + } +}; + +QHash > rawFormat::imageSizes; + +BR_REGISTER(Format, rawFormat) + +} // namespace br + +#include "raw.moc" diff --git a/openbr/plugins/format/scores.cpp b/openbr/plugins/format/scores.cpp new file mode 100644 index 0000000..453dffa --- /dev/null +++ b/openbr/plugins/format/scores.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads in scores or ground truth from a text table. + * \author Josh Klontz \cite jklontz + * + * Example of the format: + * \code + * 2.2531514 FALSE 99990377 99990164 + * 2.2549822 TRUE 99990101 99990101 + * \endcode + */ +class scoresFormat : public Format +{ + Q_OBJECT + Q_PROPERTY(int column READ get_column WRITE set_column RESET reset_column STORED false) + Q_PROPERTY(bool groundTruth READ get_groundTruth WRITE set_groundTruth RESET reset_groundTruth STORED false) + Q_PROPERTY(QString delimiter READ get_delimiter WRITE set_delimiter RESET reset_delimiter STORED false) + BR_PROPERTY(int, column, 0) + BR_PROPERTY(bool, groundTruth, false) + BR_PROPERTY(QString, delimiter, "\t") + + Template read() const + { + QFile f(file.name); + if (!f.open(QFile::ReadOnly | QFile::Text)) + qFatal("Failed to open %s for reading.", qPrintable(f.fileName())); + QList values; + while (!f.atEnd()) { + const QStringList words = QString(f.readLine()).split(delimiter); + if (words.size() <= column) qFatal("Expected file to have at least %d columns.", column+1); + const QString &word = words[column]; + bool ok; + float value = word.toFloat(&ok); + if (!ok) value = (QtUtils::toBool(word) ? BEE::Match : BEE::NonMatch); + values.append(value); + } + if (values.size() == 1) + qWarning("Only one value read, double check file line endings."); + Mat result = OpenCVUtils::toMat(values); + if (groundTruth) result.convertTo(result, CV_8U); + return result; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not implemented."); + } +}; + +BR_REGISTER(Format, scoresFormat) + +} // namespace br + +#include "scores.moc" diff --git a/openbr/plugins/format/video.cpp b/openbr/plugins/format/video.cpp new file mode 100644 index 0000000..0a1eaf8 --- /dev/null +++ b/openbr/plugins/format/video.cpp @@ -0,0 +1,147 @@ +#include + +#include +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Read all frames of a video using OpenCV + * \author Charles Otto \cite caotto + */ +class videoFormat : public Format +{ + Q_OBJECT + +public: + Template read() const + { + if (!file.exists() ) + return Template(); + + VideoCapture videoSource(file.name.toStdString()); + videoSource.open(file.name.toStdString() ); + + + Template frames; + if (!videoSource.isOpened()) { + qWarning("video file open failed"); + return frames; + } + + bool open = true; + while (open) { + cv::Mat frame; + open = videoSource.read(frame); + if (!open) break; + + frames.append(cv::Mat()); + frames.back() = frame.clone(); + } + + return frames; + } + + void write(const Template &t) const + { + int fourcc = OpenCVUtils::getFourcc(); + VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size()); + + // Did we successfully open the output file? + if (!videoSink.isOpened() ) qFatal("Failed to open output file"); + + for (Template::const_iterator it = t.begin(); it!= t.end(); ++it) { + videoSink << *it; + } + } +}; + +BR_REGISTER(Format, videoFormat) + +/*! + * \ingroup formats + * \brief Retrieves an image from a webcam. + * \author Josh Klontz \cite jklontz + */ +class webcamFormat : public Format +{ + Q_OBJECT + + Template read() const + { + static QScopedPointer videoCapture; + + if (videoCapture.isNull()) + videoCapture.reset(new VideoCapture(0)); + + Mat m; + videoCapture->read(m); + return Template(m); + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported."); + } +}; + +BR_REGISTER(Format, webcamFormat) + +/*! + * \ingroup formats + * \brief Reads image files. + * \author Josh Klontz \cite jklontz + */ +class DefaultFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t; + + if (file.name.startsWith("http://") || file.name.startsWith("https://") || file.name.startsWith("www.")) { + if (Factory::names().contains("url")) { + File urlFile = file; + urlFile.name.append(".url"); + QScopedPointer url(Factory::make(urlFile)); + t = url->read(); + } + } else { + Mat m = imread(file.resolved().toStdString()); + if (m.data) { + t.append(m); + } else { + videoFormat videoReader; + videoReader.file = file; + t = videoReader.read(); + } + } + + return t; + } + + void write(const Template &t) const + { + if (t.size() > 1) { + videoFormat videoWriter; + videoWriter.file = file; + videoWriter.write(t); + } else if (t.size() == 1) { + QtUtils::touchDir(QDir(file.path())); + imwrite(file.name.toStdString(), t); + } + } +}; + +BR_REGISTER(Format, DefaultFormat) + +} // namespace br + +#include "video.moc" diff --git a/openbr/plugins/format/xml.cpp b/openbr/plugins/format/xml.cpp new file mode 100644 index 0000000..8cf3dc2 --- /dev/null +++ b/openbr/plugins/format/xml.cpp @@ -0,0 +1,102 @@ +#ifndef BR_EMBEDDED +#include +#endif // BR_EMBEDDED +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Decodes images from Base64 xml + * \author Scott Klum \cite sklum + * \author Josh Klontz \cite jklontz + */ +class xmlFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t; + +#ifndef BR_EMBEDDED + QString fileName = file.get("path") + file.name; + + QDomDocument doc(fileName); + QFile f(fileName); + + if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); + if (!doc.setContent(&f)) qWarning("Unable to parse %s.", qPrintable(file.flat())); + f.close(); + + QDomElement docElem = doc.documentElement(); + QDomNode subject = docElem.firstChild(); + while (!subject.isNull()) { + QDomNode fileNode = subject.firstChild(); + + while (!fileNode.isNull()) { + QDomElement e = fileNode.toElement(); + + if (e.tagName() == "FORMAL_IMG") { + QByteArray byteArray = QByteArray::fromBase64(qPrintable(e.text())); + Mat m = imdecode(Mat(3, byteArray.size(), CV_8UC3, byteArray.data()), CV_LOAD_IMAGE_COLOR); + if (!m.data) qWarning("xmlFormat::read failed to decode image data."); + t.append(m); + } else if ((e.tagName() == "RELEASE_IMG") || + (e.tagName() == "PREBOOK_IMG") || + (e.tagName() == "LPROFILE") || + (e.tagName() == "RPROFILE")) { + // Ignore these other image fields for now + } else { + t.file.set(e.tagName(), e.text()); + } + + fileNode = fileNode.nextSibling(); + } + subject = subject.nextSibling(); + } + + // Calculate age + if (t.file.contains("DOB")) { + const QDate dob = QDate::fromString(t.file.get("DOB").left(10), "yyyy-MM-dd"); + const QDate current = QDate::currentDate(); + int age = current.year() - dob.year(); + if (current.month() < dob.month()) age--; + t.file.set("Age", age); + } +#endif // BR_EMBEDDED + + return t; + } + + void write(const Template &t) const + { + QStringList lines; + lines.append(""); + lines.append(""); + lines.append("\t"); + foreach (const QString &key, t.file.localKeys()) { + if ((key == "Index") || (key == "Label")) continue; + lines.append("\t\t<"+key+">"+QtUtils::toString(t.file.value(key))+""); + } + std::vector data; + imencode(".jpg",t.m(),data); + QByteArray byteArray = QByteArray::fromRawData((const char*)data.data(), data.size()); + lines.append("\t\t"+byteArray.toBase64()+""); + lines.append("\t"); + lines.append(""); + QtUtils::writeFile(file, lines); + } +}; + +BR_REGISTER(Format, xmlFormat) + +} // namespace br + +#include "xml.moc" diff --git a/openbr/plugins/frames.cpp b/openbr/plugins/frames.cpp deleted file mode 100644 index 100df34..0000000 --- a/openbr/plugins/frames.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "openbr_internal.h" - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Passes along n sequential frames to the next transform. - * \author Josh Klontz \cite jklontz - * - * For a video with m frames, AggregateFrames would create a total of m-n+1 sequences ([0,n] ... [m-n+1, m]). - */ -class AggregateFrames : public TimeVaryingTransform -{ - Q_OBJECT - Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) - BR_PROPERTY(int, n, 1) - - TemplateList buffer; - -public: - AggregateFrames() : TimeVaryingTransform(false, false) {} - -private: - void train(const TemplateList &data) - { - (void) data; - } - - void projectUpdate(const TemplateList &src, TemplateList &dst) - { - buffer.append(src); - if (buffer.size() < n) return; - Template out; - foreach (const Template &t, buffer) out.append(t); - out.file = buffer.takeFirst().file; - dst.append(out); - } - - void finalize(TemplateList &output) - { - (void) output; - buffer.clear(); - } - - void store(QDataStream &stream) const - { - (void) stream; - } - - void load(QDataStream &stream) - { - (void) stream; - } -}; - -BR_REGISTER(Transform, AggregateFrames) - -/*! - * \ingroup transforms - * \brief Only use one frame every n frames. - * \author Austin Blanton \cite imaus10 - * - * For a video with m frames, DropFrames will pass on m/n frames. - */ -class DropFrames : public UntrainableMetaTransform -{ - Q_OBJECT - Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) - BR_PROPERTY(int, n, 1) - - void project(const TemplateList &src, TemplateList &dst) const - { - if (src.first().file.get("FrameNumber") % n != 0) return; - dst = src; - } - - void project(const Template &src, Template &dst) const - { - (void) src; (void) dst; qFatal("shouldn't be here"); - } -}; - -BR_REGISTER(Transform, DropFrames) - -} // namespace br - -#include "frames.moc" diff --git a/openbr/plugins/gui.cpp b/openbr/plugins/gui.cpp index 3aab218..670fb09 100644 --- a/openbr/plugins/gui.cpp +++ b/openbr/plugins/gui.cpp @@ -928,6 +928,54 @@ public: BR_REGISTER(Transform, SurveyTransform) +class FilterTransform : public ShowTransform +{ + Q_OBJECT + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + if (Globals->parallelism > 1) + qFatal("FilterTransform cannot execute in parallel."); + + if (src.empty()) + return; + + foreach (const Template &t, src) { + Template u(t.file); + foreach (const cv::Mat &m, t) { + qImageBuffer = toQImage(m); + displayBuffer->convertFromImage(qImageBuffer); + + emit updateImage(displayBuffer->copy(displayBuffer->rect())); + + // Blocking wait for a key-press + if (this->waitInput) { + QString answer = p_window->waitForKeyPress(); + qDebug() << answer; + if (answer == "y") + u.append(m); + } + } + if (!u.empty()) + dst.append(u); + } + } + PromptWindow *p_window; + + + void init() + { + if (!Globals->useGui) + return; + + initActual(); + p_window = (PromptWindow *) window; + + emit changeTitle("Keep: y Discard: n"); + } +}; + +BR_REGISTER(Transform, FilterTransform) /*! * \ingroup transforms diff --git a/openbr/plugins/gui/adjacentoverlay.cpp b/openbr/plugins/gui/adjacentoverlay.cpp new file mode 100644 index 0000000..7df86fb --- /dev/null +++ b/openbr/plugins/gui/adjacentoverlay.cpp @@ -0,0 +1,217 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Load the image named in the specified property, draw it on the current matrix adjacent to the rect specified in the other property. + * \author Charles Otto \cite caotto + */ +class AdjacentOverlayTransform : public Transform +{ + Q_OBJECT + + Q_PROPERTY(QString imgName READ get_imgName WRITE set_imgName RESET reset_imgName STORED false) + Q_PROPERTY(QString targetName READ get_targetName WRITE set_targetName RESET reset_targetName STORED false) + BR_PROPERTY(QString, imgName, "") + BR_PROPERTY(QString, targetName, "") + + QSharedPointer opener; + void project(const Template &src, Template &dst) const + { + dst = src; + + if (imgName.isEmpty() || targetName.isEmpty() || !dst.file.contains(imgName) || !dst.file.contains(targetName)) + return; + + QVariant temp = src.file.value(imgName); + cv::Mat im; + // is this a filename? + if (temp.canConvert()) { + QString im_name = temp.toString(); + Template temp_im; + opener->project(File(im_name), temp_im); + im = temp_im.m(); + } + // a cv::Mat ? + else if (temp.canConvert()) + im = src.file.get(imgName); + else + qDebug() << "Unrecognized property type " << imgName << "for" << src.file.name; + + // Location of detected face in source image + QRectF target_location = src.file.get(targetName); + + // match width with target region + qreal target_width = target_location.width(); + qreal current_width = im.cols; + qreal current_height = im.rows; + + qreal aspect_ratio = current_height / current_width; + qreal target_height = target_width * aspect_ratio; + + cv::resize(im, im, cv::Size(target_width, target_height)); + + // ROI used to maybe crop the matched image + cv::Rect clip_roi; + clip_roi.x = 0; + clip_roi.y = 0; + clip_roi.width = im.cols; + clip_roi.height= im.rows <= dst.m().rows ? im.rows : dst.m().rows; + + int half_width = src.m().cols / 2; + int out_x = 0; + + // place in the source image we will copy the matched image to. + cv::Rect target_roi; + bool left_side = false; + int width_adjust = 0; + // Place left + if (target_location.center().rx() > half_width) { + out_x = target_location.left() - im.cols; + if (out_x < 0) { + width_adjust = abs(out_x); + out_x = 0; + } + left_side = true; + } + // place right + else { + out_x = target_location.right(); + int high = out_x + im.cols; + if (high >= src.m().cols) { + width_adjust = abs(high - src.m().cols + 1); + } + } + + cv::Mat outIm; + if (width_adjust) + { + outIm.create(dst.m().rows, dst.m().cols + width_adjust, CV_8UC3); + memset(outIm.data, 127, outIm.rows * outIm.cols * outIm.channels()); + + Rect temp; + + if (left_side) + temp = Rect(abs(width_adjust), 0, dst.m().cols, dst.m().rows); + + else + temp = Rect(0, 0, dst.m().cols, dst.m().rows); + + dst.m().copyTo(outIm(temp)); + + } + else + outIm = dst.m(); + + if (clip_roi.height + target_location.top() >= outIm.rows) + { + clip_roi.height -= abs(outIm.rows - (clip_roi.height + target_location.top() )); + } + if (clip_roi.x + clip_roi.width >= im.cols) { + clip_roi.width -= abs(im.cols - (clip_roi.x + clip_roi.width + 1)); + if (clip_roi.width < 0) + clip_roi.width = 1; + } + + if (clip_roi.y + clip_roi.height >= im.rows) { + clip_roi.height -= abs(im.rows - (clip_roi.y + clip_roi.height + 1)); + } + if (clip_roi.x < 0) + clip_roi.x = 0; + if (clip_roi.y < 0) + clip_roi.y = 0; + + if (clip_roi.height < 0) + clip_roi.height = 0; + + if (clip_roi.width < 0) + clip_roi.width = 0; + + + if (clip_roi.y + clip_roi.height >= im.rows) + { + qDebug() << "Bad clip y" << clip_roi.y + clip_roi.height << im.rows; + } + if (clip_roi.x + clip_roi.width >= im.cols) + { + qDebug() << "Bad clip x" << clip_roi.x + clip_roi.width << im.cols; + } + + if (clip_roi.y < 0 || clip_roi.height < 0) + { + qDebug() << "bad clip y, low" << clip_roi.y << clip_roi.height; + qFatal("die"); + } + if (clip_roi.x < 0 || clip_roi.width < 0) + { + qDebug() << "bad clip x, low" << clip_roi.x << clip_roi.width; + qFatal("die"); + } + + target_roi.x = out_x; + target_roi.width = clip_roi.width; + target_roi.y = target_location.top(); + target_roi.height = clip_roi.height; + + + im = im(clip_roi); + + if (target_roi.x < 0 || target_roi.x >= outIm.cols) + { + qDebug() << "Bad xdim in targetROI!" << target_roi.x << " out im x: " << outIm.cols; + qFatal("die"); + } + + if (target_roi.x + target_roi.width < 0 || (target_roi.x + target_roi.width) >= outIm.cols) + { + qDebug() << "Bad xdim in targetROI!" << target_roi.x + target_roi.width; + qFatal("die"); + } + + if (target_roi.y < 0 || target_roi.y >= outIm.rows) + { + qDebug() << "Bad ydim in targetROI!" << target_roi.y; + qFatal("die"); + } + + if ((target_roi.y + target_roi.height) < 0 || (target_roi.y + target_roi.height) > outIm.rows) + { + qDebug() << "Bad ydim in targetROI!" << target_roi.y + target_roi.height; + qDebug() << "target_roi.y: " << target_roi.y << " height: " << target_roi.height; + qFatal("die"); + } + + + std::vector channels; + cv::split(outIm, channels); + + std::vector patch_channels; + cv::split(im, patch_channels); + + for (size_t i=0; i < channels.size(); i++) + { + cv::addWeighted(channels[i](target_roi), 0, patch_channels[i % patch_channels.size()], 1, 0,channels[i](target_roi)); + } + cv::merge(channels, outIm); + dst.m() = outIm; + + } + + void init() + { + opener = QSharedPointer(br::Transform::make("Cache(Open)", NULL)); + } + +}; + +BR_REGISTER(Transform, AdjacentOverlayTransform) + +} // namespace br + +#include "adjacentoverlay.moc" diff --git a/openbr/plugins/gui/draw.cpp b/openbr/plugins/gui/draw.cpp new file mode 100644 index 0000000..63863b1 --- /dev/null +++ b/openbr/plugins/gui/draw.cpp @@ -0,0 +1,61 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Renders metadata onto the image. + * + * The inPlace argument controls whether or not the image is cloned before the metadata is drawn. + * + * \author Josh Klontz \cite jklontz + */ +class DrawTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(bool verbose READ get_verbose WRITE set_verbose RESET reset_verbose STORED false) + Q_PROPERTY(bool points READ get_points WRITE set_points RESET reset_points STORED false) + Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) + Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) + Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) + Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) + Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false) + BR_PROPERTY(bool, verbose, false) + BR_PROPERTY(bool, points, true) + BR_PROPERTY(bool, rects, true) + BR_PROPERTY(bool, inPlace, false) + BR_PROPERTY(int, lineThickness, 1) + BR_PROPERTY(bool, named, true) + BR_PROPERTY(bool, location, true) + + void project(const Template &src, Template &dst) const + { + const Scalar color(0,255,0); + const Scalar verboseColor(255, 255, 0); + dst.m() = inPlace ? src.m() : src.m().clone(); + + if (points) { + const QList pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points()); + for (int i=0; i + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Draws a grid on the image + * \author Josh Klontz \cite jklontz + */ +class DrawGridLinesTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) + Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) + Q_PROPERTY(int r READ get_r WRITE set_r RESET reset_r STORED false) + Q_PROPERTY(int g READ get_g WRITE set_g RESET reset_g STORED false) + Q_PROPERTY(int b READ get_b WRITE set_b RESET reset_b STORED false) + BR_PROPERTY(int, rows, 0) + BR_PROPERTY(int, columns, 0) + BR_PROPERTY(int, r, 196) + BR_PROPERTY(int, g, 196) + BR_PROPERTY(int, b, 196) + + void project(const Template &src, Template &dst) const + { + Mat m = src.m().clone(); + float rowStep = 1.f * m.rows / (rows+1); + float columnStep = 1.f * m.cols / (columns+1); + int thickness = qMin(m.rows, m.cols) / 256; + for (float row = rowStep/2; row < m.rows; row += rowStep) + line(m, Point(0, row), Point(m.cols, row), Scalar(r, g, b), thickness, CV_AA); + for (float column = columnStep/2; column < m.cols; column += columnStep) + line(m, Point(column, 0), Point(column, m.rows), Scalar(r, g, b), thickness, CV_AA); + dst = m; + } +}; + +BR_REGISTER(Transform, DrawGridLinesTransform) + +} // namespace br + +#include "drawgridlines.moc" diff --git a/openbr/plugins/gui/drawopticalflow.cpp b/openbr/plugins/gui/drawopticalflow.cpp new file mode 100644 index 0000000..6f3d3ae --- /dev/null +++ b/openbr/plugins/gui/drawopticalflow.cpp @@ -0,0 +1,40 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Draw a line representing the direction and magnitude of optical flow at the specified points. + * \author Austin Blanton \cite imaus10 + */ +class DrawOpticalFlow : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString original READ get_original WRITE set_original RESET reset_original STORED false) + BR_PROPERTY(QString, original, "original") + + void project(const Template &src, Template &dst) const + { + const Scalar color(0,255,0); + Mat flow = src.m(); + dst = src; + if (!dst.file.contains(original)) qFatal("The original img must be saved in the metadata with SaveMat."); + dst.m() = dst.file.get(original); + dst.file.remove(original); + foreach (const Point2f &pt, OpenCVUtils::toPoints(dst.file.points())) { + Point2f dxy = flow.at(pt.y, pt.x); + Point2f newPt(pt.x+dxy.x, pt.y+dxy.y); + line(dst, pt, newPt, color); + } + } +}; + +BR_REGISTER(Transform, DrawOpticalFlow) + +} // namespace br + +#include "drawopticalflow.moc" diff --git a/openbr/plugins/gui/drawpropertiespoint.cpp b/openbr/plugins/gui/drawpropertiespoint.cpp new file mode 100644 index 0000000..0c0d087 --- /dev/null +++ b/openbr/plugins/gui/drawpropertiespoint.cpp @@ -0,0 +1,70 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Draw the values of a list of properties at the specified point on the image + * + * The inPlace argument controls whether or not the image is cloned before it is drawn on. + * + * \author Charles Otto \cite caotto + */ +class DrawPropertiesPointTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QStringList propNames READ get_propNames WRITE set_propNames RESET reset_propNames STORED false) + Q_PROPERTY(QString pointName READ get_pointName WRITE set_pointName RESET reset_pointName STORED false) + Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) + BR_PROPERTY(QStringList, propNames, QStringList()) + BR_PROPERTY(QString, pointName, "") + BR_PROPERTY(bool, inPlace, false) + + void project(const Template &src, Template &dst) const + { + dst = src; + if (propNames.isEmpty() || pointName.isEmpty()) + return; + + dst.m() = inPlace ? src.m() : src.m().clone(); + + QVariant point = dst.file.value(pointName); + + if (!point.canConvert(QVariant::PointF)) + return; + + QPointF targetPoint = point.toPointF(); + + Point2f cvPoint = OpenCVUtils::toPoint(targetPoint); + + + const Scalar textColor(255, 255, 0); + + std::string outString = ""; + foreach (const QString &propName, propNames) + { + QVariant prop = dst.file.value(propName); + + if (!prop.canConvert(QVariant::String)) + continue; + QString propString = prop.toString(); + outString += propName.toStdString() + ": " + propString.toStdString() + " "; + + } + if (outString.empty()) + return; + + putText(dst, outString, cvPoint, FONT_HERSHEY_SIMPLEX, 0.5, textColor, 1); + } + +}; + +BR_REGISTER(Transform, DrawPropertiesPointTransform) + +} // namespace br + +#include "drawpropertiespoint.moc" diff --git a/openbr/plugins/gui/drawpropertypoint.cpp b/openbr/plugins/gui/drawpropertypoint.cpp new file mode 100644 index 0000000..98b6201 --- /dev/null +++ b/openbr/plugins/gui/drawpropertypoint.cpp @@ -0,0 +1,63 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Draw the value of the specified property at the specified point on the image + * + * The inPlace argument controls whether or not the image is cloned before it is drawn on. + * + * \author Charles Otto \cite caotto + */ +class DrawPropertyPointTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) + Q_PROPERTY(QString pointName READ get_pointName WRITE set_pointName RESET reset_pointName STORED false) + Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) + BR_PROPERTY(QString, propName, "") + BR_PROPERTY(QString, pointName, "") + BR_PROPERTY(bool, inPlace, false) + + + void project(const Template &src, Template &dst) const + { + dst = src; + if (propName.isEmpty() || pointName.isEmpty()) + return; + + dst.m() = inPlace ? src.m() : src.m().clone(); + + const Scalar textColor(255, 255, 0); + + QVariant prop = dst.file.value(propName); + + + if (!prop.canConvert(QVariant::String)) + return; + QString propString = prop.toString(); + + QVariant point = dst.file.value(pointName); + + if (!point.canConvert(QVariant::PointF)) + return; + + QPointF targetPoint = point.toPointF(); + + Point2f cvPoint = OpenCVUtils::toPoint(targetPoint); + + std::string text = propName.toStdString() + ": " + propString.toStdString(); + putText(dst, text, cvPoint, FONT_HERSHEY_SIMPLEX, 0.5, textColor, 1); + } + +}; +BR_REGISTER(Transform, DrawPropertyPointTransform) + +} // namespace br + +#include "drawpropertypoint.moc" diff --git a/openbr/plugins/gui/drawsegmentation.cpp b/openbr/plugins/gui/drawsegmentation.cpp new file mode 100644 index 0000000..28eb406 --- /dev/null +++ b/openbr/plugins/gui/drawsegmentation.cpp @@ -0,0 +1,55 @@ +#include + +#include + +using namespace std; +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Fill in the segmentations or draw a line between intersecting segments. + * \author Austin Blanton \cite imaus10 + */ +class DrawSegmentation : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(bool fillSegment READ get_fillSegment WRITE set_fillSegment RESET reset_fillSegment STORED false) + BR_PROPERTY(bool, fillSegment, true) + + void project(const Template &src, Template &dst) const + { + if (!src.file.contains("SegmentsMask") || !src.file.contains("NumSegments")) qFatal("Must supply a Contours object in the metadata to drawContours."); + Mat segments = src.file.get("SegmentsMask"); + int numSegments = src.file.get("NumSegments"); + + dst.file = src.file; + Mat drawn = fillSegment ? Mat(segments.size(), CV_8UC3, Scalar::all(0)) : src.m(); + + for (int i=1; i > contours; + Scalar color(0,255,0); + findContours(mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); + drawContours(drawn, contours, -1, color); + } + } + + dst.m() = drawn; + } +}; + +BR_REGISTER(Transform, DrawSegmentation) + +} // namespace br + +#include "drawsegmentation.moc" diff --git a/openbr/plugins/hist.cpp b/openbr/plugins/hist.cpp deleted file mode 100644 index d9f0a05..0000000 --- a/openbr/plugins/hist.cpp +++ /dev/null @@ -1,195 +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/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Histograms the matrix - * \author Josh Klontz \cite jklontz - */ -class HistTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float max READ get_max WRITE set_max RESET reset_max STORED false) - Q_PROPERTY(float min READ get_min WRITE set_min RESET reset_min STORED false) - Q_PROPERTY(int dims READ get_dims WRITE set_dims RESET reset_dims STORED false) - BR_PROPERTY(float, max, 256) - BR_PROPERTY(float, min, 0) - BR_PROPERTY(int, dims, -1) - - void project(const Template &src, Template &dst) const - { - const int dims = this->dims == -1 ? max - min : this->dims; - - std::vector mv; - split(src, mv); - Mat m(mv.size(), dims, CV_32FC1); - - for (size_t i=0; i mv; - cv::split(src, mv); - weights = mv[0]; - weights.convertTo(weights, CV_32F); - vals = mv[1]; - } else { - qFatal("Invalid channel count: %d", channels); - } - - vals.convertTo(dst, bins > 256 ? CV_16U : CV_8U, bins/(max-min), floor); - if (!split) return; - - QList outputs; outputs.reserve(bins); - for (int i=0; i Tuple; - QList tuples = Common::Sort(OpenCVUtils::matrixToVector(m)); - - float prevValue = 0; - int prevRank = 0; - for (int i=0; i(tuples[i].second / m.cols, tuples[i].second % m.cols) = rank; - prevValue = tuples[i].first; - prevRank = rank; - } - } -}; - -BR_REGISTER(Transform, RankTransform) - -/*! - * \ingroup transforms - * \brief An integral histogram - * \author Josh Klontz \cite jklontz - */ -class IntegralHistTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int bins READ get_bins WRITE set_bins RESET reset_bins STORED false) - Q_PROPERTY(int radius READ get_radius WRITE set_radius RESET reset_radius STORED false) - BR_PROPERTY(int, bins, 256) - BR_PROPERTY(int, radius, 16) - - void project(const Template &src, Template &dst) const - { - const Mat &m = src.m(); - if (m.type() != CV_8UC1) qFatal("IntegralHist requires 8UC1 matrices."); - - Mat integral(m.rows/radius+1, (m.cols/radius+1)*bins, CV_32SC1); - integral.setTo(0); - for (int i=1; i(i, j+k) += integral.at(i-1, j +k); - for (int k=0; k(i, j+k) += integral.at(i , j-bins+k); - for (int k=0; k(i, j+k) -= integral.at(i-1, j-bins+k); - for (int k=0; k(i, j+m.at((i-1)*radius+k,(j/bins-1)*radius+l))++; - } - } - dst = integral; - } -}; - -BR_REGISTER(Transform, IntegralHistTransform) - -} // namespace br - -#include "hist.moc" diff --git a/openbr/plugins/imgproc/abs.cpp b/openbr/plugins/imgproc/abs.cpp new file mode 100644 index 0000000..8e42db1 --- /dev/null +++ b/openbr/plugins/imgproc/abs.cpp @@ -0,0 +1,25 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Computes the absolute value of each element. + * \author Josh Klontz \cite jklontz + */ +class AbsTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst = cv::abs(src); + } +}; + +BR_REGISTER(Transform, AbsTransform) + +} // namespace br + +#include "abs.moc" diff --git a/openbr/plugins/imgproc/blend.cpp b/openbr/plugins/imgproc/blend.cpp new file mode 100644 index 0000000..5c8d560 --- /dev/null +++ b/openbr/plugins/imgproc/blend.cpp @@ -0,0 +1,30 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Alpha-blend two matrices + * \author Josh Klontz \cite jklontz + */ +class BlendTransform : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) + BR_PROPERTY(float, alpha, 0.5) + + void project(const Template &src, Template &dst) const + { + if (src.size() != 2) qFatal("Expected two source matrices."); + addWeighted(src[0], alpha, src[1], 1-alpha, 0, dst); + } +}; + +BR_REGISTER(Transform, BlendTransform) + +} // namespace br + +#include "blend.moc" diff --git a/openbr/plugins/imgproc/blur.cpp b/openbr/plugins/imgproc/blur.cpp new file mode 100644 index 0000000..9b6b18c --- /dev/null +++ b/openbr/plugins/imgproc/blur.cpp @@ -0,0 +1,43 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Gaussian blur + * \author Josh Klontz \cite jklontz + */ +class BlurTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) + Q_PROPERTY(bool ROI READ get_ROI WRITE set_ROI RESET reset_ROI STORED false) + BR_PROPERTY(float, sigma, 1) + BR_PROPERTY(bool, ROI, false) + + void project(const Template &src, Template &dst) const + { + if (!ROI) GaussianBlur(src, dst, Size(0,0), sigma); + else { + dst.m() = src.m(); + foreach (const QRectF &rect, src.file.rects()) { + Rect region(rect.x(), rect.y(), rect.width(), rect.height()); + Mat input = dst.m(); + Mat output = input.clone(); + GaussianBlur(input(region), output(region), Size(0,0), sigma); + dst.m() = output; + } + } + } +}; + +BR_REGISTER(Transform, BlurTransform) + +} // namespace br + +#include "blur.moc" diff --git a/openbr/plugins/imgproc/contrasteq.cpp b/openbr/plugins/imgproc/contrasteq.cpp new file mode 100644 index 0000000..0c1847b --- /dev/null +++ b/openbr/plugins/imgproc/contrasteq.cpp @@ -0,0 +1,70 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Xiaoyang Tan; Triggs, B.; + * "Enhanced Local Texture Feature Sets for Face Recognition Under Difficult Lighting Conditions," + * Image Processing, IEEE Transactions on , vol.19, no.6, pp.1635-1650, June 2010 + * \author Josh Klontz \cite jklontz + */ +class ContrastEqTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a STORED false) + Q_PROPERTY(float t READ get_t WRITE set_t RESET reset_t STORED false) + BR_PROPERTY(float, a, 1) + BR_PROPERTY(float, t, 0.1) + + void project(const Template &src, Template &dst) const + { + if (src.m().channels() != 1) qFatal("Expected single channel source matrix."); + + // Stage 1 + Mat stage1; + { + Mat abs_dst; + absdiff(src, Scalar(0), abs_dst); + Mat pow_dst; + pow(abs_dst, a, pow_dst); + float denominator = pow((float)mean(pow_dst)[0], 1.f/a); + src.m().convertTo(stage1, CV_32F, 1/denominator); + } + + // Stage 2 + Mat stage2; + { + Mat abs_dst; + absdiff(stage1, Scalar(0), abs_dst); + Mat min_dst; + min(abs_dst, t, min_dst); + Mat pow_dst; + pow(min_dst, a, pow_dst); + float denominator = pow((float)mean(pow_dst)[0], 1.f/a); + stage1.convertTo(stage2, CV_32F, 1/denominator); + } + + // Hyperbolic tangent + const int nRows = src.m().rows; + const int nCols = src.m().cols; + const float* p = (const float*)stage2.ptr(); + Mat m(nRows, nCols, CV_32FC1); + for (int i=0; i(i, j) = fast_tanh(p[i*nCols+j]); + // TODO: m.at(i, j) = t * fast_tanh(p[i*nCols+j] / t); + + dst = m; + } +}; + +BR_REGISTER(Transform, ContrastEqTransform) + +} // namespace br + +#include "contrasteq.moc" diff --git a/openbr/plugins/imgproc/crop.cpp b/openbr/plugins/imgproc/crop.cpp new file mode 100644 index 0000000..fbd1b78 --- /dev/null +++ b/openbr/plugins/imgproc/crop.cpp @@ -0,0 +1,35 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Crops about the specified region of interest. + * \author Josh Klontz \cite jklontz + */ +class CropTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int x READ get_x WRITE set_x RESET reset_x STORED false) + Q_PROPERTY(int y READ get_y WRITE set_y RESET reset_y STORED false) + Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) + Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) + BR_PROPERTY(int, x, 0) + BR_PROPERTY(int, y, 0) + BR_PROPERTY(int, width, -1) + BR_PROPERTY(int, height, -1) + + void project(const Template &src, Template &dst) const + { + dst = Mat(src, Rect(x, y, width < 1 ? src.m().cols-x-abs(width) : width, height < 1 ? src.m().rows-y-abs(height) : height)); + } +}; + +BR_REGISTER(Transform, CropTransform) + +} // namespace br + +#include "crop.moc" diff --git a/openbr/plugins/imgproc/cropblack.cpp b/openbr/plugins/imgproc/cropblack.cpp new file mode 100644 index 0000000..e944600 --- /dev/null +++ b/openbr/plugins/imgproc/cropblack.cpp @@ -0,0 +1,55 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Crop out black borders + * \author Josh Klontz \cite jklontz + */ +class CropBlackTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + Mat gray; + OpenCVUtils::cvtGray(src, gray); + + int xStart = 0; + while (xStart < gray.cols) { + if (mean(gray.col(xStart))[0] >= 1) break; + xStart++; + } + + int xEnd = gray.cols - 1; + while (xEnd >= 0) { + if (mean(gray.col(xEnd))[0] >= 1) break; + xEnd--; + } + + int yStart = 0; + while (yStart < gray.rows) { + if (mean(gray.col(yStart))[0] >= 1) break; + yStart++; + } + + int yEnd = gray.rows - 1; + while (yEnd >= 0) { + if (mean(gray.col(yEnd))[0] >= 1) break; + yEnd--; + } + + dst = src.m()(Rect(xStart, yStart, xEnd-xStart, yEnd-yStart)); + } +}; + +BR_REGISTER(Transform, CropBlackTransform) + +} // namespace br + +#include "cropblack.moc" diff --git a/openbr/plugins/imgproc/cropsquare.cpp b/openbr/plugins/imgproc/cropsquare.cpp new file mode 100644 index 0000000..01fa805 --- /dev/null +++ b/openbr/plugins/imgproc/cropsquare.cpp @@ -0,0 +1,29 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Trim the image so the width and the height are the same size. + * \author Josh Klontz \cite jklontz + */ +class CropSquareTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + const Mat &m = src; + const int newSize = min(m.rows, m.cols); + dst = Mat(m, Rect((m.cols-newSize)/2, (m.rows-newSize)/2, newSize, newSize)); + } +}; + +BR_REGISTER(Transform, CropSquareTransform) + +} // namespace br + +#include "cropsquare.moc" diff --git a/openbr/plugins/imgproc/csdn.cpp b/openbr/plugins/imgproc/csdn.cpp new file mode 100644 index 0000000..a4fd214 --- /dev/null +++ b/openbr/plugins/imgproc/csdn.cpp @@ -0,0 +1,58 @@ +#include + +using namespace std; +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Meyers, E.; Wolf, L. + * “Using biologically inspired features for face processing,” + * Int. Journal of Computer Vision, vol. 76, no. 1, pp 93–104, 2008. + * \author Scott Klum \cite sklum + */ + +class CSDNTransform : public UntrainableTransform +{ + Q_OBJECT + + Q_PROPERTY(float s READ get_s WRITE set_s RESET reset_s STORED false) + BR_PROPERTY(int, s, 16) + + void project(const Template &src, Template &dst) const + { + if (src.m().channels() != 1) qFatal("Expected single channel source matrix."); + + const int nRows = src.m().rows; + const int nCols = src.m().cols; + + Mat m; + src.m().convertTo(m, CV_32FC1); + + const int surround = s/2; + + for ( int i = 0; i < nRows; i++ ) { + for ( int j = 0; j < nCols; j++ ) { + int width = min( j+surround, nCols ) - max( 0, j-surround ); + int height = min( i+surround, nRows ) - max( 0, i-surround ); + + Rect_ ROI(max(0, j-surround), max(0, i-surround), width, height); + + Scalar_ avg = mean(m(ROI)); + + m.at(i,j) = m.at(i,j) - avg[0]; + } + } + + dst = m; + + } +}; + +BR_REGISTER(Transform, CSDNTransform) + +} // namespace br + +#include "csdn.moc" diff --git a/openbr/plugins/imgproc/cvtcolor.cpp b/openbr/plugins/imgproc/cvtcolor.cpp new file mode 100644 index 0000000..ffc10e9 --- /dev/null +++ b/openbr/plugins/imgproc/cvtcolor.cpp @@ -0,0 +1,55 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Colorspace conversion. + * \author Josh Klontz \cite jklontz + */ +class CvtColorTransform : public UntrainableTransform +{ + Q_OBJECT + Q_ENUMS(ColorSpace) + Q_PROPERTY(ColorSpace colorSpace READ get_colorSpace WRITE set_colorSpace RESET reset_colorSpace STORED false) + Q_PROPERTY(int channel READ get_channel WRITE set_channel RESET reset_channel STORED false) + +public: + enum ColorSpace { Gray = CV_BGR2GRAY, + RGBGray = CV_RGB2GRAY, + HLS = CV_BGR2HLS, + HSV = CV_BGR2HSV, + Lab = CV_BGR2Lab, + Luv = CV_BGR2Luv, + RGB = CV_BGR2RGB, + XYZ = CV_BGR2XYZ, + YCrCb = CV_BGR2YCrCb, + Color = CV_GRAY2BGR }; + +private: + BR_PROPERTY(ColorSpace, colorSpace, Gray) + BR_PROPERTY(int, channel, -1) + + void project(const Template &src, Template &dst) const + { + if (src.m().channels() > 1 || colorSpace == CV_GRAY2BGR) cvtColor(src, dst, colorSpace); + else dst = src; + + if (channel != -1) { + std::vector mv; + split(dst, mv); + dst = mv[channel % (int)mv.size()]; + } + } +}; + +BR_REGISTER(Transform, CvtColorTransform) + +} // namespace br + +#include "cvtcolor.moc" diff --git a/openbr/plugins/imgproc/cvtfloat.cpp b/openbr/plugins/imgproc/cvtfloat.cpp new file mode 100644 index 0000000..c508cba --- /dev/null +++ b/openbr/plugins/imgproc/cvtfloat.cpp @@ -0,0 +1,27 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Convert to floating point format. + * \author Josh Klontz \cite jklontz + */ +class CvtFloatTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + src.m().convertTo(dst, CV_32F); + } +}; + +BR_REGISTER(Transform, CvtFloatTransform) + +} // namespace br + +#include "cvtfloat.moc" diff --git a/openbr/plugins/imgproc/cvtuchar.cpp b/openbr/plugins/imgproc/cvtuchar.cpp new file mode 100644 index 0000000..b829b67 --- /dev/null +++ b/openbr/plugins/imgproc/cvtuchar.cpp @@ -0,0 +1,26 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Convert to uchar format + * \author Josh Klontz \cite jklontz + */ +class CvtUCharTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + OpenCVUtils::cvtUChar(src, dst); + } +}; + +BR_REGISTER(Transform, CvtUCharTransform) + +} // namespace br + +#include "cvtuchar.moc" diff --git a/openbr/plugins/imgproc/denoising.cpp b/openbr/plugins/imgproc/denoising.cpp new file mode 100644 index 0000000..93cb619 --- /dev/null +++ b/openbr/plugins/imgproc/denoising.cpp @@ -0,0 +1,51 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV Non-Local Means Denoising + * \author Josh Klontz \cite jklontz + */ +class NLMeansDenoisingTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float h READ get_h WRITE set_h RESET reset_h STORED false) + Q_PROPERTY(int templateWindowSize READ get_templateWindowSize WRITE set_templateWindowSize RESET reset_templateWindowSize STORED false) + Q_PROPERTY(int searchWindowSize READ get_searchWindowSize WRITE set_searchWindowSize RESET reset_searchWindowSize STORED false) + BR_PROPERTY(float, h, 3) + BR_PROPERTY(int, templateWindowSize, 7) + BR_PROPERTY(int, searchWindowSize, 21) + + void project(const Template &src, Template &dst) const + { + fastNlMeansDenoising(src, dst, h, templateWindowSize, searchWindowSize); + } +}; + +BR_REGISTER(Transform, NLMeansDenoisingTransform) + +} // namespace br + +#include "denoising.moc" diff --git a/openbr/plugins/imgproc/discardalpha.cpp b/openbr/plugins/imgproc/discardalpha.cpp new file mode 100644 index 0000000..368faed --- /dev/null +++ b/openbr/plugins/imgproc/discardalpha.cpp @@ -0,0 +1,40 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Drop the alpha channel (if exists). + * \author Austin Blanton \cite imaus10 + */ +class DiscardAlphaTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + if (src.m().channels() > 4 || src.m().channels() == 2) { + dst.file.fte = true; + return; + } + + dst = src; + if (src.m().channels() == 4) { + std::vector mv; + split(src, mv); + mv.pop_back(); + merge(mv, dst); + } + } +}; + +BR_REGISTER(Transform, DiscardAlphaTransform) + +} // namespace br + +#include "discardalpha.moc" diff --git a/openbr/plugins/imgproc/div.cpp b/openbr/plugins/imgproc/div.cpp new file mode 100644 index 0000000..2738d21 --- /dev/null +++ b/openbr/plugins/imgproc/div.cpp @@ -0,0 +1,29 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Enforce a multiple of \em n columns. + * \author Josh Klontz \cite jklontz + */ +class DivTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) + BR_PROPERTY(int, n, 1) + + void project(const Template &src, Template &dst) const + { + dst = Mat(src, Rect(0,0,n*(src.m().cols/n),src.m().rows)); + } +}; + +BR_REGISTER(Transform, DivTransform) + +} // namespace br + +#include "div.moc" diff --git a/openbr/plugins/imgproc/dog.cpp b/openbr/plugins/imgproc/dog.cpp new file mode 100644 index 0000000..9c0044d --- /dev/null +++ b/openbr/plugins/imgproc/dog.cpp @@ -0,0 +1,54 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Difference of gaussians + * \author Josh Klontz \cite jklontz + */ +class DoGTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float sigma0 READ get_sigma0 WRITE set_sigma0 RESET reset_sigma0 STORED false) + Q_PROPERTY(float sigma1 READ get_sigma1 WRITE set_sigma1 RESET reset_sigma1 STORED false) + BR_PROPERTY(float, sigma0, 1) + BR_PROPERTY(float, sigma1, 2) + + Size ksize0, ksize1; + + static Size getKernelSize(double sigma) + { + // Inverts OpenCV's conversion from kernel size to sigma: + // sigma = ((ksize-1)*0.5 - 1)*0.3 + 0.8 + // See documentation for cv::getGaussianKernel() + int ksize = ((sigma - 0.8) / 0.3 + 1) * 2 + 1; + if (ksize % 2 == 0) ksize++; + return Size(ksize, ksize); + } + + void init() + { + ksize0 = getKernelSize(sigma0); + ksize1 = getKernelSize(sigma1); + } + + void project(const Template &src, Template &dst) const + { + Mat g0, g1; + GaussianBlur(src, g0, ksize0, 0); + GaussianBlur(src, g1, ksize1, 0); + subtract(g0, g1, dst); + } +}; + +BR_REGISTER(Transform, DoGTransform) + +} // namespace br + +#include "dog.moc" diff --git a/openbr/plugins/imgproc/ensurechannels.cpp b/openbr/plugins/imgproc/ensurechannels.cpp new file mode 100644 index 0000000..4351738 --- /dev/null +++ b/openbr/plugins/imgproc/ensurechannels.cpp @@ -0,0 +1,51 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Enforce the matrix has a certain number of channels by adding or removing channels. + * \author Josh Klontz \cite jklontz + */ +class EnsureChannelsTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) + BR_PROPERTY(int, n, 1) + + void project(const Template &src, Template &dst) const + { + if (src.m().channels() == n) { + dst = src; + } else { + std::vector mv; + split(src, mv); + + // Add extra channels + while ((int)mv.size() < n) { + for (int i=0; i n) + mv.pop_back(); + + merge(mv, dst); + } + } +}; + +BR_REGISTER(Transform, EnsureChannelsTransform) + +} // namespace br + +#include "ensurechannels.moc" diff --git a/openbr/plugins/imgproc/flood.cpp b/openbr/plugins/imgproc/flood.cpp new file mode 100644 index 0000000..60c9ec9 --- /dev/null +++ b/openbr/plugins/imgproc/flood.cpp @@ -0,0 +1,36 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Fill black pixels with the specified color. + * \author Josh Klontz \cite jklontz + */ +class FloodTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int r READ get_r WRITE set_r RESET reset_r STORED false) + Q_PROPERTY(int g READ get_g WRITE set_g RESET reset_g STORED false) + Q_PROPERTY(int b READ get_b WRITE set_b RESET reset_b STORED false) + Q_PROPERTY(bool all READ get_all WRITE set_all RESET reset_all STORED false) + BR_PROPERTY(int, r, 0) + BR_PROPERTY(int, g, 0) + BR_PROPERTY(int, b, 0) + BR_PROPERTY(bool, all, false) + + void project(const Template &src, Template &dst) const + { + dst = src.m().clone(); + dst.m().setTo(Scalar(r, g, b), all ? Mat() : dst.m()==0); + } +}; + +BR_REGISTER(Transform, FloodTransform) + +} // namespace br + +#include "flood.moc" diff --git a/openbr/plugins/imgproc/gamma.cpp b/openbr/plugins/imgproc/gamma.cpp new file mode 100644 index 0000000..6f50132 --- /dev/null +++ b/openbr/plugins/imgproc/gamma.cpp @@ -0,0 +1,39 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Gamma correction + * \author Josh Klontz \cite jklontz + */ +class GammaTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false) + BR_PROPERTY(float, gamma, 0.2) + + Mat lut; + + void init() + { + lut.create(256, 1, CV_32FC1); + if (gamma == 0) for (int i=0; i<256; i++) lut.at(i,0) = log((float)i); + else for (int i=0; i<256; i++) lut.at(i,0) = pow(i, gamma); + } + + void project(const Template &src, Template &dst) const + { + if (src.m().depth() == CV_8U) LUT(src, lut, dst); + else pow(src, gamma, dst); + } +}; + +BR_REGISTER(Transform, GammaTransform) + +} // namespace br + +#include "gamma.moc" diff --git a/openbr/plugins/imgproc/gradient.cpp b/openbr/plugins/imgproc/gradient.cpp new file mode 100644 index 0000000..7df82e2 --- /dev/null +++ b/openbr/plugins/imgproc/gradient.cpp @@ -0,0 +1,50 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Computes magnitude and/or angle of image. + * \author Josh Klontz \cite jklontz + */ +class GradientTransform : public UntrainableTransform +{ + Q_OBJECT + Q_ENUMS(Channel) + Q_PROPERTY(Channel channel READ get_channel WRITE set_channel RESET reset_channel STORED false) + +public: + enum Channel { Magnitude, Angle, MagnitudeAndAngle }; + +private: + BR_PROPERTY(Channel, channel, Angle) + + void project(const Template &src, Template &dst) const + { + Mat dx, dy, magnitude, angle; + Sobel(src, dx, CV_32F, 1, 0, CV_SCHARR); + Sobel(src, dy, CV_32F, 0, 1, CV_SCHARR); + cartToPolar(dx, dy, magnitude, angle, true); + std::vector mv; + if ((channel == Magnitude) || (channel == MagnitudeAndAngle)) { + const float theoreticalMaxMagnitude = sqrt(2*pow(float(2*(3+10+3)*255), 2.f)); + mv.push_back(magnitude / theoreticalMaxMagnitude); + } + if ((channel == Angle) || (channel == MagnitudeAndAngle)) + mv.push_back(angle); + Mat result; + merge(mv, result); + dst.append(result); + } +}; + +BR_REGISTER(Transform, GradientTransform) + +} // namespace br + +#include "gradient.moc" diff --git a/openbr/plugins/imgproc/hist.cpp b/openbr/plugins/imgproc/hist.cpp new file mode 100644 index 0000000..47296cd --- /dev/null +++ b/openbr/plugins/imgproc/hist.cpp @@ -0,0 +1,54 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Histograms the matrix + * \author Josh Klontz \cite jklontz + */ +class HistTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float max READ get_max WRITE set_max RESET reset_max STORED false) + Q_PROPERTY(float min READ get_min WRITE set_min RESET reset_min STORED false) + Q_PROPERTY(int dims READ get_dims WRITE set_dims RESET reset_dims STORED false) + BR_PROPERTY(float, max, 256) + BR_PROPERTY(float, min, 0) + BR_PROPERTY(int, dims, -1) + + void project(const Template &src, Template &dst) const + { + const int dims = this->dims == -1 ? max - min : this->dims; + + std::vector mv; + split(src, mv); + Mat m(mv.size(), dims, CV_32FC1); + + for (size_t i=0; i + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Quantizes the values into bins. + * \author Josh Klontz \cite jklontz + */ +class HistBinTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float min READ get_min WRITE set_min RESET reset_min STORED false) + Q_PROPERTY(float max READ get_max WRITE set_max RESET reset_max STORED false) + Q_PROPERTY(int bins READ get_bins WRITE set_bins RESET reset_bins STORED false) + Q_PROPERTY(bool split READ get_split WRITE set_split RESET reset_split STORED false) + BR_PROPERTY(float, min, 0) + BR_PROPERTY(float, max, 255) + BR_PROPERTY(int, bins, 8) + BR_PROPERTY(bool, split, false) + + void project(const Template &src, Template &dst) const + { + const double floor = ((src.m().depth() == CV_32F) || (src.m().depth() == CV_64F)) ? -0.5 : 0; + + Mat weights, vals; + const int channels = src.m().channels(); + if (channels == 1) { + vals = src; + } else if (channels == 2) { + // If there are two channels, the first is channel is assumed to be a weight vector + // and the second channel contains the vectors we would like to bin. + std::vector mv; + cv::split(src, mv); + weights = mv[0]; + weights.convertTo(weights, CV_32F); + vals = mv[1]; + } else { + qFatal("Invalid channel count: %d", channels); + } + + vals.convertTo(dst, bins > 256 ? CV_16U : CV_8U, bins/(max-min), floor); + if (!split) return; + + QList outputs; outputs.reserve(bins); + for (int i=0; i + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV inpainting + * \author Josh Klontz \cite jklontz + */ +class InpaintTransform : public UntrainableTransform +{ + Q_OBJECT + Q_ENUMS(Method) + Q_PROPERTY(int radius READ get_radius WRITE set_radius RESET reset_radius STORED false) + Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) + +public: + /*!< */ + enum Method { NavierStokes = INPAINT_NS, + Telea = INPAINT_TELEA }; + +private: + BR_PROPERTY(int, radius, 1) + BR_PROPERTY(Method, method, NavierStokes) + Transform *cvtGray; + + void init() + { + cvtGray = make("Cvt(Gray)"); + } + + void project(const Template &src, Template &dst) const + { + inpaint(src, (*cvtGray)(src)<5, dst, radius, method); + } +}; + +} // namespace br + +#include "inpaint.moc" diff --git a/openbr/plugins/imgproc/integral.cpp b/openbr/plugins/imgproc/integral.cpp new file mode 100644 index 0000000..ac3e034 --- /dev/null +++ b/openbr/plugins/imgproc/integral.cpp @@ -0,0 +1,27 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Computes integral image. + * \author Josh Klontz \cite jklontz + */ +class IntegralTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + cv::integral(src, dst); + } +}; + +BR_REGISTER(Transform, IntegralTransform) + +} // namespace br + +#include "integral.moc" diff --git a/openbr/plugins/imgproc/integralhist.cpp b/openbr/plugins/imgproc/integralhist.cpp new file mode 100644 index 0000000..4bed769 --- /dev/null +++ b/openbr/plugins/imgproc/integralhist.cpp @@ -0,0 +1,46 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief An integral histogram + * \author Josh Klontz \cite jklontz + */ +class IntegralHistTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int bins READ get_bins WRITE set_bins RESET reset_bins STORED false) + Q_PROPERTY(int radius READ get_radius WRITE set_radius RESET reset_radius STORED false) + BR_PROPERTY(int, bins, 256) + BR_PROPERTY(int, radius, 16) + + void project(const Template &src, Template &dst) const + { + const Mat &m = src.m(); + if (m.type() != CV_8UC1) qFatal("IntegralHist requires 8UC1 matrices."); + + Mat integral(m.rows/radius+1, (m.cols/radius+1)*bins, CV_32SC1); + integral.setTo(0); + for (int i=1; i(i, j+k) += integral.at(i-1, j +k); + for (int k=0; k(i, j+k) += integral.at(i , j-bins+k); + for (int k=0; k(i, j+k) -= integral.at(i-1, j-bins+k); + for (int k=0; k(i, j+m.at((i-1)*radius+k,(j/bins-1)*radius+l))++; + } + } + dst = integral; + } +}; + +BR_REGISTER(Transform, IntegralHistTransform) + +} // namespace br + +#include "integralhist.moc" diff --git a/openbr/plugins/imgproc/integralsampler.cpp b/openbr/plugins/imgproc/integralsampler.cpp new file mode 100644 index 0000000..914787b --- /dev/null +++ b/openbr/plugins/imgproc/integralsampler.cpp @@ -0,0 +1,106 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Sliding window feature extraction from a multi-channel integral image. + * \author Josh Klontz \cite jklontz + */ +class IntegralSamplerTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int scales READ get_scales WRITE set_scales RESET reset_scales STORED false) + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) + Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) + Q_PROPERTY(bool secondOrder READ get_secondOrder WRITE set_secondOrder RESET reset_secondOrder STORED false) + BR_PROPERTY(int, scales, 6) + BR_PROPERTY(float, scaleFactor, 2) + BR_PROPERTY(float, stepFactor, 0.75) + BR_PROPERTY(int, minSize, 8) + BR_PROPERTY(bool, secondOrder, false) + + void project(const Template &src, Template &dst) const + { + typedef Eigen::Map< const Eigen::Matrix > InputDescriptor; + typedef Eigen::Map< const Eigen::Matrix > SecondOrderInputDescriptor; + typedef Eigen::Map< Eigen::Matrix > OutputDescriptor; + + const Mat &m = src.m(); + if (m.depth() != CV_32S) qFatal("Expected CV_32S matrix depth."); + const int channels = m.channels(); + const int rowStep = channels * m.cols; + + int descriptors = 0; + float idealSize = min(m.rows, m.cols)-1; + for (int scale=0; scale()/(currentSize*currentSize); + index++; + } + } + if (secondOrder) { + const int numDown = 1+(m.rows-currentSize-1)/currentStep; + const int numAcross = 1+(m.cols-currentSize-1)/currentStep; + const float *dataIn = n.ptr(index - numDown*numAcross); + for (int i=0; i + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Limit the size of the template + * \author Josh Klontz \cite jklontz + */ +class LimitSizeTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int max READ get_max WRITE set_max RESET reset_max STORED false) + BR_PROPERTY(int, max, -1) + + void project(const Template &src, Template &dst) const + { + const Mat &m = src; + if (m.rows > m.cols) + if (m.rows > max) resize(m, dst, Size(std::max(1, m.cols * max / m.rows), max)); + else dst = m; + else + if (m.cols > max) resize(m, dst, Size(max, std::max(1, m.rows * max / m.cols))); + else dst = m; + } +}; + +BR_REGISTER(Transform, LimitSizeTransform) + +} // namespace br + +#include "limitsize.moc" diff --git a/openbr/plugins/imgproc/madd.cpp b/openbr/plugins/imgproc/madd.cpp new file mode 100644 index 0000000..793f535 --- /dev/null +++ b/openbr/plugins/imgproc/madd.cpp @@ -0,0 +1,29 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief dst = a*src+b + * \author Josh Klontz \cite jklontz + */ +class MAddTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(double a READ get_a WRITE set_a RESET reset_a STORED false) + Q_PROPERTY(double b READ get_b WRITE set_b RESET reset_b STORED false) + BR_PROPERTY(double, a, 1) + BR_PROPERTY(double, b, 0) + + void project(const Template &src, Template &dst) const + { + src.m().convertTo(dst.m(), src.m().depth(), a, b); + } +}; + +BR_REGISTER(Transform, MAddTransform) + +} // namespace br + +#include "madd.moc" diff --git a/openbr/plugins/imgproc/mean.cpp b/openbr/plugins/imgproc/mean.cpp new file mode 100644 index 0000000..ca28ce3 --- /dev/null +++ b/openbr/plugins/imgproc/mean.cpp @@ -0,0 +1,45 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Computes the mean of a set of templates. + * \note Suitable for visualization only as it sets every projected template to the mean template. + * \author Scott Klum \cite sklum + */ +class MeanTransform : public Transform +{ + Q_OBJECT + + Mat mean; + + void train(const TemplateList &data) + { + mean = Mat::zeros(data[0].m().rows,data[0].m().cols,CV_32F); + + for (int i = 0; i < data.size(); i++) { + Mat converted; + data[i].m().convertTo(converted, CV_32F); + mean += converted; + } + + mean /= data.size(); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + dst.m() = mean; + } + +}; + +BR_REGISTER(Transform, MeanTransform) + +} // namespace br + +#include "mean.moc" diff --git a/openbr/plugins/imgproc/meanfill.cpp b/openbr/plugins/imgproc/meanfill.cpp new file mode 100644 index 0000000..5096c2b --- /dev/null +++ b/openbr/plugins/imgproc/meanfill.cpp @@ -0,0 +1,28 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Fill 0 pixels with the mean of non-0 pixels. + * \author Josh Klontz \cite jklontz + */ +class MeanFillTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst = src.m().clone(); + dst.m().setTo(mean(dst, dst.m()!=0), dst.m()==0); + } +}; + +BR_REGISTER(Transform, MeanFillTransform) + +} // namespace br + +#include "meanfill.moc" diff --git a/openbr/plugins/imgproc/pow.cpp b/openbr/plugins/imgproc/pow.cpp new file mode 100644 index 0000000..997f634 --- /dev/null +++ b/openbr/plugins/imgproc/pow.cpp @@ -0,0 +1,32 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Raise each element to the specified power. + * \author Josh Klontz \cite jklontz + */ +class PowTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(float power READ get_power WRITE set_power RESET reset_power STORED false) + Q_PROPERTY(bool preserveSign READ get_preserveSign WRITE set_preserveSign RESET reset_preserveSign STORED false) + BR_PROPERTY(float, power, 2) + BR_PROPERTY(bool, preserveSign, false) + + void project(const Template &src, Template &dst) const + { + pow(preserveSign ? abs(src) : src.m(), power, dst); + if (preserveSign) subtract(Scalar::all(0), dst, dst, src.m() < 0); + } +}; + +BR_REGISTER(Transform, PowTransform) + +} // namespace br + +#include "pow.moc" diff --git a/openbr/plugins/imgproc/rank.cpp b/openbr/plugins/imgproc/rank.cpp new file mode 100644 index 0000000..9e660e0 --- /dev/null +++ b/openbr/plugins/imgproc/rank.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Converts each element to its rank-ordered value. + * \author Josh Klontz \cite jklontz + */ +class RankTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + const Mat &m = src; + assert(m.channels() == 1); + dst = Mat(m.rows, m.cols, CV_32FC1); + typedef QPair Tuple; + QList tuples = Common::Sort(OpenCVUtils::matrixToVector(m)); + + float prevValue = 0; + int prevRank = 0; + for (int i=0; i(tuples[i].second / m.cols, tuples[i].second % m.cols) = rank; + prevValue = tuples[i].first; + prevRank = rank; + } + } +}; + +BR_REGISTER(Transform, RankTransform) + +} // namespace br + +#include "rank.moc" diff --git a/openbr/plugins/imgproc/recursiveintegralsampler.cpp b/openbr/plugins/imgproc/recursiveintegralsampler.cpp new file mode 100644 index 0000000..e3b7d27 --- /dev/null +++ b/openbr/plugins/imgproc/recursiveintegralsampler.cpp @@ -0,0 +1,162 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Construct template in a recursive decent manner. + * \author Josh Klontz \cite jklontz + */ +class RecursiveIntegralSamplerTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(int scales READ get_scales WRITE set_scales RESET reset_scales STORED false) + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(int, scales, 6) + BR_PROPERTY(float, scaleFactor, 2) + BR_PROPERTY(int, minSize, 8) + BR_PROPERTY(br::Transform*, transform, NULL) + + Transform *subTransform; + + typedef Eigen::Map< const Eigen::Matrix > InputDescriptor; + typedef Eigen::Map< Eigen::Matrix > OutputDescriptor; + typedef Eigen::Map< const Eigen::Matrix > SecondOrderInputDescriptor; + + void init() + { + if (scales >= 2) { + File subFile = file; + subFile.set("scales", scales-1); + subTransform = make(subFile.flat()); + } else { + subTransform = NULL; + } + } + + static void integralHistogram(const Mat &src, const int x, const int y, const int width, const int height, Mat &dst, int index) + { + const int channels = src.channels(); + OutputDescriptor(dst.ptr(index), channels, 1) = + ( InputDescriptor(src.ptr(y+height, x+width), channels, 1) + - InputDescriptor(src.ptr(y, x+width), channels, 1) + - InputDescriptor(src.ptr(y+height, x), channels, 1) + + InputDescriptor(src.ptr(y, x), channels, 1)).cast()/(height*width); + } + + void computeDescriptor(const Mat &src, Mat &dst) const + { + const int channels = src.channels(); + const int rows = src.rows-1; // Integral images have an extra row and column + const int columns = src.cols-1; + + Mat tmp(5, channels, CV_32FC1); + integralHistogram(src, 0, 0, columns/2, rows/2, tmp, 0); + integralHistogram(src, columns/2, 0, columns/2, rows/2, tmp, 1); + integralHistogram(src, 0, rows/2, columns/2, rows/2, tmp, 2); + integralHistogram(src, columns/2, rows/2, columns/2, rows/2, tmp, 3); + integralHistogram(src, columns/4, rows/4, columns/2, rows/2, tmp, 4); + const SecondOrderInputDescriptor a(tmp.ptr(0), channels, 1); + const SecondOrderInputDescriptor b(tmp.ptr(1), channels, 1); + const SecondOrderInputDescriptor c(tmp.ptr(2), channels, 1); + const SecondOrderInputDescriptor d(tmp.ptr(3), channels, 1); + const SecondOrderInputDescriptor e(tmp.ptr(4), channels, 1); + + dst = Mat(5, channels, CV_32FC1); + OutputDescriptor(dst.ptr(0), channels, 1) = (a+b+c+d)/4.f; + OutputDescriptor(dst.ptr(1), channels, 1) = ((a+b+c+d)/4.f-e); + OutputDescriptor(dst.ptr(2), channels, 1) = ((a+b)-(c+d))/2.f; + OutputDescriptor(dst.ptr(3), channels, 1) = ((a+c)-(b+d))/2.f; + OutputDescriptor(dst.ptr(4), channels, 1) = ((a+d)-(b+c))/2.f; + dst = dst.reshape(1, 1); + } + + Template subdivide(const Template &src) const + { + // Integral images have an extra row and column + int subWidth = (src.m().cols-1) / scaleFactor + 1; + int subHeight = (src.m().rows-1) / scaleFactor + 1; + return Template(src.file, QList() << Mat(src, Rect(0, 0, subWidth, subHeight)) + << Mat(src, Rect(src.m().cols-subWidth, 0, subWidth, subHeight)) + << Mat(src, Rect(0, src.m().rows-subHeight, subWidth, subHeight)) + << Mat(src, Rect(src.m().cols-subWidth, src.m().rows-subHeight, subWidth, subHeight))); + } + + bool canSubdivide(const Template &t) const + { + // Integral images have an extra row and column + const int subWidth = (t.m().cols-1) / scaleFactor; + const int subHeight = (t.m().rows-1) / scaleFactor; + return ((subWidth >= minSize) && (subHeight >= minSize)); + } + + void train(const TemplateList &src) + { + if (src.first().m().depth() != CV_32S) + qFatal("Expected CV_32S depth!"); + + if (subTransform != NULL) { + TemplateList subSrc; subSrc.reserve(src.size()); + foreach (const Template &t, src) + if (canSubdivide(t)) + subSrc.append(subdivide(t)); + + if (subSrc.isEmpty()) { + delete subTransform; + subTransform = NULL; + } else { + subTransform->train(subSrc); + } + } + + TemplateList dst; dst.reserve(src.size()); + foreach (const Template &t, src) { + Template u(t.file); + computeDescriptor(t, u); + dst.append(u); + } + transform->train(dst); + } + + void project(const Template &src, Template &dst) const + { + computeDescriptor(src, dst); + transform->project(dst, dst); + + if ((subTransform != NULL) && canSubdivide(src)) { + Template subDst; + subTransform->project(subdivide(src), subDst); + dst.append(subDst); + } + } + + void store(QDataStream &stream) const + { + transform->store(stream); + stream << (subTransform != NULL); + if (subTransform != NULL) + subTransform->store(stream); + } + + void load(QDataStream &stream) + { + transform->load(stream); + bool hasSubTransform; + stream >> hasSubTransform; + if (hasSubTransform) subTransform->load(stream); + else { delete subTransform; subTransform = NULL; } + } +}; + +BR_REGISTER(Transform, RecursiveIntegralSamplerTransform) + +} // namespace br + +#include "recursiveintegralsampler.moc" diff --git a/openbr/plugins/imgproc/resize.cpp b/openbr/plugins/imgproc/resize.cpp new file mode 100644 index 0000000..b1cf93f --- /dev/null +++ b/openbr/plugins/imgproc/resize.cpp @@ -0,0 +1,68 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Resize the template + * \author Josh Klontz \cite jklontz + * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement. + * \param preserveAspect If true, the image will be sized per specification, but + * a border will be applied to preserve aspect ratio. + */ +class ResizeTransform : public UntrainableTransform +{ + Q_OBJECT + Q_ENUMS(Method) + +public: + /*!< */ + enum Method { Near = INTER_NEAREST, + Area = INTER_AREA, + Bilin = INTER_LINEAR, + Cubic = INTER_CUBIC, + Lanczo = INTER_LANCZOS4}; + +private: + Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) + Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) + Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) + Q_PROPERTY(bool preserveAspect READ get_preserveAspect WRITE set_preserveAspect RESET reset_preserveAspect STORED false) + BR_PROPERTY(int, rows, -1) + BR_PROPERTY(int, columns, -1) + BR_PROPERTY(Method, method, Bilin) + BR_PROPERTY(bool, preserveAspect, false) + + void project(const Template &src, Template &dst) const + { + if (!preserveAspect) + resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method); + else { + float inRatio = (float) src.m().rows / src.m().cols; + float outRatio = (float) rows / columns; + dst = Mat::zeros(rows, columns, src.m().type()); + if (outRatio > inRatio) { + float heightAR = src.m().rows * inRatio / outRatio; + Mat buffer; + resize(src, buffer, Size(columns, heightAR), 0, 0, method); + buffer.copyTo(dst.m()(Rect(0, (rows - heightAR) / 2, columns, heightAR))); + } else { + float widthAR = src.m().cols / inRatio * outRatio; + Mat buffer; + resize(src, buffer, Size(widthAR, rows), 0, 0, method); + buffer.copyTo(dst.m()(Rect((columns - widthAR) / 2, 0, widthAR, rows))); + } + } + } +}; + +BR_REGISTER(Transform, ResizeTransform) + +} // namespace br + +#include "resize.moc" diff --git a/openbr/plugins/imgproc/rg.cpp b/openbr/plugins/imgproc/rg.cpp new file mode 100644 index 0000000..aa890d4 --- /dev/null +++ b/openbr/plugins/imgproc/rg.cpp @@ -0,0 +1,51 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Normalized RG color space. + * \author Josh Klontz \cite jklontz + */ +class RGTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + if (src.m().type() != CV_8UC3) + qFatal("Expected CV_8UC3 images."); + + const Mat &m = src.m(); + Mat R(m.size(), CV_8UC1); // R / (R+G+B) + Mat G(m.size(), CV_8UC1); // G / (R+G+B) + + for (int i=0; i(i,j); + const int b = v[0]; + const int g = v[1]; + const int r = v[2]; + const int sum = b + g + r; + if (sum > 0) { + R.at(i, j) = saturate_cast(255.0*r/(r+g+b)); + G.at(i, j) = saturate_cast(255.0*g/(r+g+b)); + } else { + R.at(i, j) = 0; + G.at(i, j) = 0; + } + } + + dst.append(R); + dst.append(G); + } +}; + +BR_REGISTER(Transform, RGTransform) + +} // namespace br + +#include "rg.moc" diff --git a/openbr/plugins/imgproc/roi.cpp b/openbr/plugins/imgproc/roi.cpp new file mode 100644 index 0000000..9eba1a0 --- /dev/null +++ b/openbr/plugins/imgproc/roi.cpp @@ -0,0 +1,46 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Crops the rectangular regions of interest. + * \author Josh Klontz \cite jklontz + */ +class ROITransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) + BR_PROPERTY(QString, propName, "") + + void project(const Template &src, Template &dst) const + { + if (!propName.isEmpty()) { + QRectF rect = src.file.get(propName); + dst += src.m()(OpenCVUtils::toRect(rect)); + } else if (!src.file.rects().empty()) { + foreach (const QRectF &rect, src.file.rects()) + dst += src.m()(OpenCVUtils::toRect(rect)); + } else if (src.file.contains(QStringList() << "X" << "Y" << "Width" << "Height")) { + dst += src.m()(Rect(src.file.get("X"), + src.file.get("Y"), + src.file.get("Width"), + src.file.get("Height"))); + } else { + dst = src; + if (Globals->verbose) + qWarning("No rects present in file."); + } + dst.file.clearRects(); + } +}; + +BR_REGISTER(Transform, ROITransform) + +} // namespace br + +#include "roi.moc" diff --git a/openbr/plugins/imgproc/roifrompoints.cpp b/openbr/plugins/imgproc/roifrompoints.cpp new file mode 100644 index 0000000..1f655c7 --- /dev/null +++ b/openbr/plugins/imgproc/roifrompoints.cpp @@ -0,0 +1,35 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Crops the rectangular regions of interest from given points and sizes. + * \author Austin Blanton \cite imaus10 + */ +class ROIFromPtsTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) + Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) + BR_PROPERTY(int, width, 1) + BR_PROPERTY(int, height, 1) + + void project(const Template &src, Template &dst) const + { + foreach (const QPointF &pt, src.file.points()) { + int x = pt.x() - (width/2); + int y = pt.y() - (height/2); + dst += src.m()(Rect(x, y, width, height)); + } + } +}; + +BR_REGISTER(Transform, ROIFromPtsTransform) + +} // namespace br + +#include "roifrompoints.moc" diff --git a/openbr/plugins/imgproc/scale.cpp b/openbr/plugins/imgproc/scale.cpp new file mode 100644 index 0000000..5036220 --- /dev/null +++ b/openbr/plugins/imgproc/scale.cpp @@ -0,0 +1,32 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Scales using the given factor + * \author Scott Klum \cite sklum + */ +class ScaleTransform : public UntrainableTransform +{ + Q_OBJECT + + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) + BR_PROPERTY(float, scaleFactor, 1.) + + void project(const Template &src, Template &dst) const + { + resize(src, dst, Size(src.m().cols*scaleFactor,src.m().rows*scaleFactor)); + } +}; + +BR_REGISTER(Transform, ScaleTransform) + +} // namespace br + +#include "scale.moc" diff --git a/openbr/plugins/imgproc/splitchannels.cpp b/openbr/plugins/imgproc/splitchannels.cpp new file mode 100644 index 0000000..17f2c83 --- /dev/null +++ b/openbr/plugins/imgproc/splitchannels.cpp @@ -0,0 +1,32 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Split a multi-channel matrix into several single-channel matrices. + * \author Josh Klontz \cite jklontz + */ +class SplitChannelsTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + std::vector mv; + split(src, mv); + foreach (const Mat &m, mv) + dst += m; + } +}; + +BR_REGISTER(Transform, SplitChannelsTransform) + +} // namespace br + +#include "splitchannels.moc" diff --git a/openbr/plugins/imgproc/subdivide.cpp b/openbr/plugins/imgproc/subdivide.cpp new file mode 100644 index 0000000..847e859 --- /dev/null +++ b/openbr/plugins/imgproc/subdivide.cpp @@ -0,0 +1,33 @@ +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Divide the matrix into 4 smaller matricies of equal size. + * \author Josh Klontz \cite jklontz + */ +class SubdivideTransform : public UntrainableTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + const Mat &m = src; + const int subrows = m.rows/2; + const int subcolumns = m.cols/2; + dst.append(Mat(m,Rect(0, 0, subcolumns, subrows)).clone()); + dst.append(Mat(m,Rect(subcolumns, 0, subcolumns, subrows)).clone()); + dst.append(Mat(m,Rect(0, subrows, subcolumns, subrows)).clone()); + dst.append(Mat(m,Rect(subcolumns, subrows, subcolumns, subrows)).clone()); + } +}; + +BR_REGISTER(Transform, SubdivideTransform) + +} // namespace br + +#include "subdivide.moc" diff --git a/openbr/plugins/independent.cpp b/openbr/plugins/independent.cpp deleted file mode 100644 index 7ac93ce..0000000 --- a/openbr/plugins/independent.cpp +++ /dev/null @@ -1,390 +0,0 @@ -#include -#include - -#include "openbr_internal.h" -#include "openbr/core/common.h" - -using namespace cv; - -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) - -/*! - * \ingroup transforms - * \brief Clones the transform so that it can be applied independently. - * \author Josh Klontz \cite jklontz - * \em Independent transforms expect single-matrix templates. - */ -class IndependentTransform : public MetaTransform -{ - Q_OBJECT - Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED false) - BR_PROPERTY(br::Transform*, transform, NULL) - - QList transforms; - - QString description(bool expanded) const - { - return transform->description(expanded); - } - - // can't use general setPropertyRecursive because of transforms oddness - bool setPropertyRecursive(const QString &name, QVariant value) - { - if (br::Object::setExistingProperty(name, value)) - return true; - - if (!transform->setPropertyRecursive(name, value)) - return false; - - for (int i=0;i < transforms.size();i++) - transforms[i]->setPropertyRecursive(name, value); - - return true; - } - - Transform *simplify(bool &newTransform) - { - newTransform = false; - bool newChild = false; - Transform *temp = transform->simplify(newChild); - if (temp == transform) { - return this; - } - IndependentTransform* indep = new IndependentTransform(); - indep->transform = temp; - - IndependentTransform *test = dynamic_cast (temp); - if (test) { - // child was independent? this changes things... - indep->transform = test->transform; - for (int i=0; i < transforms.size(); i++) { - bool newThing = false; - IndependentTransform *probe = dynamic_cast (transforms[i]->simplify(newThing)); - indep->transforms.append(probe->transform); - if (newThing) - probe->setParent(indep); - } - indep->file = indep->transform->file; - indep->trainable = indep->transform->trainable; - indep->setObjectName(indep->transform->objectName()); - - return indep; - } - - if (newChild) - indep->transform->setParent(indep); - - for (int i=0; i < transforms.size();i++) { - bool subTform = false; - indep->transforms.append(transforms[i]->simplify(subTform)); - if (subTform) - indep->transforms[i]->setParent(indep); - } - - indep->file = indep->transform->file; - indep->trainable = indep->transform->trainable; - indep->setObjectName(indep->transform->objectName()); - - return indep; - } - - void init() - { - transforms.clear(); - if (transform == NULL) - return; - - transform->setParent(this); - transforms.append(transform); - file = transform->file; - trainable = transform->trainable; - setObjectName(transform->objectName()); - } - - Transform *clone() const - { - IndependentTransform *independentTransform = new IndependentTransform(); - independentTransform->transform = transform->clone(); - independentTransform->init(); - return independentTransform; - } - - bool timeVarying() const { return transform->timeVarying(); } - - static void _train(Transform *transform, const TemplateList *data) - { - transform->train(*data); - } - - void train(const TemplateList &data) - { - // Don't bother if the transform is untrainable - if (!trainable) return; - - QList templatesList; - foreach (const Template &t, data) { - if ((templatesList.size() != t.size()) && !templatesList.isEmpty()) - qWarning("Independent::train (%s) template %s of size %d differs from expected size %d.", qPrintable(objectName()), qPrintable(t.file.name), t.size(), templatesList.size()); - while (templatesList.size() < t.size()) - templatesList.append(TemplateList()); - for (int i=0; iclone()); - - QFutureSynchronizer futures; - for (int i=0; i mats; - for (int i=0; iproject(Template(src.file, src[i]), dst); - mats.append(dst); - dst.clear(); - } - dst.append(mats); - } - - void projectUpdate(const Template &src, Template &dst) - { - dst.file = src.file; - QList mats; - for (int i=0; iprojectUpdate(Template(src.file, src[i]), dst); - mats.append(dst); - dst.clear(); - } - dst.append(mats); - } - - void finalize(TemplateList &out) - { - if (transforms.empty()) - return; - - transforms[0]->finalize(out); - for (int i=1; i < transforms.size(); i++) { - TemplateList temp; - transforms[i]->finalize(temp); - - for (int j=0; j < out.size(); j++) - out[j].append(temp[j]); - } - } - - void projectUpdate(const TemplateList &src, TemplateList &dst) - { - dst.reserve(src.size()); - foreach (const Template &t, src) { - dst.append(Template()); - projectUpdate(t, dst.last()); - } - } - - void store(QDataStream &stream) const - { - const int size = transforms.size(); - stream << size; - for (int i=0; istore(stream); - } - - void load(QDataStream &stream) - { - int size; - stream >> size; - while (transforms.size() < size) - transforms.append(transform->clone()); - for (int i=0; iload(stream); - } -}; - -BR_REGISTER(Transform, IndependentTransform) - -/*! - * \ingroup transforms - * \brief A globally shared transform. - * \author Josh Klontz \cite jklontz - */ -class SingletonTransform : public MetaTransform -{ - Q_OBJECT - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) - BR_PROPERTY(QString, description, "Identity") - - static QMutex mutex; - static QHash transforms; - static QHash trainingReferenceCounts; - static QHash trainingData; - - Transform *transform; - - void init() - { - QMutexLocker locker(&mutex); - if (!transforms.contains(description)) { - transforms.insert(description, make(description)); - trainingReferenceCounts.insert(description, 0); - } - - transform = transforms[description]; - trainingReferenceCounts[description]++; - } - - void train(const TemplateList &data) - { - QMutexLocker locker(&mutex); - trainingData[description].append(data); - trainingReferenceCounts[description]--; - if (trainingReferenceCounts[description] > 0) return; - transform->train(trainingData[description]); - trainingData[description].clear(); - } - - void project(const Template &src, Template &dst) const - { - transform->project(src, dst); - } - - void store(QDataStream &stream) const - { - if (transform->parent() == this) - transform->store(stream); - } - - void load(QDataStream &stream) - { - if (transform->parent() == this) - transform->load(stream); - } -}; - -QMutex SingletonTransform::mutex; -QHash SingletonTransform::transforms; -QHash SingletonTransform::trainingReferenceCounts; -QHash SingletonTransform::trainingData; - -BR_REGISTER(Transform, SingletonTransform) - -} // namespace br - -#include "independent.moc" diff --git a/openbr/plugins/integral.cpp b/openbr/plugins/integral.cpp deleted file mode 100644 index f6117a0..0000000 --- a/openbr/plugins/integral.cpp +++ /dev/null @@ -1,374 +0,0 @@ -#include -#include -#include -#include "openbr_internal.h" - -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Computes integral image. - * \author Josh Klontz \cite jklontz - */ -class IntegralTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - integral(src, dst); - } -}; - -BR_REGISTER(Transform, IntegralTransform) - -/*! - * \ingroup transforms - * \brief Sliding window feature extraction from a multi-channel integral image. - * \author Josh Klontz \cite jklontz - */ -class IntegralSamplerTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int scales READ get_scales WRITE set_scales RESET reset_scales STORED false) - Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) - Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) - Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) - Q_PROPERTY(bool secondOrder READ get_secondOrder WRITE set_secondOrder RESET reset_secondOrder STORED false) - BR_PROPERTY(int, scales, 6) - BR_PROPERTY(float, scaleFactor, 2) - BR_PROPERTY(float, stepFactor, 0.75) - BR_PROPERTY(int, minSize, 8) - BR_PROPERTY(bool, secondOrder, false) - - void project(const Template &src, Template &dst) const - { - typedef Eigen::Map< const Eigen::Matrix > InputDescriptor; - typedef Eigen::Map< const Eigen::Matrix > SecondOrderInputDescriptor; - typedef Eigen::Map< Eigen::Matrix > OutputDescriptor; - - const Mat &m = src.m(); - if (m.depth() != CV_32S) qFatal("Expected CV_32S matrix depth."); - const int channels = m.channels(); - const int rowStep = channels * m.cols; - - int descriptors = 0; - float idealSize = min(m.rows, m.cols)-1; - for (int scale=0; scale()/(currentSize*currentSize); - index++; - } - } - if (secondOrder) { - const int numDown = 1+(m.rows-currentSize-1)/currentStep; - const int numAcross = 1+(m.cols-currentSize-1)/currentStep; - const float *dataIn = n.ptr(index - numDown*numAcross); - for (int i=0; i > InputDescriptor; - typedef Eigen::Map< Eigen::Matrix > OutputDescriptor; - typedef Eigen::Map< const Eigen::Matrix > SecondOrderInputDescriptor; - - void init() - { - if (scales >= 2) { - File subFile = file; - subFile.set("scales", scales-1); - subTransform = make(subFile.flat()); - } else { - subTransform = NULL; - } - } - - static void integralHistogram(const Mat &src, const int x, const int y, const int width, const int height, Mat &dst, int index) - { - const int channels = src.channels(); - OutputDescriptor(dst.ptr(index), channels, 1) = - ( InputDescriptor(src.ptr(y+height, x+width), channels, 1) - - InputDescriptor(src.ptr(y, x+width), channels, 1) - - InputDescriptor(src.ptr(y+height, x), channels, 1) - + InputDescriptor(src.ptr(y, x), channels, 1)).cast()/(height*width); - } - - void computeDescriptor(const Mat &src, Mat &dst) const - { - const int channels = src.channels(); - const int rows = src.rows-1; // Integral images have an extra row and column - const int columns = src.cols-1; - - Mat tmp(5, channels, CV_32FC1); - integralHistogram(src, 0, 0, columns/2, rows/2, tmp, 0); - integralHistogram(src, columns/2, 0, columns/2, rows/2, tmp, 1); - integralHistogram(src, 0, rows/2, columns/2, rows/2, tmp, 2); - integralHistogram(src, columns/2, rows/2, columns/2, rows/2, tmp, 3); - integralHistogram(src, columns/4, rows/4, columns/2, rows/2, tmp, 4); - const SecondOrderInputDescriptor a(tmp.ptr(0), channels, 1); - const SecondOrderInputDescriptor b(tmp.ptr(1), channels, 1); - const SecondOrderInputDescriptor c(tmp.ptr(2), channels, 1); - const SecondOrderInputDescriptor d(tmp.ptr(3), channels, 1); - const SecondOrderInputDescriptor e(tmp.ptr(4), channels, 1); - - dst = Mat(5, channels, CV_32FC1); - OutputDescriptor(dst.ptr(0), channels, 1) = (a+b+c+d)/4.f; - OutputDescriptor(dst.ptr(1), channels, 1) = ((a+b+c+d)/4.f-e); - OutputDescriptor(dst.ptr(2), channels, 1) = ((a+b)-(c+d))/2.f; - OutputDescriptor(dst.ptr(3), channels, 1) = ((a+c)-(b+d))/2.f; - OutputDescriptor(dst.ptr(4), channels, 1) = ((a+d)-(b+c))/2.f; - dst = dst.reshape(1, 1); - } - - Template subdivide(const Template &src) const - { - // Integral images have an extra row and column - int subWidth = (src.m().cols-1) / scaleFactor + 1; - int subHeight = (src.m().rows-1) / scaleFactor + 1; - return Template(src.file, QList() << Mat(src, Rect(0, 0, subWidth, subHeight)) - << Mat(src, Rect(src.m().cols-subWidth, 0, subWidth, subHeight)) - << Mat(src, Rect(0, src.m().rows-subHeight, subWidth, subHeight)) - << Mat(src, Rect(src.m().cols-subWidth, src.m().rows-subHeight, subWidth, subHeight))); - } - - bool canSubdivide(const Template &t) const - { - // Integral images have an extra row and column - const int subWidth = (t.m().cols-1) / scaleFactor; - const int subHeight = (t.m().rows-1) / scaleFactor; - return ((subWidth >= minSize) && (subHeight >= minSize)); - } - - void train(const TemplateList &src) - { - if (src.first().m().depth() != CV_32S) - qFatal("Expected CV_32S depth!"); - - if (subTransform != NULL) { - TemplateList subSrc; subSrc.reserve(src.size()); - foreach (const Template &t, src) - if (canSubdivide(t)) - subSrc.append(subdivide(t)); - - if (subSrc.isEmpty()) { - delete subTransform; - subTransform = NULL; - } else { - subTransform->train(subSrc); - } - } - - TemplateList dst; dst.reserve(src.size()); - foreach (const Template &t, src) { - Template u(t.file); - computeDescriptor(t, u); - dst.append(u); - } - transform->train(dst); - } - - void project(const Template &src, Template &dst) const - { - computeDescriptor(src, dst); - transform->project(dst, dst); - - if ((subTransform != NULL) && canSubdivide(src)) { - Template subDst; - subTransform->project(subdivide(src), subDst); - dst.append(subDst); - } - } - - void store(QDataStream &stream) const - { - transform->store(stream); - stream << (subTransform != NULL); - if (subTransform != NULL) - subTransform->store(stream); - } - - void load(QDataStream &stream) - { - transform->load(stream); - bool hasSubTransform; - stream >> hasSubTransform; - if (hasSubTransform) subTransform->load(stream); - else { delete subTransform; subTransform = NULL; } - } -}; - -BR_REGISTER(Transform, RecursiveIntegralSamplerTransform) - -/*! - * \ingroup transforms - * \brief Computes magnitude and/or angle of image. - * \author Josh Klontz \cite jklontz - */ -class GradientTransform : public UntrainableTransform -{ - Q_OBJECT - Q_ENUMS(Channel) - Q_PROPERTY(Channel channel READ get_channel WRITE set_channel RESET reset_channel STORED false) - -public: - enum Channel { Magnitude, Angle, MagnitudeAndAngle }; - -private: - BR_PROPERTY(Channel, channel, Angle) - - void project(const Template &src, Template &dst) const - { - Mat dx, dy, magnitude, angle; - Sobel(src, dx, CV_32F, 1, 0, CV_SCHARR); - Sobel(src, dy, CV_32F, 0, 1, CV_SCHARR); - cartToPolar(dx, dy, magnitude, angle, true); - std::vector mv; - if ((channel == Magnitude) || (channel == MagnitudeAndAngle)) { - const float theoreticalMaxMagnitude = sqrt(2*pow(float(2*(3+10+3)*255), 2.f)); - mv.push_back(magnitude / theoreticalMaxMagnitude); - } - if ((channel == Angle) || (channel == MagnitudeAndAngle)) - mv.push_back(angle); - Mat result; - merge(mv, result); - dst.append(result); - } -}; - -BR_REGISTER(Transform, GradientTransform) - -/*! - * \ingroup transforms - * \brief Projects each row based on a computed word. - * \author Josh Klontz \cite jklontz - */ -class WordWiseTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(br::Transform* getWords READ get_getWords WRITE set_getWords RESET reset_getWords) - Q_PROPERTY(br::Transform* byWord READ get_byWord WRITE set_byWord RESET reset_byWord) - Q_PROPERTY(int numWords READ get_numWords WRITE set_numWords RESET reset_numWords) - BR_PROPERTY(br::Transform*, getWords, NULL) - BR_PROPERTY(br::Transform*, byWord, NULL) - BR_PROPERTY(int, numWords, 0) - - void train(const TemplateList &data) - { - getWords->train(data); - TemplateList bins; - getWords->project(data, bins); - - numWords = 0; - foreach (const Template &t, bins) { - double minVal, maxVal; - minMaxLoc(t, &minVal, &maxVal); - numWords = max(numWords, int(maxVal)+1); - } - - TemplateList reworded; reworded.reserve(data.size()); - foreach (const Template &t, data) - reworded.append(reword(t)); - byWord->train(reworded); - } - - void project(const Template &src, Template &dst) const - { - byWord->project(reword(src), dst); - } - - Template reword(const Template &src) const - { - Template words; - getWords->project(src, words); - QVector wordCounts(numWords, 0); - for (int i=0; i(i,0)]++; - Template reworded(src.file); reworded.reserve(numWords); - for (int i=0; i indicies(numWords, 0); - for (int i=0; i(i,0); - src.m().row(i).copyTo(reworded[word].row(indicies[word]++)); - } - return reworded; - } -}; - -BR_REGISTER(Transform, WordWiseTransform) - -} // namespace br - -#include "integral.moc" diff --git a/openbr/plugins/io/write.cpp b/openbr/plugins/io/write.cpp new file mode 100644 index 0000000..d94a9cc --- /dev/null +++ b/openbr/plugins/io/write.cpp @@ -0,0 +1,44 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Write all mats to disk as images. + * \author Brendan Klare \cite bklare + */ +class WriteTransform : public TimeVaryingTransform +{ + Q_OBJECT + Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false) + Q_PROPERTY(QString imageName READ get_imageName WRITE set_imageName RESET reset_imageName STORED false) + Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false) + BR_PROPERTY(QString, outputDirectory, "Temp") + BR_PROPERTY(QString, imageName, "image") + BR_PROPERTY(QString, imgExtension, "jpg") + + int cnt; + + void init() { + cnt = 0; + if (! QDir(outputDirectory).exists()) + QDir().mkdir(outputDirectory); + } + + void projectUpdate(const Template &src, Template &dst) + { + dst = src; + OpenCVUtils::saveImage(dst.m(), QString("%1/%2_%3.%4").arg(outputDirectory).arg(imageName).arg(cnt++, 5, 10, QChar('0')).arg(imgExtension)); + } + +}; + +BR_REGISTER(Transform, WriteTransform) + +} // namespace br + +#include "write.moc" diff --git a/openbr/plugins/keypoint.cpp b/openbr/plugins/keypoint.cpp deleted file mode 100644 index 6c3d88d..0000000 --- a/openbr/plugins/keypoint.cpp +++ /dev/null @@ -1,258 +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 -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" - -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) - -/*! - * \ingroup transforms - * \brief Wraps OpenCV Key Point Descriptor - * \author Josh Klontz \cite jklontz - */ -class KeyPointDescriptorTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString descriptor READ get_descriptor WRITE set_descriptor RESET reset_descriptor STORED false) - Q_PROPERTY(int size READ get_size WRITE set_size RESET reset_size STORED false) - BR_PROPERTY(QString, descriptor, "SIFT") - BR_PROPERTY(int, size, -1) - - Ptr descriptorExtractor; - - void init() - { - descriptorExtractor = DescriptorExtractor::create(descriptor.toStdString()); - if (descriptorExtractor.empty()) - qFatal("Failed to create DescriptorExtractor: %s", qPrintable(descriptor)); - } - - void project(const Template &src, Template &dst) const - { - std::vector keyPoints; - if (size == -1) - foreach (const QRectF &ROI, src.file.rects()) - keyPoints.push_back(KeyPoint(ROI.x()+ROI.width()/2, ROI.y()+ROI.height()/2, (ROI.width() + ROI.height())/2)); - else - foreach (const QPointF &landmark, src.file.points()) - keyPoints.push_back(KeyPoint(landmark.x(), landmark.y(), size)); - if (keyPoints.empty()) return; - descriptorExtractor->compute(src, keyPoints, dst); - } -}; - -BR_REGISTER(Transform, KeyPointDescriptorTransform) - -/*! - * \ingroup transforms - * \brief Wraps OpenCV Key Point Matcher - * \author Josh Klontz \cite jklontz - */ -class KeyPointMatcherDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(QString matcher READ get_matcher WRITE set_matcher RESET reset_matcher STORED false) - Q_PROPERTY(float maxRatio READ get_maxRatio WRITE set_maxRatio RESET reset_maxRatio STORED false) - BR_PROPERTY(QString, matcher, "BruteForce") - BR_PROPERTY(float, maxRatio, 0.8) - - Ptr descriptorMatcher; - - void init() - { - descriptorMatcher = DescriptorMatcher::create(matcher.toStdString()); - if (descriptorMatcher.empty()) - qFatal("Failed to create DescriptorMatcher: %s", qPrintable(matcher)); - } - - float compare(const Mat &a, const Mat &b) const - { - if ((a.rows < 2) || (b.rows < 2)) return 0; - - std::vector< std::vector > matches; - if (a.rows < b.rows) descriptorMatcher->knnMatch(a, b, matches, 2); - else descriptorMatcher->knnMatch(b, a, matches, 2); - - QList distances; - foreach (const std::vector &match, matches) { - if (match[0].distance / match[1].distance > maxRatio) continue; - distances.append(match[0].distance); - } - qSort(distances); - - float similarity = 0; - for (int i=0; i sizes READ get_sizes WRITE set_sizes RESET reset_sizes STORED false) - Q_PROPERTY(int nFeatures READ get_nFeatures WRITE set_nFeatures RESET reset_nFeatures STORED false) - Q_PROPERTY(int nOctaveLayers READ get_nOctaveLayers WRITE set_nOctaveLayers RESET reset_nOctaveLayers STORED false) - Q_PROPERTY(double contrastThreshold READ get_contrastThreshold WRITE set_contrastThreshold RESET reset_contrastThreshold STORED false) - Q_PROPERTY(double edgeThreshold READ get_edgeThreshold WRITE set_edgeThreshold RESET reset_edgeThreshold STORED false) - Q_PROPERTY(double sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) - BR_PROPERTY(int, size, 1) - BR_PROPERTY(QList, sizes, QList()) - BR_PROPERTY(int, nFeatures, 0) - BR_PROPERTY(int, nOctaveLayers, 3) - BR_PROPERTY(double, contrastThreshold, 0.04) - BR_PROPERTY(double, edgeThreshold, 10) - BR_PROPERTY(double, sigma, 1.6) - - SIFT sift; - - void init() - { - if (sizes.empty()) - sizes.append(size); - sift = SIFT(nFeatures, nOctaveLayers, contrastThreshold, edgeThreshold, sigma); - } - - void project(const Template &src, Template &dst) const - { - std::vector keyPoints; - foreach (const QPointF &val, src.file.points()) - foreach (const int r, sizes) - keyPoints.push_back(KeyPoint(val.x(), val.y(), r)); - - Mat m; - sift(src, Mat(), keyPoints, m, true); - m.setTo(0, m<0); // SIFT returns large negative values when it goes off the edge of the image. - dst += m; - } -}; - -BR_REGISTER(Transform, SIFTDescriptorTransform) - -/*! - * \ingroup transforms - * \brief OpenCV HOGDescriptor wrapper - * \author Austin Blanton \cite imaus10 - */ -class HoGDescriptorTransform : public UntrainableTransform -{ - Q_OBJECT - - HOGDescriptor hog; - - void project(const Template &src, Template &dst) const - { - std::vector descriptorVals; - std::vector locations; - Size winStride = Size(0,0); - Size padding = Size(0,0); - foreach (const Mat &rect, src) { - hog.compute(rect, descriptorVals, winStride, padding, locations); - Mat HoGFeats(descriptorVals, true); - dst += HoGFeats; - } - } -}; - -BR_REGISTER(Transform, HoGDescriptorTransform) - -/*! - * \ingroup transforms - * \brief Add landmarks to the template in a grid layout - * \author Josh Klontz \cite jklontz - */ -class GridTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) - Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) - BR_PROPERTY(int, rows, 1) - BR_PROPERTY(int, columns, 1) - - void project(const Template &src, Template &dst) const - { - QList landmarks; - const float row_step = 1.f * src.m().rows / rows; - const float column_step = 1.f * src.m().cols / columns; - for (float y=row_step/2; y rects = src.rects(); + if (rects.size() != 1) + qFatal("Must have one and only one rect per template"); + QRectF rect = rects.first(); + + QList srcPoints = src.points(); + QList dstPoints; + foreach (const QPointF &point, srcPoints) + dstPoints.append(QPointF(point.x() - rect.x(), point.y() - rect.y())); + + dst.setPoints(dstPoints); + } +}; + +BR_REGISTER(Transform, SetPointsInRectTransform) + /*! * \ingroup transforms * \brief Normalize points to be relative to a single point diff --git a/openbr/plugins/lbp.cpp b/openbr/plugins/lbp.cpp index b295286..1073b0e 100644 --- a/openbr/plugins/lbp.cpp +++ b/openbr/plugins/lbp.cpp @@ -46,8 +46,6 @@ class LBPTransform : public UntrainableTransform uchar lut[256]; uchar null; - friend class ColoredU2Transform; - /* Returns the number of 0->1 or 1->0 transitions in i */ static int numTransitions(int i) { @@ -125,74 +123,6 @@ class LBPTransform : public UntrainableTransform BR_REGISTER(Transform, LBPTransform) -/*! - * \ingroup transforms - * \brief For visualization of LBP patterns. - * \author Josh Klontz \cite jklontz - */ -class ColoredU2Transform : public UntrainableTransform -{ - Q_OBJECT - - /* Returns the number of 1 bits in i */ - static int bitCount(int i) - { - int count = 0; - for (int j=0; j<8; j++) - count += (i>>j)%2; - return count; - } - - void project(const Template &src, Template &dst) const - { - static Mat hueLUT, saturationLUT, valueLUT; - - if (!hueLUT.data) { - const int NUM_COLORS = 10; - hueLUT.create(1, 256, CV_8UC1); - hueLUT.setTo(0); - - uchar uid = 0; - for (int i=0; i<256; i++) { - const int transitions = LBPTransform::numTransitions(i); - int u2; - if (transitions <= 2) u2 = uid++; - else u2 = 58; - - // Assign hue based on bit count - int color = bitCount(i); - if (transitions > 2) color = NUM_COLORS-1; - hueLUT.at(0, u2) = 255*color/NUM_COLORS; - } - - saturationLUT.create(1, 256, CV_8UC1); - saturationLUT.setTo(255); - - valueLUT.create(1, 256, CV_8UC1); - valueLUT.setTo(255*3/4); - } - - if (src.m().type() != CV_8UC1) - qFatal("Expected 8UC1 source type."); - - Mat hue, saturation, value; - LUT(src, hueLUT, hue); - LUT(src, saturationLUT, saturation); - LUT(src, valueLUT, value); - - std::vector mv; - mv.push_back(hue); - mv.push_back(saturation); - mv.push_back(value); - - Mat coloredU2; - merge(mv, coloredU2); - cvtColor(coloredU2, dst, CV_HSV2BGR); - } -}; - -BR_REGISTER(Transform, ColoredU2Transform) - } // namespace br #include "lbp.moc" diff --git a/openbr/plugins/metadata/grid.cpp b/openbr/plugins/metadata/grid.cpp new file mode 100644 index 0000000..27484e9 --- /dev/null +++ b/openbr/plugins/metadata/grid.cpp @@ -0,0 +1,36 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Add landmarks to the template in a grid layout + * \author Josh Klontz \cite jklontz + */ +class GridTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) + Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) + BR_PROPERTY(int, rows, 1) + BR_PROPERTY(int, columns, 1) + + void project(const Template &src, Template &dst) const + { + QList landmarks; + const float row_step = 1.f * src.m().rows / rows; + const float column_step = 1.f * src.m().cols / columns; + for (float y=row_step/2; y + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Convert values of key_X, key_Y, key_Width, key_Height to a rect. + * \author Jordan Cheney \cite JordanCheney + */ +class KeyToRectTransform : public UntrainableMetadataTransform +{ + Q_OBJECT + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) + BR_PROPERTY(QString, key, "") + + void projectMetadata(const File &src, File &dst) const + { + dst = src; + + if (src.contains(QStringList() << key + "_X" << key + "_Y" << key + "_Width" << key + "_Height")) + dst.appendRect(QRectF(src.get(key + "_X"), + src.get(key + "_Y"), + src.get(key + "_Width"), + src.get(key + "_Height"))); + + } + +}; + +BR_REGISTER(Transform, KeyToRectTransform) + +} // namespace br + +#include "keytorect.moc" diff --git a/openbr/plugins/metadata/procrustes.cpp b/openbr/plugins/metadata/procrustes.cpp new file mode 100644 index 0000000..defa993 --- /dev/null +++ b/openbr/plugins/metadata/procrustes.cpp @@ -0,0 +1,135 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Procrustes alignment of points + * \author Scott Klum \cite sklum + */ +class ProcrustesTransform : public MetadataTransform +{ + Q_OBJECT + + Q_PROPERTY(bool warp READ get_warp WRITE set_warp RESET reset_warp STORED false) + BR_PROPERTY(bool, warp, true) + + Eigen::MatrixXf meanShape; + + void train(const TemplateList &data) + { + QList< QList > normalizedPoints; + + // Normalize all sets of points + foreach (br::Template datum, data) { + QList points = datum.file.points(); + QList rects = datum.file.rects(); + + if (points.empty() || rects.empty()) continue; + + // Assume rect appended last was bounding box + points.append(rects.last().topLeft()); + points.append(rects.last().topRight()); + points.append(rects.last().bottomLeft()); + points.append(rects.last().bottomRight()); + + // Center shape at origin + Scalar mean = cv::mean(OpenCVUtils::toPoints(points).toVector().toStdVector()); + for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]); + + // Remove scale component + float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector()); + for (int i = 0; i < points.size(); i++) points[i] /= norm; + + normalizedPoints.append(points); + } + + if (normalizedPoints.empty()) qFatal("Unable to calculate normalized points"); + + // Determine mean shape, assuming all shapes contain the same number of points + meanShape = Eigen::MatrixXf(normalizedPoints[0].size(), 2); + + for (int i = 0; i < normalizedPoints[0].size(); i++) { + double x = 0; + double y = 0; + + for (int j = 0; j < normalizedPoints.size(); j++) { + x += normalizedPoints[j][i].x(); + y += normalizedPoints[j][i].y(); + } + + x /= (double)normalizedPoints.size(); + y /= (double)normalizedPoints.size(); + + meanShape(i,0) = x; + meanShape(i,1) = y; + } + } + + void project(const Template &src, Template &dst) const + { + QList points = src.file.points(); + QList rects = src.file.rects(); + + if (points.empty() || rects.empty()) { + dst = src; + if (Globals->verbose) qWarning("Procrustes alignment failed because points or rects are empty."); + return; + } + + // Assume rect appended last was bounding box + points.append(rects.last().topLeft()); + points.append(rects.last().topRight()); + points.append(rects.last().bottomLeft()); + points.append(rects.last().bottomRight()); + + Scalar mean = cv::mean(OpenCVUtils::toPoints(points).toVector().toStdVector()); + for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]); + + Eigen::MatrixXf srcMat(points.size(), 2); + float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector()); + for (int i = 0; i < points.size(); i++) { + points[i] /= norm; + srcMat(i,0) = points[i].x(); + srcMat(i,1) = points[i].y(); + } + + Eigen::JacobiSVD svd(srcMat.transpose()*meanShape, Eigen::ComputeThinU | Eigen::ComputeThinV); + Eigen::MatrixXf R = svd.matrixU()*svd.matrixV().transpose(); + + dst = src; + + // Store procrustes stats in the order: + // R(0,0), R(1,0), R(1,1), R(0,1), mean_x, mean_y, norm + QList procrustesStats; + procrustesStats << R(0,0) << R(1,0) << R(1,1) << R(0,1) << mean[0] << mean[1] << norm; + dst.file.setList("ProcrustesStats",procrustesStats); + + if (warp) { + Eigen::MatrixXf dstMat = srcMat*R; + for (int i = 0; i < dstMat.rows(); i++) { + dst.file.appendPoint(QPointF(dstMat(i,0),dstMat(i,1))); + } + } + } + + void store(QDataStream &stream) const + { + stream << meanShape; + } + + void load(QDataStream &stream) + { + stream >> meanShape; + } + +}; + +BR_REGISTER(Transform, ProcrustesTransform) + +} // namespace br + +#include "procrustes.moc" diff --git a/openbr/plugins/motion.cpp b/openbr/plugins/motion.cpp index 4511336..5def61d 100644 --- a/openbr/plugins/motion.cpp +++ b/openbr/plugins/motion.cpp @@ -59,48 +59,6 @@ class OpticalFlowTransform : public UntrainableMetaTransform BR_REGISTER(Transform, OpticalFlowTransform) -/*! - * \ingroup transforms - * \brief Wraps OpenCV's BackgroundSubtractorMOG2 and puts the foreground mask in the Template metadata. - * \author Austin Blanton \cite imaus10 - */ -class SubtractBackgroundTransform : public TimeVaryingTransform -{ - Q_OBJECT - - // TODO: This is broken. - // BackgroundSubtractorMOG2 mog; - -public: - SubtractBackgroundTransform() : TimeVaryingTransform(false, false) {} - -private: - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - Mat mask; - // TODO: broken - // mog(src, mask); - erode(mask, mask, Mat()); - dilate(mask, mask, Mat()); - dst.file.set("Mask", QVariant::fromValue(mask)); - } - - void project(const Template &src, Template &dst) const - { - (void) src; (void) dst; qFatal("no way"); - } - - void finalize(TemplateList &output) - { - (void) output; - // TODO: Broken - // mog = BackgroundSubtractorMOG2(); - } -}; - -BR_REGISTER(Transform, SubtractBackgroundTransform) - } // namespace br #include "motion.moc" diff --git a/openbr/plugins/plugins.cmake b/openbr/plugins/plugins.cmake index 163e15c..d71aeb8 100644 --- a/openbr/plugins/plugins.cmake +++ b/openbr/plugins/plugins.cmake @@ -12,8 +12,28 @@ foreach(DIR ${BR_THIRDPARTY_PLUGINS_DIR}) set(BR_THIRDPARTY_PLUGINS ${BR_THIRDPARTY_PLUGINS} ${PLUGINS}) endforeach() +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} core) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} io) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} gui) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} imgproc) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} video) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} cluster) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} distance) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} format) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} representation) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} detection) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} classification) +set(BR_PLUGINS_DIR ${BR_PLUGINS_DIR} metadata) + + +foreach(DIR ${BR_PLUGINS_DIR}) + file(GLOB PLUGINS plugins/${DIR}/*.cpp plugins/${DIR}/*.h) + set(BR_PLUGINS ${BR_PLUGINS} ${PLUGINS}) +endforeach() + file(GLOB PLUGINS plugins/*.cpp plugins/*.h) -foreach(PLUGIN ${PLUGINS} ${BR_THIRDPARTY_PLUGINS}) +set(BR_PLUGINS ${BR_PLUGINS} ${PLUGINS}) +foreach(PLUGIN ${BR_PLUGINS} ${BR_THIRDPARTY_PLUGINS}) get_filename_component(PLUGIN_BASENAME ${PLUGIN} NAME_WE) get_filename_component(PLUGIN_PATH ${PLUGIN} PATH) set(PLUGIN_CMAKE "${PLUGIN_PATH}/${PLUGIN_BASENAME}.cmake") diff --git a/openbr/plugins/quantize.cpp b/openbr/plugins/quantize.cpp index 14ae276..3617486 100644 --- a/openbr/plugins/quantize.cpp +++ b/openbr/plugins/quantize.cpp @@ -287,56 +287,6 @@ class ProductQuantizationDistance : public UntrainableDistance BR_REGISTER(Distance, ProductQuantizationDistance) /*! - * \ingroup distances - * \brief Recurively computed distance in a product quantized space - * \author Josh Klontz \cite jklontz - */ -class RecursiveProductQuantizationDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(float t READ get_t WRITE set_t RESET reset_t STORED false) - BR_PROPERTY(float, t, -std::numeric_limits::max()) - - float compare(const Template &a, const Template &b) const - { - return compareRecursive(a, b, 0, a.size(), 0); - } - - float compareRecursive(const QList &a, const QList &b, int i, int size, float evidence) const - { - float similarity = 0; - - const int elements = a[i].total()-sizeof(quint16); - uchar *aData = a[i].data; - uchar *bData = b[i].data; - quint16 index = *reinterpret_cast(aData); - aData += sizeof(quint16); - bData += sizeof(quint16); - - const float *lut = (const float*)ProductQuantizationLUTs[index].data; - for (int j=0; j(propName, negVal+1) == negVal) + return; RNG &rng = theRNG(); - width = rng.uniform(0.f, 1.f); - height = rng.uniform(0.f, 1.f); - x = rng.uniform(0.f, 1.f-width); - y = rng.uniform(0.f, 1.f-height); + for (int n = 0; n < numSamples; n++) { + int x = rng.uniform(0, src.m().cols - width - 1); + int y = rng.uniform(0, src.m().rows - height - 1); + dst.file.appendRect(Rect(x, y, width, height)); + } } +}; + +BR_REGISTER(Transform, RndNegSampleTransform) + +/*! + * \ingroup transforms + * \brief Selects a random region. + * \author Josh Klontz \cite jklontz + */ +class RndRegionTransform : public UntrainableTransform +{ + Q_OBJECT void project(const Template &src, Template &dst) const { + RNG &rng = theRNG(); + float size = rng.uniform(0.2f, 1.f); + float x = rng.uniform(0.f, 1.f-size); + float y = rng.uniform(0.f, 1.f-size); + dst = src.m()(Rect(src.m().cols * x, src.m().rows * y, - src.m().cols * width, - src.m().rows * height)); + src.m().cols * size, + src.m().rows * size)); } }; diff --git a/openbr/plugins/regions.cpp b/openbr/plugins/regions.cpp index dad3d91..684d7bd 100644 --- a/openbr/plugins/regions.cpp +++ b/openbr/plugins/regions.cpp @@ -374,8 +374,12 @@ class RectFromPointsTransform : public UntrainableTransform QList points; - foreach (int index, indices) { + int numPoints = (indices.empty() ? src.file.points().size() : indices.size()); + for (int idx = 0; idx < numPoints; idx++) { + int index = indices.empty() ? idx : indices[idx]; if (src.file.points().size() > index) { + if (src.file.points()[index].x() <= 0 || + src.file.points()[index].y() <= 0) continue; if (src.file.points()[index].x() < minX) minX = src.file.points()[index].x(); if (src.file.points()[index].x() > maxX) maxX = src.file.points()[index].x(); if (src.file.points()[index].y() < minY) minY = src.file.points()[index].y(); @@ -390,8 +394,9 @@ class RectFromPointsTransform : public UntrainableTransform width += deltaWidth; double height = maxY-minY; - double deltaHeight = width/aspectRatio - height; - height += deltaHeight; + //double deltaHeight = width/aspectRatio - height; + double deltaHeight = height*padding; + height += deltaHeight; const int x = std::max(0.0, minX - deltaWidth/2.0); const int y = std::max(0.0, minY - deltaHeight/2.0); diff --git a/openbr/plugins/representation/hogdescriptor.cpp b/openbr/plugins/representation/hogdescriptor.cpp new file mode 100644 index 0000000..b3a032a --- /dev/null +++ b/openbr/plugins/representation/hogdescriptor.cpp @@ -0,0 +1,39 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief OpenCV HOGDescriptor wrapper + * \author Austin Blanton \cite imaus10 + */ +class HoGDescriptorTransform : public UntrainableTransform +{ + Q_OBJECT + + HOGDescriptor hog; + + void project(const Template &src, Template &dst) const + { + std::vector descriptorVals; + std::vector locations; + Size winStride = Size(0,0); + Size padding = Size(0,0); + foreach (const Mat &rect, src) { + hog.compute(rect, descriptorVals, winStride, padding, locations); + Mat HoGFeats(descriptorVals, true); + dst += HoGFeats; + } + } +}; + +BR_REGISTER(Transform, HoGDescriptorTransform) + +} // namespace br + +#include "hogdescriptor.moc" diff --git a/openbr/plugins/representation/keypointdescriptor.cpp b/openbr/plugins/representation/keypointdescriptor.cpp new file mode 100644 index 0000000..33d7993 --- /dev/null +++ b/openbr/plugins/representation/keypointdescriptor.cpp @@ -0,0 +1,50 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV Key Point Descriptor + * \author Josh Klontz \cite jklontz + */ +class KeyPointDescriptorTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(QString descriptor READ get_descriptor WRITE set_descriptor RESET reset_descriptor STORED false) + Q_PROPERTY(int size READ get_size WRITE set_size RESET reset_size STORED false) + BR_PROPERTY(QString, descriptor, "SIFT") + BR_PROPERTY(int, size, -1) + + Ptr descriptorExtractor; + + void init() + { + descriptorExtractor = DescriptorExtractor::create(descriptor.toStdString()); + if (descriptorExtractor.empty()) + qFatal("Failed to create DescriptorExtractor: %s", qPrintable(descriptor)); + } + + void project(const Template &src, Template &dst) const + { + std::vector keyPoints; + if (size == -1) + foreach (const QRectF &ROI, src.file.rects()) + keyPoints.push_back(KeyPoint(ROI.x()+ROI.width()/2, ROI.y()+ROI.height()/2, (ROI.width() + ROI.height())/2)); + else + foreach (const QPointF &landmark, src.file.points()) + keyPoints.push_back(KeyPoint(landmark.x(), landmark.y(), size)); + if (keyPoints.empty()) return; + descriptorExtractor->compute(src, keyPoints, dst); + } +}; + +BR_REGISTER(Transform, KeyPointDescriptorTransform) + +} // namespace br + +#include "keypointdescriptor.moc" diff --git a/openbr/plugins/representation/siftdescriptor.cpp b/openbr/plugins/representation/siftdescriptor.cpp new file mode 100644 index 0000000..9d8918f --- /dev/null +++ b/openbr/plugins/representation/siftdescriptor.cpp @@ -0,0 +1,60 @@ +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Specialize wrapper OpenCV SIFT wrapper + * \author Josh Klontz \cite jklontz + */ +class SIFTDescriptorTransform : public UntrainableTransform +{ + Q_OBJECT + Q_PROPERTY(int size READ get_size WRITE set_size RESET reset_size STORED false) + Q_PROPERTY(QList sizes READ get_sizes WRITE set_sizes RESET reset_sizes STORED false) + Q_PROPERTY(int nFeatures READ get_nFeatures WRITE set_nFeatures RESET reset_nFeatures STORED false) + Q_PROPERTY(int nOctaveLayers READ get_nOctaveLayers WRITE set_nOctaveLayers RESET reset_nOctaveLayers STORED false) + Q_PROPERTY(double contrastThreshold READ get_contrastThreshold WRITE set_contrastThreshold RESET reset_contrastThreshold STORED false) + Q_PROPERTY(double edgeThreshold READ get_edgeThreshold WRITE set_edgeThreshold RESET reset_edgeThreshold STORED false) + Q_PROPERTY(double sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) + BR_PROPERTY(int, size, 1) + BR_PROPERTY(QList, sizes, QList()) + BR_PROPERTY(int, nFeatures, 0) + BR_PROPERTY(int, nOctaveLayers, 3) + BR_PROPERTY(double, contrastThreshold, 0.04) + BR_PROPERTY(double, edgeThreshold, 10) + BR_PROPERTY(double, sigma, 1.6) + + SIFT sift; + + void init() + { + if (sizes.empty()) + sizes.append(size); + sift = SIFT(nFeatures, nOctaveLayers, contrastThreshold, edgeThreshold, sigma); + } + + void project(const Template &src, Template &dst) const + { + std::vector keyPoints; + foreach (const QPointF &val, src.file.points()) + foreach (const int r, sizes) + keyPoints.push_back(KeyPoint(val.x(), val.y(), r)); + + Mat m; + sift(src, Mat(), keyPoints, m, true); + m.setTo(0, m<0); // SIFT returns large negative values when it goes off the edge of the image. + dst += m; + } +}; + +BR_REGISTER(Transform, SIFTDescriptorTransform) + +} // namespace br + +#include "siftdescriptor.moc" diff --git a/openbr/plugins/sentence.cpp b/openbr/plugins/sentence.cpp deleted file mode 100644 index fca4cd6..0000000 --- a/openbr/plugins/sentence.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include "openbr_internal.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Ordered words - * \author Josh Klontz \cite jklontz - */ -class SentenceTransform : public UntrainableMetaTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - QByteArray sentence; - QDataStream stream(&sentence, QIODevice::WriteOnly); - for (int i=0; i::max() : comparisons / distance; - aWord = *reinterpret_cast(aBuffer); - aRows = *reinterpret_cast(aBuffer+4); - aColumns = *reinterpret_cast(aBuffer+8); - aData = reinterpret_cast(aBuffer+12); - aBuffer += 12 + 4*aRows*aColumns; - } else if (bWord < aWord) { - if (bBuffer == bEnd) return comparisons == 0 ? -std::numeric_limits::max() : comparisons / distance; - bWord = *reinterpret_cast(bBuffer); - bRows = *reinterpret_cast(bBuffer+4); - bColumns = *reinterpret_cast(bBuffer+8); - bData = reinterpret_cast(bBuffer+12); - bBuffer += 12 + 4*bRows*bColumns; - } else { - for (int i=0; i + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Passes along n sequential frames to the next transform. + * \author Josh Klontz \cite jklontz + * + * For a video with m frames, AggregateFrames would create a total of m-n+1 sequences ([0,n] ... [m-n+1, m]). + */ +class AggregateFrames : public TimeVaryingTransform +{ + Q_OBJECT + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) + BR_PROPERTY(int, n, 1) + + TemplateList buffer; + +public: + AggregateFrames() : TimeVaryingTransform(false, false) {} + +private: + void train(const TemplateList &data) + { + (void) data; + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + buffer.append(src); + if (buffer.size() < n) return; + Template out; + foreach (const Template &t, buffer) out.append(t); + out.file = buffer.takeFirst().file; + dst.append(out); + } + + void finalize(TemplateList &output) + { + (void) output; + buffer.clear(); + } + + void store(QDataStream &stream) const + { + (void) stream; + } + + void load(QDataStream &stream) + { + (void) stream; + } +}; + +BR_REGISTER(Transform, AggregateFrames) + +} // namespace br + +#include "aggregate.moc" diff --git a/openbr/plugins/video/drop.cpp b/openbr/plugins/video/drop.cpp new file mode 100644 index 0000000..3cd2d75 --- /dev/null +++ b/openbr/plugins/video/drop.cpp @@ -0,0 +1,35 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Only use one frame every n frames. + * \author Austin Blanton \cite imaus10 + * + * For a video with m frames, DropFrames will pass on m/n frames. + */ +class DropFrames : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) + BR_PROPERTY(int, n, 1) + + void project(const TemplateList &src, TemplateList &dst) const + { + if (src.first().file.get("FrameNumber") % n != 0) return; + dst = src; + } + + void project(const Template &src, Template &dst) const + { + (void) src; (void) dst; qFatal("shouldn't be here"); + } +}; + +BR_REGISTER(Transform, DropFrames) + +} // namespace br + +#include "drop.moc" -- libgit2 0.21.4