April 5, 2008
WikiEdge Detection
Edge Detection
The edges of an image hold much of the information in that image. The edges tell where objects are, their shape and size, and something about their texture. An edge is where the intensity of an image moves from a low value to a high value or vice versa.
There are numerous applications for edge detection, which is often used for various special effects. Digital artists use it to create dazzling image outlines. The output of an edge detector can be added back to an original image to enhance the edges.
Edge detection is often the first step in image segmentation. Image segmentation, a field of image analysis, is used to group of pixels into regions to determine an image’s composition.
A common example of image segmentation is the “magic wand” tool in photo editor software . This tool allows the user to select a pixel in an image. The software then draws a border around the pixels of similar value. The user may select a pixel in a sky region and the magic wand would draw a border around the complete sky region in the image. The user may then edit the color of the sky without worrying about altering the color of the mountains or whatever else may be in the image.
Edge detection is also used in image registration. Image registration aligns two images that may have been acquired at separate times or from different sensors.
There is infinite number of edge orientations, widths and shapes. Some edges are straight while others are curved with varying radii. There are many edge detection techniques to go with all these edges, each having its own strengths. Some edge detectors may work well in one application and perform poorly in others. Sometimes it takes experimentation to determine what is the best edge detection technique for an application.
The simplest and the quickest edge detectors determine the maximum value from a series of pixel subtractions. The homogeneity operator subtracts each 8 surrounding pixels from the center pixel of a 3x3 window. **The output of the operator is the maximum of the absolute value of each difference. **
| 11| 13| 15| | 16| 11| 11| | 16| 12| 11|
new pixel = maximum of { |11-11| |11-13| |11-15| |11-16| | | |11-11| |11-16| | 11-12| |11-11| } = 5
Similar to the homogeneity operator is the difference edge detector. It operates more quickly because it requires four subtractions per pixel as opposed to the eight needed by the homogeneity operator. The subtractions are upper left - lower right, middle left- middle right, lower left - upper right, and top middle - bottom middle.
| 11| 13| 15| | 16| 11| 11| | 16| 12| 11|
new pixel = maximum of { |11-11| |13-12| |15-16| |11-16| } = 5
The output of an edge detector operator is called an edge map.
Color Edge Detection
The method of detecting edges in color images depends on your definition of an edge. One definition of an edge is the discontinuity in an image’s luminance. Edge detection would then be done on the intensity channel of a color image in HSI space.
Another definition claims an edge exists if it is present in the red, green, and blue channel. Edge detection can be done by performing it on each of the color components. After combining the color components, the resulting image is still color.
Edge detection can also be done on each color component and then the components can be summed to createagray scale edge map. Also, the color components can be vector summed to create the gray scale edge map.
It has been shown that the large majority of edges found in the color elements of an image are also found in the intensity (brightness) component. This would imply that edge detection done on the intensity component alone would suffice.
Step by Step
This time we have variables difference and Radius in our DrawToBuffer function. Later in the function we are assigning values to the difference var by returning values from getEdge function.
for(x=mousepoint.h-Radius;x<mousepoint.h+Radius;x++){
for(y=mousepoint.v-Radius;y<mousepoint.v+Radius;y++){
difference = GetEdge(x,y,imageRowBytes,imageBaseAddress);
// calling the GetEdge() method that calculates the difference in an area
if (difference >30){
// if the difference is greater than 30 then we set the pixel black
ourSetPixel(x,y,0,0,0,bufferRowBytes,bufferBaseAddress);
}else{
ourSetPixel(x,y,255,255,255,bufferRowBytes,bufferBaseAddress);
}
}
}
here is the important part getEdge function:
unsigned char GetEdge(unsigned short horizontal,unsigned short vertical,unsigned short rowbytes,Ptr pixbase){
long x,y ,countMatrix =0,sumMatrix=0;
long sumR=0,sumG=0,sumB=0 , thisgray , centergray ,difference=0; Ptr AdressOfRed;
centergray = (unsigned char) *(rowbytes * (vertical) +pixbase+(horizontal)*4+1);
centergray += (unsigned char) *(rowbytes * (vertical) +pixbase+(horizontal)*4+2);
centergray += (unsigned char) *(rowbytes * (vertical) +pixbase+(horizontal)*4+3);
for (x=-1;x<1;x++){
for (y=-1;y<1;y++){
AdressOfRed = rowbytes * (vertical+y) +pixbase+(horizontal+x)*4+1;
thisgray =(unsigned char) *(AdressOfRed);
thisgray+=(unsigned char) *(AdressOfRed+1);
thisgray +=(unsigned char) *(AdressOfRed+2);
difference+= abs(thisgray-centergray); // accumulating the difference.
}
}
return (unsigned char)( difference/ 3);
}
Source Code
void Initialize(void); // function prototypes
void DrawLine( void );
void doEventLoop( void );
void DrawToBuffer(void);
void CopyToWindow (void);
int ourRandom( int min, int max );
void ourSetPixel(unsigned short horizontal,unsigned short vertical,unsigned char R,unsigned char G,unsigned char B,unsigned short rowbytes,Ptr pixbase);
void ourGetPixel(unsigned short horizontal,unsigned short vertical,unsigned char* R,unsigned char* G,unsigned char* B,unsigned short rowbytes,Ptr pixbase);
void ImportFile(GWorldPtr destWorld,int left,int top,int right, int bottom);
unsigned char GetEdge(unsigned short horizontal,unsigned short vertical,unsigned short rowbytes,Ptr pixbase);
#define CLIP(val, low, high) {if(val<low) val=low; if(val>high)val=high;}
//globals
WindowPtr ourWindow;
Rect windRect;
GWorldPtr ourBuffer,ourImage;
int count=0;
void DrawToBuffer(void)
// this is where the interesting stuff happens, this is where we actually set our pixels
{
long x , y,temp,distance;
Point mousepoint;
unsigned short bufferRowBytes,imageRowBytes ;
Ptr bufferBaseAddress,imageBaseAddress;
PixMapHandle bufferPixmap, imagePixmap;
unsigned char R,G,B;
int difference,Radius,i;
count++;
GetMouse(&mousepoint);
bufferPixmap=GetGWorldPixMap(ourBuffer);
// getting the pixel map of our buffer GWorld so that we can access the pixels
bufferRowBytes = ((*(bufferPixmap))->rowBytes) & 0x7fff;
* getting the number of bytes in each row of the pixel map. This is then used to * calculate the location in memory of specific pixels
bufferBaseAddress = GetPixBaseAddr(bufferPixmap );
// getting the base address in memory of the begining of the pixel data
imagePixmap=GetGWorldPixMap(ourImage);
// getting the pixel map of our buffer GWorld so that we can access the pixels
imageRowBytes = ((*(imagePixmap))->rowBytes) & 0x7fff;
* getting the number of bytes in each row of the pixel map. This is then used to * calculate the location in memory of specific pixels
imageBaseAddress = GetPixBaseAddr(imagePixmap );
CopyBits( GetPortBitMapForCopyBits( ourImage ), GetPortBitMapForCopyBits( ourBuffer ),
&windRect, &windRect, srcCopy, NULL );
// copying the image to our buffer, this erases anything we did last frame
Radius = 50;
// the area around the mouse to be influenced
CLIP (mousepoint.h,2+Radius,windRect.right-2-Radius);
// making sure we will not be accessing pixels that are outside of the pixelmap
CLIP (mousepoint.v,2+Radius,windRect.bottom-2-Radius);
for(x=mousepoint.h-Radius;x<mousepoint.h+Radius;x++){
for(y=mousepoint.v-Radius;y<mousepoint.v+Radius;y++){
difference = GetEdge(x,y,imageRowBytes,imageBaseAddress);
// calling the GetEdge() method that calculates the difference in an area
if (difference >30) {
// if the difference is greater than 30 then we set the pixel black
ourSetPixel(x,y,0,0,0,bufferRowBytes,bufferBaseAddress);
} else {
ourSetPixel(x,y,255,255,255,bufferRowBytes,bufferBaseAddress);
}
}
}
}
void CopyToWindow (void){ // copy all our buffer to the window, completely replaceing
// everything that was there
Rect sourceRect,destRect;
SetPortWindowPort(ourWindow);
GetPortBounds( GetWindowPort(ourWindow), &destRect );
GetPortBounds( ourBuffer, &sourceRect );
CopyBits( GetPortBitMapForCopyBits( ourBuffer ), GetPortBitMapForCopyBits(GetWindowPort(ourWindow)),
&sourceRect, &destRect, srcCopy, NULL );
}
void main( void )
{
Initialize();
doEventLoop();
DisposeGWorld(ourBuffer);
DisposeGWorld(ourImage);
}
void Initialize(void){
OSErr error;
SetRect(&windRect,100,100,740,580);
InitCursor();
ourWindow = NewCWindow( 0L, &windRect, "\pEdge", true, noGrowDocProc,(WindowPtr)-1L, true, 0L );
if ( ourWindow == nil ) ExitToShell();
ShowWindow( ourWindow );
SetPortWindowPort( ourWindow );
SetRect(&windRect,0,0,640,480);
error =NewGWorld(&ourBuffer, 32, &windRect, nil, nil,0 ); // creating our offscreen buffer
if (error != noErr ) ExitToShell();
error =NewGWorld(&ourImage, 32, &windRect, nil, nil,0 );
if (error != noErr ) ExitToShell();
ImportFile(ourImage,0,0,640,480); // Calling our method that opens a picture file and returns a picture handle
}
/*************** The Event Loop ***************/
void doEventLoop()
{
EventRecord anEvent;
WindowPtr evtWind;
short clickArea;
Rect screenRect;
Point thePoint;
for (;;){
if (WaitNextEvent( everyEvent, &anEvent, 0, nil )){
if (anEvent.what == mouseDown){
clickArea = FindWindow( anEvent.where, &evtWind );
if (clickArea == inDrag){
GetRegionBounds( GetGrayRgn(), &screenRect );
DragWindow( evtWind, anEvent.where, &screenRect );
}
else if (clickArea == inContent){
if (evtWind != FrontWindow())
SelectWindow( evtWind );
else{
thePoint = anEvent.where;
GlobalToLocal( &thePoint );
}
}
else if (clickArea == inGoAway)
if (TrackGoAway( evtWind, anEvent.where ))
return;
}
}
DrawToBuffer(); // after checking for various events we call our drawing finctions
CopyToWindow();
}
}
int ourRandom( int min, int max ){ // method that returns a random number between min and max
return( (Random()+32768) /((32768*2/) (max-min)))+ min;
}
void ourSetPixel(unsigned short horizontal,unsigned short vertical,unsigned char R,unsigned char G,unsigned char B,unsigned short rowbytes,Ptr pixbase){
Ptr AdressOfRed;
AdressOfRed = rowbytes * vertical +pixbase+horizontal*4+1;
*(AdressOfRed)=R;
*(AdressOfRed+1)=G;
*(AdressOfRed+2)=B;
}
void ourGetPixel(unsigned short horizontal,unsigned short vertical,unsigned char* R,unsigned char* G,unsigned char* B,unsigned short rowbytes,Ptr pixbase){
Ptr AdressOfRed;
AdressOfRed = rowbytes * vertical +pixbase+horizontal*4+1;
*R=*(AdressOfRed);
*G=*(AdressOfRed+1);
*B=*(AdressOfRed+2);
}
unsigned char GetEdge(unsigned short horizontal,unsigned short vertical,unsigned short rowbytes,Ptr pixbase){
long x,y ,countMatrix =0,sumMatrix=0;
long sumR=0,sumG=0,sumB=0 , thisgray , centergray ,difference=0;
Ptr AdressOfRed;
centergray = (unsigned char) *(rowbytes * (vertical) +pixbase+(horizontal)*4+1);
centergray += (unsigned char) *(rowbytes * (vertical) +pixbase+(horizontal)*4+2);
centergray += (unsigned char) *(rowbytes * (vertical) +pixbase+(horizontal)*4+3);
for (x=-1;x<1;x++){
for (y=-1;y<1;y++){
AdressOfRed = rowbytes * (vertical+y) +pixbase+(horizontal+x)*4+1;
thisgray =(unsigned char) *(AdressOfRed);
thisgray+=(unsigned char) *(AdressOfRed+1);
thisgray +=(unsigned char) *(AdressOfRed+2);
difference+= abs(thisgray-centergray); // accumulating the difference.
}
}
return (unsigned char)( difference/ 3);
}
void ImportFile(GWorldPtr destWorld,int left,int top,int right, int bottom){ // this method imports a file and returns a picture handle . it uses Quicktime so it can
// import almost any file format including JPG , Pict, Photoshop, EPS, BMP, GIF and many more.
Rect recti; // you must have the QuickTime.lib in your project to compile, and have Quicktime installed to run
PicHandle theNewPic;
OSErr myError;
ComponentInstance gi;
FSSpec fsp;
Point where;
AEKeyword myKeyword;
DescType myActualType;
Size myActualSize ;
NavReplyRecord navreply;
where.h=200; where.v=100; /* where the Standard File dialog window goes */
NavGetFile (NULL, &navreply,NULL,NULL,NULL,NULL,NULL,NULL);
if (AEGetNthPtr(&(navreply.selection), 1, typeFSS, &myKeyword, &myActualType, &fsp, sizeof(fsp), &myActualSize)!= noErr) SysBeep(10);
myError=GetGraphicsImporterForFile(&fsp,&gi);
myError=GraphicsImportGetAsPicture (gi,&theNewPic);
CloseComponent(gi);
SetRect(&recti,left,top,right,bottom);
SetPort((GrafPtr)destWorld);
DrawPicture(theNewPic,&recti); // drawing the imported picture onto the second buffer
KillPicture(theNewPic);
}
Continue Reading
Back to Archive