ImageAdjust¶
Status: Stable
documented, exercised by the test suite and/or worked examples, with no known limitations recorded.
Description¶
ImageAdjust[image]
stretches to the full range: the darkest pixel becomes exactly 0 and the brightest exactly 1. It is IDEMPOTENT, a second stretch being the identity. A constant image has no range to stretch and comes back unchanged, since dividing by zero is not the answer and mapping the single value to either end would be arbitrary. ImageAdjust[image, {c, b}] and [image, {c, b, g}] apply contrast c, brightness b and gamma g by a curve stated here rather than inferred: v' = (v - 1/2)(1 + c) + 1/2 + b, clipped to [0, 1], then raised to the power 1/g. Contrast pivots about mid-grey so it does not also shift brightness; clipping precedes gamma because a negative base has no real power. This curve is Mathilda's documented choice, not a claim of bit-compatibility with Mathematica. Accepts volumes as well as planes.
Examples (39)¶
Every input below was run against the current Mathilda build and its output recorded.
Basic Examples (6)¶
In[1]:= ImageData[ImageAdjust[Image[{{0.25, 0.5}, {0.5, 0.75}}]]]
Out[1]= {{0.0, 0.5}, {0.5, 1.0}}
In[2]:= chk = Image[Table[If[Mod[Quotient[i - 1, 2] + Quotient[j - 1, 2], 2] == 0, 0., 1.], {i, 1, 16}, {j, 1, 16}], "Real"];
In[3]:= disk = Image[Table[N[Boole[(i - 8.5)^2 + (j - 8.5)^2 <= 25]], {i, 1, 16}, {j, 1, 16}], "Real"];
In[4]:= ImageAdjust[chk]
Out[4]= -Image-
In[5]:= ImageAdjust[disk]
Out[5]= -Image-
In[6]:= ImageDimensions[ImageAdjust[chk]]
Out[6]= {16, 16}
Scope (20)¶
In[7]:= ramp = Image[Table[N[(j - 1)/15], {i, 1, 16}, {j, 1, 16}], "Real"];
In[8]:= zone = Image[Table[N[(1 + Cos[((i - 16)^2 + (j - 16)^2)/40.])/2], {i, 1, 32}, {j, 1, 32}], "Real"];
In[9]:= noise = Image[Table[N[Mod[i*37 + j*17, 101]]/101, {i, 1, 32}, {j, 1, 32}], "Real"];
In[10]:= rgb = Image[Table[{N[i/16], N[j/16], 0.5}, {i, 1, 16}, {j, 1, 16}], "Real"];
In[11]:= sky = Image[Table[{N[0.15 + 0.7 (16 - i)/16], N[0.35 + 0.45 (16 - i)/16], N[0.85 - 0.35 (16 - i)/16]}, {i, 1, 16}, {j, 1, 24}], "Real"];
In[12]:= bit = Image[Table[Boole[Mod[i + j, 2] == 0], {i, 1, 8}, {j, 1, 8}]];
In[13]:= byte = Image[Table[Mod[i*13 + j*7, 256], {i, 1, 16}, {j, 1, 16}]];
In[14]:= vol = Image3D[Table[N[Mod[z*7 + y*13 + x*3, 97]]/97, {z, 1, 8}, {y, 1, 10}, {x, 1, 12}], "Real"];
In[15]:= volb = Image3D[Table[N[Boole[x <= 6 && y <= 5]], {z, 1, 8}, {y, 1, 10}, {x, 1, 12}], "Real"];
In[16]:= ImageAdjust[ramp]
Out[16]= -Image-
In[17]:= ImageAdjust[zone]
Out[17]= -Image-
In[18]:= ImageAdjust[noise]
Out[18]= -Image-
In[19]:= ImageAdjust[rgb]
Out[19]= -Image-
In[20]:= ImageAdjust[sky]
Out[20]= -Image-
In[21]:= ImageAdjust[bit]
Out[21]= -Image-
In[22]:= ImageAdjust[byte]
Out[22]= -Image-
In[23]:= ImageAdjust[vol]
Out[23]= -Image-
In[24]:= ImageAdjust[volb]
Out[24]= -Image-
In[25]:= ImageChannels[ImageAdjust[rgb]]
Out[25]= 3
In[26]:= ImageDimensions[ImageAdjust[vol]]
Out[26]= {12, 10, 8}
Applications (4)¶
In[27]:= disk = Image[Table[N[Boole[(i - 8.5)^2 + (j - 8.5)^2 <= 25]], {i, 1, 16}, {j, 1, 16}], "Real"];
In[28]:= zone = Image[Table[N[(1 + Cos[((i - 16)^2 + (j - 16)^2)/40.])/2], {i, 1, 32}, {j, 1, 32}], "Real"];
In[29]:= Binarize[ImageAdjust[zone]]
Out[29]= -Image-
In[30]:= Dilation[ImageAdjust[disk], 1]
Out[30]= -Image-
Properties & Relations (4)¶
In[31]:= chk = Image[Table[If[Mod[Quotient[i - 1, 2] + Quotient[j - 1, 2], 2] == 0, 0., 1.], {i, 1, 16}, {j, 1, 16}], "Real"];
In[32]:= ImageDimensions[ImageAdjust[chk]] === ImageDimensions[chk]
Out[32]= True
In[33]:= Max[Flatten[ImageData[ImageAdjust[chk]]]] <= 1.0
Out[33]= True
In[34]:= Min[Flatten[ImageData[ImageAdjust[chk]]]] >= 0.0
Out[34]= True
Neat Examples (2)¶
In[35]:= zone = Image[Table[N[(1 + Cos[((i - 16)^2 + (j - 16)^2)/40.])/2], {i, 1, 32}, {j, 1, 32}], "Real"];
In[36]:= ImageAdjust[zone]
Out[36]= -Image-
Applications (3)¶
In[37]:= ImageData[ImageAdjust[Image[{{0.2, 0.6}}]]]
Out[37]= {{0.0, 1.0}}
In[38]:= ImageData[ImageAdjust[Image[{{0.5, 0.5}}]]]
Out[38]= {{0.5, 0.5}}
In[39]:= ImageData[ImageAdjust[Image[{{0.5}}], {0, 0.2}]]
Out[39]= {{0.7}}
Implementation notes¶
Algorithm. builtin_imageadjust has two modes. ImageAdjust[image] is a
full-range stretch: it finds the min lo and max hi over the whole buffer
and maps v -> (v − lo)/(hi − lo), so the darkest pixel becomes exactly 0 and
the brightest exactly 1. It is idempotent (a second stretch is the identity),
and a constant image has span = 0 and is returned unchanged — dividing by zero
is not the answer and mapping the single value to either end would be arbitrary.
ImageAdjust[image, {c, b}] and ImageAdjust[image, {c, b, g}] apply contrast
c, brightness b and gamma g by a stated curve: v' = (v − 1/2)(1 + c) + 1/2
+ b, clipped to [0, 1], then raised to the power 1/g. Contrast pivots about
mid-grey so a contrast change does not also shift brightness; clipping precedes
gamma because a negative base has no real power; a non-positive gamma is rejected
(not a curve). This curve is Mathilda's documented choice, not a claim of
bit-compatibility with Mathematica. Both ranks are handled — a volume is loaded
with image3d_load and rebuilt with image3d_build_real.
Data structures. One flat unit-scale double buffer (image_load /
image3d_load) mutated in place and rebuilt as a "Real" image. No histogram and
no second buffer — the stretch is a single min/max pass then a linear map; the
parametric form is a single per-element pass.
Complexity / limits. O(n) where n = width · height · channels (× depth) —
one pass for min/max and one to apply the map. The parametric curve is applied
across all channels uniformly.
Attributes: Protected.
References¶
See also: ImageLevels
- Source:
src/imagefilter.c - Specification:
docs/spec/builtins/image-processing.md - Tests:
tests/test_image.c
Notes & additional examples¶
Notes¶
ImageAdjust[image] stretches to the full range: the darkest pixel becomes
exactly 0 and the brightest exactly 1. It is idempotent — a second stretch is
the identity — and a constant image has no range to stretch and comes back
unchanged, since dividing by zero is not the answer.
ImageAdjust[image, {c, b}] and [image, {c, b, g}] apply contrast c,
brightness b and gamma g by the stated curve v' = (v − 1/2)(1 + c) + 1/2 +
b, clipped to [0, 1], then raised to the power 1/g. Contrast pivots about
mid-grey so it does not also shift brightness; clipping precedes gamma because a
negative base has no real power. This curve is Mathilda's documented choice, not
a claim of bit-compatibility with Mathematica. It accepts volumes as well as
planes.