Grayscale image: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Go}}: fix compile errors)
(→‎{{header|REXX}}: added the REXX language. -- ~~~~)
Line 997: Line 997:
# Convert back to "colour"
# Convert back to "colour"
plot(p3 <- as(p2, "pixmapRGB"))</lang>
plot(p3 <- as(p2, "pixmapRGB"))</lang>

=={{header|REXX}}==
<lang rexx>/*REXX program to convert a RGB image to grayscale. */

blue='00 00 ff'x /*define the blue color. */
image.=blue /*set the entire IMAGE to blue. */
width= 60 /* width of the IMAGE. */
height=100 /*height " " " */

do j=1 for width
do k=1 for height
r= left(image.j.k,1) ; r=c2d(r) /*extract red & convert*/
g=substr(image.j.k,2,1) ; g=c2d(g) /* " green " " */
b= right(image.j.k,1) ; b=c2d(b) /* " blue " " */
ddd=right(trunc(.2126*r + .7152*g + .0722*b),3,0) /*──► greyscale.*/
image.j.k=right(d2c(ddd,6),3,0) /*... and transform back.*/
end
end</lang>


=={{header|Ruby}}==
=={{header|Ruby}}==

Revision as of 00:46, 12 May 2012

Task
Grayscale image
You are encouraged to solve this task according to the task description, using any language you may know.

Many image processing algorithms are defined for grayscale (or else monochromatic) images. Extend the data storage type defined on this page to support grayscale images. Define two operations, one to convert a color image to a grayscale image and one for the backward conversion. To get luminance of a color use the formula recommended by CIE:

L = 0.2126·R + 0.7152·G + 0.0722·B

When using floating-point arithmetic make sure that rounding errors would not cause run-time problems or else distorted results when calculated luminance is stored as an unsigned integer.

Ada

<lang ada>type Grayscale_Image is array (Positive range <>, Positive range <>) of Luminance;</lang> Conversion to a grayscale image: <lang ada>function Grayscale (Picture : Image) return Grayscale_Image is

  type Extended_Luminance is range 0..10_000_000;
  Result : Grayscale_Image (Picture'Range (1), Picture'Range (2));
  Color  : Pixel;

begin

  for I in Picture'Range (1) loop
     for J in Picture'Range (2) loop
        Color := Picture (I, J);
        Result (I, J) :=
           Luminance
           (  (  2_126 * Extended_Luminance (Color.R)
              +  7_152 * Extended_Luminance (Color.G)
              +    722 * Extended_Luminance (Color.B)
              )
           /  10_000
           );
     end loop;
  end loop;
  return Result;

end Grayscale;</lang> Conversion to a color image: <lang ada>function Color (Picture : Grayscale_Image) return Image is

  Result : Image (Picture'Range (1), Picture'Range (2));

begin

  for I in Picture'Range (1) loop
     for J in Picture'Range (2) loop
        Result (I, J) := (others => Picture (I, J));
     end loop;
  end loop;
  return Result;

end Color;</lang>

C

Definition/interface for a grayscale image.

<lang c>typedef unsigned char luminance; typedef luminance pixel1[1]; typedef struct {

  unsigned int width;
  unsigned int height;
  luminance *buf;

} grayimage_t; typedef grayimage_t *grayimage;

grayimage alloc_grayimg(unsigned int, unsigned int); grayimage tograyscale(image); image tocolor(grayimage);</lang>

The same as alloc_img, but for grayscale images.

<lang c>grayimage alloc_grayimg(unsigned int width, unsigned int height) {

    grayimage img;
    img = malloc(sizeof(grayimage_t));
    img->buf = malloc(width*height*sizeof(pixel1));
    img->width = width;
    img->height = height;
    return img;

}</lang>

Convert from color image to grayscale image.

<lang c>grayimage tograyscale(image img) {

  unsigned int x, y;
  grayimage timg;
  double rc, gc, bc, l;
  unsigned int ofs;
  timg = alloc_grayimg(img->width, img->height);
  
  for(x=0; x < img->width; x++)
  {
     for(y=0; y < img->height; y++)
     {
       ofs = (y * img->width) + x;
       rc = (double) img->buf[ofs][0];
       gc = (double) img->buf[ofs][1];
       bc = (double) img->buf[ofs][2];
       l = 0.2126*rc + 0.7152*gc + 0.0722*bc;
       timg->buf[ofs][0] = (luminance) (l+0.5);
     }
  }
  return timg;

}</lang>

And back from a grayscale image to a color image.

<lang c>image tocolor(grayimage img) {

  unsigned int x, y;
  image timg;
  luminance l;
  unsigned int ofs;
  timg = alloc_img(img->width, img->height);
  
  for(x=0; x < img->width; x++)
  {
     for(y=0; y < img->height; y++)
     {
       ofs = (y * img->width) + x;
       l = img->buf[ofs][0];
       timg->buf[ofs][0] = l;
       timg->buf[ofs][1] = l;
       timg->buf[ofs][2] = l;
     }
  }
  return timg;

}</lang>

