From 7b75de59a7826bb70bfb861958fc2decc1de5259 Mon Sep 17 00:00:00 2001 From: Josh Klontz Date: Wed, 15 Jul 2015 12:20:11 -0400 Subject: [PATCH] dropped BBDLDAAlignmentTransform --- openbr/plugins/classification/lda.cpp | 436 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1 file changed, 0 insertions(+), 436 deletions(-) diff --git a/openbr/plugins/classification/lda.cpp b/openbr/plugins/classification/lda.cpp index 6ffdf4e..07ff553 100644 --- a/openbr/plugins/classification/lda.cpp +++ b/openbr/plugins/classification/lda.cpp @@ -15,9 +15,6 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include -#include -#include - #include #include @@ -793,439 +790,6 @@ class WCDATransform : public Transform BR_REGISTER(Transform, WCDATransform) -// For use in BBLDAAlignmentTransform -// A single decision boundary with gaussian distributed responses -struct DecisionBoundary -{ - VectorXf function; - float positiveMean, negativeMean, positiveSD, negativeSD; -}; - -QDataStream &operator<<(QDataStream &stream, const DecisionBoundary &db) -{ - return stream << db.function << db.positiveMean << db.negativeMean << db.positiveSD << db.negativeSD; -} - -QDataStream &operator>>(QDataStream &stream, DecisionBoundary &db) -{ - return stream >> db.function >> db.positiveMean >> db.negativeMean >> db.positiveSD >> db.negativeSD; -} - -/*! - * \ingroup transforms - * \brief Boosted Binary LDA classifier for local alignment. - * \author Unknown \cite Unknown - */ -class BBLDAAlignmentTransform : public MetaTransform -{ - Q_OBJECT - Q_PROPERTY(QList anchors READ get_anchors WRITE set_anchors RESET reset_anchors STORED false) // A list of two indicies to provide initial rotation and scaling of training data - Q_PROPERTY(QString detectionKey READ get_detectionKey WRITE set_detectionKey RESET reset_detectionKey STORED false) // Metadata key containing the detection bounding box - Q_PROPERTY(int iad READ get_iad WRITE set_iad RESET reset_iad STORED false) // Inter-anchor distance in pixels - Q_PROPERTY(float radius READ get_radius WRITE set_radius RESET reset_radius STORED false) // Kernel size as a fraction of IAD - Q_PROPERTY(int resolution READ get_resolution WRITE set_resolution RESET reset_resolution STORED false) // Project resolution for sampling rotation and scale - BR_PROPERTY(QList, anchors, QList()) - BR_PROPERTY(QString, detectionKey, "FrontalFace") - BR_PROPERTY(int, iad, 16) - BR_PROPERTY(float, radius, 2) - BR_PROPERTY(int, resolution, 20) - - typedef Matrix MatrixX8U; - - struct RegistrationMatrix - { - Mat src; - Point2f center; - double angle, scale; - - RegistrationMatrix(const Mat &src = Mat(), const Point2f ¢er = Point2f(), double angle = 0, double scale = 1) - : src(src) - , center(center) - , angle(angle) - , scale(scale) - {} - - Mat calculateRotationMatrix2D(int size) const - { - Mat rotationMatrix2D = getRotationMatrix2D(center, angle, scale); - rotationMatrix2D.at(0, 2) -= (center.x - size / 2.0); // Adjust the center to the middle of the dst image - rotationMatrix2D.at(1, 2) -= (center.y - size / 2.0); - return rotationMatrix2D; - } - - Point2f invert(double x, double y, int size) const - { - Mat m; - invertAffineTransform(calculateRotationMatrix2D(size), m); - - Mat src(1, 1, CV_64FC2); - src.ptr()[0] = y; // According to the docs these should be specified in the opposite order ... - src.ptr()[1] = x; // ... however running it seems to suggest this is the correct way - - Mat dst; - transform(src, dst, m); - return Point2f(dst.ptr()[0], dst.ptr()[1]); - } - - MatrixX8U warp(int size) const - { - const Mat rotationMatrix2D = calculateRotationMatrix2D(size); - Mat dst; - warpAffine(src, dst, rotationMatrix2D, Size(size, size), INTER_LINEAR, BORDER_REFLECT); - return Map(dst.ptr(), size, size); - } - }; - - float rotationMax, scaleMin, scaleMax, xTranslationMax, yTranslationMin, yTranslationMax; - QList decisionBoundaries; - - static void show(const char *name, const MatrixX8U &data) - { - const Mat mat(data.rows(), data.cols(), CV_8UC1, (void*) data.data()); - imshow(name, mat); - waitKey(-1); - } - - static void show(const char *name, MatrixXf data) - { - data.array() -= data.minCoeff(); - data.array() *= (255 / data.maxCoeff()); - show(name, MatrixX8U(data.cast())); - } - - static void show(const char *name, VectorXf data) - { - MatrixXf resized = data; - const int size = int(sqrtf(float(data.rows()))); - resized.resize(size, size); - show(name, resized); - } - - static MatrixXf getSample(const MatrixXf ®istered, int j, int k, int kernelSize) - { - MatrixXf sample(registered.block(j, k, kernelSize, kernelSize)); - const float norm = sample.norm(); - if (norm > 0) - sample /= norm; - return sample; - } - - static MatrixXf getSample(const QList ®istered, int i, int j, int k, int kernelSize) - { - return getSample(registered[i], j, k, kernelSize); - } - - static float IADError(const MatrixXf &responses, const MatrixXf &mask) - { - const int responseSize = sqrtf(responses.cols()); - int numImages = 0; - float totalError = 0; - for (int i=0; i::max(); - int maxX = -1; - int maxY = -1; - for (int j=0; j 0) { - const float response = responses(i, j*responseSize+k); - if (response > maxResponse) { - maxResponse = response; - maxY = j; - maxX = k; - } - } - totalError += sqrtf(powf(maxX - responseSize/2, 2) + powf(maxY - responseSize/2, 2)) / responseSize; - numImages++; - } - return totalError / numImages; - } - - static bool isPositive(int j, int k, int responseSize) - { - return (abs(j - responseSize/2) <= 0) && (abs(k - responseSize/2) <= 0); - } - - static MatrixXf /* output responses */ computeBoundaryRecursive(const QList ®istered, int kernelSize, const MatrixXf &prior /* input responses */, QList &boundaries) - { - const int numImages = registered.size(); - const int responseSize = registered.first().cols() - kernelSize + 1; - int positiveSamples = 0; - int negativeSamples = 0; - - // Compute weights, a weight of zero operates as a mask - MatrixXf weights(numImages, responseSize*responseSize); - float totalPositiveWeight = 0, totalNegativeWeight = 0; - for (int i=0; i 0) { - if (positive) { totalPositiveWeight += weight; positiveSamples++; } - else { totalNegativeWeight += weight; negativeSamples++; } - } - } - - // Normalize weights to preserve class sample ratio - const float positiveWeightAdjustment = positiveSamples / totalPositiveWeight; - const float negativeWeightAdjustment = negativeSamples / totalNegativeWeight; - for (int i=0; i 0) { - if (isPositive(j, k, responseSize)) positiveMean += sample * weight; - else negativeMean += sample * weight; - } - } - positiveMean /= positiveSamples; - negativeMean /= negativeSamples; - - // Compute weighted scatter matrix and decision boundary - MatrixXf scatter = MatrixXf::Zero(kernelSize*kernelSize, kernelSize*kernelSize); - for (int i=0; i 0) { - const MatrixXf ¢eredSampleMatrix = getSample(registered, i, j, k, kernelSize) - (isPositive(j, k, responseSize) ? positiveMean : negativeMean); - const Map centeredSample(centeredSampleMatrix.data(), kernelSize*kernelSize); - scatter.noalias() += centeredSample * centeredSample.transpose() * weights(i, j*responseSize+k); - } - } - scatter /= (positiveSamples + negativeSamples); // normalize for numerical stability - DecisionBoundary db; - db.function = scatter.inverse() * VectorXf(positiveMean - negativeMean); // fisher linear discriminant - - // Compute response values to decision boundary - MatrixXf posterior(numImages, responseSize*responseSize); - for (int i=0; i 0) { - const MatrixXf sample(getSample(registered, i, j, k, kernelSize)); - posterior(i, j*responseSize + k) = db.function.transpose() * Map(sample.data(), kernelSize*kernelSize); - } - - // Compute class response means - db.positiveMean = 0; - db.negativeMean = 0; - for (int i=0; i 0) { - const float response = posterior(i, j*responseSize + k); - if (isPositive(j, k, responseSize)) db.positiveMean += response; - else db.negativeMean += response; - } - db.positiveMean /= positiveSamples; - db.negativeMean /= negativeSamples; - - // Compute class response standard deviations - db.positiveSD = 0; - db.negativeSD = 0; - for (int i=0; i 0) { - const float response = posterior(i, j*responseSize + k); - if (isPositive(j, k, responseSize)) db.positiveSD += powf(response-db.positiveMean, 2.f); - else db.negativeSD += powf(response-db.negativeMean, 2.f); - } - db.positiveSD = sqrtf(db.positiveSD / positiveSamples); - db.negativeSD = sqrtf(db.negativeSD / negativeSamples); - - // Normalize responses and propogating prior probabilities - for (int i=0; i 0) { - const float positiveDensity = 1 / (sqrtf(2 * CV_PI) * db.positiveSD) * expf(-0.5 * powf((response - db.positiveMean) / db.positiveSD, 2.f)); - const float negativeDensity = 1 / (sqrtf(2 * CV_PI) * db.negativeSD) * expf(-0.5 * powf((response - db.negativeMean) / db.negativeSD, 2.f)); - response = prior(i, j*responseSize+k) * positiveDensity / (positiveDensity + negativeDensity); - } else { - response = 0; - } - } - const float previousMeanError = IADError(prior, weights); - const float meanError = IADError(posterior, weights); - qDebug() << "Samples positive & negative:" << positiveSamples << negativeSamples; - qDebug() << "Positive mean & stddev:" << db.positiveMean << db.positiveSD; - qDebug() << "Negative mean & stddev:" << db.negativeMean << db.negativeSD; - qDebug() << "Mean error before & after:" << previousMeanError << meanError; - - if (meanError < previousMeanError) { - boundaries.append(db); - return computeBoundaryRecursive(registered, kernelSize, posterior, boundaries); - } else { - return prior; - } - } - - void train(const TemplateList &data) - { - QList registrationMatricies; - { - if (Globals->verbose) - qDebug("Preprocessing training data..."); - - rotationMax = -std::numeric_limits::max(); - scaleMin = std::numeric_limits::max(); - scaleMax = -std::numeric_limits::max(); - xTranslationMax = -std::numeric_limits::max(); - yTranslationMin = std::numeric_limits::max(); - yTranslationMax = -std::numeric_limits::max(); - - foreach (const Template &t, data) { - const QRectF detection = t.file.get(detectionKey); - const QList points = t.file.points(); - const float length = sqrt(pow(points[anchors[1]].x() - points[anchors[0]].x(), 2) + - pow(points[anchors[1]].y() - points[anchors[0]].y(), 2)); - const float rotation = atan2(points[anchors[1]].y() - points[anchors[0]].y(), - points[anchors[1]].x() - points[anchors[0]].x()) * 180 / CV_PI; - const float scale = length / detection.width(); - const float xCenter = (points[anchors[0]].x() + points[anchors[1]].x()) / 2; - const float yCenter = (points[anchors[0]].y() + points[anchors[1]].y()) / 2; - const float xTranslation = (xCenter - detection.x() - detection.width() /2) / detection.width(); - const float yTranslation = (yCenter - detection.y() - detection.height()/2) / detection.width(); - - if (detection.contains(xCenter, yCenter) /* Sometimes the face detector gets the wrong face */) { - rotationMax = max(rotationMax, fabsf(rotation)); - scaleMin = min(scaleMin, scale); - scaleMax = max(scaleMax, scale); - xTranslationMax = max(xTranslationMax, fabsf(xTranslation)); - yTranslationMin = min(yTranslationMin, yTranslation); - yTranslationMax = max(yTranslationMax, yTranslation); - } - - registrationMatricies.append(RegistrationMatrix(t, Point2f(xCenter, yCenter), rotation, iad / length)); - } - } - - if (Globals->verbose) - qDebug("Learning affine model ..."); - - // Construct the registered training data for the landmark - const int regionSize = 2 * iad * radius; // Train on a search size that is twice to the kernel size - QList registered; - foreach (const RegistrationMatrix ®istrationMatrix, registrationMatricies) - registered.append(registrationMatrix.warp(regionSize).cast()); - - // Boosted LDA - const int numImages = registered.size(); - const int kernelSize = iad * radius; - const int responseSize = regionSize - kernelSize + 1; - const MatrixXf prior = MatrixXf::Ones(numImages, responseSize*responseSize); - const MatrixXf responses = computeBoundaryRecursive(registered, kernelSize, prior, decisionBoundaries); - -// for (int i=0; i::max(); -// int maxX = -1; -// int maxY = -1; -// for (int j=0; j maxResponse) { -// maxResponse = response; -// maxY = j; -// maxX = k; -// } -// } - -// MatrixXf draw = registered[i]; -// const int r = 3; -// maxX += kernelSize / 2; -// maxY += kernelSize / 2; -// for (int i=-r; i<=r; i++) -// draw(regionSize/2 + i, regionSize/2) *= 2; -// for (int i=-r; i<=r; i++) -// draw(regionSize/2, regionSize/2 + i) *= 2; -// for (int i=-r; i<=r; i++) -// draw(maxX + i, maxY) /= 2; -// for (int i=-r; i<=r; i++) -// draw(maxX, maxY + i) /= 2; -// show("draw", draw); -// show("responses", VectorXf(responses.row(i))); -// } - - if (Globals->verbose) - qDebug("Learned %d function(s) with error %g", decisionBoundaries.size(), IADError(responses, prior)); - } - - void project(const Template &src, Template &dst) const - { - dst = src; - const QRectF detection = src.file.get(detectionKey); - RegistrationMatrix registrationMatrix(src, OpenCVUtils::toPoint(detection.center())); - - const int regionSize = iad * (1 + radius); - const int kernelSize = iad * radius; - const int responseSize = regionSize - kernelSize + 1; - const float rotationStep = (2 * rotationMax) / (resolution - 1); - const float scaleStep = (scaleMax - scaleMin) / (resolution - 1); - - float bestResponse = -std::numeric_limits::max(), bestRotation = 0, bestScale = 0; - int bestX = 0, bestY =0; - - for (float rotation = -rotationMax; rotation <= rotationMax; rotation += rotationStep) { - registrationMatrix.angle = rotation; - for (float scale = scaleMin; scale <= scaleMax; scale += scaleStep) { - registrationMatrix.scale = iad / (scale * detection.width()); - const MatrixXf registered = registrationMatrix.warp(regionSize).cast(); - - for (int j=0; j(sample.data(), kernelSize*kernelSize); - const float positiveDensity = 1 / (sqrtf(2 * CV_PI) * db.positiveSD) * expf(-0.5 * powf((response - db.positiveMean) / db.positiveSD, 2.f)); - const float negativeDensity = 1 / (sqrtf(2 * CV_PI) * db.negativeSD) * expf(-0.5 * powf((response - db.negativeMean) / db.negativeSD, 2.f)); - posterior *= positiveDensity / (positiveDensity + negativeDensity); - } - if (posterior > bestResponse) { - bestResponse = posterior; - bestX = k + kernelSize/2; - bestY = j + kernelSize/2; - bestRotation = rotation; - bestScale = scale; - } - } - } - } - } - - void store(QDataStream &stream) const - { - stream << rotationMax << scaleMin << scaleMax << xTranslationMax << yTranslationMin << yTranslationMax << decisionBoundaries; - } - - void load(QDataStream &stream) - { - stream >> rotationMax >> scaleMin >> scaleMax >> xTranslationMax >> yTranslationMin >> yTranslationMax >> decisionBoundaries; - } -}; - -BR_REGISTER(Transform, BBLDAAlignmentTransform) - } // namespace br #include "classification/lda.moc" -- libgit2 0.21.4