Solved

Marr-Hildreth Program not Giving Desired Result

Posted on 2009-07-13
8
569 Views
Last Modified: 2012-08-13
Hi Experts,

The c++ code below on the lena image in .pgm format is not giving very good edge detection. The threshold value of 100 yields the best result; however, this is quite inferior to the results of the matlab edge detection using the same algorithm.

I am not sure where I am going wrong in the algorithm. Below a threshold value of 100, the resulting image doesn't detect edges anymore.

Thanks,

Dennis
#include <iostream>

#include <fstream>

#include <string>

#include <math.h>

#include <time.h>
 

 

using namespace std;
 

/*** New Data Types ***/

typedef unsigned char BYTE;
 

// create data structure to hold image

struct PIC

{

    unsigned int nChannel;

    bool InterLeaved;

    unsigned int Width, Height, Maxval;

    BYTE *img;

};
 

template <typename T>
 

T **AllocateDynamicArray( int nRows, int nCols)

{
 

    T **dynamicArray;

    dynamicArray = new T*[nRows];

    for( int i = 0 ; i < nRows ; i++ )

    dynamicArray[i] = new T [nCols];

    return dynamicArray;

}
 

template <typename T>
 

void FreeDynamicArray(T** dArray)

{

    delete [] *dArray;

    delete [] dArray;

}
 
 
 

//function that loads in the header

bool LoadP5Header(ifstream &infile, PIC &pic)

{

    bool rtv = true;

    char buf[16];

    int bufIndex;

    int width, height, maxval;
 

    infile.read(buf, 2); // get the magic number

    buf[2]='\0';
 

    if(buf[0] == 'P' && buf[1] == '5')

	{

        infile.read(buf, 1);

        while(isspace(buf[0])) // Skip white space(s)

		{

            infile.read(buf,1);

		}
 

        // get width

        bufIndex = 0;

        while(bufIndex < 15 && !isspace(buf[bufIndex]))

		{

            bufIndex++;

            infile.read(buf+bufIndex, 1);

        }

        buf[bufIndex] = NULL;  // null terminated string

        width = atoi(buf);
 

        // get height

        infile.read(buf,1);

        while(isspace(buf[0])) // Skip white space(s)

		{

            infile.read(buf,1);

        }

        bufIndex = 0;  //line 

        while(bufIndex < 15 && !isspace(buf[bufIndex]))

		{

            bufIndex++;

            infile.read(buf+bufIndex, 1);

        }

        buf[bufIndex] = NULL;  // null terminated string

        height = atoi(buf);
 

       // get Maxval

		infile.read(buf,1);

        while(isspace(buf[0])) // Skip white space(s)

		{

            infile.read(buf,1);

        }

        bufIndex = 0;

        while(bufIndex < 15 && !isspace(buf[bufIndex]))

		{

            bufIndex++;

            infile.read(buf+bufIndex, 1);

        }

        buf[bufIndex] = NULL;  // null terminated string

        maxval = atoi(buf);
 

		// Skip white space(s)

		//infile.read(buf,1);
 

        // set the image information in the struct

        pic.InterLeaved = false;

        pic.Width = width;

        pic.Height = height;

		pic.Maxval = maxval;
 

    }

    else rtv = false;
 

    return rtv;

}; // end of LoadP5Header()
 

//function that accepts an infile object and a PIC Object

//and reads in the PIC object

void LoadImage(ifstream &infile, PIC &pic)

{

    infile.read(reinterpret_cast<char *>(pic.img), pic.Width*pic.Height);

}
 

//function that accepts an outstream file and a PIC object to

//and writes the output stream to the PIC object

void WritePGM(ofstream & outfile, PIC pic)

{

    outfile << "P5" << endl;

    outfile << pic.Width << " " << pic.Height << endl;

	outfile << pic.Maxval << endl;
 

    outfile.write(reinterpret_cast<char *>(pic.img), pic.Width*pic.Height);

}
 
 

void MaskFiltering(PIC Pin_t, int Mask[5][5], char *outFileName_Pout)

