snibgo's ImageMagick pages

Coordinates of a colour

What are the coordinates of pixels of a given colour?

This page gives methods for finding coordinates of pixels of a given colour. The methods differ widely in speed.

If we want to find all pixels within a certain fuzz percentage of a given colour, we can pre-process with "-fuzz N% -fill COL -opaque COL" before any of these methods.

Input images

We will search in two images: a web-size image, and a larger photograph.

set SRC1=toes.png
toes.png
set SRC2=AGA_1434_gms.tiff

%IM%identify %SRC2% 
AGA_1434_gms.tiff TIFF 4924x7378 4924x7378+0+0 16-bit sRGB 174.1MB 0.000u 0:00.000

[Image not shown]

We will search for these two colours:

set COL1=#EFFFCF64C3FA
set COL2=#40D649304F9E

The first colour is present in the first image but not the second. The second colour is present in the second image but not the first.

We show the colours, purely for interest:

%IM%convert ^
  -size 200x100 xc:%COL1% ^
  colc_c1.png
colc_c1.png
%IM%convert ^
  -size 200x100 xc:%COL2% ^
  colc_c2.png
colc_c2.png

Method: search txt format

For this method, we dump the image in TXT: format, and search with a standard utility. (Microsoft findstr is similar to grep.)

We do four searches, for the two colours in the two images. We show the text output (from stderr), and the elapsed time.

%IM%convert ^
  %SRC1% ^
  txt: |findstr %COL1% 
96,43: (61439,53092,50170)  #EFFFCF64C3FA  srgb(94%,81%,77%)
0 00:00:03

The exact colour was found.

%IM%convert ^
  %SRC1% ^
  txt: |findstr %COL2% 
0 00:00:02

The colour was not found.

%IM%convert ^
  %SRC2% ^
  txt: |findstr %COL1% 
0 00:24:55

The colour was not found.

%IM%convert ^
  %SRC2% ^
  txt: |findstr %COL2% 
2333,3874: (16598,18736,20382)  #40D649304F9E  srgb(25%,29%,31%)
2515,4022: (16598,18736,20382)  #40D649304F9E  srgb(25%,29%,31%)
0 00:24:27

The exact colour was found.

This method always finds all the pixels that match.

It processes about 25,000 pixels per second, which is okay for small images. For large images, the performance is terrible, and the method is too slow to be usable.

Method: subimage-search

This creates a 1x1 image of the colour, and searches for that pixel in the image. -subimage-search is not available in convert so we have to use compare.

By using -metric RMSE -dissimilarity-threshold 1, we find the pixel that has the lowest RMSE from the required colour.

%IM%compare ^
  %SRC1% ^
  xc:%COL1% ^
  -metric RMSE ^
  -dissimilarity-threshold 1 ^
  -subimage-search ^
  NULL: 
0 (0) @ 96,43
0 00:00:02

The exact colour was found.

%IM%compare ^
  %SRC1% ^
  xc:%COL2% ^
  -metric RMSE ^
  -dissimilarity-threshold 1 ^
  -subimage-search ^
  NULL: 
2120.35 (0.0323545) @ 219,191
0 00:00:03

The colour was not found.

%IM%compare ^
  %SRC2% ^
  xc:%COL1% ^
  -metric RMSE ^
  -dissimilarity-threshold 1 ^
  -subimage-search ^
  NULL: 
3200.9 (0.0488427) @ 2623,3186
0 00:13:34

The colour was not found.

%IM%compare ^
  %SRC2% ^
  xc:%COL2% ^
  -metric RMSE ^
  -dissimilarity-threshold 1 ^
  -subimage-search ^
  NULL: 
0 (0) @ 1764,3807
0 00:13:45

The exact colour was found.

The methods always returns a single coordinate, even if multiple pixels are the required colour.

This method is twice the speed of the search txt method, but still too slow to be usable for large images.

Method: transparent and sparse-color

We make pixels of other colours transparent, and list the opaque colours with the sparse-color: format.

%IM%convert ^
  %SRC1% ^
  +transparent %COL1% ^
  sparse-color: 
96,43,srgba(94%,81%,77%,1) 
0 00:00:00

The exact colour was found.

%IM%convert ^
  %SRC1% ^
  +transparent %COL2% ^
  sparse-color: 
0 00:00:00

The colour was not found.

%IM%convert ^
  %SRC2% ^
  +transparent %COL1% ^
  sparse-color: 
0 00:00:08

The colour was not found.

%IM%convert ^
  %SRC2% ^
  +transparent %COL2% ^
  sparse-color: 
2333,3874,srgba(25%,29%,31%,1) 2515,4022,srgba(25%,29%,31%,1) 
0 00:00:08

The exact colour was found.

