Solved

Marr-Hildreth Program not Giving Desired Result

Posted on 2009-07-13
8
590 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 

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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

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

The greatest common divisor (gcd) of two positive integers is their largest common divisor. Let's consider two numbers 12 and 20. The divisors of 12 are 1, 2, 3, 4, 6, 12 The divisors of 20 are 1, 2, 4, 5, 10 20 The highest number among the c…
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 clear a vector as well as how to detect empty vectors in C++.
I've attached the XLSM Excel spreadsheet I used in the video and also text files containing the macros used below. https://filedb.experts-exchange.com/incoming/2017/03_w12/1151775/Permutations.txt https://filedb.experts-exchange.com/incoming/201…

630 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