Recall from Functions [qui] that a function is an object that maps a tuple of arguments to a return value, or throws an exception if no appropriate value can be returned. It is common for the same conceptual function or operation to be implemented quite differently for different types of arguments: adding two integers is very different from adding two floating-point numbers, both of which are distinct from adding an integer to a floating-point number. Despite their implementation differences, these operations all fall under the general concept of “addition”. Accordingly, in Julia, these behaviors all belong to a single object: the + function.

To facilitate using many different implementations of the same concept smoothly, functions need not be defined all at once, but can rather be defined piecewise by providing specific behaviors for certain combinations of argument types and counts. A definition of one possible behavior for a function is called a method. Thus far, we have presented only examples of functions defined with a single method, applicable to all types of arguments. However, the signatures of method definitions can be annotated to indicate the types of arguments in addition to their number, and more than a single method definition may be provided. When a function is applied to a particular tuple of arguments, the most specific method applicable to those arguments is applied. Thus, the overall behavior of a function is a patchwork of the behaviors of its various method definitions. If the patchwork is well designed, even though the implementations of the methods may be quite different, the outward behavior of the function will appear seamless and consistent.

The choice of which method to execute when a function is applied is called dispatch. Julia allows the dispatch process to choose which of a function’s methods to call based on the number of arguments given, and on the types of all of the function’s arguments. This is different than traditional object-oriented languages, where dispatch occurs based only on the first argument, which often has a special argument syntax, and is sometimes implied rather than explicitly written as an argument. [vedi nota 1] Using all of a function’s arguments to choose which method should be invoked, rather than just the first, is known as multiple dispatch. Multiple dispatch is particularly useful for mathematical code, where it makes little sense to artificially deem the operations to “belong” to one argument more than any of the others: does the addition operation in x + y belong to x any more than it does to y? The implementation of a mathematical operator generally depends on the types of all of its arguments. Even beyond mathematical operations, however, multiple dispatch ends up being a powerful and convenient paradigm for structuring and organizing programs.

[1] In C++ or Java, for example, in a method call like obj.meth(arg1,arg2), the object obj “receives” the method call and is implicitly passed to the method via the this keyword, rather than as an explicit method argument. When the current this object is the receiver of a method call, it can be omitted altogether, writing just meth(arg1,arg2), with this implied as the receiving object.

Definire metodi Until now, we have, in our examples, defined only functions with a single method having unconstrained argument types. Such functions behave just like they would in traditional dynamically typed languages. Nevertheless, we have used multiple dispatch and methods almost continually without being aware of it: all of Julia’s standard functions and operators, like the aforementioned + function, have many methods defining their behavior over various possible combinations of argument type and count.

When defining a function, one can optionally constrain the types of parameters it is applicable to, using the :: type-assertion operator, introduced in the section on Composite Types [qui]:

This function definition applies only to calls where x and y are both values of type Float64:

Applying it to any other types of arguments will result in a MethodError:

Nota lollosa: dillo a JavaScript 😜

As you can see, the arguments must be precisely of type Float64. Other numeric types, such as integers or 32-bit floating-point values, are not automatically converted to 64-bit floating-point, nor are strings parsed as numbers. Because Float64 is a concrete type and concrete types cannot be subclassed in Julia, such a definition can only be applied to arguments that are exactly of type Float64. It may often be useful, however, to write more general methods where the declared parameter types are abstract:

This method definition applies to any pair of arguments that are instances of Number. They need not be of the same type, so long as they are each numeric values. The problem of handling disparate numeric types is delegated to the arithmetic operations in the expression 2x - y.

To define a function with multiple methods, one simply defines the function multiple times, with different numbers and types of arguments. The first method definition for a function creates the function object, and subsequent method definitions add new methods to the existing function object. The most specific method definition matching the number and types of the arguments will be executed when the function is applied. Thus, the two method definitions above, taken together, define the behavior for f over all pairs of instances of the abstract type Number – but with a different behavior specific to pairs of Float64 values. If one of the arguments is a 64-bit float but the other one is not, then the f(Float64,Float64) method cannot be called and the more general f(Number,Number) method must be used:

The 2x + y definition is only used in the first case, while the 2x - y definition is used in the others. No automatic casting or conversion of function arguments is ever performed: all conversion in Julia is non-magical and completely explicit. Conversion and Promotion [prossimamente], however, shows how clever application of sufficiently advanced technology can be indistinguishable from magic. [Clarke, 61].

