LEL Masks

Image Masks, Lattice Condition Masks, and Mask Handling.

--CASA Developer--

Access to Image Masks

A boolean mask associated with an image indicates whether a pixel is good (mask value True) or bad (mask value False). If the mask value is bad, then the image pixel is not used for computation (e.g. when finding the mean of the image).


An image can have zero (all pixels are good) or more masks. One mask can be designated as the default mask. By default it will be applied to the image (from Python, designation of the default mask is handled by the ia.maskhandler method of the Image tool).


When using LEL, the basic behaviour is that the default mask is used. However, by qualifying the image name with a suffix string, it is possible to specify that no mask or another mask should be used. The suffix is a colon followed by the word nomask or the name of the alternative mask.

  myimage.data
  myimage.data:nomask
  'myimage.data:othermask'

 
The first example uses the default mask (if the image has one). The second example uses no mask (thus all pixels are designated good) and the third example uses mask othermask.

Note that if the image name is enclosed in quotes, the mask name should be enclosed too. It means that a colon cannot be part of an image name.

 It is also possible to use a mask from another image like

  myimage.data:nomask[myotherimage::othermask]

This syntax is explained in the section describing regions.

Lattice Condition Mask

We have seen in the previous section that lattices (in this case images) can have an associated mask. These masks are stored with the image - they are persistent.

It is also possible to create transient masks when a LEL expression is executed (dawn, usually). This is done with the operator [] and a boolean expression. For example,

  sum( lat1[lat1<5 && lat1>10] )

creates a mask for lat1 indicating that only its elements fulfilling the boolean condition should be taken into account in the sum function. Note that the mask is local to that part of the expression. So in the expression

  sum( lat1[lat1<5 && lat1>10] ) + sum(lat1)

the second sum function takes all elements into account. Masking can also be applied to more complex expressions and it is recursive.

  (lat1+lat2)[lat3<lat4]
  sum( lat1[lat1<5][lat1>10] )
  (lat1 + lat2[lat3<lat4]) [lat1<5]

The first example applies the mask generated by the [] operator to the expression lat1+lat2. The second example shows the recursion (which ANDs the masks). It is effectively a (slower) implementation of the first example in this subsection. In the last example, the expression inside the parentheses is only evaluated where the condition [lat1<5] is true and the resulting expression has a mask associated with it.

Please note that it is possible to select pixels on an axis by means of the function INDEXIN (or by the INDEXi IN expression) as shown in the previous section about miscellaneous functions.

Mask Handling

As explained in the previous subsections, lattices can have a mask. Examples are a mask of good pixels in an image, a mask created using a boolean condition and the operator [], or a mask defining a region within its bounding box.
A pixel is bad when the image has a mask and when the mask value for that pixel is False. Functions like max ignore the bad pixels.
Note that in a MeasurementSet a False mask value indicates a good visibility. Alas this is a historically grown distinction in radio-astronomy.
Image masks are combined and propagated throughout an expression. E.g. when two lattices are added, the mask of the result is formed by and-ing the masks of the two lattices. That is, the resultant mask is True where the mask of lattice one is true AND the mask of lattice 2 is True. Otherwise, the resultant mask is False.
In general the mask of a subexpression is formed by and-ing the masks of the operands. This is true for e.g. +, *, atan2, etc.. However, there are a few special cases:

  • The mask created by operator[condition] is formed by and-ing the condition result, the mask of the result, and the mask of the subexpression where the condition is applied to. For example, suppose lat1 and lat2 each have a mask. Then in
      sum( lat1[lat2<5] )
    
    the sum function will only sum those elements for which the mask of lat1 and lat2 is valid and for which the condition is true. 
  • The logical AND operator forms the resultant mask by looking at the result and the masks of the operands. 
      lat1[lat1<0 && lat2>0]
    
    Let us say both lat1 and lat2 have masks. The operand lat1<0 is true if the mask of lat1 is true and the operand evaluates to true, otherwise it is false. Apply the same rule to the operand lat2 > 0. The AND operator gives true if the left and right operands are both true. If the left operand is false, the right operand is no longer relevant. It is, in fact, 3-valued logic with the values true, false, and undefined. 

    Thus, the full expression generates a lattice with a mask. The mask is true when the condition in the [] operator is true, and false otherwise. The values of the output lattice are only defined where its mask is true. 

  • The logical OR operator works the same as the AND operator. If an operand has a true value the other operand can be ignored.
  • The mask of the result of the replace function is a copy of the mask of its first operand. The mask of the second operand is not used at all.
  • The iif function has three operands. Depending on the condition, an element from the second or third operand has to be taken. The resultant mask is formed by the mask of the condition and-ed with the appropriate elements from the masks of the second or third operand.
  • The value function returns the value without a mask, thus it removes the mask from a value. It has the same effect as the image:nomask construction discussed above. However, the value function is more general, because it can also be applied to a subexpression.
  • The mask function returns the mask of a value. The returned value is a boolean lattice and has no mask itself. When the value has no mask, it returns a mask consisting of all True values. When applied to an image, it returns its default mask.

Consider the following more or less equivalent examples:

   value(image1)[mask(image2)]
   image1:nomask[mask(image2)]
   image1:nomask[image2::mask0]

The first two use the default mask of image2 as the mask for image1.
The latter uses mask0 of image2 as the mask for image1. It is equivalent to the first two examples if mask0 is the default mask of image2.
It is possible that the entire mask of a subexpression is false. For example, if the mean of such a subexpression is taken, the result is undefined. This is fully supported by LEL, because a scalar value also has a mask associated with it. One can see a masked-off scalar as a lattice with an all false mask. Hence an operation involving an undefined scalar results in an undefined scalar. The following functions act as described below on fully masked-off lattices:

  • MEDIAN, MEAN, VARIANCE, STDDEV, AVDEV, MIN, MAX
    result in an undefined scalar:
  • NELEMENTS, NTRUE, NFALSE, SUM
    result in a scalar with value 0.
  • ANY
    results in a scalar with value F.
  • ALL
    results in a scalar with value T.
  • LENGTH, NDIM
    ignore the mask because only the shape of the lattice matters.

You should also be aware that if you remove a mask from an image, the values of the image that were previously masked bad may have values that are meaningless.

Mask Storage

In many of the expressions we have looked at in the examples, a mask has been generated. What happens to this mask and indeed the values of the expression depends upon the implementation. If for example, the function you are invoking with LEL writes out the result, then both the mask and result will be stored. On the other hand, it is possible to just use LEL expressions but never write out the results to disk. In this case, no data or mask is written to disk. You can read more about this in the interface section.