Commit 4f45b576adaaffce8ed3b6f470ec1a53c444d415

Authored by sklum
2 parents 79421685 df39c436

Merge branch 'master' of https://github.com/biometrics/openbr

openbr/core/eval.cpp
... ... @@ -175,9 +175,7 @@ float Evaluate(const Mat &simmat, const Mat &mask, const QString &csv)
175 175  
176 176 if ((falsePositives > previousFalsePositives) &&
177 177 (truePositives > previousTruePositives)) {
178   - // Restrict the extreme ends of the curve
179   - if ((truePositives >= 10) && (falsePositives < impostorCount/2))
180   - operatingPoints.append(OperatingPoint(thresh, float(falsePositives)/impostorCount, float(truePositives)/genuineCount));
  178 + operatingPoints.append(OperatingPoint(thresh, float(falsePositives)/impostorCount, float(truePositives)/genuineCount));
181 179 previousFalsePositives = falsePositives;
182 180 previousTruePositives = truePositives;
183 181 }
... ...
openbr/core/plot.cpp
... ... @@ -228,7 +228,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
228 228 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
229 229 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
230 230 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
231   - QString(" + scale_x_log10(labels=percent, limits=c(min(DET$X),1)) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")\n\n")));
  231 + QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")\n\n")));
232 232  
233 233 p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") +
234 234 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +
... ... @@ -236,7 +236,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
236 236 QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") +
237 237 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
238 238 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
239   - QString(" + scale_x_log10(labels=percent, limits=c(min(DET$X),1)) + scale_y_log10(labels=percent) + annotation_logticks()\n\n")));
  239 + QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks()\n\n")));
240 240  
241 241 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
242 242 QString(", xlab=\"Score%1\"").arg((p.flip ? p.major.size : p.minor.size) > 1 ? " / " + (p.flip ? p.major.header : p.minor.header) : QString()) +
... ...
openbr/core/qtutils.h
... ... @@ -74,6 +74,16 @@ namespace QtUtils
74 74 /**** Variant Utilities ****/
75 75 QString toString(const QVariant &variant);
76 76  
  77 + template <typename T>
  78 + QVariantList toVariantList(const QList<T> &list)
  79 + {
  80 + QVariantList variantList;
  81 + foreach (const T &item, list)
  82 + variantList << item;
  83 +
  84 + return variantList;
  85 + }
  86 +
77 87 /**** Point Utilities ****/
78 88 float euclideanLength(const QPointF &point);
79 89 }
... ...
openbr/plugins/landmarks.cpp
... ... @@ -109,20 +109,18 @@ class ProcrustesTransform : public Transform
109 109  
110 110 dst = src;
111 111  
  112 + // Store procrustes stats in the order:
  113 + // R(0,0), R(1,0), R(1,1), R(0,1), mean_x, mean_y, norm
  114 + QList<float> procrustesStats;
  115 + procrustesStats << R(0,0) << R(1,0) << R(1,1) << R(0,1) << mean[0] << mean[1] << norm;
  116 + dst.file.set("ProcrustesStats",QtUtils::toVariantList(procrustesStats));
  117 +