For non-numeric values, and for fewer or more than two arguments, the function f remains undefined, and applying it will still result in a MethodError:

You can easily see which methods exist for a function by entering the function object itself in an interactive session:

This output tells us that f is a function object with two methods. To find out what the signatures of those methods are, use the methods() function:

which shows that f has two methods, one taking two Float64 arguments and one taking arguments of type Number. It also indicates the file and line number where the methods were defined: because these methods were defined at the REPL, we get the apparent line number none:1. No, la rev. attuale è più specifica.

In the absence of a type declaration with ::, the type of a method parameter is Any by default, meaning that it is unconstrained since all values in Julia are instances of the abstract type Any. Thus, we can define a catch-all method for f like so:

This catch-all is less specific than any other possible method definition for a pair of parameter values, so it will only be called on pairs of arguments to which no other method definition applies.

Although it seems a simple concept, multiple dispatch on the types of values is perhaps the single most powerful and central feature of the Julia language. Core operations typically have dozens of methods:

Segmentazione ed etichettatura Segmentation is the process of separating objects of interest from the background. The most simple approach is probably intensity thresholding, which is easily done with numpy functions:

The result is a binary image, in which the individual objects still need to be identified and labeled. The function label generates an array where each object is assigned a unique number:

The label function generates an array where the objects in the input are labeled with an integer index. It returns a tuple consisting of the array of object labels and the number of objects found, unless the output parameter is given, in which case only the number of objects is returned. The connectivity of the objects is defined by a structuring element. For instance, in two dimensions using a four-connected structuring element gives:

These two objects are not connected because there is no way in which we can place the structuring element such that it overlaps with both objects. However, an 8-connected structuring element results in only a single object:

If no structuring element is provided, one is generated by calling generate_binary_structure (see Binary morphology [qui]) using a connectivity of one (which in 2D is the 4-connected structure of the first example). The input can be of any type, any value not equal to zero is taken to be part of an object. This is useful if you need to ‘re-label’ an array of object indices, for instance after removing unwanted objects. Just apply the label function again to the index array. For instance:

Note: The structuring element used by label is assumed to be symmetric.

There is a large number of other approaches for segmentation, for instance from an estimation of the borders of the objects that can be obtained for instance by derivative filters. One such an approach is watershed segmentation. The function watershed_ift generates an array where each object is assigned a unique label, from an array that localizes the object borders, generated for instance by a gradient magnitude filter. It uses an array containing initial markers for the objects:

The inputs of this function are the array to which the transform is applied, and an array of markers that designate the objects by a unique label, where any non-zero value is a marker. For instance:

Here two markers were used to designate an object (marker = 2) and the background (marker = 1). The order in which these are processed is arbitrary: moving the marker for the background to the lower right corner of the array yields a different result:

The result is that the object (marker = 2) is smaller because the second marker was processed earlier. This may not be the desired effect if the first marker was supposed to designate a background object. Therefore watershed_ift treats markers with a negative value explicitly as background markers and processes them after the normal markers. For instance, replacing the first marker by a negative marker gives a result similar to the first example:

The connectivity of the objects is defined by a structuring element. If no structuring element is provided, one is generated by calling generate_binary_structure (see Binary morphology [stesso link]) using a connectivity of one (which in 2D is a 4-connected structure.) For example, using an 8-connected structure with the last example yields a different object:

Note: The implementation of watershed_ift limits the data types of the input to UInt8 and UInt16.

Tipi valore In Julia, you can’t dispatch on a value such as true or false. However, you can dispatch on parametric types, and Julia allows you to include “plain bits” values (Types, Symbols, Integers, floating-point numbers, tuples, etc.) as type parameters. A common example is the dimensionality parameter in Array{T,N}, where T is a type (e.g., Float64) but N is just an Int.

You can create your own custom types that take values as parameters, and use them to control dispatch of custom types. By way of illustration of this idea, let’s introduce a parametric type, Val{T}, which serves as a customary way to exploit this technique for cases where you don’t need a more elaborate hierarchy.

There is no more to the implementation of Val than this. Some functions in Julia’s standard library accept Val types as arguments, and you can also use it to write your own functions. For example:

