Commit 59acf29449553755b093799c14c2ebdc170c49de
1 parent
d5677d9b
Initial texture map changes
Showing
1 changed file
with
109 additions
and
1 deletions
openbr/plugins/landmarks.cpp
| @@ -268,11 +268,19 @@ class ProcrustesAlignTransform : public Transform | @@ -268,11 +268,19 @@ class ProcrustesAlignTransform : public Transform | ||
| 268 | 268 | ||
| 269 | void project(const Template &src, Template &dst) const | 269 | void project(const Template &src, Template &dst) const |
| 270 | { | 270 | { |
| 271 | + float srcMinX = FLT_MAX; | ||
| 272 | + float srcMinY = FLT_MAX; | ||
| 273 | + float srcMaxX = -FLT_MAX; | ||
| 274 | + float srcMaxY = -FLT_MAX; | ||
| 271 | QList<QPointF> imagePoints = src.file.points(); | 275 | QList<QPointF> imagePoints = src.file.points(); |
| 272 | MatrixXf p(imagePoints.size() * 2, 1); | 276 | MatrixXf p(imagePoints.size() * 2, 1); |
| 273 | for (int i = 0; i < imagePoints.size(); i++) { | 277 | for (int i = 0; i < imagePoints.size(); i++) { |
| 274 | p(i * 2) = imagePoints[i].x(); | 278 | p(i * 2) = imagePoints[i].x(); |
| 275 | p(i * 2 + 1) = imagePoints[i].y(); | 279 | p(i * 2 + 1) = imagePoints[i].y(); |
| 280 | + if (imagePoints[i].x() < srcMinX) srcMinX = imagePoints[i].x(); | ||
| 281 | + if (imagePoints[i].y() < srcMinY) srcMinY = imagePoints[i].y(); | ||
| 282 | + if (imagePoints[i].x() < srcMaxX) srcMaxX = imagePoints[i].x(); | ||
| 283 | + if (imagePoints[i].y() < srcMaxY) srcMaxY = imagePoints[i].y(); | ||
| 276 | } | 284 | } |
| 277 | p = vectorToMatrix(p); | 285 | p = vectorToMatrix(p); |
| 278 | 286 | ||
| @@ -297,6 +305,9 @@ class ProcrustesAlignTransform : public Transform | @@ -297,6 +305,9 @@ class ProcrustesAlignTransform : public Transform | ||
| 297 | dst = src; | 305 | dst = src; |
| 298 | dst.file.setList<QPointF>("ProcrustesPoints", procrustesPoints); | 306 | dst.file.setList<QPointF>("ProcrustesPoints", procrustesPoints); |
| 299 | dst.file.set("ProcrustesBound", QRectF(0, 0, width + 2 * padding, (qRound(width / aspectRatio) + 2 * padding))); | 307 | dst.file.set("ProcrustesBound", QRectF(0, 0, width + 2 * padding, (qRound(width / aspectRatio) + 2 * padding))); |
| 308 | + dst.file.set("ProcrustesSrcBound", QRectF(qRound(srcMinX - padding), qRound(srcMinY - padding), | ||
| 309 | + qRound(srcMaxX - srcMinX + 2 * padding), | ||
| 310 | + qRound(srcMaxY - srcMinY + 2 * padding))); | ||
| 300 | } | 311 | } |
| 301 | 312 | ||
| 302 | void store(QDataStream &stream) const | 313 | void store(QDataStream &stream) const |
| @@ -336,7 +347,7 @@ class DelaunayTransform : public UntrainableTransform | @@ -336,7 +347,7 @@ class DelaunayTransform : public UntrainableTransform | ||
| 336 | BR_PROPERTY(float, scaleFactor, 1) | 347 | BR_PROPERTY(float, scaleFactor, 1) |
| 337 | BR_PROPERTY(bool, warp, true) | 348 | BR_PROPERTY(bool, warp, true) |
| 338 | 349 | ||
| 339 | - void project(const Template &src, Template &dst) const | 350 | + void project(const Template &src, Template &dst, ) const |
| 340 | { | 351 | { |
| 341 | QList<QPointF> points = src.file.points(); | 352 | QList<QPointF> points = src.file.points(); |
| 342 | QList<QRectF> rects = src.file.rects(); | 353 | QList<QRectF> rects = src.file.rects(); |
| @@ -466,6 +477,103 @@ BR_REGISTER(Transform, DelaunayTransform) | @@ -466,6 +477,103 @@ BR_REGISTER(Transform, DelaunayTransform) | ||
| 466 | 477 | ||
| 467 | /*! | 478 | /*! |
| 468 | * \ingroup transforms | 479 | * \ingroup transforms |
| 480 | + * \brief Maps texture from one set of points to another. Assumes that points are rigidly transformed | ||
| 481 | + * \author Scott Klum \cite sklum | ||
| 482 | + * \author Brendan Klare \cite bklare | ||
| 483 | + */ | ||
| 484 | +class TextureMapTransform : public UntrainableTransform | ||
| 485 | +{ | ||
| 486 | + Q_OBJECT | ||
| 487 | + | ||
| 488 | + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | ||
| 489 | + Q_PROPERTY(bool warp READ get_warp WRITE set_warp RESET reset_warp STORED false) | ||
| 490 | + BR_PROPERTY(float, scaleFactor, 1) | ||
| 491 | + BR_PROPERTY(bool, warp, true) | ||
| 492 | + | ||
| 493 | + QList<QPointF> getTriangulation(QList<QPointF> points, QRect bound) { | ||
| 494 | + points.append(bound.topLeft()); | ||
| 495 | + points.append(bound.topRight()); | ||
| 496 | + points.append(bound.bottomLeft()); | ||
| 497 | + points.append(bound.bottomRight()); | ||
| 498 | + | ||
| 499 | + Subdiv2D subdiv(); | ||
| 500 | + for (int i = 0; i < points.size(); i++) | ||
| 501 | + subdiv.insert(OpenCVUtils::toPoint(points[i])); | ||
| 502 | + | ||
| 503 | + vector<Vec6f> triangleList; | ||
| 504 | + subdiv.getTriangleList(triangleList); | ||
| 505 | + | ||
| 506 | + QList<QPointF> validTriangles; | ||
| 507 | + for (size_t i = 0; i < triangleList.size(); i++) { | ||
| 508 | + bool valid = true; | ||
| 509 | + QList<QPointF> vertices; | ||
| 510 | + vertices.append(QPointF(triangleList[i][0],triangleList[i][1])); | ||
| 511 | + vertices.append(QPointF(triangleList[i][2],triangleList[i][3])); | ||
| 512 | + vertices.append(QPointF(triangleList[i][4],triangleList[i][5])); | ||
| 513 | + for (int j = 0; j < 3; j++) | ||
| 514 | + if (vertices[j].x() > cols || vertices[j].y() > rows || vertices[j].x() < 0 || vertices[j].y() < 0) | ||
| 515 | + valid = false; | ||
| 516 | + | ||
| 517 | + if (valid) | ||
| 518 | + validTriangles.append(vertices); | ||
| 519 | + } | ||
| 520 | + | ||
| 521 | + return validTriangles; | ||
| 522 | + } | ||
| 523 | + | ||
| 524 | + void project(const Template &src, Template &dst) const | ||
| 525 | + { | ||
| 526 | + QList<QPointF> dstPoints = dst.file.setList<QPointF>("ProcrustesPoints", procrustesPoints); | ||
| 527 | + QRectF dstBound = dst.file.setList<QPointF>("ProcrustesBound", procrustesPoints); | ||
| 528 | + QList<QPointF> srcPoints = dst.file.points(); | ||
| 529 | + QRectF srcBound = dst.file.setList<QPointF>("ProcrustesSrcBound", procrustesPoints); | ||
| 530 | + | ||
| 531 | + if (dstPoints.empty() || srcPoints.empty()) { | ||
| 532 | + dst = src; | ||
| 533 | + if (Globals->verbose) qWarning("Delauney triangulation failed because points or rects are empty."); | ||
| 534 | + return; | ||
| 535 | + } | ||
| 536 | + | ||
| 537 | + QList<QPointF> dstTri = getTriangulation(dstPoints, dstBound); | ||
| 538 | + QList<QPointF> srcTri = getTriangulation(srcPoints, srcBound); | ||
| 539 | + | ||
| 540 | + dst.m() = Mat::zeros(dstBound.height(), dstBound.width(), src.m().type()); | ||
| 541 | + for (int i = 0; i < srcTriangles.size(); i+=3) { | ||
| 542 | + Point2f srcPoints[3]; | ||
| 543 | + Point2f dstPoints[3]; | ||
| 544 | + for (int j = 0; j < 3; j++) { | ||
| 545 | + srcPoints[j] = OpenCVUtils::toPoint(srcTri[i+j]); | ||
| 546 | + srcPoints[j] = OpenCVUtils::toPoint(dstTri[i+j]); | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + Mat buffer(dstBound.height(), dstBound.width(), src.m().type()); | ||
| 550 | + warpAffine(src.m(), buffer, getAffineTransform(srcPoints, dstPoints), Size(cols,rows)); | ||
| 551 | + | ||
| 552 | + Mat mask = Mat::zeros(rows, cols, CV_8UC1); | ||
| 553 | + Point maskPoints[1][3]; | ||
| 554 | + maskPoints[0][0] = dstPoints[0]; | ||
| 555 | + maskPoints[0][1] = dstPoints[1]; | ||
| 556 | + maskPoints[0][2] = dstPoints[2]; | ||
| 557 | + const Point* ppt = { maskPoints[0] }; | ||
| 558 | + fillConvexPoly(mask, ppt, 3, Scalar(255,255,255), 8); | ||
| 559 | + | ||
| 560 | + Mat output(dstBound.height(), dstBound.width(), src.m().type()); | ||
| 561 | + | ||
| 562 | + if (i > 0) { | ||
| 563 | + Mat overlap; | ||
| 564 | + bitwise_and(dst.m(),mask,overlap); | ||
| 565 | + mask.setTo(0, overlap!=0); | ||
| 566 | + } | ||
| 567 | + | ||
| 568 | + bitwise_and(buffer,mask,output); | ||
| 569 | + | ||
| 570 | + dst.m() += output; | ||
| 571 | + } | ||
| 572 | + } | ||
| 573 | +}; | ||
| 574 | + | ||
| 575 | +/*! | ||
| 576 | + * \ingroup transforms | ||
| 469 | * \brief Creates a Delaunay triangulation based on a set of points | 577 | * \brief Creates a Delaunay triangulation based on a set of points |
| 470 | * \author Scott Klum \cite sklum | 578 | * \author Scott Klum \cite sklum |
| 471 | */ | 579 | */ |