Implement function that loads 8bit colored image and converts it to 24bit from code 1 to code 2. Code 1: // bmp.cpp : Defines the entry point for the application.
//

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>

// Define necessary BMP structures with proper packing
#pragma pack(push, 1)
struct BITMAPFILEHEADER {
    uint16_t bfType;      // Signature "BM"
    uint32_t bfSize;      // Size of the file
    uint16_t bfReserved1; // Reserved, must be zero
    uint16_t bfReserved2; // Reserved, must be zero
    uint32_t bfOffBits;   // Offset to start of pixel data
};

struct BITMAPINFOHEADER {
    uint32_t biSize;          // Size of this header (40 bytes)
    int32_t  biWidth;         // Width of the bitmap in pixels
    int32_t  biHeight;        // Height of the bitmap in pixels
    uint16_t biPlanes;        // Number of color planes (must be 1)
    uint16_t biBitCount;      // Number of bits per pixel
    uint32_t biCompression;   // Compression type
    uint32_t biSizeImage;     // Size of the image data
    int32_t  biXPelsPerMeter; // Horizontal resolution (pixels per meter)
    int32_t  biYPelsPerMeter; // Vertical resolution (pixels per meter)
    uint32_t biClrUsed;       // Number of colors in the palette
    uint32_t biClrImportant;  // Important colors (generally ignored)
};

struct RGBTRIPLE {
    uint8_t rgbtBlue;
    uint8_t rgbtGreen;
    uint8_t rgbtRed;
};

struct RGBQUAD {
    uint8_t rgbBlue;
    uint8_t rgbGreen;
    uint8_t rgbRed;
    uint8_t rgbReserved;
};
#pragma pack(pop)

// Function declarations
void CreateBmp555(const std::string& fname, uint16_t color);
void CreateBmp24(const std::string& fname, RGBTRIPLE color);
void CreateBmp8(const std::string& fname, uint8_t color);
bool Convert256To24(const std::string& fin, const std::string& fout);

// Entry point
int main()
{
    uint16_t color1 = 0x1F02; // Example color in 5-5-5 format

    RGBTRIPLE color2;
    color2.rgbtRed = 111;
    color2.rgbtGreen = 20;
    color2.rgbtBlue = 160;

    uint8_t color3 = 4; // Example color index for 8-bit BMP

    CreateBmp555("test1.bmp", color1);
    CreateBmp24("test2.bmp", color2);
    CreateBmp8("test3.bmp", color3);

    if (Convert256To24("test3.bmp", "test4.bmp")) {
        std::cout << "Conversion successful.\n";
    } else {
        std::cerr << "Conversion failed.\n";
    }

    return 0;
}

// Creates a 16-bit BMP with 5-5-5 color depth
void CreateBmp555(const std::string& fname, uint16_t color)
{
    const int Width = 33;
    const int Height = 50;

    BITMAPFILEHEADER bfh = {};
    BITMAPINFOHEADER bih = {};
    std::vector<uint8_t> Palette(1024, 0); // 1KB palette (not used for 16-bit BMP)

    bfh.bfType = 0x4D42; // 'BM'
    bfh.bfOffBits = sizeof(bfh) + sizeof(bih) + Palette.size();
    bfh.bfSize = bfh.bfOffBits + sizeof(color) * Width * Height
                + Height * ((sizeof(color) * Width) % 4);

    bih.biSize = sizeof(bih);
    bih.biWidth = Width;
    bih.biHeight = Height;
    bih.biPlanes = 1;
    bih.biBitCount = 16;
    bih.biCompression = 3; // BI_BITFIELDS
    bih.biSizeImage = sizeof(color) * Width * Height
                     + Height * ((sizeof(color) * Width) % 4);
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;

    std::ofstream ofs(fname, std::ios::binary);
    if (!ofs) {
        std::cerr << "Failed to create " << fname << "\n";
        return;
    }

    // Write headers
    ofs.write(reinterpret_cast<char*>(&bfh), sizeof(bfh));
    ofs.write(reinterpret_cast<char*>(&bih), sizeof(bih));

    // Write palette (not used for 16-bit BMP)
    ofs.write(reinterpret_cast<char*>(Palette.data()), Palette.size());

    // Write pixel data
    std::vector<uint8_t> padding((sizeof(color) * Width) % 4, 0);
    for (int i = 0; i < Height; ++i) {
        for (int j = 0; j < Width; ++j) {
            ofs.write(reinterpret_cast<char*>(&color), sizeof(color));
        }
        // Write padding
        ofs.write(reinterpret_cast<char*>(padding.data()), padding.size());
    }

    ofs.close();
}

