#include <iostream> #include <fstream> #include <string> #include <cstdlib> #include <cmath> using namespace std; //a class that represents the three colour values (R,G,B)/>/> in each pixel. class Pixel { private: unsigned int P1, P2, P3; public: Pixel () {}; void setPixels (unsigned int Pixel1, unsigned int Pixel2, unsigned int Pixel3); unsigned int getPixel1 (); unsigned int getPixel2 (); unsigned int getPixel3 (); }; void Pixel::setPixels (unsigned int Pixel1, unsigned int Pixel2, unsigned int Pixel3) { P1 = Pixel1; P2 = Pixel2; P3 = Pixel3; } unsigned int Pixel::getPixel1 () { return P1; } unsigned int Pixel::getPixel2 () { return P2; } unsigned int Pixel::getPixel3 () { return P3; } //*****************int main () stars here! ****************************************************************************** int main () { //information contained in the header file is represented by the following variables unsigned char Magic [2]; unsigned int TotRows = 512; unsigned int TotCol = 512; unsigned int MaxVal = 255; int size = (3 * TotRows * TotCol); char *charImage = new char [size]; //opening original image ifstream OldImage; OldImage.open ("image.ppm", ios::in  ios::binary); if (!OldImage) { cout << "\nError: Cannot open image file! " << endl; } //reading the header of the original image file OldImage >> Magic [0] >> Magic [1] >> TotRows >> TotCol >> MaxVal; OldImage.read(charImage, size); unsigned int val1, val2, val3; //an array of pixels, which is used to represent the image Pixel **PixelVal; PixelVal = new Pixel* [TotRows]; int T=0; //Reading the image data and setting the pixels values as unsigned integers for(int i=0; i < TotRows; i++) { PixelVal[i] = new Pixel [TotCol]; for(int j=0; j < TotCol; j++) { val1 = (unsigned int)charImage[T]; val2 = (unsigned int)charImage[T+1]; val3 = (unsigned int)charImage[T+2]; PixelVal[i][j].setPixels (val1, val2, val3); T=T+3; } } if (OldImage.fail()) { cout << "Can't read image " << endl; } OldImage.close(); //Calculating the grayscale in each pixel. The values of the 3 colours (R, B and G) are all the same for(int i=0; i < TotRows; i++) { for(int j=0; j < TotCol; j++) { val1=(PixelVal[i][j].getPixel1()+PixelVal[i][j].getPixel1()+PixelVal[i][j].getPixel1())/3; val2=val1; val3=val1; PixelVal[i][j].setPixels(val1, val2, val3); } } unsigned int valX, valY = 0; unsigned int GX [3][3]; unsigned int GY [3][3]; //Sobel Horizontal Mask GX[0][0] = 1; GX[0][1] = 0; GX[0][2] = 1; GX[1][0] = 2; GX[1][1] = 0; GX[1][2] = 2; GX[2][0] = 1; GX[2][1] = 0; GX[2][2] = 1; //Sobel Vertical Mask GY[0][0] = 1; GY[0][1] = 2; GY[0][2] = 1; GY[1][0] = 0; GY[1][1] = 0; GY[1][2] = 0; GY[2][0] = 1; GY[2][1] =2; GY[2][2] = 1; //SOBEL edge detector implementation. Note: in each Pixel, the values of the 3 colours is the same. //Therefore the calculation is performed on the first one only. The other 2 colours are then set to be = to the first one. for(int i=0; i < TotRows; i++) { for(int j=0; j < TotCol; j++) { //setting the pixels around the border to 0, because the Sobel kernel cannot be allied to them if ((i==0)(i==TotRows1)(j==0)(j==TotCol1)) { valX=0; valY=0; } else { //calculating the X and Y convolutions for (int x = 1; x <= 1; x++) { for (int y = 1; y <= 1; y++) { valX = valX + PixelVal[i+x][j+y].getPixel1() * GX[1+x][1+y]; valY = valY + PixelVal[i+x][j+y].getPixel1() * GY[1+x][1+y]; } } } //Gradient magnitude val1 = sqrt(valX*valX + valY*valY); //setting the new pixel value PixelVal[i][j].setPixels(val1, val1, val1); } } //creating a new file to host the copied image ofstream NewImage; NewImage.open ("image1.ppm", ios::out  ios::binary); if (!NewImage) { cout << "\nError: Cannot open image file! " <<endl; } //writing the header in the new image file NewImage << "P6" << endl << TotRows << " " << TotCol << " " << MaxVal << endl; T=0; for(int i=0; i < TotRows; i++) { for(int j=0; j < TotCol; j++) { val1 = PixelVal[i][j].getPixel1(); val2 = PixelVal[i][j].getPixel2(); val3 = PixelVal[i][j].getPixel3(); charImage[T]=(unsigned char)val1; charImage[T+1]=(unsigned char)val2; charImage[T+2]=(unsigned char)val3; T=T+3; } } cout << T; NewImage.write(charImage, size); if (NewImage.fail()) { cout << "Can't write image " << endl; } NewImage.close(); delete [] charImage; return 0; }
Sobel edge detector using c++ only
Page 1 of 17 Replies  2477 Views  Last Post: 05 May 2013  04:16 PM
#1
Sobel edge detector using c++ only
Posted 05 May 2013  03:05 AM
I have a problem with the following code. The processed image does not show right (it becomes all messy and gray). AFter spending a ridiculous number of hours trying to fix the code, I still don't know what the problem is. I'd be super grateful for any help.
Replies To: Sobel edge detector using c++ only
#2
Re: Sobel edge detector using c++ only
Posted 05 May 2013  10:49 AM
A few things standout:
1. In the else case on lines 156165, it looks like you use the previous value of valX and valY from the previous column. I don't know the algorithm, and so I'm not sure if it's intentional or not.
2. On lines 162163, i+x and i+y will underrun your PixelVal 2 dimensional arrays when x == 1 or y == 1, and i == 0 or j == 0.
3. On lines 162163, 1+x and 1+y will overrun your GX and GY matrices when x == 1 or y == 1.
4. Your computation for grayscale on line 117 looks very very wrong even for a naive approach without factoring in any color perception theory.
1. In the else case on lines 156165, it looks like you use the previous value of valX and valY from the previous column. I don't know the algorithm, and so I'm not sure if it's intentional or not.
4. Your computation for grayscale on line 117 looks very very wrong even for a naive approach without factoring in any color perception theory.
This post has been edited by Skydiver: 05 May 2013  04:38 PM
Reason for edit:: Struck out the points on which I struck out.
#3
Re: Sobel edge detector using c++ only
Posted 05 May 2013  12:27 PM
Thanks for checking my code out! Yes, you are right about point 1  I should reset the valX and valY values to 0 before each iteration. I am not sure what you mean in your points 2 and 3. The idea is to apply the GX and GY masks on the Pixels array. I will double check the numbers on those lines anywas, thanks so much!
#4
Re: Sobel edge detector using c++ only
Posted 05 May 2013  01:28 PM
Skydiver is of course correct,
your lines at 117 for calculating the grayscale is plain wrong.
The way you calculate the grayscale is to find the average luminance
of the pixel. Each pixel is composed of Red , Green and Blue color information
For red the weighted average is 30%
For green the weighted average is 59%
For blue the weighted average is 11%
so therefore average luminance can be calculated as follows.
once you have the average luminance set RGB values as follows.
basically all three values are set to luminance.
Try sorting that bit out and see how far that gets you.
Regards
Snoopy.
your lines at 117 for calculating the grayscale is plain wrong.
The way you calculate the grayscale is to find the average luminance
of the pixel. Each pixel is composed of Red , Green and Blue color information
For red the weighted average is 30%
For green the weighted average is 59%
For blue the weighted average is 11%
so therefore average luminance can be calculated as follows.
Luminance = ((30*Red)+(59*Green)+(11*Blue))/100;
once you have the average luminance set RGB values as follows.
Red = Luminance; Green = Red; Blue = Green;
basically all three values are set to luminance.
Try sorting that bit out and see how far that gets you.
Regards
Snoopy.
#5
Re: Sobel edge detector using c++ only
Posted 05 May 2013  01:38 PM
I understand now, thanks!
#6
Re: Sobel edge detector using c++ only
Posted 05 May 2013  03:25 PM
Yup, point 3, won't overrun. When x == 1, then 1 + x == 1 + 1 == 2. This is still within range for your 3x3 matrix.
As for point 2, it will overrun when x == 1 and i == 0. x + i == 1 + 0 == 1. The way you are using PixelVal array, I don't see 1 as a valid index.
As for point 2, it will overrun when x == 1 and i == 0. x + i == 1 + 0 == 1. The way you are using PixelVal array, I don't see 1 as a valid index.
#7
Re: Sobel edge detector using c++ only
Posted 05 May 2013  03:53 PM
ok i understand what you mean, but
should keep x and y from being on the borders of the array, so that should take care of it no? The second nested loop does not start in that case, I think.
Thanks again for helping me out with this!
sorry I meant i and j are never 0 nor ==TotRows or TotColinside the second loop
//setting the pixels around the border to 0, because the Sobel kernel cannot be allied to them 151 if ((i==0)(i==TotRows1)(j==0)(j==TotCol1)) 152 { 153 valX=0; 154 valY=0; 155 }
should keep x and y from being on the borders of the array, so that should take care of it no? The second nested loop does not start in that case, I think.
Thanks again for helping me out with this!
sorry I meant i and j are never 0 nor ==TotRows or TotColinside the second loop
#8
Re: Sobel edge detector using c++ only
Posted 05 May 2013  04:16 PM
Excellent! That means point 2 is also invalid.
Page 1 of 1