{

    

        ofstream outfile_Pout;
 

        outfile_Pout.open(outFileName_Pout, ios::binary);

        

        int **IMG_matrix = AllocateDynamicArray<int>(Pin_t.Width+6,Pin_t.Height+6);

        int **IMG_temp_matrix = AllocateDynamicArray<int>(Pin_t.Width,Pin_t.Height);

   

        int Npixels, pixelCnt, LoGValue;

        int threshVal = 100;

            

		Npixels = Pin_t.Width*Pin_t.Height;

		Pin_t.Maxval = 255;
 

		        //create a two pixel layer of black border around the original image to apply the mask to boundary values

 

        for(int j = 0; j <= Pin_t.Height+3; j++)

        {

             IMG_matrix[0][j] = 0;

             IMG_matrix[1][j] = 0;

             IMG_matrix[Pin_t.Width+2][j] = 0; 

             IMG_matrix[Pin_t.Width+3][j] = 0; 

        }

        

        for(int k = 0; k <= Pin_t.Width+3; k++)

        {

             IMG_matrix[k][0] = 0;

             IMG_matrix[k][1] = 0;

             IMG_matrix[k][Pin_t.Height+2] = 0;

             IMG_matrix[k][Pin_t.Height+3] = 0;

        }

 

        pixelCnt = 0;

        //make 2D matrix of input

 

        for(int j = 2; j < Pin_t.Height + 2; j++)

        {

              for(int k = 2; k < Pin_t.Width + 2; k++)

              {

                    IMG_matrix[k][j] = (int) Pin_t.img[pixelCnt];

                    pixelCnt++;

              }

         }
 

        

        for(int j=2; j <= Pin_t.Height+1; j++)

        {           

            for(int k=2; k <= Pin_t.Width+1; k++)

            {      
 

                      LoGValue = Mask[2][2]*IMG_matrix[k][j] + Mask[3][2]*IMG_matrix[k+1][j] + Mask[4][2]*IMG_matrix[k+2][j] + Mask[1][2]*IMG_matrix[k-1][j] + Mask[0][2]*IMG_matrix[k-2][j]

                                     + Mask[2][3]*IMG_matrix[k][j+1] + Mask[2][4]*IMG_matrix[k][j+2] + Mask[2][1]*IMG_matrix[k][j-1] + Mask[2][0]*IMG_matrix[k][j-2]

                                     + Mask[3][1]*IMG_matrix[k+1][j-1] + Mask[3][4]*IMG_matrix[k+1][j+1]

									 + Mask[1][1]*IMG_matrix[k-1][j-1] + Mask[1][4]*IMG_matrix[k-1][j+1];
 

                                     

                 if(LoGValue < threshVal) LoGValue = 0;

                 else if(LoGValue < threshVal) LoGValue = 255;
 

                 IMG_temp_matrix[k-2][j-2] = LoGValue;

            } 

        }

        

        //output the calculated 2D matrix to original 1D

        pixelCnt = 0;

        for(int j = 0; j < Pin_t.Height; j++)

        {

              for(int k = 0; k < Pin_t.Width; k++)

              {

                    Pin_t.img[pixelCnt] = (unsigned char) (IMG_temp_matrix[k][j]);

                    pixelCnt++;

              }

         }
 

        WritePGM(outfile_Pout, Pin_t);

 }
 
 

int main()

{  

    

	ifstream infile_Pin1;                /* input file */

    char inFileName_Pin1[128]="Lena.pgm";            /* input file name */

    PIC Pin1;            //source image

    

    /* open input/output file */

    infile_Pin1.open(inFileName_Pin1, ios::binary);
 

	/* check input file */

    if(!infile_Pin1)

    {

        cout<<"Cannot open input file "<< inFileName_Pin1 <<endl;

        return 1;

    }
 
 

   int LaplacianOfGaussian[5][5] = {{0, 0, 1, 0, 0},

                                 {0, 1, 2, 1, 0},

                                 {1, 2, -16, 2, 1},

								 {0, 1, 2, 1, 0},

                                 {0, 0, 1, 0, 0}};
 
 
 

    if (LoadP5Header(infile_Pin1, Pin1)) // load pgm (Pin) image header information

    {

        // allocate the memory for the input image

        //the dimensions of the original
 

        Pin1.img = new BYTE[Pin1.Width*Pin1.Height];
 

        LoadImage(infile_Pin1, Pin1);

   

       MaskFiltering(Pin1,LaplacianOfGaussian, "Lena_Hildreth_1.pgm");   
 

   }

   

}

