Point operations are the simplest image processing transformations. An output pixel value depends on the input pixel value only. Examples of such transformations are tone adjustments for brightness and contrast, thresholding, color space transformation, image compositing, image arithmetic or logic, etc.

Mathematically, a point operator can be written as

h(x, y) = g(f(x, y))

or

h = g ∘ f

Various basic algorithms are suitable for point operations within nGI. The most common algorithm is transform() which comes in multiple incarnations: with one, two or three source views.

Writing Point Operations

There are many ways to write and use point functions in nGI. First, point functions can be written in the form of ordinary functions or ordinary template functions. Second, point functions can be written in the form of function objects (i.e. structures or classes that define operator ()). Third, point functions can be written in the form of lambda expressions. Finally, one of the many predefined point functors of nGI can be used.

The following examples are all taken from the point_operations sample.

Point Operations Using Functions

Ordinary C++ functions can be used as point operations with the transform() algorithm. Here is a very simple example of pixel negation:

// ordinary function usable for unary point operation
inline unsigned char negate_mono(unsigned char const& a)
{
    return ~a;
}

and here is the code fragment that you would need to actually perform the operation:

transform(source.view(), destination.view(), negate_mono);

Of course, you can also use binary or ternary functions with the respective versions of the transform algorithm to implement point operations between two or more images.

In addition, you can use template functions to make them more generic and independent of the respective data type.

// template function usable for unary point operation
template<typename T>
inline T negate(T const& a)
{
    return ~a;
}

When you use a template function, you must specify the type to the point function:

transform(source.get_view(), destination.get_view(), negate<ungsigned char>);

Point Operations Using Function Objects

C++ function objects provide more power than ordinary functions, and can be used equally well as point operations with the transform algorithm. Here is the same simple example of pixel negation as above, but written with a function object:

// function object usable for unary point operations
struct negate {
  unsigned char operator()(unsigned char const& a) {
    return ~a;
  }
};

...

transform(source.get_view(), destination.get_view(), negate());

Of course, you can also use binary or ternary function objects with the respective versions of the transform algorithm to implement point operations between two or more images.

In addition, you can use template function objects to make them more generic and independent of the respective data type.

Point Operations Using Lambda Functions

Lambda function objects provide even more convenience, since they avoid the manual creation of functions or function objects altogether. Instead, special lambda expression syntax is used to write the function on-the-fly. Here is the same simple example of pixel negation as above, but written with a lambda expression:

transform(source.get_view(),destination.get_view(), ~_1);

Of course, you can also use binary or ternary lambda expressions with the respective versions of the transform algorithm to implement point operations between two or more images.

Classification of Point Operations

There are various ways to classify point operations. One way to classify them is by the number of arguments. Unary point operations take one argument, binary point operations take two arguments and tertiary point arguments take three arguments. Another way is to separate them into arithmetic, logical, and other operations. The table shows this classification for the operations implemented within nGI.

arithmetic logic other
unary negate, absolute, increment, decrement not color space transformation
binary add, difference, subtract and, or, xor maximum, minimum, compare_bigger, compare_bigger_or_equal, compare_equal, compare_smaller, compare_smaller_or_equal, compositing,
tertiary compositing

The code samples in this chapter use the following input images:

The first source image, a printed circuit board.

The second source image, a color gradient
image.

The third source image, another color gradient
image.

absolute

Absolute takes the absolute value of its arguments.

f(x) = |x|

It is defined for signed types only. You can use the function absolute() or the function object pt_absolute_1, but note that the former may be optimized for performance.

absolute(source.get_view(), destination.get_view());

add

Add adds its arguments. The version implemented here avoids overflow and saturates (i.e. for 8 bits: sum > 255 will be saturated to 255).

