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,9 +175,7 @@ float Evaluate(const Mat &simmat, const Mat &mask, const QString &csv)
175 175
176 if ((falsePositives > previousFalsePositives) && 176 if ((falsePositives > previousFalsePositives) &&
177 (truePositives > previousTruePositives)) { 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 previousFalsePositives = falsePositives; 179 previousFalsePositives = falsePositives;
182 previousTruePositives = truePositives; 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,7 +228,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
228 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + 228 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
229 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + 229 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
230 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + 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 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\"") + 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 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + 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,7 +236,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
236 QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + 236 QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") +
237 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + 237 (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
238 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + 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 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + 241 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
242 QString(", xlab=\"Score%1\"").arg((p.flip ? p.major.size : p.minor.size) > 1 ? " / " + (p.flip ? p.major.header : p.minor.header) : QString()) + 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,6 +74,16 @@ namespace QtUtils
74 /**** Variant Utilities ****/ 74 /**** Variant Utilities ****/
75 QString toString(const QVariant &variant); 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 /**** Point Utilities ****/ 87 /**** Point Utilities ****/
78 float euclideanLength(const QPointF &point); 88 float euclideanLength(const QPointF &point);
79 } 89 }
openbr/plugins/landmarks.cpp
@@ -109,20 +109,18 @@ class ProcrustesTransform : public Transform @@ -109,20 +109,18 @@ class ProcrustesTransform : public Transform
109 109
110 dst = src; 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 if (warp) { 118 if (warp) {
113 Eigen::MatrixXf dstMat = srcMat*R; 119 Eigen::MatrixXf dstMat = srcMat*R;
114 for (int i = 0; i < dstMat.rows(); i++) { 120 for (int i = 0; i < dstMat.rows(); i++) {
115 dst.file.appendPoint(QPointF(dstMat(i,0),dstMat(i,1))); 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 void store(QDataStream &stream) const 126 void store(QDataStream &stream) const
@@ -150,33 +148,34 @@ class DelaunayTransform : public UntrainableTransform @@ -150,33 +148,34 @@ class DelaunayTransform : public UntrainableTransform
150 148
151 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) 149 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
152 Q_PROPERTY(bool warp READ get_warp WRITE set_warp RESET reset_warp STORED false) 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 BR_PROPERTY(float, scaleFactor, 1) 151 BR_PROPERTY(float, scaleFactor, 1)
155 BR_PROPERTY(bool, warp, true) 152 BR_PROPERTY(bool, warp, true)
156 - BR_PROPERTY(bool, draw, false)  
157 153
158 void project(const Template &src, Template &dst) const 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 QList<QPointF> points = src.file.points(); 156 QList<QPointF> points = src.file.points();
163 QList<QRectF> rects = src.file.rects(); 157 QList<QRectF> rects = src.file.rects();
164 158
165 if (points.empty() || rects.empty()) { 159 if (points.empty() || rects.empty()) {
166 dst = src; 160 dst = src;
167 - dst.file.clearRects();  
168 qWarning("Delauney triangulation failed because points or rects are empty."); 161 qWarning("Delauney triangulation failed because points or rects are empty.");
169 return; 162 return;
170 } 163 }
171 164
  165 + int cols = src.m().cols;
  166 + int rows = src.m().rows;
  167 +
172 // Assume rect appended last was bounding box 168 // Assume rect appended last was bounding box
173 points.append(rects.last().topLeft()); 169 points.append(rects.last().topLeft());
174 points.append(rects.last().topRight()); 170 points.append(rects.last().topRight());
175 points.append(rects.last().bottomLeft()); 171 points.append(rects.last().bottomLeft());
176 points.append(rects.last().bottomRight()); 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 for (int i = 0; i < points.size(); i++) { 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 dst = src; 179 dst = src;
181 qWarning("Delauney triangulation failed because points lie on boundary."); 180 qWarning("Delauney triangulation failed because points lie on boundary.");
182 return; 181 return;
@@ -187,78 +186,67 @@ class DelaunayTransform : public UntrainableTransform @@ -187,78 +186,67 @@ class DelaunayTransform : public UntrainableTransform
187 vector<Vec6f> triangleList; 186 vector<Vec6f> triangleList;
188 subdiv.getTriangleList(triangleList); 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 Eigen::MatrixXf R(2,2); 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 cv::Scalar mean(2); 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 QList<Point2f> mappedPoints; 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 for (int j = 0; j < 3; j++) { 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 Eigen::MatrixXf dstMat = srcMat*R; 232 Eigen::MatrixXf dstMat = srcMat*R;
245 233
246 Point2f srcPoints[3]; 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 Point2f dstPoints[3]; 237 Point2f dstPoints[3];
250 for (int j = 0; j < 3; j++) { 238 for (int j = 0; j < 3; j++) {
251 // Scale and shift destination points 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 dstPoints[j] = warpedPoint; 241 dstPoints[j] = warpedPoint;
254 mappedPoints.append(warpedPoint); 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 Point maskPoints[1][3]; 250 Point maskPoints[1][3];
263 maskPoints[0][0] = dstPoints[0]; 251 maskPoints[0][0] = dstPoints[0];
264 maskPoints[0][1] = dstPoints[1]; 252 maskPoints[0][1] = dstPoints[1];
@@ -267,7 +255,7 @@ class DelaunayTransform : public UntrainableTransform @@ -267,7 +255,7 @@ class DelaunayTransform : public UntrainableTransform
267 255
268 fillConvexPoly(mask, ppt, 3, Scalar(255,255,255), 8); 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 if (i > 0) { 260 if (i > 0) {
273 Mat overlap; 261 Mat overlap;
@@ -283,12 +271,40 @@ class DelaunayTransform : public UntrainableTransform @@ -283,12 +271,40 @@ class DelaunayTransform : public UntrainableTransform
283 // Overwrite any rects 271 // Overwrite any rects
284 Rect boundingBox = boundingRect(mappedPoints.toVector().toStdVector()); 272 Rect boundingBox = boundingRect(mappedPoints.toVector().toStdVector());
285 dst.file.setRects(QList<QRectF>() << OpenCVUtils::fromRect(boundingBox)); 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 BR_REGISTER(Transform, DelaunayTransform) 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 } // namespace br 308 } // namespace br
293 309
294 #include "landmarks.moc" 310 #include "landmarks.moc"
share/openbr/cmake/InstallDependencies.cmake
@@ -55,12 +55,15 @@ endfunction() @@ -55,12 +55,15 @@ endfunction()
55 # Qt Plugins 55 # Qt Plugins
56 function(install_qt_imageformats) 56 function(install_qt_imageformats)
57 if(${BR_INSTALL_DEPENDENCIES}) 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 DESTINATION bin/imageformats) 67 DESTINATION bin/imageformats)
65 endif() 68 endif()
66 endfunction() 69 endfunction()