Open in new window

0
Comment
Question by:datopdogg7
  • 4
  • 3
8 Comments
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24842850
If you would post the .pgm files I would try to find out where it failed (though it might be difficult if not comparing it with working code).
0
 

Author Comment

by:datopdogg7
ID: 24842905
I found an issue with the program, it had to do with the computation of the mask products. However, the output image is still unstaisfactory.

I shall post the image result as well as the updated code.
#include <iostream>

#include <fstream>

#include <string>

#include <math.h>

#include <time.h>
 

 

using namespace std;
 

/*** New Data Types ***/

typedef unsigned char BYTE;
 

// create data structure to hold image

struct PIC

{

    unsigned int nChannel;

    bool InterLeaved;

    unsigned int Width, Height, Maxval;

    BYTE *img;

};
 

template <typename T>
 

T **AllocateDynamicArray( int nRows, int nCols)

{
 

    T **dynamicArray;

    dynamicArray = new T*[nRows];

    for( int i = 0 ; i < nRows ; i++ )

    dynamicArray[i] = new T [nCols];

    return dynamicArray;

}
 

template <typename T>
 

void FreeDynamicArray(T** dArray)

{

    delete [] *dArray;

    delete [] dArray;

}
 
 
 

//function that loads in the header

bool LoadP5Header(ifstream &infile, PIC &pic)

{

    bool rtv = true;

    char buf[16];

    int bufIndex;

    int width, height, maxval;
 

    infile.read(buf, 2); // get the magic number

    buf[2]='\0';
 

    if(buf[0] == 'P' && buf[1] == '5')

	{

        infile.read(buf, 1);

        while(isspace(buf[0])) // Skip white space(s)

		{

            infile.read(buf,1);

		}
 

        // get width

        bufIndex = 0;

        while(bufIndex < 15 && !isspace(buf[bufIndex]))

		{

            bufIndex++;

            infile.read(buf+bufIndex, 1);

        }

        buf[bufIndex] = NULL;  // null terminated string

        width = atoi(buf);
 

        // get height

        infile.read(buf,1);

        while(isspace(buf[0])) // Skip white space(s)

		{

            infile.read(buf,1);

        }

        bufIndex = 0;  //line 

        while(bufIndex < 15 && !isspace(buf[bufIndex]))

		{

            bufIndex++;

            infile.read(buf+bufIndex, 1);

        }

        buf[bufIndex] = NULL;  // null terminated string

        height = atoi(buf);
 

       // get Maxval

		infile.read(buf,1);

        while(isspace(buf[0])) // Skip white space(s)

		{

            infile.read(buf,1);

        }

        bufIndex = 0;

        while(bufIndex < 15 && !isspace(buf[bufIndex]))

		{

            bufIndex++;

            infile.read(buf+bufIndex, 1);

        }

        buf[bufIndex] = NULL;  // null terminated string

        maxval = atoi(buf);
 

		// Skip white space(s)

		//infile.read(buf,1);
 

        // set the image information in the struct

        pic.InterLeaved = false;

        pic.Width = width;

        pic.Height = height;

		pic.Maxval = maxval;
 

    }

    else rtv = false;
 

    return rtv;

}; // end of LoadP5Header()
 

//function that accepts an infile object and a PIC Object

//and reads in the PIC object

void LoadImage(ifstream &infile, PIC &pic)

{

    infile.read(reinterpret_cast<char *>(pic.img), pic.Width*pic.Height);

}
 

//function that accepts an outstream file and a PIC object to

//and writes the output stream to the PIC object

void WritePGM(ofstream & outfile, PIC pic)

{

    outfile << "P5" << endl;

    outfile << pic.Width << " " << pic.Height << endl;

	outfile << pic.Maxval << endl;
 

    outfile.write(reinterpret_cast<char *>(pic.img), pic.Width*pic.Height);

}
 
 

void MaskFiltering(PIC Pin_t, int Mask[5][5], char *outFileName_Pout)