For consistency across Julia, the call site should always pass a Val type rather than creating an instance, i.e., use foo(Val{:bar}) rather than foo(Val{:bar}()).

It’s worth noting that it’s extremely easy to mis-use parametric “value” types, including Val; in unfavorable cases, you can easily end up making the performance of your code much worse. In particular, you would never want to write actual code as illustrated above. For more information about the proper (and improper) uses of Val, please read the more extensive discussion in the performance tips [prossimamente].

TipiNullablerappresentanti valori mancanti In many settings, you need to interact with a value of type T that may or may not exist. To handle these settings, Julia provides a parametric type called Nullable{T}, which can be thought of as a specialized container type that can contain either zero or one values. Nullable{T} provides a minimal interface designed to ensure that interactions with missing values are safe. At present, the interface consists of several possible interactions:

Construct a Nullable object.

Check if a Nullable object has a missing value.

Access the value of a Nullable object with a guarantee that a NullException will be thrown if the object’s value is missing.

Access the value of a Nullable object with a guarantee that a default value of type T will be returned if the object’s value is missing.

Perform an operation on the value (if it exists) of a Nullable object, getting a Nullable result. The result will be missing if the original value was missing.

Performing a test on the value (if it exists) of a Nullable object, getting a result that is missing if either the Nullable itself was missing, or the test failed.

Perform general operations on single Nullable objects, propagating the missing data.

Costruire oggettiNullable To construct an object representing a missing value of type T, use the Nullable{T}() function:

To construct an object representing a non-missing value of type T, use the Nullable(x::T) function:

Note the core distinction between these two ways of constructing a Nullable object: in one style, you provide a type, T, as a function parameter; in the other style, you provide a single value of type T as an argument.

Verificare se un oggettoNullableha un valore You can check if a Nullable object has any value using isnull():

L’esempio della guida mi da errore, chissà se è la versione.

accedere senza rischi al valore di un oggettoNullable You can safely access the value of a Nullable object using get():

If the value is not present, as it would be for Nullable{Float64}, a NullException error will be thrown. The error-throwing nature of the get() function ensures that any attempt to access a missing value immediately fails.

In cases for which a reasonable default value exists that could be used when a Nullable object’s value turns out to be missing, you can provide this default value as a second argument to get():

Tip: Make sure the type of the default value passed to get() and that of the Nullable object match to avoid type instability, which could hurt performance. Use convert() manually if needed.

Eseguire operazioni con oggettiNullableNullable objects represent values that are possibly missing, and it is possible to write all code using these objects by first testing to see if the value is missing with isnull(), and then doing an appropriate action. However, there are some common use cases where the code could be more concise or clear by using a higher-order function.

The map function takes as arguments a function f and a Nullable value x. It produces a Nullable:

If x is a missing value, then it produces a missing value;

If x has a value, then it produces a Nullable containing f(get(x)) as value.

This is useful for performing simple operations on values that might be missing if the desired behaviour is to simply propagate the missing values forward.

The filter function takes as arguments a predicate function p (that is, a function returning a boolean) and a Nullable value x. It produces a Nullable value:

If x is a missing value, then it produces a missing value;

If p(get(x)) is true, then it produces the original value x;

If p(get(x)) is false, then it produces a missing value.

In this way, filter can be thought of as selecting only allowable values, and converting non-allowable values to missing values.

While map and filter are useful in specific cases, by far the most useful higher-order function is broadcast, which can handle a wide variety of cases, including making existing operations work and propagate Nullables. An example will motivate the need for broadcast. Suppose we have a function that computes the greater of two real roots of a quadratic equation, using the quadratic formula:

Unicode per √ 8730 o 0x221a ma io userei sqrt.

We may verify that the result

as we expect, since 5.0 is the greater of two real roots of the quadratic equation.

Suppose now that we want to find the greatest real root of a quadratic equations where the coefficients might be missing values. Having missing values in datasets is a common occurrence in real-world data, and so it is important to be able to deal with them. But we cannot find the roots of an equation if we do not know all the coefficients. The best solution to this will depend on the particular use case; perhaps we should throw an error. However, for this example, we will assume that the best solution is to propagate the missing values forward; that is, if any input is missing, we simply produce a missing output.

The broadcast() function makes this task easy; we can simply pass the root function we wrote to broadcast:

