-distance

This command measures the distances of input pixels from benchmarks with a particular isovalue and creates a data set of displacements, each entry similarly positioned as the input pixel and measuring its distance to the isovalue.

Suppose the isovalue is zero. Then a non-zero pixel from the input at least twenty seven pixels away from one with zero will correspond to a data set entry with the same coordinates as the input pixel but with a value of twenty seven. Pixels in an input image with the isovalue will always be zero in the data set since they are no distance at all from themselves.

Since isovalues are simple scalar values, the command operates primarily on single channel (gray scale) images. Colors, being vectors, cannot be isovalues. If the -distance command has a multi-spectral operand, it will treat each image channel independently, producing a like multi-spectral image with each channel an independent distance data set of measurements to isovalues.

Usually, operands for -distance arise from logic commands (-lt, --eq,...) and are single channel bi-valued (0, 1) images. The logic command presumably identifies pixels meeting some criterion and produces the immediate operand with identified pixels set to one and failing pixels set to zero. When given an isovalue of one, the -distance command determines how far away the failing pixels are from those which are not. It then generates an output image dimensioned as the operand but with pixels shaded to reflect the distances that their counterparts are from the nearest isovalues.

Pixels in the original image (figure: left) with the isovalue zero presumably meet some criterion. They are also black (zero) in the resulting data set (figure: middle), as no distance at all separates these from the isovalue. All other pixels in the data set exhibit varying shades of gray, reflecting the varying distances of their counterparts to input pixels with the given isovalue. The lightest pixels in the data set correspond to those in the operand image that are furthest from the isovalue.

It is not uncommon to further convert the data set produced by -distance with a -map command, as was done with the figure: right image. The Hue-Saturation-Value map used in this example identifies red with zero and violet to the highest value found in the data set. Often, such color coding makes distance relationships easier to grasp than with unadorned gray scale images.

The command has two variants. The first assumes the measure of pixel edges to be fixed, with tabulation of length along each dimension being little more than a count of edges:

-distance isovalue,_metric {0=chebyshev | 1=manhattan | 2=euclidean | 3=squared-euclidean }

  1. isovalue: A reference intensity. It may be specified as a particular floating point or integer value, or as a percentage of the intensity range from the lightest to the darkest pixel in the operand image (iM – im) . Pixels with this value serve as benchmarks. They are “signposts” from which one measures distances. They render black in the resulting data set because, obviously, no distance separates them from themselves.
  2. _metric: A selector assuming one of four values, from zero to three, choosing a particular measuring function. This selector may be omitted and defaults to 2, selecting the Euclidean metric. See "Metric Functions", below.

The isovalue may be any value in the input range, but as a practical matter is best expressed as an integer value, often zero or one, with the input image commonly having a single binary value gray scale channel. (True/False, 1, 0). Floating point values can lead to unexpected results because the match to the isovalue must be exact, but the slight variations common with floating point values precludes such exact matches and, conceivably, the -distance command might not identify any pixel in an operand as having a given isovalue, while there may be thousand of pixels that happen to be exceedingly close. In these circumstances, -distance will produce data sets with every entry set to the largest floating point value the math library can express, since, from the command's perspective, the isovalue is "very far away" from every pixel in the operand. The receipt of such a data set is usually cause to re-think what one is trying to do. It may make sense to use -round on an operand to slightly flatten a dataset into distinct isovalues.

The second variant makes use of a custom metric map, this being realized by a gray scale image that is the same size as the input image.

-distance isovalue[%],[metric],_method

  1. isovalue: a reference intensity. This parameter functions identically to that of the first variant.
  2. [metric]: a selection from the image list of a single channel, gray scale metric map composed of zero or positive value pixels. The metric map is identical in size to the operands and the intensity of each pixel in the metric map represents the “cost” of traversing the corresponding pixel in the operand images. These are multipliers and may scale the distance associated with a pixel from zero to an arbitrarily large value. See “Metric Maps” below. The -distance command will fail if the dimensions of the metric map differ from that of any of the selected operand images.
  3.  _method: a selector ranging from 0-4, choosing one of three metric methods along with two variants:
    1. Fast Marching (selector is 0): When all multipliers in the metric map are constant, Fast Marching behaves very much like the Euclidean metric (see Metric Functions). This is a low-connectivity metric (see -label for an introduction to the idea of image connectivity). That is, two pixel regions touching only at corners are disjoint for measuring purposes.
    2. Low-Connectivity Dijkstra (selector is 1): When all multipliers in the metric map are constant, this metric measures distances much like the Manhattan metric (see Metric Functions). As the name implies, this too is a low-connectivity metric; pixel regions touching only at corners are disjoint for measuring purposes.
    3. High-Connectivity Dijkstra (selector is 2): When all multipliers in the metric map are constant, evaluates distances approximating a Euclidean metric. A high-connectivity metric, pixel regions touching only at corners are not considered disjoint for measuring purposes
    4. Low-Connectivity Dijkstra with Return Path Map (selector is 3): The metric is the same as method 1, but also produces a secondary Return Path map. The G'MIC -minimal_path command harnesses -distance to generate this map when it is used with its connectivity selector set to 'low'.
    5.  High-Connectivity Dijkstra (selector is 4): The metric is the same as method 2, but also produces a secondary Return Path map. The G'MIC -minimal_path command harnesses -distance to generate this map when it is used when its connectivity selector set to 'high'.

