snibgo's ImageMagick pages

Making an image grayscale (monochrome)

ImageMagick has an infinite number of ways of converting a colour image to monochrome. However, it provides a smaller number of simple methods.

Introduction

Start by creating a source image to show the methods. We will write some results to a text file, with limited precision to aid the elimination of duplicates.

%IMG7%magick hald:8 mmh8.png

set SRC=mmh8.png
set MM_INFO=-precision 4 -format "%%T, %%H, %%[fx:mean], %%[fx:standard_deviation], %%[colorspace], %%[gamma]\n" -write info:

echo Method,Parameter,Mean,StdDev,colorspace,gamma>mmList.csv

A hald clut is not an interesting image, so I'll use a small extract from a photograph. This is a fairly "raw" photo, without saturation increase or sharpening for the web.

Source image:

if not exist toes.png xcopy /y %PICTLIB%20130713\toes.png
set SRC_TOES=toes.png
toes.png

The methods

Each method is shown in three boxes.

The left box contains code that generates one or more images and the mean, standard deviation, colorspace and gamma of each.

The images are not shown here but are used for regression testing.

The middle box contains code that generates a single image.

The right box show the image generated by the code in the middle box.

The following commands use nested loops. The inner loop loops through parameters. The outer loop is merely a way of creating setting a "Method" for the format string, without having to re-write the format operation every time.

Short and sweet: "-monochrome". The result is black and white only, with no greys. Anticipating the numerical result below, the mean value is within the range of greyscale versions but the standard deviation is much higher.