If one or more of the inputs is missing, then the output of broadcast() will be missing.

There exists special syntactic sugar for the broadcast() function using a dot notation:

In particular, the regular arithmetic operators can be broadcast() conveniently using .-prefixed operators:

Trasformate di distanza Distance transforms are used to calculate the minimum distance from each element of an object to the background. The following functions implement distance transforms for three different distance metrics: Euclidean, City Block, and Chessboard distances.

The function distance_transform_cdt uses a chamfer type algorithm to calculate the distance transform of the input, by replacing each object element (defined by values larger than zero) with the shortest distance to the background (all non-object elements). The structure determines the type of chamfering that is done. If the structure is equal to 'cityblock' a structure is generated using generate_binary_structure with a squared distance equal to 1. If the structure is equal to 'chessboard', a structure is generated using generate_binary_structure with a squared distance equal to the rank of the array. These choices correspond to the common interpretations of the cityblock and the chessboard distance metrics in two dimensions.

In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned.

The distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (both Int32). The basics of the algorithm used to implement this function is described in G. Borgefors, “Distance transformations in arbitrary dimensions.”, Computer Vision, Graphics, and Image Processing, 27:321-345, 1984.

The function distance_transform_edt calculates the exact euclidean distance transform of the input, by replacing each object element (defined by values larger than zero) with the shortest euclidean distance to the background (all non-object elements).

In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned.

Optionally the sampling along each axis can be given by the sampling parameter which should be a sequence of length equal to the input rank, or a single number in which the sampling is assumed to be equal along all axes.

The function distance_transform_bf uses a brute-force algorithm to calculate the distance transform of the input, by replacing each object element (defined by values larger than zero) with the shortest distance to the background (all non-object elements). The metric must be one of 'euclidean', 'cityblock', or 'chessboard'.

In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned.

Optionally the sampling along each axis can be given by the sampling parameter which should be a sequence of length equal to the input rank, or a single number in which the sampling is assumed to be equal along all axes. This parameter is only used in the case of the euclidean distance transform.

The distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (Float64 and Int32).

Note: This function uses a slow brute-force algorithm, the function distance_transform_cdt can be used to more efficiently calculate cityblock and chessboard distance transforms. The function distance_transform_edt can be used to more efficiently calculate the exact euclidean distance transform.

[W]e can use thePLT Scheme SICP Picture Languagepackage to run the exercises. You can load the picture package by putting the following (require...) expression at the beginning of your Scheme Racket file.

(require (planet "sicp.ss" ("soegaard" "sicp.plt" 2 1)))

Since segments->painter takes a list of segments as its input parameter, each solution is just a matter of figuring out the coordinates of the endpoints of each required segment. Recall that the (0.0, 0.0) coordinate is the bottom left corner in the frame, not the top left corner as it is in many GUI environments. For each solution, we can define the list of segments first, then define a painter.

The painter that draws the outline of the designated frame.

Note that I used 0.99 instead of 1.0 for the edges of the frame. This is because the top and right edges won’t be displayed if you use 1.0.

The painter that draws an “X” by connecting opposite corners of the frame.

The painter that draws a diamond shape by connecting the midpoints of the sides

The wave painter in the last part of the exercise is the image shown in Figure 2.10 [qui] of the text. It consists of 17 separate segments.

Operazioni sui tipi Since types in Julia are themselves objects, ordinary functions can operate on them. Some functions that are particularly useful for working with or exploring types have already been introduced, such as the <: operator, which indicates whether its left hand operand is a subtype of its right hand operand.

The isa() function tests if an object is of a given type and returns true or false:

The typeof() function, already used throughout the manual in examples, returns the type of its argument. Since, as noted above, types are objects, they also have types, and we can ask what their types are:

What if we repeat the process? What is the type of a type of a type? As it happens, types are all composite values and thus all have a type of DataType:

DataType is its own type.

Another operation that applies to some types is supertype(), which reveals a type’s supertype. Only declared types (DataType) have unambiguous supertypes:

If you apply supertype() to other type objects (or non-type objects), a MethodError is raised:

Visualizzazione su misura
Non so tradurre “Custom pretty-printing”.

Often, one wants to customize how instances of a type are displayed. This is accomplished by overloading the show() function. For example, suppose we define a type to represent complex numbers in polar form:

Unicode per Θ: 920, 0x398.

