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 ?

kushal
  • 301
  • 1
  • 7
  • 18
  • 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. – Harald K Oct 12 '15 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 ) . .. – kushal Oct 12 '15 at 18:25
  • Sure. But you don't seem to mention such a requirement in your question..? Normally, people want decent quality as well. – Harald K Oct 12 '15 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. – kushal Oct 12 '15 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. – Mark Setchell Oct 12 '15 at 22:16
  • just bear me with me for a moment here.. We get 256 levels cause that are the number of combinations we get for a true color image .. Right ? 28.So per pixel, R, G, B are stored in 8 bit registers. So each has 256 levels. Now take an example of a 16-bit color depth. There are 4 components.. 4 bits for R,G,B, and alpha . So here max combinations will be 2 4 i.e.16 possible levels . Therefore in an 8 bit color image , since there is unequal distributions of bits amongst the pixels, blue component acts some what of a limiting value. – kushal Oct 13 '15 at 05:04
  • and so it should have 2 2 i.e. 4 levels. Despite the fact that R & G will have 8 levels. So effectively i should have an image having 4 gray levels. Like it'd be great if i could understand why is this happening on a more lower level. @MarkSetchell – kushal Oct 13 '15 at 05:08

3 Answers3

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]];
MBo
  • 77,366
  • 5
  • 53
  • 86
  • 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 Oct 13 '15 at 07:11
  • @onemasse This is typical bit manipulation that is often used in image processing. See for bit manupulation: http://stackoverflow.com/q/2249731/461499 – Rob Audenaerde Oct 13 '15 at 07:20
  • 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 Oct 13 '15 at 07:22
  • @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 Oct 13 '15 at 07:55
  • @onemasse Yes, it possible. of course. Probably nobody sees difference for 2/3 bit color fields – MBo Oct 13 '15 at 08:05
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.

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

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432