for %%T in (monochrome) do (
for %%H in (--) do (
%IMG7%magick ^
  %SRC% ^
  -monochrome ^
  %MM_INFO% ^
  mm_%%T.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -monochrome ^
  mmt_monochrome.png
mmt_monochrome.pngjpg

One method uses "-modulate 100,0,100" to zero the saturation. By default this works in HSL colorspace, but we can change this.

for %%T in (modulate) do (
for %%H in (HCL,HCLp,HSL,HSI,HSB,HSV) do (
%IMG7%magick ^
  %SRC% ^
  -set option:modulate:colorspace %%H ^
  -modulate 100,0,100 ^
  %MM_INFO% ^
  mm_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -modulate 100,0,100 ^
  mmt_modulate.png
mmt_modulate.pngjpg

The next method uses "-grayscale XX".

for %%T in (grayscale) do (
FOR %%H ^
IN (Rec601Luminance,Rec709Luminance,Rec601Luma,Rec709Luma,Lightness,Average,MS,RMS,Brightness) ^
DO (
%IMG7%magick ^
  %SRC% ^
  -grayscale %%H ^
  %MM_INFO% ^
  mm_gr_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -grayscale RMS ^
  mmt_gr_RMS.png
mmt_gr_RMS.pngjpg

Next, we convert to one of the colorspaces that record monochrome in the first channel.

for %%T in (colorspace_channel_1) do (
FOR %%H ^
IN (YCC,YDbDr,YCbCr,YIQ,YPbPr,YUV,LCH,LCHab,LCHuv,Lab) ^
DO (
%IMG7%magick ^
  %SRC% ^
  -colorspace %%H ^
  -channel GB -evaluate set 50%% ^
  -colorspace sRGB ^
  %MM_INFO% ^
  mm_cc1_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -colorspace Lab ^
  -channel GB -evaluate set 50%% ^
  -colorspace sRGB ^
  mmt_cc1_Lab.png
mmt_cc1_Lab.pngjpg

Two colorspaces are specifically for monochrome.

for %%T in (colorspace) do (
FOR %%H ^
IN (Gray,LinearGray) ^
DO (
%IMG7%magick ^
  %SRC% ^
  -colorspace %%H ^
  %MM_INFO% ^
  mm_cs_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -colorspace Gray ^
  mmt_cs_gray.png
mmt_cs_gray.pngjpg

"-colorspace gray" can be used with an intensity setting.

%IMG7%magick -list intensity

for %%T in (colorspace_gray) do (
FOR /F %%H ^
IN (mmIntensity.lis) ^
DO (
%IMG7%magick ^
  %SRC% ^
  -intensity %%H ^
  -colorspace gray ^
  +intensity ^
  %MM_INFO% ^
  mm_cgr_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -intensity Lightness ^
  -colorspace gray ^
  +intensity ^
  mmt_cgr_Lightness.png
mmt_cgr_Lightness.pngjpg

We can use just a single channel of R, G or B. The effect is similar to placing a colour filter over the lens of a camera with black and white film. For example, a red filter darkens blue skies, emphasising clouds. The green filter shown here lightens grass. This artificial image returns the same mean and standard deviation for all channels.

for %%T in (single_channel) do (
FOR %%H IN (Red) DO (
%IMG7%magick ^
  %SRC% ^
  -separate -delete 1-2 ^
  %MM_INFO% ^
  mm_1c_%%H.png>>mmList.csv
)

FOR %%H IN (Green) DO (
%IMG7%magick ^
  %SRC% ^
  -separate -delete 0,2 ^
  %MM_INFO% ^
  mm_1c_%%H.png>>mmList.csv
)

FOR %%H IN (Blue) DO (
%IMG7%magick ^
  %SRC% ^
  -separate -delete 0-1 ^
  %MM_INFO% ^
  mm_1c_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -separate -delete 0,2 ^
  mmt_1c_Green.png
mmt_1c_Green.pngjpg

We can use "-poly" to apply any weights we wish to the channels. The weights should generally sum to one. In each pair of parameters, the second is the exponent, which we will set to one. With weights of 3,-1,-1 we implement a very strong red filter. It is so strong that the pink toes are burnt out.

for %%T in (poly) do (
FOR %%H IN (StrongRed) DO (
%IMG7%magick ^
  %SRC% ^
  -separate ^
  -poly "3,1 -1,1 -1,1" ^
  %MM_INFO% ^
  mm_poly.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  -separate ^
  -poly "3,1 -1,1 -1,1" ^
  mmt_poly.png
mmt_poly.pngjpg

By using floating-point (HDRI) IM and "-auto-level" we prevent clipping.

for %%T in (polyFloat) do (
FOR %%H IN (StrongRed) DO (
rem %IMG7%magick ^
rem   %SRC% ^
rem   -separate ^
rem   -poly "3,1 -1,1 -1,1" ^
rem   -auto-level ^
rem   %MM_INFO% ^
rem   mm_polyf.png>>mmList.csv
)
)
rem %IMG7%magick ^
rem   %SRC_TOES% ^
rem   -separate ^
rem   -poly "3,1 -1,1 -1,1" ^
rem   -auto-level ^
rem   mmt_polyf.png

We can find the distance from any given colour. The specified colour becomes black. The most distant colours become the lightest. The lightest tone depends on the distance from the specified colour to the furthest corner of the colour cube, so it is at least 50%. White can be achieved only if the specified colour was in a corner of the colour cube. When the specified colour is black, the effect is identical to "-intensity RMS -colorspace gray".

In the samples below, the given colour is "khaki", which is quite light. Most colours in the source photo are darker than this, and the darkest colours are the most distant, so the result resembles a photographic negative with tones reversed.

set SPEC_COL=khaki

for %%T in (colour_distance) do (
FOR %%H IN (%SPEC_COL%) DO (
%IMG7%magick ^
  %SRC% ^
  ^( +clone -fill %%H -colorize 100 ^) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  %MM_INFO% ^
  mm_cd_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  ^( +clone -fill khaki -colorize 100 ^) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  mmt_cd_khaki.png
mmt_cd_khaki.pngjpg

We can adapt the previous method to more closely simulate a colour filter over a lens. We use the negative of the color, and negate the result. This simulation is closer, but still inaccurate.

for %%T in (colour_distance_2) do (
FOR %%H IN (%SPEC_COL%) DO (
%IMG7%magick ^
  %SRC% ^
  ^( +clone -fill %%H -colorize 100 -negate ^) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -negate ^
  %MM_INFO% ^
  mm_cd2_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  ^( +clone -fill khaki -colorize 100 -negate ^) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -negate ^
  mmt_cd2_khaki.png
mmt_cd2_khaki.pngjpg

We can find the distance in any desired colorspace.

for %%T in (colour_distance_Lab) do (
FOR %%H IN (%SPEC_COL%) DO (
%IMG7%magick ^
  %SRC% ^
  ^( +clone -fill %%H -colorize 100 ^) ^
  -colorspace Lab ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -set colorspace sRGB ^
  %MM_INFO% ^
  mm_cdLab_%%H.png>>mmList.csv
)
)
%IMG7%magick ^
  %SRC_TOES% ^
  ^( +clone -fill khaki -colorize 100 ^) ^
  -colorspace Lab ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -set colorspace sRGB ^
  mmt_cdLab_khaki.png
mmt_cdLab_khaki.pngjpg

The results

From a single colour image, we have created 42 monochrome images. They are not all different.

call csv2tab mmList
Method Parameter Mean StdDev colorspace gamma
monochrome -- 0.49 0.4999 Gray 0.4545
modulate HCL 0.5 0.196 sRGB 0.4546
modulate HCLp 0.5 0.196 sRGB 0.4546
modulate HSL 0.5 0.1606 sRGB 0.4546
modulate HSI 0.5 0.1606 sRGB 0.4546
modulate HSB 0.7539 0.1967 sRGB 0.4546
modulate HSV 0.7539 0.1967 sRGB 0.4546
grayscale Rec601Luminance 0.3133 0.1992 LinearGray 1
grayscale Rec709Luminance 0.3133 0.2234 LinearGray 1
grayscale Rec601Luma 0.5 0.196 Gray 0.4545
grayscale Rec709Luma 0.5 0.2198 Gray 0.4545
grayscale Lightness 0.5 0.1606 Gray 0.4545
grayscale Average 0.5 0.1693 Gray 0.4545
grayscale MS 2.202e+04 1.147e+04 Gray 0.4545
grayscale RMS 0.5563 0.1628 Gray 0.4545
grayscale Brightness 0.7539 0.1967 Gray 0.4545
colorspace_channel_1 YCC 0.4898 0.2127 sRGB 0.4545
colorspace_channel_1 YDbDr 0.5 0.196 sRGB 0.4545
colorspace_channel_1 YCbCr 0.5 0.196 sRGB 0.4545
colorspace_channel_1 YIQ 0.5 0.196 sRGB 0.4545
colorspace_channel_1 YPbPr 0.5 0.196 sRGB 0.4545
colorspace_channel_1 YUV 0.5 0.196 sRGB 0.4545
colorspace_channel_1 LCH 0.5497 0.2047 sRGB 0.4545
colorspace_channel_1 LCHab 0.5497 0.2047 sRGB 0.4545
colorspace_channel_1 LCHuv 0.5497 0.2047 sRGB 0.4545
colorspace_channel_1 Lab 0.5497 0.2047 sRGB 0.4545
colorspace Gray 0.5 0.2198 Gray 0.4545
colorspace LinearGray 0.3133 0.2234 LinearGray 1
colorspace_gray Average 0.5 0.2198 Gray 0.4545
colorspace_gray Brightness 0.5 0.2198 Gray 0.4545
colorspace_gray Lightness 0.5 0.2198 Gray 0.4545
colorspace_gray Mean 0.5 0.2198 Gray 0.4545
colorspace_gray MS 0.5 0.2198 Gray 0.4545
colorspace_gray Rec601Luma 0.5 0.2198 Gray 0.4545
colorspace_gray Rec601Luminance 0.5 0.2198 Gray 0.4545
colorspace_gray Rec709Luma 0.5 0.2198 Gray 0.4545
colorspace_gray Rec709Luminance 0.5 0.2198 Gray 0.4545
colorspace_gray RMS 0.5 0.2198 Gray 0.4545
single_channel Red 0.5 0.2932 Gray 0.4546
single_channel Green 0.5 0.2932 Gray 0.4546
single_channel Blue 0.5 0.2932 Gray 0.4546
poly StrongRed 0.5 0.9725 Gray 0.4546
colour_distance khaki 0.4282 0.1489 Gray 0.4545
colour_distance_2 khaki 0.5718 0.1489 Gray 0.4545
colour_distance_Lab khaki 0.2571 0.1191 sRGB 0.4545

Images with roughly the same mean and standard deviation are probably visually identical. So we sort on Mean and StdDev, eliminating duplicates:

cSort /immList.csv /h /kMean,StdDev /u /ommList2.csv
call csv2tab mmList2
Method Parameter Mean StdDev colorspace gamma
colour_distance_Lab khaki 0.2571 0.1191 sRGB 0.4545
grayscale Rec601Luminance 0.3133 0.1992 LinearGray 1
grayscale Rec709Luminance 0.3133 0.2234 LinearGray 1
colour_distance khaki 0.4282 0.1489 Gray 0.4545
colorspace_channel_1 YCC 0.4898 0.2127 sRGB 0.4545
monochrome -- 0.49 0.4999 Gray 0.4545
grayscale Lightness 0.5 0.1606 Gray 0.4545
grayscale Average 0.5 0.1693 Gray 0.4545
colorspace_channel_1 YIQ 0.5 0.196 sRGB 0.4545
colorspace_gray Lightness 0.5 0.2198 Gray 0.4545
single_channel Blue 0.5 0.2932 Gray 0.4546
poly StrongRed 0.5 0.9725 Gray 0.4546
colorspace_channel_1 Lab 0.5497 0.2047 sRGB 0.4545
grayscale RMS 0.5563 0.1628 Gray 0.4545
colour_distance_2 khaki 0.5718 0.1489 Gray 0.4545
grayscale Brightness 0.7539 0.1967 Gray 0.4545
grayscale MS 2.202e+04 1.147e+04 Gray 0.4545

Illustrations

We can illustrate the methods.

Grayscale:

FOR %%H ^
IN (Rec601Luminance,Rec709Luminance,Rec601Luma,Rec709Luma,^
Lightness,Average,MS,RMS,Brightness) ^
DO (
  set twiddle=0
  if %%H==Rec601Luminance set twiddle=1
  if %%H==Rec709Luminance set twiddle=1

  if !twiddle!==1 (
    set tweakRgb=-set colorspace RGB -colorspace sRGB
  ) else (
    set tweakRgb=
  )

%IMG7%magick ^
  %SRC_TOES% ^
  -grayscale %%H ^
  -gravity SouthWest -undercolor #fff -annotate 0 "grayscale %%H" ^
  !tweakRgb! ^
  toes_gr_%%H.png
)
toes_gr_Rec601Luminance.pngjpg toes_gr_Rec709Luminance.pngjpg toes_gr_Rec601Luma.pngjpg toes_gr_Rec709Luma.pngjpg toes_gr_Lightness.pngjpg toes_gr_Average.pngjpg toes_gr_MS.pngjpg toes_gr_RMS.pngjpg toes_gr_Brightness.pngjpg

Colorspace gray:

FOR %%H ^
IN (Rec709Luminance,Rec709Luma,Brightness) ^
DO (
%IMG7%magick ^
  %SRC_TOES% ^
  -intensity %%H ^
  -colorspace gray ^
  +intensity ^
  -gravity SouthWest -undercolor #fff -annotate 0 "intensity %%H" ^
  toes_cgr_%%H.png
)
toes_cgr_Rec709Luminance.pngjpg toes_cgr_Rec709Luma.pngjpg toes_cgr_Brightness.pngjpg

Colorspace channel:

FOR %%H ^
IN (YCC,YIQ,LCH,Lab) ^
DO (
%IMG7%magick ^
  %SRC_TOES% ^
  -colorspace %%H ^
  -channel GB -evaluate set 50%% ^
  -colorspace sRGB ^
  -gravity SouthWest -undercolor #fff -annotate 0 "colorspace %%H" ^
  toes_cc1_%%H.png
)
toes_cc1_YCC.pngjpg toes_cc1_YIQ.pngjpg toes_cc1_LCH.pngjpg toes_cc1_Lab.pngjpg

Single channel 1:

%IMG7%magick ^
  %SRC_TOES% ^
  -separate -delete 1-2 ^
  -gravity SouthWest -undercolor #fff -annotate 0 Red ^
  toes_1c_red.png

%IMG7%magick ^
  %SRC_TOES% ^
  -separate -delete 0,2 ^
  -gravity SouthWest -undercolor #fff -annotate 0 Green ^
  toes_1c_green.png

%IMG7%magick ^
  %SRC_TOES% ^
  -separate -delete 0-1 ^
  -gravity SouthWest -undercolor #fff -annotate 0 Blue ^
  toes_1c_blue.png
toes_1c_red.pngjpg toes_1c_green.pngjpg toes_1c_blue.pngjpg

Colour distance:

call %PICTBAT%hashCol %SRC_TOES% -crop 10x10+116+86

%IMG7%magick ^
  %SRC_TOES% ^
  ^( +clone -fill %HASH_COL% -colorize 100 ^) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  toes_cd.png
toes_cd.pngjpg

Colour distance 2:

%IMG7%magick ^
  %SRC_TOES% ^
  ^( +clone -fill %HASH_COL% -colorize 100 -negate ^) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -negate ^
  toes_cd2.png
toes_cd2.pngjpg

Colour distance using smallest distance to two sampled areas:

set ONE_HASH_COL=%HASH_COL%

call %PICTBAT%hashCol %SRC_TOES% -crop 10x10+225+116

%IMG7%magick ^
  %SRC_TOES% ^
  ^( -clone 0 ^
    ^( +clone -fill %ONE_HASH_COL% -colorize 100 ^) ^
    -compose Difference -composite ^
    -grayscale RMS ^
  ^) ^
  ^( -clone 0 ^
    ^( +clone -fill %HASH_COL% -colorize 100 ^) ^
    -compose Difference -composite ^
    -grayscale RMS ^
  ^) ^
  -delete 0 ^
  -compose Darken -composite ^
  toes_cd3.png
toes_cd3.pngjpg

Colour distance Lab:

%IMG7%magick ^
  %SRC_TOES% ^
  ^( +clone -fill %HASH_COL% -colorize 100 ^) ^
  -colorspace Lab ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -set colorspace sRGB ^
  toes_cd4.png
toes_cd4.pngjpg

Col filt. Create a hald-clut, creating a mask that is lightest at the hue (channel a and b of Lab) of the toes ...

%IMG7%magick ^
  hald:8 ^
  ( +clone -fill %HASH_COL% -colorize 100 ) ^
  -colorspace Lab ^
  -compose Difference -composite ^
  -gamma 0.5 ^
  -separate ^
  -delete 0 ^
  -compose Add -composite ^
  -gamma 2 ^
  -set colorspace sRGB ^
  -negate ^
  toes_cfDist.png
toes_cfDist.pngjpg

... and apply it:

%IMG7%magick ^
  %SRC_TOES% ^
  ( -clone 0 -modulate 100,0,100 ) ^
  +swap ^
  ( -clone 1 ^
    toes_cfDist.png -hald-clut ^
    -auto-level ^
    -write toes_cf_halded.png ^
  ) ^
  -composite ^
  toes_cf.png
toes_cf_halded.png toes_cf.pngjpg

The grayscale image is roughly what a B&W camera would show with a skin-colour filter over the lens.

The colour image has the toes at unchanged saturation, and the grass at lower saturation.

Script

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

hashCol.bat is:

rem Returns the average colour of %1 %2 .. in environment variable HASH_COL.
@rem
@rem Updated:
@rem   22-August-2022 Upgraded for IM v7.
@rem

for /F "usebackq skip=1 tokens=6 delims=():,%% " %%L ^
in (`%IMG7%magick %* -resize 1x1 -colorspace sRGB txt:`) ^
do set HASH_COL=%%L

echo %HASH_COL%

All data and images on this page were created by the commands shown, (except that to conserve bandwidth some PNG files were converted to JPG), using:

%IMG7%magick -version
Version: ImageMagick 7.1.0-42 Q16-HDRI x64 396d87c:20220709 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenCL 
Delegates (built-in): bzlib cairo freetype gslib heic jng jp2 jpeg jxl lcms lqr lzma openexr pangocairo png ps raqm raw rsvg tiff webp xml zip zlib
Compiler: Visual Studio 2022 (193231332)

Source file for this web page is mmono.h1. To re-create this web page, execute mmono.bat.


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v2.3 9-May-2014.

Page created 23-Aug-2022 21:40:19.

Copyright © 2022 Alan Gibson.