Here, we’ve added a custom constructor function so that it can take arguments of different Real types and promote them to a common type (see Constructors and Conversion and Promotion [prossimamente]). (Of course, we would have to define lots of other methods, too, to make it act like a Number, e.g. +, *, one, zero, promotion rules and so on.) By default, instances of this type display rather simply, with information about the type name and the field values, as e.g. Polar{Float64}(3.0,4.0).

If we want it to display instead as 3.0 * exp(4.0im), we would define the following method to print the object to a given output object io (representing a file, terminal, buffer, etcetera; see Networking and Streams [prossimamente]):

More fine-grained control over display of Polar objects is possible. In particular, sometimes one wants both a verbose multi-line printing format, used for displaying a single object in the REPL and other interactive environments, and also a more compact single-line format used for print() or for displaying the object as part of another object (e.g. in an array). Although by default the show(io, z) function is called in both cases, you can define a different multi-line format for displaying an object by overloading a three-argument form of show that takes the text/plain MIME type as its second argument (see Multimedia I/O [prossimamente]), for example:

where the single-line show(io, z) form is still used for an array of Polar values. Technically, the REPL calls display(z) to display the result of executing a line, which defaults to show(STDOUT, MIME("text/plain"), z), which in turn defaults to show(STDOUT, z), but you should not define new display() methods unless you are defining a new multimedia display handler (see Multimedia I/O [prossimamente]).

Moreover, you can also define show methods for other MIME types in order to enable richer display (HTML, images, etcetera) of objects in environments that support this (e.g. IJulia). For example, we can define formatted HTML display of Polar objects, with superscripts and italics, via:

A Polar object will then display automatically using HTML in an environment that supports HTML display, but you can call show manually to get HTML output if you want:

Morfologia morfologia binaria The generate_binary_structure functions generates a binary structuring element for use in binary morphology operations. The rank of the structure must be provided. The size of the structure that is returned is equal to three in each direction. The value of each element is equal to one if the square of the Euclidean distance from the element to the center is less or equal to connectivity. For instance, two dimensional 4-connected and 8-connected structures are generated as follows:

Most binary morphology functions can be expressed in terms of the basic operations erosion and dilation.

The binary_erosion function implements binary erosion of arrays of arbitrary rank with the given structuring element. The origin parameter controls the placement of the structuring element as described in Filter functions [qui]. If no structuring element is provided, an element with connectivity equal to one is generated using generate_binary_structure. The border_value parameter gives the value of the array outside boundaries. The erosion is repeated iterations times. If iterations is less than one, the erosion is repeated until the result does not change anymore. If a mask array is given, only those elements with a true value at the corresponding mask element are modified at each iteration.

The binary_dilation function implements binary dilation of arrays of arbitrary rank with the given structuring element. The origin parameter controls the placement of the structuring element as described in Filter functions [stesso link]. If no structuring element is provided, an element with connectivity equal to one is generated using generate_binary_structure. The border_value parameter gives the value of the array outside boundaries. The dilation is repeated iterations times. If iterations is less than one, the dilation is repeated until the result does not change anymore. If a mask array is given, only those elements with a true value at the corresponding mask element are modified at each iteration.

Here is an example of using binary_dilation to find all elements that touch the border, by repeatedly dilating an empty array from the border using the data array as the mask:

The binary_erosion and binary_dilation functions both have an iterations parameter which allows the erosion or dilation to be repeated a number of times. Repeating an erosion or a dilation with a given structure n times is equivalent to an erosion or a dilation with a structure that is n-1 times dilated with itself. A function is provided that allows the calculation of a structure that is dilated a number of times with itself:

The iterate_structure function returns a structure by dilation of the input structure iteration-1 times with itself.

For instance:

If the origin of the original structure is equal to 0, then it is also equal to 0 for the iterated structure. If not, the origin must also be adapted if the equivalent of the iterations erosions or dilations must be achieved with the iterated structure. The adapted origin is simply obtained by multiplying with the number of iterations. For convenience the iterate_structure also returns the adapted origin if the origin parameter is not None:

Other morphology operations can be defined in terms of erosion and d dilation. The following functions provide a few of these operations for convenience:

