/**
 * @file pointbase.cpp
 * @author Bc. Pavol Debnár
 * This is the main file for the thesis implementing the pointBase class
 * 2022/23
 */

/*! \mainpage Stitching Barrel Surface Images and Correcting Their Brightness
 *
 * \section intro_sec Introduction
 *
 * This is the documentation for the thesis
 * 
 * There are two .cpp files: test.cpp and pointbase.cpp
 * 
 * test.cpp contains sample use cases of the implemented library
 * 
 * pointbase.cpp is the implemented library
 * 
 * in the camera folder are scripts for dataset capture, method of operation is mentioned below
 * 
 * \section prereq Prerequisites
 * OpenCV version: 4.5.2 https://docs.opencv.org/4.x/d7/d9f/tutorial_linux_install.html
 *
 * C++17
 * jsoncpp:
 * 
 *     sudo apt install libjsoncpp-dev
 * 
 *     sudo ln -s /usr/include/jsoncpp/json/ /usr/include/json
 *
 * \section install_sec Installation
 *
 *  Install using make
 * 
 * \section cam_op Dataset capture
 * 
 * The scripts in the camera folder are used for image capture from BBX-mini
 * 
 * The mode of operation is following:
 * 
 * 0. (If you want to stitch images automatically, make the project and run online stitching)
 *
 * 1. Make sure, that you are connected to bbx-mini and that you have addresses that can ping each other
 * 
 * 2. Copy the contents of camera/raspberry to BBX-mini and make
 * 
 * 3. Edit the addresses in the server and client scripts
 * 
 * 4. Run a GStreamer pipeline on BBX-mini (sample pipeline is in runThisOnRpi.txt)
 * 
 * 5. server.py provides manual stitching with the 'v' button is pressed, server2.py saves the sharpest image each second 
 * 
 * 6. run a chosen server.py on your workstation
 * 
 * 7. run the client.py script on bbx-mini (lighting is activated by running /lights/lights - needs to be created by make as in step 2)
 */



#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/stitching.hpp"
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/features2d.hpp>
#include "opencv2/calib3d/calib3d.hpp"
#include <opencv2/stitching/detail/exposure_compensate.hpp>
#include <unistd.h> 
#include <fstream>
#include <iostream>
#include <json/json.h>
#include <cmath> 
#include <fstream>
#include <algorithm>
#include <map>
#include <typeinfo>
using namespace cv;
using namespace std;
#include <filesystem>
namespace fs = std::filesystem;

//C++17
//sudo apt install libjsoncpp-dev
//sudo ln -s /usr/include/jsoncpp/json/ /usr/include/json


/**
 * @brief struct necessary for comparison between 2 opencv points - when using std::map ; used to sort points according to X value
 */

struct ComparePoints
{
    bool operator () (const cv::Point& a, const cv::Point& b) const
    { 
        return (a.x < b.x) || (a.x == b.x && a.y < b.y);
    }
} CP;

/**
 * @brief class implemented as solution for the thesis providing stitching and stitching features
 * 
 */

class PointBase {
    public:
        vector <Point2d> points; /**< all points - points are being removed as adjacencies are found */ 
        vector <Point2d> doneVector; /**< points that have been successfully searched for adjacencies  */
        vector <Point2d> toDoVector; /**< points yet to be searched for adjacent points*/
        std::map<Point2d, string, ComparePoints> pathMap; /**< map for file paths - get the path of a file via pathMap[Point2d(x,y)] */ 

        double imgWidthDeg; /**< image width in degress taken from file */ 
        double imgHeightDeg; /**< image height in sensor format */ 



    /**
     * @brief load the points from a database - necessary for the offline algorithm
     * 
     * loaded points in pointbase are sorted according to X value
     * @param path - location where images and their jsons are stored in string format
     * @return int - 0 if success, -1 if no points were loaded
     */
    int load(string path)
    {
        //std::string path = "../data/";
        for (const auto & entry : fs::directory_iterator(path))
        {
            string filePath = fs::canonical(entry.path()); // absolute path without .. 
            //cout << filePath.find(".json") << std::endl;
            if (filePath.find(".json") != string::npos) 
            {
                //std::cout << filePath << std::endl;
                std::ifstream coord_file(filePath, std::ifstream::binary);
                Json::Value coords;

                coord_file >> coords;
                //cout << "x: " << coords["depthPos"]["x"].asDouble() << std::endl;
                //cout << "y: " << coords["depthPos"]["y"].asDouble() << std::endl;
                Point2d newPoint = Point2d(coords["depthPos"]["x"].asDouble() , coords["depthPos"]["y"].asDouble());
                //if (newPoint.x >360.0) continue;
                newPoint.x = fmod(newPoint.x,360.0) ;
                pathMap[newPoint] = filePath;
                
                points.push_back(newPoint);

                imgWidthDeg = coords["rectangle"]["w"].asDouble();
                imgHeightDeg = coords["rectangle"]["h"].asDouble();
            }

        }

        if (points.empty()) {
            cout << "No points detected, check path \n";
            return -1;
        }
        else 
        {
            sort(points.begin(),points.end(),CP); 
            return 0;
        }
    }






    /**
     * @brief prints the points from the database into the terminal
     * 
     * @param which - if which is 0, the points vector is printed; 1 prints the complete contents of pathMap
     */
    void printPoints(int which=0)
    {
        if (which==1) // prints current points vector
        {
            for(int i =0; i<points.size(); i++)
            {
                cout << "point " << i <<'\n';
                cout << "x: " << points[i].x << '\n';
                cout << "y: " << points[i].y << '\n';
            }
        }
        
        if (which==0) // prints Contents of pointBase
        {
            for (const auto& [point, path] : pathMap) 
            {
                std::cout << "path: " << path<<   "; \n";
                std::cout << "x: " << point.x  << "; \n";
                std::cout << "y: " << point.y  << "; \n";
            }
        }
        

    }






    /**
     * @brief returns the path of a point specified by x and y values
     * 
     * @param x - x-value of the point
     * @param y - y-value of the point
     * @return string - path of the point
     */
    string getPath(double x, double y)
    {
        string path = pathMap[Point2d(x,y)];
        return(path);
    }








    

    /**
     * @brief shows the location of loaded points on an images
     * useful for debugging
     * images have to be loaded using the load() function
     */
    void showPointImg()
    {
        Mat img(1200, 360, CV_8UC3,   Scalar(0, 0, 0));
        for(int i =0; i<points.size(); i++)
        {
            circle(img, Point2d(points[i].x, (points[i].y)/10),3, Scalar(255, 0, 0), 3);
        }
        imshow("Location of Images", img);//Showing the circle//
        waitKey(0);//Waiting for Keystroke//
    }



    /**
     * @brief DESTRUCTIVE!! - removes found adj. points from points vector, adds them to the toBeDone vector
     * Searches for adjacent points to a given point
     * Returns either empty vector, or a vector with the found adjacent points
     * @param inputPoint - opencv Point2d of the searched point
     * @return vector of <Point2d> - points of images which are adjacent to the inputPoint
    */
    vector<Point2d> getAdjacents(Point2d inputPoint)
    {
        vector<Point2d> resultVector;
        for (int i = 0; i < points.size(); i++)
        {
            
            double yDifference = inputPoint.y-points[i].y;
            double xDifference = inputPoint.x - points[i].x;
            
            
            /*
            if(abs(yDifference)<15 && (xDifference<45) && (xDifference>0))
            {
                resultVector.push_back(points[i]);
                toDoVector.push_back(points[i]);
                points.erase(points.begin()+i);
                continue;
            }
               
            
            
            if(abs(yDifference)<15 && (xDifference> -45) && (xDifference<0))
            {
                resultVector.push_back(points[i]);
                toDoVector.push_back(points[i]);
                points.erase(points.begin()+i);
                continue;
            }
                

           
            if(abs(xDifference)<15 && (yDifference<45) && (yDifference>0))
            {
                resultVector.push_back(points[i]);
                toDoVector.push_back(points[i]);
                points.erase(points.begin()+i);
                continue;
            }
               

            
            if(abs(xDifference)<15 && (yDifference> -45) && (yDifference<0))
            {
                resultVector.push_back(points[i]);
                toDoVector.push_back(points[i]);
                points.erase(points.begin()+i);
                continue;
            }*/
            
            
            if(abs(yDifference)<0.9*imgHeightDeg && abs(xDifference)<0.45*imgWidthDeg)
            {
                resultVector.push_back(points[i]);
                toDoVector.push_back(points[i]);
                points.erase(points.begin()+i);
                continue;
            }
        }
        return resultVector;

    }


    /**
     * @brief rotation function
     * https://stackoverflow.com/questions/22041699/rotate-an-image-without-cropping-in-opencv-in-c
     * @param src - input image
     * @param angle - angle in degrees
     * @return rotated image 
    */
    Mat rotate(Mat src, double angle)
    {
        cv::Point2f center((src.cols-1)/2.0, (src.rows-1)/2.0);
        cv::Mat rot = cv::getRotationMatrix2D(center, angle, 1.0);
        // determine bounding rectangle, center not relevant
        cv::Rect2f bbox = cv::RotatedRect(cv::Point2f(), src.size(), angle).boundingRect2f();
        // adjust transformation matrix
        rot.at<double>(0,2) += bbox.width/2.0 - src.cols/2.0;
        rot.at<double>(1,2) += bbox.height/2.0 - src.rows/2.0;

        cv::Mat dst;
        cv::warpAffine(src, dst, rot, bbox.size());
        return dst;
    }






