Commit 6d4161b8ff587baaf2b1064603ff91086621ee83
Merge pull request #101 from biometrics/slidingwindow
Add initial sliding window transform
Showing
8 changed files
with
197 additions
and
0 deletions
.gitignore
data/INRIAPerson/README.md
0 โ 100644
openbr/core/bee.cpp
| ... | ... | @@ -75,6 +75,21 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata) |
| 75 | 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 | 93 | if (file.name.isEmpty()) qFatal("Missing file-name in %s.", qPrintable(sigset)); |
| 79 | 94 | fileList.append(file); |
| 80 | 95 | ... | ... |
openbr/core/core.cpp
| ... | ... | @@ -43,6 +43,11 @@ struct AlgorithmCore |
| 43 | 43 | { |
| 44 | 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 | 51 | if (transform.isNull()) qFatal("Null transform."); |
| 47 | 52 | qDebug("%d training files", data.size()); |
| 48 | 53 | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -162,6 +162,7 @@ void reset_##NAME() { NAME = DEFAULT; } |
| 162 | 162 | * Rects | QList<Rect> | List of unnamed rects |
| 163 | 163 | * Age | float | Age used for demographic filtering |
| 164 | 164 | * Gender | QString | Subject gender |
| 165 | + * Train | bool | The data is for training, as opposed to enrollment | |
| 165 | 166 | * _* | * | Reserved for internal use |
| 166 | 167 | */ |
| 167 | 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 | 35 | rm *.eye description.txt BioID-FaceDatabase-V1.2.zip |
| 36 | 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 | 56 | # KTH |
| 39 | 57 | if [ ! -d ../data/KTH/vid ]; then |
| 40 | 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>' | ... | ... |