The binary_opening function implements binary opening of arrays of arbitrary rank with the given structuring element. Binary opening is equivalent to a binary erosion followed by a binary dilation with the same structuring element. The origin parameter controls the placement of the structuring element as described in Filter functions [stesso link]. If no structuring element is provided, an element with connectivity equal to one is generated using generate_binary_structure. The iterations parameter gives the number of erosions that is performed followed by the same number of dilations.

The binary_closing function implements binary closing of arrays of arbitrary rank with the given structuring element. Binary closing is equivalent to a binary dilation followed by a binary erosion with the same structuring element. The origin parameter controls the placement of the structuring element as described in Filter functions [stesso link]. If no structuring element is provided, an element with connectivity equal to one is generated using generate_binary_structure. The iterations parameter gives the number of dilations that is performed followed by the same number of erosions.

The binary_fill_holes function is used to close holes in objects in a binary image, where the structure defines the connectivity of the holes. The origin parameter controls the placement of the structuring element as described in Filter functions [stesso link]. If no structuring element is provided, an element with connectivity equal to one is generated using generate_binary_structure.

The binary_hit_or_miss function implements a binary hit-or-miss transform of arrays of arbitrary rank with the given structuring elements. The hit-or-miss transform is calculated by erosion of the input with the first structure, erosion of the logical not of the input with the second structure, followed by the logical and of these two erosions. The origin parameters control the placement of the structuring elements as described in Filter functions [stesso link]. If origin2 equals None it is set equal to the origin1 parameter. If the first structuring element is not provided, a structuring element with connectivity equal to one is generated using generate_binary_structure, if structure2 is not provided, it is set equal to the logical not of structure1.

morfologia grey-scale Grey-scale morphology operations are the equivalents of binary morphology operations that operate on arrays with arbitrary values. Below we describe the grey-scale equivalents of erosion, dilation, opening and closing. These operations are implemented in a similar fashion as the filters described in Filter functions [stesso link], and we refer to this section for the description of filter kernels and footprints, and the handling of array borders. The grey-scale morphology operations optionally take a structure parameter that gives the values of the structuring element. If this parameter is not given the structuring element is assumed to be flat with a value equal to zero. The shape of the structure can optionally be defined by the footprint parameter. If this parameter is not given, the structure is assumed to be rectangular, with sizes equal to the dimensions of the structure array, or by the size parameter if structure is not given. The size parameter is only used if both structure and footprint are not given, in which case the structuring element is assumed to be rectangular and flat with the dimensions given by size. The size parameter, if provided, must be a sequence of sizes or a single number in which case the size of the filter is assumed to be equal along each axis. The footprint parameter, if provided, must be an array that defines the shape of the kernel by its non-zero elements.

Similar to binary erosion and dilation there are operations for grey-scale erosion and dilation:

Grey-scale opening and closing operations can be defined similar to their binary counterparts:

The grey_opening function implements grey-scale opening of arrays of arbitrary rank. Grey-scale opening is equivalent to a grey-scale erosion followed by a grey-scale dilation.

The grey_closing function implements grey-scale closing of arrays of arbitrary rank. Grey-scale opening is equivalent to a grey-scale dilation followed by a grey-scale erosion.

The morphological_gradient function implements a grey-scale morphological gradient of arrays of arbitrary rank. The grey-scale morphological gradient is equal to the difference of a grey-scale dilation and a grey-scale erosion.

The morphological_laplace function implements a grey-scale morphological laplace of arrays of arbitrary rank. The grey-scale morphological laplace is equal to the sum of a grey-scale dilation and a grey-scale erosion minus twice the input.

The white_tophat function implements a white top-hat filter of arrays of arbitrary rank. The white top-hat is equal to the difference of the input and a grey-scale opening.

The black_tophat function implements a black top-hat filter of arrays of arbitrary rank. The black top-hat is equal to the difference of a grey-scale closing and the input.

TipiUnionAll We have said that a parametric type like Ptr acts as a supertype of all its instances (Ptr{Int64} etc.). How does this work? Ptr itself cannot be a normal data type, since without knowing the type of the referenced data the type clearly cannot be used for memory operations. The answer is that Ptr (or other parametric types like Array) is a different kind of type called a UnionAll type. Such a type expresses the iterated union of types for all values of some parameter.