    /**
     * @brief blends using distance transform
     * 
     * @param roi original roi with 1 or more stitched images
     * @param transformedRoi roi-sized! image with the new image in the appropriate location
     * @param showImg bool, switch to enable showing of the new roi
     * @return Mat - the blended image
     */
    Mat dtBlend(Mat roi, Mat transformedRoi, bool showImg = false)
    {
        

        Mat roiMask, roiDT, imgWarpedMask,imgDT,roiMaskFinal,imgMaskFinal,imgNew;
        threshold(roi,roiMask,1,255,CV_8U);
        threshold(transformedRoi,imgWarpedMask,1,255,CV_8U);


        distanceTransform(roiMask,roiDT,DIST_L2, 3);
        distanceTransform(imgWarpedMask,imgDT,DIST_L2, 3);
        normalize(roiDT, roiDT, 0.0, 1.0, NORM_MINMAX);
        normalize(imgDT, imgDT, 0.0, 1.0, NORM_MINMAX);

        cv::divide(roiDT,(roiDT+imgDT),roiMaskFinal);  
        cv::divide(imgDT,(roiDT+imgDT),imgMaskFinal);  
                        
                        

        normalize(roiMaskFinal, roiMaskFinal, 0.0, 255.0, NORM_MINMAX);
        normalize(imgMaskFinal, imgMaskFinal, 0.0, 255.0, NORM_MINMAX);

                        
        
        roi.convertTo(roi,CV_32FC1);
        transformedRoi.convertTo(transformedRoi,CV_32FC1);
        roiMaskFinal.convertTo(roiMaskFinal,CV_32FC1);
        imgMaskFinal.convertTo(imgMaskFinal,CV_32FC1);
                        
                        
        multiply(roiMaskFinal,roi,roi);             
        multiply(imgMaskFinal,transformedRoi,transformedRoi);              
        cv::add(roi,transformedRoi, roi);

        if(showImg)
        {
            normalize(roi, roi, 0.0, 1.0, NORM_MINMAX);
            imshow("newRoi",roi);
            waitKey(0);
        }

        normalize(roi, roi, 0.0, 255.0, NORM_MINMAX);
        return roi;
     

    }

















    Mat exposureBlend(Mat roi, Mat transformedRoi, bool showImg = false)
    {
        std::vector<UMat> images;
        UMat umat,umat2;
        cv::cvtColor(roi, roi, cv::COLOR_GRAY2BGR);
        cv::cvtColor(transformedRoi, transformedRoi, cv::COLOR_GRAY2BGR);

        roi.copyTo(umat);
        images.push_back(umat);

        transformedRoi.copyTo(umat2);
        images.push_back(umat2);


        
        cv::Mat_<cv::Point> corners(2,1);
        /*corners.push_back(Point(0.0,0.0));
        corners.push_back(Point(0.0,0.0));*/
        corners(0,0) = Point(0.0,0.0);
        corners(1,0) = Point(0.0,0.0);
        
        
        std::vector<Mat> masks(2);
        
        //masks[0] = Mat::ones(roi.size(), CV_8UC3);
        //masks[1] = Mat::ones(transformedRoi.size(), CV_8UC3);
        threshold(roi,masks[0],1,255,CV_8U);
        threshold(transformedRoi,masks[1],1,255,CV_8U);
        //cout<<"\n"<< cv::typeToString( masks[0].type() )<<"\n";
        /*cv::cvtColor(masks[0], masks[0], cv::COLOR_GRAY2BGR);
        cv::cvtColor(masks[1], masks[1], cv::COLOR_GRAY2BGR);*/
        normalize(masks[0],  masks[0], 0.0, 255.0, NORM_MINMAX);
        normalize(masks[1],  masks[1], 0.0, 255.0, NORM_MINMAX);

        std::vector<UMat> umatMasks;
        for (int i=0; i<masks.size() ; i++)
        {
            UMat umatMask;
            masks[i].copyTo(umatMask);
            umatMasks.push_back(umatMask);
        }

             imshow("old1",images[0]);
             imshow("old21",images[1]);

        
        Ptr <detail::ExposureCompensator> compensator = detail::ExposureCompensator::createDefault(detail::ExposureCompensator::GAIN);

         
        compensator->feed(corners, images, umatMasks );
        
       
        for (size_t i = 0; i < images.size(); ++i)
        {
            
            cout<< cv::typeToString( umatMasks[i].type() );
            compensator->apply(i, Point(0, 0), images[i], umatMasks[i]);
        }
        imshow("1",images[0]);
        imshow("2",images[1]);
        waitKey(0);
        //return images[0];
    }
 




    /**
     * @brief blends using image pyramids transform
     * 
     * @param roi original roi with 1 or more stitched images
     * @param transformedRoi roi-sized! image with the new image in the appropriate location
     * @param showImg bool, switch to enable showing of the new roi
     * @return Mat - the blended image
     */
    Mat pyramidBlend(Mat roi, Mat transformedRoi, bool showImg = false)
    {
        /*roi.convertTo(roi,CV_32F);
        transformedRoi.convertTo(transformedRoi,CV_32F);*/
        Mat mask1, mask2, overlapMask;
        threshold(roi,mask1,1,255,CV_8U);
        threshold(transformedRoi,mask2,1,255,CV_8U);
        
        bitwise_and(mask1,mask2,overlapMask);
        int levels = 4;
        
        std::vector<Mat> roiPyr(levels), transPyr(levels);
        buildPyramid(roi, roiPyr, levels);
        buildPyramid(transformedRoi, transPyr, levels);

        
        std::vector<Mat> maskPyr(levels);
        maskPyr[0] = overlapMask;
        
        for (int i = 1; i < levels; i++) 
        {
            Mat tmp;
            pyrDown(maskPyr[i - 1], tmp);
            maskPyr[i] = tmp;

        }

        for (int i = 0; i < levels; i++) 
        {
           roiPyr[i].convertTo(roiPyr[i],CV_8U);
           transPyr[i].convertTo(transPyr[i],CV_8U);
           maskPyr[i].convertTo(maskPyr[i],CV_8U);
           //normalize(roiPyr[i],roiPyr[i],0.0,1.0,NORM_MINMAX);
           //normalize(transPyr[i],transPyr[i],0.0,1.0,NORM_MINMAX);
           normalize(maskPyr[i],maskPyr[i],0.0,1.0,NORM_MINMAX);
            
        }

        
        std::vector<Mat> blendPyr(levels);
        for (int i = levels-1; i >=0; i--) {
            cout<< cv::typeToString( maskPyr[i].type() )<<std::endl;
            cout<< cv::typeToString( transPyr[i].type() )<<std::endl;
            Mat currLayer = roiPyr[i].mul(maskPyr[i]) + transPyr[i].mul(Scalar::all(1.0) - maskPyr[i]);
            //normalize(currLayer,currLayer,0.0,255.0,NORM_MINMAX);
            imshow("overlapMask",overlapMask);
            waitKey(0);
            if (i == levels - 1) 
            {
                blendPyr[i] = currLayer;
            } 
            else 
            {
                Mat tmp;
                //cout<<"MMMMMMMMM blendPyr[i + 1] SIZE: "<<blendPyr[i + 1].size() <<std::endl;
                //cout<<"MMMMMMMMM currLayer SIZE: "<<currLayer.size() <<std::endl;
                pyrUp(blendPyr[i + 1], tmp, currLayer.size());
                normalize(tmp,tmp,0.0,255.0,NORM_MINMAX);
                /*imshow("tmp",tmp);
                waitKey(0);*/
                blendPyr[i] = (currLayer + tmp)/2;
            }
        }

       /* for (int i = 0; i < levels; i++)
        {
            imshow("blendPyr[i]",blendPyr[i]);
            waitKey(0);
        }*/
        

        Mat result = blendPyr[0];

        Mat combinedMask;
        bitwise_or(mask1,mask2,combinedMask);
        normalize(combinedMask,combinedMask,0.0,1.0,NORM_MINMAX);
        combinedMask.convertTo(combinedMask,CV_32F);
        result.convertTo(result,CV_32F);
        multiply(result,combinedMask,result);
        result.convertTo(result,CV_8U);

        if(showImg)
        {
            
            Mat sharpening_kernel = (Mat_<double>(3, 3) << 0, -1, 0,
            -1, 5 -1,
            0, -1, 0);
            filter2D(result, result, 0, sharpening_kernel);
            normalize(result,result,0.0,255.0,NORM_MINMAX);
            imshow("newRoi",result);
            waitKey(0);
        }

        return(result);


    }