$$f(x,y)= \{ \begin{matrix} x+y & \mid & x+y \< max \\ max & \mid & x+y \geqslant max \end{matrix}$$

You can use the function add() or the function object pt_add_2, but note that the former may be optimized for performance.

add(source1.get_view(), source2.get_view(), destination.get_view());

Sum of the printed circuit board image and the color gradient
image.

The function add() is overloaded and allows you to add a constant value (y is constant) to a view.

add(source.get_view(), 100, destination.get_view());

Sum of the printed circuit board image and the constant
100.

and

And logically bitwise ANDs its arguments.

f(x, y) = x ∧ y

You can use the function and() or the function object pt_and_2, but note that the former may be optimized for performance.

and(source1.get_view(), source2.get_view(), destination.get_view());

Bitwise logical and of the printed circuit board image and the color
gradient image.

The function and() is overloaded and allows you to AND another image view or a constant value (y is constant) with a view.

and(source.get_view(), 100, destination.get_view());

Bitwise logical and of the printed circuit board image and the
constant 100.

blend

Blend blends its arguments.

f(x, y, α) = x ⋅ (1 − α) + y ⋅ α

You can use the function blend() or the function object pt_blend_3, but note that the former may be optimized for performance.

blend(source1.get_view(), source2.get_view(), alpha.get_view(), destination.get_view());

Blend the printed circuit board image and the color gradient image
using the second color gradient image as the blend
factor.

The function blend() is overloaded and allows you to blend a view with a constant value (y is constant).

blend(source.get_view(), 100, alpha.get_view(), destination.get_view());

Blend the printed circuit board image and the constant 100 using the
second color gradient image as the blend
factor.

Another overload of blend() allows you to blend two views with a constant blend factor (α is constant).

blend_constant(source.get_view(), source2.get_view(), 100, destination.get_view());

Blend the printed circuit board image and the color gradient image
using a constant blend factor of 100.

Another overload of blend() allows you to blend a view and a constant with a constant blend factor (x and α are constant).

blend_constant(source.get_view(), 100, 100, destination.get_view());

Blend the printed circuit board image and the constant 100 using the
second color gradient image as the blend
factor.

compare_bigger

Compare bigger compares its arguments.

$$f(x)= \{ \begin{matrix} max & \mid & x > y\\ min & \mid & x \leqslant y \end{matrix}$$

You can use the function compare_bigger() or the function object pt_compare_bigger_2, but note that the former may be optimized for performance.

compare_bigger(source1.get_view(), source2.get_view(), destination.get_view());

Comparison (bigger) of the printed circuit board image and the color
gradient image.

The function compare_bigger() is overloaded and allows you to compare a constant value (y is constant) and a view.

compare_bigger(source.get_view(), 100, destination.get_view());

Comparison (bigger) of the printed circuit board image and the
constant 100.

compare_bigger_or_equal

Compare bigger_or_equal compares its arguments.

$$f(x)= \{ \begin{matrix} max & \mid & x \geqslant y\\ min & \mid & x \< y \end{matrix}$$

You can use the function compare_bigger_or_equal() or the function object pt_compare_bigger_or_equal_2, but note that the former may be optimized for performance.

compare_bigger_or_equal(source1.get_view(), source2.get_view(), destination.get_view());

Comparison (bigger or eqal) of the printed circuit board image and the
color gradient image.

The function compare_bigger_or_equal() is overloaded and allows you to compare a constant value (y is constant) and a view.

compare_bigger_or_eqal(source.get_view(), 100, destination.get_view());

Comparison (bigger or equal) of the printed circuit board image and
the constant 100.

compare_equal

Compare equal compares its arguments.

$$f(x)= \{ \begin{matrix} max & \mid & x = y\\ min & \mid & x \neq y \end{matrix}$$

You can use the function compare_equal() or the function object pt_compare_equal_2, but note that the former may be optimized for performance.

compare_equal(source1.get_view(), source2.get_view(), destination.get_view());

Comparison (eqal) of the printed circuit board image and the color
gradient image.

The function compare_equal() is overloaded and allows you to compare a constant value (y is constant) and a view.

compare_eqal(source.get_view(), 100, destination.get_view());

Comparison (equal) of the printed circuit board image and the constant
100.

compare_smaller

Compare smaller compares its arguments.

$$f(x)= \{ \begin{matrix} max & \mid & x \< y\\ min & \mid & x \geqslant y \end{matrix}$$

You can use the function compare_smaller() or the function object pt_compare_smaller_2, but note that the former may be optimized for performance.

compare_smaller(source1.get_view(), source2.get_view(), destination.get_view());

Comparison (smaller) of the printed circuit board image and the color
gradient image.

The function compare_smaller() is overloaded and allows you to compare a constant value (y is constant) and a view.

compare_eqal(source.get_view(), 100, destination.get_view());

Comparison (smaller) of the printed circuit board image and the
constant 100.

compare_smaller_or_equal

Compare smaller_or_equal compares its arguments.

$$f(x)= \{ \begin{matrix} max & \mid & x \leqslant y\\ min & \mid & x > y \end{matrix}$$

You can use the function compare_smaller_or_equal() or the function object pt_compare_smaller_or_equal_2, but note that the former may be optimized for performance.

compare_smaller_or_equal(source1.get_view(), source2.get_view(), destination.get_view());

Comparison (smaller or eqal) of the printed circuit board image and
the color gradient image.

The function compare_smaller_or_equal() is overloaded and allows you to compare a constant value (y is constant) and a view.

compare_eqal(source.get_view(), 100, destination.get_view());

Comparison (smaller or eqal) of the printed circuit board image and
the constant 100.

compositing

Compositing is about composing a new image from two or more other images. The foundations of that have been laid down by Thomas Porter and Tom Duff in 1984 while working at Lucasfilm Ltd. Despite the age it is still worthwhile to read their original paper (http://keithp.com/~keithp/porterduff/p253-porter.pdf).

The following chapters show the results of compositing operations defined by the original Porter/Duff paper, as well as the code used to create them.

src

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_src_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black background.

dst

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_dst_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black background.

src-over

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_src_over_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

src-in

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_src_in_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

src-out

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_src_out_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

src-atop

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_src_atop_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

dst-over

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_dst_over_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

dst-in

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_dst_in_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

dst-out

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_dst_out_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

dst-atop

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_dst_atop_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

xor

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_xor_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black background.

plus

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_plus_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black background.

multiply

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_multiply_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

screen

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_screen_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

overlay

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_overlay_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

darken

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_darken_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

lighten

transform(rgba1.get_view(), rgba2.get_view(), rgba.get_view(), 
    pt_composite_lighten_2<color_rgba<unsigned char>, color_rgba<unsigned char> >());

The result on white background.

The resulting on black
background.

decrement

Decrement decreases its argument by one. The version implemented avoids underflow and saturates (i.e. for 8 bits: 0 decremented will be saturated to 0).

$$f(x)= \{ \begin{matrix} x-1 & \mid & x > min\\ x & \mid & x = min \end{matrix}$$

You can use the function decrement() or the function object pt_decrement_1, but note that the former may be optimized for performance.

decrement(source.get_view(), destination.get_view());

The resulting image.

difference

Difference calculates the absolute difference between its arguments.

f(x, y) = |x − y|

You can use the function difference() or the function object pt_difference_2, but note that the former may be optimized for performance.

difference(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function difference() is overloaded and allows you to add a constant value (y is constant) to a view.

difference(source.get_view(), 100, destination.get_view());

The resulting image.

divide

Divide divides its arguments. The version implemented here avoids overflow by scaling.

$$f(x,y)= \frac{x}{y} \cdot max$$

For performance reasons we use a version of this formula, where max is not the maximum value usable for the type, but one more, so that the multiplication can be implemented with a shift operation.

You can use the function divide() or the function object pt_divide_2, but note that the former may be optimized for performance.

divide(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function divide() is overloaded and allows you to divide a view with a constant value (y is constant).

divide(source.get_view(), 100, destination.get_view());

The resulting image.

Another overload of divide() allows to divide a constant value (x is constant) with a view.

divide(source.get_view(), 100, destination.get_view());

The resulting image.

increment

Increment increases its argument by one. The version implemented here avoids overflow and saturates (i.e. for 8 bits: 255 incremented will be saturated to 255).

$$f(x)= \{ \begin{matrix} x+1 & \mid & x \< max\\ x & \mid & x = max \end{matrix}$$

You can use the function increment() or the function object pt_increment_1, but note that the former may be optimized for performance.

increment(source.get_view(), destination.get_view());

The resulting image.

maximum

Maximum calculates the maximum of its arguments.

$$f(x)= \{ \begin{matrix} x & \mid & x > y\\ y & \mid & x \leqslant y \end{matrix}$$

You can use the function maximum() or the function object pt_maximum_2, but note that the former may be optimized for performance.

maximum(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function maximum() is overloaded and allows you to calculate the maximum of a constant value (y is constant) and a view.

maximum(source.get_view(), 100, destination.get_view());

The resulting image.

minimum

Minimum calculates the minimum of its arguments.

$$f(x)= \{ \begin{matrix} x & \mid & x \< y\\ y & \mid & x \geqslant y \end{matrix}$$

You can use the function minimum() or the function object pt_minimum_2, but note that the former may be optimized for performance.

minimum(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function minimum() is overloaded and allows you to calculate the maximum of a constant value (y is constant) and a view.

minimum(source.get_view(), 100, destination.get_view());

The resulting image.

multiply

Multiply multiplies its arguments. The version implemented here avoids overflow by scaling.

$$f(x,y)= x \cdot \frac{y}{max}$$

For performance reasons we use a version of this formula, where max is not the maximum value usable for the type, but one more, so that the division can be implemented with a shift operation.

You can use the function multiply() or the function object pt_multiply_2, but note that the former may be optimized for performance.

multiply(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function multiply() is overloaded and allows you to multiply a constant value (y is constant) to a view.

multiply(source.get_view(), 100, destination.get_view());

The resulting image.

negate

Negate negates its argument using two’s complement.

f(x) =  − x

It is defined for signed types only. You can use the function negate() or the function object pt_negate_1, but note that the former may be optimized for performance.

negate(source.get_view(), destination.get_view());

The resulting image.

If you are working with an unsigned type, not is probably what you are looking for.

not

Not negates its argument using one’s complement. Not performs a bit-wise negation of all the bits of its argument.

f(x) = ¬x

This can be used to create an inverse of an image. Dark will be turned to bright and vice versa, just like a photographic negative.

It is defined for integral types only. It has no meaning with floating point types. You can use the function not() or the function object pt_not_1, but note that the former may be optimized for performance.

not(source.get_view(), destination.get_view());

The resulting image.

If you are working with a floating point type, negate is probably what you are looking for.

or

Or logically bitwise ORs its arguments.

f(x, y) = x ∨ y

You can use the function or() or the function object pt_or_2, but note that the former may be optimized for performance.

or(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function or() is overloaded and allows you to OR an image view or a constant value (y is constant) with a view.

or(source.get_view(), 100, destination.get_view());

The resulting image.

subtract

Subtract subtracts its arguments. The version implemented here avoids overflow and saturates (i.e. for 8 bits: difference \< 0 will be saturated to 0).

$$f(x,y)= \{ \begin{matrix} x-y & \mid & x-y > min\\ max & \mid & x-y leqslant min \end{matrix}$$

You can use the function subtract() or the function object pt_subtract_2, but note that the former may be optimized for performance.

subtract(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function subtract() is overloaded and allows you to subtract a constant value (y is constant) from a view.

subtract(source.get_view(), 100, destination.get_view());

The resulting image.

Another overload of subtract() allows to subtract a view from a constant value (x is constant).

subtract(source.get_view(), 100, destination.get_view());

The resulting image.

xor

Xor logically bitwise XORs its arguments.

$$f(x, y)=x \underbar{\vee} y$$

You can use the function xor() or the function object pt_xor_2, but note that the former may be optimized for performance.

xor(source1.get_view(), source2.get_view(), destination.get_view());

The resulting image.

The function xor() is overloaded and allows you to XOR an image view or a constant value (y is constant) with a view.

xor(source.get_view(), 100, destination.get_view());

The resulting image.