Commit 4f45b576adaaffce8ed3b6f470ec1a53c444d415
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
5 changed files
with
95 additions
and
68 deletions
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 &files, const File &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 &files, const File &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() | ... | ... |