From 7f8093c9934ddc1f73e86bbef3eb62a45ca2ee7a Mon Sep 17 00:00:00 2001 From: DepthDeluxe Date: Thu, 28 Jan 2016 14:16:10 -0500 Subject: [PATCH] added CUDAPCATransform plugin sketch currently uses CPU code from plugins/core but will start to change things one by one --- openbr/plugins/cuda/cudapca.cpp | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+), 0 deletions(-) create mode 100644 openbr/plugins/cuda/cudapca.cpp diff --git a/openbr/plugins/cuda/cudapca.cpp b/openbr/plugins/cuda/cudapca.cpp new file mode 100644 index 0000000..e7e5e0c --- /dev/null +++ b/openbr/plugins/cuda/cudapca.cpp @@ -0,0 +1,205 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright 2012 The MITRE Corporation * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// CUDA includes +#include + +#include +#include + +#include +#include +#include + +namespace br +{ +/*! + * \ingroup transforms + * \brief Projects input into learned Principal Component Analysis subspace using CUDA. + * \author Brendan Klare \cite bklare + * \author Josh Klontz \cite jklontz + * \author Colin Heinzmann \cite DepthDeluxe + * + * \br_property float keep Options are: [keep < 0 - All eigenvalues are retained, keep == 0 - No PCA is performed and the eigenvectors form an identity matrix, 0 < keep < 1 - Keep is the fraction of the variance to retain, keep >= 1 - keep is the number of leading eigenvectors to retain] Default is 0.95. + * \br_property int drop The number of leading eigen-dimensions to drop. + * \br_property bool whiten Whether or not to perform PCA whitening (i.e., normalize variance of each dimension to unit norm) + */ +class CUDAPCATransform : public Transform +{ + Q_OBJECT + +protected: + Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false) + Q_PROPERTY(int drop READ get_drop WRITE set_drop RESET reset_drop STORED false) + Q_PROPERTY(bool whiten READ get_whiten WRITE set_whiten RESET reset_whiten STORED false) + + BR_PROPERTY(float, keep, 0.95) + BR_PROPERTY(int, drop, 0) + BR_PROPERTY(bool, whiten, false) + + Eigen::VectorXf mean, eVals; + Eigen::MatrixXf eVecs; + + int originalRows; + +public: + CUDAPCATransform() : keep(0.95), drop(0), whiten(false) {} + +private: + double residualReconstructionError(const Template &src) const + { + Template proj; + project(src, proj); + + Eigen::Map srcMap(src.m().ptr(), src.m().rows*src.m().cols); + Eigen::Map projMap(proj.m().ptr(), keep); + + return (srcMap - mean).squaredNorm() - projMap.squaredNorm(); + } + + void train(const TemplateList &trainingSet) + { + if (trainingSet.first().m().type() != CV_32FC1) + qFatal("Requires single channel 32-bit floating point matrices."); + + originalRows = trainingSet.first().m().rows; + int dimsIn = trainingSet.first().m().rows * trainingSet.first().m().cols; + const int instances = trainingSet.size(); + + // Map into 64-bit Eigen matrix + Eigen::MatrixXd data(dimsIn, instances); + for (int i=0; i(trainingSet[i].m().ptr(), dimsIn, 1).cast(); + + trainCore(data); + } + + void project(const Template &src, Template &dst) const + { + dst = cv::Mat(1, keep, CV_32FC1); + + // Map Eigen into OpenCV + Eigen::Map inMap(src.m().ptr(), src.m().rows*src.m().cols, 1); + Eigen::Map outMap(dst.m().ptr(), keep, 1); + + // Do projection + outMap = eVecs.transpose() * (inMap - mean); + } + + void store(QDataStream &stream) const + { + stream << keep << drop << whiten << originalRows << mean << eVals << eVecs; + } + + void load(QDataStream &stream) + { + stream >> keep >> drop >> whiten >> originalRows >> mean >> eVals >> eVecs; + } + +protected: + void trainCore(Eigen::MatrixXd data) + { + int dimsIn = data.rows(); + int instances = data.cols(); + const bool dominantEigenEstimation = (dimsIn > instances); + + Eigen::MatrixXd allEVals, allEVecs; + if (keep != 0) { + // Compute and remove mean + mean = Eigen::VectorXf(dimsIn); + for (int i=0; i eSolver(cov); + allEVals = eSolver.eigenvalues(); + allEVecs = eSolver.eigenvectors(); + if (dominantEigenEstimation) allEVecs = data * allEVecs; + } else { + // Null case + mean = Eigen::VectorXf::Zero(dimsIn); + allEVecs = Eigen::MatrixXd::Identity(dimsIn, dimsIn); + allEVals = Eigen::VectorXd::Ones(dimsIn); + } + + if (keep <= 0) { + keep = dimsIn - drop; + } else if (keep < 1) { + // Keep eigenvectors that retain a certain energy percentage. + const double totalEnergy = allEVals.sum(); + if (totalEnergy == 0) { + keep = 0; + } else { + double currentEnergy = 0; + int i=0; + while ((currentEnergy / totalEnergy < keep) && (i < allEVals.rows())) { + currentEnergy += allEVals(allEVals.rows()-(i+1)); + i++; + } + keep = i - drop; + } + } else { + if (keep + drop > allEVals.rows()) { + qWarning("Insufficient samples, needed at least %d but only got %d.", (int)keep + drop, (int)allEVals.rows()); + keep = allEVals.rows() - drop; + } + } + + // Keep highest energy vectors + eVals = Eigen::VectorXf((int)keep, 1); + eVecs = Eigen::MatrixXf(allEVecs.rows(), (int)keep); + for (int i=0; i() / allEVecs.col(index).norm(); + if (whiten) eVecs.col(i) /= sqrt(eVals(i)); + } + + // Debug output + if (Globals->verbose) qDebug() << "PCA Training:\n\tDimsIn =" << dimsIn << "\n\tKeep =" << keep; + } + + void writeEigenVectors(const Eigen::MatrixXd &allEVals, const Eigen::MatrixXd &allEVecs) const + { + const int originalCols = mean.rows() / originalRows; + + { // Write out mean image + cv::Mat out(originalRows, originalCols, CV_32FC1); + Eigen::Map outMap(out.ptr(), mean.rows(), 1); + outMap = mean.col(0); + // OpenCVUtils::saveImage(out, Globals->Debug+"/PCA/eigenVectors/mean.png"); + } + + // Write out sample eigen vectors (16 highest, 8 lowest), filename = eigenvalue. + for (int k=0; k<(int)allEVals.size(); k++) { + if ((k < 8) || (k >= (int)allEVals.size()-16)) { + cv::Mat out(originalRows, originalCols, CV_64FC1); + Eigen::Map outMap(out.ptr(), mean.rows(), 1); + outMap = allEVecs.col(k); + // OpenCVUtils::saveImage(out, Globals->Debug+"/PCA/eigenVectors/"+QString::number(allEVals(k),'f',0)+".png"); + } + } + } +}; + +BR_REGISTER(Transform, CUDAPCATransform) +} // namespace br + +#include "cuda/cudapca.moc" -- libgit2 0.21.4