Notes

  • tocolor and tograyscale do not free the previous image, so it must be freed normally calling free_img. With a cast we can use the same function also for grayscale images, or we can define something like

<lang c>#define free_grayimg(IMG) free_img((image)(IMG))</lang>

  • Luminance is rounded. Since the C implementation is based on unsigned char (256 possible values per components), L can be at most 255.0 and rounding gives 255, as we expect. Changing the color_component type would only change 256, 255.0 and 255 values here written in something else, the code would work the same.

C#

To convert TO grayscale: <lang csharp> Bitmap tImage = new Bitmap("spectrum.bmp");

for (int x = 0; x < tImage.Width; x++) { for (int y = 0; y < tImage.Height; y++) { Color tCol = tImage.GetPixel(x, y);

// L = 0.2126·R + 0.7152·G + 0.0722·B double L = 0.2126 * tCol.R + 0.7152 * tCol.G + 0.0722 * tCol.B; tImage.SetPixel(x, y, Color.FromArgb(Convert.ToInt32(L), Convert.ToInt32(L), Convert.ToInt32(L))); } }

// Save tImage.Save("spectrum2.bmp"); </lang>

D

This example uses Bitmap template as defined on Basic bitmap storage problem page.


<lang D>struct Lumin {

   ubyte[1] value;
   void opCall(ubyte l) { value[0] = l; }
   void opCall(ubyte[1] v) { value[] = v[]; }
   
   ubyte l() { return value[0]; }

}

alias Bitmap!(Lumin) GrayBitmap;

GrayBitmap rgbToGray(RgbBitmap bitmap) {

   auto gb = GrayBitmap(bitmap.width, bitmap.height);
   int x, y;
   foreach (ref elem; gb) {
       elem(bitmap[x, y].lumAVG);
       if (++x == bitmap.width) { x = 0; y++; }
   }
   return gb;

}

RgbBitmap grayToRgb(GrayBitmap gray) {

   auto rgb = RgbBitmap(gray.width, gray.height);
   int x, y;
   foreach (ref elem; rgb) {
       elem(gray[x, y].l);
       if (++x == gray.width) { x = 0; y++; }
   }
   return rgb;

}</lang>


Adding the following opCall methods to Lumin and Rgb structs would allow to create simple conversion function template instead of two separate functions. <lang D>//in Rgb struct:

   void opCall(Rgb v) { value[] = v.value[]; }

//in Lumin struct:

   void opCall(Lumin l) { value[] = l.value[]; }</lang>

Conversion function template: <lang D>Bitmap!(TO) convert(FR, TO)(Bitmap!(FR) source, TO delegate(FR) dg) {

   auto dest = Bitmap!(TO)(source.width, source.height);
   int x, y;
   foreach (ref elem; dest) {
       elem( dg(source[x, y]) );
       if (++x == source.width) { x = 0; y++; }
   }
   return dest;

}</lang>

Sample usage of conversion function: <lang D>// assuming t0 is of RgbBitmap type.. // convert RgbBitmap to GrayBitmap auto t1 = convert(t0, delegate Lumin(Rgb v) { Lumin res; res(cast(ubyte)(0.2126*v.r + 0.7152*v.g + 0.0722*v.b)); return res; } ); // convert Graybitmap to grayscale - RgbBitmap auto t2 = convert(t1, delegate Rgb(Lumin v) { Rgb res; res(v.l); return res; });</lang>

Euphoria

<lang euphoria>function to_gray(sequence image)

   sequence color
   for i = 1 to length(image) do
       for j = 1 to length(image[i]) do
           color = and_bits(image[i][j], {#FF0000,#FF00,#FF}) /
                                         {#010000,#0100,#01} -- unpack color triple
           image[i][j] = floor(0.2126*color[1] + 0.7152*color[2] + 0.0722*color[3])
       end for
   end for
   return image

end function

function to_color(sequence image)

   for i = 1 to length(image) do
       for j = 1 to length(image[i]) do
           image[i][j] = image[i][j]*#010101
       end for
   end for
   return image

end function</lang>

Forth

<lang forth>\ grayscale bitmap (without word-alignment for scan lines)

\ bdim, bwidth, bdata all work with graymaps

graymap ( w h -- gmp )
 2dup * bdata allocate throw
 dup >r 2! r> ;
gxy ( x y gmp -- addr )
 dup bwidth rot * rot + swap bdata + ;
g@ ( x y gmp -- c ) gxy c@ ;
g! ( c x y bmp -- ) gxy c! ;
gfill ( c gmp -- )
 dup bdata swap bdim * rot fill ;
gshow ( gmp -- )
 dup bdim
 0 do cr
   dup 0 do
     over i j rot g@ if [char] * emit else space then
   loop
 loop
 2drop ;

\ RGB <-> Grayscale

lum>rgb ( 0..255 -- pixel )
  dup 8 lshift or
  dup 8 lshift or ;
pixel>rgb ( pixel -- r g b )
 256 /mod 256 /mod ;
rgb>lum ( pixel -- 0..255 )
 pixel>rgb
  722 *   swap
 7152 * + swap
 2126 * + 10000 / ;
bitmap>graymap ( bmp -- gmp )
 dup bdim graymap
 dup bdim nip 0 do
   dup bwidth 0 do
     over i j rot b@ rgb>lum
     over i j rot g!
   loop
 loop nip ;
graymap>bitmap ( gmp -- bmp )
 dup bdim bitmap
 dup bdim nip 0 do
   dup bwidth 0 do
     over i j rot g@ lum>rgb
     over i j rot b!
   loop
 loop nip ;</lang>

Fortran

(These fragments should be added to RCImageBasic module, see Basic bitmap storage)

First let's define a new type; the sc stands for Single Channel, which can be luminance (as it is here).

<lang fortran>type scimage

  integer, dimension(:,:), pointer :: channel
  integer :: width, height

end type scimage</lang>

In order to allow proper overloading, the following subroutines of the storage should be renamed appending the _rgb suffix: valid_image, inside_image, alloc_img, free_img, fill_img, get_pixel, put_pixel, init_img. The single channel version would be named with the _sc suffix, then we should define the proper interfaces to use the already written code as before. Here there are only the interfaces and subroutines needed for the task.

<lang fortran>interface alloc_img

  module procedure alloc_img_rgb, alloc_img_sc

end interface

interface free_img

  module procedure free_img_rgb, free_img_sc

end interface</lang>

Now we can define useful interfaces and subroutines more task-related:

<lang fortran>interface assignment(=)

  module procedure rgbtosc, sctorgb

end interface</lang>

<lang fortran>subroutine alloc_img_sc(img, w, h)

 type(scimage) :: img
 integer, intent(in) :: w, h
 allocate(img%channel(w, h))
 img%width = w
 img%height = h

end subroutine alloc_img_sc

subroutine free_img_sc(img)

 type(scimage) :: img
 if ( associated(img%channel) ) deallocate(img%channel)

end subroutine free_img_sc

subroutine rgbtosc(sc, colored)

 type(rgbimage), intent(in) :: colored
 type(scimage), intent(inout) :: sc
 if ( ( .not. valid_image(sc) ) .and. valid_image(colored) ) then
    call alloc_img(sc, colored%width, colored%height)
 end if
 if ( valid_image(sc) .and. valid_image(colored) ) then
    sc%channel = floor(0.2126*colored%red + 0.7152*colored%green + &
                       0.0722*colored%blue)
 end if
 

end subroutine rgbtosc

subroutine sctorgb(colored, sc)

 type(scimage), intent(in) :: sc
 type(rgbimage), intent(inout) :: colored
 if ( ( .not. valid_image(colored) ) .and. valid_image(sc) ) then
    call alloc_img_rgb(colored, sc%width, sc%height)
 end if
 if ( valid_image(sc) .and. valid_image(colored) ) then
    colored%red = sc%channel
    colored%green = sc%channel
    colored%blue = sc%channel
 end if

end subroutine sctorgb</lang>

Usage example (fragment) which can be used to convert from rgb image to grayscale image and back (since we only can output the rgb kind):

<lang fortran>type(scimage) :: gray type(rgbimage) :: animage

 ! ... here we "load" or create animage
 ! while gray must be created or initialized to null
 ! or errors can arise...
 call init_img(gray)
 gray = animage
 animage = gray
 call output_ppm(an_unit, animage)</lang>

Go

<lang go>package raster

import (

   "math"
   "math/rand"

)

// Grmap parallels Bitmap, but with an element type of uint16 // in place of Pixel. type Grmap struct {

   Comments   []string
   rows, cols int
   px         []uint16
   pxRow      [][]uint16

}

// NewGrmap constructor. func NewGrmap(x, y int) (b *Grmap) {

   g := &Grmap{
       Comments: []string{creator}, // creator a const in bitmap source file
       rows:     y,
       cols:     x,
       px:       make([]uint16, x*y),
       pxRow:    make([][]uint16, y),
   }
   x0, x1 := 0, x
   for i := range g.pxRow {
       g.pxRow[i] = g.px[x0:x1]
       x0, x1 = x1, x1+x
   }
   return g

}

func (b *Grmap) Extent() (cols, rows int) {

   return b.cols, b.rows

}

func (g *Grmap) Fill(c uint16) {

   for i := range g.px {
       g.px[i] = c
   }

}

func (g *Grmap) SetPx(x, y int, c uint16) bool {

   defer func() { recover() }()
   g.pxRow[y][x] = c
   return true

}

func (g *Grmap) GetPx(x, y int) (uint16, bool) {

   defer func() { recover() }()
   return g.pxRow[y][x], true

}

// Grmap method of Bitmap, converts (color) Bitmap to (grayscale) Grmap func (b *Bitmap) Grmap() *Grmap {

   g := NewGrmap(b.cols, b.rows)
   g.Comments = append([]string{}, b.Comments...)
   for i, p := range b.px {
       g.px[i] = uint16((int64(p.R)*2126 + int64(p.G)*7152 + int64(p.B)*722) *
           math.MaxUint16 / (math.MaxUint8 * 10000))
   }
   return g

}

// Bitmap method Grmap, converts Grmap to Bitmap. All pixels in the resulting // color Bitmap will be (very nearly) shades of gray. func (g *Grmap) Bitmap() *Bitmap {

   b := NewBitmap(g.cols, g.rows)
   b.Comments = append([]string{}, g.Comments...)
   for i, p := range g.px {
       roundedSum := int(p) * 3 * math.MaxUint8 / math.MaxUint16
       rounded := uint8(roundedSum / 3)
       remainder := roundedSum % 3
       b.px[i].R = rounded
       b.px[i].G = rounded
       b.px[i].B = rounded
       if remainder > 0 {
           odd := rand.Intn(3)
           switch odd + (remainder * 3) {
           case 3:
               b.px[i].R++
           case 4:
               b.px[i].G++
           case 5:
               b.px[i].B++
           case 6:
               b.px[i].G++
               b.px[i].B++
           case 7:
               b.px[i].R++
               b.px[i].B++
           case 8:
               b.px[i].R++
               b.px[i].G++
           }
       }
   }
   return b

}</lang> For demonstration program see task Bitmap/Read a PPM file.

Haskell

<lang haskell>module Bitmap.Gray(module Bitmap.Gray) where

import Bitmap import Control.Monad.ST

newtype Gray = Gray Int deriving (Eq, Ord)

instance Color Gray where

   luminance (Gray x) = x
   black = Gray 0
   white = Gray 255
   toNetpbm = map $ toEnum . luminance
   fromNetpbm = map $ Gray . fromEnum
   netpbmMagicNumber _ = "P5"
   netpbmMaxval _ = "255"

toGrayImage :: Color c => Image s c -> ST s (Image s Gray) toGrayImage = mapImage $ Gray . luminance</lang>

A Gray image can be converted to an RGB image with Bitmap.RGB.toRGBImage, defined here.

J

Color bitmap structure and basic functions for manipulations with it are described here.

Grayscale image is stored as two-dimensional array of luminance values. Allowed luminance scale is the same as for the color bitmap; the functions below are neutral to scale.

<lang j>NB. converts the image to grayscale according to formula NB. L = 0.2126*R + 0.7152*G + 0.0722*B toGray=: [: <. +/ .*"1&0.2126 0.7152 0.0722

NB. converts grayscale image to the color image, with all channels equal toColor=: 3 & $"0</lang>

Example:

<lang j>viewRGB toColor toGray myimg</lang>

Java

<lang java>void convertToGrayscale(final BufferedImage image){

   for(int i=0; i<image.getWidth(); i++){
       for(int j=0; j<image.getHeight(); j++){
           int color = image.getRGB(i,j);
           int alpha = (color >> 24) & 255;
           int red = (color >> 16) & 255;
           int green = (color >> 8) & 255;
           int blue = (color) & 255;
           final int lum = (int)(0.2126 * red + 0.7152 * green + 0.0722 * blue);
           alpha = (alpha << 24);
           red = (lum << 16);
           green = (lum << 8);
           blue = lum;
           color = alpha + red + green + blue;
           image.setRGB(i,j,color);
       }
   }

} </lang>

JavaScript

Live Demo: http://jsfiddle.net/J4zVy/embedded/result/.

To load local files into the canvas you have to run a local webserver. See: http://stackoverflow.com/questions/3276072/canvas-getimagedata-uncaught-error-security-err-dom-exception-18.

<lang JavaScript><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

       "http://www.w3.org/TR/html4/strict.dtd">

<html><head><script type="text/javascript"> window.addEventListener(

   "load", function(){   
       var img = new Image();   
       // ***********************************************************
       // RUN LOCAL WEBSERVER TO LOAD LOCAL FILES: e.g. python -m http.server (python3) 
       // ***********************************************************
       // img.src = prompt("enter image path","http://localhost:8000/test.jpg");
       img.src = 
           'data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub/\
           /ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcpp\
           V0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7'; 
       img.onload = function(){ 
           var can1 = new CustomCanvas("color", img.width, img.height);
           var can2 = new CustomCanvas("grayscale",  img.width, img.height);
           can1.ctx.drawImage(img,0, 0, img.width, img.height); 
           var imgData = can1.ctx.getImageData(0, 0, can1.w, can1.h);      
           // desaturate
           var avg; var max; var rwgt=0.2126; var gwgt=0.7152;  var bwgt=0.0722;
           for(var i = 0, max = can1.w*can1.h*4; i < max; i=i+4){
               avg = imgData.data[i]*rwgt + imgData.data[i+1]*gwgt + imgData.data[i+2]*bwgt;
               imgData.data[i  ] = avg;  // red
               imgData.data[i+1] = avg;  // green
               imgData.data[i+2] = avg;} // blue, alpha=alpha
           can2.ctx.putImageData(imgData, 0, 0);  
           }
       }, false); 
   function CustomCanvas(id, w, h, s) { /* Custom Canvas Object */
       var c = document.createElement("canvas");
       c.setAttribute('id', id); c.setAttribute('width', w);
       c.setAttribute('height', h); (s)?c.setAttribute('style', s):0;
       document.body.appendChild(c, document.body.firstChild);
       this.ctx = document.getElementById(id).getContext("2d");
       this.w = w; this.h = h;}           

</script></head><body></body></html> </lang>

Lua

<lang lua>function ConvertToGrayscaleImage( bitmap )

   local size_x, size_y = #bitmap, #bitmap[1]
   local gray_im = {}
 
   for i = 1, size_x do
       gray_im[i] = {}
       for j = 1, size_y do 
           gray_im[i][j] = math.floor( 0.2126*bitmap[i][j][1] + 0.7152*bitmap[i][j][2] + 0.0722*bitmap[i][j][3] )
       end
   end
   
   return gray_im

end

function ConvertToColorImage( gray_im )

   local size_x, size_y = #gray_im, #gray_im[1]    
   local bitmap = Allocate_Bitmap( size_x, size_y )         -- this function is defined at http://rosettacode.org/wiki/Basic_bitmap_storage#Lua
   for i = 1, size_x do
       for j = 1, size_y do 
           bitmap[i][j] = { gray_im[i][j], gray_im[i][j], gray_im[i][j] }
       end
   end
   
   return bitmap

end</lang>

Mathematica

Mathematica has a built-in grayscale conversion function called "ColorConvert". This example does not use it since it appears the luminance calculation is different from the CIE spec. Grayscale to RGB "conversion" just changes the single channel grayscale image to a triple channel image. <lang Mathematica>toGrayscale[rgb_Image] := ImageApply[#.{0.2126, 0.7152, 0.0722}&, rgb] toFakeRGB[L_Image] := ImageApply[{#, #, #}&, L] </lang>

MATLAB

Built in colour to grayscale converter uses the following forumula: 0.2989*R + 0.5870*G + 0.1140*B <lang Matlab>function [grayImage] = colortograyscale(inputImage)

  grayImage = rgb2gray(inputImage);</lang>

OCaml

Conversion to a grayscale image: <lang ocaml>let to_grayscale ~img:(_, r_channel, g_channel, b_channel) =

 let width = Bigarray.Array2.dim1 r_channel
 and height = Bigarray.Array2.dim2 r_channel in
 let gray_channel =
   let kind = Bigarray.int8_unsigned
   and layout = Bigarray.c_layout
   in
   (Bigarray.Array2.create kind layout width height)
 in
 for y = 0 to pred height do
   for x = 0 to pred width do
     let r = r_channel.{x,y}
     and g = g_channel.{x,y}
     and b = b_channel.{x,y} in
     let v = (2_126 * r +  7_152 * g + 722 * b) / 10_000 in
     gray_channel.{x,y} <- v;
   done;
 done;
 (gray_channel)</lang>

Conversion to a color image: <lang ocaml>let to_color ~img:gray_channel =

 let width = Bigarray.Array2.dim1 gray_channel
 and height = Bigarray.Array2.dim2 gray_channel in
 let all_channels =
   let kind = Bigarray.int8_unsigned
   and layout = Bigarray.c_layout
   in
   Bigarray.Array3.create kind layout 3 width height
 in
 let r_channel = Bigarray.Array3.slice_left_2 all_channels 0
 and g_channel = Bigarray.Array3.slice_left_2 all_channels 1
 and b_channel = Bigarray.Array3.slice_left_2 all_channels 2
 in
 Bigarray.Array2.blit gray_channel r_channel;
 Bigarray.Array2.blit gray_channel g_channel;
 Bigarray.Array2.blit gray_channel b_channel;
 (all_channels,
  r_channel,
  g_channel,
  b_channel)</lang>

and functions to get/set a pixel:

<lang ocaml>let gray_get_pixel_unsafe (gray_channel) =

 (fun x y -> gray_channel.{x,y})

let gray_put_pixel_unsafe (gray_channel) v =

 (fun x y -> gray_channel.{x,y} <- v)</lang>

Octave

Use package: image

<lang octave>function [grayImage] = colortograyscale(inputImage)

  grayImage = rgb2gray(inputImage);</lang>

Differently from MATLAB, the grayscale is computed as mean of the three RGB values. Changing this non-optimal behaviour is a matter of fixing three lines in the rgb2gray.m file; since it's a GPL-ed code, here it is a semplified version (error checking, usage help, argument checking removed)

<lang octave>function gray = rgb2gray (rgb)

   switch(class(rgb))
   case "double"
     gray = luminance(rgb);
   case "uint8"
     gray = uint8(luminance(rgb));
   case "uint16"
     gray = uint16(luminance(rgb));
   endswitch

endfunction

function lum = luminance(rgb)

  lum = 0.2126*rgb(:,:,1) + 0.7152*rgb(:,:,2) + 0.0722*rgb(:,:,3); 

endfunction</lang>

Original code of the rgb2gray.m in the image package version 1.0.8 is by Kai Habel (under the GNU General Public License)

Oz

We define a "graymap" as a two-dimensional array of floats. In module "Grayscale.oz", we implement conversion functions from and to bitmaps:

<lang oz>functor import

  Array2D

export

  ToGraymap
  FromGraymap

define

  fun {ToGraymap bitmap(Arr)}
     graymap({Array2D.map Arr Luminance})
  end
  
  fun {Luminance Color}
     F = {Record.map Color Int.toFloat}
  in
     0.2126*F.1 + 0.7152*F.2 + 0.0722*F.3
  end
  
  fun {FromGraymap graymap(Arr)}
     bitmap({Array2D.map Arr ToColor})
  end
  
  fun {ToColor Lum}
     L = {Float.toInt Lum}
  in
     color(L L L)
  end

end</lang>

Perl

Library: Imlib2

Since we are using Imlib2, this one does not implement really a gray-scale (single channel) storage; it only converts an RGB image to an RGB image with the same three colour components for each pixel (which result in a gray-scale-like image)

<lang perl>#! /usr/bin/perl

use strict; use Image::Imlib2;

sub tograyscale {

   my $img = shift;
   my $gimg = Image::Imlib2->new($img->width, $img->height);
   for ( my $x = 0; $x < $gimg->width; $x++ ) {

for ( my $y = 0; $y < $gimg->height; $y++ ) { my ( $r, $g, $b, $a ) = $img->query_pixel($x, $y); my $gray = int(0.2126 * $r + 0.7152 * $g + 0.0722 * $b); # discard alpha info... $gimg->set_color($gray, $gray, $gray, 255); $gimg->draw_point($x, $y); }

   }
   return $gimg;

}

my $animage = Image::Imlib2->load("Lenna100.jpg"); my $gscale = tograyscale($animage); $gscale->set_quality(80); $gscale->save("Lennagray.jpg");

exit 0;</lang>

Perl 6

This script expects to be fed a P6 .ppm file name at the command line. It will convert it to grey scale and save it as a binary portable grey map (P5 .pgm) file. <lang perl6>sub MAIN ($filename = 'default.ppm') {

   my $in = open( $filename, :r , :bin ) or die "$!\n";
   
   my ($type, $dim, $depth) = $in.lines[^3];
   
   my $outfile = $filename.subst('.ppm', '.pgm');
   my $out = open( $outfile, :w, :bin ) or die "$!\n";
   
   $out.say("P5\n$dim\n$depth");
   
   for $in.slurp.ords -> $r, $g, $b {
       my $gs = $r * 0.2126 + $g * 0.7152 + $b * 0.0722;
       $out.print: chr($gs min 255);
   }
   $in.close;
   $out.close;

}</lang> Using the .ppm file from the Write a PPM file task:

Original: Grey Scale:

PHP

Uses the Bitmap class defined for writing a PPM file <lang PHP>class BitmapGrayscale extends Bitmap {

 public function toGrayscale(){
   for ($i = 0; $i < $this->h; $i++){
     for ($j = 0; $j < $this->w; $j++){
       $l = ($this->data[$j][$i][0] * 0.2126)
          + ($this->data[$j][$i][1] * 0.7152)
          + ($this->data[$j][$i][2] * 0.0722);
       $l = round($l);
       $this->data[$j][$i] = array($l,$l,$l);
     }
   }
 }

}

$b = new BitmapGrayscale(16,16); $b->fill(0,0,null,null, array(255,255,0)); $b->setPixel(0, 15, array(255,0,0)); $b->setPixel(0, 14, array(0,255,0)); $b->setPixel(0, 13, array(0,0,255)); $b->toGrayscale(); $b->writeP6('p6-grayscale.ppm');</lang>

PL/I

<lang PL/I> do j = 1 to hbound(image,1);

  do i = 0 to hbound(image,2);
     color = image(i,j);
     R = substr(color, 17, 8);
     G = substr(color, 9, 8);
     B = substr(color, 1, 8);
     grey =  trunc(0.2126*R + 0.7152*G + 0.0722*B);
     greybits = grey;
     image(i,j) = substr(greybits, length(greybits)-7, 8);
  end;

end; </lang>

PicoLisp

<lang PicoLisp># Convert color image (PPM) to greyscale image (PGM) (de ppm->pgm (Ppm)

  (mapcar
     '((Y)
        (mapcar
           '((C)
              (/
                 (+
                    (* (car C) 2126)  # Red
                    (* (cadr C) 7152)  # Green
                    (* (caddr C) 722) )  # Blue
                 10000 ) )
           Y ) )
     Ppm ) )
  1. Convert greyscale image (PGM) to color image (PPM)

(de pgm->ppm (Pgm)

  (mapcar
     '((Y)
        (mapcar
           '((G) (list G G G))
           Y ) )
     Pgm ) )</lang>

