Commit 6d4161b8ff587baaf2b1064603ff91086621ee83

Authored by jklontz
2 parents 57423a27 c19e8b53

Merge pull request #101 from biometrics/slidingwindow

Add initial sliding window transform
.gitignore
@@ -34,3 +34,6 @@ scripts/results @@ -34,3 +34,6 @@ scripts/results
34 34
35 ### vim ### 35 ### vim ###
36 *.swp 36 *.swp
  37 +
  38 +### autogenerated sigsets ###
  39 +data/INRIAPerson/sigset
data/INRIAPerson/README.md 0 โ†’ 100644
  1 +## INRIA Person Database
  2 +Dataset for human detection in several formats: original positive and negative images with bounding box annotations and normalized positive images (just the bounding box).
  3 +* [Website](http://pascal.inrialpes.fr/data/human/)
openbr/core/bee.cpp
@@ -75,6 +75,21 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata) @@ -75,6 +75,21 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata)
75 else if (!ignoreMetadata) file.set(key, value); 75 else if (!ignoreMetadata) file.set(key, value);
76 } 76 }
77 77
  78 + // add bounding boxes, if they exist (will be child elements of <presentation>)
  79 + if (fileNode.hasChildNodes()) {
  80 + QList<QRectF> rects;
  81 + QDomNodeList bboxes = fileNode.childNodes();
  82 + for (int i=0; i<bboxes.length(); i++) {
  83 + QDomElement bbox = bboxes.at(i).toElement();
  84 + qreal x = bbox.attribute("x").toDouble();
  85 + qreal y = bbox.attribute("y").toDouble();
  86 + qreal width = bbox.attribute("width").toDouble();
  87 + qreal height = bbox.attribute("height").toDouble();
  88 + rects += QRectF(x, y, width, height);
  89 + }
  90 + file.setRects(rects);
  91 + }
  92 +