The Return Maps cited in methods (d) and (e) are control word datasets, the control words correspond to each pixel in the operand image and are intended to direct some abstract pointer or cursor. G'MIC images exhibit three degrees of independent movement along width, height and depth axes, giving each pixel as many as twenty six distinct neighbors to which an abstract cursor sitting on the pixel may move. The control words are so composed to either direct the cursor one step toward the nearest isovalue, where it is to read the next control word, or stay in place because the pixel upon which it is situated is an isovalue. Other G'MIC commands such as -minimal_path implement cursors that read these Return Maps.

Metric Functions

A metric space is a set of points associated with a metric, a function that takes a pair of points from the set and returns the distance separating them. The metric space with which we are most familar, Euclidean space, uses the Pythagorean Formula as a metric, but other spaces exist which use different metrics. Of particular interest are those metrics that are cheap to compute, but which give rise to spaces sufficiently like a Euclidean one to serve most purposes. We should not be too alarmed if the distances in these spaces seem strange (or just plain wrong) to our Euclidean trained minds. For many purposes, all that we ask of a metric is to consistently distinguish near points from far points, and to do so as cheaply as possible. With this frame of mind, we are usually disposed to avoid the (expensive!) square root operation of the Pythagorean Formula.

The following series of images each illustrate one of the four metric spaces supported by the -distance command. The antecedent image is everywhere black except for the upper left hand pixel, which has been set to an intensity of one. We invoke the  -distance command four times to measure distances to this isovalue, specifying different metrics each time.

To help visualize distances, we harness the -map command to encode distance with color. Here, red — rgb(255, 0, 0) — maps to a distance of zero and reddish violet to the maximum distance in the dataset. Pixels with similar colors are at a similar distance to the reference point under the given metric. There is no loss in generality if we simply regard these images as unit squares, sides equal to one, as that is a simple rescaling from whatever number of pixels these images are in this page.

We can succinctly generate the illustrations with the following G'MIC pipeline:

$ gmic 256,256,1,1 -set[-1] 1,0,0,0
  -repeat 4,mapid [0] -distance[-1] 1,'$mapid'
  -map[-1] 1,1 -normalize[-1] 0,255 -o[-1] dis_'$mapid'.png
  -rm[-1] -done -rm[0]

Chebyshev

The chebyshev metric (_metric=0) defines the distance between any two points in the space as the largest of their differences along any spatial dimension.

Construct a rectangle (a cuboid in three dimensions) on the line segment connecting the two points. In a space with a chebyshev metric, the separating distance is just the longest side of that geometric. the locus of points at a particular distance from the reference point form a square centered on the reference point.

In this metric space, the length of the diagnonal is the same as the longest side. Indeed, in the square image on the left, the distance from the reference point to any point on either of the opposite sides is the same, which makes no sense to anyone grounded in the Euclidean metric. That oddity notwithstanding, the chebyshev metric furnishes a consistent reporting of distances between points  — and it is exceedingly cheap to compute.

Manhattan

The manhattan metric (_metric=1) defines the distance between any two points as the sum of the absolutes of differences along each spatial dimension:

We construct a rectangle (or cuboid) on the segment connecting the two points and add up the components along each dimension. The distance is exactly that of a taxi constrained to travel along the streets of a square grid such as in Manhattan, New York City (more or less).The locus of points at a particular distance from the reference form a diamond centered on the reference point.

At the price of a little extra computation, at least with respect to the chebyshev metric, distances are fairly akin to those obtained with the Pythagorean Theorem. In the image at the left, diagonals of squares are longer than the sides, akin to what one would find with the Euclidean metric, though values differ. With the manhattan metric, the diagonal of the square image on the left is two, not the square root of two, for sides equal to one.

Euclidean