<lang PicoLisp># Write greyscale image (PGM) to file (de pgmWrite (Pgm File)

  (out File
     (prinl "P5")
     (prinl (length (car Pgm)) " " (length Pgm))
     (prinl 255)
     (for Y Pgm (apply wr Y)) ) )
  1. Create an empty image of 120 x 90 pixels

(setq *Ppm (make (do 90 (link (need 120)))))

  1. Fill background with green color

(ppmFill *Ppm 0 255 0)

  1. Draw a diagonal line

(for I 80 (ppmSetPixel *Ppm I I 0 0 0))


  1. Convert to greyscale image (PGM)

(setq *Pgm (ppm->pgm *Ppm))

  1. Write greyscale image to .pgm file

(pgmWrite *Pgm "img.pgm")

  1. Convert to color image and write to .ppm file

(ppmWrite (pgm->ppm *Pgm) "img.ppm")</lang>

PureBasic

<lang PureBasic>Procedure ImageGrayout(image)

 w = ImageWidth(image)
 h = ImageHeight(image)
 StartDrawing(ImageOutput(image))
 For x = 0 To w - 1
   For y = 0 To h - 1
     color = Point(x, y)
     r    = color & $ff
     g    = color >> 8 & $ff
     b    = color >> 16 & $ff
     gray = 0.2126*r + 0.7152*g + 0.0722*b
     Plot(x, y, gray + gray << 8 + gray << 16)
   Next
 Next
 StopDrawing()

