The custom module exists to allow users to define their own custom stretching algorithms. Like other open source applications, the PyStretch developers understand that it is not possible to write every image manipulation algorithm that a user is apt to require. We therefore have deferred to the structure used by ImageJ, a popular open source image manipulation suite. That is, we have made the inclusion of additional algorithms easy!
It is hoped that users will write and contribute image stretching algorithms. The benefits are three-fold:
Algorithms can be contributed to the PyStretch Github page.
PyStretch contains a module name ‘custom’. Within the custom module a framework script “Custom.py” exists with two functions: linear_stretch and custom_stretch.
linear_stretch serves as a tutorial by example, form which users can explore the syntax and implementation of both argument passing and the manipulation of shared memory arrays.
custom_stretch is an empty def function with helper comments designed to store your image manipulation algorithm.
The custom stretch has access to all of the format manipulation power of GDAL as well as the image segmentation and multiprocessing capabilities already implemented in PyStretch. A flag already exists to call the script via the same command-line interface as PyStretch.
To use the custom function, write your algorithm in the custom_stretch function contained in Custom.py using your favorite IDE, save the file, and call:
$ pystretcher.py --custom your_input_image.jp2
This more in depth look hopes to provide you with a deep understanding of how the image array is accessed and manipulated as well as what variables are available for use.
Line 74 of Custom.py begins the custom_stretch function.:
def custom_stretch(shared_array, i, \**kwargs):
Here we pass the shared_array, the row or rows to be manipulated, and a dictionary of keyword arguments. To make your code more readable, it is suggested that you unpack the dictionary of keyword args (**kwargs). For example:
clip = kwargs['clip']
maximum = kwargs['maximum']
Line 79 needs to be uncommented to provide the function access to the shared array. This is performed by providing an ndarray view of the ctypes buffer. For more information see PEP3118 In short, this returns a read/write view which numpy can work with.
Therefore:
#arr = shared_array.asarray()
Becomes:
arr = shared_array.asarray()
Then you can perform an logical operations which need to be pre-calculated. For example, if the minimum and standard deviation of the array need to be recalculated or modified in some way, it is easier to perform that manipulation now.
For example:
standard_deviation += 5
Which can also be written as:
standard_deviation = standard_deviation + 5
Note
It is not necessary to perform any manipulation on the parameters (kwargs). You could also perform manipulation while manipulating the array. The above is only a suggestion to facilitate a cleaner separation in code for readability for other users.
Finally, perform any manipulation to the array. Access to the array slice, one per core, is achieved by using:
arr[i]
That is, we provide a slice view of the entire shared array.
Note
It is essential to pass a view of the array using arr[i]. If a slice is not passed then the algorithm will overwrite the same indices multiple times in an unknowable order (the speed of the individual process will dictate which core access the index fist and which cores have subsequent access). You must pass the slice to your array manipulation logic.
Warning
In memory duplication is not difficult when using numpy. Be sure to perform your manipulation without making an in-memory copy. For example:
arr[i] = arr[i] + 1 # Will make an in-memory copy
arr[i] += 1 # Will perform the addition in place
Likewise:
arr[i] = arr[i] / 2 # Will make an in-memory copy
arr[i] \*= 1/arr[i] #Will perform the division in place(using multiplication of the inverse)