// Creates a 24-bit BMP
void CreateBmp24(const std::string& fname, RGBTRIPLE color)
{
    const int Width = 35;
    const int Height = 50;

    BITMAPFILEHEADER bfh = {};
    BITMAPINFOHEADER bih = {};
    std::vector<uint8_t> Palette(1024, 0); // Palette not used for 24-bit BMP

    bfh.bfType = 0x4D42; // 'BM'
    bfh.bfOffBits = sizeof(bfh) + sizeof(bih) + Palette.size();
    bfh.bfSize = bfh.bfOffBits + sizeof(color) * Width * Height
                + Height * (Width % 4); // Padding for 24-bit BMP

    bih.biSize = sizeof(bih);
    bih.biWidth = Width;
    bih.biHeight = Height;
    bih.biPlanes = 1;
    bih.biBitCount = 24;
    bih.biCompression = 0; // BI_RGB
    bih.biSizeImage = sizeof(color) * Width * Height + Height * (Width % 4);
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;

    std::ofstream ofs(fname, std::ios::binary);
    if (!ofs) {
        std::cerr << "Failed to create " << fname << "\n";
        return;
    }

    // Write headers
    ofs.write(reinterpret_cast<char*>(&bfh), sizeof(bfh));
    ofs.write(reinterpret_cast<char*>(&bih), sizeof(bih));

    // Write palette (not used for 24-bit BMP)
    ofs.write(reinterpret_cast<char*>(Palette.data()), Palette.size());

    // Write pixel data
    std::vector<uint8_t> padding(Width % 4, 0);
    for (int i = 0; i < Height; ++i) {
        for (int j = 0; j < Width; ++j) {
            ofs.write(reinterpret_cast<char*>(&color), sizeof(color));
        }
        // Write padding
        ofs.write(reinterpret_cast<char*>(padding.data()), padding.size());
    }

    ofs.close();
}

// Creates an 8-bit BMP with a simple palette
void CreateBmp8(const std::string& fname, uint8_t color)
{
    const int Width = 35;
    const int Height = 50;

    BITMAPFILEHEADER bfh = {};
    BITMAPINFOHEADER bih = {};
    std::vector<RGBQUAD> Palette(256, RGBQUAD{0, 0, 0, 0});

    // Initialize palette with a gradient
    for (int i = 1; i < 256; ++i) {
        Palette[i].rgbBlue  = Palette[i - 1].rgbBlue  + 20;
        Palette[i].rgbGreen = Palette[i - 1].rgbGreen + 30;
        Palette[i].rgbRed   = Palette[i - 1].rgbRed   + 10;
    }

    bfh.bfType = 0x4D42; // 'BM'
    bfh.bfOffBits = sizeof(bfh) + sizeof(bih) + Palette.size() * sizeof(RGBQUAD);
    bfh.bfSize = bfh.bfOffBits + sizeof(uint8_t) * Width * Height
                + Height * ((3 * Width) % 4); // Padding for 8-bit BMP

    bih.biSize = sizeof(bih);
    bih.biWidth = Width;
    bih.biHeight = Height;
    bih.biPlanes = 1;
    bih.biBitCount = 8;
    bih.biCompression = 0; // BI_RGB
    bih.biSizeImage = sizeof(uint8_t) * Width * Height + Height * ((3 * Width) % 4);
    bih.biClrUsed = 256;
    bih.biClrImportant = 0;

    std::ofstream ofs(fname, std::ios::binary);
    if (!ofs) {
        std::cerr << "Failed to create " << fname << "\n";
        return;
    }

    // Write headers
    ofs.write(reinterpret_cast<char*>(&bfh), sizeof(bfh));
    ofs.write(reinterpret_cast<char*>(&bih), sizeof(bih));

    // Write palette
    for (const auto& colorQuad : Palette) {
        ofs.write(reinterpret_cast<const char*>(&colorQuad), sizeof(RGBQUAD));
    }

    // Write pixel data
    std::vector<uint8_t> padding((3 * Width) % 4, 0);
    for (int i = 0; i < Height; ++i) {
        for (int j = 0; j < Width; ++j) {
            ofs.write(reinterpret_cast<char*>(&color), sizeof(color));
        }
        // Write padding
        ofs.write(reinterpret_cast<char*>(padding.data()), padding.size());
    }

    ofs.close();
}

