Directional Blurs

Lawdy. We've hardly messed with Cosine and Sine. Let's make corkscrews.

gmic -srand 78124 \
street.png \
'(1^0)' \
-resize[-1] [-2],[-2],[-1],[-1],1 \
5,5,1,2,'2*u-1' \
-resize[-1] [-3],[-3],[-1],[-1],5 \
-orientation[-1]
\
-eigen2tensor[-2,-1] \
-repeat 3 \
-smooth[-2] [-1],50 \
-done \
-rm[-1]
At first glance, it may not be clear how we went about setting EigenOne, EigenTwo, Cosine and Sine.
  1. For the eigenvalues, we started with a one pixel, two channel image with EigenOne set to constant one and EigenTwo set to constant zero. '(1^0)'. We resized this to the input dimensions but did not bother to split the channels out as they are set to the way we would like to have them and are ready for -eigen2tensor.
  2. We made a tiny 5-by-5, two channel image for Cosine and Sine, using the special math notation '2*u-1'. This sets the fifty or so pixels to random values between -1 and 1. 'u' itself is an interesting variable, taking on a different value in the range of [0 – 1] every time it is referenced. We then resized this 5-by-5 image up to the dimensions of the input image, using a high quality cubic interpolation (go look at Ramp Recipes, which is where we got this technique).

  3. Whenever we use random processes we like to set the random number generator to a specific seed. Later, we can produce the exact same result. That's what -srand 78124 does. See -plasma for details.
  4. -orientation ensures that each pixel in our Cosine and Sine image are scaled to unit vectors.
  5. As with EigenOne and EigenTwo, there is no need to split out Cosine and Sine gray scales. We left them as two channel images ready for -eigen2tensor.

In effect, we specified 25 random directions on a 5 by 5 grid. On resizing, we interpolated these samples over the much larger dimensions of the input image, using a cubic interpolation scheme. We got it by setting -resize’s interpolation parameter to '5'. This filled the Cosine and Sine images with smoothly varying values for each pixel, these nestled among the original 25 explicit samples. The curves manifested by -smooth are Bézier-like, which, like the interpolation used here, are grounded on cubic polynomials. Run this pipeline with other -resize interpolation schemes and see what happens!

Zeroing Out EigenOne and EigenTwo

gmic \
-srand 78124 \
street.png \
'(1^0)' \
-resize[-1] [-2],[-2],[-1],[-1],1 \
msk.png \
-normalize[-1] 0,1 \
-mul[-2,-1]
\
5,5,1,2,'2*u-1' \
-resize[-1] [-3],[-3],[-1],[-1],5 \
-orientation[-1] \
-eigen2tensor[-2,-1] \
-repeat 3 \
-smooth[-2] [-1],50 \
-done \
-rm[-1]
Despite utter chaos, some people Just Know How To Keep On Shopping.
We introduce a mask (msk.png) cut to the outline of the woman in the foreground. Nothing complicated. The woman is zero bits, everything else is 255. It sets a region in the EigenOne to zero. Concomitantly, EigenTwo is entirely zero, so the region occupied by our shopper has both EigenOne and EigenTwo set to zero, and she is not blurred at all.
So, generally, when both eigenvalues are zero blurring turns off.
And, more generally, we may vary the overall degree of blurring by varying EigenOne and EigenTwo. To the extent they differ, directional blurring take place.

Yea, yea, yea. We hear some of you out there telling us that all we had to do was select/float the woman off of a pristine copy and paste her onto the swirly stuff. Sure. No argument. Six of one. Half a dozen of the other. Either way, we're still cutting masks, and that's the time pig. Moreover, we're demonstrating principles here, and the principle here is: blurring is going to be no more than the larger of EigenOne or EigenTwo. Both zero. No Blur. That's the takeaway!

Selective Whirl Blur

Instead of total chaos, we can just have streamers of chaos.

gmic -srand 78924
street.png
'(1^0)'
-resize[-1] [-2],[-2],[-1],[-1],1
--luminance[0]
-apply_curve[-1] 0.4,0,0,63,64,127,255,191,64,255,0

-normalize[-1] 0,1
-mul[-2,-1]
5,5,1,2,'2*u-1'
-resize[-1] [-3],[-3],[-1],[-1],5
-orientation[-1]
-eigen2tensor[-2,-1]
-repeat 3
-smooth[-2] [-1],50
-done
-rm[-1]

The deal here is we took the luminance of the input image and then made a gray scale mask where the midtones map to white, but shadows and highlights map to black, the digerati's classic posterization curve. The tool for this is -apply_curve, G'MIC's equivalent to GIMP's curve tool. We used this mask as a multiplier against EigenOne. EigenTwo is constant zero, so we are dealing with skinny blurring ellipses throughout. This gives rise to the hairlike swirl.
The effect is something like a selective blur, with the highlights and shadows holding structure while midtones form swirling banners.

The Anisotropic Smoothing Bits

We've been referencing the anisotropic smoothing bits for pages and pages without introducing them to you. That's because we live in Brooklyn, Noo Yawk and Why Do You Wanna Know?

OK. Got the Attitude out of the way. Here are the Anisotropic Smoothing Bits:

  1. -structuretensors: Finds edges. Makes a tensor field encoding where they are along with their orientation. Its output is often called a “structure tensor field” because the field encodes these edges and their orientations. This makes up the first part of the anisotropic smoothing pipeline. We rarely call -structuretensors; -smooth or -diffusiontensors calls it on our behalf.
  2. -diffusiontensors: Calls -structuretensors internally to perform edge detection and orientation, then finds the blurring directions and magnitudes for -smooth. It encodes these in a “diffusion tensor field”.
  3. -smooth: The general purpose directional blurring tool. It's behavior is entirely dictated by the tensor map it is given. You can give it whatever you want. If you don't give it anything, anisotropic smoothing kicks in for the operand image. -smooth calls -diffusiontensors; -diffusiontensors calls -structuretensors and they pass tensor fields around. Eventually -smooth gets a diffusion tensor field which tells it what to do.

At the data container level, diffusion and structure tensor fields look exactly the same. The conversion from structure to diffusion tensor fields entail redirecting and rescaling vectors, but one tensor field could easily be swapped for the other and -smooth wouldn't care – it would just give you funny results and take a very long time to do it.

Along the way, there are two useful utilities.

  1. -eigen: Unpacks structure tensor fields into a set of vector magnitudes and directions – our EigenOne, EigenTwo, Cosine and Sine images. -eigen packages these as a pair of two-channel images; we often further unpack them into four grayscale images. These are simple -split and -append exercises.
  2. -eigen2tensor: The functional reverse of -eigen. Given a magnitude and direction pair of images, creates a tensor field. You can concoct your own magnitude and direction fields, as we have been doing throughout this Cookbook Recipe, then call -eigen2tensor as a last step before invoking -smooth.
Eigenvalues and Eigenvectors Tensors for the Tonsorially Challenged