{

    

        ofstream outfile_Pout;
 

        outfile_Pout.open(outFileName_Pout, ios::binary);

        

        int **IMG_matrix = AllocateDynamicArray<int>(Pin_t.Width+6,Pin_t.Height+6);

        int **IMG_temp_matrix = AllocateDynamicArray<int>(Pin_t.Width,Pin_t.Height);

   

        int Npixels, pixelCnt, LoGValue;

        int threshVal = 75;

            

		Npixels = Pin_t.Width*Pin_t.Height;

		Pin_t.Maxval = 255;
 

		//create a two pixel layer of black border around the original image to apply the mask to boundary values

 

        for(int j = 0; j <= Pin_t.Height+3; j++)

        {

             IMG_matrix[0][j] = 0;

             IMG_matrix[1][j] = 0;

             IMG_matrix[Pin_t.Width+2][j] = 0; 

             IMG_matrix[Pin_t.Width+3][j] = 0; 

        }

        

        for(int k = 0; k <= Pin_t.Width+3; k++)

        {

             IMG_matrix[k][0] = 0;

             IMG_matrix[k][1] = 0;

             IMG_matrix[k][Pin_t.Height+2] = 0;

             IMG_matrix[k][Pin_t.Height+3] = 0;

        }

 

        pixelCnt = 0;

        //make 2D matrix of input

 

        for(int j = 2; j < Pin_t.Height + 2; j++)

        {

              for(int k = 2; k < Pin_t.Width + 2; k++)

              {

                    IMG_matrix[k][j] = (int) Pin_t.img[pixelCnt];

                    pixelCnt++;

              }

         }
 

        

        for(int j=2; j <= Pin_t.Height+1; j++)

        {           

            for(int k=2; k <= Pin_t.Width+1; k++)

            {      
 

                      LoGValue = Mask[2][2]*IMG_matrix[k][j] + Mask[3][2]*IMG_matrix[k+1][j] + Mask[4][2]*IMG_matrix[k+2][j] + Mask[1][2]*IMG_matrix[k-1][j] + Mask[0][2]*IMG_matrix[k-2][j]

                                     + Mask[2][3]*IMG_matrix[k][j+1] + Mask[2][4]*IMG_matrix[k][j+2] + Mask[2][1]*IMG_matrix[k][j-1] + Mask[2][0]*IMG_matrix[k][j-2]

                                     + Mask[3][1]*IMG_matrix[k+1][j-1] + Mask[3][3]*IMG_matrix[k+1][j+1]

									 + Mask[1][1]*IMG_matrix[k-1][j-1] + Mask[1][3]*IMG_matrix[k-1][j+1];
 

                                     

                 if(LoGValue < threshVal) LoGValue = 0;

                 else if(LoGValue < threshVal) LoGValue = 255;
 

                 IMG_temp_matrix[k-2][j-2] = LoGValue;

            } 

        }

        

        //output the calculated 2D matrix to original 1D

        pixelCnt = 0;

        for(int j = 0; j < Pin_t.Height; j++)

        {

              for(int k = 0; k < Pin_t.Width; k++)

              {

                    Pin_t.img[pixelCnt] = (unsigned char) (IMG_temp_matrix[k][j]);

                    pixelCnt++;

              }

         }
 

        WritePGM(outfile_Pout, Pin_t);

 }
 
 

int main()

{  

    

	ifstream infile_Pin1;                /* input file */

    char inFileName_Pin1[128]="Lena.pgm";            /* input file name */

    PIC Pin1;            //source image

    

    /* open input/output file */

    infile_Pin1.open(inFileName_Pin1, ios::binary);
 

	/* check input file */

    if(!infile_Pin1)

    {

        cout<<"Cannot open input file "<< inFileName_Pin1 <<endl;

        return 1;

    }
 
 

   int LaplacianOfGaussian[5][5] = {{0, 0, 1, 0, 0},

                                 {0, 1, 2, 1, 0},

                                 {1, 2, -16, 2, 1},

								 {0, 1, 2, 1, 0},

                                 {0, 0, 1, 0, 0}};
 
 
 

    if (LoadP5Header(infile_Pin1, Pin1)) // load pgm (Pin) image header information

    {

        // allocate the memory for the input image

        //the dimensions of the original
 

        Pin1.img = new BYTE[Pin1.Width*Pin1.Height];
 

        LoadImage(infile_Pin1, Pin1);

   

       MaskFiltering(Pin1,LaplacianOfGaussian, "Lena_Hildreth_1.pgm");   
 

   }

   

}

