diff --git a/modules/contrib/include/opencv2/contrib/contrib.hpp b/modules/contrib/include/opencv2/contrib/contrib.hpp index 8a3f85d113..64faf7797d 100644 --- a/modules/contrib/include/opencv2/contrib/contrib.hpp +++ b/modules/contrib/include/opencv2/contrib/contrib.hpp @@ -845,6 +845,131 @@ namespace cv bool get_uv(double x, double y, int&u, int&v); void create_map(int M, int N, int R, int S, double ro0, double smin); }; + + CV_EXPORTS Mat subspaceProject(InputArray W, InputArray mean, InputArray src); + CV_EXPORTS Mat subspaceReconstruct(InputArray W, InputArray mean, InputArray src); + + class CV_EXPORTS LDA + { + public: + // Initializes a LDA with num_components (default 0) and specifies how + // samples are aligned (default dataAsRow=true). + LDA(int num_components = 0) : + _num_components(num_components) {}; + + // Initializes and performs a Discriminant Analysis with Fisher's + // Optimization Criterion on given data in src and corresponding labels + // in labels. If 0 (or less) number of components are given, they are + // automatically determined for given data in computation. + LDA(const Mat& src, vector labels, + int num_components = 0) : + _num_components(num_components) + { + this->compute(src, labels); //! compute eigenvectors and eigenvalues + } + + // Initializes and performs a Discriminant Analysis with Fisher's + // Optimization Criterion on given data in src and corresponding labels + // in labels. If 0 (or less) number of components are given, they are + // automatically determined for given data in computation. + LDA(InputArray src, InputArray labels, + int num_components = 0) : + _num_components(num_components) + { + this->compute(src, labels); //! compute eigenvectors and eigenvalues + } + + // Serializes this object to a given filename. + void save(const string& filename) const; + + // Deserializes this object from a given filename. + void load(const string& filename); + + // Serializes this object to a given cv::FileStorage. + void save(FileStorage& fs) const; + + // Deserializes this object from a given cv::FileStorage. + void load(const FileStorage& node); + + // Destructor. + ~LDA() {} + + //! Compute the discriminants for data in src and labels. + void compute(InputArray src, InputArray labels); + + // Projects samples into the LDA subspace. + Mat project(InputArray src); + + // Reconstructs projections from the LDA subspace. + Mat reconstruct(InputArray src); + + // Returns the eigenvectors of this LDA. + Mat eigenvectors() const { return _eigenvectors; }; + + // Returns the eigenvalues of this LDA. + Mat eigenvalues() const { return _eigenvalues; } + + protected: + bool _dataAsRow; + int _num_components; + Mat _eigenvectors; + Mat _eigenvalues; + + void lda(InputArray src, InputArray labels); + }; + + class CV_EXPORTS FaceRecognizer + { + public: + //! virtual destructor + virtual ~FaceRecognizer() {} + + // Trains a FaceRecognizer. + virtual void train(InputArray src, InputArray labels) = 0; + + // Gets a prediction from a FaceRecognizer. + virtual int predict(InputArray src) const = 0; + + // Serializes this object to a given filename. + virtual void save(const string& filename) const; + + // Deserializes this object from a given filename. + virtual void load(const string& filename); + + // Serializes this object to a given cv::FileStorage. + virtual void save(FileStorage& fs) const = 0; + + // Deserializes this object from a given cv::FileStorage. + virtual void load(const FileStorage& fs) = 0; + + // Returns eigenvectors (if any) + virtual Mat eigenvectors() const { return Mat(); } + }; + + CV_EXPORTS Ptr createEigenFaceRecognizer(int num_components = 0); + CV_EXPORTS Ptr createFisherFaceRecognizer(int num_components = 0); + CV_EXPORTS Ptr createLBPHFaceRecognizer(int radius=1, int neighbors=8, + int grid_x=8, int grid_y=8); + + enum + { + COLORMAP_AUTUMN = 0, + COLORMAP_BONE = 1, + COLORMAP_JET = 2, + COLORMAP_WINTER = 3, + COLORMAP_RAINBOW = 4, + COLORMAP_OCEAN = 5, + COLORMAP_SUMMER = 6, + COLORMAP_SPRING = 7, + COLORMAP_COOL = 8, + COLORMAP_HSV = 9, + COLORMAP_PINK = 10, + COLORMAP_HOT = 11, + COLORMAP_MKPJ1 = 12, + COLORMAP_MKPJ2 = 13 + }; + + CV_EXPORTS void applyColorMap(InputArray src, OutputArray dst, int colormap); } diff --git a/modules/contrib/src/colormap.cpp b/modules/contrib/src/colormap.cpp new file mode 100644 index 0000000000..fad34983ac --- /dev/null +++ b/modules/contrib/src/colormap.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2011. Philipp Wagner . + * Released to public domain under terms of the BSD Simplified license. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * See + */ +#include "precomp.hpp" +#include + +namespace cv +{ + +static Mat linspace(float x0, float x1, int n) +{ + Mat pts(n, 1, CV_32FC1); + float step = (x1-x0)/floor(n-1); + for(int i = 0; i < n; i++) + pts.at(i,0) = x0+i*step; + return pts; +} + +//------------------------------------------------------------------------------ +// cv::sortMatrixRowsByIndices +//------------------------------------------------------------------------------ +static void sortMatrixRowsByIndices(InputArray _src, InputArray _indices, OutputArray _dst) +{ + if(_indices.getMat().type() != CV_32SC1) + CV_Error(CV_StsUnsupportedFormat, "cv::sortRowsByIndices only works on integer indices!"); + Mat src = _src.getMat(); + vector indices = _indices.getMat(); + _dst.create(src.rows, src.cols, src.type()); + Mat dst = _dst.getMat(); + for(int idx = 0; idx < indices.size(); idx++) { + Mat originalRow = src.row(indices[idx]); + Mat sortedRow = dst.row(idx); + originalRow.copyTo(sortedRow); + } +} + +static Mat sortMatrixRowsByIndices(InputArray src, InputArray indices) +{ + Mat dst; + sortMatrixRowsByIndices(src, indices, dst); + return dst; +} + + +Mat argsort(InputArray _src, bool ascending=true) +{ + Mat src = _src.getMat(); + if (src.rows != 1 && src.cols != 1) + CV_Error(CV_StsBadArg, "cv::argsort only sorts 1D matrices."); + int flags = CV_SORT_EVERY_ROW+(ascending ? CV_SORT_ASCENDING : CV_SORT_DESCENDING); + Mat sorted_indices; + sortIdx(src.reshape(1,1),sorted_indices,flags); + return sorted_indices; +} + +template static +Mat interp1_(const Mat& X_, const Mat& Y_, const Mat& XI) +{ + int n = XI.rows; + // sort input table + vector sort_indices = argsort(X_); + + Mat X = sortMatrixRowsByIndices(X_,sort_indices); + Mat Y = sortMatrixRowsByIndices(Y_,sort_indices); + // interpolated values + Mat yi = Mat::zeros(XI.size(), XI.type()); + for(int i = 0; i < n; i++) { + int c = 0; + int low = 0; + int high = X.rows - 1; + // set bounds + if(XI.at<_Tp>(i,0) < X.at<_Tp>(low, 0)) + high = 1; + if(XI.at<_Tp>(i,0) > X.at<_Tp>(high, 0)) + low = high - 1; + // binary search + while((high-low)>1) { + c = low + ((high - low) >> 1); + if(XI.at<_Tp>(i,0) > X.at<_Tp>(c,0)) { + low = c; + } else { + high = c; + } + } + // linear interpolation + yi.at<_Tp>(i,0) += Y.at<_Tp>(low,0) + + (XI.at<_Tp>(i,0) - X.at<_Tp>(low,0)) + * (Y.at<_Tp>(high,0) - Y.at<_Tp>(low,0)) + / (X.at<_Tp>(high,0) - X.at<_Tp>(low,0)); + } + return yi; +} + +static Mat interp1(InputArray _x, InputArray _Y, InputArray _xi) +{ + // get matrices + Mat x = _x.getMat(); + Mat Y = _Y.getMat(); + Mat xi = _xi.getMat(); + // check types & alignment + assert((x.type() == Y.type()) && (Y.type() == xi.type())); + assert((x.cols == 1) && (x.rows == Y.rows) && (x.cols == Y.cols)); + // call templated interp1 + switch(x.type()) { + case CV_8SC1: return interp1_(x,Y,xi); break; + case CV_8UC1: return interp1_(x,Y,xi); break; + case CV_16SC1: return interp1_(x,Y,xi); break; + case CV_16UC1: return interp1_(x,Y,xi); break; + case CV_32SC1: return interp1_(x,Y,xi); break; + case CV_32FC1: return interp1_(x,Y,xi); break; + case CV_64FC1: return interp1_(x,Y,xi); break; + default: CV_Error(CV_StsUnsupportedFormat, ""); return Mat(); + } +} + +namespace colormap +{ + + class ColorMap { + + protected: + Mat _lut; + + public: + virtual ~ColorMap() {} + + // Applies the colormap on a given image. + // + // This function expects BGR-aligned data of type CV_8UC1 or + // CV_8UC3. If the wrong image type is given, the original image + // will be returned. + // + // Throws an error for wrong-aligned lookup table, which must be + // of size 256 in the latest OpenCV release (2.3.1). + void operator()(InputArray src, OutputArray dst) const; + + // Setup base map to interpolate from. + virtual void init(int n) = 0; + + // Interpolates from a base colormap. + static Mat linear_colormap(InputArray X, + InputArray r, InputArray g, InputArray b, + int n) { + return linear_colormap(X,r,g,b,linspace(0,1,n)); + } + + // Interpolates from a base colormap. + static Mat linear_colormap(InputArray X, + InputArray r, InputArray g, InputArray b, + float begin, float end, float n) { + return linear_colormap(X,r,g,b,linspace(begin,end,n)); + } + + // Interpolates from a base colormap. + static Mat linear_colormap(InputArray X, + InputArray r, InputArray g, InputArray b, + InputArray xi); + }; + + // Equals the GNU Octave colormap "autumn". + class Autumn : public ColorMap { + public: + Autumn() : ColorMap() { + init(256); + } + + Autumn(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float g[] = { 0, 0.01587301587301587, 0.03174603174603174, 0.04761904761904762, 0.06349206349206349, 0.07936507936507936, 0.09523809523809523, 0.1111111111111111, 0.126984126984127, 0.1428571428571428, 0.1587301587301587, 0.1746031746031746, 0.1904761904761905, 0.2063492063492063, 0.2222222222222222, 0.2380952380952381, 0.253968253968254, 0.2698412698412698, 0.2857142857142857, 0.3015873015873016, 0.3174603174603174, 0.3333333333333333, 0.3492063492063492, 0.3650793650793651, 0.3809523809523809, 0.3968253968253968, 0.4126984126984127, 0.4285714285714285, 0.4444444444444444, 0.4603174603174603, 0.4761904761904762, 0.492063492063492, 0.5079365079365079, 0.5238095238095238, 0.5396825396825397, 0.5555555555555556, 0.5714285714285714, 0.5873015873015873, 0.6031746031746031, 0.6190476190476191, 0.6349206349206349, 0.6507936507936508, 0.6666666666666666, 0.6825396825396826, 0.6984126984126984, 0.7142857142857143, 0.7301587301587301, 0.746031746031746, 0.7619047619047619, 0.7777777777777778, 0.7936507936507936, 0.8095238095238095, 0.8253968253968254, 0.8412698412698413, 0.8571428571428571, 0.873015873015873, 0.8888888888888888, 0.9047619047619048, 0.9206349206349206, 0.9365079365079365, 0.9523809523809523, 0.9682539682539683, 0.9841269841269841, 1}; + float b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "bone". + class Bone : public ColorMap { + public: + Bone() : ColorMap() { + init(256); + } + + Bone(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 0, 0.01388888888888889, 0.02777777777777778, 0.04166666666666666, 0.05555555555555555, 0.06944444444444445, 0.08333333333333333, 0.09722222222222221, 0.1111111111111111, 0.125, 0.1388888888888889, 0.1527777777777778, 0.1666666666666667, 0.1805555555555556, 0.1944444444444444, 0.2083333333333333, 0.2222222222222222, 0.2361111111111111, 0.25, 0.2638888888888889, 0.2777777777777778, 0.2916666666666666, 0.3055555555555555, 0.3194444444444444, 0.3333333333333333, 0.3472222222222222, 0.3611111111111111, 0.375, 0.3888888888888888, 0.4027777777777777, 0.4166666666666666, 0.4305555555555555, 0.4444444444444444, 0.4583333333333333, 0.4722222222222222, 0.4861111111111112, 0.5, 0.5138888888888888, 0.5277777777777778, 0.5416666666666667, 0.5555555555555556, 0.5694444444444444, 0.5833333333333333, 0.5972222222222222, 0.611111111111111, 0.6249999999999999, 0.6388888888888888, 0.6527777777777778, 0.6726190476190474, 0.6944444444444442, 0.7162698412698412, 0.7380952380952381, 0.7599206349206349, 0.7817460317460316, 0.8035714285714286, 0.8253968253968254, 0.8472222222222221, 0.8690476190476188, 0.8908730158730158, 0.9126984126984128, 0.9345238095238095, 0.9563492063492063, 0.978174603174603, 1}; + float g[] = { 0, 0.01388888888888889, 0.02777777777777778, 0.04166666666666666, 0.05555555555555555, 0.06944444444444445, 0.08333333333333333, 0.09722222222222221, 0.1111111111111111, 0.125, 0.1388888888888889, 0.1527777777777778, 0.1666666666666667, 0.1805555555555556, 0.1944444444444444, 0.2083333333333333, 0.2222222222222222, 0.2361111111111111, 0.25, 0.2638888888888889, 0.2777777777777778, 0.2916666666666666, 0.3055555555555555, 0.3194444444444444, 0.3353174603174602, 0.3544973544973544, 0.3736772486772486, 0.3928571428571428, 0.412037037037037, 0.4312169312169312, 0.4503968253968254, 0.4695767195767195, 0.4887566137566137, 0.5079365079365078, 0.5271164021164021, 0.5462962962962963, 0.5654761904761904, 0.5846560846560845, 0.6038359788359787, 0.623015873015873, 0.6421957671957671, 0.6613756613756612, 0.6805555555555555, 0.6997354497354497, 0.7189153439153438, 0.7380952380952379, 0.7572751322751322, 0.7764550264550264, 0.7916666666666666, 0.8055555555555555, 0.8194444444444444, 0.8333333333333334, 0.8472222222222222, 0.861111111111111, 0.875, 0.8888888888888888, 0.9027777777777777, 0.9166666666666665, 0.9305555555555555, 0.9444444444444444, 0.9583333333333333, 0.9722222222222221, 0.986111111111111, 1}; + float b[] = { 0, 0.01917989417989418, 0.03835978835978836, 0.05753968253968253, 0.07671957671957672, 0.09589947089947089, 0.1150793650793651, 0.1342592592592592, 0.1534391534391534, 0.1726190476190476, 0.1917989417989418, 0.210978835978836, 0.2301587301587301, 0.2493386243386243, 0.2685185185185185, 0.2876984126984127, 0.3068783068783069, 0.326058201058201, 0.3452380952380952, 0.3644179894179894, 0.3835978835978835, 0.4027777777777777, 0.4219576719576719, 0.4411375661375661, 0.4583333333333333, 0.4722222222222222, 0.4861111111111111, 0.5, 0.5138888888888888, 0.5277777777777777, 0.5416666666666666, 0.5555555555555556, 0.5694444444444444, 0.5833333333333333, 0.5972222222222222, 0.6111111111111112, 0.625, 0.6388888888888888, 0.6527777777777778, 0.6666666666666667, 0.6805555555555556, 0.6944444444444444, 0.7083333333333333, 0.7222222222222222, 0.736111111111111, 0.7499999999999999, 0.7638888888888888, 0.7777777777777778, 0.7916666666666666, 0.8055555555555555, 0.8194444444444444, 0.8333333333333334, 0.8472222222222222, 0.861111111111111, 0.875, 0.8888888888888888, 0.9027777777777777, 0.9166666666666665, 0.9305555555555555, 0.9444444444444444, 0.9583333333333333, 0.9722222222222221, 0.986111111111111, 1}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + + + + // Equals the GNU Octave colormap "jet". + class Jet : public ColorMap { + + public: + Jet() { + init(256); + } + Jet(int n) : ColorMap() { + init(n); + } + + void init(int n) { + // breakpoints + Mat X = linspace(0,1,256); + // define the basemap + float r[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00588235294117645,0.02156862745098032,0.03725490196078418,0.05294117647058827,0.06862745098039214,0.084313725490196,0.1000000000000001,0.115686274509804,0.1313725490196078,0.1470588235294117,0.1627450980392156,0.1784313725490196,0.1941176470588235,0.2098039215686274,0.2254901960784315,0.2411764705882353,0.2568627450980392,0.2725490196078431,0.2882352941176469,0.303921568627451,0.3196078431372549,0.3352941176470587,0.3509803921568628,0.3666666666666667,0.3823529411764706,0.3980392156862744,0.4137254901960783,0.4294117647058824,0.4450980392156862,0.4607843137254901,0.4764705882352942,0.4921568627450981,0.5078431372549019,0.5235294117647058,0.5392156862745097,0.5549019607843135,0.5705882352941174,0.5862745098039217,0.6019607843137256,0.6176470588235294,0.6333333333333333,0.6490196078431372,0.664705882352941,0.6803921568627449,0.6960784313725492,0.7117647058823531,0.7274509803921569,0.7431372549019608,0.7588235294117647,0.7745098039215685,0.7901960784313724,0.8058823529411763,0.8215686274509801,0.8372549019607844,0.8529411764705883,0.8686274509803922,0.884313725490196,0.8999999999999999,0.9156862745098038,0.9313725490196076,0.947058823529412,0.9627450980392158,0.9784313725490197,0.9941176470588236,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9862745098039216,0.9705882352941178,0.9549019607843139,0.93921568627451,0.9235294117647062,0.9078431372549018,0.892156862745098,0.8764705882352941,0.8607843137254902,0.8450980392156864,0.8294117647058825,0.8137254901960786,0.7980392156862743,0.7823529411764705,0.7666666666666666,0.7509803921568627,0.7352941176470589,0.719607843137255,0.7039215686274511,0.6882352941176473,0.6725490196078434,0.6568627450980391,0.6411764705882352,0.6254901960784314,0.6098039215686275,0.5941176470588236,0.5784313725490198,0.5627450980392159,0.5470588235294116,0.5313725490196077,0.5156862745098039,0.5}; + float g[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001960784313725483,0.01764705882352935,0.03333333333333333,0.0490196078431373,0.06470588235294117,0.08039215686274503,0.09607843137254901,0.111764705882353,0.1274509803921569,0.1431372549019607,0.1588235294117647,0.1745098039215687,0.1901960784313725,0.2058823529411764,0.2215686274509804,0.2372549019607844,0.2529411764705882,0.2686274509803921,0.2843137254901961,0.3,0.3156862745098039,0.3313725490196078,0.3470588235294118,0.3627450980392157,0.3784313725490196,0.3941176470588235,0.4098039215686274,0.4254901960784314,0.4411764705882353,0.4568627450980391,0.4725490196078431,0.4882352941176471,0.503921568627451,0.5196078431372548,0.5352941176470587,0.5509803921568628,0.5666666666666667,0.5823529411764705,0.5980392156862746,0.6137254901960785,0.6294117647058823,0.6450980392156862,0.6607843137254901,0.6764705882352942,0.692156862745098,0.7078431372549019,0.723529411764706,0.7392156862745098,0.7549019607843137,0.7705882352941176,0.7862745098039214,0.8019607843137255,0.8176470588235294,0.8333333333333333,0.8490196078431373,0.8647058823529412,0.8803921568627451,0.8960784313725489,0.9117647058823528,0.9274509803921569,0.9431372549019608,0.9588235294117646,0.9745098039215687,0.9901960784313726,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9901960784313726,0.9745098039215687,0.9588235294117649,0.943137254901961,0.9274509803921571,0.9117647058823528,0.8960784313725489,0.8803921568627451,0.8647058823529412,0.8490196078431373,0.8333333333333335,0.8176470588235296,0.8019607843137253,0.7862745098039214,0.7705882352941176,0.7549019607843137,0.7392156862745098,0.723529411764706,0.7078431372549021,0.6921568627450982,0.6764705882352944,0.6607843137254901,0.6450980392156862,0.6294117647058823,0.6137254901960785,0.5980392156862746,0.5823529411764707,0.5666666666666669,0.5509803921568626,0.5352941176470587,0.5196078431372548,0.503921568627451,0.4882352941176471,0.4725490196078432,0.4568627450980394,0.4411764705882355,0.4254901960784316,0.4098039215686273,0.3941176470588235,0.3784313725490196,0.3627450980392157,0.3470588235294119,0.331372549019608,0.3156862745098041,0.2999999999999998,0.284313725490196,0.2686274509803921,0.2529411764705882,0.2372549019607844,0.2215686274509805,0.2058823529411766,0.1901960784313728,0.1745098039215689,0.1588235294117646,0.1431372549019607,0.1274509803921569,0.111764705882353,0.09607843137254912,0.08039215686274526,0.06470588235294139,0.04901960784313708,0.03333333333333321,0.01764705882352935,0.001960784313725483,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + float b[] = {0.5,0.5156862745098039,0.5313725490196078,0.5470588235294118,0.5627450980392157,0.5784313725490196,0.5941176470588235,0.6098039215686275,0.6254901960784314,0.6411764705882352,0.6568627450980392,0.6725490196078432,0.6882352941176471,0.7039215686274509,0.7196078431372549,0.7352941176470589,0.7509803921568627,0.7666666666666666,0.7823529411764706,0.7980392156862746,0.8137254901960784,0.8294117647058823,0.8450980392156863,0.8607843137254902,0.8764705882352941,0.892156862745098,0.907843137254902,0.9235294117647059,0.9392156862745098,0.9549019607843137,0.9705882352941176,0.9862745098039216,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9941176470588236,0.9784313725490197,0.9627450980392158,0.9470588235294117,0.9313725490196079,0.915686274509804,0.8999999999999999,0.884313725490196,0.8686274509803922,0.8529411764705883,0.8372549019607844,0.8215686274509804,0.8058823529411765,0.7901960784313726,0.7745098039215685,0.7588235294117647,0.7431372549019608,0.7274509803921569,0.7117647058823531,0.696078431372549,0.6803921568627451,0.6647058823529413,0.6490196078431372,0.6333333333333333,0.6176470588235294,0.6019607843137256,0.5862745098039217,0.5705882352941176,0.5549019607843138,0.5392156862745099,0.5235294117647058,0.5078431372549019,0.4921568627450981,0.4764705882352942,0.4607843137254903,0.4450980392156865,0.4294117647058826,0.4137254901960783,0.3980392156862744,0.3823529411764706,0.3666666666666667,0.3509803921568628,0.335294117647059,0.3196078431372551,0.3039215686274508,0.2882352941176469,0.2725490196078431,0.2568627450980392,0.2411764705882353,0.2254901960784315,0.2098039215686276,0.1941176470588237,0.1784313725490199,0.1627450980392156,0.1470588235294117,0.1313725490196078,0.115686274509804,0.1000000000000001,0.08431372549019622,0.06862745098039236,0.05294117647058805,0.03725490196078418,0.02156862745098032,0.00588235294117645,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + // now build lookup table + this->_lut = ColorMap::linear_colormap(X, + Mat(256,1, CV_32FC1, r).clone(), // red + Mat(256,1, CV_32FC1, g).clone(), // green + Mat(256,1, CV_32FC1, b).clone(), // blue + n); + } + }; + + // Equals the GNU Octave colormap "winter". + class Winter : public ColorMap { + public: + Winter() : ColorMap() { + init(256); + } + + Winter(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + float g[] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; + float b[] = {1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65, 0.6, 0.55, 0.5}; + Mat X = linspace(0,1,11); + this->_lut = ColorMap::linear_colormap(X, + Mat(11,1, CV_32FC1, r).clone(), // red + Mat(11,1, CV_32FC1, g).clone(), // green + Mat(11,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "rainbow". + class Rainbow : public ColorMap { + public: + Rainbow() : ColorMap() { + init(256); + } + + Rainbow(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9365079365079367, 0.8571428571428572, 0.7777777777777777, 0.6984126984126986, 0.6190476190476191, 0.53968253968254, 0.4603174603174605, 0.3809523809523814, 0.3015873015873018, 0.2222222222222223, 0.1428571428571432, 0.06349206349206415, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.03174603174603208, 0.08465608465608465, 0.1375661375661377, 0.1904761904761907, 0.2433862433862437, 0.2962962962962963, 0.3492063492063493, 0.4021164021164023, 0.4550264550264553, 0.5079365079365079, 0.5608465608465609, 0.6137566137566139, 0.666666666666667}; + float g[] = { 0, 0.03968253968253968, 0.07936507936507936, 0.119047619047619, 0.1587301587301587, 0.1984126984126984, 0.2380952380952381, 0.2777777777777778, 0.3174603174603174, 0.3571428571428571, 0.3968253968253968, 0.4365079365079365, 0.4761904761904762, 0.5158730158730158, 0.5555555555555556, 0.5952380952380952, 0.6349206349206349, 0.6746031746031745, 0.7142857142857142, 0.753968253968254, 0.7936507936507936, 0.8333333333333333, 0.873015873015873, 0.9126984126984127, 0.9523809523809523, 0.992063492063492, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9841269841269842, 0.9047619047619047, 0.8253968253968256, 0.7460317460317465, 0.666666666666667, 0.587301587301587, 0.5079365079365079, 0.4285714285714288, 0.3492063492063493, 0.2698412698412698, 0.1904761904761907, 0.1111111111111116, 0.03174603174603208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + float b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01587301587301582, 0.09523809523809534, 0.1746031746031744, 0.2539682539682535, 0.333333333333333, 0.412698412698413, 0.4920634920634921, 0.5714285714285712, 0.6507936507936507, 0.7301587301587302, 0.8095238095238093, 0.8888888888888884, 0.9682539682539679, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "ocean". + class Ocean : public ColorMap { + public: + Ocean() : ColorMap() { + init(256); + } + + Ocean(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904762, 0.09523809523809523, 0.1428571428571428, 0.1904761904761905, 0.2380952380952381, 0.2857142857142857, 0.3333333333333333, 0.3809523809523809, 0.4285714285714285, 0.4761904761904762, 0.5238095238095238, 0.5714285714285714, 0.6190476190476191, 0.6666666666666666, 0.7142857142857143, 0.7619047619047619, 0.8095238095238095, 0.8571428571428571, 0.9047619047619048, 0.9523809523809523, 1}; + float g[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02380952380952381, 0.04761904761904762, 0.07142857142857142, 0.09523809523809523, 0.119047619047619, 0.1428571428571428, 0.1666666666666667, 0.1904761904761905, 0.2142857142857143, 0.2380952380952381, 0.2619047619047619, 0.2857142857142857, 0.3095238095238095, 0.3333333333333333, 0.3571428571428572, 0.3809523809523809, 0.4047619047619048, 0.4285714285714285, 0.4523809523809524, 0.4761904761904762, 0.5, 0.5238095238095238, 0.5476190476190477, 0.5714285714285714, 0.5952380952380952, 0.6190476190476191, 0.6428571428571429, 0.6666666666666666, 0.6904761904761905, 0.7142857142857143, 0.7380952380952381, 0.7619047619047619, 0.7857142857142857, 0.8095238095238095, 0.8333333333333334, 0.8571428571428571, 0.8809523809523809, 0.9047619047619048, 0.9285714285714286, 0.9523809523809523, 0.9761904761904762, 1}; + float b[] = { 0, 0.01587301587301587, 0.03174603174603174, 0.04761904761904762, 0.06349206349206349, 0.07936507936507936, 0.09523809523809523, 0.1111111111111111, 0.126984126984127, 0.1428571428571428, 0.1587301587301587, 0.1746031746031746, 0.1904761904761905, 0.2063492063492063, 0.2222222222222222, 0.2380952380952381, 0.253968253968254, 0.2698412698412698, 0.2857142857142857, 0.3015873015873016, 0.3174603174603174, 0.3333333333333333, 0.3492063492063492, 0.3650793650793651, 0.3809523809523809, 0.3968253968253968, 0.4126984126984127, 0.4285714285714285, 0.4444444444444444, 0.4603174603174603, 0.4761904761904762, 0.492063492063492, 0.5079365079365079, 0.5238095238095238, 0.5396825396825397, 0.5555555555555556, 0.5714285714285714, 0.5873015873015873, 0.6031746031746031, 0.6190476190476191, 0.6349206349206349, 0.6507936507936508, 0.6666666666666666, 0.6825396825396826, 0.6984126984126984, 0.7142857142857143, 0.7301587301587301, 0.746031746031746, 0.7619047619047619, 0.7777777777777778, 0.7936507936507936, 0.8095238095238095, 0.8253968253968254, 0.8412698412698413, 0.8571428571428571, 0.873015873015873, 0.8888888888888888, 0.9047619047619048, 0.9206349206349206, 0.9365079365079365, 0.9523809523809523, 0.9682539682539683, 0.9841269841269841, 1}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "summer". + class Summer : public ColorMap { + public: + Summer() : ColorMap() { + init(256); + } + + Summer(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 0, 0.01587301587301587, 0.03174603174603174, 0.04761904761904762, 0.06349206349206349, 0.07936507936507936, 0.09523809523809523, 0.1111111111111111, 0.126984126984127, 0.1428571428571428, 0.1587301587301587, 0.1746031746031746, 0.1904761904761905, 0.2063492063492063, 0.2222222222222222, 0.2380952380952381, 0.253968253968254, 0.2698412698412698, 0.2857142857142857, 0.3015873015873016, 0.3174603174603174, 0.3333333333333333, 0.3492063492063492, 0.3650793650793651, 0.3809523809523809, 0.3968253968253968, 0.4126984126984127, 0.4285714285714285, 0.4444444444444444, 0.4603174603174603, 0.4761904761904762, 0.492063492063492, 0.5079365079365079, 0.5238095238095238, 0.5396825396825397, 0.5555555555555556, 0.5714285714285714, 0.5873015873015873, 0.6031746031746031, 0.6190476190476191, 0.6349206349206349, 0.6507936507936508, 0.6666666666666666, 0.6825396825396826, 0.6984126984126984, 0.7142857142857143, 0.7301587301587301, 0.746031746031746, 0.7619047619047619, 0.7777777777777778, 0.7936507936507936, 0.8095238095238095, 0.8253968253968254, 0.8412698412698413, 0.8571428571428571, 0.873015873015873, 0.8888888888888888, 0.9047619047619048, 0.9206349206349206, 0.9365079365079365, 0.9523809523809523, 0.9682539682539683, 0.9841269841269841, 1}; + float g[] = { 0.5, 0.5079365079365079, 0.5158730158730158, 0.5238095238095238, 0.5317460317460317, 0.5396825396825397, 0.5476190476190477, 0.5555555555555556, 0.5634920634920635, 0.5714285714285714, 0.5793650793650793, 0.5873015873015873, 0.5952380952380952, 0.6031746031746031, 0.6111111111111112, 0.6190476190476191, 0.626984126984127, 0.6349206349206349, 0.6428571428571428, 0.6507936507936508, 0.6587301587301587, 0.6666666666666666, 0.6746031746031746, 0.6825396825396826, 0.6904761904761905, 0.6984126984126984, 0.7063492063492063, 0.7142857142857143, 0.7222222222222222, 0.7301587301587301, 0.7380952380952381, 0.746031746031746, 0.753968253968254, 0.7619047619047619, 0.7698412698412698, 0.7777777777777778, 0.7857142857142857, 0.7936507936507937, 0.8015873015873016, 0.8095238095238095, 0.8174603174603174, 0.8253968253968254, 0.8333333333333333, 0.8412698412698413, 0.8492063492063492, 0.8571428571428572, 0.8650793650793651, 0.873015873015873, 0.8809523809523809, 0.8888888888888888, 0.8968253968253967, 0.9047619047619048, 0.9126984126984127, 0.9206349206349207, 0.9285714285714286, 0.9365079365079365, 0.9444444444444444, 0.9523809523809523, 0.9603174603174602, 0.9682539682539683, 0.9761904761904762, 0.9841269841269842, 0.9920634920634921, 1}; + float b[] = { 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "spring". + class Spring : public ColorMap { + public: + Spring() : ColorMap() { + init(256); + } + + Spring(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float g[] = { 0, 0.01587301587301587, 0.03174603174603174, 0.04761904761904762, 0.06349206349206349, 0.07936507936507936, 0.09523809523809523, 0.1111111111111111, 0.126984126984127, 0.1428571428571428, 0.1587301587301587, 0.1746031746031746, 0.1904761904761905, 0.2063492063492063, 0.2222222222222222, 0.2380952380952381, 0.253968253968254, 0.2698412698412698, 0.2857142857142857, 0.3015873015873016, 0.3174603174603174, 0.3333333333333333, 0.3492063492063492, 0.3650793650793651, 0.3809523809523809, 0.3968253968253968, 0.4126984126984127, 0.4285714285714285, 0.4444444444444444, 0.4603174603174603, 0.4761904761904762, 0.492063492063492, 0.5079365079365079, 0.5238095238095238, 0.5396825396825397, 0.5555555555555556, 0.5714285714285714, 0.5873015873015873, 0.6031746031746031, 0.6190476190476191, 0.6349206349206349, 0.6507936507936508, 0.6666666666666666, 0.6825396825396826, 0.6984126984126984, 0.7142857142857143, 0.7301587301587301, 0.746031746031746, 0.7619047619047619, 0.7777777777777778, 0.7936507936507936, 0.8095238095238095, 0.8253968253968254, 0.8412698412698413, 0.8571428571428571, 0.873015873015873, 0.8888888888888888, 0.9047619047619048, 0.9206349206349206, 0.9365079365079365, 0.9523809523809523, 0.9682539682539683, 0.9841269841269841, 1}; + float b[] = { 1, 0.9841269841269842, 0.9682539682539683, 0.9523809523809523, 0.9365079365079365, 0.9206349206349207, 0.9047619047619048, 0.8888888888888888, 0.873015873015873, 0.8571428571428572, 0.8412698412698413, 0.8253968253968254, 0.8095238095238095, 0.7936507936507937, 0.7777777777777778, 0.7619047619047619, 0.746031746031746, 0.7301587301587302, 0.7142857142857143, 0.6984126984126984, 0.6825396825396826, 0.6666666666666667, 0.6507936507936508, 0.6349206349206349, 0.6190476190476191, 0.6031746031746033, 0.5873015873015873, 0.5714285714285714, 0.5555555555555556, 0.5396825396825398, 0.5238095238095238, 0.5079365079365079, 0.4920634920634921, 0.4761904761904762, 0.4603174603174603, 0.4444444444444444, 0.4285714285714286, 0.4126984126984127, 0.3968253968253969, 0.3809523809523809, 0.3650793650793651, 0.3492063492063492, 0.3333333333333334, 0.3174603174603174, 0.3015873015873016, 0.2857142857142857, 0.2698412698412699, 0.253968253968254, 0.2380952380952381, 0.2222222222222222, 0.2063492063492064, 0.1904761904761905, 0.1746031746031746, 0.1587301587301587, 0.1428571428571429, 0.126984126984127, 0.1111111111111112, 0.09523809523809523, 0.07936507936507942, 0.06349206349206349, 0.04761904761904767, 0.03174603174603174, 0.01587301587301593, 0}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "cool". + class Cool : public ColorMap { + public: + Cool() : ColorMap() { + init(256); + } + + Cool(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 0, 0.01587301587301587, 0.03174603174603174, 0.04761904761904762, 0.06349206349206349, 0.07936507936507936, 0.09523809523809523, 0.1111111111111111, 0.126984126984127, 0.1428571428571428, 0.1587301587301587, 0.1746031746031746, 0.1904761904761905, 0.2063492063492063, 0.2222222222222222, 0.2380952380952381, 0.253968253968254, 0.2698412698412698, 0.2857142857142857, 0.3015873015873016, 0.3174603174603174, 0.3333333333333333, 0.3492063492063492, 0.3650793650793651, 0.3809523809523809, 0.3968253968253968, 0.4126984126984127, 0.4285714285714285, 0.4444444444444444, 0.4603174603174603, 0.4761904761904762, 0.492063492063492, 0.5079365079365079, 0.5238095238095238, 0.5396825396825397, 0.5555555555555556, 0.5714285714285714, 0.5873015873015873, 0.6031746031746031, 0.6190476190476191, 0.6349206349206349, 0.6507936507936508, 0.6666666666666666, 0.6825396825396826, 0.6984126984126984, 0.7142857142857143, 0.7301587301587301, 0.746031746031746, 0.7619047619047619, 0.7777777777777778, 0.7936507936507936, 0.8095238095238095, 0.8253968253968254, 0.8412698412698413, 0.8571428571428571, 0.873015873015873, 0.8888888888888888, 0.9047619047619048, 0.9206349206349206, 0.9365079365079365, 0.9523809523809523, 0.9682539682539683, 0.9841269841269841, 1}; + float g[] = { 1, 0.9841269841269842, 0.9682539682539683, 0.9523809523809523, 0.9365079365079365, 0.9206349206349207, 0.9047619047619048, 0.8888888888888888, 0.873015873015873, 0.8571428571428572, 0.8412698412698413, 0.8253968253968254, 0.8095238095238095, 0.7936507936507937, 0.7777777777777778, 0.7619047619047619, 0.746031746031746, 0.7301587301587302, 0.7142857142857143, 0.6984126984126984, 0.6825396825396826, 0.6666666666666667, 0.6507936507936508, 0.6349206349206349, 0.6190476190476191, 0.6031746031746033, 0.5873015873015873, 0.5714285714285714, 0.5555555555555556, 0.5396825396825398, 0.5238095238095238, 0.5079365079365079, 0.4920634920634921, 0.4761904761904762, 0.4603174603174603, 0.4444444444444444, 0.4285714285714286, 0.4126984126984127, 0.3968253968253969, 0.3809523809523809, 0.3650793650793651, 0.3492063492063492, 0.3333333333333334, 0.3174603174603174, 0.3015873015873016, 0.2857142857142857, 0.2698412698412699, 0.253968253968254, 0.2380952380952381, 0.2222222222222222, 0.2063492063492064, 0.1904761904761905, 0.1746031746031746, 0.1587301587301587, 0.1428571428571429, 0.126984126984127, 0.1111111111111112, 0.09523809523809523, 0.07936507936507942, 0.06349206349206349, 0.04761904761904767, 0.03174603174603174, 0.01587301587301593, 0}; + float b[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "hsv". + class HSV : public ColorMap { + public: + HSV() : ColorMap() { + init(256); + } + + HSV(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9523809523809526, 0.8571428571428568, 0.7619047619047614, 0.6666666666666665, 0.5714285714285716, 0.4761904761904763, 0.3809523809523805, 0.2857142857142856, 0.1904761904761907, 0.0952380952380949, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.09523809523809557, 0.1904761904761905, 0.2857142857142854, 0.3809523809523809, 0.4761904761904765, 0.5714285714285714, 0.6666666666666663, 0.7619047619047619, 0.8571428571428574, 0.9523809523809523, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float g[] = { 0, 0.09523809523809523, 0.1904761904761905, 0.2857142857142857, 0.3809523809523809, 0.4761904761904762, 0.5714285714285714, 0.6666666666666666, 0.7619047619047619, 0.8571428571428571, 0.9523809523809523, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9523809523809526, 0.8571428571428577, 0.7619047619047619, 0.6666666666666665, 0.5714285714285716, 0.4761904761904767, 0.3809523809523814, 0.2857142857142856, 0.1904761904761907, 0.09523809523809579, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + float b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.09523809523809523, 0.1904761904761905, 0.2857142857142857, 0.3809523809523809, 0.4761904761904762, 0.5714285714285714, 0.6666666666666666, 0.7619047619047619, 0.8571428571428571, 0.9523809523809523, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9523809523809526, 0.8571428571428577, 0.7619047619047614, 0.6666666666666665, 0.5714285714285716, 0.4761904761904767, 0.3809523809523805, 0.2857142857142856, 0.1904761904761907, 0.09523809523809579, 0}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "pink". + class Pink : public ColorMap { + public: + Pink() : ColorMap() { + init(256); + } + + Pink(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 0, 0.1571348402636772, 0.2222222222222222, 0.2721655269759087, 0.3142696805273544, 0.3513641844631533, 0.3849001794597505, 0.415739709641549, 0.4444444444444444, 0.4714045207910317, 0.4969039949999532, 0.5211573066470477, 0.5443310539518174, 0.5665577237325317, 0.5879447357921312, 0.6085806194501846, 0.6285393610547089, 0.6478835438717, 0.6666666666666666, 0.6849348892187751, 0.7027283689263065, 0.7200822998230956, 0.7370277311900888, 0.753592220347252, 0.7663560447348133, 0.7732293307186413, 0.7800420555749596, 0.7867957924694432, 0.7934920476158722, 0.8001322641986387, 0.8067178260046388, 0.8132500607904444, 0.8197302434079591, 0.8261595987094034, 0.8325393042503717, 0.8388704928078611, 0.8451542547285166, 0.8513916401208816, 0.8575836609041332, 0.8637312927246217, 0.8698354767504924, 0.8758971213537393, 0.8819171036881968, 0.8878962711712378, 0.8938354428762595, 0.8997354108424372, 0.9055969413076769, 0.9114207758701963, 0.9172076325837248, 0.9229582069908971, 0.9286731730990523, 0.9343531843023135, 0.9399988742535192, 0.9456108576893002, 0.9511897312113418, 0.9567360740266436, 0.9622504486493763, 0.9677334015667416, 0.9731854638710686, 0.9786071518602129, 0.9839989676081821, 0.9893613995077727, 0.9946949227868761, 1}; + float g[] = { 0, 0.1028688999747279, 0.1454785934906616, 0.1781741612749496, 0.2057377999494559, 0.2300218531141181, 0.2519763153394848, 0.2721655269759087, 0.2909571869813232, 0.3086066999241838, 0.3253000243161777, 0.3411775438127727, 0.3563483225498992, 0.3708990935094579, 0.3849001794597505, 0.3984095364447979, 0.4114755998989117, 0.4241393401869012, 0.4364357804719847, 0.4483951394230328, 0.4600437062282361, 0.4714045207910317, 0.4824979096371639, 0.4933419132673033, 0.5091750772173156, 0.5328701692569688, 0.5555555555555556, 0.5773502691896257, 0.5983516452371671, 0.6186404847588913, 0.6382847385042254, 0.6573421981221795, 0.6758625033664688, 0.6938886664887108, 0.7114582486036499, 0.7286042804780002, 0.7453559924999299, 0.7617394000445604, 0.7777777777777778, 0.7934920476158723, 0.8089010988089465, 0.8240220541217402, 0.8388704928078611, 0.8534606386520677, 0.8678055195451838, 0.8819171036881968, 0.8958064164776166, 0.9094836413191612, 0.9172076325837248, 0.9229582069908971, 0.9286731730990523, 0.9343531843023135, 0.9399988742535192, 0.9456108576893002, 0.9511897312113418, 0.9567360740266436, 0.9622504486493763, 0.9677334015667416, 0.9731854638710686, 0.9786071518602129, 0.9839989676081821, 0.9893613995077727, 0.9946949227868761, 1}; + float b[] = { 0, 0.1028688999747279, 0.1454785934906616, 0.1781741612749496, 0.2057377999494559, 0.2300218531141181, 0.2519763153394848, 0.2721655269759087, 0.2909571869813232, 0.3086066999241838, 0.3253000243161777, 0.3411775438127727, 0.3563483225498992, 0.3708990935094579, 0.3849001794597505, 0.3984095364447979, 0.4114755998989117, 0.4241393401869012, 0.4364357804719847, 0.4483951394230328, 0.4600437062282361, 0.4714045207910317, 0.4824979096371639, 0.4933419132673033, 0.5039526306789697, 0.5143444998736397, 0.5245305283129621, 0.5345224838248488, 0.5443310539518174, 0.5539659798925444, 0.563436169819011, 0.5727497953228163, 0.5819143739626463, 0.5909368402852788, 0.5998236072282915, 0.6085806194501846, 0.6172133998483676, 0.6257270902992705, 0.6341264874742278, 0.642416074439621, 0.6506000486323554, 0.6586823467062358, 0.6666666666666666, 0.6745564876468501, 0.6823550876255453, 0.6900655593423541, 0.6976908246297114, 0.7052336473499384, 0.7237468644557459, 0.7453559924999298, 0.7663560447348133, 0.7867957924694432, 0.8067178260046388, 0.8261595987094034, 0.8451542547285166, 0.8637312927246217, 0.8819171036881968, 0.8997354108424372, 0.9172076325837248, 0.9343531843023135, 0.9511897312113418, 0.9677334015667416, 0.9839989676081821, 1}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // Equals the GNU Octave colormap "hot". + class Hot : public ColorMap { + public: + Hot() : ColorMap() { + init(256); + } + + Hot(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = { 0, 0.03968253968253968, 0.07936507936507936, 0.119047619047619, 0.1587301587301587, 0.1984126984126984, 0.2380952380952381, 0.2777777777777778, 0.3174603174603174, 0.3571428571428571, 0.3968253968253968, 0.4365079365079365, 0.4761904761904762, 0.5158730158730158, 0.5555555555555556, 0.5952380952380952, 0.6349206349206349, 0.6746031746031745, 0.7142857142857142, 0.753968253968254, 0.7936507936507936, 0.8333333333333333, 0.873015873015873, 0.9126984126984127, 0.9523809523809523, 0.992063492063492, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float g[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.03174603174603163, 0.0714285714285714, 0.1111111111111112, 0.1507936507936507, 0.1904761904761905, 0.23015873015873, 0.2698412698412698, 0.3095238095238093, 0.3492063492063491, 0.3888888888888888, 0.4285714285714284, 0.4682539682539679, 0.5079365079365079, 0.5476190476190477, 0.5873015873015872, 0.6269841269841268, 0.6666666666666665, 0.7063492063492065, 0.746031746031746, 0.7857142857142856, 0.8253968253968254, 0.8650793650793651, 0.9047619047619047, 0.9444444444444442, 0.984126984126984, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904745, 0.1269841269841265, 0.2063492063492056, 0.2857142857142856, 0.3650793650793656, 0.4444444444444446, 0.5238095238095237, 0.6031746031746028, 0.6825396825396828, 0.7619047619047619, 0.8412698412698409, 0.92063492063492, 1}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // A perceptually improved Jet colormap (MKPJ1) by Matteo Niccoli + // + // Author's personal website: + // http://mycarta.wordpress.com/ + // + // Author's FEX page: + // http://www.mathworks.com/matlabcentral/fileexchange/authors/87376 + // + class MKPJ1 : public ColorMap { + public: + MKPJ1() : ColorMap() { + init(256); + } + + MKPJ1(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0476, 0.09710000000000001, 0.1466, 0.1961, 0.2456, 0.2952, 0.3447, 0.3942, 0.4437, 0.4932, 0.5427, 0.5921999999999999, 0.6417, 0.6912, 0.7407, 0.7903, 0.8398, 0.8893, 0.9388, 0.9883, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float g[] = {0, 0, 0, 0.007900000000000001, 0.0574, 0.1069, 0.1564, 0.2059, 0.2555, 0.305, 0.3545, 0.404, 0.4535, 0.503, 0.5525, 0.602, 0.6515, 0.701, 0.7506, 0.8001, 0.8496, 0.8991, 0.9486, 0.9981, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9622000000000001, 0.9127, 0.8632, 0.8137, 0.7642, 0.7146, 0.6651, 0.6156, 0.5661, 0.5165999999999999, 0.4671, 0.4176, 0.3681, 0.3186, 0.2691, 0.2195, 0.17, 0.1205, 0.07099999999999999, 0.0215}; + float b[] = {0.8594000000000001, 0.9089, 0.9584, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9524, 0.9029, 0.8534, 0.8038999999999999, 0.7544, 0.7048, 0.6553, 0.6058, 0.5563, 0.5068, 0.4573, 0.4078, 0.3583, 0.3088, 0.2593, 0.2097, 0.1602, 0.1107, 0.0612, 0.0117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + // A perceptually improved Jet colormap (MKPJ2) by Matteo Niccoli + // + // Author's personal website: + // http://mycarta.wordpress.com/ + // + // Author's FEX page: + // http://www.mathworks.com/matlabcentral/fileexchange/authors/87376 + // + class MKPJ2 : public ColorMap { + public: + MKPJ2() : ColorMap() { + init(256); + } + + MKPJ2(int n) : ColorMap() { + init(n); + } + + void init(int n) { + float r[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0005, 0.0298, 0.0653, 0.1055, 0.1524, 0.2094, 0.2787, 0.3565, 0.4364, 0.5154, 0.5903, 0.6582, 0.7183, 0.7714, 0.8176, 0.8579, 0.8935999999999999, 0.9254, 0.954, 0.98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float g[] = {0.0116, 0.09089999999999999, 0.1791, 0.2667, 0.3472, 0.4196, 0.4849, 0.5404, 0.5878, 0.6292, 0.6657999999999999, 0.6988, 0.729, 0.7574, 0.7846, 0.8110000000000001, 0.837, 0.8628, 0.8885, 0.9145, 0.9409999999999999, 0.9687, 0.9975000000000001, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9963, 0.9743000000000001, 0.9537, 0.9344, 0.9162, 0.8987000000000001, 0.8821, 0.8659, 0.85, 0.8343, 0.8186, 0.8025, 0.7859, 0.7683, 0.7491, 0.7276, 0.7026, 0.6728, 0.6363, 0.5915, 0.5346, 0.4602}; + float b[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9995000000000001, 0.9702, 0.9347, 0.8945, 0.8476, 0.7906, 0.7213000000000001, 0.6435, 0.5636, 0.4846, 0.4097, 0.3418, 0.2817, 0.2286, 0.1824, 0.1421, 0.1064, 0.0746, 0.046, 0.02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Mat X = linspace(0,1,64); + this->_lut = ColorMap::linear_colormap(X, + Mat(64,1, CV_32FC1, r).clone(), // red + Mat(64,1, CV_32FC1, g).clone(), // green + Mat(64,1, CV_32FC1, b).clone(), // blue + n); // number of sample points + } + }; + + void ColorMap::operator()(InputArray _src, OutputArray _dst) const + { + if(_lut.total() != 256) + CV_Error(CV_StsAssert, "cv::LUT only supports tables of size 256."); + Mat src = _src.getMat(); + // Return original matrix if wrong type is given (is fail loud better here?) + if(src.type() != CV_8UC1 && src.type() != CV_8UC3) + { + src.copyTo(_dst); + return; + } + // Turn into a BGR matrix into its grayscale representation. + if(src.type() == CV_8UC3) + cvtColor(src.clone(), src, CV_BGR2GRAY); + cvtColor(src.clone(), src, CV_GRAY2BGR); + // Apply the ColorMap. + LUT(src, _lut, _dst); + } + + Mat ColorMap::linear_colormap(InputArray X, + InputArray r, InputArray g, InputArray b, + InputArray xi) { + Mat lut, lut8; + Mat planes[] = { + interp1(X, b, xi), + interp1(X, g, xi), + interp1(X, r, xi)}; + merge(planes, 3, lut); + lut.convertTo(lut8, CV_8U, 255.); + return lut8; + } + + } + + void applyColorMap(InputArray src, OutputArray dst, int colormap) + { + colormap::ColorMap* cm = + colormap == COLORMAP_AUTUMN ? (colormap::ColorMap*)(new colormap::Autumn) : + colormap == COLORMAP_BONE ? (colormap::ColorMap*)(new colormap::Bone) : + colormap == COLORMAP_JET ? (colormap::ColorMap*)(new colormap::Jet) : + colormap == COLORMAP_WINTER ? (colormap::ColorMap*)(new colormap::Winter) : + colormap == COLORMAP_OCEAN ? (colormap::ColorMap*)(new colormap::Ocean) : + colormap == COLORMAP_SUMMER ? (colormap::ColorMap*)(new colormap::Summer) : + colormap == COLORMAP_SPRING ? (colormap::ColorMap*)(new colormap::Spring) : + colormap == COLORMAP_COOL ? (colormap::ColorMap*)(new colormap::Cool) : + colormap == COLORMAP_HSV ? (colormap::ColorMap*)(new colormap::HSV) : + colormap == COLORMAP_HOT ? (colormap::ColorMap*)(new colormap::Hot) : + colormap == COLORMAP_MKPJ1 ? (colormap::ColorMap*)(new colormap::MKPJ1) : + colormap == COLORMAP_MKPJ2 ? (colormap::ColorMap*)(new colormap::MKPJ2) : 0; + + if( !cm ) + CV_Error( CV_StsBadArg, "Unknown colormap id; use one of COLORMAP_*"); + + (*cm)(src, dst); + + delete cm; + } +} diff --git a/modules/contrib/src/facerec.cpp b/modules/contrib/src/facerec.cpp new file mode 100644 index 0000000000..26fc35abcd --- /dev/null +++ b/modules/contrib/src/facerec.cpp @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2011. Philipp Wagner . + * Released to public domain under terms of the BSD Simplified license. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * See + */ +#include "precomp.hpp" +#include + +namespace cv +{ + +using std::set; + +// Reads a sequence from a FileNode::SEQ with type _Tp into a result vector. +template +inline void readFileNodeList(const FileNode& fn, vector<_Tp>& result) { + if (fn.type() == FileNode::SEQ) { + for (FileNodeIterator it = fn.begin(); it != fn.end();) { + _Tp item; + it >> item; + result.push_back(item); + } + } +} + +// Writes the a list of given items to a cv::FileStorage. +template +inline void writeFileNodeList(FileStorage& fs, const string& name, + const vector<_Tp>& items) { + // typedefs + typedef typename vector<_Tp>::const_iterator constVecIterator; + // write the elements in item to fs + fs << name << "["; + for (constVecIterator it = items.begin(); it != items.end(); ++it) { + fs << *it; + } + fs << "]"; +} + +static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double beta=0) +{ + // number of samples + int n = (int) src.total(); + // return empty matrix if no data given + if(n == 0) + return Mat(); + // dimensionality of samples + int d = src.getMat(0).total(); + // create data matrix + Mat data(n, d, rtype); + // copy data + for(int i = 0; i < n; i++) { + Mat xi = data.row(i); + src.getMat(i).reshape(1, 1).convertTo(xi, rtype, alpha, beta); + } + return data; +} + +// Removes duplicate elements in a given vector. +template +inline vector<_Tp> remove_dups(const vector<_Tp>& src) { + typedef typename set<_Tp>::const_iterator constSetIterator; + typedef typename vector<_Tp>::const_iterator constVecIterator; + set<_Tp> set_elems; + for (constVecIterator it = src.begin(); it != src.end(); ++it) + set_elems.insert(*it); + vector<_Tp> elems; + for (constSetIterator it = set_elems.begin(); it != set_elems.end(); ++it) + elems.push_back(*it); + return elems; +} + + +// Turk, M., and Pentland, A. "Eigenfaces for recognition.". Journal of +// Cognitive Neuroscience 3 (1991), 71–86. +class Eigenfaces : public FaceRecognizer +{ +private: + int _num_components; + vector _projections; + vector _labels; + Mat _eigenvectors; + Mat _eigenvalues; + Mat _mean; + +public: + using FaceRecognizer::save; + using FaceRecognizer::load; + + // Initializes an empty Eigenfaces model. + Eigenfaces(int num_components = 0) : + _num_components(num_components) { } + + // Initializes and computes an Eigenfaces model with images in src and + // corresponding labels in labels. num_components will be kept for + // classification. + Eigenfaces(InputArray src, InputArray labels, + int num_components = 0) : + _num_components(num_components) { + train(src, labels); + } + + // Computes an Eigenfaces model with images in src and corresponding labels + // in labels. + void train(InputArray src, InputArray labels); + + // Predicts the label of a query image in src. + int predict(const InputArray src) const; + + // See FaceRecognizer::load. + void load(const FileStorage& fs); + + // See FaceRecognizer::save. + void save(FileStorage& fs) const; + + // Returns the eigenvectors of this PCA. + Mat eigenvectors() const { return _eigenvectors; } + + // Returns the eigenvalues of this PCA. + Mat eigenvalues() const { return _eigenvalues; } + + // Returns the sample mean of this PCA. + Mat mean() const { return _mean; } + + // Returns the number of components used in this PCA. + int num_components() const { return _num_components; } +}; + +// Belhumeur, P. N., Hespanha, J., and Kriegman, D. "Eigenfaces vs. Fisher- +// faces: Recognition using class specific linear projection.". IEEE +// Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997), +// 711–720. +class Fisherfaces: public FaceRecognizer +{ +private: + int _num_components; + Mat _eigenvectors; + Mat _eigenvalues; + Mat _mean; + vector _projections; + vector _labels; + +public: + using FaceRecognizer::save; + using FaceRecognizer::load; + + // Initializes an empty Fisherfaces model. + Fisherfaces(int num_components = 0) : + _num_components(num_components) {} + + // Initializes and computes a Fisherfaces model with images in src and + // corresponding labels in labels. num_components will be kept for + // classification. + Fisherfaces(InputArray src, + InputArray labels, + int num_components = 0) : + _num_components(num_components) { + train(src, labels); + } + + ~Fisherfaces() { } + + // Computes a Fisherfaces model with images in src and corresponding labels + // in labels. + void train(InputArray src, InputArray labels); + + // Predicts the label of a query image in src. + int predict(InputArray src) const; + + // See FaceRecognizer::load. + virtual void load(const FileStorage& fs); + + // See FaceRecognizer::save. + virtual void save(FileStorage& fs) const; + + // Returns the eigenvectors of this Fisherfaces model. + Mat eigenvectors() const { return _eigenvectors; } + + // Returns the eigenvalues of this Fisherfaces model. + Mat eigenvalues() const { return _eigenvalues; } + + // Returns the sample mean of this Fisherfaces model. + Mat mean() const { return _eigenvalues; } + + // Returns the number of components used in this Fisherfaces model. + int num_components() const { return _num_components; } +}; + +// Face Recognition based on Local Binary Patterns. +// +// TODO Allow to change the distance metric. +// TODO Allow to change LBP computation (Extended LBP used right now). +// TODO Optimize, Optimize, Optimize! +// +// Ahonen T, Hadid A. and Pietikäinen M. "Face description with local binary +// patterns: Application to face recognition." IEEE Transactions on Pattern +// Analysis and Machine Intelligence, 28(12):2037-2041. +// +class LBPH : public FaceRecognizer +{ +private: + int _grid_x; + int _grid_y; + int _radius; + int _neighbors; + + vector _histograms; + vector _labels; + +public: + using FaceRecognizer::save; + using FaceRecognizer::load; + + // Initializes this LBPH Model. The current implementation is rather fixed + // as it uses the Extended Local Binary Patterns per default. + // + // radius, neighbors are used in the local binary patterns creation. + // grid_x, grid_y control the grid size of the spatial histograms. + LBPH(int radius=1, int neighbors=8, int grid_x=8, int grid_y=8) : + _grid_x(grid_x), + _grid_y(grid_y), + _radius(radius), + _neighbors(neighbors) {} + + // Initializes and computes this LBPH Model. The current implementation is + // rather fixed as it uses the Extended Local Binary Patterns per default. + // + // (radius=1), (neighbors=8) are used in the local binary patterns creation. + // (grid_x=8), (grid_y=8) controls the grid size of the spatial histograms. + LBPH(InputArray src, + InputArray labels, + int radius=1, int neighbors=8, + int grid_x=8, int grid_y=8) : + _grid_x(grid_x), + _grid_y(grid_y), + _radius(radius), + _neighbors(neighbors) { + train(src, labels); + } + + ~LBPH() { } + + // Computes a LBPH model with images in src and + // corresponding labels in labels. + void train(InputArray src, InputArray labels); + + // Predicts the label of a query image in src. + int predict(InputArray src) const; + + // See FaceRecognizer::load. + void load(const FileStorage& fs); + + // See FaceRecognizer::save. + void save(FileStorage& fs) const; + + // Getter functions. + int neighbors() const { return _neighbors; } + int radius() const { return _radius; } + int grid_x() const { return _grid_x; } + int grid_y() const { return _grid_y; } + +}; + + +//------------------------------------------------------------------------------ +// FaceRecognizer +//------------------------------------------------------------------------------ +void FaceRecognizer::save(const string& filename) const { + FileStorage fs(filename, FileStorage::WRITE); + if (!fs.isOpened()) + CV_Error(CV_StsError, "File can't be opened for writing!"); + this->save(fs); + fs.release(); +} + +void FaceRecognizer::load(const string& filename) { + FileStorage fs(filename, FileStorage::READ); + if (!fs.isOpened()) + CV_Error(CV_StsError, "File can't be opened for writing!"); + this->load(fs); + fs.release(); +} + + +//------------------------------------------------------------------------------ +// Eigenfaces +//------------------------------------------------------------------------------ +void Eigenfaces::train(InputArray src, InputArray _lbls) { + // assert type + if(_lbls.getMat().type() != CV_32SC1) + CV_Error(CV_StsUnsupportedFormat, "Labels must be given as integer (CV_32SC1)."); + // get labels + vector labels = _lbls.getMat(); + // observations in row + Mat data = asRowMatrix(src, CV_64FC1); + // number of samples + int n = data.rows; + // dimensionality of data + //int d = data.cols; + // assert there are as much samples as labels + if(n != labels.size()) + CV_Error(CV_StsBadArg, "The number of samples must equal the number of labels!"); + // clip number of components to be valid + if((_num_components <= 0) || (_num_components > n)) + _num_components = n; + // perform the PCA + PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, _num_components); + // copy the PCA results + _mean = pca.mean.reshape(1,1); // store the mean vector + _eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row + transpose(pca.eigenvectors, _eigenvectors); // eigenvectors by column + _labels = labels; // store labels for prediction + // save projections + for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) { + Mat p = subspaceProject(_eigenvectors, _mean, data.row(sampleIdx)); + this->_projections.push_back(p); + } +} + +int Eigenfaces::predict(InputArray _src) const { + // get data + Mat src = _src.getMat(); + // project into PCA subspace + Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1)); + double minDist = DBL_MAX; + int minClass = -1; + for(int sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { + double dist = norm(_projections[sampleIdx], q, NORM_L2); + if(dist < minDist) { + minDist = dist; + minClass = _labels[sampleIdx]; + } + } + return minClass; +} + +void Eigenfaces::load(const FileStorage& fs) { + //read matrices + fs["num_components"] >> _num_components; + fs["mean"] >> _mean; + fs["eigenvalues"] >> _eigenvalues; + fs["eigenvectors"] >> _eigenvectors; + // read sequences + readFileNodeList(fs["projections"], _projections); + readFileNodeList(fs["labels"], _labels); +} + +void Eigenfaces::save(FileStorage& fs) const { + // write matrices + fs << "num_components" << _num_components; + fs << "mean" << _mean; + fs << "eigenvalues" << _eigenvalues; + fs << "eigenvectors" << _eigenvectors; + // write sequences + writeFileNodeList(fs, "projections", _projections); + writeFileNodeList(fs, "labels", _labels); +} + +//------------------------------------------------------------------------------ +// Fisherfaces +//------------------------------------------------------------------------------ +void Fisherfaces::train(InputArray src, InputArray _lbls) { + if(_lbls.getMat().type() != CV_32SC1) + CV_Error(CV_StsUnsupportedFormat, "Labels must be given as integer (CV_32SC1)."); + // get data + vector labels = _lbls.getMat(); + Mat data = asRowMatrix(src, CV_64FC1); + // dimensionality + int N = data.rows; // number of samples + //int D = data.cols; // dimension of samples + // assert correct data alignment + if(labels.size() != N) + CV_Error(CV_StsUnsupportedFormat, "Labels must be given as integer (CV_32SC1)."); + // compute the Fisherfaces + int C = remove_dups(labels).size(); // number of unique classes + // clip number of components to be a valid number + if((_num_components <= 0) || (_num_components > (C-1))) + _num_components = (C-1); + // perform a PCA and keep (N-C) components + PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, (N-C)); + // project the data and perform a LDA on it + LDA lda(pca.project(data),labels, _num_components); + // store the total mean vector + _mean = pca.mean.reshape(1,1); + // store labels + _labels = labels; + // store the eigenvalues of the discriminants + lda.eigenvalues().convertTo(_eigenvalues, CV_64FC1); + // Now calculate the projection matrix as pca.eigenvectors * lda.eigenvectors. + // Note: OpenCV stores the eigenvectors by row, so we need to transpose it! + gemm(pca.eigenvectors, lda.eigenvectors(), 1.0, Mat(), 0.0, _eigenvectors, CV_GEMM_A_T); + // store the projections of the original data + for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) { + Mat p = subspaceProject(_eigenvectors, _mean, data.row(sampleIdx)); + _projections.push_back(p); + } +} + +int Fisherfaces::predict(InputArray _src) const { + Mat src = _src.getMat(); + // project into LDA subspace + Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1)); + // find 1-nearest neighbor + double minDist = DBL_MAX; + int minClass = -1; + for(int sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { + double dist = norm(_projections[sampleIdx], q, NORM_L2); + if(dist < minDist) { + minDist = dist; + minClass = _labels[sampleIdx]; + } + } + return minClass; +} + + +// See FaceRecognizer::load. +void Fisherfaces::load(const FileStorage& fs) { + //read matrices + fs["num_components"] >> _num_components; + fs["mean"] >> _mean; + fs["eigenvalues"] >> _eigenvalues; + fs["eigenvectors"] >> _eigenvectors; + // read sequences + readFileNodeList(fs["projections"], _projections); + readFileNodeList(fs["labels"], _labels); +} + +// See FaceRecognizer::save. +void Fisherfaces::save(FileStorage& fs) const { + // write matrices + fs << "num_components" << _num_components; + fs << "mean" << _mean; + fs << "eigenvalues" << _eigenvalues; + fs << "eigenvectors" << _eigenvectors; + // write sequences + writeFileNodeList(fs, "projections", _projections); + writeFileNodeList(fs, "labels", _labels); +} +//------------------------------------------------------------------------------ +// LBPH +//------------------------------------------------------------------------------ + +template static +void olbp_(InputArray _src, OutputArray _dst) { + // get matrices + Mat src = _src.getMat(); + // allocate memory for result + _dst.create(src.rows-2, src.cols-2, CV_8UC1); + Mat dst = _dst.getMat(); + // zero the result matrix + dst.setTo(0); + // calculate patterns + for(int i=1;i(i,j); + unsigned char code = 0; + code |= (src.at<_Tp>(i-1,j-1) >= center) << 7; + code |= (src.at<_Tp>(i-1,j) >= center) << 6; + code |= (src.at<_Tp>(i-1,j+1) >= center) << 5; + code |= (src.at<_Tp>(i,j+1) >= center) << 4; + code |= (src.at<_Tp>(i+1,j+1) >= center) << 3; + code |= (src.at<_Tp>(i+1,j) >= center) << 2; + code |= (src.at<_Tp>(i+1,j-1) >= center) << 1; + code |= (src.at<_Tp>(i,j-1) >= center) << 0; + dst.at(i-1,j-1) = code; + } + } +} + + +//------------------------------------------------------------------------------ +// cv::elbp +//------------------------------------------------------------------------------ +template static +inline void elbp_(InputArray _src, OutputArray _dst, int radius, int neighbors) { + //get matrices + Mat src = _src.getMat(); + // allocate memory for result + _dst.create(src.rows-2*radius, src.cols-2*radius, CV_32SC1); + Mat dst = _dst.getMat(); + // zero + dst.setTo(0); + for(int n=0; n(-radius) * sin(2.0*CV_PI*n/static_cast(neighbors)); + float y = static_cast(radius) * cos(2.0*CV_PI*n/static_cast(neighbors)); + // relative indices + int fx = static_cast(floor(x)); + int fy = static_cast(floor(y)); + int cx = static_cast(ceil(x)); + int cy = static_cast(ceil(y)); + // fractional part + float ty = y - fy; + float tx = x - fx; + // set interpolation weights + float w1 = (1 - tx) * (1 - ty); + float w2 = tx * (1 - ty); + float w3 = (1 - tx) * ty; + float w4 = tx * ty; + // iterate through your data + for(int i=radius; i < src.rows-radius;i++) { + for(int j=radius;j < src.cols-radius;j++) { + // calculate interpolated value + float t = w1*src.at<_Tp>(i+fy,j+fx) + w2*src.at<_Tp>(i+fy,j+cx) + w3*src.at<_Tp>(i+cy,j+fx) + w4*src.at<_Tp>(i+cy,j+cx); + // floating point precision, so check some machine-dependent epsilon + dst.at(i-radius,j-radius) += ((t > src.at<_Tp>(i,j)) || (std::abs(t-src.at<_Tp>(i,j)) < std::numeric_limits::epsilon())) << n; + } + } + } +} + +static void elbp(InputArray src, OutputArray dst, int radius, int neighbors) +{ + switch (src.type()) { + case CV_8SC1: elbp_(src,dst, radius, neighbors); break; + case CV_8UC1: elbp_(src, dst, radius, neighbors); break; + case CV_16SC1: elbp_(src,dst, radius, neighbors); break; + case CV_16UC1: elbp_(src,dst, radius, neighbors); break; + case CV_32SC1: elbp_(src,dst, radius, neighbors); break; + case CV_32FC1: elbp_(src,dst, radius, neighbors); break; + case CV_64FC1: elbp_(src,dst, radius, neighbors); break; + default: break; + } +} + +static Mat +histc_(const Mat& src, int minVal=0, int maxVal=255, bool normed=false) +{ + Mat result; + // Establish the number of bins. + int histSize = maxVal-minVal+1; + // Set the ranges. + float range[] = { minVal, maxVal } ; + const float* histRange = { range }; + // calc histogram + calcHist(&src, 1, 0, Mat(), result, 1, &histSize, &histRange, true, false); + // normalize + if(normed) { + result /= src.total(); + } + return result.reshape(1,1); +} + +static Mat histc(InputArray _src, int minVal, int maxVal, bool normed) +{ + Mat src = _src.getMat(); + switch (src.type()) { + case CV_8SC1: + return histc_(Mat_(src), minVal, maxVal, normed); + break; + case CV_8UC1: + return histc_(src, minVal, maxVal, normed); + break; + case CV_16SC1: + return histc_(Mat_(src), minVal, maxVal, normed); + break; + case CV_16UC1: + return histc_(src, minVal, maxVal, normed); + break; + case CV_32SC1: + return histc_(Mat_(src), minVal, maxVal, normed); + break; + case CV_32FC1: + return histc_(src, minVal, maxVal, normed); + break; + default: + CV_Error(CV_StsUnmatchedFormats, "This type is not implemented yet."); break; + } + return Mat(); +} + + +static Mat spatial_histogram(InputArray _src, int numPatterns, + int grid_x, int grid_y, bool normed) +{ + Mat src = _src.getMat(); + // calculate LBP patch size + int width = static_cast(floor(src.cols/grid_x)); + int height = static_cast(floor(src.rows/grid_y)); + // allocate memory for the spatial histogram + Mat result = Mat::zeros(grid_x * grid_y, numPatterns, CV_32FC1); + // return matrix with zeros if no data was given + if(src.empty()) + return result.reshape(1,1); + // initial result_row + int resultRowIdx = 0; + // iterate through grid + for(int i = 0; i < grid_y; i++) { + for(int j = 0; j < grid_x; j++) { + Mat src_cell = Mat(src, Range(i*height,(i+1)*height), Range(j*width,(j+1)*width)); + Mat cell_hist = histc(src_cell, 0, (numPatterns-1), true); + // copy to the result matrix + Mat result_row = result.row(resultRowIdx); + cell_hist.reshape(1,1).convertTo(result_row, CV_32FC1); + // increase row count in result matrix + resultRowIdx++; + } + } + // return result as reshaped feature vector + return result.reshape(1,1); +} + +//------------------------------------------------------------------------------ +// cv::elbp, cv::olbp, cv::varlbp wrapper +//------------------------------------------------------------------------------ + +static Mat elbp(InputArray src, int radius, int neighbors) { + Mat dst; + elbp(src, dst, radius, neighbors); + return dst; +} + +void LBPH::load(const FileStorage& fs) { + fs["radius"] >> _radius; + fs["neighbors"] >> _neighbors; + fs["grid_x"] >> _grid_x; + fs["grid_y"] >> _grid_y; + //read matrices + readFileNodeList(fs["histograms"], _histograms); + readFileNodeList(fs["labels"], _labels); +} + +// See FaceRecognizer::save. +void LBPH::save(FileStorage& fs) const { + fs << "radius" << _radius; + fs << "neighbors" << _neighbors; + fs << "grid_x" << _grid_x; + fs << "grid_y" << _grid_y; + // write matrices + writeFileNodeList(fs, "histograms", _histograms); + writeFileNodeList(fs, "labels", _labels); +} + +void LBPH::train(InputArray _src, InputArray _lbls) { + if(_src.kind() != _InputArray::STD_VECTOR_MAT && _src.kind() != _InputArray::STD_VECTOR_VECTOR) + CV_Error(CV_StsUnsupportedFormat, "LBPH::train expects InputArray::STD_VECTOR_MAT or _InputArray::STD_VECTOR_VECTOR."); + // get the vector of matrices + vector src; + _src.getMatVector(src); + // turn the label matrix into a vector + vector labels = _lbls.getMat(); + if(labels.size() != src.size()) + CV_Error(CV_StsUnsupportedFormat, "The number of labels must equal the number of samples."); + // store given labels + _labels = labels; + // store the spatial histograms of the original data + for(int sampleIdx = 0; sampleIdx < src.size(); sampleIdx++) { + // calculate lbp image + Mat lbp_image = elbp(src[sampleIdx], _radius, _neighbors); + // get spatial histogram from this lbp image + Mat p = spatial_histogram( + lbp_image, /* lbp_image */ + static_cast(std::pow(2.0, static_cast(_neighbors))), /* number of possible patterns */ + _grid_x, /* grid size x */ + _grid_y, /* grid size y */ + true); + // add to templates + _histograms.push_back(p); + } +} + + +int LBPH::predict(InputArray _src) const { + Mat src = _src.getMat(); + // get the spatial histogram from input image + Mat lbp_image = elbp(src, _radius, _neighbors); + Mat query = spatial_histogram( + lbp_image, /* lbp_image */ + static_cast(std::pow(2.0, static_cast(_neighbors))), /* number of possible patterns */ + _grid_x, /* grid size x */ + _grid_y, /* grid size y */ + true /* normed histograms */); + // find 1-nearest neighbor + double minDist = DBL_MAX; + int minClass = -1; + for(int sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) { + double dist = compareHist(_histograms[sampleIdx], query, CV_COMP_CHISQR); + if(dist < minDist) { + minDist = dist; + minClass = _labels[sampleIdx]; + } + } + return minClass; +} + + +Ptr createEigenFaceRecognizer(int num_components) +{ + return new Eigenfaces(num_components); +} + +Ptr createFisherFaceRecognizer(int num_components) +{ + return new Fisherfaces(num_components); +} + +Ptr createLBPHFaceRecognizer(int radius, int neighbors, + int grid_x, int grid_y) +{ + return new LBPH(radius, neighbors, grid_x, grid_y); +} + +} diff --git a/modules/contrib/src/lda.cpp b/modules/contrib/src/lda.cpp new file mode 100644 index 0000000000..591fa755b7 --- /dev/null +++ b/modules/contrib/src/lda.cpp @@ -0,0 +1,1055 @@ +/* + * Copyright (c) 2011. Philipp Wagner . + * Released to public domain under terms of the BSD Simplified license. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * See + */ + +#include "precomp.hpp" +#include +#include +#include + +namespace cv +{ + +using std::map; +using std::set; +using std::cout; +using std::endl; + +// Removes duplicate elements in a given vector. +template +inline vector<_Tp> remove_dups(const vector<_Tp>& src) { + typedef typename set<_Tp>::const_iterator constSetIterator; + typedef typename vector<_Tp>::const_iterator constVecIterator; + set<_Tp> set_elems; + for (constVecIterator it = src.begin(); it != src.end(); ++it) + set_elems.insert(*it); + vector<_Tp> elems; + for (constSetIterator it = set_elems.begin(); it != set_elems.end(); ++it) + elems.push_back(*it); + return elems; +} + +static Mat argsort(InputArray _src, bool ascending=true) +{ + Mat src = _src.getMat(); + if (src.rows != 1 && src.cols != 1) + CV_Error(CV_StsBadArg, "cv::argsort only sorts 1D matrices."); + int flags = CV_SORT_EVERY_ROW+(ascending ? CV_SORT_ASCENDING : CV_SORT_DESCENDING); + Mat sorted_indices; + sortIdx(src.reshape(1,1),sorted_indices,flags); + return sorted_indices; +} + +static Mat asRowMatrix(InputArrayOfArrays src, int rtype, double alpha=1, double beta=0) +{ + // number of samples + int n = (int) src.total(); + // return empty matrix if no data given + if(n == 0) + return Mat(); + // dimensionality of samples + int d = src.getMat(0).total(); + // create data matrix + Mat data(n, d, rtype); + // copy data + for(int i = 0; i < n; i++) { + Mat xi = data.row(i); + src.getMat(i).reshape(1, 1).convertTo(xi, rtype, alpha, beta); + } + return data; +} + +void sortMatrixColumnsByIndices(InputArray _src, InputArray _indices, OutputArray _dst) { + if(_indices.getMat().type() != CV_32SC1) + CV_Error(CV_StsUnsupportedFormat, "cv::sortColumnsByIndices only works on integer indices!"); + Mat src = _src.getMat(); + vector indices = _indices.getMat(); + _dst.create(src.rows, src.cols, src.type()); + Mat dst = _dst.getMat(); + for(int idx = 0; idx < indices.size(); idx++) { + Mat originalCol = src.col(indices[idx]); + Mat sortedCol = dst.col(idx); + originalCol.copyTo(sortedCol); + } +} + +Mat sortMatrixColumnsByIndices(InputArray src, InputArray indices) { + Mat dst; + sortMatrixColumnsByIndices(src, indices, dst); + return dst; +} + + +template static bool +isSymmetric_(InputArray src) { + Mat _src = src.getMat(); + if(_src.cols != _src.rows) + return false; + for (int i = 0; i < _src.rows; i++) { + for (int j = 0; j < _src.cols; j++) { + _Tp a = _src.at<_Tp> (i, j); + _Tp b = _src.at<_Tp> (j, i); + if (a != b) { + return false; + } + } + } + return true; +} + +template static bool +isSymmetric_(InputArray src, double eps) { + Mat _src = src.getMat(); + if(_src.cols != _src.rows) + return false; + for (int i = 0; i < _src.rows; i++) { + for (int j = 0; j < _src.cols; j++) { + _Tp a = _src.at<_Tp> (i, j); + _Tp b = _src.at<_Tp> (j, i); + if (std::abs(a - b) > eps) { + return false; + } + } + } + return true; +} + +static bool isSymmetric(InputArray src, double eps=1e-16) +{ + Mat m = src.getMat(); + switch (m.type()) { + case CV_8SC1: return isSymmetric_(m); break; + case CV_8UC1: + return isSymmetric_(m); break; + case CV_16SC1: + return isSymmetric_(m); break; + case CV_16UC1: + return isSymmetric_(m); break; + case CV_32SC1: + return isSymmetric_(m); break; + case CV_32FC1: + return isSymmetric_(m, eps); break; + case CV_64FC1: + return isSymmetric_(m, eps); break; + default: + break; + } + return false; +} + + +//------------------------------------------------------------------------------ +// subspace::project +//------------------------------------------------------------------------------ +Mat subspaceProject(InputArray _W, InputArray _mean, InputArray _src) +{ + // get data matrices + Mat W = _W.getMat(); + Mat mean = _mean.getMat(); + Mat src = _src.getMat(); + // create temporary matrices + Mat X, Y; + // copy data & make sure we are using the correct type + src.convertTo(X, W.type()); + // get number of samples and dimension + int n = X.rows; + int d = X.cols; + // center the data if correct aligned sample mean is given + if(mean.total() == d) + subtract(X, repeat(mean.reshape(1,1), n, 1), X); + // finally calculate projection as Y = (X-mean)*W + gemm(X, W, 1.0, Mat(), 0.0, Y); + return Y; +} + +//------------------------------------------------------------------------------ +// subspace::reconstruct +//------------------------------------------------------------------------------ +Mat subspaceReconstruct(InputArray _W, InputArray _mean, InputArray _src) +{ + // get data matrices + Mat W = _W.getMat(); + Mat mean = _mean.getMat(); + Mat src = _src.getMat(); + // get number of samples and dimension + int n = src.rows; + int d = src.cols; + // initalize temporary matrices + Mat X, Y; + // copy data & make sure we are using the correct type + src.convertTo(Y, W.type()); + // calculate the reconstruction + gemm(Y, + W, + 1.0, + (d == mean.total()) ? repeat(mean.reshape(1,1), n, 1) : Mat(), + (d == mean.total()) ? 1.0 : 0.0, + X, + GEMM_2_T); + return X; +} + + +class EigenvalueDecomposition { +private: + + // Holds the data dimension. + int n; + + // Stores real/imag part of a complex division. + double cdivr, cdivi; + + // Pointer to internal memory. + double *d, *e, *ort; + double **V, **H; + + // Holds the computed eigenvalues. + Mat _eigenvalues; + + // Holds the computed eigenvectors. + Mat _eigenvectors; + + // Allocates memory. + template + _Tp *alloc_1d(int m) { + return new _Tp[m]; + } + + // Allocates memory. + template + _Tp *alloc_1d(int m, _Tp val) { + _Tp *arr = alloc_1d<_Tp> (m); + for (int i = 0; i < m; i++) + arr[i] = val; + return arr; + } + + // Allocates memory. + template + _Tp **alloc_2d(int m, int n) { + _Tp **arr = new _Tp*[m]; + for (int i = 0; i < m; i++) + arr[i] = new _Tp[n]; + return arr; + } + + // Allocates memory. + template + _Tp **alloc_2d(int m, int n, _Tp val) { + _Tp **arr = alloc_2d<_Tp> (m, n); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + arr[i][j] = val; + } + } + return arr; + } + + void cdiv(double xr, double xi, double yr, double yi) { + double r, d; + if (std::abs(yr) > std::abs(yi)) { + r = yi / yr; + d = yr + r * yi; + cdivr = (xr + r * xi) / d; + cdivi = (xi - r * xr) / d; + } else { + r = yr / yi; + d = yi + r * yr; + cdivr = (r * xr + xi) / d; + cdivi = (r * xi - xr) / d; + } + } + + // Nonsymmetric reduction from Hessenberg to real Schur form. + + void hqr2() { + + // This is derived from the Algol procedure hqr2, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + // Initialize + int nn = this->n; + int n = nn - 1; + int low = 0; + int high = nn - 1; + double eps = pow(2.0, -52.0); + double exshift = 0.0; + double p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y; + + // Store roots isolated by balanc and compute matrix norm + + double norm = 0.0; + for (int i = 0; i < nn; i++) { + if (i < low | i > high) { + d[i] = H[i][i]; + e[i] = 0.0; + } + for (int j = max(i - 1, 0); j < nn; j++) { + norm = norm + std::abs(H[i][j]); + } + } + + // Outer loop over eigenvalue index + int iter = 0; + while (n >= low) { + + // Look for single small sub-diagonal element + int l = n; + while (l > low) { + s = std::abs(H[l - 1][l - 1]) + std::abs(H[l][l]); + if (s == 0.0) { + s = norm; + } + if (std::abs(H[l][l - 1]) < eps * s) { + break; + } + l--; + } + + // Check for convergence + // One root found + + if (l == n) { + H[n][n] = H[n][n] + exshift; + d[n] = H[n][n]; + e[n] = 0.0; + n--; + iter = 0; + + // Two roots found + + } else if (l == n - 1) { + w = H[n][n - 1] * H[n - 1][n]; + p = (H[n - 1][n - 1] - H[n][n]) / 2.0; + q = p * p + w; + z = sqrt(std::abs(q)); + H[n][n] = H[n][n] + exshift; + H[n - 1][n - 1] = H[n - 1][n - 1] + exshift; + x = H[n][n]; + + // Real pair + + if (q >= 0) { + if (p >= 0) { + z = p + z; + } else { + z = p - z; + } + d[n - 1] = x + z; + d[n] = d[n - 1]; + if (z != 0.0) { + d[n] = x - w / z; + } + e[n - 1] = 0.0; + e[n] = 0.0; + x = H[n][n - 1]; + s = std::abs(x) + std::abs(z); + p = x / s; + q = z / s; + r = sqrt(p * p + q * q); + p = p / r; + q = q / r; + + // Row modification + + for (int j = n - 1; j < nn; j++) { + z = H[n - 1][j]; + H[n - 1][j] = q * z + p * H[n][j]; + H[n][j] = q * H[n][j] - p * z; + } + + // Column modification + + for (int i = 0; i <= n; i++) { + z = H[i][n - 1]; + H[i][n - 1] = q * z + p * H[i][n]; + H[i][n] = q * H[i][n] - p * z; + } + + // Accumulate transformations + + for (int i = low; i <= high; i++) { + z = V[i][n - 1]; + V[i][n - 1] = q * z + p * V[i][n]; + V[i][n] = q * V[i][n] - p * z; + } + + // Complex pair + + } else { + d[n - 1] = x + p; + d[n] = x + p; + e[n - 1] = z; + e[n] = -z; + } + n = n - 2; + iter = 0; + + // No convergence yet + + } else { + + // Form shift + + x = H[n][n]; + y = 0.0; + w = 0.0; + if (l < n) { + y = H[n - 1][n - 1]; + w = H[n][n - 1] * H[n - 1][n]; + } + + // Wilkinson's original ad hoc shift + + if (iter == 10) { + exshift += x; + for (int i = low; i <= n; i++) { + H[i][i] -= x; + } + s = std::abs(H[n][n - 1]) + std::abs(H[n - 1][n - 2]); + x = y = 0.75 * s; + w = -0.4375 * s * s; + } + + // MATLAB's new ad hoc shift + + if (iter == 30) { + s = (y - x) / 2.0; + s = s * s + w; + if (s > 0) { + s = sqrt(s); + if (y < x) { + s = -s; + } + s = x - w / ((y - x) / 2.0 + s); + for (int i = low; i <= n; i++) { + H[i][i] -= s; + } + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; // (Could check iteration count here.) + + // Look for two consecutive small sub-diagonal elements + int m = n - 2; + while (m >= l) { + z = H[m][m]; + r = x - z; + s = y - z; + p = (r * s - w) / H[m + 1][m] + H[m][m + 1]; + q = H[m + 1][m + 1] - z - r - s; + r = H[m + 2][m + 1]; + s = std::abs(p) + std::abs(q) + std::abs(r); + p = p / s; + q = q / s; + r = r / s; + if (m == l) { + break; + } + if (std::abs(H[m][m - 1]) * (std::abs(q) + std::abs(r)) < eps * (std::abs(p) + * (std::abs(H[m - 1][m - 1]) + std::abs(z) + std::abs( + H[m + 1][m + 1])))) { + break; + } + m--; + } + + for (int i = m + 2; i <= n; i++) { + H[i][i - 2] = 0.0; + if (i > m + 2) { + H[i][i - 3] = 0.0; + } + } + + // Double QR step involving rows l:n and columns m:n + + for (int k = m; k <= n - 1; k++) { + bool notlast = (k != n - 1); + if (k != m) { + p = H[k][k - 1]; + q = H[k + 1][k - 1]; + r = (notlast ? H[k + 2][k - 1] : 0.0); + x = std::abs(p) + std::abs(q) + std::abs(r); + if (x != 0.0) { + p = p / x; + q = q / x; + r = r / x; + } + } + if (x == 0.0) { + break; + } + s = sqrt(p * p + q * q + r * r); + if (p < 0) { + s = -s; + } + if (s != 0) { + if (k != m) { + H[k][k - 1] = -s * x; + } else if (l != m) { + H[k][k - 1] = -H[k][k - 1]; + } + p = p + s; + x = p / s; + y = q / s; + z = r / s; + q = q / p; + r = r / p; + + // Row modification + + for (int j = k; j < nn; j++) { + p = H[k][j] + q * H[k + 1][j]; + if (notlast) { + p = p + r * H[k + 2][j]; + H[k + 2][j] = H[k + 2][j] - p * z; + } + H[k][j] = H[k][j] - p * x; + H[k + 1][j] = H[k + 1][j] - p * y; + } + + // Column modification + + for (int i = 0; i <= min(n, k + 3); i++) { + p = x * H[i][k] + y * H[i][k + 1]; + if (notlast) { + p = p + z * H[i][k + 2]; + H[i][k + 2] = H[i][k + 2] - p * r; + } + H[i][k] = H[i][k] - p; + H[i][k + 1] = H[i][k + 1] - p * q; + } + + // Accumulate transformations + + for (int i = low; i <= high; i++) { + p = x * V[i][k] + y * V[i][k + 1]; + if (notlast) { + p = p + z * V[i][k + 2]; + V[i][k + 2] = V[i][k + 2] - p * r; + } + V[i][k] = V[i][k] - p; + V[i][k + 1] = V[i][k + 1] - p * q; + } + } // (s != 0) + } // k loop + } // check convergence + } // while (n >= low) + + // Backsubstitute to find vectors of upper triangular form + + if (norm == 0.0) { + return; + } + + for (n = nn - 1; n >= 0; n--) { + p = d[n]; + q = e[n]; + + // Real vector + + if (q == 0) { + int l = n; + H[n][n] = 1.0; + for (int i = n - 1; i >= 0; i--) { + w = H[i][i] - p; + r = 0.0; + for (int j = l; j <= n; j++) { + r = r + H[i][j] * H[j][n]; + } + if (e[i] < 0.0) { + z = w; + s = r; + } else { + l = i; + if (e[i] == 0.0) { + if (w != 0.0) { + H[i][n] = -r / w; + } else { + H[i][n] = -r / (eps * norm); + } + + // Solve real equations + + } else { + x = H[i][i + 1]; + y = H[i + 1][i]; + q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; + t = (x * s - z * r) / q; + H[i][n] = t; + if (std::abs(x) > std::abs(z)) { + H[i + 1][n] = (-r - w * t) / x; + } else { + H[i + 1][n] = (-s - y * t) / z; + } + } + + // Overflow control + + t = std::abs(H[i][n]); + if ((eps * t) * t > 1) { + for (int j = i; j <= n; j++) { + H[j][n] = H[j][n] / t; + } + } + } + } + + // Complex vector + + } else if (q < 0) { + int l = n - 1; + + // Last vector component imaginary so matrix is triangular + + if (std::abs(H[n][n - 1]) > std::abs(H[n - 1][n])) { + H[n - 1][n - 1] = q / H[n][n - 1]; + H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1]; + } else { + cdiv(0.0, -H[n - 1][n], H[n - 1][n - 1] - p, q); + H[n - 1][n - 1] = cdivr; + H[n - 1][n] = cdivi; + } + H[n][n - 1] = 0.0; + H[n][n] = 1.0; + for (int i = n - 2; i >= 0; i--) { + double ra, sa, vr, vi; + ra = 0.0; + sa = 0.0; + for (int j = l; j <= n; j++) { + ra = ra + H[i][j] * H[j][n - 1]; + sa = sa + H[i][j] * H[j][n]; + } + w = H[i][i] - p; + + if (e[i] < 0.0) { + z = w; + r = ra; + s = sa; + } else { + l = i; + if (e[i] == 0) { + cdiv(-ra, -sa, w, q); + H[i][n - 1] = cdivr; + H[i][n] = cdivi; + } else { + + // Solve complex equations + + x = H[i][i + 1]; + y = H[i + 1][i]; + vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; + vi = (d[i] - p) * 2.0 * q; + if (vr == 0.0 & vi == 0.0) { + vr = eps * norm * (std::abs(w) + std::abs(q) + std::abs(x) + + std::abs(y) + std::abs(z)); + } + cdiv(x * r - z * ra + q * sa, + x * s - z * sa - q * ra, vr, vi); + H[i][n - 1] = cdivr; + H[i][n] = cdivi; + if (std::abs(x) > (std::abs(z) + std::abs(q))) { + H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q + * H[i][n]) / x; + H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n + - 1]) / x; + } else { + cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, + q); + H[i + 1][n - 1] = cdivr; + H[i + 1][n] = cdivi; + } + } + + // Overflow control + + t = max(std::abs(H[i][n - 1]), std::abs(H[i][n])); + if ((eps * t) * t > 1) { + for (int j = i; j <= n; j++) { + H[j][n - 1] = H[j][n - 1] / t; + H[j][n] = H[j][n] / t; + } + } + } + } + } + } + + // Vectors of isolated roots + + for (int i = 0; i < nn; i++) { + if (i < low | i > high) { + for (int j = i; j < nn; j++) { + V[i][j] = H[i][j]; + } + } + } + + // Back transformation to get eigenvectors of original matrix + + for (int j = nn - 1; j >= low; j--) { + for (int i = low; i <= high; i++) { + z = 0.0; + for (int k = low; k <= min(j, high); k++) { + z = z + V[i][k] * H[k][j]; + } + V[i][j] = z; + } + } + } + + // Nonsymmetric reduction to Hessenberg form. + void orthes() { + // This is derived from the Algol procedures orthes and ortran, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutines in EISPACK. + int low = 0; + int high = n - 1; + + for (int m = low + 1; m <= high - 1; m++) { + + // Scale column. + + double scale = 0.0; + for (int i = m; i <= high; i++) { + scale = scale + std::abs(H[i][m - 1]); + } + if (scale != 0.0) { + + // Compute Householder transformation. + + double h = 0.0; + for (int i = high; i >= m; i--) { + ort[i] = H[i][m - 1] / scale; + h += ort[i] * ort[i]; + } + double g = sqrt(h); + if (ort[m] > 0) { + g = -g; + } + h = h - ort[m] * g; + ort[m] = ort[m] - g; + + // Apply Householder similarity transformation + // H = (I-u*u'/h)*H*(I-u*u')/h) + + for (int j = m; j < n; j++) { + double f = 0.0; + for (int i = high; i >= m; i--) { + f += ort[i] * H[i][j]; + } + f = f / h; + for (int i = m; i <= high; i++) { + H[i][j] -= f * ort[i]; + } + } + + for (int i = 0; i <= high; i++) { + double f = 0.0; + for (int j = high; j >= m; j--) { + f += ort[j] * H[i][j]; + } + f = f / h; + for (int j = m; j <= high; j++) { + H[i][j] -= f * ort[j]; + } + } + ort[m] = scale * ort[m]; + H[m][m - 1] = scale * g; + } + } + + // Accumulate transformations (Algol's ortran). + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + V[i][j] = (i == j ? 1.0 : 0.0); + } + } + + for (int m = high - 1; m >= low + 1; m--) { + if (H[m][m - 1] != 0.0) { + for (int i = m + 1; i <= high; i++) { + ort[i] = H[i][m - 1]; + } + for (int j = m; j <= high; j++) { + double g = 0.0; + for (int i = m; i <= high; i++) { + g += ort[i] * V[i][j]; + } + // Double division avoids possible underflow + g = (g / ort[m]) / H[m][m - 1]; + for (int i = m; i <= high; i++) { + V[i][j] += g * ort[i]; + } + } + } + } + } + + // Releases all internal working memory. + void release() { + // releases the working data + delete[] d; + delete[] e; + delete[] ort; + for (int i = 0; i < n; i++) { + delete[] H[i]; + delete[] V[i]; + } + delete[] H; + delete[] V; + } + + // Computes the Eigenvalue Decomposition for a matrix given in H. + void compute() { + // Allocate memory for the working data. + V = alloc_2d (n, n, 0.0); + d = alloc_1d (n); + e = alloc_1d (n); + ort = alloc_1d (n); + // Reduce to Hessenberg form. + orthes(); + // Reduce Hessenberg to real Schur form. + hqr2(); + // Copy eigenvalues to OpenCV Matrix. + _eigenvalues.create(1, n, CV_64FC1); + for (int i = 0; i < n; i++) { + _eigenvalues.at (0, i) = d[i]; + } + // Copy eigenvectors to OpenCV Matrix. + _eigenvectors.create(n, n, CV_64FC1); + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + _eigenvectors.at (i, j) = V[i][j]; + // Deallocate the memory by releasing all internal working data. + release(); + } + +public: + EigenvalueDecomposition() + : n(0) { } + + // Initializes & computes the Eigenvalue Decomposition for a general matrix + // given in src. This function is a port of the EigenvalueSolver in JAMA, + // which has been released to public domain by The MathWorks and the + // National Institute of Standards and Technology (NIST). + EigenvalueDecomposition(InputArray src) { + compute(src); + } + + // This function computes the Eigenvalue Decomposition for a general matrix + // given in src. This function is a port of the EigenvalueSolver in JAMA, + // which has been released to public domain by The MathWorks and the + // National Institute of Standards and Technology (NIST). + void compute(InputArray src) + { + if(isSymmetric(src)) { + // Fall back to OpenCV for a symmetric matrix! + cv::eigen(src, _eigenvalues, _eigenvectors); + } else { + Mat tmp; + // Convert the given input matrix to double. Is there any way to + // prevent allocating the temporary memory? Only used for copying + // into working memory and deallocated after. + src.getMat().convertTo(tmp, CV_64FC1); + // Get dimension of the matrix. + this->n = tmp.cols; + // Allocate the matrix data to work on. + this->H = alloc_2d (n, n); + // Now safely copy the data. + for (int i = 0; i < tmp.rows; i++) { + for (int j = 0; j < tmp.cols; j++) { + this->H[i][j] = tmp.at(i, j); + } + } + // Deallocates the temporary matrix before computing. + tmp.release(); + // Performs the eigenvalue decomposition of H. + compute(); + } + } + + ~EigenvalueDecomposition() {} + + // Returns the eigenvalues of the Eigenvalue Decomposition. + Mat eigenvalues() { return _eigenvalues; } + // Returns the eigenvectors of the Eigenvalue Decomposition. + Mat eigenvectors() { return _eigenvectors; } +}; + + +//------------------------------------------------------------------------------ +// Linear Discriminant Analysis implementation +//------------------------------------------------------------------------------ +void LDA::save(const string& filename) const { + FileStorage fs(filename, FileStorage::WRITE); + if (!fs.isOpened()) + CV_Error(CV_StsError, "File can't be opened for writing!"); + this->save(fs); + fs.release(); +} + +// Deserializes this object from a given filename. +void LDA::load(const string& filename) { + FileStorage fs(filename, FileStorage::READ); + if (!fs.isOpened()) + CV_Error(CV_StsError, "File can't be opened for writing!"); + this->load(fs); + fs.release(); +} + +// Serializes this object to a given FileStorage. +void LDA::save(FileStorage& fs) const { + // write matrices + fs << "num_components" << _num_components; + fs << "eigenvalues" << _eigenvalues; + fs << "eigenvectors" << _eigenvectors; +} + +// Deserializes this object from a given FileStorage. +void LDA::load(const FileStorage& fs) { + //read matrices + fs["num_components"] >> _num_components; + fs["eigenvalues"] >> _eigenvalues; + fs["eigenvectors"] >> _eigenvectors; +} + +void LDA::lda(InputArray _src, InputArray _lbls) { + // get data + Mat src = _src.getMat(); + vector labels = _lbls.getMat(); + // turn into row sampled matrix + Mat data; + // ensure working matrix is double precision + src.convertTo(data, CV_64FC1); + // maps the labels, so they're ascending: [0,1,...,C] + vector mapped_labels(labels.size()); + vector num2label = remove_dups(labels); + map label2num; + for (int i = 0; i < num2label.size(); i++) + label2num[num2label[i]] = i; + for (int i = 0; i < labels.size(); i++) + mapped_labels[i] = label2num[labels[i]]; + // get sample size, dimension + int N = data.rows; + int D = data.cols; + // number of unique labels + int C = num2label.size(); + // throw error if less labels, than samples + if (labels.size() != N) + CV_Error(CV_StsBadArg, "Error: The number of samples must equal the number of labels."); + // warn if within-classes scatter matrix becomes singular + if (N < D) + cout << "Warning: Less observations than feature dimension given!" + << "Computation will probably fail." + << endl; + // clip number of components to be a valid number + if ((_num_components <= 0) || (_num_components > (C - 1))) + _num_components = (C - 1); + // holds the mean over all classes + Mat meanTotal = Mat::zeros(1, D, data.type()); + // holds the mean for each class + vector meanClass(C); + vector numClass(C); + // initialize + for (int i = 0; i < C; i++) { + numClass[i] = 0; + meanClass[i] = Mat::zeros(1, D, data.type()); //! Dx1 image vector + } + // calculate sums + for (int i = 0; i < N; i++) { + Mat instance = data.row(i); + int classIdx = mapped_labels[i]; + add(meanTotal, instance, meanTotal); + add(meanClass[classIdx], instance, meanClass[classIdx]); + numClass[classIdx]++; + } + // calculate means + meanTotal.convertTo(meanTotal, meanTotal.type(), + 1.0 / static_cast (N)); + for (int i = 0; i < C; i++) + meanClass[i].convertTo(meanClass[i], meanClass[i].type(), + 1.0 / static_cast (numClass[i])); + // subtract class means + for (int i = 0; i < N; i++) { + int classIdx = mapped_labels[i]; + Mat instance = data.row(i); + subtract(instance, meanClass[classIdx], instance); + } + // calculate within-classes scatter + Mat Sw = Mat::zeros(D, D, data.type()); + mulTransposed(data, Sw, true); + // calculate between-classes scatter + Mat Sb = Mat::zeros(D, D, data.type()); + for (int i = 0; i < C; i++) { + Mat tmp; + subtract(meanClass[i], meanTotal, tmp); + mulTransposed(tmp, tmp, true); + add(Sb, tmp, Sb); + } + // invert Sw + Mat Swi = Sw.inv(); + // M = inv(Sw)*Sb + Mat M; + gemm(Swi, Sb, 1.0, Mat(), 0.0, M); + EigenvalueDecomposition es(M); + _eigenvalues = es.eigenvalues(); + _eigenvectors = es.eigenvectors(); + // reshape eigenvalues, so they are stored by column + _eigenvalues = _eigenvalues.reshape(1, 1); + // get sorted indices descending by their eigenvalue + vector sorted_indices = argsort(_eigenvalues, false); + // now sort eigenvalues and eigenvectors accordingly + _eigenvalues = sortMatrixColumnsByIndices(_eigenvalues, sorted_indices); + _eigenvectors = sortMatrixColumnsByIndices(_eigenvectors, sorted_indices); + // and now take only the num_components and we're out! + _eigenvalues = Mat(_eigenvalues, Range::all(), Range(0, _num_components)); + _eigenvectors = Mat(_eigenvectors, Range::all(), Range(0, _num_components)); +} + +void LDA::compute(InputArray _src, InputArray _lbls) { + switch(_src.kind()) { + case _InputArray::STD_VECTOR_MAT: + lda(asRowMatrix(_src, CV_64FC1), _lbls); + break; + case _InputArray::MAT: + lda(_src.getMat(), _lbls); + break; + default: + CV_Error(CV_StsNotImplemented, "This data type is not supported by subspace::LDA::compute."); + break; + } +} + +// Projects samples into the LDA subspace. +Mat LDA::project(InputArray src) { + return subspaceProject(_eigenvectors, Mat(), _dataAsRow ? src : src.getMat().t()); +} + +// Reconstructs projections from the LDA subspace. +Mat LDA::reconstruct(InputArray src) { + return subspaceReconstruct(_eigenvectors, Mat(), _dataAsRow ? src : src.getMat().t()); +} + +} + diff --git a/samples/cpp/facerec_demo.cpp b/samples/cpp/facerec_demo.cpp new file mode 100644 index 0000000000..399a5b8e2e --- /dev/null +++ b/samples/cpp/facerec_demo.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011. Philipp Wagner . + * Released to public domain under terms of the BSD Simplified license. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * See + */ + +#include "opencv2/opencv.hpp" + +#include +#include +#include + +using namespace cv; +using namespace std; + +void read_csv(const string& filename, vector& images, vector& labels, char separator = ';') { + std::ifstream file(filename.c_str(), ifstream::in); + if (!file) + throw std::exception(); + string line, path, classlabel; + while (getline(file, line)) { + stringstream liness(line); + getline(liness, path, separator); + getline(liness, classlabel); + images.push_back(imread(path, 0)); + labels.push_back(atoi(classlabel.c_str())); + } +} + +int main(int argc, const char *argv[]) { + // check for command line arguments + if (argc != 2) { + cout << "usage: " << argv[0] << " " << endl; + exit(1); + } + // path to your CSV + string fn_csv = string(argv[1]); + // images and corresponding labels + vector images; + vector labels; + // read in the data + try { + read_csv(fn_csv, images, labels); + } catch (exception& e) { + cerr << "Error opening file \"" << fn_csv << "\"." << endl; + exit(1); + } + // get width and height + //int width = images[0].cols; + int height = images[0].rows; + // get test instances + Mat testSample = images[images.size() - 1]; + int testLabel = labels[labels.size() - 1]; + // ... and delete last element + images.pop_back(); + labels.pop_back(); + // build the Fisherfaces model + Ptr model = createFisherFaceRecognizer(); + model->train(images, labels); + // test model + int predicted = model->predict(testSample); + cout << "predicted class = " << predicted << endl; + cout << "actual class = " << testLabel << endl; + // get the eigenvectors + Mat W = model->eigenvectors(); + // show first 10 fisherfaces + for (int i = 0; i < min(10, W.cols); i++) { + // get eigenvector #i + Mat ev = W.col(i).clone(); + // reshape to original site + Mat grayscale, cgrayscale; + cvtColor(ev.reshape(1, height), grayscale, COLOR_BGR2GRAY); + // show image (with Jet colormap) + applyColorMap(grayscale, cgrayscale, COLORMAP_JET); + imshow(format("%d", i), cgrayscale); + } + waitKey(0); + return 0; +}