// Converts an 8-bit BMP to a 24-bit BMP using its color palette
bool Convert256To24(const std::string& fin, const std::string& fout)
{
    BITMAPFILEHEADER bfh;
    BITMAPINFOHEADER bih;
    std::vector<RGBQUAD> Palette(256);
    int Width, Height;

    // Open input file
    std::ifstream ifs(fin, std::ios::binary);
    if (!ifs) {
        std::cerr << "Failed to open input file " << fin << "\n";
        return false;
    }

    // Read headers
    ifs.read(reinterpret_cast<char*>(&bfh), sizeof(bfh));
    ifs.read(reinterpret_cast<char*>(&bih), sizeof(bih));

    // Read palette
    ifs.read(reinterpret_cast<char*>(Palette.data()), Palette.size() * sizeof(RGBQUAD));

    Width = bih.biWidth;
    Height = bih.biHeight;

    // Open output file
    std::ofstream ofs(fout, std::ios::binary);
    if (!ofs) {
        std::cerr << "Failed to create output file " << fout << "\n";
        return false;
    }

    // Prepare new headers for 24-bit BMP
    BITMAPFILEHEADER out_bfh = {};
    BITMAPINFOHEADER out_bih = bih;

    out_bih.biBitCount = 24;
    out_bih.biCompression = 0; // BI_RGB
    out_bih.biSizeImage = sizeof(RGBTRIPLE) * Width * Height;

    out_bfh = bfh;
    out_bfh.bfOffBits = sizeof(out_bfh) + sizeof(out_bih);
    out_bfh.bfSize = out_bfh.bfOffBits + out_bih.biSizeImage;

    // Write new headers
    ofs.write(reinterpret_cast<char*>(&out_bfh), sizeof(out_bfh));
    ofs.write(reinterpret_cast<char*>(&out_bih), sizeof(out_bih));

    // Move input file pointer to the start of pixel data
    ifs.seekg(bfh.bfOffBits, std::ios::beg);

    // Read pixel data
    std::vector<uint8_t> inBuf(Width);
    std::vector<RGBTRIPLE> outBuf(Width);

    // Calculate padding for input (8-bit BMP)
    int inputPadding = (4 - (Width % 4)) % 4;

    // Calculate padding for output (24-bit BMP)
    int outputPadding = (4 - (3 * Width) % 4) % 4;
    std::vector<uint8_t> outPad(outputPadding, 0);

    for (int i = 0; i < Height; ++i) {
        // Read one row of pixel indices
        ifs.read(reinterpret_cast<char*>(inBuf.data()), Width);
        // Skip input padding
        ifs.ignore(inputPadding);

        // Convert indices to RGBTRIPLE
        for (int j = 0; j < Width; ++j) {
            outBuf[j].rgbtRed   = Palette[inBuf[j]].rgbRed;
            outBuf[j].rgbtGreen = Palette[inBuf[j]].rgbGreen;
            outBuf[j].rgbtBlue  = Palette[inBuf[j]].rgbBlue;
        }

        // Write converted pixel data
        ofs.write(reinterpret_cast<char*>(outBuf.data()), sizeof(RGBTRIPLE) * Width);
        // Write output padding
        ofs.write(reinterpret_cast<char*>(outPad.data()), outputPadding);
    }

    ifs.close();
    ofs.close();

    return true;
}
// Anisimov Vasiliy <PRESIDIO_ANONYMIZED_EMAIL_ADDRESS>
// Laboratory Work 1