The euclidean metric (_metric=2, default) defines the distance between any two points using the Pythagorean Formula:

It is the "ordinary" distance which would be obtained with a ruler measuring the line segment between a given pair of points. The locus of points at a particular distance from the reference form a circle centered on the reference point.

This is the  natural, "common-sense" distance which most of us associate with the space around us and is the default method of the -distance command. It is computationally expensive compared to the other metrics, but renders the distance that one expects.

Squared-Euclidean

The squared-euclidean metric (_metric=3) is simply the euclidean metric without the square root operation:

Usually, the square root operation is the most expensive; its elimination gives rise to a metric space that behaves very much like a Euclidean one, differing only in that distances between points grow quadratically as points separate instead of linearly. Usefully, distances which sort in a particular order with a euclidean metric would sort to the same order with the squared-euclidean metric. This stems from the fact that the locus of points at a particular distance from the reference still forms a circle centered on the reference point, like the euclidean metric.

Custom Metric Maps

Heretofore, we've been content to regard a pixel as representing a fixed object, its sides defining a unit length. We see elsewhere that choosing one or another metric (Euclidean, Manhattan...) can affect the computed distance between points, but in every case the pixel itself is a thing of constant measure and the tabulation of distances along the cardinal axes, common to each metric function, amounts to counting so many edges, each of unit length. It is under this regimen that the first form of the -distance command operates.

When pixels vary in size or embody local traversal costs, the concept of 'distance' takes on a more general aspect. To accommodate this, the second form of the -distance command takes a metric map having the same width, height and depth as the operand image, with a one-to-one correspondence between the pixels of each. When such a map is present, the -distance command no longer associates a fixed length with pixel edges, but consults the metric map for a non-negative multiplier. If the multiplier is zero, the corresponding pixel is a dimensionless point and may be traversed without accruing distance. On the other hand, the multiplier may be positive and arbitrarily large, setting the size or “traversal cost” of the pixel.

In this regimen, some very useful nuances may be introduced to the computation of distances. For example, in the so-called real world which purportedly exists outside the realm of computer graphics, we are told that hills are expensive. Climbing them requires additional fuel, Going down them wears out brakes. We generally go slower on such steep grades. In contrast, roads with low grades are relatively cheap to travel and we can go pretty fast. In a sense, two points connected by a road with low grades are closer together than a like pair of points with a lot of intervening hills, even though, “as the crow flies,” both pairs of points are about the same distance apart.

We might consider a gray scale image as a height map of such hilly terrain. It might even look like an image produced by -plasma. Applying the -gradient_norm command to this height map yields a new gray scale image where rapid changes in intensity in the original – steep grades – register as white or light gray in the gradient norm image – expensive pixels. We can then use this gradient norm image as a custom metric map. It will associate steep gradients with expensive pixels and the -distance command will produce a data set of distances adjusted to reflect the vagaries of the terrain.

Here's a gallery of distance data sets, each to a fixed triplet of pixels of a particular isovalue plotting the vertices of an isosceles triangle. Each data set has different metric map.

Metric MapDistance data setComment
None For comparison, this distance map arises from the application of -distance 1,2, harnessing the Euclidean metric. The vertices of the triangle have isovalues of 1 in the operand image. There is no metric map so all pixels in the operand were considered to be unit size. Unsurprisingly, points equidistant from vertex pairs form line segments. This is a “typical” distance data set.
We have applied a metric map, a gradient norm of a plasma, which tells the distance command about steep gradients situated among the isovalues. Observe the equal distance loci (lightest grays) which formed line segments in the previous dataset, but here are jagged, reflecting the varying expense of traversing pixels, altering the de facto distances among isovalues.
The metric map reflects an elaborate system of walls which partition the intervening regions between vertices. The effective distance to a vertex is fairly constant for large areas; the cost of traveling within walls is nearly free. The expense climbing over walls is reflected by abrupt changes in cost.
Vertices are surrounded by rectangular regions of expensive travel, these just touching at corners. We generated the dataset on the near left with a low-connectivity metric, so that the regions just surrounding each vertex are disconnected from the outside. Compare the shade of gray of pixels in the middle of this dataset with those of the following set.
This set differs from the previous only in that a high-connectivity metric was used, so that regions just touching at corners are considered “connected.” The middle pixels are darker – in effect less distant than – middle pixels in the previous set because now there are corner-crossing bridges, effectively shrinking the distance (or cost of travel) from the middle to the vertices.
The metric map in this example places a high cost of travel from nearly every point, with the exception of a network of roads where travel is nearly free. Points very close to vertices “as the crow flies” are, in effect, extremely distant from a cost-of-travel viewpoint. Every point on a “road” is more proximate to isovalues than the off-road pixel.