112 118 if (warp) {
113 119 Eigen::MatrixXf dstMat = srcMat*R;
114 120 for (int i = 0; i < dstMat.rows(); i++) {
115 121 dst.file.appendPoint(QPointF(dstMat(i,0),dstMat(i,1)));
116 122 }
117 123 }
118   -
119   - dst.file.set("Procrustes_0_0", R(0,0));
120   - dst.file.set("Procrustes_1_0", R(1,0));
121   - dst.file.set("Procrustes_1_1", R(1,1));
122   - dst.file.set("Procrustes_0_1", R(0,1));
123   - dst.file.set("Procrustes_mean_0", mean[0]);
124   - dst.file.set("Procrustes_mean_1", mean[1]);
125   - dst.file.set("Procrustes_norm", norm);
126 124 }
127 125  
128 126 void store(QDataStream &stream) const
... ... @@ -150,33 +148,34 @@ class DelaunayTransform : public UntrainableTransform
150 148  
151 149 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
152 150 Q_PROPERTY(bool warp READ get_warp WRITE set_warp RESET reset_warp STORED false)
153   - Q_PROPERTY(bool draw READ get_draw WRITE set_draw RESET reset_draw STORED false)
154 151 BR_PROPERTY(float, scaleFactor, 1)
155 152 BR_PROPERTY(bool, warp, true)
156   - BR_PROPERTY(bool, draw, false)
157 153  
158 154 void project(const Template &src, Template &dst) const
159 155 {
160   - Subdiv2D subdiv(Rect(0,0,src.m().cols,src.m().rows));
161   -
162 156 QList<QPointF> points = src.file.points();
163 157 QList<QRectF> rects = src.file.rects();
164 158  
165 159 if (points.empty() || rects.empty()) {
166 160 dst = src;
167   - dst.file.clearRects();
168 161 qWarning("Delauney triangulation failed because points or rects are empty.");
169 162 return;
170 163 }
171 164  
  165 + int cols = src.m().cols;
  166 + int rows = src.m().rows;
  167 +
172 168 // Assume rect appended last was bounding box
173 169 points.append(rects.last().topLeft());
174 170 points.append(rects.last().topRight());
175 171 points.append(rects.last().bottomLeft());
176 172 points.append(rects.last().bottomRight());
177 173  
  174 + Subdiv2D subdiv(Rect(0,0,cols,rows));
  175 + // Make sure points are valid for Subdiv2D
  176 + // TODO: Modify points to make them valid
178 177 for (int i = 0; i < points.size(); i++) {
179   - if (points[i].x() < 0 || points[i].y() < 0 || points[i].y() >= src.m().rows || points[i].x() >= src.m().cols) {
  178 + if (points[i].x() < 0 || points[i].y() < 0 || points[i].y() >= rows || points[i].x() >= cols) {
180 179 dst = src;
181 180 qWarning("Delauney triangulation failed because points lie on boundary.");
182 181 return;
... ... @@ -187,78 +186,67 @@ class DelaunayTransform : public UntrainableTransform
187 186 vector<Vec6f> triangleList;
188 187 subdiv.getTriangleList(triangleList);
189 188  
190   - QList< QList<Point> > validTriangles;
  189 + QList<QPointF> validTriangles;
191 190  
192   - for (size_t i = 0; i < triangleList.size(); ++i) {
  191 + for (size_t i = 0; i < triangleList.size(); i++) {
  192 + // Check the triangle to make sure it's falls within the matrix
  193 + bool valid = true;
193 194  
194   - vector<Point> pt(3);
195   - pt[0] = Point(cvRound(triangleList[i][0]), cvRound(triangleList[i][1]));
196   - pt[1] = Point(cvRound(triangleList[i][2]), cvRound(triangleList[i][3]));
197   - pt[2] = Point(cvRound(triangleList[i][4]), cvRound(triangleList[i][5]));
  195 + QList<QPointF> vertices;
  196 + vertices.append(QPointF(triangleList[i][0],triangleList[i][1]));
  197 + vertices.append(QPointF(triangleList[i][2],triangleList[i][3]));
  198 + vertices.append(QPointF(triangleList[i][4],triangleList[i][5]));
  199 + for (int j = 0; j < 3; j++) if (vertices[j].x() > cols || vertices[j].y() > rows || vertices[j].x() < 0 || vertices[j].y() < 0) valid = false;
198 200  
199   - bool inside = true;
200   - for (int j = 0; j < 3; j++) {
201   - if (pt[j].x > src.m().cols || pt[j].y > src.m().rows || pt[j].x < 0 || pt[j].y < 0) inside = false;
202   - }
203   -
204   - if (inside) validTriangles.append(QList<Point>()<< pt[0] << pt[1] << pt[2]);
  201 + if (valid) validTriangles.append(vertices);
205 202 }
206 203  
207   - dst.m() = src.m().clone();
  204 + if (warp) {
  205 + dst.m() = Mat::zeros(rows,cols,src.m().type());
208 206  
209   - if (draw) {
210   - foreach(const QList<Point>& triangle, validTriangles) {
211   - line(dst.m(), triangle[0], triangle[1], Scalar(0,0,0), 1);
212   - line(dst.m(), triangle[1], triangle[2], Scalar(0,0,0), 1);
213   - line(dst.m(), triangle[2], triangle[0], Scalar(0,0,0), 1);
214   - }
215   - }
  207 + QList<float> procrustesStats = src.file.getList<float>("ProcrustesStats");
216 208  
217   - if (warp) {
218 209 Eigen::MatrixXf R(2,2);
219   - R(0,0) = src.file.get<float>("Procrustes_0_0");
220   - R(1,0) = src.file.get<float>("Procrustes_1_0");
221   - R(1,1) = src.file.get<float>("Procrustes_1_1");
222   - R(0,1) = src.file.get<float>("Procrustes_0_1");
  210 + R(0,0) = procrustesStats.at(0);
  211 + R(1,0) = procrustesStats.at(1);
  212 + R(1,1) = procrustesStats.at(2);
  213 + R(0,1) = procrustesStats.at(3);
223 214  
224 215 cv::Scalar mean(2);
225   - mean[0] = src.file.get<float>("Procrustes_mean_0");
226   - mean[1] = src.file.get<float>("Procrustes_mean_1");
  216 + mean[0] = procrustesStats.at(4);
  217 + mean[1] = procrustesStats.at(5);
227 218  
228   - float norm = src.file.get<float>("Procrustes_norm");
229   -
230   - dst.m() = Mat::zeros(src.m().rows,src.m().cols,src.m().type());
  219 + float norm = procrustesStats.at(6);
231 220  
232 221 QList<Point2f> mappedPoints;
233 222  
234   - dst.file = src.file;
235   -
236   - for (int i = 0; i < validTriangles.size(); i++) {
237   - Eigen::MatrixXf srcMat(validTriangles[i].size(), 2);
  223 + for (int i = 0; i < validTriangles.size(); i+=3) {
  224 + // Matrix to store original (pre-transformed) triangle vertices
  225 + Eigen::MatrixXf srcMat(3, 2);
238 226  
239 227 for (int j = 0; j < 3; j++) {
240   - srcMat(j,0) = (validTriangles[i][j].x-mean[0])/norm;
241   - srcMat(j,1) = (validTriangles[i][j].y-mean[1])/norm;
  228 + srcMat(j,0) = (validTriangles[i+j].x()-mean[0])/norm;
  229 + srcMat(j,1) = (validTriangles[i+j].y()-mean[1])/norm;
242 230 }
243 231  
244 232 Eigen::MatrixXf dstMat = srcMat*R;
245 233  
246 234 Point2f srcPoints[3];
247   - for (int j = 0; j < 3; j++) srcPoints[j] = validTriangles[i][j];
  235 + for (int j = 0; j < 3; j++) srcPoints[j] = OpenCVUtils::toPoint(validTriangles[i+j]);
248 236  
249 237 Point2f dstPoints[3];
250 238 for (int j = 0; j < 3; j++) {
251 239 // Scale and shift destination points
252   - Point2f warpedPoint = Point2f(dstMat(j,0)*scaleFactor+src.m().cols/2,dstMat(j,1)*scaleFactor+src.m().rows/2);
  240 + Point2f warpedPoint = Point2f(dstMat(j,0)*scaleFactor+cols/2,dstMat(j,1)*scaleFactor+rows/2);
253 241 dstPoints[j] = warpedPoint;
254 242 mappedPoints.append(warpedPoint);
255 243 }
256 244  
257   - Mat buffer(src.m().rows,src.m().cols,src.m().type());
  245 + Mat buffer(rows,cols,src.m().type());
258 246  
259   - warpAffine(src.m(), buffer, getAffineTransform(srcPoints, dstPoints), Size(src.m().cols,src.m().rows));
  247 + warpAffine(src.m(), buffer, getAffineTransform(srcPoints, dstPoints), Size(cols,rows));
260 248  
261   - Mat mask = Mat::zeros(src.m().rows, src.m().cols, CV_8UC1);
  249 + Mat mask = Mat::zeros(rows, cols, CV_8UC1);
262 250 Point maskPoints[1][3];
263 251 maskPoints[0][0] = dstPoints[0];
264 252 maskPoints[0][1] = dstPoints[1];
... ... @@ -267,7 +255,7 @@ class DelaunayTransform : public UntrainableTransform
267 255  
268 256 fillConvexPoly(mask, ppt, 3, Scalar(255,255,255), 8);
269 257  
270   - Mat output(src.m().rows,src.m().cols,src.m().type());
  258 + Mat output(rows,cols,src.m().type());
271 259  
272 260 if (i > 0) {
273 261 Mat overlap;
... ... @@ -283,12 +271,40 @@ class DelaunayTransform : public UntrainableTransform
283 271 // Overwrite any rects
284 272 Rect boundingBox = boundingRect(mappedPoints.toVector().toStdVector());
285 273 dst.file.setRects(QList<QRectF>() << OpenCVUtils::fromRect(boundingBox));
286   - }
  274 + } else dst = src;
  275 +
  276 + dst.file.set("DelaunayTriangles", QtUtils::toVariantList(validTriangles));
287 277 }
288 278 };
289 279  
290 280 BR_REGISTER(Transform, DelaunayTransform)
291 281  
  282 +/*!
  283 + * \ingroup transforms
  284 + * \brief Creates a Delaunay triangulation based on a set of points
  285 + * \author Scott Klum \cite sklum
  286 + */
  287 +class DrawDelaunayTransform : public UntrainableTransform
  288 +{
  289 + Q_OBJECT
  290 +
  291 + void project(const Template &src, Template &dst) const
  292 + {
  293 + QList<Point2f> validTriangles = OpenCVUtils::toPoints(src.file.getList<QPointF>("DelaunayTriangles"));
  294 +
  295 + // Clone the matrix do draw on it
  296 + dst.m() = src.m().clone();
  297 +
  298 + for (int i = 0; i < validTriangles.size(); i+=3) {
  299 + line(dst.m(), validTriangles[i], validTriangles[i+1], Scalar(0,0,0), 1);
  300 + line(dst.m(), validTriangles[i+1], validTriangles[i+2], Scalar(0,0,0), 1);
  301 + line(dst.m(), validTriangles[i+2], validTriangles[i], Scalar(0,0,0), 1);
  302 + }
  303 + }
  304 +};
  305 +
  306 +BR_REGISTER(Transform, DrawDelaunayTransform)
  307 +
292 308 } // namespace br
293 309  
294 310 #include "landmarks.moc"
... ...
share/openbr/cmake/InstallDependencies.cmake
... ... @@ -55,12 +55,15 @@ endfunction()
55 55 # Qt Plugins
56 56 function(install_qt_imageformats)
57 57 if(${BR_INSTALL_DEPENDENCIES})
58   - install(FILES ${QT_QGIF_PLUGIN_RELEASE}
59   - ${QT_QICO_PLUGIN_RELEASE}
60   - ${QT_QJPEG_PLUGIN_RELEASE}
61   - ${QT_QMNG_PLUGIN_RELEASE}
62   - ${QT_QSVG_PLUGIN_RELEASE}
63   - ${QT_QTIFF_PLUGIN_RELEASE}
  58 + set(IMAGE_FORMATS_DIR "${_qt5Core_install_prefix}/plugins/imageformats/")
  59 + install(FILES ${IMAGE_FORMATS_DIR}/qgif.dll
  60 + ${IMAGE_FORMATS_DIR}/qico.dll
  61 + ${IMAGE_FORMATS_DIR}/qjpeg.dll
  62 + ${IMAGE_FORMATS_DIR}/qmng.dll
  63 + ${IMAGE_FORMATS_DIR}/qsvg.dll
  64 + ${IMAGE_FORMATS_DIR}/qtga.dll
  65 + ${IMAGE_FORMATS_DIR}/qtiff.dll
  66 + ${IMAGE_FORMATS_DIR}/qwbmp.dll
64 67 DESTINATION bin/imageformats)
65 68 endif()
66 69 endfunction()
... ...