#include "bitmap.h"
#include <cstdint>
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
#include <cmath>
#include <unistd.h>
#include <thread>

// Function to generate a Gaussian kernel
std::vector<std::vector<float> > generateGaussianKernel(int size, float sigma) {
    std::vector<std::vector<float> > kernel(size, std::vector<float>(size));
    float sum = 0.0;
    int halfSize = size / 2;

    // Calculate the Gaussian values for each position in the kernel
    for (int x = -halfSize; x <= halfSize; x++) {
        for (int y = -halfSize; y <= halfSize; y++) {
            float exponent = -(x * x + y * y) / (2 * sigma * sigma);
            float value = exp(exponent) / (2 * M_PI * sigma * sigma);

            kernel[x + halfSize][y + halfSize] = value;
            sum += value;
        }
    }

    // Normalize the kernel to ensure the sum of all values is 1
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            kernel[i][j] /= sum;
        }
    }

    return kernel;
}

// Extract the image name from a full path
std::string getImageName(std::string &imagePath) {
    std::string token;
    std::istringstream tokenStream(imagePath);
    std::string imageName;

    while (std::getline(tokenStream, token, '/')) {
        imageName = token;
    }
    
    return imageName;
}



// Load a BMP image from a file
void Bitmap::load(std::string fileName) {
    std::ifstream file(fileName, std::ios::binary);

    if (!file.is_open()) {
        std::cerr << "Error: Could not open file " << fileName << std::endl;
        exit(1);
    }

    unsigned char fileType[2];
    file.read(reinterpret_cast<char*>(fileType), 2);
    
    if ((fileType[0] != 'B') && (fileType[1] != 'M')) {
        std::cerr << "Error: File is not a BMP file" << std::endl;
        exit(1);
    }

    bmpFileHeader header;
    file.read(reinterpret_cast<char*>(&header), sizeof(bmpFileHeader));

    bmpFileDibInfo dibInfo;
    file.read(reinterpret_cast<char*>(&dibInfo), sizeof(bmpFileDibInfo));

    // Read the color masks if present
    uint32_t redMask = 0x00FF0000;
    uint32_t greenMask = 0x0000FF00;
    uint32_t blueMask = 0x000000FF;
    uint32_t alphaMask = 0xFF000000;

    if (dibInfo.headerSize == 56 || dibInfo.headerSize == 108 || dibInfo.headerSize == 124) {
        file.read(reinterpret_cast<char*>(&redMask), sizeof(redMask));
        file.read(reinterpret_cast<char*>(&greenMask), sizeof(greenMask));
        file.read(reinterpret_cast<char*>(&blueMask), sizeof(blueMask));
        if (dibInfo.headerSize >= 108) {
            file.read(reinterpret_cast<char*>(&alphaMask), sizeof(alphaMask));
        }
    }

    // Seek to the pixel data start position
    file.seekg(header.bmpOffset, std::ios::beg);

    // If it's a 1-bit monochrome image
    if (dibInfo.bitsPerPixel == 1) {
        isGrayscale = true;

        // Read pixel data (each byte contains 8 pixels, packed as bits)
        for (int x = 0; x < dibInfo.height; x++) {
            std::vector<Pixel> pixelRow;
            for (int y = 0; y < (dibInfo.width + 7) / 8; y++) {
                uint8_t byte = file.get();  // Read one byte (8 pixels)
                for (int bit = 0; bit < 8 && y * 8 + bit < dibInfo.width; bit++) {

                    uint8_t colorIndex = (byte >> (7 - bit)) & 0x01;

                    Pixel pixel(0, 0, 0);
                    if (colorIndex == 0) {
                        pixel.blue = 255;
                        pixel.green = 255;
                        pixel.red = 255;
                    }
                    pixelRow.push_back(pixel);
                }
            }
            // Handle padding bytes (each row must be a multiple of 4 bytes)
            file.seekg((4 - ((dibInfo.width + 7) / 8) % 4) % 4, std::ios::cur);

            pixels.insert(pixels.begin(), pixelRow);
        }
    } 
    // Handle 8-bit grayscale
    else if (dibInfo.bitsPerPixel == 8) {
        isGrayscale = true;
        std::vector<std::vector<uint8_t>> palette(256, std::vector<uint8_t>(4));

        for (int x = 0; x < dibInfo.height; x++) {
            std::vector<Pixel> pixelRow;
            for (int y = 0; y < dibInfo.width; y++) {
                uint8_t grayValue = file.get();
                Pixel pixel(grayValue, grayValue, grayValue);
                pixelRow.push_back(pixel);
            }
            file.seekg(dibInfo.width % 4, std::ios::cur);
            pixels.insert(pixels.begin(), pixelRow);
        }
    }
    // Handle 16-bit BMP with custom masks
    else if (dibInfo.bitsPerPixel == 16) {
        isGrayscale = false;
        for (int x = 0; x < dibInfo.height; x++) {
            std::vector<Pixel> pixelRow;
            for (int y = 0; y < dibInfo.width; y++) {
                uint16_t pixelValue = (file.get() | (file.get() << 8));
                uint8_t blue = ((pixelValue & blueMask) >> __builtin_ctz(blueMask)) * (255.0 / ((1 << __builtin_popcount(blueMask)) - 1));
                uint8_t green = ((pixelValue & greenMask) >> __builtin_ctz(greenMask)) * (255.0 / ((1 << __builtin_popcount(greenMask)) - 1));
                uint8_t red = ((pixelValue & redMask) >> __builtin_ctz(redMask)) * (255.0 / ((1 << __builtin_popcount(redMask)) - 1));
                pixelRow.push_back(Pixel(red, green, blue));
            }
            file.seekg(dibInfo.width % 4, std::ios::cur);
            pixels.insert(pixels.begin(), pixelRow);
        }
    }
    // Handle 24-bit RGB
    else if (dibInfo.bitsPerPixel == 24) {
        isGrayscale = false;
        for (int x = 0; x < dibInfo.height; x++) {
            std::vector<Pixel> pixelRow;
            for (int y = 0; y < dibInfo.width; y++) {
                int blue = file.get();
                int green = file.get();
                int red = file.get();
                pixelRow.push_back(Pixel(red, green, blue));
            }
            file.seekg(dibInfo.width % 4, std::ios::cur);
            pixels.insert(pixels.begin(), pixelRow);
        }
    } else {
        std::cerr << "Error: Unsupported BMP format (only 1-bit, 8-bit grayscale, 16-bit, and 24-bit RGB are supported), you image is " << dibInfo.bitsPerPixel << " bits per pixel" << std::endl;
        exit(1);
    }
    std::cout << "Loaded " << getImageName(fileName) << " with " << dibInfo.width << "x" << dibInfo.height << " pixels and " << dibInfo.bitsPerPixel << " bits per pixel" << std::endl;
    file.close();
}