EndProcedure</lang>


Python

Works with: Python version 3.1

Extending the example given here <lang python># String masquerading as ppm file (version P3) import io ppmfileout = io.StringIO()

def togreyscale(self):

   for h in range(self.height):
       for w in range(self.width):
           r, g, b = self.get(w, h)
           l = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
           self.set(w, h, Colour(l, l, l))

Bitmap.togreyscale = togreyscale


  1. Draw something simple

bitmap = Bitmap(4, 4, white) bitmap.fillrect(1, 0, 1, 2, Colour(127, 0, 63)) bitmap.set(3, 3, Colour(0, 127, 31)) print('Colour:')

  1. Write to the open 'file' handle

bitmap.writeppmp3(ppmfileout) print(ppmfileout.getvalue()) print('Grey:') bitmap.togreyscale() ppmfileout = io.StringIO() bitmap.writeppmp3(ppmfileout) print(ppmfileout.getvalue())


The print statement above produces the following output :

Colour: P3

  1. generated from Bitmap.writeppmp3

4 4 255

  255 255 255   255 255 255   255 255 255     0 127  31
  255 255 255   255 255 255   255 255 255   255 255 255
  255 255 255   127   0  63   255 255 255   255 255 255
  255 255 255   127   0  63   255 255 255   255 255 255

