Good evening, dinasset
Thanks be to Issabella, for furnishing such an interesting image. The building seems precariously situated. Am I correct that the foreground is a dried lake/seabed? It reminds me of the ruined monasteries that dot the countryside of Erie and the United Kingdom, once beautiful buildings gone to ruin. In particular,
Tintern Abbey in Wales, a beautiful, somber, bittersweet place.
On to business -
First, I think the thread has become misnamed. The questions are no longer small, and the answers certainly are not! This post, in fact, has become something like 3,000 words, so it will likely go into
Beginners Cookbook at G'MIC Sourceforge in the fullness of time.
Second - I'd like to drill down on the term "gamma" as it is used with the
Adjust Color Levels dialogue. In particular, the usage as inferred from this sentence in your post #16:
Quote:
Let's consider just the mean (the "ia" in GMIC); how can we "move" it either towards black or towards white when needed such that all values between "im"(minimum value) and "ia" are "normalized" between 0 and USER-MEAN-GOAL and values between "ia" and "iM"(maximum value) are "normalized" between USER-MEAN-GOAL and 255?
Particular DistinctionsThe particular notion that I wish to dispel is that "blackpoint" is equivalent to G'MIC's "im", "gamma" is equivalent to G'MIC's "ia" and "whitepoint" is equivalent to 'iM"
Such is not the case. They constitute two sets of terms, each fulfilling different purposes.
- im, ia, and iM are statistical indicators concerning, in particular, the last item on an image list. G'MIC computes these at the behest of a -print command by visiting every pixel in an image and finding the smallest, largest and average value of the ensemble. I can direct G'MIC to tabulate these data for Issabella's image (IMG_1730.JPG) and report on them via "$ gmic -input IMG_1730.JPG -print" and will be told, among other things, that the minimum pixel intensity value found in this image across three channels is 10, ("im") the maximum intensity value found is 255 ("iM") and the mean (average) value is 137.543 ("ia"). These are observable statistical properties of image.
- The values associated with "blackpoint", "gamma", and "whitepoint" are arguments that, when plugged into the computational engine that implements the "Input Level" slider, produces what I call a transfer function. They are arguments which can be chosen at whim, though one usually has some reason to choose particular values because of particular trends seen in image statistics. These reasons are driven as much by individual notions and heuristics. There is no invariant formula that I'm aware of that furnishes "correct" values for these arguments, based on some statistical ensemble of an image.
Blackpoints, Whitepoints and GammaTransfer functions may be applied to any number of images and have no association with any particular image. And while "blackpoint" and "whitepoint" are numeric quantities measurable in terms of pixel intensity, and, as such, are “of a kind” with "im", "ia," and "iM", they do not have the same
meaning as these quantities. They serve purposes different than the expression of minimum or maximum values of particular images.
In devising transfer functions, "blackpoint" and "whitepoint" peg – or
define – the two end point values of an intensity curve plotted on a graph that has horizontal and vertical scales, each graduated in the intensity values that unsigned eight bit integers are capable of expressing: the range [0,...255]. The curve is a literal visualization of a transfer function.
By themselves, "blackpoint" and "whitepoint" can only define very simple line-like (linear) transfer functions. The third ingredient, “gamma”, enables the generation of very versatile
non-linear transfer functions, but before I take up the subject of this third ingredient, I want to spend a little bit of time with the concept of the transfer function itself – how it works and what it “means.” For that end, I only need the two “essential ingredients”, “whitepoint” and “blackpoint”.
Linear transfer functionsNow, it so happens that you have seen about a bazillon and seven of these things, give or take about five or six. Every time you bring up Gimp's Curve tool you see at least one transfer function, and can see as many as four per RGBA image, one for each channel.
The mechanism of these transfer functions is very simple.
- The horizontal scale represents operands from the set of intensities [0,...255]. You might consider them to be the set of intensities from a particular image, say the red channel of IMG_1730.JPG, but that is your interpretation, and is not intrinsic to the operands, for here in Brooklyn New York, I might be at the very same moment regarding them as the set of intensities from the gray scale image grayscale_16.png, and Ronounours in Caen may have yet a third image in mind. This transfer function exists independently of all of us, and the various interpretations that we might impose.
- The curve itself is an operator. It maps operands to resultants, here another set of intensities from the set [0,...255].
- The mechanism of operation entails choosing an operand, say “128” (mid gray), tracing vertically up until we encounter a point on the operator, inducing us to travel horizontally to the left until we encounter the vertical scale where we stop upon a resultant.
- Now for some particulars. Suppose for this particular transfer function, the “blackpoint” is zero, and the “whitepoint” is 125, which suffices to establish a simple linear transfer function. Then in this particular example, our resultant is 62.745, since this particular linear transfer function simply reduces operands by the ratio of 125/255, and 128 times 0.490196078 (125/255 in decimal) is 62.745.
That is the gist of how a transfer function works. It is a
map from the operand grays to the resultant grays. This particular transfer function, or map, as you probably surmise, would be good for taking images that depict bright sunny scenes and turning them into versions lit by moonlight, darker, somber scenes. That said, I cannot stress enough that this particular usage has nothing to do with how we composed the original map. The “whitepoint” and “blackpoint” values do not have anything to do with particular images, or values like “im” or “iM” that might be discovered in particular images.
GammaLinear transfer functions, those simply composed from pegging white and black points, are useful in a limited sort of way, but do not adequately perform most of the mappings that photographers routinely require. Photographers routinely want to “stretch shadows” or “compress highlights” or desire “middle grays” to be darker or lighter than in the images they have in hand. These all call for
non-linear mappings, that is, transfers from operands to resultants that
cannot be adequately described by straight lines connecting the white and black points. The gamma value may not be essential for constructing transfer functions, but it is practically called for in most “real world” circumstances.
Black and white points are pixel intensities that peg particular operands to resultants. Doing such on two points on the graph allows a ratio to be established which scales operands linearly into resultants. Blackpoint matches up operand value zero to a resultant value, and necessarily is a pixel intensity. Similarly, Whitepoint matches up operand value 255 to another resultant value and is also necessarily a pixel intensity. Together these fix new intensity ranges for the resultant values. The appropriate range of values for both black and white points is [0,...255].
Gamma is intrinsically
different from black and white points. It is not a pixel intensity, but a unitless number, a value that just “is” and one not representing a physical quantity. Thus, speaking of a gamma value as being equal to the average intensity of an image channel, “ia,” is a nonsense expression, akin to saying that an object is blue shaped.
When transfer functions are linear, the
gamma value A. K. A. C
2, is necessarily equal to one and is the midpoint of a relative logarithmic scale that ranges from (0,...10], the set from which gamma values are drawn.
You can observe the logarithmic behavior of gamma as you manipulate the middle slider of the Input Level control. You find it at its default value of 1.0. Pull it right (toward “white”) and it ranges toward (but never includes!) zero. In fact, it stops at 0.10. The image, meanwhile, exhibits stretched shadows and compressed highlights and, on average, becomes very dark. In contrast, pulling the control left (toward “black”) involves a rapid increase of numbers toward the value 10. Truly, this odd scale permits high precision for setting gamma values in the range of one to (nearly!) zero, but allows only coarse settings in the range of one to ten. The image, meanwhile, exhibits compressed shadows and stretched highlights and, on average, becomes very light.
Much of this behavior stems from how Gimp defines the transfer function, repeated here from the previous post:
-
-
(1.0)In particular, Gimp takes the reciprocal of the middle slider, C
2, and uses that as the exponent of a power function. Pull the slider left, and the gamma value becomes less than one, the reciprocal becomes greater than one, and the mapping of operand values
i to resultants
i' follows a curve that bows down toward the southeast at degrees increasingly extreme as the slider approaches zero. Conversely, pulling the slider right sends the gamma value greater than one, the reciprocal goes less than one, and the mapping of operands to resultants follows a curve that bows up toward the northeast at degrees increasingly extreme as the slider approaches ten.
Clearly, if this argument becomes zero, then we will be dividing by zero in the exponent, Gimp will crash and Mitch Natterer will get rude email, which he does not want. So, to allow Mitch to sleep at night, gamma values may approach, but never
be zero. Thus, the set of numbers from which we may choose gamma is open at one end; the elements of the set approach, but do not include zero.
The Adjust Color Levels tool has a nice big button that generates curves to effect its settings, transferring focus to Gimp's Color Curve tool. The Adjust Color Levels tool is essentially an alternate “front-end” to the Curve tool, one couched in terms more meaningful to photographers, but every tranform it performs may be alternately expressed by the Curve tool, though perhaps not as easily. It is not uncommon to refine the initial transfer functions produced by the Adjust Color Levels with the Curve tool.
The Emulation PipelineAs I noted in the previous post, three G'MIC commands emulate the Color Adjust Levels dialog box. They do so by working in sequence through the following pipeline:
$gmic -input changeme.png -cut <C[size=50]1[/size]>,<C[size=50]3> -normalize[-1] 0,255 -apply_gamma <C[size=50]2[/size]> -normalize[-1] <C[size=50]4[/size]>,<C[size=50]5[/size]> -output[-1] imchanged.png
You transcribe the five values C
1 – C
5 from the Input and Output Level sliders, as depicted in my previous post, and that creates a pipeline that remaps the intensity levels in 'changeme.png' in a manner identical to the settings from the Color Adjust Levels dialog box.
The remapping takes place in stages. First, the -cut command sets all operand values less than <C
1> to C
1, sets all operand values greater than C
3 to C
3 but otherwise leaves operands alone.
Second, a -normalize command rescales the truncated range back to [0,...255], producing a simple linear transfer function that, when graphed, starts at coordinate point (C
1, 0), the “blackpoint”, and ends at (C
3, 255), the “whitepoint.
At this stage, the transfer function is functional, but limited, as it just rescales operand values to resultants. In particular, operands equal to C
1 map to resultant zero, operands equal to C
3 map to resultant 255, and all other operands
i map to resultants
i' by the relationship:
-
-
(
(2.0)
Third, -apply_gamma changes the mapping to a nonlinear one. There are two distinct outcomes, one distinguished by gamma values less than one, bending the curve “southeast,” the other distinguished by gamma values greater than one, bending the curve “northwest.” The first ( gamma=0.5694 ) produces a darker resultant scale, the second (gamma=1.9788) a lighter scale. In both cases, the progression of dark to light has been altered from a constant progression to a variable one that emphasizes either highlights or shadows.
Fourth, C
4 and C
5 together renormalize the range of the output to something other than the full range.
This graph summarizes how the various settings in the Color Adjust Levels transform from the initial identity transform, where operands map unchanged to resultants, to one that alters the appearance of an image.
From this, it is clear that C
2 is the most remarkable argument of the five, in that it neither scales nor cuts the curve, but instead controls the characteristics of a power function. It is also along among the five in that it is not a pixel measure, and this makes its behavior difficult to relate to an image. We know that when it equals one, the transfer function is linear. Otherwise it is a nonlinear function that, roughly alters highlights and shadows at different rates. It is not intuitive what an apt setting of this parameter might be if one had the shape of a particular transfer function in mind.
A Better Emulation PipelineHaving the shape of a particular transfer function in mind suggests a solution to the singular parameter, gamma. There is the old bromide that three points not on a line suffices to define at least a degree two curve.
So instead of stumbling around for apt values of gamma, the approach that suggests itself is to solve for gamma using quantities that are more intuitively accessible – pixel measures. The idea is to define a gray that the midpoint of the transfer function should pass through, so that the curve starts at the blackpoint, proceeds through the midpoint positioned at some desired gray, and then finishes at the white point.
The scheme is not hard to implement. Regarding the power relation in which C
2 is used to compute the resultant (
2.0, above), we can adapt it to solve for C
2, since we know the operand gray level (
i) and we have declared the resultant gray,
i' by fiat to be a particular value. We basically turn the relation around, using standard logarithmic identities that I first slept through in high school algebra.
Unless you insist, I'll skip the algebraic to-ing and fro-ing that got C
2 nestled in isolation on the left hand side, and with known (or declared by fiat) quantities on the right hand. Not surprisingly, should
i =
i', C
2 reduces to one, which describes a linear transfer function. Otherwise, we generally choose
i, the operand middle gray, to be equal to 128 and set
i' to be the gray we would like to have in the translation from operands to resultants. Generally, to darken, choose
i' so that
i >
i'; reverse the inequality to lighten. One is not restricted to using middle grays; if one is particular about a particular change in the shadow or highlights, a reference value of 80 or 210 for operand
i is not out of the question. The signal advantage of choosing from the middle of the curve is generally a smoother transfer function.
Rather than plugging in numbers by fiat, it is worth our while to clean up our scripting act by defining a G'MIC function to package the mechanics. Here's one approach:
#@gmic leveladjust : blackpt, current, target, whitept, outblacklvl, outwhitelvl
#@gmic : Adjust selected images using level adjust arguments given
#@gmic : black and white points, current and target intensities,
#@gmic : and required output black and white levels.
#@gmic : Default values: _blackpt=0 _current=127 target=127 _whitept=255 _outblacklvl=0 _outwhitelvl=255
leveladjust :
-check "${1=0}>=0 && ${1}<=255 && ${2=127}>=0 && ${2}<=255 && ${3=127}>=0 && ${3}<=255 && ${4=255}>=0 && ${4}<=255 && ${5=0}>=0 && ${5}<=255 && ${6=255}>=0 && ${6}<=255"
-e[^-1] "Adjust selected images. blackpt: $1, current: $2, target: $3, whitept: $4 outblacklevel: $5 outwhitelevel: $6 "
-verbose -
-repeat @#
gamma=@{"-findgamma "{$2}","{$3}}
-local[$>]
-cut[-1] {$1},{$4}
-normalize[-1] 0,255
-apply_gamma {$gamma}
-normalize[-1] {$5},{$6}
-endlocal
-done
-verbose +
# ---------------------------------------------------------------------------
#@gmic findgamma : current, target
#@gmic : Provide current and target intensities.
#@gmic : Returns corresponding gamma.
#@gmic : Default values: _current=127 _target=127
findgamma :
-check "${1=127}>=0 && ${1}<=255 && ${2=127}>=0 && ${2}<=255"
-e[^-1] "Computing gamma for intensity adjustment from $1 to $2."
-verbose -
-status {(log($1)-log(255))/(log($2)-log(255))}
-verbose +
Copy it to your .gmic file ($HOME/.gmic, UNIX-like, %APPDATA%/gmic and then relaunch your G'MIC plug-in.
If, after contemplating some image such as , your eye tells you that a transfer function ought to start at a blackpoint of 10, pass through 90 at operand value 128, and stop at a whitepoint of 230, and you would prefer that the final image be normalized between 15, and 250, you would write:
gmic -input IMG_1730.JPG -leveladjust 10,128,90,230,15,250 -output[-1] IMG_1730_darker.png
Thus, instead of trying to decide on a particular gamma value, you give -leveladjust a midpoint gray observed in the given operand image ("current") and follow it with what level that gray should be in the resultant image ("target"), and the command will find the appropriate gamma value and apply it in conjunction with the other parameters.
HistogrammeryDeciding what best to do with an image in a script is probably a topic for another day. The G'MIC -histogram command will convert an image into another image with a depth of one row and a width of however many histogram bins that you want to segment the counts into. You can limit the -histogram count to specific ranges like the lowest 10% of an image's dynamic range (or some range in the middle). Each pixel in the converted image contains counts of pixels observed in the original image that are at particular gray levels.
What might be done with that data is often a matter of taste. It is one thing to run a script over a set of images because they are out of gamut according to some ICC profile; profiles contain hard numbers upon which to base adjustments. Matters of taste are more difficult to encode.
Take care
Garry