-shared

Figure 1: Extremely identical twins.

Shared places "clones" of its selected images on the image stack. These clones, more formally called shared images, behave in almost every significant way as copies of their reference image except in one significant respect: they share the image buffer of their reference image instead of having their own copy. Any operation invoked on one image is reflected in the other. The shared copy is more like an alias of its reference image than a copy.

One can, using various addessing schemes, limit the shared image buffer region, however, in each scheme, the shared region must form a contiguous section of the image buffer. Thus, one cannot designate an arbitrarily shaped rectangle as shared, because the rectangle cleaves image rows into left, center and right portions and the left and right excluded portions intersperse with included ones, a noncontiguous state of affairs.

When establishing a shared image, one designates starting and ending points in the image buffer relative to some reference point, using a particular unit measure. Each variant of the command employs a different unit measure of increasingly larger size. The command 'knows' the pertinent unit measure from counting the number of its arguments, five implying pixel resolution and none implying the entire image.

Here are the five variants:

  1. pixel resoltion: x0[%],x1[%],y[%],z[%],v[%] - y, z, and v designate the particular image row, slice, and channel location to start counting pixels. x0 designates a pixel count from the beginning of row y; x1 indicates the location of the last pixel in the buffer. x0 - x1 + 1 is the size of the buffer in pixels.
  2. row resolution: y0[%],y1[%],z[%],v[%] - z and v designate the particular slice and channel location to start counting rows. y0 designates a row count from the beginning of the slice z; y1 indicates the location of the last row in the buffer. y1 - y0 + 1 is the size of the buffer in rows.
  3. slice resolution: z0[%],z1[%],v - v designates the particular channel location to start counting slices. z0 designates a slice count from the beginning of channel v. z1 indicates the location of the last slice in the buffer. z1 - z0 + 1 is the size of the buffer in slices.
  4. channel resolution: v0[%],v1[%] - v0 designates a channel count from the beginning of the image buffer. v1 indicates the location of the last channel in the buffer. v1 - v0 + 1 is the size of the buffer in channels.
  5. image resolution: (no args) Selects the entire image buffer.

Range arguments are inclusive, so the pair x,x is legitmate and indicates that the buffer begins at x (so many pixels, rows, slices or channels from the reference point) and consists of one unit (x - x + 1), nor are there limits on range arguments, which probably will get you into trouble. Thus '-shared[-1] 0,{w*h*d*s-1},0,0,0' is a rather roundabout way of sharing out the entire buffer of the preceding image on the stack; '-shared[-1]' without arguments achieves the same end and will more likely preserve your sanity.

It is probably clear to the reader that this command operates at a very low level, bypassing most of the safe guards and checks that normally operate on command line input. The -shared command offers an extremely efficient, low-overhead access mechanism to image data but furnishes few safety nets, should one miscalculate buffer sizes or delete reference images. G'MIC sanity-checks ranges for buffer over runs, but deleting referenced images without doing the same on outstanding shares will probably trigger an operating system memory access violation, terminating G'MIC without giving it a chance to issue any illuminating mea culpas (even though it probably is your fault). That said, one shouldn't avoid the command for its fearfulness, never benefiting from its usefulness. If quantities stem from established image constants and the math is correct, the -shared command furnishes the quickest means of accessing image data.

Example

#@gimp S-equalizer : gimp_equalize_s, gimp_equalize_s
#@gimp : Contrast = float(1,0,2)
#@gimp : Brightness = float(0,-1,1)
gimp_equalize_s :
-repeat $!
-local[$>]
-split_opacity
-local[0]
-to_rgb
-rgb2hsv
-shared 1,1 #selects 2nd channel
-mul[-1] $1
-add[-1] $2
-cut[-1] 0,1
-remove[-1]
-hsv2rgb
-endl
-append c
-endl
-done

David Tschumperlé furnishes an example via GimpChat, of a filter (left) which sets the saturation and brightness of an image.

The 'colorfulness' of an image stems from the difference between the largest and smallest saturation value, while its brightness stems from the magnitude of the average saturation value. This filter computes the HSV colorspace values and places the image in that mode, with channel zero containing bue, channel one saturation and channel two containing intensity values. The filter manipulates the saturation channel through a shared image, which seems in its organization to be a single channel grayscale residing on the image stack after the inital image. From an administrative standpoint, there are two images on the stack, but only one HSV image buffer, with its saturation channel shared by the next image on the stack. 

Notice that the clean-up step '-rm[-1]' is harmless; no memory violations take place so long as the deletion of shares take place before the deletion of reference images. Had this line been mistakenly written as '-rm[0]', however, then the share is left with dangling memory references to a no longer existing image buffer; a memory access violation invariably occurs forthwith.

Garry Osgood