#include <stdio.h>
#include <opencv2/opencv.hpp>

/***************** Mat转vector **********************/
template<typename _Tp>
std::vector<_Tp> convertMat2Vector(const cv::Mat &mat)
{
	return (std::vector<_Tp>)(mat.reshape(1, 1));//通道数不变，按行转为一行
}
 
/****************** vector转Mat *********************/
template<typename _Tp>
cv::Mat convertVector2Mat(std::vector<_Tp> v, int channels, int rows)
{
	cv::Mat mat = cv::Mat(v);//将vector变成单列的mat
	cv::Mat dest = mat.reshape(channels, rows).clone();//PS：必须clone()一份，否则返回出错
	return dest;
}

// 锐化矩阵
//              j-1                     -1
//  j-nChannels  j  j+nChannels     -1   5  -1
//              j+1                     -1
void Sharpen(const cv::Mat& myImage,cv::Mat& Result)
{
    CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images
    const int nChannels = myImage.channels();
    Result.create(myImage.size(),myImage.type());
    for(int j = 1 ; j < myImage.rows-1; ++j)
    {
        const uchar* previous = myImage.ptr<uchar>(j - 1);
        const uchar* current  = myImage.ptr<uchar>(j    );
        const uchar* next     = myImage.ptr<uchar>(j + 1);
        uchar* output = Result.ptr<uchar>(j);
        for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
        {
            *output++ = cv::saturate_cast<uchar>(5*current[i]
                         -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
        }
    }
    Result.row(0).setTo(cv::Scalar(0));
    Result.row(Result.rows-1).setTo(cv::Scalar(0));
    Result.col(0).setTo(cv::Scalar(0));
    Result.col(Result.cols-1).setTo(cv::Scalar(0));
}

class MyData
{
public:
    MyData() : A(0), X(0), id()
    {
    }
    explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") // explicit to avoid implicit conversion
    {
    }
    void write(cv::FileStorage &fs) const //Write serialization for this class
    {
        fs << "{"
           << "A" << A << "X" << X << "id" << id << "}";
    }
    void read(const cv::FileNode &node) //Read serialization for this class
    {
        A = (int)node["A"];
        X = (double)node["X"];
        id = (std::string)node["id"];
    }

public: // Data Members
    int A;
    double X;
    std::string id;
};
//These write and read functions must be defined for the serialization in FileStorage to work
static void write(cv::FileStorage &fs, const std::string &, const MyData &x)
{
    x.write(fs);
}
static void read(const cv::FileNode &node, MyData &x, const MyData &default_value = MyData())
{
    if (node.empty())
        x = default_value;
    else
        x.read(node);
}
// This function will print our custom class to the console
static std::ostream &operator<<(std::ostream &out, const MyData &m)
{
    out << "{ id = " << m.id << ", ";
    out << "X = " << m.X << ", ";
    out << "A = " << m.A << "}";
    return out;
}

int main(int argc, char** argv )
{
    char * addr = "./src/cv_test/backGround.jpg";
    cv::String imageName(addr);
    //创建Mat
    cv::Mat image, foreground;
    //读取图片
    image = cv::imread(imageName, 1 );
    foreground = cv::imread("./src/cv_test/foreGround.png", 1);
    //判断图片是否存在
    if(!image.empty() && image.data && !foreground.empty()){
        // //创建窗口
        // cv::namedWindow("Display Image", cv::WINDOW_AUTOSIZE );
        // //显示图片
        // cv::imshow("Display Image", image);
        //彩图转灰度图
        cv::Mat gray_image;
        cv::cvtColor(image, gray_image, cv::COLOR_BGR2GRAY);
        cv::namedWindow("Display Image2", cv::WINDOW_FREERATIO );
        cv::imshow("Display Image2", gray_image);
        // //保存图片
        // cv::imwrite( "./src/cv_test/Gray_Image.jpg", gray_image);

        {//mat 与 vector 转换
            // //https://blog.csdn.net/guyuealian/article/details/80253066
            // std::vector<uchar> v = convertMat2Vector<uchar>(gray_image);
            // cv::Mat dest = convertVector2Mat<uchar>(v, 1, 352); //把数据转为1通道，4行的Mat数据
            // cv::namedWindow("Display Image dest", cv::WINDOW_KEEPRATIO );
            // cv::imshow("Display Image dest", dest);
        }

        {//创建Mat
            // //浅拷贝
            // cv::Mat C(gray_image);
            // cv::Mat D(C, cv::Rect(0, 0, 400, 400));
            // cv::Mat E = C(cv::Range::all(), cv::Range(1,100));
            // std::cout<<*C.data<<", "<<*D.data<<std::endl;
            // // cv::namedWindow("Display Image D", cv::WINDOW_AUTOSIZE );
            // // cv::imshow("Display Image D", D);
            // // cv::namedWindow("Display Image E", cv::WINDOW_AUTOSIZE );
            // // cv::imshow("Display Image E", E);
            // //深拷贝
            // cv::Mat F = D.clone();
            // cv::Mat G; E.copyTo(G);
            // std::cout<<*D.data<<", "<<*F.data<<std::endl;
            // // cv::namedWindow("Display Image F", cv::WINDOW_AUTOSIZE);
            // // cv::imshow("Display Image F", F);
            // // cv::namedWindow("Display Image G", cv::WINDOW_AUTOSIZE );
            // // cv::imshow("Display Image G", G);

            // cv::Mat M(6, 6, CV_8UC3, cv::Scalar(255, 0, 255));//BGR
            // std::cout << "M = " << M << std::endl;
            // cv::namedWindow("Display Image M", cv::WINDOW_KEEPRATIO );
            // cv::imshow("Display Image M", M);
            // M.create(12, 3, CV_8UC3);
            // std::cout << "M = " << M << std::endl;
            // cv::namedWindow("Display Image N", cv::WINDOW_KEEPRATIO );
            // cv::imshow("Display Image N", M);

            // cv::Mat K = cv::Mat::eye(4, 4, CV_64F);
            // std::cout << "K = " << K << std::endl;
            // cv::Mat O = cv::Mat::ones(5, 5, CV_32F);
            // std::cout << "O = " << O << std::endl;
            // cv::Mat Z = cv::Mat::zeros(6, 6, CV_8UC1);
            // std::cout << "Z = " << Z << std::endl;
        }

        {//平滑
            // cv::Mat result, result2;
            // //手动锐化
            // Sharpen(image, result);
            // cv::namedWindow("Display sharpen 1", cv::WINDOW_KEEPRATIO);
            // cv::imshow("Display sharpen 1", result);
            // //封装方法
            // cv::Mat kernel = (cv::Mat_<char>(3, 3) << 1, 1, 1,
            //                                         1, -9, 1,
            //                                         1, 1, 1);
            // cv::filter2D( image, result2, image.depth(), kernel );
            // cv::namedWindow("Display sharpen 2", cv::WINDOW_KEEPRATIO);
            // cv::imshow("Display sharpen 2", result2);
        }

        {//图像操作
            // // //计时
            // // double t = (double)cv::getTickCount();
            // // int x = 5, y = 2;
            // // cv::Scalar intensity = gray_image.at<uchar>(y, x);
            // // cv::Scalar intensity2 = gray_image.at<uchar>(cv::Point(x, y));
            // // cv::Vec3b intensity3 = image.at<cv::Vec3b>(y, x);
            // // uchar blue = intensity3.val[0];
            // // uchar green = intensity3.val[1];
            // // uchar red = intensity3.val[2];
            // // std::cout << "intensity: " << intensity << ", " << intensity2 << ", " << intensity3 << std::endl;
            // // std::cout << "color: " << int(blue) << ", " << int(green) << ", " << int(red) << std::endl;
            // if (image.channels() == 3)
            // {
            //     for (int j = 0; j < image.rows; j++)
            //     {
            //         for (int i = 0; i < image.cols; i++)
            //         {
            //             cv::Vec3b point = image.at<cv::Vec3b>(j, i);
            //             uchar temp = point.val[2];
            //             point.val[2] = point.val[1];
            //             point.val[1] = point.val[0];
            //             point.val[0] = temp;
            //             image.at<cv::Vec3b>(j, i) = point;
            //         }
            //     }
            //     cv::namedWindow("Display manipulated image", cv::WINDOW_KEEPRATIO);
            //     cv::imshow("Display manipulated image", image);
            // }
            // // t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();
            // // std::cout << "Times passed in seconds: " << t << std::endl;
        }

        {//融合
            // double alpha = 0.5;
            // cv::Mat result;
            // cv::addWeighted(image,alpha, foreground, 1-alpha, 0.0, result);
            // cv::imshow("linear blend", result);
        }

        {//对比度与亮度, gamma correction
            // // When γ<1, the original dark regions will be brighter and 
            // // the histogram will be shifted to the right whereas it will be the opposite with γ>1
            // double alpha = 1.25, beta = -50, gamma = 2, temp = 0;
            // cv::Mat result = cv::Mat::zeros(image.size(), image.type());
            // for (int i = 0; i < image.rows; i++)
            // {
            //     for (int j = 0; j < image.cols; j++)
            //     {
            //         for (int ch = 0; ch < image.channels(); ch++)
            //         {
            //             temp = alpha * image.at<cv::Vec3b>(i, j)[ch] + beta;
            //             //std::cout<<temp<<std::endl;
            //             result.at<cv::Vec3b>(i, j)[ch] = cv::saturate_cast<uchar>( pow( temp/255, gamma)*255 );
            //         }
            //     }
            // }
            // cv::imshow("contrast & lightness",result);
        }

        {//drawing基本类型
            // // cv::Point p0(50,50), p1(100,100); 
            // // double b = 255, g = 0, r = 255;
            // // cv::Scalar vector4(b,g,r);
            // int width = 500;
            // cv::Mat atom = cv::Mat::zeros(width, width, CV_8UC3);
            // // cv::line(atom,
            // //      p0,
            // //      p1,
            // //      cv::Scalar(255, 0, 0),
            // //      2,
            // //      cv::LINE_8);
            // // cv::rectangle(atom,
            // //           cv::Point(210,200),
            // //           p1,
            // //           cv::Scalar(0, 255, 255),
            // //           cv::FILLED,
            // //           cv::LINE_8);
            // // cv::circle(atom,
            // //        p1,
            // //        width / 32,
            // //        cv::Scalar(0, 0, 255),
            // //        cv::FILLED,
            // //        cv::LINE_8);
            // // cv::ellipse(atom,
            // //         p0,
            // //         cv::Size(width / 4, width / 16),
            // //         M_PI_4,
            // //         0,
            // //         360,
            // //         cv::Scalar(255, 0, 0),
            // //         2,
            // //         cv::LINE_8);
            // // cv::Point rook_points[1][20];
            // // rook_points[0][0] = cv::Point(width / 4, 7 * width / 8);
            // // rook_points[0][1] = cv::Point(3 * width / 4, 7 * width / 8);
            // // rook_points[0][2] = cv::Point(3 * width / 4, 13 * width / 16);
            // // rook_points[0][3] = cv::Point(11 * width / 16, 13 * width / 16);
            // // rook_points[0][4] = cv::Point(19 * width / 32, 3 * width / 8);
            // // rook_points[0][5] = cv::Point(3 * width / 4, 3 * width / 8);
            // // rook_points[0][6] = cv::Point(3 * width / 4, width / 8);
            // // rook_points[0][7] = cv::Point(26 * width / 40, width / 8);
            // // rook_points[0][8] = cv::Point(26 * width / 40, width / 4);
            // // rook_points[0][9] = cv::Point(22 * width / 40, width / 4);
            // // rook_points[0][10] = cv::Point(22 * width / 40, width / 8);
            // // rook_points[0][11] = cv::Point(18 * width / 40, width / 8);
            // // rook_points[0][12] = cv::Point(18 * width / 40, width / 4);
            // // rook_points[0][13] = cv::Point(14 * width / 40, width / 4);
            // // rook_points[0][14] = cv::Point(14 * width / 40, width / 8);
            // // rook_points[0][15] = cv::Point(width / 4, width / 8);
            // // rook_points[0][16] = cv::Point(width / 4, 3 * width / 8);
            // // rook_points[0][17] = cv::Point(13 * width / 32, 3 * width / 8);
            // // rook_points[0][18] = cv::Point(5 * width / 16, 13 * width / 16);
            // // rook_points[0][19] = cv::Point(width / 4, 13 * width / 16);
            // // const cv::Point *ppt[1] = {rook_points[0]};
            // // int npt[] = {20};
            // // fillPoly(atom,
            // //          ppt,
            // //          npt,
            // //          1,
            // //          cv::Scalar(255, 255, 255),
            // //          cv::LINE_8);

            // cv::RNG rnd;
            // cv::Size textsize = cv::getTextSize("OpenCV forever!", cv::FONT_HERSHEY_COMPLEX, 3, 5, 0);
            // cv::Point org(150, 150);

            // cv::putText(atom, "OpenCV forever!", org, cv::FONT_HERSHEY_COMPLEX, 1,
            //         cv::Scalar(rnd.uniform(0,255), rnd.uniform(0,255), rnd.uniform(0,255)), 3, cv::LINE_8);
            // cv::namedWindow("atom", cv::WINDOW_FREERATIO);
            // cv::moveWindow("atom", 50, 50);
            // cv::imshow("atom", atom);
        }

        {//DFT & IDFT
            // https://blog.csdn.net/abcjennifer/article/details/7622228
            cv::Mat padded; //expand input image to optimal size
            int m = cv::getOptimalDFTSize(gray_image.rows);
            int n = cv::getOptimalDFTSize(gray_image.cols); // on the border add zero values
            cv::copyMakeBorder(gray_image, padded, 0, m - gray_image.rows, 0, n - gray_image.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
            cv::Mat planes[] = {cv::Mat_<float>(padded), cv::Mat::zeros(padded.size(), CV_32F)};
            cv::Mat complexI;
            cv::merge(planes, 2, complexI); // Add to the expanded another plane with zeros
            cv::dft(complexI, complexI);    // this way the result may fit in the source matrix
            
            // compute the magnitude and switch to logarithmic scale
            // => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
            cv::split(complexI, planes);                    // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
            cv::Mat magI;
            cv::magnitude(planes[0], planes[1], magI); // planes[0] = magnitude
            magI += cv::Scalar::all(1); // switch to logarithmic scale
            cv::log(magI, magI);
            // crop the spectrum, if it has an odd number of rows or columns, -2 = fffffffe, ignore the LSB
            magI = magI(cv::Rect(0, 0, magI.cols & -2, magI.rows & -2));
            // rearrange the quadrants of Fourier image  so that the origin is at the image center
            int cx = magI.cols / 2;
            int cy = magI.rows / 2;
            //  q0 q1
            //  q2 q3
            cv::Mat q0(magI, cv::Rect(0, 0, cx, cy));   // Top-Left - Create a ROI per quadrant
            cv::Mat q1(magI, cv::Rect(cx, 0, cx, cy));  // Top-Right
            cv::Mat q2(magI, cv::Rect(0, cy, cx, cy));  // Bottom-Left
            cv::Mat q3(magI, cv::Rect(cx, cy, cx, cy)); // Bottom-Right
            cv::Mat tmp;                            // swap quadrants (Top-Left with Bottom-Right)
            q0.copyTo(tmp);
            q3.copyTo(q0);
            tmp.copyTo(q3);
            q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
            q2.copyTo(q1);
            tmp.copyTo(q2);
            //  q3 q2
            //  q1 q0
            cv::normalize(magI, magI, 0, 1, cv::NORM_MINMAX); // Transform the matrix with float values into a
                                                      // viewable image form (float between values 0 and 1).
            cv::Mat mask = cv::Mat::zeros(magI.size(), CV_8UC1);
            cv::Mat img, img2;
            // 中心：基本轮廓特征，总体灰度信息；
            // 内圈：分块与区域特征
            // 次外圈：边缘、线条、纹理细节
            // 外圈：高频噪声
            double ratio = 3.0;
            // 保留高频细节
            // mask.setTo(255);
            // mask(cv::Rect(magI.cols/ratio,magI.rows/ratio,magI.cols*(ratio-2.0)/ratio, magI.rows*(ratio-2.0)/ratio)).setTo(0);
            // 保留低频特征
            mask(cv::Rect(magI.cols / ratio, magI.rows / ratio, magI.cols * (ratio - 2.0) / ratio, magI.rows * (ratio - 2.0) / ratio)).setTo(255);
            //cv::imshow("mask",mask);

            magI.copyTo(img, mask);
            cv::namedWindow("spectrum magnitude", cv::WINDOW_FREERATIO);
            cv::imshow("spectrum magnitude", img);
            // std::cout<<img(cv::Rect(img.cols/2.0-5,img.rows/2.0-5,10,10))<<std::endl;

            complexI.copyTo(img2, mask);
            cv::idft(img2, img2);
            cv::split(img2, planes);
            cv::Mat magR = planes[0];
            magR += cv::Scalar::all(1); // switch to logarithmic scale
            cv::log(magR, magR);
            cv::normalize(magR, magR, -1.0, 0.5, cv::NORM_MINMAX);
            cv::namedWindow("spatial domain", cv::WINDOW_FREERATIO);
            cv::imshow("spatial domain", magR);
        }

        {//yaml & xml
            // std::string filename = "./src/cv_test/my.yaml";
            // { //write
            //     cv::Mat R = cv::Mat_<uchar>::eye(3, 3),
            //         T = cv::Mat_<double>::zeros(3, 1);
            //     MyData m(1);
            //     cv::FileStorage fs(filename, cv::FileStorage::WRITE);
            //     fs << "iterationNr" << 100;
            //     fs << "strings"
            //        << "["; // text - string sequence
            //     fs << "image1.jpg"
            //        << "Awesomeness"
            //        << "./src/cv_test/baboon.jpg";
            //     fs << "]";       // close sequence
            //     fs << "Mapping"; // text - mapping
            //     fs << "{"
            //        << "One" << 1;
            //     fs << "Two" << 2 << "}";
            //     fs << "R" << R; // cv::Mat
            //     fs << "T" << T;
            //     fs << "MyData" << m; // your own data structures
            //     fs.release();        // explicit close
            //     std::cout << "Write Done." << std::endl;
            // }
            // { //read
            //     std::cout << std::endl
            //          << "Reading: " << std::endl;
            //     cv::FileStorage fs;
            //     fs.open(filename, cv::FileStorage::READ);
            //     int itNr;
            //     //fs["iterationNr"] >> itNr;
            //     itNr = (int)fs["iterationNr"];
            //     std::cout << itNr;
            //     if (!fs.isOpened())
            //     {
            //         std::cerr << "Failed to open " << filename << std::endl;
            //         return 1;
            //     }
            //     cv::FileNode n = fs["strings"]; // Read string sequence - Get node
            //     if (n.type() != cv::FileNode::SEQ)
            //     {
            //         std::cerr << "strings is not a sequence! FAIL" << std::endl;
            //         return 1;
            //     }
            //     cv::FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node
            //     for (; it != it_end; ++it)
            //         std::cout << (std::string)*it << std::endl;
            //     n = fs["Mapping"]; // Read mappings from a sequence
            //     std::cout << "Two  " << (int)(n["Two"]) << "; ";
            //     std::cout << "One  " << (int)(n["One"]) << std::endl
            //          << std::endl;
            //     MyData m;
            //     cv::Mat R, T;
            //     fs["R"] >> R; // Read cv::Mat
            //     fs["T"] >> T;
            //     fs["MyData"] >> m; // Read your own structure_
            //     std::cout << std::endl
            //          << "R = " << R << std::endl;
            //     std::cout << "T = " << T << std::endl
            //          << std::endl;
            //     std::cout << "MyData = " << std::endl
            //          << m << std::endl
            //          << std::endl;
            //     //Show default behavior for non existing nodes
            //     std::cout << "Attempt to read NonExisting (should initialize the data structure with its default).";
            //     fs["NonExisting"] >> m;
            //     std::cout << std::endl
            //          << "NonExisting = " << std::endl
            //          << m << std::endl;
            // }
        }

        //中断;
        cv::waitKey(0);
    }else
    {
        std::cout<<imageName<<std::endl;
        std::cout<<"empty image"<<std::endl;
    }
    
    return 0;
}