Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Marr-Hildreth Program not Giving Desired Result

Posted on 2009-07-13
8
Medium Priority
?
603 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

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

This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
Article by: Nadia
Suppose you use Uber application as a rider and you request a ride to go from one place to another. Your driver just arrived at the parking lot of your place. The only thing you know about the ride is the license plate number. How do you find your U…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

721 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