Open in new window

Lena-Marr-Hidlreth.jpg
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24843344
The program opens lena.pgm as input.

 
0
 

Author Comment

by:datopdogg7
ID: 24843407
Yes, I cannot upload .pgm files to this question. Only .jpeg, etc.

I took a screen capture of the output image and saved it as a .jpeg

Here is the link to Lena.pgm

http://www.ece.umd.edu/class/enee631.F2001/am/am3/bb2.htm
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 53

Accepted Solution

by:
Infinity08 earned 250 total points
ID: 24847113
Could it be because of this :

>>                  if(LoGValue < threshVal) LoGValue = 0;
>>                  else if(LoGValue < threshVal) LoGValue = 255;

The else if checks the same condition as the if ... Did you mean :

                 if (LoGValue < threshVal) LoGValue = 0;
                 else if (LoGValue >= threshVal) LoGValue = 255;

or just :

                 if (LoGValue < threshVal) LoGValue = 0;
                 else LoGValue = 255;

?
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 250 total points
ID: 24847424
I debugged a little bit and some hundred values for LogValue all were in a range [-1500,-300] what makes them all 0 as you don't seem to expect negative values. I added variables for each term

       int t1 = Mask[2][2]*IMG_matrix[k][j];
       int t2 = Mask[3][2]*IMG_matrix[k+1][j];
       int t3 = Mask[4][2]*IMG_matrix[k+2][j];
       int t4 = Mask[1][2]*IMG_matrix[k-1][j];
       int t5 = Mask[0][2]*IMG_matrix[k-2][j];
       int t6 = Mask[2][3]*IMG_matrix[k][j+1];  
       int t7 = Mask[2][4]*IMG_matrix[k][j+2];
       int t8 = Mask[2][1]*IMG_matrix[k][j-1];
       int t9 = Mask[2][0]*IMG_matrix[k][j-2];
       int ta = Mask[3][1]*IMG_matrix[k+1][j-1];
       int tb = Mask[3][3]*IMG_matrix[k+1][j+1];
       int tc = Mask[1][1]*IMG_matrix[k-1][j-1];
       int td = Mask[1][3]*IMG_matrix[k-1][j+1];

and it turned out that t1 is nearly almost less than -2000 because of Mask[2][2] is -16, while the other values were about 100 in average what is not sufficient to make the LogValue a positive..

In the middle of the picture (column 250) things were better but still more than 50 percent negative values (mostly a series of 5 or 6).  

>>>>                 if(LoGValue < threshVal) LoGValue = 0;
>>>>                  else if(LoGValue < threshVal) LoGValue = 255;

Here that if/else doesn't consider negative values for LogValue ???



>>>> int **IMG_matrix = AllocateDynamicArray<int>(Pin_t.Width+6,Pin_t.Height+6);
you were allocating 6 pixels more in width though you only have a border of 4. Is there a reason I didn't see?

>>>> void FreeDynamicArray(T** dArray)
>>>> {
>>>>     delete [] *dArray;
>>>>     delete [] dArray;
>>>> }
 
You never call the FreeDynamicArray what doesn't matter much as it would delete only the first row. The deletion must be done in a loop same as the allocation.

I used a vector instead of int **IMG_matrix what is the better way as it initializes the matrix with zeros as well and you don't need to care for deallocation.

#include <vector>
using namespace std;

....

        // int **IMG_matrix = AllocateDynamicArray<int>(Pin_t.Width+6,Pin_t.Height+6);
        // int **IMG_temp_matrix = AllocateDynamicArray<int>(Pin_t.Width,Pin_t.Height);
        vector<vector<int> >  IMG_matrix(Pin_t.Width+6, vector<int>(Pin_t.Height+6, 0));
        vector<vector<int> >  IMG_temp_matrix(Pin_t.Width, vector<int>(Pin_t.Height, 0));
 
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24847462
>>>> The else if checks the same condition as the if ...

Good catch ;-)
0
 

Author Comment

by:datopdogg7
ID: 24862999
Thanks guys. That was the problem.
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Article by: Nadia
Linear search (searching each index in an array one by one) works almost everywhere but it is not optimal in many cases. Let's assume, we have a book which has 42949672960 pages. We also have a table of contents. Now we want to read the content on p…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now