• Advertisement


This topic is now archived and is closed to further replies.

Converting a color image to a grayscale image

This topic is 5574 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am programming a small game for my computer science course and I was wondering if either Direct X or Windows GDI has a function that will let me convert a colored image to a grayscale image.

Share this post

Link to post
Share on other sites
It can''t be hard doing it yourself.

I notice that Paint Shop Pro, when converting a color image to grayscale, converts red (255,0,0) to gray 76, green (0,255,0) to gray 150, and blue (0,0,255) to gray 28. 76+150+28 = 254, close to 255, which is white.

Therefore, probably to convert a picture to grayscale, you take the red component and multiply it by 76/255 (to scale it from 0..255 down to 0..76), multiply the green by 150/255, and the blue by 28/255 (be sure to use floating point and then convert to integer/byte). Then add those values together and that is the gray value.

So cyan (0,255,255) becomes 150+28 = gray 178, or RGB(178,178,178). Orange (255,128,0) becomes 76+75 = gray 151, or RGB(151,151,151).

~CGameProgrammer( );

Share this post

Link to post
Share on other sites
Ya that is what i was thinking of doing but it would be a very slow process when you have to do it for a 640x480 surface when you are doing it many times in a row.

Share this post

Link to post
Share on other sites
The conversion for color to grayscale is:
R*0.30 + G*0.59 + B*0.11
since green is what our eyes are most sensitive to, then red, then blue, this will give a decent brightness level in the final grayscale image.

You can accomplish this in real-time on geforce-level hardware by using a dot3 operation. You can render your scene to a texture that can contain an image large enough to contain the backbuffer, then render that texture back into the backbuffer on a big quad using the dot3 operation. in d3d texture stage terms:

setrenderstate(texturefactor, ARGB(1, 0.30, 0.59, 0.11))
color arg1: tfactor
color arg2: texture containing final scene
color op: dotproduct3

hope this helps

Share this post

Link to post
Share on other sites
It''s a very nice solution indeed
A real beauty !

I rushed to read the SDK on the dotproduct3 colorop
And I had to conclude: this will really work !

Except it doesn''t for me :-(

The dotproduct3 operand seems to do nothing at all
I just get the original texture

Can it be that this is not supported on my videocard ?
( it''s a Matrox G550 supposed to be of type Geforce2 )

Or may my choices of memorypool, usage and other parameters
be wrong when I create devices, textures and the like ?

I have also never got the MIPMAPLODBIAS to have any effect :-|
(if that could be in any way related ..)

Share this post

Link to post
Share on other sites
well, i did this once. i sum all red, green, and blue components and divide it by 3, then use the result as your final red, green, and blue.

For example:
Red: 120
Green: 50
Blue: 100
Total = 120 + 50 + 100 = 270
Divide it by 3 = 270 / 3 = 90

New value:
Red: 90
Green: 90
Blue: 90

It works OK. I don''t know with Assasin''s method. Looks like it''s better.

My compiler generates one error message: "does not compile."

Share this post

Link to post
Share on other sites
Fire up the DX Caps Viewer, and navigate down to
DirectX Graphics Adapters / video_card_here / D3D Device Types / HAL / Caps / TextureOpCaps

and see if it lists D3DTEXOPCAPS_DOTPRODUCT3 along with "Yes" in there. If not, then your card might not support the dot3 operation. If it says that the operation is supported, it could be something else. I'm not seeing a caps entry to indicate whether the texturefactor renderstate is supported, so I'm assuming that it's a required thing and should be there too.

The dot3 operation should replicate a single value throughout all the color channels (R,G,B, and alpha all get the same value) which should make it a grayscale image. I'm not sure if you're doing the whole render-to-texture thing, but you can probably get the same result by rendering your normal geometry using the same texture stage setup and putting in a default texture on the object instead of the texture holding the whole scene. That should basically just give you a grayscale representation of the texture applied to the object. You can get more fancy and throw lighting stuff into the mix too, but the basic effect should work if your hardware supports the dot3 operation.

[edited by - Assassin on November 21, 2002 4:13:18 AM]

Share this post

Link to post
Share on other sites
Original post by CGameProgrammer
alnite: The reason you can''t average the red/green/blue is that they are not the same brightness. Green is brightest, then red, then blue.

Well, I didn''t know about that technical stuff. I did it about three years ago in VB. But it still works. You get grayscale, not greenscale.

My compiler generates one error message: "does not compile."

Share this post

Link to post
Share on other sites
Assassin thank you very much
You have been indbiocuousoelasicasicalistically helpful :-)

