0
votes

I am working on a assignment that performs convolution without using external libraries. The problem is that the resulting image is supposed to be blurred but the produced image is wrong.

Here is the link to the picture i am talking about.

enter image description here

The program takes the first 5 bytes of the greyscale RAW image as header and the rest into a 2D vector. Each pixel intensity (0-255) is represented by one byte in unsigned char. The 2D vector is used for convolution then the resulting vector is written into a new RAW image with the header.

#include <iostream>
#include <string>
#include <fstream>
#include <stdio.h>
#include <vector>
#include <math.h>

using namespace std;

vector<unsigned char> header;
vector < vector<unsigned char> > img;

short int width = 0;
short int height = 0;

void read(string filename, short int &width, short int &height, vector < vector<unsigned char> > &img, vector<unsigned char> &header)
{
    const char* path = (char*)filename.c_str();
    cout << endl << "Opening: " << path << endl << endl;

    ifstream inputfile;
    inputfile.open(path, ios::binary);
    inputfile.seekg(0);

    if (!inputfile)
    {
        inputfile.clear();
        string pathin;
        cout << "File not found "<< endl <<"Enter name of image to open : ";
        getline(cin, pathin);

        read(pathin, width, height, img, header);
    }
    else
    {   //copy header into array
        unsigned char h = 0;
        for (int x = 0; x < 5; x++)
        {

            inputfile >>(h);
            header.push_back(h);
        }
        //extract width and height from array
        width = (header[1]<<8 | header[0]);
        height = (header[3]<<8 | header[2]);
        //copy image data into array
        char a = '0';
        for (int x = 0; x < width; x++)
        {
            vector <unsigned char> row;
            for (int y = 0; y < height; y++)
            {
                inputfile.get(a);
                    row.push_back(a);
                    a = '0';
            }
            img.push_back(row);
        }
        inputfile.close();
    }
    return;
}

void convolution(vector < vector<unsigned char> > &img, short int width, short int height)
{
    vector < vector<int> > mask = { { 1, 1, 1 },
                                    { 1, 1, 1 },
                                    { 1, 1, 1 } };
    int sum;
    double min = 0, max = 0, norm = 0; 

    vector<vector<int>> temp;
    for (int i = 0; i < width; ++i)
    {
        vector<int>temprow;
        for (int j = 0; j < height; ++j)
        {
            temprow.push_back(0);

        }
        temp.push_back(temprow);
    }


    for (int i = 1; i < (width-1); i++)
    {
        for (int j = 1; j < (height-1); j++)
        {
            sum = 0;
            for (int u = -1; u <= 1; u++)
            {
                for (int v = -1; v <= 1; v++)
                {
                    sum = sum + img[i + u][j + v] * (mask[u+1][v+1]);       
                }
            }
            if (sum > max)
                max = sum;
            if (sum < min)
                sum = min;

            temp[i][j] = sum;

        }
    }
    //normalize pixels
    norm = ceil(max / 255);
    cout << "norm = "<<norm<<endl;
    for (int i = 1; i < width; i++)
    {
        for (int j = 1; j < height; j++)
        {
            temp[i][j] = temp[i][j] /norm ;
        }
    }
    //get max and min again
    cout << "max = " << max << endl << "min = " << min<<endl;
    max = 0; min = 0;
    for (int i = 1; i < width-1; i++)
    {
        for (int j = 1; j < height-1; j++)
        {
            if (temp[i][j] > max)
                max = temp[i][j];
            if (temp[i][j] < min)
                min = temp[i][j];
        }
    }
    cout << "max = " << max << endl << "min = " << min << endl;
    //display temp
    cout << "temp: ";
    for (int i = 0; i < 5; i++)
    {
        cout << endl;
        for (int j = 0; j < 5; j++)
        {
            cout << temp[i][j] << "  ";
        }
    }
    //convert int to char
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            img[i][j] = temp[i][j] & 0xff;
        }
    }
    cout << endl;
    //display img 
    cout << "img: ";
    for (int i = 0; i < 5; ++i)
    {
        cout << endl;
        for (int j = 0; j < 5; ++j)
            printf("%d   ", ((unsigned char)(img[i][j])));
    }
    cout << endl << endl;
}

void write(short int width, short int height, vector < vector<unsigned char> > img, vector<unsigned char> header)
{
    string path = "test.raw";
    ofstream output;
    output.open(path, ios::binary);

    for (int i = 0; i < 5; ++i)
    {
        cout << endl;
        for (int j = 0; j < 5; ++j)
            printf("%d   ", ((unsigned char)(img[i][j])));
    }

    if (!output)
    {
        cout << "Error saving file." << endl;
        return;     
    }
    else
    {
        for (int x = 0; x < 5; x++)
            output.put (header[x]);
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
                output.put (img[x][y]);
        }

        output.close();
        cout << "File is saved as: " << path << endl << endl;
    }
}

int main()
{
    string pathin; 

    cout << "Enter name of image to open: ";
    cin >> pathin;

    read(pathin, width, height, img, header);
    convolution(img, width, height);
    write(width, height, img, header);
    system("PAUSE");
    return 0;
}

At this point i am convinced something went wrong at writing stage since there is supposed to be a black border but it is missing in the image. I could display the data with openCV but couldn't get it to work either. I hope someone can point out what is causing this problem.

dropbox link to the image i am working with

1

1 Answers

0
votes

This is not a complete, solved, "do it all for you" type of answer, rather some general guidelines to help you get started.

In general, with image processing, you need to be able to see what you are working with, As you seem to have an unusual format, it will help if you can view your input and output images with a reliable program.

So, first off, you can use ImageMagick (which is installed on most Linux distros and is available for macOS and Windows) to convert your images to JPEG or PNG like this:

Convert your 512x479 image with 5 byte header to JPEG in Terminal:

magick -depth 8 -size 512x479+5 gray:cana.raw result.jpg

enter image description here

That's better - now you can see your results easily and consistently.


Next, it's a good idea to have some sensible sample images, so generate those with Perl by taking the first 5 header bytes off your image and appending a bunch (245,248 exactly) of zeroes (black pixels):

perl -e 'print `head -c5 cana.raw` . "\x00"x245248' > black.raw

Then make a white one too:

perl -e 'print `head -c5 cana.raw` . "\xff"x245248' > white.raw

And a mid-grey one:

perl -e 'print `head -c5 cana.raw` . "\x80"x245248' > grey.raw

So, try applying your reading and writing, without any processing to each of those and ensure they come out unaltered. Then add in your convolution and test again.


Finally, become friends with your debugger. You don't say what compiler you are using so I can't be very specific here, but check you have the image sizes correct and that you are processing the correct number of rows and the correct number of elements per row.


As for your code... you forgot to set max to 255.

Also, you appear to be using the same array for storage of the output image as you are using for the input image. That doesn't tend to work very well for convolutions because each new pixel depends on the values of the pixels above (and below) it, and if you have already processed the row above on the previous pass through the loop and put the output values in there... you will be creating a mess that half depends on the original image and half on the new image - which means you will get ghosting and repeated elements - like your image appears. Try creating a second array for your output image.