C:\Users\yassir\Desktop>cl /EHsc /std:c++17 /I"C:\opencv\build\include" /Fe:ChessImageProcessor.exe chess.cpp /link /LIBPATH:"C:\opencv\build\x64\vc16\lib" opencv_world4100.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.41.34120 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

chess.cpp
Microsoft (R) Incremental Linker Version 14.41.34120.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:ChessImageProcessor.exe
/LIBPATH:C:\opencv\build\x64\vc16\lib
opencv_world4100.lib
chess.obj
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::error(int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,char const *,char const *,int)" (?error@cv@@YAXHABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PBD1H@Z) referenced in function "public: __thiscall cv::MatConstIterator::MatConstIterator(class cv::Mat const *)" (??0MatConstIterator@cv@@QAE@PBVMat@1@@Z)
chess.obj : error LNK2019: unresolved external symbol "public: __thiscall cv::Mat::Mat(void)" (??0Mat@cv@@QAE@XZ) referenced in function "class cv::Mat __cdecl gradientx(class cv::Mat const &)" (?gradientx@@YA?AVMat@cv@@ABV12@@Z)
chess.obj : error LNK2019: unresolved external symbol "public: __thiscall cv::Mat::Mat(class cv::Mat const &)" (??0Mat@cv@@QAE@ABV01@@Z) referenced in function "public: static void __cdecl std::_Default_allocator_traits<class std::allocator<class cv::Mat> >::construct<class cv::Mat,class cv::Mat &>(class std::allocator<class cv::Mat> &,class cv::Mat * const,class cv::Mat &)" (??$construct@VMat@cv@@AAV12@@?$_Default_allocator_traits@V?$allocator@VMat@cv@@@std@@@std@@SAXAAV?$allocator@VMat@cv@@@1@QAVMat@cv@@AAV34@@Z)
chess.obj : error LNK2019: unresolved external symbol "public: __thiscall cv::Mat::Mat(int,int,int,void *,unsigned int)" (??0Mat@cv@@QAE@HHHPAXI@Z) referenced in function "public: __thiscall cv::Mat::Mat<double>(class std::vector<double,class std::allocator<double> > const &,bool)" (??$?0N@Mat@cv@@QAE@ABV?$vector@NV?$allocator@N@std@@@std@@_N@Z)
chess.obj : error LNK2019: unresolved external symbol "public: __thiscall cv::Mat::Mat(class cv::Mat const &,class cv::Range const &,class cv::Range const &)" (??0Mat@cv@@QAE@ABV01@ABVRange@1@1@Z) referenced in function "public: class cv::Mat __thiscall cv::Mat::operator()(class cv::Range,class cv::Range)const " (??RMat@cv@@QBE?AV01@VRange@1@0@Z)
chess.obj : error LNK2019: unresolved external symbol "public: __thiscall cv::Mat::~Mat(void)" (??1Mat@cv@@QAE@XZ) referenced in function "class cv::Mat __cdecl gradientx(class cv::Mat const &)" (?gradientx@@YA?AVMat@cv@@ABV12@@Z)
chess.obj : error LNK2019: unresolved external symbol "public: void __thiscall cv::Mat::copyTo(class cv::_OutputArray const &)const " (?copyTo@Mat@cv@@QBEXABV_OutputArray@2@@Z) referenced in function "public: __thiscall cv::Mat::Mat<double>(class std::vector<double,class std::allocator<double> > const &,bool)" (??$?0N@Mat@cv@@QAE@ABV?$vector@NV?$allocator@N@std@@@std@@_N@Z)
chess.obj : error LNK2019: unresolved external symbol "public: void __thiscall cv::Mat::convertTo(class cv::_OutputArray const &,int,double,double)const " (?convertTo@Mat@cv@@QBEXABV_OutputArray@2@HNN@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "public: bool __thiscall cv::Mat::empty(void)const " (?empty@Mat@cv@@QBE_NXZ) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "public: unsigned int __thiscall cv::Mat::total(void)const " (?total@Mat@cv@@QBEIXZ) referenced in function "public: class cv::MatIterator_<double> __thiscall cv::Mat::end<double>(void)" (??$end@N@Mat@cv@@QAE?AV?$MatIterator_@N@1@XZ)
chess.obj : error LNK2019: unresolved external symbol "public: __thiscall cv::Mat::Mat(class cv::Mat &&)" (??0Mat@cv@@QAE@$$QAV01@@Z) referenced in function "class cv::Mat __cdecl gradientx(class cv::Mat const &)" (?gradientx@@YA?AVMat@cv@@ABV12@@Z)
chess.obj : error LNK2019: unresolved external symbol "public: int __thiscall cv::MatConstIterator::lpos(void)const " (?lpos@MatConstIterator@cv@@QBEHXZ) referenced in function "int __cdecl cv::operator-(class cv::MatConstIterator const &,class cv::MatConstIterator const &)" (??Gcv@@YAHABVMatConstIterator@0@0@Z)
chess.obj : error LNK2019: unresolved external symbol "public: void __thiscall cv::MatConstIterator::seek(int,bool)" (?seek@MatConstIterator@cv@@QAEXH_N@Z) referenced in function "public: class cv::MatConstIterator & __thiscall cv::MatConstIterator::operator++(void)" (??EMatConstIterator@cv@@QAEAAV01@XZ)
chess.obj : error LNK2019: unresolved external symbol "public: void __thiscall cv::MatConstIterator::seek(int const *,bool)" (?seek@MatConstIterator@cv@@QAEXPBH_N@Z) referenced in function "public: __thiscall cv::MatConstIterator::MatConstIterator(class cv::Mat const *)" (??0MatConstIterator@cv@@QAE@PBVMat@1@@Z)
chess.obj : error LNK2019: unresolved external symbol "class cv::MatExpr __cdecl cv::operator-(class cv::Mat const &)" (??Gcv@@YA?AVMatExpr@0@ABVMat@0@@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "class cv::MatExpr __cdecl cv::max(class cv::Mat const &,double)" (?max@cv@@YA?AVMatExpr@1@ABVMat@1@N@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::copyMakeBorder(class cv::_InputArray const &,class cv::_OutputArray const &,int,int,int,int,int,class cv::Scalar_<double> const &)" (?copyMakeBorder@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@HHHHHABV?$Scalar_@N@1@@Z) referenced in function "class std::vector<class cv::Mat,class std::allocator<class cv::Mat> > __cdecl getChessTiles(class cv::Mat const &,class std::vector<int,class std::allocator<int> > const &,class std::vector<int,class std::allocator<int> > const &)" (?getChessTiles@@YA?AV?$vector@VMat@cv@@V?$allocator@VMat@cv@@@std@@@std@@ABVMat@cv@@ABV?$vector@HV?$allocator@H@std@@@2@1@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::reduce(class cv::_InputArray const &,class cv::_OutputArray const &,int,int,int)" (?reduce@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@HHH@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "class cv::Mat __cdecl cv::imread(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int)" (?imread@cv@@YA?AVMat@1@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "bool __cdecl cv::imwrite(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class cv::_InputArray const &,class std::vector<int,class std::allocator<int> > const &)" (?imwrite@cv@@YA_NABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABV_InputArray@1@ABV?$vector@HV?$allocator@H@std@@@3@@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "class cv::Mat __cdecl cv::getGaussianKernel(int,double,int)" (?getGaussianKernel@cv@@YA?AVMat@1@HNH@Z) referenced in function "class std::tuple<class std::vector<int,class std::allocator<int> >,class std::vector<int,class std::allocator<int> >,bool> __cdecl getChessLines(class std::vector<double,class std::allocator<double> > const &,class std::vector<double,class std::allocator<double> > const &,double,double,class cv::Size_<int> const &)" (?getChessLines@@YA?AV?$tuple@V?$vector@HV?$allocator@H@std@@@std@@V12@_N@std@@ABV?$vector@NV?$allocator@N@std@@@2@0NNABV?$Size_@H@cv@@@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::filter2D(class cv::_InputArray const &,class cv::_OutputArray const &,int,class cv::_InputArray const &,class cv::Point_<int>,double,int)" (?filter2D@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@H0V?$Point_@H@1@NH@Z) referenced in function "class std::tuple<class std::vector<int,class std::allocator<int> >,class std::vector<int,class std::allocator<int> >,bool> __cdecl getChessLines(class std::vector<double,class std::allocator<double> > const &,class std::vector<double,class std::allocator<double> > const &,double,double,class cv::Size_<int> const &)" (?getChessLines@@YA?AV?$tuple@V?$vector@HV?$allocator@H@std@@@std@@V12@_N@std@@ABV?$vector@NV?$allocator@N@std@@@2@0NNABV?$Size_@H@cv@@@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::Sobel(class cv::_InputArray const &,class cv::_OutputArray const &,int,int,int,int,double,double,int)" (?Sobel@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@HHHHNNH@Z) referenced in function "class cv::Mat __cdecl gradientx(class cv::Mat const &)" (?gradientx@@YA?AVMat@cv@@ABV12@@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::resize(class cv::_InputArray const &,class cv::_OutputArray const &,class cv::Size_<int>,double,double,int)" (?resize@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@V?$Size_@H@1@NNH@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::equalizeHist(class cv::_InputArray const &,class cv::_OutputArray const &)" (?equalizeHist@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
chess.obj : error LNK2019: unresolved external symbol "void __cdecl cv::cvtColor(class cv::_InputArray const &,class cv::_OutputArray const &,int,int)" (?cvtColor@cv@@YAXABV_InputArray@1@ABV_OutputArray@1@HH@Z) referenced in function "void __cdecl process_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?process_image@@YAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)
C:\opencv\build\x64\vc16\lib\opencv_world4100.lib : warning LNK4272: library machine type 'x64' conflicts with target machine type 'x86'
ChessImageProcessor.exe : fatal error LNK1120: 26 unresolved externals : // Include necessary headers
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <filesystem> // C++17 feature; if not available, use alternative methods
#include <cmath>
#include <algorithm>
#include <numeric> // Include this header for std::adjacent_difference

namespace fs = std::filesystem; // For filesystem operations

// Function definitions

cv::Mat gradientx(const cv::Mat &img)
{
    // Compute gradient in x-direction using larger Sobel kernel
    cv::Mat grad_x;
    cv::Sobel(img, grad_x, CV_32F, 1, 0, 31);
    return grad_x;
}

cv::Mat gradienty(const cv::Mat &img)
{
    // Compute gradient in y-direction using larger Sobel kernel
    cv::Mat grad_y;
    cv::Sobel(img, grad_y, CV_32F, 0, 1, 31);
    return grad_y;
}

bool checkMatch(const std::vector<int> &lineset)
{
    std::vector<int> linediff(lineset.size());
    std::adjacent_difference(lineset.begin(), lineset.end(), linediff.begin());
    linediff.erase(linediff.begin()); // Remove the first element which is not valid

    int x = 0;
    int cnt = 0;
    for (const auto &line : linediff)
    {
        if (std::abs(line - x) < 5)
        {
            cnt++;
        }
        else
        {
            cnt = 0;
            x = line;
        }
    }
    return cnt == 5;
}

std::vector<int> pruneLines(std::vector<int> lineset, int image_dim, int margin = 20)
{
    // Remove lines near the margins
    lineset.erase(
        std::remove_if(lineset.begin(), lineset.end(), [margin, image_dim](int x)
                       { return x <= margin || x >= image_dim - margin; }),
        lineset.end());

    if (lineset.empty())
    {
        return lineset;
    }

    std::vector<int> linediff(lineset.size());
    std::adjacent_difference(lineset.begin(), lineset.end(), linediff.begin());
    linediff.erase(linediff.begin()); // Remove the first element which is not valid

    int x = 0;
    int cnt = 0;
    int start_pos = 0;
    for (size_t i = 0; i < linediff.size(); ++i)
    {
        int line = linediff[i];
        if (std::abs(line - x) < 5)
        {
            cnt++;
            if (cnt == 5)
            {
                size_t end_pos = i + 2;
                return std::vector<int>(lineset.begin() + start_pos, lineset.begin() + end_pos);
            }
        }
        else
        {
            cnt = 0;
            x = line;
            start_pos = i;
        }
    }
    return lineset;
}

std::vector<double> skeletonize_1d(const std::vector<double> &arr)
{
    std::vector<double> _arr = arr;

    for (size_t i = 0; i < _arr.size() - 1; ++i)
    {
        if (_arr[i] <= _arr[i + 1])
        {
            _arr[i] = 0;
        }
    }
    for (size_t i = _arr.size() - 1; i > 0; --i)
    {
        if (_arr[i - 1] > _arr[i])
        {
            _arr[i] = 0;
        }
    }
    return _arr;
}

std::tuple<std::vector<int>, std::vector<int>, bool> getChessLines(const std::vector<double> &hdx, const std::vector<double> &hdy,
                                                                   double hdx_thresh, double hdy_thresh, const cv::Size &image_shape)
{
    // Generate Gaussian window
    int window_size = 21;
    double sigma = 8.0;
    cv::Mat gaussMat = cv::getGaussianKernel(window_size, sigma, CV_64F);
    std::vector<double> gausswin(gaussMat.begin<double>(), gaussMat.end<double>());

    // Threshold signals
    std::vector<double> hdx_thresh_binary(hdx.size());
    std::vector<double> hdy_thresh_binary(hdy.size());

    std::transform(hdx.begin(), hdx.end(), hdx_thresh_binary.begin(),
                   [hdx_thresh](double val)
                   { return val > hdx_thresh ? 1.0 : 0.0; });
    std::transform(hdy.begin(), hdy.end(), hdy_thresh_binary.begin(),
                   [hdy_thresh](double val)
                   { return val > hdy_thresh ? 1.0 : 0.0; });

    // Blur signals using convolution with Gaussian window
    std::vector<double> blur_x(hdx_thresh_binary.size());
    std::vector<double> blur_y(hdy_thresh_binary.size());

    cv::Mat hdx_thresh_binary_mat(hdx_thresh_binary);
    cv::Mat hdy_thresh_binary_mat(hdy_thresh_binary);
    cv::Mat gausswin_mat(gausswin);

    cv::filter2D(hdx_thresh_binary_mat, blur_x, -1, gausswin_mat);
    cv::filter2D(hdy_thresh_binary_mat, blur_y, -1, gausswin_mat);

    // Skeletonize signals
    std::vector<double> skel_x = skeletonize_1d(blur_x);
    std::vector<double> skel_y = skeletonize_1d(blur_y);

    // Find line positions
    std::vector<int> lines_x;
    std::vector<int> lines_y;

    for (size_t i = 0; i < skel_x.size(); ++i)
    {
        if (skel_x[i] > 0)
            lines_x.push_back(static_cast<int>(i));
    }
    for (size_t i = 0; i < skel_y.size(); ++i)
    {
        if (skel_y[i] > 0)
            lines_y.push_back(static_cast<int>(i));
    }

    // Prune lines
    lines_x = pruneLines(lines_x, image_shape.width);
    lines_y = pruneLines(lines_y, image_shape.height);

    // Check if lines match expected pattern
    bool is_match = (lines_x.size() == 7) && (lines_y.size() == 7) &&
                    checkMatch(lines_x) && checkMatch(lines_y);

    return {lines_x, lines_y, is_match};
}

std::vector<cv::Mat> getChessTiles(const cv::Mat &img, const std::vector<int> &lines_x, const std::vector<int> &lines_y)
{
    int stepx = static_cast<int>(std::round(static_cast<double>(lines_x[1] - lines_x[0])));
    int stepy = static_cast<int>(std::round(static_cast<double>(lines_y[1] - lines_y[0])));

    // Pad the image if necessary
    int padl_x = 0, padr_x = 0, padl_y = 0, padr_y = 0;
    if (lines_x.front() - stepx < 0)
    {
        padl_x = std::abs(lines_x.front() - stepx);
    }
    if (lines_x.back() + stepx > img.cols - 1)
    {
        padr_x = lines_x.back() + stepx - img.cols + 1;
    }
    if (lines_y.front() - stepy < 0)
    {
        padl_y = std::abs(lines_y.front() - stepy);
    }
    if (lines_y.back() + stepy > img.rows - 1)
    {
        padr_y = lines_y.back() + stepy - img.rows + 1;
    }
    cv::Mat img_padded;
    cv::copyMakeBorder(img, img_padded, padl_y, padr_y, padl_x, padr_x, cv::BORDER_REPLICATE);

    std::vector<int> setsx{lines_x.front() - stepx + padl_x};
    std::vector<int> setsy{lines_y.front() - stepy + padl_y};
    for (const auto &x : lines_x)
        setsx.push_back(x + padl_x);
    setsx.push_back(lines_x.back() + stepx + padl_x);
    for (const auto &y : lines_y)
        setsy.push_back(y + padl_y);
    setsy.push_back(lines_y.back() + stepy + padl_y);

    std::vector<cv::Mat> squares;
    for (int j = 0; j < 8; ++j)
    {
        for (int i = 0; i < 8; ++i)
        {
            int x1 = setsx[i];
            int x2 = setsx[i + 1];
            int y1 = setsy[j];
            int y2 = setsy[j + 1];
            // Adjust sizes to ensure squares are of equal size
            if ((x2 - x1) != stepx)
            {
                x2 = x1 + stepx;
            }
            if ((y2 - y1) != stepy)
            {
                y2 = y1 + stepy;
            }
            cv::Mat square = img_padded(cv::Range(y1, y2), cv::Range(x1, x2));
            squares.push_back(square);
        }
    }
    return squares;
}

void process_image(const std::string &image_path, const std::string &output_dir)
{
    // Load the image
    cv::Mat image = cv::imread(image_path);
    if (image.empty())
    {
        std::cout << "Failed to load image: " << image_path << std::endl;
        return;
    }

    // Convert to grayscale
    cv::Mat gray;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);

    // Preprocessing
    cv::Mat equ;
    cv::equalizeHist(gray, equ);
    cv::Mat norm_image;
    equ.convertTo(norm_image, CV_32F, 1.0 / 255.0);

    // Compute the gradients
    cv::Mat grad_x = gradientx(norm_image);
    cv::Mat grad_y = gradienty(norm_image);

    // Clip the gradients
    cv::Mat Dx_pos = cv::max(grad_x, 0);
    cv::Mat Dx_neg = cv::max(-grad_x, 0);
    cv::Mat Dy_pos = cv::max(grad_y, 0);
    cv::Mat Dy_neg = cv::max(-grad_y, 0);

    // Compute the Hough transform
    cv::Mat sum_Dx_pos, sum_Dx_neg, sum_Dy_pos, sum_Dy_neg;
    cv::reduce(Dx_pos, sum_Dx_pos, 0, cv::REDUCE_SUM, CV_64F);
    cv::reduce(Dx_neg, sum_Dx_neg, 0, cv::REDUCE_SUM, CV_64F);
    cv::reduce(Dy_pos, sum_Dy_pos, 1, cv::REDUCE_SUM, CV_64F);
    cv::reduce(Dy_neg, sum_Dy_neg, 1, cv::REDUCE_SUM, CV_64F);

    double norm_factor_x = static_cast<double>(norm_image.rows * norm_image.rows);
    double norm_factor_y = static_cast<double>(norm_image.cols * norm_image.cols);

    std::vector<double> hough_Dx;
    std::vector<double> hough_Dy;
    for (int i = 0; i < sum_Dx_pos.cols; ++i)
    {
        double val = (sum_Dx_pos.at<double>(0, i) * sum_Dx_neg.at<double>(0, i)) / norm_factor_x;
        hough_Dx.push_back(val);
    }
    for (int i = 0; i < sum_Dy_pos.rows; ++i)
    {
        double val = (sum_Dy_pos.at<double>(i, 0) * sum_Dy_neg.at<double>(i, 0)) / norm_factor_y;
        hough_Dy.push_back(val);
    }

    // Adaptive thresholding
    int a = 1;
    bool is_match = false;
    std::vector<int> lines_x;
    std::vector<int> lines_y;

    while (a < 5)
    {
        double threshold_x = *std::max_element(hough_Dx.begin(), hough_Dx.end()) * (static_cast<double>(a) / 5.0);
        double threshold_y = *std::max_element(hough_Dy.begin(), hough_Dy.end()) * (static_cast<double>(a) / 5.0);

        std::tie(lines_x, lines_y, is_match) = getChessLines(hough_Dx, hough_Dy, threshold_x, threshold_y, norm_image.size());

        if (is_match)
        {
            break;
        }
        else
        {
            a++;
        }
    }

    if (is_match)
    {
        std::cout << "7 horizontal and vertical lines found, slicing up squares" << std::endl;
        std::vector<cv::Mat> squares = getChessTiles(gray, lines_x, lines_y);
        std::cout << "Tiles generated: (" << squares[0].rows << "x" << squares[0].cols << ") * " << squares.size() << std::endl;

        // Extract filename and FEN (assuming filename is FEN)
        std::string img_filename = fs::path(image_path).filename().string();
        std::string fen = fs::path(img_filename).stem().string();
        std::string img_save_dir = (fs::path(output_dir) / fen).string();

        if (!fs::exists(img_save_dir))
        {
            fs::create_directories(img_save_dir);
            std::cout << "Created dir " << img_save_dir << std::endl;
        }

        std::string letters = "ABCDEFGH";
        for (size_t i = 0; i < squares.size(); ++i)
        {
            std::string filename = fen + "_" + letters[i % 8] + std::to_string((i / 8) + 1) + ".png";
            std::string save_path = (fs::path(img_save_dir) / filename).string();
            if (i % 8 == 0)
            {
                std::cout << "#" << i << ": saving " << save_path << "..." << std::endl;
            }
            // Resize to 32x32 and save
            cv::Mat resized;
            cv::resize(squares[i], resized, cv::Size(32, 32), 0, 0, cv::INTER_AREA);
            cv::imwrite(save_path, resized);
        }
    }
    else
    {
        std::cout << "No squares to save for " << image_path << std::endl;
    }
}

// Main code

int main()
{
    // Specify input and output directories
    std::string input_folder = "train_images"; // Replace with your input folder
    std::string output_folder = "tiles";       // Replace with your desired output folder

    // Create output directory if it doesn't exist
    fs::create_directories(output_folder);

    // Process images in the input folder
    for (const auto &entry : fs::directory_iterator(input_folder))
    {
        if (entry.is_regular_file())
        {
            std::string filename = entry.path().filename().string();
            std::string extension = entry.path().extension().string();
            std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);

            if (extension == ".png" || extension == ".jpg" || extension == ".jpeg")
            {
                std::string image_path = entry.path().string();
                process_image(image_path, output_folder);
            }
        }
    }

    return 0;
}