My D3DTEXOPCAPS_DOTPRODUCT3 is listed with "No" (under HAL)

The reference device says "yes" however
So I tried to create my device as a reference device

But that doesn''t work either ..

Share this post

Link to post
Share on other sites
OK, this is apparently more complicated than I had first thought (in Direct3D at least). The reason? Direct3D's dotproduct3 operation first converts the input values into signed values, in the range [-1,1] when they were in fact in the range [0,1] to begin with. When doing normalmapping, this is useful because the normals can point in any direction. However, for our purposes we want to be using unsigned values since we're dealing in colorspace and not a geometric space. I'll mention this on the directxdev list in a little while, I've been generally unimpressed with the flexbility of the d3d texture stage states.

Moving on, the solution: Make a texture (8x8 pixels or 1x1, whatever you want) that is filled with the following color: RGB(166,203,143). That is the 0.30, 0.59, 0.11 values converted into the [0,1] space in a [-1,1] range; in other words, (color*0.5) + 0.5. That color is a kind of light puke-green. Now that you have that texture, it's time for some trickery. Put the value 0x80ffffff into your texturefactor state (0.5 alpha, 1.0 RGB) and set up the first stage to do D3DTOP_BLENDFACTORALPHA using TEXTURE and TFACTOR arguments, where the texture is whichever texture you will be rendering in grayscale. The 2nd stage is then D3DTOP_DOTPRODUCT3 using TEXTURE and CURRENT as arguments, where the texture used is the special texture filled with the magic value. Hey presto, grayscale rendering!

What's happening? the blendfactoralpha operation does
alpha*arg1 + (1-alpha)*arg2
so with the specified tfactor and arguments, you get
0.5*texture + 0.5*1 .... or (texture*0.5) + 0.5 which is what we needed up above to get the color value into the right space for the signed dotproduct. Then the dotproduct does its thing to get a grayscale result from the inputs, operating on signed values that are in fact both in the [0,1] subset of the [-1,1] range being used. The main result of this is that you will see noticeable banding on smooth gradients, since it's using only 128 colors instead of 256.

If you could invoke an unsigned dotproduct operation, you could do away with this whole mess and do it in a single stage, but whoever designed the direct3d texture stage interface was being pretty closed-minded. OpenGL lets you do it with nVidia register combiner extensions quite easily.

Hope this helps.

Addendum: you can actually replace the custom texture thing with a vertex color entry if you're doing the render-to-texture- then-render-texture-to- backbuffer-on-a-big-quad thing, by simply filling in the diffuse element in the vertex data with the same color that the texture would contain. Then use DIFFUSE instead of TEXTURE in the 2nd stage when doing the dot3.

[edited by - Assassin on November 21, 2002 8:37:48 PM]

Share this post

Link to post
Share on other sites
After actually installing the sdk,
I now have a socalled "reference device" up and running

And using this reference device, my dotprotuct3 and
LODbias is working as they should !!
(veeery slooow and of absolutely no use of course)

My HAL caps reported "no" for both of them

So now I see what you meant in your latest reply Assasin.
The image is greyscale allright, but somewhat dark
and inverted-like ..

I want a new Geforce 4 card for christmas :-)

Again thank you

CGameProgrammer .. you are somewhat missing our point here.
We are able to write a function that converts to greyscale,
but that is not what we want to do at all.
We want to apply texture stages to convert our image
in real time, as this would be much faster!

And the Dotproduct3 colorop is the right way to go
(if your card suports it) ...

Share this post

Link to post
Share on other sites

  • Advertisement