ImageMagick has an infinite number of ways of converting a colour image to monochrome. However, it provides a smaller number of simple methods.
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 |
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 |
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 |
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 |
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 |
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 |
"-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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 ) |
|
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 ) |
|
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 ) |
|
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 |
|
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 |
|
Colour distance 2: %IMG7%magick ^ %SRC_TOES% ^ ^( +clone -fill %HASH_COL% -colorize 100 -negate ^) ^ -compose Difference -composite ^ -grayscale RMS ^ -negate ^ toes_cd2.png |
|
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 |
|
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 |
|
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 |
|
... 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 |
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. |
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.