78 if (file.name.isEmpty()) qFatal("Missing file-name in %s.", qPrintable(sigset)); 93 if (file.name.isEmpty()) qFatal("Missing file-name in %s.", qPrintable(sigset));
79 fileList.append(file); 94 fileList.append(file);
80 95
openbr/core/core.cpp
@@ -43,6 +43,11 @@ struct AlgorithmCore @@ -43,6 +43,11 @@ struct AlgorithmCore
43 { 43 {
44 TemplateList data(TemplateList::fromGallery(input)); 44 TemplateList data(TemplateList::fromGallery(input));
45 45
  46 + // set the Train bool metadata, in case a Transform's project
  47 + // needs to know if it's called during train or enroll
  48 + for (int i=0; i<data.size(); i++)
  49 + data[i].file.set("Train", true);
  50 +
46 if (transform.isNull()) qFatal("Null transform."); 51 if (transform.isNull()) qFatal("Null transform.");
47 qDebug("%d training files", data.size()); 52 qDebug("%d training files", data.size());
48 53
openbr/openbr_plugin.h
@@ -162,6 +162,7 @@ void reset_##NAME() { NAME = DEFAULT; } @@ -162,6 +162,7 @@ void reset_##NAME() { NAME = DEFAULT; }
162 * Rects | QList<Rect> | List of unnamed rects 162 * Rects | QList<Rect> | List of unnamed rects
163 * Age | float | Age used for demographic filtering 163 * Age | float | Age used for demographic filtering
164 * Gender | QString | Subject gender 164 * Gender | QString | Subject gender
  165 + * Train | bool | The data is for training, as opposed to enrollment
165 * _* | * | Reserved for internal use 166 * _* | * | Reserved for internal use
166 */ 167 */
167 struct BR_EXPORT File 168 struct BR_EXPORT File
openbr/plugins/slidingwindow.cpp 0 โ†’ 100644
  1 +#include "openbr_internal.h"
  2 +#include "openbr/core/opencvutils.h"
  3 +#include "openbr/core/common.h"
  4 +
  5 +using namespace cv;
  6 +
  7 +namespace br
  8 +{
  9 +
  10 +/*!
  11 + * \ingroup transforms
  12 + * \brief Applies a transform to a sliding window.
  13 + * Discards negative detections.
  14 + * \author Austin Blanton \cite imaus10
  15 + */
  16 +class SlidingWindowTransform : public Transform
  17 +{
  18 + Q_OBJECT
  19 + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false)
  20 + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
  21 + Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
  22 + Q_PROPERTY(double stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false)
  23 + Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false)
  24 + Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false)
  25 + Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false)
  26 + Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false)
  27 + BR_PROPERTY(br::Transform *, transform, NULL)
  28 + BR_PROPERTY(int, minSize, 8)
  29 + BR_PROPERTY(double, scaleFactor, 0.75)
  30 + BR_PROPERTY(double, stepSize, 1)
  31 + BR_PROPERTY(bool, takeLargestScale, true)
  32 + BR_PROPERTY(bool, negSamples, true)
  33 + BR_PROPERTY(int, negToPosRatio, 1)
  34 + BR_PROPERTY(double, maxOverlap, 0)
  35 +
  36 +public:
  37 + SlidingWindowTransform() : Transform(false, true) {}
  38 +
  39 +private:
  40 + void train(const TemplateList &data)
  41 + {
  42 + if (transform->trainable) {
  43 + TemplateList full;
  44 + foreach (const Template &tmpl, data) {
  45 + foreach (const Rect &rect, OpenCVUtils::toRects(tmpl.file.rects())) {
  46 + Template pos(tmpl.file, Mat(tmpl, rect));
  47 + full += pos;
  48 +
  49 + // add random negative samples
  50 + if (negSamples) {
  51 + Mat m = tmpl.m();
  52 + int sample = 0;
  53 + while (sample < negToPosRatio) {
  54 + int x = Common::RandSample(1, m.cols)[0];
  55 + int y = Common::RandSample(1, m.rows)[0];
  56 + int maxWidth = m.cols - x, maxHeight = m.rows - y;
  57 + int maxSize = std::min(maxWidth, maxHeight);
  58 + int size = (maxSize <= minSize ? maxSize : Common::RandSample(1, maxSize, minSize)[0]);
  59 + Rect negRect(x, y, size, size);
  60 + Rect intersect = negRect & rect;
  61 + if (intersect.area() > maxOverlap*rect.area())
  62 + continue;
  63 + Template neg(tmpl.file, Mat(tmpl, negRect));
  64 + neg.file.set("Label", QString("neg"));
  65 + full += neg;
  66 + sample++;
  67 + }
  68 + }
  69 + }
  70 + }
  71 + transform->train(full);
  72 + }
  73 + }
  74 +
  75 + void project(const Template &src, Template &dst) const
  76 + {
  77 + dst = src;
  78 + // no need to slide a window over ground truth data
  79 + if (src.file.getBool("Train", false)) return;
  80 +
  81 + dst.file.clearRects();
  82 + int rows = src.m().rows, cols = src.m().cols;
  83 + for (double size=std::min(rows, cols); size>=minSize; size*=scaleFactor) {
  84 + for (double y=0; y+size<rows; y+=(size*stepSize)) {
  85 + for (double x=0; x+size<cols; x+=(size*stepSize)) {
  86 + Rect window(x, y, size, size);
  87 + Template windowMat(src.file, Mat(src.m(), window));
  88 + Template detect;
  89 + transform->project(windowMat, detect);
  90 + // the result will be in the Label
  91 + if (detect.file.get<QString>(QString("Label")) == "pos") {
  92 + dst.file.appendRect(OpenCVUtils::fromRect(window));
  93 + if (takeLargestScale) return;
  94 + }
  95 + }
  96 + }
  97 + }
  98 + }
  99 +};
  100 +
  101 +BR_REGISTER(Transform, SlidingWindowTransform)
  102 +
  103 +} // namespace br
  104 +
  105 +#include "slidingwindow.moc"