// Write the BMP image to a file
void Bitmap::write(std::string fileName) {
    std::ofstream file(fileName, std::ios::binary);
    if (!file) {
        std::cerr << "Error: Could not open file for writing." << std::endl;
        return;
    }

    file.write("BM", 2);

    bmpFileHeader header = {0};
    header.bmpOffset = 2 + sizeof(bmpFileHeader) + sizeof(bmpFileDibInfo);

    bmpFileDibInfo dibInfo = {0};
    dibInfo.headerSize = sizeof(bmpFileDibInfo);
    dibInfo.width = pixels[0].size();
    dibInfo.height = pixels.size();
    dibInfo.planes = 1;
    
    if (dibInfo.bitsPerPixel == 8) {
        dibInfo.bitsPerPixel = 8;
        dibInfo.compression = 0;
        dibInfo.imageSize = dibInfo.width * dibInfo.height;
        dibInfo.colorsUsed = 256;
    } else {
        dibInfo.bitsPerPixel = 24;
        dibInfo.imageSize = dibInfo.width * dibInfo.height * 3;
        dibInfo.colorsUsed = 0;
    }
    dibInfo.xPixelsPerMeter = 2835;
    dibInfo.yPixelsPerMeter = 2835;
    dibInfo.importantColors = 0;

    if (dibInfo.bitsPerPixel == 8) {
        header.bmpOffset += 256 * 4;  // Color palette (256 colors, 4 bytes each)
        header.fileSize = header.bmpOffset + (dibInfo.width * dibInfo.height + dibInfo.width % 4) * dibInfo.height;
    } else {
        header.fileSize = header.bmpOffset + (dibInfo.width * 3 + dibInfo.width % 4) * dibInfo.height;
    }

    file.write(reinterpret_cast<char*>(&header), sizeof(bmpFileHeader));
    file.write(reinterpret_cast<char*>(&dibInfo), sizeof(bmpFileDibInfo));

    // If 8-bit, write the color palette
    if (dibInfo.bitsPerPixel == 8) {
        for (int i = 0; i < 256; i++) {
            file.put(palette[i][0]); // Blue
            file.put(palette[i][1]); // Green
            file.put(palette[i][2]); // Red
            file.put(0); // Reserved
        }
    }
    

    // Write pixel data
    for (int x = dibInfo.height - 1; x >= 0; x--) {
        const std::vector<Pixel> &rowPixels = pixels[x];
        for (int y = 0; y < dibInfo.width; y++) {
            const Pixel &pixel = rowPixels[y];
            if (dibInfo.bitsPerPixel == 8) {
                // Write 8-bit pixel index
                uint8_t index = findClosestPaletteIndex(pixel);
                file.put(index);
            } else {
                // Write 24-bit color pixel (BGR)
                file.put((unsigned char)(pixel.blue));
                file.put((unsigned char)(pixel.green));
                file.put((unsigned char)(pixel.red));
            }
        }

        // Write padding bytes
        for (int i = 0; i < dibInfo.width % 4; i++) {
            file.put(0);
        }
    }

    file.close();
}

