Re: Drawing NSImage in grayscale
Re: Drawing NSImage in grayscale
- Subject: Re: Drawing NSImage in grayscale
- From: Peter Ammon <email@hidden>
- Date: Sun, 2 Dec 2001 16:08:35 -0500
On Sunday, December 2, 2001, at 03:21 PM, Tom Waters wrote:
gray = .56g + .33r + .11b as i recall.
On Saturday, December 1, 2001, at 01:33 PM, John C. Randolph wrote:
On Saturday, December 1, 2001, at 10:35 AM, Peter Ammon wrote:
On Friday, November 30, 2001, at 05:13 PM, Peter Ammon wrote:
Are there any easy techniques (e.g. compositing operations,
perhaps in several passes) for drawing a color NSImage
instantiated from a tiff file in grayscale?
The only way I can think of would be to create an
NSBitmapImageRep with one data plane and draw into that.
Thanks for any ideas!
-Peter
I went ahead and implemented my idea, so here it is in case
someone else can get use out of it. It seems to work for the
meshed images I've tried; I haven't tested it with planar
images.
Peter,
Your code will give a good approximation, but I suggest that you
have a look in Newman & Sproul for the standard RGB weighting
function. Basically, we're much more sensitive to light in the
green band than the blue, and there is a formula that accounts
for this.
-jcr
Hi, and thanks for the help Tom and John. Indeed, changing with the
weights really improved the appearance of the grayscale image.
According to the good folks on comp.graphics.algorithms, there's not a
standard set of weights. The right thing to do seems to be to play
around with it until you get results that look the way you want them
to. I found that NTSC (r*0.299 + g*0.587 + b*0.114) looks good for my
particular images.
I made a small change in my previous method before I posted it which
caused it to break, so somewhat guiltily, I repost a fixed version of
the entire thing here:
- (NSImage*)grayscale {
const double conversionWeights[3]={.299, .587, .111};
NSArray* currentReps=[self representations];
NSData* tiffData=[NSBitmapImageRep
TIFFRepresentationOfImageRepsInArray:currentReps
usingCompression:NSTIFFCompressionNone
factor:0.0];
NSBitmapImageRep* bitmap=[NSBitmapImageRep
imageRepWith
Data:tiffData];
//we don't know how to handle anything but CHAR_BIT bits per sample
NSAssert1([bitmap bitsPerSample]==CHAR_BIT, @"bitsPerSample is %d in
grayscale method", [bitmap bitsPerSample]);
unsigned char* planes[5];
[bitmap getBitmapDataPlanes:planes];
int numberPlanes=0;
while (numberPlanes < 5 && planes[numberPlanes]) numberPlanes++;
const BOOL hasAlpha=[bitmap hasAlpha];
const int samples=[bitmap samplesPerPixel];
NSBitmapImageRep* grayBitmap=[[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:nil
pixelsWide:[bitmap pixelsWide]
pixelsHigh:[bitmap pixelsHigh]
bitsPerSample:8
samplesPerPixel:
hasAlpha ? 2 : 1
hasAlpha:
hasAlpha
isPlanar:YES
colorSpaceName:NSCalibratedWhiteColorSpace
bytesPerRow:0
bitsPerPixel:0]
autorelease];
if (! grayBitmap) return nil;
unsigned char* destDataPlanes[2];
[grayBitmap getBitmapDataPlanes:destDataPlanes];
unsigned char* grayData=destDataPlanes[0];
unsigned char* alphaData=destDataPlanes[1];
if (! [bitmap isPlanar]) {
int height, maxHeight=[bitmap pixelsHigh], bytesPerRow=[bitmap
bytesPerRow], rowLength=[bitmap pixelsWide]*samples;
unsigned char* writer=grayData;
unsigned char* alpha=alphaData;
for (height=0; height<maxHeight; height++) {
unsigned char* start=planes[0]+height*bytesPerRow;
unsigned char* pos=start;
while (pos-start < rowLength) {
int sampleCounter=samples;
double average=0;
if (sampleCounter==3 || (sampleCounter==4 && hasAlpha)) {
average+=conversionWeights[0]**pos++;
average+=conversionWeights[1]**pos++;
average+=conversionWeights[2]**pos++;
if (hasAlpha) *alpha++=*pos++;
*writer++=(unsigned char)average;
}
else {
if (hasAlpha) {
while (--sampleCounter) average+=*pos++;
*writer++=(unsigned char)(average/(samples-1));
*alpha++=*pos++;
}
else {
while (sampleCounter--) average+=*pos++;
*writer++=(unsigned char)(average/samples);
}
}
}
}
}
else { //bitmap is planar
int height, maxHeight=[bitmap pixelsHigh], bytesPerRow=[bitmap
bytesPerRow], rowLength=[bitmap pixelsWide]*samples;
unsigned char* writer=grayData;
unsigned char* alpha=alphaData;
for (height=0; height<maxHeight; height++) {
int offset=height*bytesPerRow;
int i;
for (i=0; i<rowLength; i++) {
int sampleCounter=samples;
double average=0;
if (sampleCounter==3 || (sampleCounter==4 && hasAlpha)) {
average+=conversionWeights[0]*planes[0][i+offset];
average+=conversionWeights[1]*planes[1][i+offset];
average+=conversionWeights[2]*planes[2][i+offset];
if (hasAlpha) *alpha++=planes[3][i+offset];
*writer++=(unsigned char)average;
}
else {
if (hasAlpha) {
*alpha++=planes[--sampleCounter][i+offset];
while (sampleCounter--)
average+=planes[sampleCounter][i+offset];
*writer++=(unsigned char)(average/(samples-1));
}
else {
while (sampleCounter--)
average+=planes[sampleCounter][i+offset];
*writer++=(unsigned char)(average/samples);
}
}
}
}
}
NSImage* newImage=[[[NSImage alloc] initWithSize:[self size]]
autorelease];
[newImage addRepresentation:grayBitmap];
return newImage;
}