This is substantially faster than the subimage-search method. If more than one pixel is of that colour, it returns the coordinates of all of them.

This is fast, but a faster method is available...

Method: difference

In this method, we find the difference between the image and a same-size image of the required colour. This difference is zero (black) where the image is the required colour, so we find the minimum value. If this minimum is zero, we have found the required colour.

%IM%convert ^
  %SRC1% ^
  ( +clone -fill %COL1% -colorize 100 ) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -define identify:locate=minimum ^
  -define identify:limit=1 ^
  -identify ^
  NULL: 
 Channel minimum locations:
  Gray: 0 (0) 96,43
0 00:00:00

The exact colour was found.

%IM%convert ^
  %SRC1% ^
  ( +clone -fill %COL2% -colorize 100 ) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -define identify:locate=minimum ^
  -define identify:limit=1 ^
  -identify ^
  NULL: 
 Channel minimum locations:
  Gray: 776 (0.011841) 87,223
0 00:00:00

The colour was not found.

%IM%convert ^
  %SRC2% ^
  ( +clone -fill %COL1% -colorize 100 ) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -define identify:locate=minimum ^
  -define identify:limit=1 ^
  -identify ^
  NULL: 
 Channel minimum locations:
  Gray: 3201 (0.0488441) 2623,3186
0 00:00:03

The colour was not found.

%IM%convert ^
  %SRC2% ^
  ( +clone -fill %COL2% -colorize 100 ) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -define identify:locate=minimum ^
  -define identify:limit=1 ^
  -identify ^
  NULL: 
 Channel minimum locations:
  Gray: 0 (0) 2333,3874
0 00:00:03

The exact colour was found.

This is much faster than a subimage-search, more than two orders of magnitude. It has found the same answers, both for the exact match and the nearest match. The RMSE scores are slightly different because pixel values have been rounded to integers before and after calculating the RMS.

Multiple results

By removing -define identify:limit=1, we get all the pixels that have the required colour.

%IM%convert ^
  %SRC2% ^
  ( +clone -fill %COL2% -colorize 100 ) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -define identify:locate=minimum ^
  -identify ^
  NULL: 
 Channel minimum locations:
  Gray: 0 (0) 2333,3874 2515,4022
0 00:00:02

The exact colour was found.

Parsing the output is slightly painful, so we do the entire job in a script, colCoords.bat.

call %PICTBAT%colCoords %SRC2% %COL2% colc_coords.csv

The resulting output, colc_coords.csv, is:

2333,3874
2515,4022

Future

A process module could be written to directly find a colour, or find a list of colours defined by a second image. As this would involve a single pass over the image, it should be significantly faster.

Scripts

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

colCoords.bat uses the Unix-like tools cut and sed.

colCoords.bat

rem Given an image %1
rem and a colour %2,
rem writes coordinates of pixels that are exactly that colour to CSV file %3.
rem Optional %4 limits the number that will be found.
@rem
@rem Output lines will contain x,y.
@rem If no pixels with this colour are found, output will contain no lines.


@if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ccs

set COL=%2

@set OUTFILE=%OUTBASE%.csv

if not "%3"=="" if not "%3"=="." set OUTFILE=%3

set LIMIT=%4
if "%LIMIT%"=="." set LIMIT=
if "%LIMIT%"=="" set LIMIT=0

echo LIMIT=%LIMIT%

set TMP_TXT=%TEMP%\ccs_tmp.txt

if %LIMIT%==0 (
  set sLIMIT=
) else (
  set sLIMIT=-define identify:limit=%LIMIT%
)


%IM%convert ^
  %INFILE% ^
  ( +clone -fill %COL% -colorize 100 ) ^
  -compose Difference -composite ^
  -grayscale RMS ^
  -define identify:locate=minimum ^
  %sLIMIT% ^
  -identify ^
  NULL: >%TMP_TXT%

set ANY_FOUND=0
for /F "tokens=2 delims=()" %%A in (%TMP_TXT%) do (
  if "%%A"=="0" (
    set ANY_FOUND=1
  )
)

if %ANY_FOUND%==1 (
  cut -d^) -s -f2 %TMP_TXT% |cut -b2- - |sed -e 's/ /\n/g' - >%OUTFILE%
) else (
  copy NUL %OUTFILE%
)


call echoRestore

endlocal & set ccsOUTFILE=%OUTFILE%

All images on this page were created by the commands shown, using:

%IM%identify -version
Version: ImageMagick 6.9.5-3 Q16 x86 2016-07-22 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo flif freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG to JPG.

Source file for this web page is colcoords.h1. To re-create this web page, run "procH1 colcoords".


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 v1.0 21-March-2017.

Page created 28-Jun-2017 14:23:35.

Copyright © 2017 Alan Gibson.