April 5, 2008
WikiAdjusting Saturation
Adjusting Saturation
Step by Step
This algorithm adjusts the color saturation of an RGB color image in a similar manner as the color control on a color television. The algorithm is based upon the R-Y, G-Y, and B-Y color model used in many of today’s color televisions. The color components in terms of R,G,B are: R-Y = 0.70R-0.59G-0.11B B-Y = -0.30R-0.59G+0.89B G-Y= -0.30R+0.41G-0.11B,
where the illuminance component Y is defined as:
Y = 0.30R +0.59G+ 0.11B
In the program saturation is the value that could be changed the amount of color to be added back to the illuminance or black and white image. The first thing program does is compute the R-Y, G-Y, and B-Y components. Next, it scales these values using the variable saturation and adds them back to the illuminance component to generate the new RGB values. Upon completion of the program, the saturated corrected image is returned in the three structures R,G,B.
if (mousepoint.h < 100 )mousepoint.h =100;
// checking that we will not be setting and getting pixels outside of the
// pixmap
if (mousepoint.v < 100 )mousepoint.v =100;
// otherwise we will crash the computer
if (mousepoint.h > windRect.right-100 )mousepoint.h =windRect.right-100;
if (mousepoint.v > windRect.bottom-100 )mousepoint.v =windRect.bottom-100;
for(x=mousepoint.h-100;x<mousepoint.h+100;x++){
// repeating for a 200 x 200 square around the mouse
for(y=mousepoint.v-100;y<mousepoint.v+100;y++){
ourGetPixel(x,y,&R,&G,&B,imageRowBytes,imageBaseAddress);
// geting the color value of a pixel in our image buffer
// , note that we need to pass
// the address (!) of three unsigned chars to hold
// the values
saturation = 0;
R1 = R;G1=G;B1 = B;
// this is from the litle C refrence book
RY1 = (70*R1-59*G1-11*B1/)100;
BY1 = (-30*R1-59*G1+89*B1/)100;
GY1 = (-30*R1+41*G1-11*B1/)100;
Y = (30 *R1 +59 *G1+11*B1/)100;
RY = (RY1 *saturation/)100;
GY = (GY1 *saturation/)100;
BY = (BY1 *saturation/)100;
temp = RY+Y;
// checking that our values will not exceed 255 or be negative
if (temp > 255) temp = 255; else if (temp < 0) temp = 0;
R = temp;
temp = GY+Y;
if (temp > 255) temp = 255; else if (temp < 0) temp = 0;
G = temp;
temp = BY+Y;
if (temp > 255) temp = 255; else if (temp < 0) temp = 0;
B = temp;
ourSetPixel(x,y,R,G,B,bufferRowBytes,bufferBaseAddress);
// calling our method that set individual pixels and expects :
// X coordinate of the pixel,
// y coordinate of the pixel, R value, G value, B value, the
// number of bytes in each row of the pixel map, the base
// address in memory of the begining of the pixel data
}
}
SetPort((GrafPtr)ourBuffer);
SetRect(&Recti,mousepoint.h-100,mousepoint.v-100,mousepoint.h+100,
mousepoint.v+100);
FrameRect(&Recti);
// drawing a frame around the affected area
}
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);
//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, R1 ,G1,B1,RY1,BY1,GY1,RY,BY,GY,Y,saturation;
Point mousepoint;
unsigned short imageRowBytes,bufferRowBytes;
Ptr imageBaseAddress,bufferBaseAddress;
PixMapHandle imagePixmap,bufferPixmap;
unsigned char R,G,B ;
Rect Recti;
SetPortWindowPort(ourWindow); // set our window as the "port" . It's important , otherwise we will get the mouse location
// in global coordinates , not the coordinates of our window
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
// only manipulating small portion of the image instead of the whole!
if (mousepoint.h < 100 )mousepoint.h =100;
// checking that we will not be setting and getting pixels outside of the pixmap
if (mousepoint.v < 100 )mousepoint.v =100;
// otherwise we will crash the computer
if (mousepoint.h > windRect.right-100 )mousepoint.h =windRect.right-100;
if (mousepoint.v > windRect.bottom-100 )mousepoint.v =windRect.bottom-100;
for(x=mousepoint.h-100;x<mousepoint.h+100;x++){
// repeating for a 200 x 200 square around the mouse
for(y=mousepoint.v-100;y<mousepoint.v+100;y++){
ourGetPixel(x,y,&R,&G,&B,imageRowBytes,imageBaseAddress);
// geting the color value of a pixel in our image buffer , note that we need to pass
// the address (!) of three unsigned chars to hold the values
saturation = 0;
R1 = R;G1=G;B1 = B;
// this is from the litle C refrence book
RY1 = (70*R1-59*G1-11*B1/)100;
BY1 = (-30*R1-59*G1+89*B1/)100;
GY1 = (-30*R1+41*G1-11*B1/)100;
Y = (30 *R1 +59 *G1+11*B1/)100;
RY = (RY1 *saturation/)100;
GY = (GY1 *saturation/)100;
BY = (BY1 *saturation/)100;
temp = RY+Y;
// checking that our values will not exceed 255 or be negative
if (temp > 255) temp = 255; else if (temp < 0) temp = 0;
R = temp;
temp = GY+Y;
if (temp > 255) temp = 255; else if (temp < 0) temp = 0;
G = temp;
temp = BY+Y;
if (temp > 255) temp = 255; else if (temp < 0) temp = 0;
B = temp;
ourSetPixel(x,y,R,G,B,bufferRowBytes,bufferBaseAddress);
// calling our method that set individual pixels and expects : X coordinate of the pixel,
// y coordinate of the pixel, R value, G value, B value, the number of bytes in each row of the pixel map, the base
// address in memory of the begining of the pixel data
}
}
SetPort((GrafPtr)ourBuffer);
SetRect(&Recti,mousepoint.h-100,mousepoint.v-100,mousepoint.h+100,mousepoint.v+100);
FrameRect(&Recti);
// drawing a frame around the affected area
}
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();
}
void Initialize(void){
OSErr error;
SetRect(&windRect,100,100,740,580);
InitCursor();
ourWindow = NewCWindow( 0L, &windRect, "\pAdjust Saturation", 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);
}
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