scripts/downloadDatasets.sh
@@ -35,6 +35,24 @@ if [ ! -d ../data/BioID/img ]; then @@ -35,6 +35,24 @@ if [ ! -d ../data/BioID/img ]; then
35 rm *.eye description.txt BioID-FaceDatabase-V1.2.zip 35 rm *.eye description.txt BioID-FaceDatabase-V1.2.zip
36 fi 36 fi
37 37
  38 +# INRIA person
  39 +if [ ! -d ../data/INRIAPerson/img ]; then
  40 + echo "Downloading INRIA person dataset..."
  41 + if hash curl 2>/dev/null; then
  42 + curl -OL http://pascal.inrialpes.fr/data/human/INRIAPerson.tar
  43 + else
  44 + wget http://pascal.inrialpes.fr/data/human/INRIAPerson.tar
  45 + fi
  46 + tar -xf INRIAPerson.tar
  47 + mkdir ../data/INRIAPerson/img ../data/INRIAPerson/sigset
  48 + ./writeINRIAPersonSigset.sh Train > ../data/INRIAPerson/sigset/train.xml
  49 + ./writeINRIAPersonSigset.sh Test > ../data/INRIAPerson/sigset/test.xml
  50 + ./writeINRIAPersonSigset.sh train_64x128_H96 > ../data/INRIAPerson/sigset/train_normalized.xml
  51 + ./writeINRIAPersonSigset.sh test_64x128_H96 > ../data/INRIAPerson/sigset/test_normalized.xml
  52 + mv INRIAPerson/* ../data/INRIAPerson/img
  53 + rm -r INRIAPerson*
  54 +fi
  55 +
38 # KTH 56 # KTH
39 if [ ! -d ../data/KTH/vid ]; then 57 if [ ! -d ../data/KTH/vid ]; then
40 echo "Downloading KTH..." 58 echo "Downloading KTH..."
scripts/writeINRIAPersonSigset.sh 0 โ†’ 100755
  1 +#!/bin/bash
  2 +
  3 +# prints out a <data:bbox> element from rectangle coordinates
  4 +# (from the ViPER standard: http://viper-toolkit.sourceforge.net/docs/file/)
  5 +printBBox()
  6 +{
  7 + width=$(($3-$1))
  8 + height=$(($4-$2))
  9 + echo -e "\t\t\t<data:bbox height=\"$height\" width=\"$width\" x=\"$1\" y=\"$2\" />"
  10 +}
  11 +# export printBBox so xargs can call it using bash -c below
  12 +export -f printBBox
  13 +SEDREGEX='s/.*(\([0-9]*\), \([0-9]*\)) - (\([0-9]*\), \([0-9]*\))/printBBox \1 \2 \3 \4/'
  14 +
  15 +echo '<?xml version="1.0" encoding="UTF-8"?>'
  16 +echo '<biometric-signature-set>'
  17 +
  18 +# print out the positive image sigs
  19 +for fullpath in INRIAPerson/$1/pos/*; do
  20 + # get just the filename, minus the path
  21 + filename=$(basename "$fullpath")
  22 + echo -e "\t<biometric-signature name=\"${filename%.*}\">"
  23 +
  24 + # if this folder has annotations, add bounding boxes
  25 + echo -en "\t\t<presentation Label=\"pos\" file-name=\"$1/pos/$filename\""
  26 + if [ -d INRIAPerson/$1/annotations ]; then
  27 + echo ">"
  28 + annotation="INRIAPerson/$1/annotations/${filename%.*}.txt"
  29 + grep 'Bounding box' $annotation | sed "$SEDREGEX" | xargs -n 5 bash -c 'printBBox $@'
  30 + echo -e "\t\t</presentation>"
  31 + # otherwise, just end the presentation
  32 + else
  33 + echo " />"
  34 + fi
  35 +
  36 + echo -e '\t</biometric-signature>'
  37 +done
  38 +
  39 +# print out the negative image sigs
  40 +for fullpath in INRIAPerson/$1/neg/*; do
  41 + filename=$(basename "$fullpath")
  42 + echo -e "\t<biometric-signature name=\"${filename%.*}\">"
  43 + echo -e "\t\t<presentation Label=\"neg\" file-name=\"$1/neg/$filename\" />"
  44 + echo -e '\t</biometric-signature>'
  45 +done
  46 +
  47 +echo '</biometric-signature-set>'