// Helper function to find the closest palette index for a given pixel
uint8_t Bitmap::findClosestPaletteIndex(const Pixel &pixel) {
    uint8_t closestIndex = 0;
    int closestDistance = INT_MAX;
    for (int i = 0; i < 256; i++) {
        int distance = (pixel.red - palette[i][2]) * (pixel.red - palette[i][2]) +
                       (pixel.green - palette[i][1]) * (pixel.green - palette[i][1]) +
                       (pixel.blue - palette[i][0]) * (pixel.blue - palette[i][0]);
        if (distance < closestDistance) {
            closestDistance = distance;
            closestIndex = i;
        }
    }
    return closestIndex;
}


// Rotate the image 90 degrees clockwise or counterclockwise
void Bitmap::rotate(bool clockwise) {
    int32_t width = pixels.size();
    int32_t heigth = pixels[0].size();

    std::vector <std::vector <Pixel> > rotatedPixels(heigth, std::vector <Pixel>(width));

    for (int32_t x = 0; x < width; x++) {
        for (int32_t y = 0; y < heigth; y++) {
            if (clockwise) {
                rotatedPixels[y][width - x - 1] = pixels[x][y];
            } else {
                rotatedPixels[heigth - y - 1][x] = pixels[x][y];
            }
        }
    }
    
    pixels = rotatedPixels;
}


void Bitmap::applyGaussianFilter(const std::vector<std::vector<float>>& kernel, int numThreads) {
    int32_t width = pixels.size();
    int32_t height = pixels[0].size();

    if (numThreads <= 0) {
        numThreads = std::thread::hardware_concurrency();
    }

    int stripHeight = height / numThreads;
    std::vector<std::thread> threads;

    for (int i = 0; i < numThreads; ++i) {
        int startY = i * stripHeight;
        int endY = (i == numThreads - 1) ? height : (i + 1) * stripHeight;
        threads.emplace_back(&Bitmap::applyGaussianFilterThread, this, std::ref(kernel), startY, endY);
    }

    for (auto& thread : threads) {
        thread.join();
    }
}