Grey: P3

  1. generated from Bitmap.writeppmp3

4 4 254

  254 254 254   254 254 254   254 254 254    93  93  93
  254 254 254   254 254 254   254 254 254   254 254 254
  254 254 254    31  31  31   254 254 254   254 254 254
  254 254 254    31  31  31   254 254 254   254 254 254

</lang>

R

Library: pixmap

<lang r># Conversion from Grey to RGB uses the following code setAs("pixmapGrey", "pixmapRGB", function(from, to){

   z = new(to, as(from, "pixmap"))
   z@red = from@grey
   z@green = from@grey
   z@blue = from@grey
   z@channels = c("red", "green", "blue")
   z

})

  1. Conversion from RGB to grey uses built-in coefficients of 0.3, 0.59, 0.11. To see this, type

getMethods(addChannels)

  1. We can override this behaviour with

setMethod("addChannels", "pixmapRGB", function(object, coef=NULL){

   if(is.null(coef)) coef = c(0.2126, 0.7152, 0.0722)
   z = new("pixmapGrey", object)
   z@grey = coef[1] * object@red + coef[2] * object@green +
       coef[3] * object@blue
   z@channels = "grey"
   z

})

  1. Colour image

plot(p1 <- pixmapRGB(c(c(1,0,0,0,0,1), c(0,1,0,0,1,0), c(0,0,1,1,0,0)), nrow=6, ncol=6))

  1. Convert to grey

