Commit 9f89a05f088e6a5ff509b53929e081580b34d88e
1 parent
bb446dfb
proposed implementation of #123
Showing
4 changed files
with
88 additions
and
78 deletions
openbr/core/opencvutils.cpp
| ... | ... | @@ -300,6 +300,16 @@ QList<QRectF> OpenCVUtils::fromRects(const QList<Rect> &cvRects) |
| 300 | 300 | return qRects; |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | +bool OpenCVUtils::overlaps(const QList<Rect> &posRects, const Rect &negRect, double overlap) | |
| 304 | +{ | |
| 305 | + foreach (const Rect &posRect, posRects) { | |
| 306 | + Rect intersect = negRect & posRect; | |
| 307 | + if (intersect.area() > overlap*posRect.area()) | |
| 308 | + return true; | |
| 309 | + } | |
| 310 | + return false; | |
| 311 | +} | |
| 312 | + | |
| 303 | 313 | QDataStream &operator<<(QDataStream &stream, const Mat &m) |
| 304 | 314 | { |
| 305 | 315 | // Write header | ... | ... |
openbr/core/opencvutils.h
| ... | ... | @@ -87,6 +87,7 @@ namespace OpenCVUtils |
| 87 | 87 | QRectF fromRect(const cv::Rect &cvRect); |
| 88 | 88 | QList<cv::Rect> toRects(const QList<QRectF> &qRects); |
| 89 | 89 | QList<QRectF> fromRects(const QList<cv::Rect> &cvRects); |
| 90 | + bool overlaps(const QList<cv::Rect> &posRects, const cv::Rect &negRect, double overlap); | |
| 90 | 91 | |
| 91 | 92 | int getFourcc(); |
| 92 | 93 | } | ... | ... |
openbr/plugins/slidingwindow.cpp
| ... | ... | @@ -101,12 +101,73 @@ private: |
| 101 | 101 | |
| 102 | 102 | BR_REGISTER(Transform, SlidingWindowTransform) |
| 103 | 103 | |
| 104 | +static TemplateList cropTrainingSamples(const TemplateList &data, const float aspectRatio, const int minSize, const float maxOverlap, const int negToPosRatio) | |
| 105 | +{ | |
| 106 | + TemplateList result; | |
| 107 | + foreach (const Template &tmpl, data) { | |
| 108 | + QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | |
| 109 | + QList<Rect> negRects; | |
| 110 | + for (int i=0; i<posRects.size(); i++) { | |
| 111 | + Rect &posRect = posRects[i]; | |
| 112 | + | |
| 113 | + // Adjust for training samples that have different aspect ratios | |
| 114 | + const int diff = posRect.width - int(posRect.height * aspectRatio); | |
| 115 | + posRect.x += diff / 2; | |
| 116 | + posRect.width += diff; | |
| 117 | + | |
| 118 | + // Ignore samples larger than the image | |
| 119 | + if ((posRect.x + posRect.width >= tmpl.m().cols) || | |
| 120 | + (posRect.y + posRect.height >= tmpl.m().rows) || | |
| 121 | + (posRect.x < 0) || | |
| 122 | + (posRect.y < 0)) | |
| 123 | + continue; | |
| 124 | + | |
| 125 | + result += Template(tmpl.file, Mat(tmpl, posRect)); | |
| 126 | + | |
| 127 | + // Add random negative samples | |
| 128 | + Mat m = tmpl.m(); | |
| 129 | + int sample = 0; | |
| 130 | + while (sample < negToPosRatio) { | |
| 131 | + const int x = rand() % m.cols; | |
| 132 | + const int y = rand() % m.rows; | |
| 133 | + const int maxWidth = m.cols - x; | |
| 134 | + const int maxHeight = m.rows - y; | |
| 135 | + if (maxWidth <= minSize || maxHeight <= minSize) | |
| 136 | + continue; | |
| 137 | + | |
| 138 | + int height; | |
| 139 | + int width; | |
| 140 | + if (aspectRatio > (float) maxWidth / (float) maxHeight) { | |
| 141 | + width = rand() % (maxWidth - minSize) + minSize; | |
| 142 | + height = qRound(width / aspectRatio); | |
| 143 | + } else { | |
| 144 | + height = rand() % (maxHeight - minSize) + minSize; | |
| 145 | + width = qRound(height * aspectRatio); | |
| 146 | + } | |
| 147 | + Rect negRect(x, y, width, height); | |
| 148 | + | |
| 149 | + // The negative samples cannot overlap the positive samples at | |
| 150 | + // all, but they may partially overlap with other negatives. | |
| 151 | + if (OpenCVUtils::overlaps(posRects, negRect, 0) || | |
| 152 | + OpenCVUtils::overlaps(negRects, negRect, maxOverlap)) | |
| 153 | + continue; | |
| 154 | + | |
| 155 | + result += Template(tmpl.file, Mat(tmpl, negRect)); | |
| 156 | + result.last().file.set("Label", QString("neg")); | |
| 157 | + sample++; | |
| 158 | + } | |
| 159 | + } | |
| 160 | + } | |
| 161 | + | |
| 162 | + return result; | |
| 163 | +} | |
| 164 | + | |
| 104 | 165 | /*! |
| 105 | 166 | * \ingroup transforms |
| 106 | 167 | * \brief . |
| 107 | 168 | * \author Austin Blanton \cite imaus10 |
| 108 | 169 | */ |
| 109 | -class BuildScalesTransform : public Transform | |
| 170 | +class BuildScalesTransform : public MetaTransform | |
| 110 | 171 | { |
| 111 | 172 | Q_OBJECT |
| 112 | 173 | Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) |
| ... | ... | @@ -117,7 +178,6 @@ class BuildScalesTransform : public Transform |
| 117 | 178 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 118 | 179 | Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) |
| 119 | 180 | Q_PROPERTY(float minScale READ get_minScale WRITE set_minScale RESET reset_minScale STORED false) |
| 120 | - Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false) | |
| 121 | 181 | BR_PROPERTY(br::Transform *, transform, NULL) |
| 122 | 182 | BR_PROPERTY(double, scaleFactor, 0.75) |
| 123 | 183 | BR_PROPERTY(bool, takeLargestScale, false) |
| ... | ... | @@ -126,92 +186,27 @@ class BuildScalesTransform : public Transform |
| 126 | 186 | BR_PROPERTY(int, minSize, 8) |
| 127 | 187 | BR_PROPERTY(double, maxOverlap, 0) |
| 128 | 188 | BR_PROPERTY(float, minScale, 1.0) |
| 129 | - BR_PROPERTY(bool, negSamples, true) | |
| 130 | 189 | |
| 131 | -public: | |
| 132 | - BuildScalesTransform() : Transform(false, true) {} | |
| 133 | 190 | private: |
| 134 | - int windowHeight; | |
| 135 | 191 | float aspectRatio; |
| 192 | + int windowHeight; | |
| 136 | 193 | |
| 137 | - void train(const TemplateList &_data) | |
| 194 | + void train(const TemplateList &data) | |
| 138 | 195 | { |
| 139 | - TemplateList data(_data); // have to make a copy b/c data is const | |
| 140 | 196 | aspectRatio = getAspectRatio(data); |
| 141 | - data.first().file.set("aspectRatio", aspectRatio); | |
| 142 | - windowHeight = (int) qRound((float) windowWidth / aspectRatio); | |
| 143 | - | |
| 197 | + windowHeight = qRound(windowWidth / aspectRatio); | |
| 144 | 198 | if (transform->trainable) { |
| 145 | 199 | TemplateList full; |
| 146 | - foreach (const Template &tmpl, data) { | |
| 147 | - QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | |
| 148 | - QList<Rect> negRects; | |
| 149 | - foreach (Rect posRect, posRects) { | |
| 150 | - | |
| 151 | - //Adjust for training samples that have different aspect ratios | |
| 152 | - int diff = posRect.width - (int)((float) posRect.height * aspectRatio); | |
| 153 | - posRect.x += diff / 2; | |
| 154 | - posRect.width += diff; | |
| 155 | - | |
| 156 | - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { | |
| 157 | - continue; | |
| 158 | - } | |
| 159 | - | |
| 160 | - Mat scaledImg; | |
| 161 | - resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,qRound(windowWidth / aspectRatio))); | |
| 162 | - Template pos(tmpl.file, scaledImg); | |
| 163 | - full += pos; | |
| 164 | - | |
| 165 | - // add random negative samples | |
| 166 | - if (negSamples) { | |
| 167 | - Mat m = tmpl.m(); | |
| 168 | - int sample = 0; | |
| 169 | - while (sample < negToPosRatio) { | |
| 170 | - int x = Common::RandSample(1, m.cols)[0]; | |
| 171 | - int y = Common::RandSample(1, m.rows)[0]; | |
| 172 | - int maxWidth = m.cols - x; | |
| 173 | - int maxHeight = m.rows - y; | |
| 174 | - if (maxWidth <= minSize || maxHeight <= minSize) | |
| 175 | - continue; | |
| 176 | - int height; | |
| 177 | - int width; | |
| 178 | - if (aspectRatio > (float) maxWidth / (float) maxHeight) { | |
| 179 | - width = Common::RandSample(1,maxWidth,minSize)[0]; | |
| 180 | - height = (int) qRound(width / aspectRatio); | |
| 181 | - } else { | |
| 182 | - height = Common::RandSample(1,maxHeight,minSize)[0]; | |
| 183 | - width = (int) qRound(height * aspectRatio); | |
| 184 | - } | |
| 185 | - Rect negRect(x, y, width, height); | |
| 186 | - // the negative samples cannot overlap the positive at all | |
| 187 | - // but they may overlap with other negatives | |
| 188 | - if (overlaps(posRects, negRect, 0) || overlaps(negRects, negRect, maxOverlap)) | |
| 189 | - continue; | |
| 190 | - negRects.append(negRect); | |
| 191 | - Template neg(tmpl.file, Mat()); | |
| 192 | - resize(Mat(tmpl, negRect), neg, Size(windowWidth, windowHeight)); | |
| 193 | - neg.file.set("Label", QString("neg")); | |
| 194 | - full += neg; | |
| 195 | - sample++; | |
| 196 | - } | |
| 197 | - } | |
| 198 | - } | |
| 200 | + foreach (const Template &roi, cropTrainingSamples(data, aspectRatio, minSize, maxOverlap, negToPosRatio)) { | |
| 201 | + Mat resized; | |
| 202 | + resize(roi, resized, Size(windowWidth, windowHeight)); | |
| 203 | + full += Template(roi.file, resized); | |
| 199 | 204 | } |
| 205 | + full.first().file.set("aspectRatio", aspectRatio); | |
| 200 | 206 | transform->train(full); |
| 201 | 207 | } |
| 202 | 208 | } |
| 203 | 209 | |
| 204 | - bool overlaps(QList<Rect> posRects, Rect negRect, double overlap) | |
| 205 | - { | |
| 206 | - foreach (const Rect posRect, posRects) { | |
| 207 | - Rect intersect = negRect & posRect; | |
| 208 | - if (intersect.area() > overlap*posRect.area()) | |
| 209 | - return true; | |
| 210 | - } | |
| 211 | - return false; | |
| 212 | - } | |
| 213 | - | |
| 214 | - | |
| 215 | 210 | void project(const Template &src, Template &dst) const |
| 216 | 211 | { |
| 217 | 212 | dst = src; | ... | ... |
scripts/pedestrianBaselineLBP.sh
| 1 | 1 | #!/bin/bash |
| 2 | 2 | |
| 3 | -#Right now this is just a simple proof of concept. No quanititative eval is performed | |
| 3 | +# Right now this is just a simple proof of concept. No quantitative eval is performed | |
| 4 | 4 | # but instead the qualitative results are displayed. |
| 5 | 5 | |
| 6 | -#Make sure you set your data path. This will likely by your openbr/data directory. | |
| 7 | -INRIA_PATH=$DATA/INRIAPerson | |
| 6 | +# Make sure you set your data path. This will likely by your openbr/data directory. | |
| 7 | +if [ -z "$DATA" ]; then | |
| 8 | + INRIA_PATH=../data/INRIAPerson | |
| 9 | +else | |
| 10 | + INRIA_PATH=$DATA/INRIAPerson | |
| 11 | +fi | |
| 8 | 12 | |
| 9 | 13 | ALG="Open+Cvt(Gray)+Rename(neg,0)+BuildScales(Blur(2)+LBP(1,2)+SlidingWindow(Hist(59)+Cat+LDA(isBinary=true),windowWidth=10,takeLargestScale=false,threshold=2),windowWidth=10,takeLargestScale=false,minScale=4)+ConsolidateDetections+Discard" |
| 10 | 14 | ... | ... |