Solved

Marr-Hildreth Program not Giving Desired Result

Posted on 2009-07-13
8
575 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
Is Your AD Toolbox Looking More Like a Toybox?

Managing Active Directory can get complicated.  Often, the native tools for managing AD are just not up to the task.  The largest Active Directory installations in the world have relied on one tool to manage their day-to-day administration tasks: Hyena. Start your trial today.

 

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
 
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

3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
How to convert MFC::CString to UTF8 wchar_t* 10 211
Dice Roll Probabilities 3 98
How can i compile this github project?? 2 85
max float value 3 41
One of Google's most recent algorithm changes affecting local searches is entitled "The Pigeon Update." This update has dramatically enhanced search inquires for the keyword "Yelp." Google searches with the word "Yelp" included will now yield Yelp a…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

810 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