I have been looking for a CImg function that would translate a RGB image to grayscale directly, but couldn’t find one. This is what I did, it could probably help you with this task.

It separates all 3 RGB channels and then calculates two different gray image versions –one standard arithmetic and the other using different weights for each channel.

#include "CImg.h"
#include  <iostream>

// Use the library namespace to ease the declarations afterward.
using namespace cimg_library;
using namespace std;

int main() {

  * VARIABLES ---------------------
  * Creation of two instances of images of unsigned char pixels.
  * The first image _image_ is initialized by reading an image file from the disk.
  * Here, lena.jpg must be in the same directory than the current program.
  * Note that you must also have installed the ImageMagick package
  * in order to be able to read JPG images.
  * var(size_x, size_y, size_z, dv, default_fill)
  CImg<unsigned char> image("images/lena.jpg"),
          gray(image.width(), image.height(), 1, 1, 0),
          grayWeight(image.width(), image.height(), 1, 1, 0),
          imgR(image.width(), image.height(), 1, 3, 0),
          imgG(image.width(), image.height(), 1, 3, 0),
          imgB(image.width(), image.height(), 1, 3, 0);

  // for all pixels x,y in image
  cimg_forXY(image,x,y) {
    imgR(x,y,0,0) = image(x,y,0,0),    // Red component of image sent to imgR
    imgG(x,y,0,1) = image(x,y,0,1),    // Green component of image sent to imgG
    imgB(x,y,0,2) = image(x,y,0,2);    // Blue component of image sent to imgB

    // want to print out the RGB value of each pixel? Uncomment the following:
    * cout << image.width() << "x" << image.height() << endl;
    * cout << "(" << x << "," << y << ") ="
    *             << " R:" << (int)image(x,y,0,0)
    *             << " G:" << (int)image(x,y,0,1)
    *             << " B:" << (int)image(x,y,0,2) << endl;

    // Separation of channels
    int R = (int)image(x,y,0,0);
    int G = (int)image(x,y,0,1);
    int B = (int)image(x,y,0,2);
    // Arithmetic addition of channels for gray
    int grayValue = (int)(0.33*R + 0.33*G + 0.33*B);
    // Real weighted addition of channels for gray
    int grayValueWeight = (int)(0.299*R + 0.587*G + 0.114*B);
    // saving píxel values into image information
    gray(x,y,0,0) = grayValue;
    grayWeight(x,y,0,0) = grayValueWeight;

  // 4 display windows, one for each image
  CImgDisplay main_disp(image,"Original"),
      draw_dispGrWeight(grayPond,"Gray (Weighted)");

  // wait until main window is closed
  while (!main_disp.is_closed()){

  return 0;


lena in channels

lena in gray

But why use weighted gray images?

Because our eyes don’t perceive all frequencies of visible light, or color, in the same way. We have more M cone opsines than L or S, so we are more sensible to light from the green frequencies. The following graph shows the relative brightness sensitivity of the human visual system as a function of wavelength.

Eye Sensitivity


To see the difference between a weighted image and one that it not, have a close look at the example above. Can you see how we managed to save the luminance of the original image?

Author: Bea Cabrera

Freelance Filmmaker with a passion for big cities, snowboard, cinema and a weakness for the smell of freshly ground coffee. Engineer & Graphic Designer in a previous life, loving and living both: art and technology.  

10 Replies to “RGB to Grayscale using CImg”

  1. bvalabas says: February 19, 2014 at 2:26 pm

    There is a simpler way to do this. Basically, you want to compute the so-called ‘luminance’ map of the RGB colors, and you can do this with one line in CImg :


    1. bea cabrera says: February 19, 2014 at 2:56 pm

      But in that case you can’t have a weighted mix of the different color channels, right?

  2. bvalabas says: February 20, 2014 at 9:59 am

    This is actually a weighted mix of the channels, with more weights to the green channel.
    If you need to specify your own weights, you can also do a simple thing like this :

    CImgList channels = img<'c';
    ((channels[0]*=0.2) += (channels[1]*=0.6) += (channels[2]*=0.4)).move_to(img);

    with weights 0.1, 0.6 and 0.3 here :)

    1. bea cabrera says: February 20, 2014 at 10:10 am

      Is it by default? I didn’t know. But don’t you think that having to convert the whole image to another color format is consuming? After all, your total solution is 3 lines and mine’s 4… Strip away the fuzz and printing the other three channels separately and you’re left with the essentials:

      >> int R = (int)image(x,y,0,0);
      >> int G = (int)image(x,y,0,1);
      >> int B = (int)image(x,y,0,2);
      >> int grayValueWeight = (int)(0.299*R + 0.587*G + 0.114*B);

      So in your opinion why should I convert the whole image first?

  3. Ray says: March 26, 2014 at 10:07 am

    I tried to test it but it would not compile and run; it said “image.cimg.library::CImg::width’ cannot be used as a function…

    1. Bea says: March 26, 2014 at 11:18 am

      Which line exactly is the error on?

      1. HighlanderBQ says: October 17, 2016 at 1:48 am

        yo tuve uno en la linea 60: |60|error: ‘grayPond’ was not declared in this scope|

  4. Ray says: March 26, 2014 at 10:08 am

    Thank you very much for your post! by the way :) I find it hard to employ image processes with CImg because we haven’t been taught much in school…

  5. arch says: September 8, 2015 at 7:30 pm

    Thank you..this worked perfectly for the project I was working on!

    1. bea cabrera says: September 8, 2015 at 7:40 pm

      Thank YOU for stopping by! I’m glad it could help.

Leave a Reply

Your email address will not be published.

You may use these <abbr title="HyperText Markup Language">html</abbr> tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


This site uses Akismet to reduce spam. Learn how your comment data is processed.