plot(p2 <- as(p1, "pixmapGrey"))

  1. Convert back to "colour"

plot(p3 <- as(p2, "pixmapRGB"))</lang>

REXX

<lang rexx>/*REXX program to convert a RGB image to grayscale. */

blue='00 00 ff'x /*define the blue color. */ image.=blue /*set the entire IMAGE to blue. */

width= 60                             /* width of the IMAGE.           */

height=100 /*height " " " */

 do j=1 for width
   do k=1 for height
   r=  left(image.j.k,1)      ;  r=c2d(r)     /*extract red   & convert*/
   g=substr(image.j.k,2,1)    ;  g=c2d(g)     /*   "    green "    "   */
   b= right(image.j.k,1)      ;  b=c2d(b)     /*   "    blue  "    "   */
   ddd=right(trunc(.2126*r + .7152*g + .0722*b),3,0)   /*──► greyscale.*/
   image.j.k=right(d2c(ddd,6),3,0)            /*... and transform back.*/
   end
 end</lang>

Ruby

Extending Basic_bitmap_storage#Ruby <lang ruby>class RGBColour

 def to_grayscale
   luminosity = Integer(0.2126*@red + 0.7152*@green + 0.0722*@blue)
   self.class.new(luminosity, luminosity, luminosity)
 end

