/**
 * author:SiZheng.Chen
 * update date:20210120
**/

#include "roughPore.h"

RoughPoreData detectRoughPore(Mat image){
    double scale=5.0/1214;
    RoughPoreData data;
    if(image.empty()||image.type()!=CV_8UC3){
        cout<<"image is empty or type error."<<endl;
        data.roughImg=image;
        return data;
    }

    vector<Mat> bgrList;
    Mat grayImg,RedImg,GreenImg,BlueImg;
    Mat bgrImg = image.clone();
    cvtColor(image,grayImg,COLOR_BGR2GRAY);
    split(image,bgrList);
    BlueImg = bgrList[0];
    GreenImg = bgrList[1];
    RedImg = bgrList[2];

    Mat hairImg;
    adaptiveThreshold(grayImg,hairImg,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY_INV,75,9);

    // imshow("win",hairImg);
    // waitKey();

    Mat strcutKernel=getStructuringElement(MORPH_ELLIPSE,cv::Size(3,3));
    dilate(hairImg,hairImg,strcutKernel);

    vector<vector<cv::Point> > contours;
    vector<Vec4i> hirearchy;

    findContours(hairImg,contours,hirearchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);

    int padding = 5;
    int width=image.cols;
    int height=image.rows;
    for(int i=0;i<contours.size();++i){
        double area = contourArea(contours[i]);
        cv::Rect box = boundingRect(contours[i]);
        int x0 = box.x;
        int y0 = box.y;
        int x3 = x0+box.width;
        int y3 = y0+box.height;

        x0=(x0-padding)>=0?(x0-padding):0;
        y0=(y0-padding)>=0?(y0-padding):0;
        x3=(x3+padding)<=width?(x3+padding):width;
        y3= (y3+padding)<=height?(y3+padding):height;

        int sum_r=0;
        int sum_g=0;
        int sum_b=0;
        int count=0;
        
        Mat roi_img = hairImg(cv::Rect(x0,y0,(x3-x0),(y3-y0)));
        Mat roi_red = RedImg(cv::Rect(x0,y0,(x3-x0),(y3-y0)));
        Mat roi_green = GreenImg(cv::Rect(x0,y0,(x3-x0),(y3-y0)));
        Mat roi_blue = BlueImg(cv::Rect(x0,y0,(x3-x0),(y3-y0)));

        for(int j=0;j<roi_img.rows;++j){
            for(int k=0;k<roi_img.cols;++k){
                if(((int)roi_img.at<uchar>(j,k))!=255){
                    sum_b+=(int)roi_blue.at<uchar>(j,k);
                    sum_g+=(int)roi_green.at<uchar>(j,k);
                    sum_r+=(int)roi_red.at<uchar>(j,k);
                    count++;
                }
            }
        }

        int avg_red=int(sum_r*1.0/count);
        int avg_green=int(sum_g*1.0/count);
        int avg_blue=int(sum_b*1.0/count);

        drawContours(bgrImg,contours,i,Scalar(avg_blue,avg_green,avg_red),FILLED);
    }

    // imshow("win",bgrImg);
    // waitKey();

    cvtColor(bgrImg,grayImg,COLOR_BGR2GRAY);
    applyColorMap(grayImg,bgrImg,5);
    bgrList.clear();
    split(bgrImg,bgrList);
    applyColorMap(bgrList[1],bgrImg,2);
    cvtColor(bgrImg,grayImg,COLOR_BGR2GRAY);

    medianBlur(grayImg,grayImg,5);

    cv::Ptr<CLAHE> clahe=createCLAHE();
    clahe->setClipLimit(3.5);
    clahe->setTilesGridSize(cv::Size(10,10));
    clahe->apply(grayImg,grayImg);
    
    // imshow("win",grayImg);
    // waitKey();

    //----------------------------------- normal -------------------------------------------------------------
    Mat bin_normal_image;
    adaptiveThreshold(grayImg,bin_normal_image,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY_INV,225,15);
    vector<vector<cv::Point> > contours_normal;
    vector<Vec4i> hirearchy_normal;
    Mat normal_pore=Mat(grayImg.size(),CV_8UC1,Scalar(0));
    findContours(bin_normal_image,contours_normal,hirearchy_normal,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    for(int i=0;i<contours_normal.size();++i){
        double area=contourArea(contours_normal[i]);
        if(area>20){
            RotatedRect rrt=minAreaRect(contours_normal[i]);
            float width=rrt.size.width>rrt.size.height?rrt.size.width:rrt.size.height;
            float height=rrt.size.width<rrt.size.height?rrt.size.width:rrt.size.height;
            float eccentricity=(width+height)!=0?((width-height)*1.0/(width+height)):(width-height);
            float rate_area = area/(width*height);

            if(width>50&&height>70&&eccentricity<0.3&&rate_area>0.40){
                drawContours(normal_pore,contours_normal,i,Scalar(255),FILLED);
            }
        }
    }
    //------------------------------------- lost -------------------------------------------------------------
    Mat bin_lost_image;
    adaptiveThreshold(grayImg,bin_lost_image,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY_INV,225,20);
    Mat structKernel=getStructuringElement(MORPH_RECT,cv::Size(5,5));
    erode(bin_lost_image,bin_lost_image,structKernel);
    dilate(bin_lost_image,bin_lost_image,structKernel);

    vector<vector<cv::Point> > contours_lost;
    vector<Vec4i> hirearchy_lost;
    Mat lost_pore=Mat(grayImg.size(),CV_8UC1,Scalar(0));
    findContours(bin_lost_image,contours_lost,hirearchy_lost,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    for(int i=0;i<contours_lost.size();++i){
        double area=contourArea(contours_lost[i]);
        if(2000<area){
            RotatedRect box=minAreaRect(contours_lost[i]);
            float width=box.size.width>box.size.height?box.size.width:box.size.height;
            float height=box.size.width<box.size.height?box.size.width:box.size.height;

            float eccentricity=(width+height)!=0?((width-height)/(width+height)):(width-height);
            float rate_area=area/(width*height);

            if(width>50&&height>70&&eccentricity<0.25&&rate_area>0.3){
                drawContours(lost_pore,contours_lost,i,Scalar(255),FILLED);
            }
        }
    }
    //---------------------------------------- the end ---------------------------------------------------------
    Mat pore=max(normal_pore,lost_pore);
    contours.clear();
    hirearchy.clear();
    int count=0;
    float sumOfRadius=0;
    double sumOfArea=0;

    findContours(pore,contours,hirearchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    for(int i=0;i<contours.size();++i){
        double area = contourArea(contours[i]);
        RotatedRect box=minAreaRect(contours[i]);
        float width=box.size.width>box.size.height?box.size.width:box.size.height;
        float height=box.size.width<box.size.height?box.size.width:box.size.height;
        count++;
        sumOfArea+=area;
        sumOfRadius+=((width+height)/2);
        RotatedRect ell=fitEllipse(contours[i]);
        ellipse(image,ell,Scalar(0,100,0),1,1);
    }

    data.avgRadius=(sumOfRadius/count)*scale;
    data.roughPoreCount=count;
    data.roughImg=image;
    data.roughArea=sumOfArea;
    // imshow("win",image);
    // waitKey();
    return data;
}