Application: Modifying a Warning Image

It makes no sense to ask for distances to mauve, navy blue, pink or any other multiple-component color: -distance measures only to single channel intensity values. One may ask for distances to particular isovalues in multispectral images and obtain multispectral datasets indicating distances to the isovalue in each of the channels, with each channel treated as an independent grayscale image. If you are interested in the distances of all pixels to those colored some particular lime green, this will not give you what you want. Colors consist of multiple intensities; -distance will only measure to one such intensity.

Finding distances to particular colors can be done, but with two steps. Step one is a logic question: ask if a pixel has a particular color. Posing that question to every pixel in an image produces a new dataset of binary yes/no, one/zero values. This is a dataset upon which -distance can operate, the second step. A practical example follows.

Desktop publishing systems can produce 'out of gamut' warning images where all out of gamut pixels in some original are replaced with magenta, green, or some other highly saturated color, a kind that rarely appears in photographic images. Perhaps we might be interested in large out-of-gamut areas, but regard 'speckles' as acceptable. We don't wish to be bothered with images that may be out of gamut in a few pixels here and there. For our purposes, let any clump of pixels with a diameter of eight pixels or less be a 'speckle' or a line too thin to matter. The following G'MIC pipeline produces a modified warning image which, if it contains white (true) areas, corresponds to an original that really does require our attention.

$ gmic -input wizard_gamut-warn.png 'fn={0,n}'
  -select_color 255,0,127 -distance[-1] 1
  -ge[-1] 4 -if '{iM>0}' -output[-1] 'bad-${fn}' -endif

Here is a walkthrough of this command sequence:

1

gmic -input wizard_gamut-warn.png 'fn={0,n}'

A desktop publishing system reviewing images for printing on coated paper using four color process inks (CMYK) finds the RGB image of the wizard largely out of gamut, according to the output ICC profile furnished by the printing house.

The particular subsystem making this discovery generates a new image, replacing all out-of-gamut colors with rgb(255,0,127), a reddish magenta. Other systems could use different indicator colors.

The human in charge doesn't want to be bothered if only isolated pixels run out of gamut, but this example probably would concern him. The G'MIC pipeline which alerts him generates a new warning image, without speckles, but with areas coinciding with large out-of-gamut regions in the original. When the human sees the revised warning image, prefixed with 'bad-', he or she can inspect it, determine how bad the problem is, and decide upon some action.

The first part of this pipeline entails basic setup housekeeping. Of note is the setting of a local variable, 'fn', to the file name of the first image on the stack, which will be used later in the pipeline if a warning image needs to be generated.

2

-select_color 0,255,0,127

Since -distance does not work with color directly, the G'MIC -select_color command can be harnessed to set out-of-gamut pixels to black and in-gamut colors to white, a dataset with which -distance can work.

3

-distance[-1] 1

Given the binary file from the last step, the -distance command makes a single channel gray scale dataset where pixels that are equal to the isovalue — in-gamut pixels — are black. Out of gamut pixels are some measurable distance from these; the command sets the intensity value of these to the distance from the nearest in-gamut pixel.

Out-of-gamut pixels in isolated 'speckles' would be very close to in-gamut pixels. They would have small values, be very dark and would not be of great concern to the human. On the other hand, out-of-gamut pixels in large masses will acquire very high distance values, exhibit a light gray and flag a condition which the human would like to know about.

4

-ge[-1] 4 -if '{iM>0}' -output[-1] 'bad-${fn}' -endif

The -ge command — greater than or equal to some reference value — produces another binary dataset. Here, any pixel with a value greater than or equal to 4 is in a clump larger than a  mere speckle. The combination of -distance and -ge filters out the isolated out-of-gamut speckles, but identifies the broad swatch of background and many areas in the wizard's robe as sizeable regions of out-of-gamut color.

The control command -if ... -endif assesses the environmental value iM, which always contains the largest value observed in the last image on the stack. Since large areas of out-of-gamut pixels have been found, there are pixels in the dataset equal to one, setting the value of the aggregate variable, iM, to one. This triggers the writing of this image to a new file, which has the same name as the old file, but with the prefix 'bad-' added to the file name. Note the use of the variable fn set in step one: bad-${fn}, The ${...} construct tells G'MIC to access the value stored in varialble 'fn' and substitute it into the new file name.

Later on, the human in charge will look at the files prefixed with 'bad-' to determine how bad the corresponding images are, and, with luck, be able to adjust them accordingly.

Garry Osgood