Commit 48d184af11d199e4c9079d49d3c910abfcdababc

Authored by Brendan Klare
1 parent 260843b5

Killed

Showing 1 changed file with 0 additions and 369 deletions
openbr/plugins/landmarks.cpp
... ... @@ -141,204 +141,6 @@ BR_REGISTER(Transform, ProcrustesTransform)
141 141  
142 142 /*!
143 143 * \ingroup transforms
144   - * \brief Improved procrustes alignment of points, to include a post processing scaling of points
145   - * to faciliate subsequent texture mapping.
146   - * \author Brendan Klare \cite bklare
147   - * \param width Width of output coordinate space (before padding)
148   - * \param padding Amount of padding around the coordinate space
149   - * \param useFirst whether or not to use the first instance as the reference object
150   - */
151   -class ProcrustesAlignTransform : public Transform
152   -{
153   - Q_OBJECT
154   -
155   - Q_PROPERTY(float width READ get_width WRITE set_width RESET reset_width STORED false)
156   - Q_PROPERTY(float padding READ get_padding WRITE set_padding RESET reset_padding STORED false)
157   - Q_PROPERTY(bool useFirst READ get_useFirst WRITE set_useFirst RESET reset_useFirst STORED false)
158   - BR_PROPERTY(float, width, 80)
159   - BR_PROPERTY(float, padding, 8)
160   - BR_PROPERTY(bool, useFirst, false)
161   -
162   -
163   - Eigen::MatrixXf referenceShape;
164   - float minX;
165   - float minY;
166   - float maxX;
167   - float maxY;
168   - float aspectRatio;
169   -
170   - void init() {
171   - aspectRatio = 0;
172   - }
173   -
174   - static MatrixXf getRotation(MatrixXf ref, MatrixXf sample) {
175   - MatrixXf R = sample.transpose() * ref;
176   - JacobiSVD<MatrixXf> svd(R, ComputeFullU | ComputeFullV);
177   - R = svd.matrixU() * svd.matrixV().transpose();
178   - return R;
179   - }
180   -
181   - //Converts x y points in a single vector to two column matrix
182   - static MatrixXf vectorToMatrix(MatrixXf vector) {
183   - int n = vector.rows();
184   - MatrixXf matrix(n / 2, 2);
185   - for (int i = 0; i < n / 2; i++) {
186   - for (int j = 0; j < 2; j++) {
187   - matrix(i, j) = vector(i * 2 + j);
188   - }
189   - }
190   - return matrix;
191   - }
192   -
193   - static MatrixXf matrixToVector(MatrixXf matrix) {
194   - int n2 = matrix.rows();
195   - MatrixXf vector(n2 * 2, 1);
196   - for (int i = 0; i < n2; i++) {
197   - for (int j = 0; j < 2; j++) {
198   - vector(i * 2 + j) = matrix(i, j);
199   - }
200   - }
201   - return vector;
202   - }
203   -
204   - void train(const TemplateList &data)
205   - {
206   - MatrixXf points(data[0].file.points().size() * 2, data.size());
207   -
208   - // Normalize all sets of points
209   - for (int j = 0; j < data.size(); j++) {
210   - QList<QPointF> imagePoints = data[j].file.points();
211   -
212   - float meanX = 0,
213   - meanY = 0;
214   - for (int i = 0; i < imagePoints.size(); i++) {
215   - points(i * 2, j) = imagePoints[i].x();
216   - points(i * 2 + 1, j) = imagePoints[i].y();
217   - meanX += imagePoints[i].x();
218   - meanY += imagePoints[i].y();
219   - }
220   - meanX /= imagePoints.size();
221   - meanY /= imagePoints.size();
222   -
223   - for (int i = 0; i < imagePoints.size(); i++) {
224   - points(i * 2, j) -= meanX;
225   - points(i * 2 + 1, j) -= meanY;
226   - }
227   - }
228   -
229   - //normalize scale
230   - for (int i = 0; i < points.cols(); i++)
231   - points.col(i) = points.col(i) / points.col(i).norm();
232   -
233   - //Normalize rotation
234   - if (!useFirst) {
235   - referenceShape = vectorToMatrix(points.rowwise().sum() / points.cols());
236   - } else {
237   - referenceShape = vectorToMatrix(points.col(0));
238   - }
239   -
240   - for (int i = 0; i < points.cols(); i++) {
241   - MatrixXf p = vectorToMatrix(points.col(i));
242   - MatrixXf R = getRotation(referenceShape, p);
243   - points.col(i) = matrixToVector(p * R);
244   - }
245   -
246   - //Choose crop boundaries and adjustments that captures most data
247   - MatrixXf minXs(points.cols(),1);
248   - MatrixXf minYs(points.cols(),1);
249   - MatrixXf maxXs(points.cols(),1);
250   - MatrixXf maxYs(points.cols(),1);
251   - for (int j = 0; j < points.cols(); j++) {
252   - minX = FLT_MAX,
253   - minY = FLT_MAX,
254   - maxX = -FLT_MAX,
255   - maxY = -FLT_MAX;
256   - for (int i = 0; i < points.rows(); i++) {
257   - if (i % 2 == 0) {
258   - if (points(i,j) > maxX)
259   - maxX = points(i, j);
260   - if (points(i,j) < minX)
261   - minX = points(i, j);
262   - } else {
263   - if (points(i,j) > maxY)
264   - maxY = points(i, j);
265   - if (points(i,j) < minY)
266   - minY = points(i, j);
267   - }
268   - }
269   -
270   - minXs(j) = minX;
271   - maxXs(j) = maxX;
272   - minYs(j) = minY;
273   - maxYs(j) = maxY;
274   - }
275   -
276   - minX = eigMean(minXs) - 0 * eigStd(minXs);
277   - minY = eigMean(minYs) - 0 * eigStd(minYs);
278   - maxX = eigMean(maxXs) + 0 * eigStd(maxXs);
279   - maxY = eigMean(maxYs) + 0 * eigStd(maxYs);
280   - aspectRatio = (maxX - minX) / (maxY - minY);
281   - }
282   -
283   - void project(const Template &src, Template &dst) const
284   - {
285   - QList<QPointF> imagePoints = src.file.points();
286   - MatrixXf p(imagePoints.size() * 2, 1);
287   - for (int i = 0; i < imagePoints.size(); i++) {
288   - p(i * 2) = imagePoints[i].x();
289   - p(i * 2 + 1) = imagePoints[i].y();
290   - }
291   - p = vectorToMatrix(p);
292   -
293   - //Normalize translation
294   - p.col(0) = p.col(0) - MatrixXf::Ones(p.rows(),1) * (p.col(0).sum() / p.rows());
295   - p.col(1) = p.col(1) - MatrixXf::Ones(p.rows(),1) * (p.col(1).sum() / p.rows());
296   -
297   - //Normalize scale
298   - p /= matrixToVector(p).norm();
299   -
300   - //Normalize rotation
301   - MatrixXf R = getRotation(referenceShape, p);
302   - p = p * R;
303   -
304   - //Translate and scale into output space and store in output list
305   - QList<QPointF> procrustesPoints;
306   - for (int i = 0; i < p.rows(); i++)
307   - procrustesPoints.append( QPointF(
308   - (p(i, 0) - minX) / (maxX - minX) * (width - 1) + padding,
309   - (p(i, 1) - minY) / (maxY - minY) * (qRound( width / aspectRatio) - 1) + padding));
310   -
311   - dst = src;
312   - dst.file.setList<QPointF>("ProcrustesPoints", procrustesPoints);
313   - dst.file.set("ProcrustesBound", QRectF(0, 0, width + 2 * padding, (qRound(width / aspectRatio) + 2 * padding)));
314   - dst.file.set("ProcrustesPadding", padding);
315   - }
316   -
317   - void store(QDataStream &stream) const
318   - {
319   - stream << referenceShape;
320   - stream << minX;
321   - stream << minY;
322   - stream << maxX;
323   - stream << maxY;
324   - stream << aspectRatio;
325   - }
326   -
327   - void load(QDataStream &stream)
328   - {
329   - stream >> referenceShape;
330   - stream >> minX;
331   - stream >> minY;
332   - stream >> maxX;
333   - stream >> maxY;
334   - stream >> aspectRatio;
335   - }
336   -};
337   -
338   -BR_REGISTER(Transform, ProcrustesAlignTransform)
339   -
340   -/*!
341   - * \ingroup transforms
342 144 * \brief Creates a Delaunay triangulation based on a set of points
343 145 * \author Scott Klum \cite sklum
344 146 */
... ... @@ -481,177 +283,6 @@ BR_REGISTER(Transform, DelaunayTransform)
481 283  
482 284 /*!
483 285 * \ingroup transforms
484   - * \brief Maps texture from one set of points to another. Assumes that points are rigidly transformed
485   - * \author Brendan Klare \cite bklare
486   - * \author Scott Klum \cite sklum
487   - */
488   -class TextureMapTransform : public UntrainableTransform
489   -{
490   - Q_OBJECT
491   -
492   - static QRectF getBounds(QList<QPointF> points, int dstPadding) {
493   - float srcMinX = FLT_MAX;
494   - float srcMinY = FLT_MAX;
495   - float srcMaxX = -FLT_MAX;
496   - float srcMaxY = -FLT_MAX;
497   - for (int i = 0; i < points.size(); i++) {
498   - if (points[i].x() < srcMinX) srcMinX = points[i].x();
499   - if (points[i].y() < srcMinY) srcMinY = points[i].y();
500   - if (points[i].x() > srcMaxX) srcMaxX = points[i].x();
501   - if (points[i].y() > srcMaxY) srcMaxY = points[i].y();
502   - }
503   -
504   - float padding = (srcMaxX - srcMinX) / 80 * dstPadding;
505   - return QRectF(qRound(srcMinX - padding), qRound(srcMinY - padding), qRound(srcMaxX - srcMinX + 2 * padding), qRound(srcMaxY - srcMinY + 2 * padding));
506   - }
507   -
508   - static int getVertexIndex(QPointF trianglePts, QList<QPointF> pts)
509   - {
510   - for (int i = 0; i < pts.size(); i++)
511   - if (trianglePts.x() == pts[i].x() && trianglePts.y() == pts[i].y())
512   - return i;
513   - return -1;
514   - }
515   -
516   - static QList<QPointF> addBounds(const QList<QPointF> _points, const QRectF bound)
517   - {
518   - QList<QPointF> points(_points);
519   - points.append(bound.topLeft());
520   - points.append(QPointF(bound.right() - 1, bound.top()));
521   - points.append(QPointF(bound.left(), bound.bottom() - 1));
522   - points.append(QPointF(bound.right() - 1, bound.bottom() - 1));
523   - return points;
524   - }
525   -
526   - static QList<QList<int> > getTriangulation(const QList<QPointF> _points, const QRectF bound)
527   - {
528   - QList<QPointF> points(_points);
529   -
530   - Subdiv2D subdiv(OpenCVUtils::toRect(bound));
531   - for (int i = 0; i < points.size(); i++)
532   - subdiv.insert(OpenCVUtils::toPoint(points[i]));
533   -
534   - vector<Vec6f> triangleList;
535   - subdiv.getTriangleList(triangleList);
536   -
537   - QList<QList<int> > triangleIndices;
538   - for (size_t i = 0; i < triangleList.size(); i++) {
539   - bool valid = true;
540   - QList<QPointF> vertices;
541   - vertices.append(QPointF(triangleList[i][0],triangleList[i][1]));
542   - vertices.append(QPointF(triangleList[i][2],triangleList[i][3]));
543   - vertices.append(QPointF(triangleList[i][4],triangleList[i][5]));
544   - for (int j = 0; j < 3; j++)
545   - if (vertices[j].x() > bound.right() || vertices[j].y() > bound.bottom() || vertices[j].x() < bound.left() || vertices[j].y() < bound.top())
546   - valid = false;
547   -
548   - if (valid) {
549   - QList<int> tri;
550   - for (int j = 0; j < 3; j++)
551   - tri.append(getVertexIndex(vertices[j], points));
552   - triangleIndices.append(tri);
553   - }
554   - }
555   -
556   - return triangleIndices;
557   - }
558   -
559   - void project(const Template &src, Template &dst) const
560   - {
561   - QList<QPointF> dstPoints = dst.file.getList<QPointF>("ProcrustesPoints");
562   - QList<QPointF> srcPoints = dst.file.points();
563   - QRectF dstBound = dst.file.get<QRectF>("ProcrustesBound");
564   - QRectF srcBound = getBounds(srcPoints, dst.file.get<int>("ProcrustesPadding"));
565   - if (dstPoints.empty() || srcPoints.empty()) {
566   - dst = src;
567   - if (Globals->verbose) qWarning("Delauney triangulation failed because points or rects are empty.");
568   - return;
569   - }
570   -
571   - dstPoints = addBounds(dstPoints, dstBound);
572   - srcPoints = addBounds(srcPoints, srcBound);
573   -
574   - QList<QList<int> > triIndices = getTriangulation(srcPoints, srcBound);
575   -
576   - int dstWidth = dstBound.width() + dstBound.x();
577   - int dstHeight = dstBound.height() + dstBound.y();
578   - dst.m() = Mat::zeros(dstHeight, dstWidth, src.m().type());
579   - for (int i = 0; i < triIndices.size(); i++) {
580   - Point2f srcPoint1[3];
581   - Point2f dstPoint1[3];
582   - for (int j = 0; j < 3; j++) {
583   - srcPoint1[j] = OpenCVUtils::toPoint(srcPoints[triIndices[i][j]]);
584   - dstPoint1[j] = OpenCVUtils::toPoint(dstPoints[triIndices[i][j]]);
585   - }
586   -
587   - Mat buffer(dstHeight, dstWidth, src.m().type());
588   - warpAffine(src.m(), buffer, getAffineTransform(srcPoint1, dstPoint1), Size(dstBound.width(), dstBound.height()));
589   -
590   - Mat mask = Mat::zeros(dstHeight, dstWidth, CV_8UC1);
591   - Point maskPoints[1][3];
592   - maskPoints[0][0] = dstPoint1[0];
593   - maskPoints[0][1] = dstPoint1[1];
594   - maskPoints[0][2] = dstPoint1[2];
595   - const Point* ppt = { maskPoints[0] };
596   - fillConvexPoly(mask, ppt, 3, Scalar(255, 255, 255), 8);
597   -
598   - for (int i = 0; i < dstHeight; i++) {
599   - for (int j = 0; j < dstWidth; j++) {
600   - if (mask.at<uchar>(i,j) == 255) {
601   - if (dst.m().type() == CV_32FC3 || dst.m().type() == CV_8UC3)
602   - dst.m().at<cv::Vec3b>(i,j) = buffer.at<cv::Vec3b>(i,j);
603   - else if (dst.m().type() == CV_32F)
604   - dst.m().at<float>(i,j) = buffer.at<float>(i,j);
605   - else if (dst.m().type() == CV_8U)
606   - dst.m().at<uchar>(i,j) = buffer.at<uchar>(i,j);
607   - else
608   - qFatal("Unsupported pixel format.");
609   - }
610   - }
611   - }
612   -
613   - }
614   -
615   - dst.file.clearPoints();
616   - dst.file.clearRects();
617   - dst.file.setPoints(dstPoints);
618   - }
619   -};
620   -
621   -BR_REGISTER(Transform, TextureMapTransform)
622   -
623   -/*!
624   - * \ingroup initializers
625   - * \brief Initialize Procrustes croppings
626   - * \author Brendan Klare \cite bklare
627   - */
628   -class ProcrustesInitializer : public Initializer
629   -{
630   - Q_OBJECT
631   -
632   - void initialize() const
633   - {
634   - Globals->abbreviations.insert("ProcrustesStasmFace","ProcrustesAlign(padding=16)+TextureMap+Resize(48,48)");
635   - Globals->abbreviations.insert("ProcrustesStasmEyes","SelectPoints([28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47])+ProcrustesAlign(padding=8)+TextureMap+Resize(24,48)");
636   - Globals->abbreviations.insert("ProcrustesStasmPeriocular","SelectPoints([28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,16,17,18,19,20,21,22,23,24,25,26,27])+ProcrustesAlign(padding=8)+TextureMap+Resize(36,48)");
637   - Globals->abbreviations.insert("ProcrustesStasmBrow","SelectPoints([16,17,18,19,20,21,22,23,24,25,26,27])+ProcrustesAlign(padding=8)+TextureMap+Resize(24,48)");
638   - Globals->abbreviations.insert("ProcrustesStasmNose","SelectPoints([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58])+ProcrustesAlign(padding=8)+TextureMap+Resize(36,48)");
639   - Globals->abbreviations.insert("ProcrustesStasmMouth","SelectPoints([59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76])+ProcrustesAlign(padding=10)+TextureMap+Resize(36,48)");
640   - Globals->abbreviations.insert("ProcrustesStasmJaw", "SelectPoints([2,3,4,5,6,7,8,9,10])+ProcrustesAlign(padding=8)+TextureMap+Resize(36,48)");
641   -
642   - Globals->abbreviations.insert("ProcrustesLargeStasmFace","ProcrustesAlign(padding=16)+TextureMap+Resize(480,480)");
643   - Globals->abbreviations.insert("ProcrustesLargeStasmEyes","SelectPoints([28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47])+ProcrustesAlign(padding=8)+TextureMap+Resize(240,480)");
644   - Globals->abbreviations.insert("ProcrustesLargeStasmPeriocular","SelectPoints([28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,16,17,18,19,20,21,22,23,24,25,26,27])+ProcrustesAlign(padding=8)+TextureMap+Resize(360,480)");
645   - Globals->abbreviations.insert("ProcrustesLargeStasmBrow","SelectPoints([16,17,18,19,20,21,22,23,24,25,26,27])+ProcrustesAlign(padding=8)+TextureMap+Resize(240,480)");
646   - Globals->abbreviations.insert("ProcrustesLargeStasmNose","SelectPoints([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58])+ProcrustesAlign(padding=8)+TextureMap+Resize(360,480)");
647   - Globals->abbreviations.insert("ProcrustesLargeStasmMouth","SelectPoints([59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76])+ProcrustesAlign(padding=20)+TextureMap+Resize(360,480)");
648   - Globals->abbreviations.insert("ProcrustesLargeStasmJaw", "SelectPoints([2,3,4,5,6,7,8,9,10])+ProcrustesAlign(padding=8)+TextureMap+Resize(360,480)");
649   - }
650   -};
651   -BR_REGISTER(Initializer, ProcrustesInitializer)
652   -
653   -/*!
654   - * \ingroup transforms
655 286 * \brief Creates a Delaunay triangulation based on a set of points
656 287 * \author Scott Klum \cite sklum
657 288 */
... ...