    /**
     * @brief stitches images using opencv pipeline
     * points have to be loaded in the pointBase first
     * saves the result image in current directory
     */
    void stitchImgs()
    {
        int imgIdx = 0;
        //main loop
        while (!points.empty())
        {
            //take the first point from the left and find adjts. to it
            Point2d p = points[0];
            vector<Point2d> stitchQueue;
            stitchQueue.push_back(p);
            points.erase(points.begin());

            vector<Point2d> temp = getAdjacents(p); //find adj. of first point - inits toDo vector
            

            if (toDoVector.empty()) //if first point has no adjts. - log him as singleton
            {
                ofstream singletonsFile("singletons.log", ios_base::app);
                if(!singletonsFile)
                {
                    singletonsFile << "x: " << p.x << std::endl;
                    singletonsFile << "y: " << p.y << std::endl;
                    singletonsFile << pathMap[Point2d(p.x,p.y) ] << std::endl;
                    singletonsFile.close();
                }
                else
                {
                    ofstream sing;
                    sing.open("singletons.log",ios_base::app);
                    sing << "x: " << p.x << std::endl;
                    sing << "y: " << p.y << std::endl;
                    sing << pathMap[Point2d(p.x,p.y) ] << std::endl;
                    sing.close();
                }
                stitchQueue.pop_back();
                continue;
            }

            //finds adjacencies to every point
            while(!toDoVector.empty())
            {
                Point2d p2 = toDoVector[0];
                stitchQueue.push_back(p2);
                temp = getAdjacents(p2);
                toDoVector.erase(toDoVector.begin());
            }
            
            //stitching
            // Define object to store the stitched image
            Mat pano;
            bool try_use_gpu=true;
            // Create a Stitcher class object with mode scans
            Ptr<Stitcher> stitcher = Stitcher::create(Stitcher::SCANS);
            //stitcher->setPanoConfidenceThresh(0.85);
            cout << stitcher->panoConfidenceThresh() << "CONFIDENCE TRESH \n" ;

            //Ptr<Feature2D> finder = stitcher->featuresFinder();
            Ptr<Feature2D> finder = SIFT::create(1600);
            stitcher->setFeaturesFinder(finder);
            cout << finder->getDefaultName()<<" FINDER \n";


            vector<Mat> imgs;

            //open stream for logging
            string logFileName = to_string(imgIdx) + ".log";
            ofstream logFile(logFileName);
            for (Point2d coords: stitchQueue)
            {
                //log which images are in which result file
                logFile << "x: " << coords.x << std::endl;
                logFile << "y: " << coords.y << std::endl;
                logFile << pathMap[Point2d(coords.x,coords.y) ] << std::endl;

                //log to stdout
                cout << "x: " << coords.x << std::endl;
                cout << "y: " << coords.y << std::endl;
                

                string jsonPath = pathMap[coords];
                string pngPath = jsonPath.erase(jsonPath.length()-4);
                pngPath = pngPath + "png";
                cout << "file: " << pngPath << std::endl;

                Mat img = imread(pngPath,IMREAD_COLOR);
                //imshow("Location of Images", img);//Showing the circle//

                if (!img.empty())
                {
                    imgs.push_back(img); //store loaded image in vector of images
                }
                else
                {
                     cout << "Error: Cannot read image"<< std::endl;
                }
                
            }
            logFile.close();


            //stitching
            cout << "Stitching" << std::endl;
            Stitcher::Status status = stitcher->stitch(imgs, pano);
            if (status != Stitcher::OK)
            {
                // Check if images could not be stitched
                // status is OK if images are stitched successfully
                cout << "Can't stitch images\n";
            }
            else
            {

                string resultName = "result" + to_string(imgIdx) +".jpg";

                // Store a new image stitched from the given
                //set of images as "result.jpg"
                imwrite(resultName, pano);
                
                // Show the result
                imshow(resultName, pano);
                imgIdx++;
                
                waitKey(0);
            }
            
            
        }
        
    }




    /*returns minimum and maximum X and Y values from pointbase
      minX,maxX,minY,maxY
    */
   /**
     * @brief gets the values of the top right, left and bottom right left images in pointBase
     * 
     * @return vector, where the values are [minX,maxX,minY,maxY]
     */
    std::vector <double> getEdgeValues()
    {
        double minX=999;
        double maxX=0;
        double minY=900000;
        double maxY=0;
        std::vector <double> res(4);

        for (Point2d coords: points)
        {
            if (coords.x < minX) minX=coords.x;
            if (coords.x > maxX) maxX=coords.x;
            if (coords.y < minY) minY=coords.y;
            if (coords.y > maxY) maxY=coords.y;
        }
        res[0]=minX;
        res[1]=maxX;
        res[2]=minY;
        res[3]=maxY;
        return(res);
    }