end

class Pixmap

 def to_grayscale
   gray = self.class.new(@width, @height)
   @width.times do |x|
     @height.times do |y|
       gray[x,y] = self[x,y].to_grayscale
     end
   end
   gray
 end

end</lang>

Scala

Uses the Scala Basic Bitmap Storage class. <lang scala>object BitmapOps {

  def luminosity(c:Color)=(0.2126*c.getRed + 0.7152*c.getGreen + 0.0722*c.getBlue+0.5).toInt
  def grayscale(bm:RgbBitmap)={
     val image=new RgbBitmap(bm.width, bm.height)
     for(x <- 0 until bm.width; y <- 0 until bm.height; l=luminosity(bm.getPixel(x,y)))
        image.setPixel(x, y, new Color(l,l,l))
     image
  }

}</lang>

Tcl

Library: Tk

<lang tcl>package require Tk

proc grayscale image {

   set w [image width $image]
   set h [image height $image]
   for {set x 0} {$x<$w} {incr x} {
       for {set y 0} {$y<$h} {incr y} {
           lassign [$image get $x $y] r g b
           set l [expr {int(0.2126*$r + 0.7152*$g + 0.0722*$b)}]
           $image put [format "#%02x%02x%02x" $l $l $l] -to $x $y
       }
   }

}</lang> Photo images are always 8-bits-per-channel RGBA.