void Bitmap::applyGaussianFilterThread(const std::vector<std::vector<float>>& kernel, int startY, int endY) {
    int32_t width = pixels.size();
    int32_t height = pixels[0].size();
    int halfKernelSize = kernel.size() / 2;

    for (int x = 0; x < width; x++) {
        for (int y = startY; y < endY; y++) {
            float red = 0.0, green = 0.0, blue = 0.0;

            // Convolve the kernel with the image
            for (int i = -halfKernelSize; i <= halfKernelSize; i++) {
                for (int j = -halfKernelSize; j <= halfKernelSize; j++) {
                    int nx = x + i;
                    int ny = y + j;

                    if (nx < 0) nx = 0;
                    if (nx >= width) nx = width - 1;
                    if (ny < 0) ny = 0;
                    if (ny >= height) ny = height - 1;

                    red += pixels[nx][ny].red * kernel[i + halfKernelSize][j + halfKernelSize];
                    green += pixels[nx][ny].green * kernel[i + halfKernelSize][j + halfKernelSize];
                    blue += pixels[nx][ny].blue * kernel[i + halfKernelSize][j + halfKernelSize];
                }
            }

            pixels[x][y].red = uint8_t(red);
            pixels[x][y].green = uint8_t(green);
            pixels[x][y].blue = uint8_t(blue);
        }
    }
}

// Function to print help message
void printHelp() {
    std::cout << "Usage: main [options] [input_image] [output_dir] [num_threads]" << std::endl;
    std::cout << "Options:" << std::endl;
    std::cout << "  -i <input_image>     Specify the input image file  (default: image.bmp)" << std::endl;
    std::cout << "  -o <output_dir>      Specify the output directory  (default: output/)" << std::endl;
    std::cout << "  -n <num_threads>     Specify the number of threads (default: best possible)" << std::endl;
    std::cout << "  -h                   Display this help message" << std::endl;
    std::cout << "If input_image, output_dir or num_threads are not provided, default values are used." << std::endl;
}

void readArgs(int argc, char* argv[], std::string& inputImage, std::string& outputDir, int& numThreads) {
    numThreads = std::thread::hardware_concurrency();
    int opt;
    // Parse command-line options
    while ((opt = getopt(argc, argv, "i:o:n:h")) != -1) {
        switch (opt) {
            case 'i':
                if ((optarg) && (optarg[0] != '-')) {
                    try {
                        inputImage = optarg; // Set number of threads from command-line argument
                    } catch (const std::invalid_argument& e) {
                        std::cerr << "Error: Invalid argument for -i option." << std::endl;
                        printHelp();
                        exit(1);
                    }
                } else {
                    std::cerr << "Error: -i option requires an argument." << std::endl;
                    printHelp();
                    exit(1);
                }

                break;
            case 'o':
                if ((optarg) && (optarg[0] != '-')) {
                    try {
                        outputDir = optarg; // Set number of threads from command-line argument
                    } catch (const std::invalid_argument& e) {
                        std::cerr << "Error: Invalid argument for -o option." << std::endl;
                        printHelp();
                        exit(1);
                    }
                } else {
                    std::cerr << "Error: -o option requires an argument." << std::endl;
                    printHelp();
                    exit(1);
                }

                break;
            case 'h':
                printHelp();          // Display help message and exit
                exit(0);
            case 'n':
                if (optarg) {
                    try {
                        numThreads = std::stoi(optarg); // Set number of threads from command-line argument
                    } catch (const std::invalid_argument& e) {
                        std::cerr << "Error: Invalid argument for -n option." << std::endl;
                        printHelp();
                        return;
                    } catch (const std::out_of_range& e) {
                        std::cerr << "Error: Argument for -n option is out of range." << std::endl;
                        printHelp();
                        return;
                    }
                } else {
                    std::cerr << "Error: -n option requires an argument." << std::endl;
                    printHelp();
                    exit(1);
                }
                break;
            default:
                printHelp();          // Display help message and exit with error
                return;
        }
    }

    std::cout << "Number of threads set to " << numThreads << std::endl;
    // Check for positional arguments (input image and output directory)
    if (optind < argc) {
        inputImage = argv[optind++];  // Set input image file from positional argument
    }
    if (optind < argc) {
        outputDir = argv[optind++];   // Set output directory from positional argument
    }

    // Ensure the output directory ends with a '/'
    if (outputDir.back() != '/') {
        outputDir += '/';
    }
} Change only load function if you can