    /**
     * @brief image stitching using SIFT
     * saves the source image to the current directory
     * 
     * @param imgW source image width
     * @param imgH source image height
     */
    void cumdump(int imgW,int imgH)
    {   //sift takes too long if images are large, so we shrink them
        int imgWidth = imgW;
        int imgHeight = imgH;
        if (imgWidth > 1000)
        {
            imgWidth = int(0.4*imgWidth);
            imgHeight = int(0.4*imgHeight);
        }
        
        int placeCounter =0;
        int roiCounter = 0;
        int tempMatchCounter =0;

        std::vector <double> edges;
        edges = getEdgeValues();
        double minX=edges[0];
        double maxX=edges[1];
        double minY=edges[2];
        double maxY=edges[3];

        int windowHeight = (int)(maxY*(imgHeight/imgHeightDeg))+imgHeight*2;
        int windowWidth = (int)((360/imgWidthDeg)*imgWidth) + imgWidth;

        Mat img = Mat::zeros(windowHeight,windowWidth,CV_8UC1);
        rectangle(img,Point(3,3),Point(15,15),Scalar(128,128, 0),2,2,0);

        int imgIdx = 0;
        //main loop
        while (!points.empty())
        {
            //take the first point from the left and find adjts. to it
            Point2d p = points[0];
            vector<Point2d> stitchQueue;
            stitchQueue.push_back(p);
            points.erase(points.begin());

            vector<Point2d> temp = getAdjacents(p); //find adj. of first point - inits toDo vector
            

            if (toDoVector.empty()) //if first point has no adjts. - log him as singleton
            {
                ofstream singletonsFile("singletons.log", ios_base::app);
                if(!singletonsFile)
                {
                    singletonsFile << "x: " << p.x << std::endl;
                    singletonsFile << "y: " << p.y << std::endl;
                    singletonsFile << pathMap[Point2d(p.x,p.y) ] << std::endl;
                    singletonsFile.close();
                }
                else
                {
                    ofstream sing;
                    sing.open("singletons.log",ios_base::app);
                    sing << "x: " << p.x << std::endl;
                    sing << "y: " << p.y << std::endl;
                    sing << pathMap[Point2d(p.x,p.y) ] << std::endl;
                    sing.close();
                }
                stitchQueue.pop_back();
                continue;
            }

            //finds adjacencies to every point
            while(!toDoVector.empty())
            {
                Point2d p2 = toDoVector[0];
                stitchQueue.push_back(p2);
                temp = getAdjacents(p2);
                toDoVector.erase(toDoVector.begin());
            }
            
            //stitching
            // Define object to store the stitched image
            /* a funct was created for this, see getEdgeValues()
            double minX=999;
            double maxX=0;
            double minY=900000;
            double maxY=0;
            
            for (Point2d coords: stitchQueue)
            {
                if (coords.x < minX) minX=coords.x;
                if (coords.x > maxX) maxX=coords.x;
                if (coords.y < minY) minY=coords.y;
                if (coords.y > maxY) maxY=coords.y;
            }
            */
            
            int xDiff = (int)round(maxX)-(int)round(minX);
            int yDiff = (int)round(maxY)-(int)round(minY);
            cout <<xDiff <<"\n";
            cout <<yDiff <<"\n";

            //img creation put to beginning of funcion 
            /*
            Mat img = Mat::zeros((int)(maxY*(imgHeight/imgHeightDeg))+imgHeight,(int)((360/imgWidthDeg)*imgWidth) + imgWidth,CV_8UC3);
            rectangle(img,Point(3,3),Point(15,15),Scalar(128,128, 0),2,2,0);
            */
            

            //open stream for logging
            string logFileName = to_string(imgIdx) + ".log";
            ofstream logFile(logFileName);
            int qIdx = 0;
            for (Point2d coords: stitchQueue)
            {
                //log which images are in which result file
                logFile << "x: " << coords.x << std::endl;
                logFile << "y: " << coords.y << std::endl;
                logFile << pathMap[Point2d(coords.x,coords.y) ] << std::endl;

                //log to stdout
                cout << "x: " << coords.x << std::endl;
                cout << "y: " << coords.y << std::endl;
                

                string jsonPath = pathMap[coords];
                string pngPath = jsonPath.erase(jsonPath.length()-4);
                pngPath = pngPath + "png";
                cout << "file: " << pngPath << std::endl;
                
                Mat imgSmall = imread(pngPath,IMREAD_GRAYSCALE);
                //resizing
                cout << "w:" <<imgWidth << " h: " <<imgHeight <<"\n";
                resize(imgSmall, imgSmall, Size(imgWidth, imgHeight), INTER_LINEAR);
                //imgSmall = imgSmall(Range(15,imgHeight),Range(0,imgWidth)); // Slicing to crop the image

                Ptr<CLAHE> clahe = cv::createCLAHE(4.0, cv::Size(8,8));
                clahe->apply(imgSmall,imgSmall);
                cout<< cv::typeToString( imgSmall.type() );
                
                cout <<minX <<"\n";
                cout <<minY <<"\n";
                
                //cout << (int)round(coords.x)-(int)round(minX)<< "\n";
                //cout << (int)round(coords.y)-(int)round(minY)<< "\n";
                cout << (int)round(coords.y)*(int)(imgHeight/imgHeightDeg)<< "\n";

                //first image is added to result img, others will be stitched
                
                if (qIdx==0) 
                {   cout << "AM TRYING  TO PLACE TO \n \n";
                    cout << "imgHeightDeg" << imgHeightDeg <<"\n";
                    placeCounter++;
                    cout << "X: " << (int)round(coords.x)*(int)(imgWidth/imgWidthDeg) << " Y:" << (int)round(coords.y)*(int)(imgHeight/imgHeightDeg) << " W: " <<imgSmall.cols << " H: " << imgSmall.rows << "\n";
                    imgSmall.copyTo(img(cv::Rect((int)round(coords.x)*(int)(imgWidth/imgWidthDeg),(int)round(coords.y)*(int)(imgHeight/imgHeightDeg),imgSmall.cols, imgSmall.rows))); 
                }
                else {
                    //default ROI location and size
                    int xLoc=(int)round(coords.x)*(int)(imgWidth/imgWidthDeg) - imgWidth; //=normalized x-coord. minus 1 width of image
                    int yLoc=(int)round(coords.y)*(int)(imgHeight/imgHeightDeg) - imgHeight;
                    int xWitdth=3*imgWidth;
                    int yHeight=3*imgHeight;

                    //edge cases
                    if ((int)round(coords.x)*(int)(imgWidth/imgWidthDeg) - imgWidth < 0) xLoc=0;
                    if ((int)round(coords.y)*(int)(imgHeight/imgHeightDeg) - imgHeight < 0) yLoc = 0;
                    if (xLoc + xWitdth > windowWidth) xWitdth = windowWidth - xLoc;
                    if (yLoc + yHeight > windowHeight) yHeight = windowHeight - yLoc;

                    //create the ROI
                    cout << "AM DOING ROI \n \n";
                    Mat roi;
                    roi = img(Rect(xLoc, yLoc, xWitdth, yHeight));
                    /*imshow("roi",roi);
                    imwrite("temp.jpg", roi);
                    waitKey(0);*/

                    //detect the keypoints from both the ROI and the img we want to add
                    cv::Ptr<SIFT> siftPtr = SIFT::create();
                    std::vector<KeyPoint> keypointsROI, keypointsImg;
                    cv::Ptr<SiftDescriptorExtractor> siftExtrPtr;
                    cv::Mat descriptorsROI, descriptorsImg;
                    siftPtr->detect(roi, keypointsROI);
                    siftPtr->detect(imgSmall, keypointsImg);



                    //compute descriptors from found keypoints
                    cout<<" SIZE \n"<<cv::typeToString(roi.type()) <<" \n";
                    siftPtr->compute(roi,
                                keypointsROI,
                                descriptorsROI);
                    siftPtr->compute(imgSmall,
                                keypointsImg,
                                descriptorsImg);

                    //match the descriptors
                    cv::FlannBasedMatcher matcher;

                     //matcher.match part1
                    /*std::vector<cv::DMatch> matches;
                    
                    matcher.match(descriptorsROI,descriptorsImg,matches);*/

                    //trying out knnmatch
                    std::vector<std::vector<cv::DMatch>> matches;
                    matcher.knnMatch(descriptorsROI, descriptorsImg, matches, 2);  // Find 2 nearest matches
                    cv::Mat imageMatches;
                    
                    drawMatches(roi,keypointsROI,imgSmall,keypointsImg,matches,imageMatches);
                    cv::namedWindow("matches");
                    cv::imshow("matches",imageMatches);
                    //cv::waitKey(0);




                    //matcher.match part2
                    /*
                    double max_dist = 0; double min_dist = 100;
                    //cout<<"ROWS DESC:"<<descriptorsImg.rows<<"\n";
                    //cout<<"SIZE DESC:"<<descriptorsImg.size()<<"\n";

                    //Quick calculation of max and min distances between keypoints
                    for( int i = 0; i < descriptorsImg.rows; i++ )
                    { 
                        double dist = matches[i].distance;
                        cout<<"DIST:"<<dist<<"\n";
                        if( dist < min_dist ) min_dist = dist;
                        if( dist > max_dist ) max_dist = dist;
                    }
                    printf("-- Max dist : %f \n", max_dist );
                    printf("-- Min dist : %f \n", min_dist );
                    
                    //find the GOOD matches from all descriptor matches
                    //e.g. the ones, that have a distance LESS than 3* the smallest computed distance
                    std::vector< DMatch > good_matches;
                    for( int i = 0; i < descriptorsImg.rows; i++ )
                    { 
                        if( matches[i].distance < 3*min_dist ) { good_matches.push_back( matches[i]);  }

                    }
                    */
                   //knnmatcher part2
                   cout<<"TU BY MALI BYT MATCHE " <<'\n';
                   std::vector< DMatch > good_matches;
                   //float ratio = 0.4; // As in Lowe's paper; can be tuned
                   /*
                   //this is continous worsening of the ratio in order to get good matches - after 0.4 the results were getting kinda bad tho
                   for (float ratio = 0.4; good_matches.size()<3; ratio+=0.05)
                   {
                        good_matches.clear();
                        cout << "RATIO: " <<ratio <<"\n";
                        for (int i = 0; i < matches.size(); ++i)
                        {
                            
                            if (matches[i][0].distance < ratio * matches[i][1].distance)
                            {
                                good_matches.push_back(matches[i][0]);
                                cout<<"GOOD MATCH DISTANCE " << matches[i][0].distance <<'\n';
                            }
                        }
                   }
                   */
                   float ratio =0.4; //0.15 malo vysledok 0
                   for (int i = 0; i < matches.size(); ++i)
                    {
                            
                        if (matches[i][0].distance < ratio * matches[i][1].distance)
                            {
                                good_matches.push_back(matches[i][0]);
                                cout<<"GOOD MATCH DISTANCE " << matches[i][0].distance <<'\n';
                            }
                    }
                   


                    std::vector< Point2d > obj;
                    std::vector< Point2d > scene;
                    
                    for( int i = 0; i < good_matches.size(); i++ )
                    {
                    //Get the keypoints from the good matches
                    obj.push_back( static_cast<cv::Point2i>( keypointsImg[ good_matches[i].trainIdx ].pt ));
                    scene.push_back(  static_cast<cv::Point2i>(keypointsROI[ good_matches[i].queryIdx ].pt ));
                    }


                    
                    //find a transformation based on good matches
                    //we do not need a Homography, since we deal with affine transformations (no viewport transf. are expected)
                    //Mat H = findHomography( Mat(obj), Mat(scene), RANSAC );
                    Mat H;
                    if(good_matches.size()>=3) H = estimateAffinePartial2D( Mat(obj), Mat(scene), noArray(),RANSAC );
                    

                    //here we check for errors - either there were not enough good matches, OR an affine transformation could not be estimated
                    //we use template matching if errors occured
                    if(good_matches.size()<3 || H.empty())
                    {
                        tempMatchCounter++;
                        //Mat workRoi, workImg;
                        //workRoi = roi.clone();
                        //workImg = imgSmall.clone();

                        Mat roiEdit, imgSmallEdit;
                        roiEdit = roi.clone();
                        imgSmallEdit = imgSmall.clone();

                        
                        GaussianBlur(roi, roiEdit, Size(3, 3), 0);
                        GaussianBlur(imgSmall, imgSmallEdit, Size(3, 3), 0);

                        Canny(roiEdit, roiEdit, 100, 200, 3, false);
                        Canny(imgSmallEdit, imgSmallEdit, 100, 200, 3, false);



                        Mat result;
                        double min_val, max_val;
                        Point min_loc, max_loc;
                        matchTemplate(roiEdit,imgSmallEdit,result,TM_CCOEFF);
                        minMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc);


                        Mat imgNew;
                        imgNew = Mat::zeros(roi.rows,roi.cols,CV_8U);
                        imgSmall.copyTo(imgNew(Rect(max_loc.x , max_loc.y , imgSmall.cols,imgSmall.rows))); 
                        //hodnotenie
                        Mat imgEvalSource = imgNew(Rect(max_loc.x, max_loc.y, imgSmall.cols,imgSmall.rows));
                        imwrite("hodnotenie/"+to_string(imgIdx)+"sift-temp-src.png",imgEvalSource);
                        

                        Mat newRoi;
                        newRoi = dtBlend(roi,imgNew, false);
                        newRoi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));

                        //hodnotenie
                        Mat imgEvalDst= newRoi(Rect(max_loc.x, max_loc.y,imgSmall.cols,imgSmall.rows));
                        imwrite("hodnotenie/"+to_string(imgIdx)+"sift-temp-dst.png",imgEvalDst);
                        imgIdx++;

                    }
                    else
                    {
                        roiCounter++;
                        cv::Mat resultWarp;
                        imwrite("partResults/"+to_string(imgIdx)+"imgSmall.png", imgSmall);/*eval*/
                        //warpPerspective(imgSmall,roi,H,cv::Size(roi.cols,roi.rows));
                        warpAffine(imgSmall,resultWarp,H,cv::Size(roi.cols,roi.rows));
                        //imshow("imgSmall", imgSmall);
                        //imshow( "resultWarp", resultWarp );
                        //imshow("roi", roi);
                        //cv::waitKey(0);

                        //hodnotenie
                        Mat resultWarpMask;
                        threshold(resultWarp,resultWarpMask,1,255,CV_8U);
                        Mat srcSave;
                        resultWarp.copyTo(srcSave,resultWarpMask);
                        
                        //imshow("srcSave",srcSave);

                        cv::Rect bounding_box = cv::boundingRect(srcSave);
                        cv::Mat cropped_image = resultWarp(bounding_box);
                        
                        imwrite("hodnotenie/"+to_string(imgIdx)+"sift-sift-src.png",cropped_image);

                        
                        Mat resultRoi;
                        imwrite("partResults/"+to_string(imgIdx)+"roi1.png", roi);/*save roi for eval*/
                        resultRoi = dtBlend(roi,resultWarp,false);
                        resultRoi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));
                        
                        //hodnotenie
                        Mat dstSave;
                        