Vedit macro language

Conversion to a grayscale image.
<lang vedit>// Convert RGB image to grayscale (8 bit/pixel) // #10 = buffer that contains image data // On return: // #20 = buffer for the new grayscale image

RGB_TO_GRAYSCALE:

File_Open("|(VEDIT_TEMP)\gray.data", OVERWRITE+NOEVENT+NOMSG)

  1. 20 = Buf_Num

BOF Del_Char(ALL) Buf_Switch(#10) Repeat(File_Size/3) {

   #9 =  Cur_Char() * 2126
   #9 += Cur_Char(1) * 7152
   #9 += Cur_Char(2) * 722
   Char(3)
   Buf_Switch(#20)
   Ins_Char(#9 / 10000)
   Buf_Switch(#10)

} Return</lang>

Conversion to a color image.
<lang vedit>// Convert grayscale image (8 bits/pixel) into RGB (24 bits/pixel) // #20 = buffer that contains image data // On return: // #10 = buffer for the new RGB image

GRAYSCALE_TO_RGB:

File_Open("|(VEDIT_TEMP)\RGB.data", OVERWRITE+NOEVENT+NOMSG)

  1. 10 = Buf_Num

BOF Del_Char(ALL) Buf_Switch(#20) // input image (grayscale) BOF Repeat(File_Size) {

   #9 =  Cur_Char()
   Char
   Buf_Switch(#10)		// output image (RGB)
   Ins_Char(#9, COUNT, 3)
   Buf_Switch(#20)

} Return</lang>

Visual Basic .NET

Convert a Bitmap to Grayscale.

<lang vbnet> Imports System.Drawing.Imaging

 Public Function Grayscale(ByVal Map As Bitmap) As Bitmap
   Dim oData() As Integer = GetData(Map)
   Dim oReturn As New Bitmap(Map.Width, Map.Height, Map.PixelFormat)
   Dim a As Integer = 0
   Dim r As Integer = 0
   Dim g As Integer = 0
   Dim b As Integer = 0
   Dim l As Integer = 0
   For i As Integer = 0 To oData.GetUpperBound(0)
     a = (oData(i) >> 24)
     r = (oData(i) >> 16) And 255
     g = (oData(i) >> 8) And 255
     b = oData(i) And 255
     l = CInt(r * 0.2126F + g * 0.7152F + b * 0.0722F)
     oData(i) = (a << 24) Or (l << 16) Or (l << 8) Or l
   Next
   SetData(oReturn, oData)
   Return oReturn
 End Function
 Private Function GetData(ByVal Map As Bitmap) As Integer()
   Dim oBMPData As BitmapData = Nothing
   Dim oData() As Integer = Nothing
   oBMPData = Map.LockBits(New Rectangle(0, 0, Map.Width, Map.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
   Array.Resize(oData, Map.Width * Map.Height)
   Runtime.InteropServices.Marshal.Copy(oBMPData.Scan0, oData, 0, oData.Length)
   Map.UnlockBits(oBMPData)
   Return oData
 End Function
 Private Sub SetData(ByVal Map As Bitmap, ByVal Data As Integer())
   Dim oBMPData As BitmapData = Nothing
   oBMPData = Map.LockBits(New Rectangle(0, 0, Map.Width, Map.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb)
   Runtime.InteropServices.Marshal.Copy(Data, 0, oBMPData.Scan0, Data.Length)
   Map.UnlockBits(oBMPData)
 End Sub</lang>