Convolution of images not giving expected result

34 Views Asked by At

I'm writing some code to apply a series of kernels to a given image. A .pgm image path is given by the user, aswell as a path to a text file, which contains a series of kernels to be applied to the image. This is how the image file is written. The text file is written as follows:

3    //This is the number of kernels that are contained
1    //This is the scaling constant for this kernel. The result of the sum is multiplied by this.
3 3   //These are the x and y dimensions
0 -1 0   //This would be the matrix
-1 5 -1
0 -1 0
0.11111   //Here starts the next kernel; with the scaling constant
3 3
1 1 1
1 1 1
1 1 1
0.003906   //And the last one
5 5
1 4 6 4 1
4 16 24 16 4
6 24 36 24 6
4 16 24 16 4
1 4 6 4 1

For this project, we are iterating through the inner image matrix(ignoring borders, so, naturally, the output image will be smaller. And that's why we are applying a padding.)

I've already made and tried the functions to upload the image, the kernels, set the padding(zeroes and repplication) and save the new file. They all seem to work as expected. However, the function that does the convolution does not work, despite multiple attempts, using different approaches. The last one I tried seems to be the one that works the best, however, it still does not do the work. For example, when passing this image: Original Image, with this kernel:

3
1
3 3
0 -1 0
-1 5 -1
0 -1 0
0.11111
3 3
1 1 1
1 1 1
1 1 1
0.003906
5 5
1 4 6 4 1
4 16 24 16 4
6 24 36 24 6
4 16 24 16 4
1 4 6 4 1

The expected result would be this: expected result. However, I'm getting this: actual result.

Compiler shows no errors tho. This is my code. The only function where I need help is "aplicarKernel". Just some tips on the logic would be more than enough.

#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

//TODO: Borrar memoria dinamica en todo lado

struct Kernel{
    float escalar;
    int filas;
    int columnas;
    int **matriz;

    Kernel(){}

    Kernel(float escalar, int filas, int columnas){
        this->escalar = escalar;
        this->filas = filas;
        this->columnas = columnas;
        this->matriz = new int*[columnas];
        for(int i = 0; i<columnas; i++){
            this->matriz[i] = new int[filas];
        }
    }
};

int** subirImg(string rutaImg, int* filas, int* columnas);
Kernel* subirKernel(string rutaKernel, int* numeroKernels);
int** aplicarKernel(int** matrizImg, Kernel* kernels, int filasImagen, int columnasImagen, int numKernels, int**matrizConPadding);
int calculoMatrices(int** matrizImg, int** matrizKernel, int filasKernel, int columnasKernel);
int** agregarPadding(int opcion,int** matrizImg, int filasImagen, int columnasImagen);
int** zeroPadding(int** matrizImg, int* filasImg, int* columnasImg);
int** replicationPadding(int** matrizImg, int* filasImg, int* columnasImg);
void almacenarNuevaImagen(int** matrizImg, int filasImg, int columnasImg);

int main(){
    string rutaImg, rutaKernel,valAux;
    int filasImagen, columnasImagen,numKernels, opcionPadding;
    int filasConPadding, columnasConPadding;
    int** matrizImg;
    int** matrizConPadding;
    Kernel* kernels;

    cout<<"\tBienvenido\nIngrese ruta de la imagen:\n";
    getline(cin, rutaImg);
    matrizImg = subirImg(rutaImg, &filasImagen, &columnasImagen);

    filasConPadding = filasImagen+2;
    columnasConPadding = columnasImagen+2;

    cout<<"Ingrese ruta kernel:\n";
    getline(cin, rutaKernel);
    kernels = subirKernel(rutaKernel,&numKernels);


    cout<<"Desea agregar padding de ceros(0) o de replicacion(1):\n";
    getline(cin, valAux);
    opcionPadding = stoi(valAux);

    //TODO: Solucionar problema al agregar padding en aplicacion de kernel*/
    matrizConPadding = agregarPadding(opcionPadding,matrizImg,filasImagen,columnasImagen);

    /*cout<<"Matriz:\n"<<" "<<filasImagen<<" "<<columnasImagen<<endl;
   for(int i = 0; i<filasImagen+2;i++){
       for(int j = 0; j<columnasImagen+2; j++){
           cout<<matrizConPadding[i][j]<<" ";
       }
       cout<<endl;
   }
   cout<<endl;*/
    /* cout << "Kernels: " << endl;
   for (int i = 0; i < numKernels; ++i) {
       cout << "Kernel " << i + 1 << ":" << endl;
       cout << "Escalar: " << kernels[i].escalar << endl;
       cout << "Filas: " << kernels[i].filas << ", Columnas: " << kernels[i].columnas << endl;
       cout << "Matriz:" << endl;
       for (int j = 0; j < kernels[i].filas; ++j) {
           for (int k = 0; k < kernels[i].columnas; ++k) {
               cout << kernels[i].matriz[j][k] << " ";
           }
           cout << endl;
       }
       cout << endl;
   }
   cout<<endl;
*/
    cout<<"Aplicando kernel...\n";
    matrizImg = aplicarKernel(matrizImg,kernels,filasImagen,columnasImagen,numKernels,matrizConPadding);
    cout<<"Guardando imagen convulcionada en mismo directorio...\n";
    almacenarNuevaImagen(matrizImg,filasImagen,columnasImagen);
    return 0;
}

int** subirImg(string rutaImg, int* filas, int* columnas){
    int filasImg, columnasImg;
    int** matrizImg;
    string valAux;

    ifstream imagen("./"+rutaImg);

    if(!imagen.is_open()){
        cout<<"La imagen no ha podido abrirse\n";
        exit(1);
    }
    cout<<"Imagen abierta correctamente\n";

    getline(imagen,valAux);
    getline(imagen, valAux);
    getline(imagen, valAux, ' ');
    filasImg = stoi(valAux);
    getline(imagen, valAux, '\n');
    columnasImg = stoi(valAux);
    getline(imagen, valAux);


    matrizImg = new int* [columnasImg];
    for(int i = 0; i<columnasImg;i++){
        matrizImg[i]= new int [filasImg];
    }


    stringstream flujo_imagen;
    flujo_imagen<<imagen.rdbuf();
    imagen.close();

    string linea;
    int i = 0, j=0;
    while(getline(flujo_imagen,linea)){
        stringstream buffer_linea(linea);
        string valAux;
        while(buffer_linea>>valAux){
            matrizImg[i][j]= stoi(valAux);
            j++;
            if(j==columnasImg){
                j = 0;
                i++;
            }
        }

    }

    cout<<"PRUEBAAAAAA\n";
    for(int i = 0 ; i<filasImg;i++){
        for(int j = 0; j<columnasImg; j++){
            cout<<matrizImg[i][j]<<" ";
        }
        cout<<endl;
    }
    cout<<"PRUEBAAAAAA\n";

    *filas = filasImg;
    *columnas = columnasImg;
    return matrizImg;
}

Kernel* subirKernel(string rutaKernel, int* numeroKernels){
    int numKernels;

    string valAux;

    Kernel* kernels;

    ifstream archivo_kernel("./"+rutaKernel);
    if(!archivo_kernel.is_open()){
        cout<<"No se ha podido abrir el archivo\n";
        exit(1);
    }
    getline(archivo_kernel, valAux);
    numKernels = stoi(valAux);

    *numeroKernels = numKernels;

    kernels = new Kernel[numKernels];


    for(int i = 0; i < numKernels; i++){
        int filasKernel,columnasKernel;
        float escalar;
        string valAux;

        getline(archivo_kernel,valAux);
        escalar = stof(valAux);
        getline(archivo_kernel, valAux, ' ');
        filasKernel = stoi(valAux);
        getline(archivo_kernel, valAux, '\n');
        columnasKernel = stoi(valAux);

        kernels[i]= Kernel(escalar,filasKernel,columnasKernel);

        for(int ii = 0;ii<filasKernel;ii++){
            getline(archivo_kernel,valAux);
            stringstream buffer_linea(valAux);
            for(int jj=0; jj<columnasKernel;jj++){
                buffer_linea>>valAux;
                kernels[i].matriz[ii][jj] = stoi(valAux);
            }
        }
    }

    cout<<"PRUEBAAAAAA\n";
    for(int x = 0; x<numKernels; x++){
        for(int i = 0 ; i<kernels[x].filas;i++){
            for(int j = 0; j<kernels[x].columnas; j++){
                cout<<kernels[x].matriz[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    cout<<"PRUEBAAAAAA\n";

    return kernels;
}

int** aplicarKernel(int** matrizImg, Kernel* kernels, int filasImagen, int columnasImagen, int numKernels, int**matrizConPadding) {

    // Create a copy of the original image matrix to store the convolved image
    int** matrizCopia = new int*[filasImagen];
    for (int i = 0; i < filasImagen; i++) {
        matrizCopia[i] = new int[columnasImagen]();
    }

    // Loop through each kernel
    for (int i = 0; i < numKernels; i++) {
        int filasKernel = kernels[i].filas;
        int columnasKernel = kernels[i].columnas;
        float escalar = kernels[i].escalar;

        // Loop through each pixel of the image matrix
        for (int y = 0; y < filasImagen; y++) {
            for (int x = 0; x < columnasImagen; x++) {
                // Apply the kernel to the image matrix at position (x, y)
                int sum = 0;
                for (int ky = 0; ky < filasKernel; ky++) {
                    for (int kx = 0; kx < columnasKernel; kx++) {
                        // Ensure that the kernel is applied within the bounds of the image
                        int imgX = x + kx - filasKernel / 2;
                        int imgY = y + ky - columnasKernel / 2;
                        if (imgX >= 0 && imgX < columnasImagen && imgY >= 0 && imgY < filasImagen) {
                            sum += matrizImg[imgY][imgX] * kernels[i].matriz[ky][kx];
                        }
                    }
                }
                // Apply the scaling factor and update the convolved image matrix
                matrizCopia[y][x] += sum * escalar;
            }
        }
        for(int u = 0; u<filasImagen; u++){
            for(int v = 0; v<columnasImagen; v++){
                if(matrizImg[u][v]>255){
                    matrizImg[u][v] = 255;
                    //Doesn't seem to work every time
                }
                else{
                    matrizImg[u][v] = matrizCopia[u][v];
                }
            }
        }
    }

/*    //Put the convoluted matrix inside the padded matrix. Code doesn't work with this.
    int auxFilas = 0,auxColumnas =0;
    for(int i = 1; i<filasImagen-1; i++){
        for(int j = 1;j<columnasImagen-1;j++){
            matrizConPadding[i][j]= matrizImg[auxFilas][auxColumnas];
            auxFilas++;
            auxColumnas++;
        }
    }*/

    // Output the convolved image matrix. Ideally, should output the padded matrix
    return matrizImg;
}



int calculoMatrices(int** matrizImg, int** matrizKernel, int filasKernel, int columnasKernel){
    int suma=0;
    for(int i =0; i<filasKernel; i++){
        for(int j = 0; j<columnasKernel; j++){
            suma += matrizImg[i][j] * matrizKernel[i][j];
        }
    }
    return suma;
}

int** agregarPadding(int opcion,int** matrizImg, int filasImagen, int columnasImagen){
    int nFilas, nColumnas;
    switch (opcion) {
        case 0:
            cout<<"Aplicando zero padding...\n";
            matrizImg= zeroPadding(matrizImg,&filasImagen,&columnasImagen);
            break;
        case 1:
            cout<<"Aplicando replication padding...\n";
            matrizImg = replicationPadding(matrizImg,&filasImagen,&columnasImagen);
            break;
        default:
            cout<<"Opcion no disponible\n";
            exit(1);
    }
    cout<<"Padding aplicado correctamente\n";
    return matrizImg;

}

int** zeroPadding(int** matrizImg, int* filasImg, int* columnasImg){
    int nuevasFilas=(*filasImg)+2, nuevasColumnas=(*columnasImg)+2;
    int** matrizConPadding = new int*[nuevasFilas];
    for(int i = 0; i<nuevasFilas;i++) {
        matrizConPadding[i] = new int[nuevasColumnas];
    }

    for(int i = 0; i<nuevasFilas;i++){
        for(int j = 0; j<nuevasColumnas;j++){
            if(i==0||i==nuevasFilas-1||j==0||j==nuevasColumnas-1){
                matrizConPadding[i][j]=0;
            }
            else{
                matrizConPadding[i][j]=matrizImg[i-1][j-1];
            }
        }
    }
    *filasImg = nuevasFilas;
    *columnasImg = nuevasColumnas;

    cout<<"PRUEBAAAAAA\n";
    for(int i = 0 ; i<nuevasFilas;i++){
        for(int j = 0; j<nuevasColumnas; j++){
            cout<<matrizConPadding[i][j]<<" ";
        }
        cout<<endl;
    }
    cout<<"PRUEBAAAAAA\n";

    return matrizConPadding;
}

int** replicationPadding(int** matrizImg, int* filasImg, int* columnasImg){
    int nuevasFilas=(*filasImg)+2, nuevasColumnas=(*columnasImg)+2;
    int** matrizConPadding = new int*[nuevasFilas];
    for(int i = 0; i<nuevasFilas;i++) {
        matrizConPadding[i] = new int[nuevasColumnas];
    }

    for(int i = 0; i<nuevasFilas;i++){
        for(int j = 0; j<nuevasColumnas;j++){
            if (i == 0 || i == nuevasFilas - 1 || j == 0 || j == nuevasColumnas - 1) {
                if(i==0){
                    if(j==0){
                        matrizConPadding[i][j] = matrizImg[0][0];
                    }
                    else if(j==nuevasColumnas-1){
                        matrizConPadding[i][j] = matrizImg[0][(*columnasImg)-1];
                    }
                    else{
                        matrizConPadding[i][j] = matrizImg[0][j-1];
                    }
                }
                else if(i==nuevasFilas-1){
                    if(j==0){
                        matrizConPadding[i][j] = matrizImg[(*filasImg)-1][0];
                    }
                    else if(j==nuevasColumnas-1){
                        matrizConPadding[i][j] = matrizImg[(*filasImg)-1][(*columnasImg)-1];
                    }
                    else{
                        matrizConPadding[i][j] = matrizImg[(*filasImg)-1][j-1];
                    }
                }
                else{
                    if(j==0){
                        matrizConPadding[i][j] = matrizImg[i - 1][0];
                    }
                    else if(j == nuevasColumnas - 1){
                        matrizConPadding[i][j] = matrizImg[i - 1][(*columnasImg) - 1];
                    }
                    else{
                        matrizConPadding[i][j] = matrizImg[i - 1][j - 1];
                    }
                }
            }
            else{
                matrizConPadding[i][j]=matrizImg[i-1][j-1];
            }
        }
    }

    *filasImg = nuevasFilas;
    *columnasImg = nuevasColumnas;

    cout<<"PRUEBAAAAAA\n";
    for(int i = 0 ; i<nuevasFilas;i++){
        for(int j = 0; j<nuevasColumnas; j++){
            cout<<matrizConPadding[i][j]<<" ";
        }
        cout<<endl;
    }
    cout<<"PRUEBAAAAAA\n";

    return matrizConPadding;
}

void almacenarNuevaImagen(int** matrizImg, int filasImg, int columnasImg){
    ofstream nuevo_archivo("./nuevaImagen.pgm");
    nuevo_archivo<<"P5"<<endl;
    nuevo_archivo<<"#WPI-ME/CHSLT generated image Ver.1.0 (0)"<<endl;
    nuevo_archivo<<filasImg<<" "<<columnasImg<<endl;

    int maxGris = 0;
    for(int i = 0; i<filasImg; i++){
        for(int j = 0; j<columnasImg; j++){
            if(matrizImg[i][j]>maxGris){
                maxGris = matrizImg[i][j];
            }
        }
    }

    nuevo_archivo<<maxGris<<endl;
    for(int i = 0;i<filasImg;i++){
        for(int j = 0; j<columnasImg; j++){
            nuevo_archivo<<matrizImg[i][j]<<" ";
        }
    }
    nuevo_archivo.close();
}
0

There are 0 best solutions below