                        resultRoi.copyTo(dstSave,resultWarpMask);
                        resultRoi.convertTo(resultRoi,CV_8U);
                        //imshow("resultRoi",resultRoi);
                        //waitKey(0);
                        cv::Rect bounding_box2 = cv::boundingRect(resultWarpMask);
                        cout<<"AAAAAAAAA\n";
                        cv::Mat cropped_image2 = resultRoi(bounding_box2);
                        imwrite("hodnotenie/"+to_string(imgIdx)+"sift-sift-dst.png",cropped_image2);

                        /*save rois for eval*/
                        
                        imwrite("partResults/"+to_string(imgIdx)+"roi2.png", resultWarp);
                        imwrite("partResults/"+to_string(imgIdx)+"roi3.png", resultRoi);
                        imgIdx++;

                        //destroyAllWindows();
                    }

                    
                   //cout<<CV_VERSION;
                }
                //if(qIdx==2) break;
                
                
                qIdx++;
            }
            logFile.close();
            string resultName = "result" + to_string(imgIdx) +".png";
            cout<<"ROIS: " <<roiCounter << "\n PlacedImgs: " <<placeCounter <<"\n tempMatches: " << tempMatchCounter <<'\n';
            //imshow("fag",img);
            //waitKey(0);
            //imwrite(resultName, img);
            //imgIdx++;

            //stitching
            /*
            cout << "Stitching" << std::endl;
            Stitcher::Status status = stitcher->stitch(imgs, pano);
            if (status != Stitcher::OK)
            {
                // Check if images could not be stitched
                // status is OK if images are stitched successfully
                cout << "Can't stitch images\n";
            }
            else
            {

                string resultName = "result" + to_string(imgIdx) +".jpg";

                // Store a new image stitched from the given
                //set of images as "result.jpg"
                imwrite(resultName, pano);
                
                // Show the result
                imshow(resultName, pano);
                
                
                waitKey(0);
            }
            */
            
        }

    imwrite("result.png", img);
    }



























































































































    /**
     * @brief stitches images using pattern matching
     *  saves the source image to the current directory
     * @param imgW width of the source images
     * @param imgH height of the source images
     */
    void patternMatchTry(int imgW,int imgH)
    {
        int imgWidth = imgW;
        int imgHeight = imgH;
        if (imgWidth > 1000)
        {
            imgWidth = int(0.4*imgWidth);
            imgHeight = int(0.4*imgHeight);
        }


        std::vector <double> edges;
        edges = getEdgeValues();
        double minX=edges[0];
        double maxX=edges[1];
        double minY=edges[2];
        double maxY=edges[3];

        int windowHeight = (int)(maxY*(imgHeight/imgHeightDeg))+imgHeight*2;
        int windowWidth = (int)((360/imgWidthDeg)*imgWidth) + imgWidth;

        Mat img = Mat::zeros(windowHeight,windowWidth,CV_8UC1);
        rectangle(img,Point(3,3),Point(15,15),Scalar(128,128, 0),2,2,0);

        int imgIdx = 0;
        //main loop
        while (!points.empty())
        {
            //take the first point from the left and find adjts. to it
            Point2d p = points[0];
            vector<Point2d> stitchQueue;
            stitchQueue.push_back(p);
            points.erase(points.begin());

            vector<Point2d> temp = getAdjacents(p); //find adj. of first point - inits toDo vector
            

            if (toDoVector.empty()) //if first point has no adjts. - log him as singleton
            {
                ofstream singletonsFile("singletons.log", ios_base::app);
                if(!singletonsFile)
                {
                    singletonsFile << "x: " << p.x << std::endl;
                    singletonsFile << "y: " << p.y << std::endl;
                    singletonsFile << pathMap[Point2d(p.x,p.y) ] << std::endl;
                    singletonsFile.close();
                }
                else
                {
                    ofstream sing;
                    sing.open("singletons.log",ios_base::app);
                    sing << "x: " << p.x << std::endl;
                    sing << "y: " << p.y << std::endl;
                    sing << pathMap[Point2d(p.x,p.y) ] << std::endl;
                    sing.close();
                }
                stitchQueue.pop_back();
                continue;
            }

            //finds adjacencies to every point
            while(!toDoVector.empty())
            {
                Point2d p2 = toDoVector[0];
                stitchQueue.push_back(p2);
                temp = getAdjacents(p2);
                toDoVector.erase(toDoVector.begin());
            }
            
            //stitching
            // Define object to store the stitched image
            /* a funct was created for this, see getEdgeValues()
            double minX=999;
            double maxX=0;
            double minY=900000;
            double maxY=0;
            
            for (Point2d coords: stitchQueue)
            {
                if (coords.x < minX) minX=coords.x;
                if (coords.x > maxX) maxX=coords.x;
                if (coords.y < minY) minY=coords.y;
                if (coords.y > maxY) maxY=coords.y;
            }
            */
            
            int xDiff = (int)round(maxX)-(int)round(minX);
            int yDiff = (int)round(maxY)-(int)round(minY);
            cout <<xDiff <<"\n";
            cout <<yDiff <<"\n";

            //img creation put to beginning of funcion 
            /*
            Mat img = Mat::zeros((int)(maxY*(imgHeight/imgHeightDeg))+imgHeight,(int)((360/imgWidthDeg)*imgWidth) + imgWidth,CV_8UC3);
            rectangle(img,Point(3,3),Point(15,15),Scalar(128,128, 0),2,2,0);
            */
            

            //open stream for logging
            string logFileName = to_string(imgIdx) + ".log";
            ofstream logFile(logFileName);
            int qIdx = 0;
            for (Point2d coords: stitchQueue)
            {
                //log which images are in which result file
                logFile << "x: " << coords.x << std::endl;
                logFile << "y: " << coords.y << std::endl;
                logFile << pathMap[Point2d(coords.x,coords.y) ] << std::endl;

                //log to stdout
                cout << "x: " << coords.x << std::endl;
                cout << "y: " << coords.y << std::endl;
                

                string jsonPath = pathMap[coords];
                string pngPath = jsonPath.erase(jsonPath.length()-4);
                pngPath = pngPath + "png";
                cout << "file: " << pngPath << std::endl;
                
                Mat imgSmall = imread(pngPath,IMREAD_GRAYSCALE);
                resize(imgSmall, imgSmall, Size(imgWidth, imgHeight), INTER_LINEAR);

                Ptr<CLAHE> clahe = cv::createCLAHE(4.0, cv::Size(8,8));
                clahe->apply(imgSmall,imgSmall);
                cout<< cv::typeToString( imgSmall.type() );
                
                cout <<minX <<"\n";
                cout <<minY <<"\n";
                
                //cout << (int)round(coords.x)-(int)round(minX)<< "\n";
                //cout << (int)round(coords.y)-(int)round(minY)<< "\n";
                cout << (int)round(coords.y)*(int)(imgHeight/imgHeightDeg)<< "\n";

                //first image is added to result img, others will be stitched
                if (qIdx==0) 
                {
                    imgSmall.copyTo(img(cv::Rect((int)round(coords.x)*(int)(imgWidth/imgWidthDeg),(int)round(coords.y)*(int)(imgHeight/imgHeightDeg),imgSmall.cols, imgSmall.rows))); 
                    cout<<"IMAGE WAS PLACED \n";
                }
                else {
                    //default ROI location and size
                    int xLoc=(int)round(coords.x)*(int)(imgWidth/imgWidthDeg) - imgWidth; //=normalized x-coord. minus 1 width of image
                    int yLoc=(int)round(coords.y)*(int)(imgHeight/imgHeightDeg) - imgHeight;
                    int xmod;
                    //6 level img pyramid needs to be divisible by 64
                    int xWitdth=3*imgWidth + 64; // the plus is so we dont make the width of the image smaller than 3*imgWidth
                    xmod = xWitdth % 64; 
                    xWitdth = xWitdth - xmod;
                    //same for y
                    int yHeight=3*imgHeight + 64;
                    xmod = yHeight%64;
                    yHeight = yHeight - xmod;

                    //edge cases
                    if ((int)round(coords.x)*(int)(imgWidth/imgWidthDeg) - imgWidth < 0) xLoc=0;
                    if ((int)round(coords.y)*(int)(imgHeight/imgHeightDeg) - imgHeight < 0) yLoc = 0;
                    if (xLoc + xWitdth > windowWidth) 
                    {
                        xWitdth = windowWidth - xLoc + 64;
                        xLoc = xLoc -64;
                    }
                    if (yLoc + yHeight > windowHeight) 
                    {
                        yHeight = windowHeight - yLoc +64;
                        yLoc = yLoc - 64;
                    }

                    //create the ROI
                    Mat roi; //we dont need to apply CLAHE to ROI, since it consists of images which have clahe already applied
                    roi = img(Rect(xLoc, yLoc, xWitdth, yHeight));

                    Mat roiEdit, imgSmallEdit;
                    roiEdit = roi.clone();
                    imgSmallEdit = imgSmall.clone();

                    
                    GaussianBlur(roi, roiEdit, Size(3, 3), 0);
                    GaussianBlur(imgSmall, imgSmallEdit, Size(3, 3), 0);

                    Canny(roiEdit, roiEdit, 100, 200, 3, false);
                    Canny(imgSmallEdit, imgSmallEdit, 100, 200, 3, false);

                    //3x3 ball dilation
                    //Mat circleKernel;
                    //circleKernel = getStructuringElement(MORPH_ELLIPSE,cv::Size(5,5));
                    //dilate everything
                    //dilate( roiEdit, roiEdit, circleKernel );
                    //dilate( imgSmallEdit, imgSmallEdit, circleKernel );


                    //Mat rectKernel = getStructuringElement(MORPH_RECT,cv::Size(15,5));
                    //rectKernel = rotate(rectKernel,-81.0);

                    //Mat roiEditOpening, imgSmallEditOpening;
                    //morphologyEx(roiEdit, roiEditOpening, cv::MORPH_OPEN, rectKernel);
                    //morphologyEx(imgSmallEdit, imgSmallEditOpening, cv::MORPH_OPEN, rectKernel);
                    //roiEdit=roiEdit-roiEditOpening;
                    //imgSmallEdit=imgSmallEdit-imgSmallEditOpening;



                    

                    //imshow("roiEdit",roiEdit);
                    //imshow("rectKernel",rectKernel);
                    //imshow("imgSmallEdit",imgSmallEdit);
                    //waitKey(0);
                    cout <<"ROTATE SUCC" <<std::endl;
                    cout <<"ROTATE SIZE " << imgSmallEdit.size() << " NORMAL SIZE: " <<imgSmall.size() <<std::endl;
                    cout <<"ROI SIZE " << roiEdit.size() << " NORMAL SIZE: " <<roi.size() <<std::endl;

                    //vector<Mat> rectHolder;
                    //Mat rect1,rect2,rect3,rect4,rect5;
                    int rectH = imgSmall.rows, rectW = imgSmall.cols;
                    /*
                    Point2d coord1 = Point2d(int(imgWidth*0.1),int(imgHeight*0.1));
                    Point2d coord2 = Point2d(int(imgWidth*0.1),int(imgHeight*0.7));
                    Point2d coord3 = Point2d(int(imgWidth*0.5),int(imgHeight*0.5));
                    Point2d coord4 = Point2d(int(imgWidth*0.7),int(imgHeight*0.2));
                    Point2d coord5 = Point2d(int(imgWidth*0.7),int(imgHeight*0.7));
                    */
                    //vector<Point2d> coordRects;
                    /*
                    coordRects.push_back(coord1);
                    coordRects.push_back(coord2);
                    coordRects.push_back(coord3);
                    coordRects.push_back(coord4);
                    coordRects.push_back(coord5);
                    */
                    
                    /*
                    vector<Point2d> coordRects;
                    vector<Mat> rects;

                    for (int y= 0; y<imgSmall.rows - rectH; y=y+10)
                    {
                        for (int x=0 ; x< imgSmall.cols - rectW; x = x+10)
                        {
                            Point2d currentPoint = Point2d(x,y);
                            coordRects.push_back(currentPoint);

                            rects.push_back(imgSmallEdit(Rect(currentPoint.x,currentPoint.y,rectW,rectH)));
                        }
                    }
                    
                    cout << "RECTS DONE \n";
                    
                    
                    vector<Mat> results;
                    double min_val, max_val;
                    Point min_loc, max_loc;
                    double globMax = 0.0;
                    int minIDX = 0;
                    Mat result;
                    
                    
                    for (int i = 0; i < rects.size(); i++)
                    {
                        
                        matchTemplate(roiEdit,rects[i],result,TM_CCOEFF);
                        results.push_back(result);
                        
                        
                        minMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc);
                        if (max_val>globMax)
                        {
                            globMax=max_val;
                            minIDX = i;
                        }
                    }
                    
                    minMaxLoc(results[minIDX],&min_val,&max_val,&min_loc,&max_loc);
                    
                   
                    cout << "SIZE:"<< results[minIDX].size() << '\n';
                    cout << "IDX:" << minIDX << "\n";
                    */
                    //min_loc is the top left of the best of the five sub-rois
                    //the top left of where to put the small img is min_loc - coordrects[minIDX]

                    Mat result;
                    double min_val, max_val;
                    Point min_loc, max_loc;
                    matchTemplate(roiEdit,imgSmallEdit,result,TM_CCOEFF);
                    minMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc);

                    


                    //debug stuff
                    Mat roiWithLocation, chosenSmallRoiImg;
                    imshow("matchResult",result);
                    roiWithLocation= roiEdit.clone();
                    chosenSmallRoiImg = imgSmallEdit.clone();
                    rectangle(roiWithLocation,max_loc,Point(max_loc.x+rectW,max_loc.y+rectH),Scalar(128,128, 0),2,2,0);
                    rectangle(chosenSmallRoiImg,max_loc,Point(rectW,rectH),Scalar(128,128, 0),2,2,0);
                    imshow("roiWithLocation",roiWithLocation);
                    imshow("chosenSmallRoiImg",chosenSmallRoiImg);

                    //cout<<"HOW MANY RECTS " <<rects.size() <<"\n";
                    cout<<"Location in roi X:" << max_loc.x << " Y " << max_loc.y << "\n";
                    //imshow("chosen smallROI", rects[minIDX]);
                    
                    Mat imgNew;
                    imgNew = Mat::zeros(roi.rows,roi.cols,CV_8U);
                    imgSmall.copyTo(imgNew(Rect(max_loc.x , max_loc.y , rectW,rectH)));

                    //hodnotenie
                    Mat imgEvalSource = imgNew(Rect(max_loc.x, max_loc.y,rectW,rectH));
                    imwrite("hodnotenie/"+to_string(imgIdx)+"src.png",imgEvalSource);

                    //moved to dtBlend function
                    /*
                    Mat roiMask, roiDT, imgWarpedMask,imgDT,roiMaskFinal,imgMaskFinal,imgNew;
                    threshold(roi,roiMask,1,255,CV_8U);
                    imgWarpedMask = Mat::zeros(roi.rows,roi.cols,CV_8U);
                    imgWarpedMask(Rect(max_loc.x,max_loc.y,rectW,rectH))=1.0;


                    distanceTransform(roiMask,roiDT,DIST_L2, 3);
                    distanceTransform(imgWarpedMask,imgDT,DIST_L2, 3);
                    normalize(roiDT, roiDT, 0.0, 1.0, NORM_MINMAX);
                    normalize(imgDT, imgDT, 0.0, 1.0, NORM_MINMAX);

                    cv::divide(roiDT,(roiDT+imgDT),roiMaskFinal);  
                    cv::divide(imgDT,(roiDT+imgDT),imgMaskFinal);  
                    
                    

                    normalize(roiMaskFinal, roiMaskFinal, 0.0, 255.0, NORM_MINMAX);
                    normalize(imgMaskFinal, imgMaskFinal, 0.0, 255.0, NORM_MINMAX);
                    imgNew = Mat::zeros(roi.rows,roi.cols,CV_8U);
                    imgSmall.copyTo(imgNew(Rect(max_loc.x , max_loc.y , rectW,rectH))); 
                    

                    roiMaskFinal.convertTo(roiMaskFinal,CV_8UC1);
                    imgMaskFinal.convertTo(imgMaskFinal,CV_8UC1);
                    roi.convertTo(roi,CV_32FC1);
                    imgNew.convertTo(imgNew,CV_32FC1);
                    roiMaskFinal.convertTo(roiMaskFinal,CV_32FC1);
                    imgMaskFinal.convertTo(imgMaskFinal,CV_32FC1);
                    
                    
                    multiply(roiMaskFinal,roi,roi);
                    cout<<cv::typeToString(imgMaskFinal.type())<< "imgMaskFinal TYPE \n";
                    cout<<cv::typeToString(imgNew.type())<< "imgNew TYPE \n";
                    
                    multiply(imgMaskFinal,imgNew,imgNew);
                    
                    cv::add(roi,imgNew, roi);
                    normalize(roi, roi, 0.0, 1.0, NORM_MINMAX);
                    imshow("roiafter",roi);
                    

                    //uncomment this and comment from neareast Mat in order to disable blending
                    //imgSmall.copyTo(roi(Rect(max_loc.x , max_loc.y , rectW,rectH))); 
                    //imshow("roiafter",roi);


                    normalize(roi, roi, 0.0, 255.0, NORM_MINMAX);
                    roi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));
                    */
                    imshow("roiorig",roi);
                    imshow("imgSmall", imgSmall);
                    Mat newRoi;
                    newRoi = dtBlend(roi,imgNew,false);
                    //newRoi = pyramidBlend(roi,imgNew,true);
                    newRoi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));

                    //hodnotenie
                    Mat imgEvalDst= newRoi(Rect(max_loc.x, max_loc.y,rectW,rectH));
                    imwrite("hodnotenie/"+to_string(imgIdx)+"dst.png",imgEvalDst);
                    imgIdx++;
                    
                    
                    imwrite("roiafter.jpg",newRoi);
                    //waitKey(0);

                   

                    
                    
                    
                   //cout<<CV_VERSION;
                }
                //if(qIdx==2) break;
                
                
                qIdx++;
            }
            logFile.close();
            string resultName = "result" + to_string(imgIdx) +".png";
            //imshow("fag",img);
            //waitKey(0);
            //imwrite(resultName, img);
            //imgIdx++;

            //stitching
            /*
            cout << "Stitching" << std::endl;
            Stitcher::Status status = stitcher->stitch(imgs, pano);
            if (status != Stitcher::OK)
            {
                // Check if images could not be stitched
                // status is OK if images are stitched successfully
                cout << "Can't stitch images\n";
            }
            else
            {

                string resultName = "result" + to_string(imgIdx) +".jpg";

                // Store a new image stitched from the given
                //set of images as "result.jpg"
                imwrite(resultName, pano);
                
                // Show the result
                imshow(resultName, pano);
                
                
                waitKey(0);
            }
            */
            
        }

    imwrite("result.png", img);
    }

























    /**
     * @brief online image stitching using pattern matching
     * saves the result image to the current directory
     * do not load() any database previous to using this function
     * @param imgW source image width
     * @param imgH source image height
     * @param path path to the images
     */
    void onlineStitch(int imgW,int imgH, string path)
    {

        int idx = 0;
        PointBase curr,newPb;
        vector <Point2d> diffVector;
        Mat img;
        int windowHeight,windowWidth;

        
        int imgWidth = imgW;
        int imgHeight = imgH;
        
        windowHeight = (int)(3000*(600/50))+imgHeight*2;
        windowWidth = (int)((360/60)*1200) + imgWidth;
                        

       img = Mat::zeros(windowHeight,windowWidth,CV_8UC1);

        
        


        while (true)
        {
            while (diffVector.empty())
            {
                if (idx ==0)
                {
                    if(curr.load(path) == -1) {continue;} //no points loaded
                    else
                    {
                        diffVector=curr.points;

                        
                    }
                    
                    
                }
                else
                {
                    diffVector.clear();
                    if (newPb.load(path) == -1) {continue;} //no points loaded
                    else
                    {
                        for (int i = 0; i < newPb.points.size(); i++)
                        {
                            if(std::find(curr.points.begin(), curr.points.end(), newPb.points[i]) != curr.points.end()) 
                            {
                                /* v contains x */
                                cout<<"FOUND NOTHING \n";
                            } 
                            else 
                            {
                            diffVector.push_back(newPb.points[i]);
                            cout<<"FOUND\n";
                            }
                        }

                        curr.points=newPb.points;
                        curr.pathMap=newPb.pathMap;
                        idx++;
                    }
                    
                    

                }
                sleep(2);
            }
        
        
        


        



            int qIdx = 0;
            for (Point2d coords: diffVector)
            {
                idx++;
                //log to stdout
                cout << "x: " << coords.x << std::endl;
                cout << "y: " << coords.y << std::endl;
                

                string jsonPath = curr.pathMap[coords];
                curr.printPoints(0);
                
                string pngPath = jsonPath.erase(jsonPath.length()-4);
                
                pngPath = pngPath + "png";
                cout<< "path: " <<path <<std::endl;
                cout << "file: " << pngPath << std::endl;
                
                Mat imgSmall = imread(pngPath,IMREAD_GRAYSCALE);
                resize(imgSmall, imgSmall, Size(imgWidth, imgHeight), INTER_LINEAR);
                

                Ptr<CLAHE> clahe = cv::createCLAHE(4.0, cv::Size(8,8));
                clahe->apply(imgSmall,imgSmall);
                cout<< cv::typeToString( imgSmall.type() );

                
                //cout << (int)round(coords.x)-(int)round(minX)<< "\n";
                //cout << (int)round(coords.y)-(int)round(minY)<< "\n";
                cout << (int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg)<< "\n";

                //we need to calculate ROI first, to decide whether it will be empty - if yes, we can just place the picture at the req. location
                //default ROI location and size
                int xLoc=(int)round(coords.x)*(int)(imgWidth/curr.imgWidthDeg) - imgWidth; //=normalized x-coord. minus 1 width of image
                int yLoc=(int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg) - imgHeight;
                int xWitdth=3*imgWidth;
                int yHeight=3*imgHeight;
                //edge cases
                if ((int)round(coords.x)*(int)(imgWidth/curr.imgWidthDeg) - imgWidth < 0) xLoc=0;
                if ((int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg) - imgHeight < 0) yLoc = 0;
                if (xLoc + xWitdth > windowWidth) xWitdth = windowWidth - xLoc;
                if (yLoc + yHeight > windowHeight) yHeight = windowHeight - yLoc;

                //create the ROI
                Mat roi; //we dont need to apply CLAHE to ROI, since it consists of images which have clahe already applied
                roi = img(Rect(xLoc, yLoc, xWitdth, yHeight));
                imshow("roiprev",roi);
                //waitKey(0);
                cout<<img.size()  <<std::endl;
                //cout<<"AAAAAAAAAAAAAAA\n"  <<std::endl;
                if (countNonZero(roi) < 1) 
                {
                    
                    //imgSmall.copyTo(img(cv::Rect((int)round(coords.x)*(int)(imgWidth/curr.imgWidthDeg),(int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg),imgSmall.cols, imgSmall.rows))); 
                    imgSmall.copyTo(img(cv::Rect(xLoc,yLoc,imgSmall.cols, imgSmall.rows))); 
                    cout<<"DINGUS WAS PLACED \n";
                }
                else {
                    
                    Mat roiEdit, imgSmallEdit;
                    roiEdit = roi.clone();
                    imgSmallEdit = imgSmall.clone();
                    
                    
                    GaussianBlur(roi, roiEdit, Size(3, 3), 0);
                    GaussianBlur(imgSmall, imgSmallEdit, Size(3, 3), 0);

                    Canny(roiEdit, roiEdit, 100, 200, 3, false);
                    Canny(imgSmallEdit, imgSmallEdit, 100, 200, 3, false);

                    //3x3 ball dilation
                    Mat circleKernel;
                    circleKernel = getStructuringElement(MORPH_ELLIPSE,cv::Size(5,5));
                    //dilate everything
                    //dilate( roiEdit, roiEdit, circleKernel );
                    //dilate( imgSmallEdit, imgSmallEdit, circleKernel );
                    morphologyEx(roiEdit, roiEdit, cv::MORPH_OPEN, circleKernel);
                    morphologyEx(roiEdit, roiEdit, cv::MORPH_OPEN, circleKernel);

                    //rotate images, because we cant create a kernel of a tilted line in opencv
                    
                    //Mat rotatedRoi;
                    //rotatedRoi = rotate(roiEdit,81.0);
                    //Mat rotatedImg = rotate(imgSmallEdit,81.0);

                    //Mat rectKernel = getStructuringElement(MORPH_RECT,cv::Size(21,2));
                    //rectKernel = rotate(rectKernel,81.0);
                    //erode(roiEdit,roiEdit,rectKernel);
                    //erode(imgSmallEdit,imgSmallEdit,rectKernel);

                    //imshow("roiEdit",roiEdit);
                    //waitKey(0);
                    cout <<"ROTATE SUCC" <<std::endl;
                    cout <<"ROTATE SIZE " << imgSmallEdit.size() << " NORMAL SIZE: " <<imgSmall.size() <<std::endl;
                    cout <<"ROI SIZE " << roiEdit.size() << " NORMAL SIZE: " <<roi.size() <<std::endl;

                    //vector<Mat> rectHolder;
                    //Mat rect1,rect2,rect3,rect4,rect5;
                    int rectH = imgSmall.rows, rectW = imgSmall.cols;
                    

                    Mat result;
                    double min_val, max_val;
                    Point min_loc, max_loc;
                    matchTemplate(roiEdit,imgSmallEdit,result,TM_CCOEFF);
                    minMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc);


                    //debug stuff
                    Mat roiWithLocation, chosenSmallRoiImg;
                    //imshow("matchResult",result);
                    roiWithLocation= roiEdit.clone();
                    chosenSmallRoiImg = imgSmallEdit.clone();
                    rectangle(roiWithLocation,max_loc,Point(max_loc.x+rectW,max_loc.y+rectH),Scalar(128,128, 0),2,2,0);
                    rectangle(chosenSmallRoiImg,max_loc,Point(rectW,rectH),Scalar(128,128, 0),2,2,0);
                    //imshow("roiWithLocation",roiWithLocation);
                    //imshow("chosenSmallRoiImg",chosenSmallRoiImg);

                    //cout<<"HOW MANY RECTS " <<rects.size() <<"\n";
                    cout<<"Location in roi X:" << max_loc.x << " Y " << max_loc.y << "\n";
                    //imshow("chosen smallROI", rects[minIDX]);
                    
                    Mat imgNew;
                    imgNew = Mat::zeros(roi.rows,roi.cols,CV_8U);
                    imgSmall.copyTo(imgNew(Rect(max_loc.x , max_loc.y , rectW,rectH)));


                    //imshow("roiorig",roi);
                    //imshow("imgSmall", imgSmall);
                    
                    Mat newRoi;
                    //newRoi = dtBlend(roi,imgNew,true);
                    newRoi = dtBlend(roi,imgNew,false);
                    newRoi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));
                    //imshow("newRoi", newRoi);
                    
                    imwrite("roiafter.jpg",newRoi);
                    //waitKey(0);
                    diffVector.pop_back();

                    
                    
                    

                }
                //viewing stuff
                roi = img(Rect(xLoc, yLoc, xWitdth, yHeight));
                imshow("roi",roi);
                Mat dotImage(1200, 360, CV_8UC3,   Scalar(0, 0, 0));
                for(int i =0; i<curr.points.size(); i++)
                {
                    circle(dotImage, Point2d(curr.points[i].x, (curr.points[i].y)/10),3, Scalar(255, 0, 0), 3);
                }
                circle(dotImage, Point2d(coords.x, (coords.y)/10),3, Scalar(0, 0, 255), 3);

                imshow("Location of Images", dotImage);//Showing the circle//
                //imshow("img",img);
                waitKey(10);
                imwrite("result.png", img);
                qIdx++;
            }
            //string resultName = "result" + to_string(imgIdx) +".png";
            
            diffVector.clear();
            
        }
    }
        
        




    /**
     * @brief online image stitching using SIFT
     * saves the result image to the current directory
     * do not load() any database previous to using this function
     * @param imgW source image width
     * @param imgH source image height
     * @param path path to the images
     */
    void onlineStitchSIFT(int imgW,int imgH, string path)
    {

        int idx = 0;
        PointBase curr,newPb;
        vector <Point2d> diffVector;
        Mat img;
        int windowHeight,windowWidth;

        
        int imgWidth = imgW;
        int imgHeight = imgH;
        
        windowHeight = (int)(3000*(600/50))+imgHeight*2;
        windowWidth = (int)((360/60)*1200) + imgWidth;
                        

       img = Mat::zeros(windowHeight,windowWidth,CV_8UC1);

        
        


        while (true)
        {
            while (diffVector.empty())
            {
                if (idx ==0)
                {
                    if(curr.load(path) == -1) {continue;} //no points loaded
                    else
                    {
                        diffVector=curr.points;

                        
                    }
                    
                    
                }
                else
                {
                    diffVector.clear();
                    if (newPb.load(path) == -1) {continue;} //no points loaded
                    else
                    {
                        for (int i = 0; i < newPb.points.size(); i++)
                        {
                            if(std::find(curr.points.begin(), curr.points.end(), newPb.points[i]) != curr.points.end()) 
                            {
                                /* v contains x */
                                cout<<"FOUND NOTHING \n";
                            } 
                            else 
                            {
                            diffVector.push_back(newPb.points[i]);
                            cout<<"FOUND\n";
                            }
                        }

                        curr.points=newPb.points;
                        curr.pathMap=newPb.pathMap;
                        idx++;
                    }
                    
                    

                }
                sleep(2);
            }
        
        
        


        



            int qIdx = 0;
            for (Point2d coords: diffVector)
            {
                idx++;
                //log to stdout
                cout << "x: " << coords.x << std::endl;
                cout << "y: " << coords.y << std::endl;
                

                string jsonPath = curr.pathMap[coords];
                curr.printPoints(0);
                
                string pngPath = jsonPath.erase(jsonPath.length()-4);
                
                pngPath = pngPath + "png";
                cout<< "path: " <<path <<std::endl;
                cout << "file: " << pngPath << std::endl;
                
                Mat imgSmall = imread(pngPath,IMREAD_GRAYSCALE);
                resize(imgSmall, imgSmall, Size(imgWidth, imgHeight), INTER_LINEAR);
                

                Ptr<CLAHE> clahe = cv::createCLAHE(4.0, cv::Size(8,8));
                clahe->apply(imgSmall,imgSmall);
                cout<< cv::typeToString( imgSmall.type() );

                
                //cout << (int)round(coords.x)-(int)round(minX)<< "\n";
                //cout << (int)round(coords.y)-(int)round(minY)<< "\n";
                cout << (int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg)<< "\n";

                //we need to calculate ROI first, to decide whether it will be empty - if yes, we can just place the picture at the req. location
                //default ROI location and size
                int xLoc=(int)round(coords.x)*(int)(imgWidth/curr.imgWidthDeg) - imgWidth; //=normalized x-coord. minus 1 width of image
                int yLoc=(int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg) - imgHeight;
                int xWitdth=3*imgWidth;
                int yHeight=3*imgHeight;
                //edge cases
                if ((int)round(coords.x)*(int)(imgWidth/curr.imgWidthDeg) - imgWidth < 0) xLoc=0;
                if ((int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg) - imgHeight < 0) yLoc = 0;
                if (xLoc + xWitdth > windowWidth) xWitdth = windowWidth - xLoc;
                if (yLoc + yHeight > windowHeight) yHeight = windowHeight - yLoc;

                //create the ROI
                Mat roi; //we dont need to apply CLAHE to ROI, since it consists of images which have clahe already applied
                roi = img(Rect(xLoc, yLoc, xWitdth, yHeight));
                imshow("roiprev",roi);
                //waitKey(0);
                cout<<img.size()  <<std::endl;
                //cout<<"AAAAAAAAAAAAAAA\n"  <<std::endl;
                if (countNonZero(roi) < 1) 
                {
                    
                    //imgSmall.copyTo(img(cv::Rect((int)round(coords.x)*(int)(imgWidth/curr.imgWidthDeg),(int)round(coords.y)*(int)(imgHeight/curr.imgHeightDeg),imgSmall.cols, imgSmall.rows))); 
                    imgSmall.copyTo(img(cv::Rect(xLoc,yLoc,imgSmall.cols, imgSmall.rows))); 
                    cout<<"DINGUS WAS PLACED \n";
                }
                else {

                    //detect the keypoints from both the ROI and the img we want to add
                    cv::Ptr<SIFT> siftPtr = SIFT::create(200);
                    std::vector<KeyPoint> keypointsROI, keypointsImg;
                    cv::Ptr<SiftDescriptorExtractor> siftExtrPtr;
                    cv::Mat descriptorsROI, descriptorsImg;
                    siftPtr->detect(roi, keypointsROI);
                    siftPtr->detect(imgSmall, keypointsImg);



                    //compute descriptors from found keypoints
                    cout<<" SIZE \n"<<cv::typeToString(roi.type()) <<" \n";
                    siftPtr->compute(roi,
                                keypointsROI,
                                descriptorsROI);
                    siftPtr->compute(imgSmall,
                                keypointsImg,
                                descriptorsImg);

                    //match the descriptors
                    cv::FlannBasedMatcher matcher;

                    std::vector<std::vector<cv::DMatch>> matches;
                    matcher.knnMatch(descriptorsROI, descriptorsImg, matches, 2);  // Find 2 nearest matches
                    cv::Mat imageMatches;
                    
                    drawMatches(roi,keypointsROI,imgSmall,keypointsImg,matches,imageMatches);
                    //cv::namedWindow("matches");
                    //cv::imshow("matches",imageMatches);
                    //cv::waitKey(0);


                   cout<<"TU BY MALI BYT MATCHE " <<'\n';
                   std::vector< DMatch > good_matches;
                   
                   float ratio =0.4; //0.15 malo vysledok 0
                   for (int i = 0; i < matches.size(); ++i)
                    {
                            
                        if (matches[i][0].distance < ratio * matches[i][1].distance)
                            {
                                good_matches.push_back(matches[i][0]);
                                cout<<"GOOD MATCH DISTANCE " << matches[i][0].distance <<'\n';
                            }
                    }
                   


                    std::vector< Point2d > obj;
                    std::vector< Point2d > scene;
                    
                    for( int i = 0; i < good_matches.size(); i++ )
                    {
                    //Get the keypoints from the good matches
                    obj.push_back( static_cast<cv::Point2i>( keypointsImg[ good_matches[i].trainIdx ].pt ));
                    scene.push_back(  static_cast<cv::Point2i>(keypointsROI[ good_matches[i].queryIdx ].pt ));
                    }
                    
                    //find a transformation based on good matches
                    //we do not need a Homography, since we deal with affine transformations (no viewport transf. are expected)
                    //Mat H = findHomography( Mat(obj), Mat(scene), RANSAC );
                    Mat H;
                    if(good_matches.size()>=3) H = estimateAffinePartial2D( Mat(obj), Mat(scene), noArray(),RANSAC );
                    

                    //here we check for errors - either there were not enough good matches, OR an affine transformation could not be estimated
                    //we use template matching if errors occured
                    if(good_matches.size()<3 || H.empty())
                    {

                        Mat roiEdit, imgSmallEdit;
                        roiEdit = roi.clone();
                        imgSmallEdit = imgSmall.clone();

                        
                        GaussianBlur(roi, roiEdit, Size(3, 3), 0);
                        GaussianBlur(imgSmall, imgSmallEdit, Size(3, 3), 0);

                        Canny(roiEdit, roiEdit, 100, 200, 3, false);
                        Canny(imgSmallEdit, imgSmallEdit, 100, 200, 3, false);

                        Mat result;
                        double min_val, max_val;
                        Point min_loc, max_loc;
                        matchTemplate(roiEdit,imgSmallEdit,result,TM_CCOEFF);
                        minMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc);


                        
                        Mat imgNew;
                        imgNew = Mat::zeros(roi.rows,roi.cols,CV_8U);
                        imgSmall.copyTo(imgNew(Rect(max_loc.x , max_loc.y , imgSmall.cols,imgSmall.rows))); 

                        Mat newRoi;
                        newRoi = dtBlend(roi,imgNew, false);
                        newRoi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));
                        
                    }
                    else
                    {
                        cout<<"SIFT"<<std::endl;
                        cv::Mat resultWarp;
                        //warpPerspective(imgSmall,roi,H,cv::Size(roi.cols,roi.rows));
                        warpAffine(imgSmall,resultWarp,H,cv::Size(roi.cols,roi.rows));
                       
                        Mat resultRoi;
                        resultRoi = dtBlend(roi,resultWarp,false);
                        resultRoi.copyTo(img(Rect(xLoc, yLoc, xWitdth, yHeight)));
                        
                    }   
                    diffVector.pop_back();

                    
                    
                    

                }
                //viewing stuff
                roi = img(Rect(xLoc, yLoc, xWitdth, yHeight));
                imshow("roi",roi);
                Mat dotImage(1200, 360, CV_8UC3,   Scalar(0, 0, 0));
                for(int i =0; i<curr.points.size(); i++)
                {
                    circle(dotImage, Point2d(curr.points[i].x, (curr.points[i].y)/10),3, Scalar(255, 0, 0), 3);
                }
                circle(dotImage, Point2d(coords.x, (coords.y)/10),3, Scalar(0, 0, 255), 3);

                imshow("Location of Images", dotImage);//Showing the circle//
                //imshow("img",img);
                waitKey(10);
                imwrite("result.png", img);
                qIdx++;
            }
            //string resultName = "result" + to_string(imgIdx) +".png";
            
            diffVector.clear();
            
        }
    }




};





























































































