UnionAll types are usually written using the keyword where. For example Ptr could be more accurately written as Ptr{T} where T, meaning all values whose type is Ptr{T} for some value of T. In this context, the parameter T is also often called a “type variable” since it is like a variable that ranges over types. Each where introduces a single type variable, so these expressions are nested for types with multiple parameters, for example Array{T,N} where N where T.

The type application syntax A{B,C} requires A to be a UnionAll type, and first substitutes B for the outermost type variable in A. The result is expected to be another UnionAll type, into which C is then substituted. So A{B,C} is equivalent to A{B}{C}. This explains why it is possible to partially instantiate a type, as in Array{Float64}: the first parameter value has been fixed, but the second still ranges over all possible values. Using explicit where syntax, any subset of parameters can be fixed. For example, the type of all 1-dimensional arrays can be written as Array{T,1} where T.

Type variables can be restricted with subtype relations. Array{T} where T<:Integer refers to all arrays whose element type is some kind of Integer. The syntax Array{<:Integer} is a convenient shorthand for Array{T} where T<:Integer. Type variables can have both lower and upper bounds. Array{T} where Int<:T<:Number refers to all arrays of Numbers that are able to contain Ints (since T must be at least as big as Int). The syntax where T>:Int also works to specify only the lower bound of a type variable, and Array{>:Int} is equivalent to Array{T} where T>:Int.

Since where expressions nest, type variable bounds can refer to outer type variables. For example Tuple{T,Array{S}} where S<:AbstractArray{T}whereT<:Real refers to 2-tuples whose first element is some Real, and whose second element is an Array of any kind of array whose element type contains the type of the first tuple element.

The where keyword itself can be nested inside a more complex declaration. For example, consider the two types created by the following declarations:

Type T1 defines a 1-dimensional array of 1-dimensional arrays; each of the inner arrays consists of objects of the same type, but this type may vary from one inner array to the next. On the other hand, type T2 defines a 1-dimensional array of 1-dimensional arrays all of whose inner arrays must have the same type. Note that T2 is an abstract type, e.g., Array{Array{Int,1},1} <: T2, whereas T1 is a concrete type. As a consequence, T1 can be constructed with a zero-argument constructor a=T1() but T2 cannot.

There is a convenient syntax for naming such types, similar to the short form of function definition syntax:

Vector{T} = Array{T,1}

This is equivalent to const Vector = Array{T,1} where T. Writing Vector{Float64} is equivalent to writing Array{Float64,1}, and the umbrella type Vector has as instances all Array objects where the second parameter – the number of array dimensions – is 1, regardless of what the element type is. In languages where parametric types must always be specified in full, this is not especially helpful, but in Julia, this allows one to write just Vector for the abstract type including all one-dimensional dense arrays of any element type.

Aliases di tipi Sometimes it is convenient to introduce a new name for an already expressible type. This can be done with a simple assignment statement. For example, UInt is aliased to either UInt32 or UInt64 as is appropriate for the size of pointers on the system:

Su una macchina 32-bit il risultato sarebbe stato UInt32.

This is accomplished via the following code in base/boot.jl:

if Int === Int64
const UInt = UInt64
else
const UInt = UInt32
end

Of course, this depends on what Int is aliased to – but that is predefined to be the correct type – either Int32 or Int64.

Note that unlike Int, Float does not exist as a type alias for a specific sized AbstractFloat. Unlike with integer registers, the floating point register sizes are specified by the IEEE-754 standard. Whereas the size of Int reflects the size of a native pointer on that machine.

Filtri del dominio di Fourier The functions described in this section perform filtering operations in the Fourier domain. Thus, the input array of such a function should be compatible with an inverse Fourier transform function, such as the functions from the numpy.fft module. We therefore have to deal with arrays that may be the result of a real or a complex Fourier transform. In the case of a real Fourier transform only half of the of the symmetric complex transform is stored. Additionally, it needs to be known what the length of the axis was that was transformed by the real fft. The functions described here provide a parameter n that in the case of a real transform must be equal to the length of the real transform axis before transformation. If this parameter is less than zero, it is assumed that the input array was the result of a complex Fourier transform. The parameter axis can be used to indicate along which axis the real transform was executed.

The fourier_shift function multiplies the input array with the multidimensional Fourier transform of a shift operation for the given shift. The shift parameter is a sequences of shifts for each dimension, or a single value for all dimensions.

