2

I have a 8 bit color image . What is the method to convert this into a Grayscale Image .

For a normal 24 bit true color RGB image, we either perform averaging ( R + G + B ) / 3

And then there's' the Weighted Averaging wherein we calculate 0.21 R + 0.72 G + 0.07 B.

However these above formula works for a 24 bit image (correct me if i'm wrong) . Where 8 bits are used to denote R, G, B content each. Thus when we apply the above averaging methods, we get a resultant 8 bit grayscale image from a 24 bit True color image.

So how to calculate grayscale image for an 8 bit color image :

Please note : Structure of an 8 bit color image is as follows : Refer this link

Bit 7 6 5 4 3 2 1 0 Data R R R G G G B B 

As we can see,

  • Bits 7,6,5 denote Red content
  • Bits 4,3,2 denote Green content
  • Bits 1,0 denote Blue content

So the above image will actually have 4 shades in total (because, in grayscale, a white pixel is obtained when there is 100 % contribution of each of the R,G,B components. And since Blue component has only 2 bits, effectively, there are 22 combinations i.e. 4 levels. )

Therefore, if i consider 2 bits of R ,G and B, i manage to obtain gray levels as follows :

R G B GrayLevel 00 00 00 Black 01 01 01 Gray 1 10 10 10 Gray 2 11 11 11 White 

Which bits to consider from Red and Green components and which to ignore .!

How to quantify the graylevels for values of bits other than the ones mentioned above.

EDIT

I want to implement the above system upon an FPGA, hence memory is a keen aspect. Quality of the image doesn't matter much. Somehow is it possible to quantify all the values of the 8 bit color img into the respective gray shades ?

7
  • Why do you think it's a good idea to use only 4 levels? The reason less bits are used for blue, is because our eyes are least perceptible to change in blue (that is also the reason why blue contributes very little to the typical weighted average). You will have better results by scaling each value to a higher bit rate, then applying a weighted average. Commented Oct 12, 2015 at 18:16
  • But won't scaling up the image, use more memory (from the hardware aspect, if i want to implement this using a fpga or so ) . .. Commented Oct 12, 2015 at 18:25
  • Sure. But you don't seem to mention such a requirement in your question..? Normally, people want decent quality as well. Commented Oct 12, 2015 at 18:30
  • @haraldK , I've edited the post regarding using an fpga. What i feel is that using complex formula's like the weighted average is too complicated to be designed ( like speaking from the circuit point of view) .. (please correct me if i am wrong) . But in order to get more accurate grayscale images, we shifted to using 24 bit true color space. So that each of the R,G,B component has its dedicated 256 shades, and thereby giving a better quality picture. Commented Oct 12, 2015 at 18:44
  • I don't see your argument as to why you want 4 output levels - do you need to reduce your image from 8 bits per pixel to 2 bits per pixel so it takes 1/4 of the space? Why can't you make an 8 bit greyscale image? Or 4 bit? Please explain. Commented Oct 12, 2015 at 22:16

3 Answers 3

2

This approach gives output range of gray 0..255 (not all gray levels are used):

b = rgb8 & 3; g = (rgb8 >> 2) & 7; r = rgb8 >> 5; gray255 = 8 * b + 11 * r + 22 * g; 

If you have 256 bytes available, you can fill LUT (Look-Up Table) once, and use it instead of calculations:

grayimage[i] = LUT[rgb8image[i]]; 
Sign up to request clarification or add additional context in comments.

5 Comments

I agree, a look up table is the best approach in this scenario. But I'm not familiar with how you convert 8 bit RGB to gray. Is this the canonical way of doing it? Could you point me to a reference?
@onemasse This is typical bit manipulation that is often used in image processing. See for bit manupulation: stackoverflow.com/q/2249731/461499
I just extracted r,g,b bit fields and chosen reasonable coefficients gc,rc,bc considering that max value of b * bc+r * rc+g * gc = 3 * bc+7 * rc+7 * gc = 255 and gс/rс/bс ratio is about 7/3/1 (there is no need in exact values)
@MBo, ok then I understand. But if you're going to use a LUT you could use a proper calculation like Y' = 0.299 * R + 0.587 * G + 0.114 + B or the coefficients that OP mentions in his post.
@onemasse Yes, it possible. of course. Probably nobody sees difference for 2/3 bit color fields
0

If you really want to stick to 2 bits per gray pixel and you can afford simple multipliers, you can think of the formula

G = 5 x R + 9 x G + 4 B

where R and G are taken with 3 bits and B with just 2 (the coefficient has been adapted). This will yield a 7 bits value, in range [0,110], of which you will keep the most significant 2.

You may think to adapt the coefficients to occupy the four levels more evenly.

Comments

0

You essentially have a Rubik's cube of colours, which measures 8 x 8 x 4 if you can take a moment to imagine that. One side has 8 squares going from black to red, one side has 8 squares going from black to green and one side has 4 squares going from black to blue.

In essence, you can divide it up how you like since you don't care too much for quality. So, if you want 4 output grey levels, you can essentially make any two cuts you like and lump together everything inside each of the resulting shapes as a single grey level. Normally, you would aim to make the volumes of each lump the same - so you could cut the red side in half and the green side in half and ignore any differences in the blue channel as one option.

One way to do it might be to make equi-volumed lumps according to the distance from the origin, i.e. from black. I don't have an 8x8x4 cube available, but imagine the Earth was 8x8x4, then we would be making all pixels in the inner core black, those in the outer core dark grey, those in the mantle light grey and the crust white - such that the number of your original pixels in each lump was the same. It sounds complicated but isn't!

enter image description here

Let's run through all your possible Red, Green and Blue values and calculate the distance of each one from black, using

d=R^2 +G^2 +B^2 

then sort the values by that distance and then number the lines:

#!/bin/bash for r in 0 1 2 3 4 5 6 7; do for g in 0 1 2 3 4 5 6 7; do for b in 0 1 2 3; do # Calculate distance from black corner (r=g=b=0) - actually squared but it doesn't matter ((d2=(r*r)+(g*g)+(b*b))) echo $d2 $r $g $b done done done | sort -n | nl # sort numerically by distance from black, then number output lines sequentially 

That gives this where the first column is the line number, the second column is the distance from black (and the values are sorted by this column), and then there follows R, G and B:

 1 0 0 0 0 # From here onwards, pixels map to black 2 1 0 0 1 3 1 0 1 0 4 1 1 0 0 5 2 0 1 1 6 2 1 0 1 7 2 1 1 0 8 3 1 1 1 9 4 0 0 2 10 4 0 2 0 11 4 2 0 0 12 5 0 1 2 13 5 0 2 1 14 5 1 0 2 15 5 1 2 0 16 5 2 0 1 17 5 2 1 0 18 6 1 1 2 19 6 1 2 1 20 6 2 1 1 21 8 0 2 2 22 8 2 0 2 23 8 2 2 0 24 9 0 0 3 25 9 0 3 0 26 9 1 2 2 27 9 2 1 2 28 9 2 2 1 29 9 3 0 0 30 10 0 1 3 31 10 0 3 1 32 10 1 0 3 33 10 1 3 0 34 10 3 0 1 35 10 3 1 0 36 11 1 1 3 37 11 1 3 1 38 11 3 1 1 39 12 2 2 2 40 13 0 2 3 41 13 0 3 2 42 13 2 0 3 43 13 2 3 0 44 13 3 0 2 45 13 3 2 0 46 14 1 2 3 47 14 1 3 2 48 14 2 1 3 49 14 2 3 1 50 14 3 1 2 51 14 3 2 1 52 16 0 4 0 53 16 4 0 0 54 17 0 4 1 55 17 1 4 0 56 17 2 2 3 57 17 2 3 2 58 17 3 2 2 59 17 4 0 1 60 17 4 1 0 61 18 0 3 3 62 18 1 4 1 63 18 3 0 3 64 18 3 3 0 # From here onwards pixels map to dark grey 65 18 4 1 1 66 19 1 3 3 67 19 3 1 3 68 19 3 3 1 69 20 0 4 2 70 20 2 4 0 71 20 4 0 2 72 20 4 2 0 73 21 1 4 2 74 21 2 4 1 75 21 4 1 2 76 21 4 2 1 77 22 2 3 3 78 22 3 2 3 79 22 3 3 2 80 24 2 4 2 81 24 4 2 2 82 25 0 4 3 83 25 0 5 0 84 25 3 4 0 85 25 4 0 3 86 25 4 3 0 87 25 5 0 0 88 26 0 5 1 89 26 1 4 3 90 26 1 5 0 91 26 3 4 1 92 26 4 1 3 93 26 4 3 1 94 26 5 0 1 95 26 5 1 0 96 27 1 5 1 97 27 3 3 3 98 27 5 1 1 99 29 0 5 2 100 29 2 4 3 101 29 2 5 0 102 29 3 4 2 103 29 4 2 3 104 29 4 3 2 105 29 5 0 2 106 29 5 2 0 107 30 1 5 2 108 30 2 5 1 109 30 5 1 2 110 30 5 2 1 111 32 4 4 0 112 33 2 5 2 113 33 4 4 1 114 33 5 2 2 115 34 0 5 3 116 34 3 4 3 117 34 3 5 0 118 34 4 3 3 119 34 5 0 3 120 34 5 3 0 121 35 1 5 3 122 35 3 5 1 123 35 5 1 3 124 35 5 3 1 125 36 0 6 0 126 36 4 4 2 127 36 6 0 0 128 37 0 6 1 129 37 1 6 0 # From here onwards pixels map to light grey 130 37 6 0 1 131 37 6 1 0 132 38 1 6 1 133 38 2 5 3 134 38 3 5 2 135 38 5 2 3 136 38 5 3 2 137 38 6 1 1 138 40 0 6 2 139 40 2 6 0 140 40 6 0 2 141 40 6 2 0 142 41 1 6 2 143 41 2 6 1 144 41 4 4 3 145 41 4 5 0 146 41 5 4 0 147 41 6 1 2 148 41 6 2 1 149 42 4 5 1 150 42 5 4 1 151 43 3 5 3 152 43 5 3 3 153 44 2 6 2 154 44 6 2 2 155 45 0 6 3 156 45 3 6 0 157 45 4 5 2 158 45 5 4 2 159 45 6 0 3 160 45 6 3 0 161 46 1 6 3 162 46 3 6 1 163 46 6 1 3 164 46 6 3 1 165 49 0 7 0 166 49 2 6 3 167 49 3 6 2 168 49 6 2 3 169 49 6 3 2 170 49 7 0 0 171 50 0 7 1 172 50 1 7 0 173 50 4 5 3 174 50 5 4 3 175 50 5 5 0 176 50 7 0 1 177 50 7 1 0 178 51 1 7 1 179 51 5 5 1 180 51 7 1 1 181 52 4 6 0 182 52 6 4 0 183 53 0 7 2 184 53 2 7 0 185 53 4 6 1 186 53 6 4 1 187 53 7 0 2 188 53 7 2 0 189 54 1 7 2 190 54 2 7 1 191 54 3 6 3 192 54 5 5 2 193 54 6 3 3 # From here onwards pixels map to white 194 54 7 1 2 195 54 7 2 1 196 56 4 6 2 197 56 6 4 2 198 57 2 7 2 199 57 7 2 2 200 58 0 7 3 201 58 3 7 0 202 58 7 0 3 203 58 7 3 0 204 59 1 7 3 205 59 3 7 1 206 59 5 5 3 207 59 7 1 3 208 59 7 3 1 209 61 4 6 3 210 61 5 6 0 211 61 6 4 3 212 61 6 5 0 213 62 2 7 3 214 62 3 7 2 215 62 5 6 1 216 62 6 5 1 217 62 7 2 3 218 62 7 3 2 219 65 4 7 0 220 65 5 6 2 221 65 6 5 2 222 65 7 4 0 223 66 4 7 1 224 66 7 4 1 225 67 3 7 3 226 67 7 3 3 227 69 4 7 2 228 69 7 4 2 229 70 5 6 3 230 70 6 5 3 231 72 6 6 0 232 73 6 6 1 233 74 4 7 3 234 74 5 7 0 235 74 7 4 3 236 74 7 5 0 237 75 5 7 1 238 75 7 5 1 239 76 6 6 2 240 78 5 7 2 241 78 7 5 2 242 81 6 6 3 243 83 5 7 3 244 83 7 5 3 245 85 6 7 0 246 85 7 6 0 247 86 6 7 1 248 86 7 6 1 249 89 6 7 2 250 89 7 6 2 251 94 6 7 3 252 94 7 6 3 253 98 7 7 0 254 99 7 7 1 255 102 7 7 2 256 107 7 7 3 

Obviously, the best way to do that is with a lookup table, which is exactly what this is.

Just for kicks, we can look at how it performs if we make some sample images with ImageMagick and process them with this lookup table:

# Make a sample convert -size 100x100 xc: -sparse-color Bilinear '30,10 red 10,80 blue 70,60 lime 80,20 yellow' -resize 400x400! gradient.png 

enter image description here

# Process with suggested LUT convert gradient.png -fx "@lut.fx" result.png 

enter image description here

lut.fx implements the LUT and looks like this:

dd=(49*r*r)+(49*g*g)+(16*b*b); (dd < 19) ? 0.0 : ((dd < 38) ? 0.25 : ((dd < 54) ? 0.75 : 1.0)) 

By comparison, if you implement my initial suggestion at the start of my answer, by doing:

R < 0.5 && G < 0.5 => black result R < 0.5 && G >= 0.5 => dark grey result R >= 0.5 && G < 0.5 => light grey result r >= 0.5 && G >= 0.5 => white result 

You will get this output - which, as you can see, is better at differentiating red from green, but worse at reflecting the brightness of the original.

enter image description here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.