The fourier_gaussian function multiplies the input array with the multidimensional Fourier transform of a Gaussian filter with given standard-deviations sigma. The sigma parameter is a sequences of values for each dimension, or a single value for all dimensions.

The fourier_uniform function multiplies the input array with the multidimensional Fourier transform of a uniform filter with given sizes size. The size parameter is a sequences of values for each dimension, or a single value for all dimensions.

The fourier_ellipsoid function multiplies the input array with the multidimensional Fourier transform of a elliptically shaped filter with given sizes size. The size parameter is a sequences of values for each dimension, or a single value for all dimensions. This function is only implemented for dimensions 1, 2, and 3.

No, niente esempi.

Funzioni di interpolazione This section describes various interpolation functions that are based on B-spline theory. A good introduction to B-splines can be found in M. Unser, “Splines: A Perfect Fit for Signal and Image Processing,” IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999.

pre-filtri per splines Interpolation using splines of an order larger than 1 requires a pre-filtering step. The interpolation functions [here] described […] apply pre-filtering by calling spline_filter, but they can be instructed not to do this by setting the prefilter keyword equal to False. This is useful if more than one interpolation operation is done on the same array. In this case it is more efficient to do the pre-filtering only once and use a prefiltered array as the input of the interpolation functions. The following two functions implement the pre-filtering:

The spline_filter1d function calculates a one-dimensional spline filter along the given axis. An output array can optionally be provided. The order of the spline must be larger then 1 and less than 6.

Note: The multidimensional filter is implemented as a sequence of one-dimensional spline filters. The intermediate arrays are stored in the same data type as the output. Therefore, if an output with a limited precision is requested, the results may be imprecise because intermediate results may be stored with insufficient precision. This can be prevented by specifying a output type of high precision.

funzioni di interpolazione Following functions all employ spline interpolation to effect some type of geometric transformation of the input array. This requires a mapping of the output coordinates to the input coordinates, and therefore the possibility arises that input values outside the boundaries are needed. This problem is solved in the same way as described in Filter functions [qui] for the multidimensional filter functions. Therefore these functions all support a mode parameter that determines how the boundaries are handled, and a cval parameter that gives a constant value in case that the ‘constant’ mode is used.

The geometric_transform function applies an arbitrary geometric transform to the input. The given mapping function is called at each point in the output to find the corresponding coordinates in the input. mapping must be a callable object that accepts a tuple of length equal to the output array rank and returns the corresponding input coordinates as a tuple of length equal to the input array rank. The output shape and output type can optionally be provided. If not given they are equal to the input shape and type.

For example:

Optionally extra arguments can be defined and passed to the filter function. The extra_arguments and extra_keywords arguments can be used to pass a tuple of extra arguments and/or a dictionary of named arguments that are passed to derivative at each call. For example, we can pass the shifts in our example as arguments

or

Note: The mapping function can also be written in C and passed using a scipy.LowLevelCallable. See Extending scipy.ndimage in C [prossimamente] for more information.

The function map_coordinates applies an arbitrary coordinate transformation using the given array of coordinates. The shape of the output is derived from that of the coordinate array by dropping the first axis. The parameter coordinates is used to find for each point in the output the corresponding coordinates in the input. The values of coordinates along the first axis are the coordinates in the input array at which the output value is found. (See also the numarray coordinates function.) Since the coordinates may be non- integer coordinates, the value of the input at these coordinates is determined by spline interpolation of the requested order.

Here is an example that interpolates a 2D array at (0.5, 0.5) and (1, 2):

The affine_transform function applies an affine transformation to the input array. The given transformation matrix and offset are used to find for each point in the output the corresponding coordinates in the input. The value of the input at the calculated coordinates is determined by spline interpolation of the requested order. The transformation matrix must be two-dimensional or can also be given as a one-dimensional sequence or array. In the latter case, it is assumed that the matrix is diagonal. A more efficient interpolation algorithm is then applied that exploits the separability of the problem. The output shape and output type can optionally be provided. If not given they are equal to the input shape and type.

The shift function returns a shifted version of the input, using spline interpolation of the requested order.

The zoom function returns a rescaled version of the input, using spline interpolation of the requested order.

The rotate function returns the input array rotated in the plane defined by the two axes given by the parameter axes, using spline interpolation of the requested order. The angle must be given in degrees. If reshape is true, then the size of the output array is adapted to contain the rotated input.