arrlp 0.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. arrlp-0.1.6/PKG-INFO +49 -0
  2. arrlp-0.1.6/README.md +30 -0
  3. arrlp-0.1.6/pyproject.toml +17 -0
  4. arrlp-0.1.6/src/arrlp/__init__.py +23 -0
  5. arrlp-0.1.6/src/arrlp/modules/__init__.py +0 -0
  6. arrlp-0.1.6/src/arrlp/modules/compress_LP/__init__.py +0 -0
  7. arrlp-0.1.6/src/arrlp/modules/compress_LP/compress.py +114 -0
  8. arrlp-0.1.6/src/arrlp/modules/compress_LP/test_compress.py +79 -0
  9. arrlp-0.1.6/src/arrlp/modules/convolve_LP/__init__.py +0 -0
  10. arrlp-0.1.6/src/arrlp/modules/convolve_LP/convolve.py +67 -0
  11. arrlp-0.1.6/src/arrlp/modules/convolve_LP/test_convolve.py +59 -0
  12. arrlp-0.1.6/src/arrlp/modules/coordinates_LP/__init__.py +0 -0
  13. arrlp-0.1.6/src/arrlp/modules/coordinates_LP/coordinates.py +125 -0
  14. arrlp-0.1.6/src/arrlp/modules/coordinates_LP/test_coordinates.py +43 -0
  15. arrlp-0.1.6/src/arrlp/modules/correlate_LP/__init__.py +0 -0
  16. arrlp-0.1.6/src/arrlp/modules/correlate_LP/correlate.py +194 -0
  17. arrlp-0.1.6/src/arrlp/modules/correlate_LP/test_correlate.py +58 -0
  18. arrlp-0.1.6/src/arrlp/modules/kernel_LP/__init__.py +0 -0
  19. arrlp-0.1.6/src/arrlp/modules/kernel_LP/kernel.py +187 -0
  20. arrlp-0.1.6/src/arrlp/modules/kernel_LP/test_kernel.py +50 -0
  21. arrlp-0.1.6/src/arrlp/modules/relabel_LP/__init__.py +0 -0
  22. arrlp-0.1.6/src/arrlp/modules/relabel_LP/relabel.py +60 -0
  23. arrlp-0.1.6/src/arrlp/modules/relabel_LP/test_relabel.py +37 -0
  24. arrlp-0.1.6/src/arrlp/modules.json +38 -0
  25. arrlp-0.1.6/src/arrlp/py.typed +0 -0
  26. arrlp-0.1.6/src/arrlp/scripts/__init__.py +0 -0
  27. arrlp-0.1.6/src/arrlp/scripts.json +1 -0
arrlp-0.1.6/PKG-INFO ADDED
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.3
2
+ Name: arrlp
3
+ Version: 0.1.6
4
+ Summary: A library providing custom functions for arrays.
5
+ Requires-Dist: astropy
6
+ Requires-Dist: corelp
7
+ Requires-Dist: ipykernel
8
+ Requires-Dist: numba
9
+ Requires-Dist: numpy
10
+ Requires-Dist: pytest
11
+ Requires-Dist: scipy
12
+ Requires-Dist: sphinx
13
+ Requires-Dist: sphinx-design
14
+ Requires-Dist: sphinx-rtd-theme
15
+ Requires-Dist: spyder-kernels
16
+ Requires-Dist: toml
17
+ Requires-Python: >=3.12
18
+ Description-Content-Type: text/markdown
19
+
20
+ # arrLP
21
+
22
+ ```text
23
+ Author : Lancelot PINCET
24
+ GitHub : https://github.com/LancelotPincet/arrLP
25
+ HTTPS : https://github.com/LancelotPincet/arrLP.git
26
+ SSH : git@github.com:LancelotPincet/arrLP.git
27
+ PyPI : https://pypi.org/project/arrLP
28
+ Docs : https://arrLP.readthedocs.io
29
+ ```
30
+
31
+ **A library providing custom functions for arrays.**
32
+
33
+ arrLP is available on [PyPI](https://pypi.org/project/arrLP) for pip installs.
34
+ For more information, do not hesitate to consult the [Documentation](https://arrLP.readthedocs.io).
35
+
36
+ ---
37
+
38
+ ## MIT License
39
+
40
+ <details>
41
+ <summary>details</summary>
42
+
43
+ Intellectual property behind this Library is protected via an [MIT license](LICENSE). This means everyone can *freely* use it in a personnal, academic or commercial manner, if they **keep the copyright name** at the top of the codes.
44
+
45
+ The library can be redistributed, *with or without modifications*, in open or closed projects. However the **MIT license must be conserved**. For example in a commercial closed project, this means the **copyright and license must be visible somewhere**, like in the documentation or credits.
46
+
47
+ The license also explains that the **code performances are not warrantied**, and you are responsible for how you are using it. For more information on your rights and obligations please refer to [descriptive websites](https://en.wikipedia.org/wiki/MIT_License), or contact author for approvales.
48
+
49
+ </details>
arrlp-0.1.6/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # arrLP
2
+
3
+ ```text
4
+ Author : Lancelot PINCET
5
+ GitHub : https://github.com/LancelotPincet/arrLP
6
+ HTTPS : https://github.com/LancelotPincet/arrLP.git
7
+ SSH : git@github.com:LancelotPincet/arrLP.git
8
+ PyPI : https://pypi.org/project/arrLP
9
+ Docs : https://arrLP.readthedocs.io
10
+ ```
11
+
12
+ **A library providing custom functions for arrays.**
13
+
14
+ arrLP is available on [PyPI](https://pypi.org/project/arrLP) for pip installs.
15
+ For more information, do not hesitate to consult the [Documentation](https://arrLP.readthedocs.io).
16
+
17
+ ---
18
+
19
+ ## MIT License
20
+
21
+ <details>
22
+ <summary>details</summary>
23
+
24
+ Intellectual property behind this Library is protected via an [MIT license](LICENSE). This means everyone can *freely* use it in a personnal, academic or commercial manner, if they **keep the copyright name** at the top of the codes.
25
+
26
+ The library can be redistributed, *with or without modifications*, in open or closed projects. However the **MIT license must be conserved**. For example in a commercial closed project, this means the **copyright and license must be visible somewhere**, like in the documentation or credits.
27
+
28
+ The license also explains that the **code performances are not warrantied**, and you are responsible for how you are using it. For more information on your rights and obligations please refer to [descriptive websites](https://en.wikipedia.org/wiki/MIT_License), or contact author for approvales.
29
+
30
+ </details>
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "arrlp"
3
+ version = "0.1.6"
4
+ description = "A library providing custom functions for arrays."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [ "astropy", "corelp", "ipykernel", "numba", "numpy", "pytest", "scipy", "sphinx", "sphinx-design", "sphinx-rtd-theme", "spyder-kernels", "toml",]
8
+
9
+ [build-system]
10
+ requires = [ "uv_build>=0.8.8,<0.9.0",]
11
+ build-backend = "uv_build"
12
+
13
+ [tool.uv]
14
+ required-environments = [ "sys_platform == 'linux' and platform_machine == 'x86_64'", "sys_platform == 'win32' and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",]
15
+
16
+ [tool.uv.sources.corelp]
17
+ workspace = true
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-28
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+
8
+ """
9
+ A library providing custom functions for arrays.
10
+ """
11
+
12
+
13
+
14
+ # %% Lazy imports
15
+ from corelp import getmodule
16
+ __getattr__, __all__ = getmodule(__file__)
17
+
18
+
19
+
20
+ # %% Test function run
21
+ if __name__ == "__main__":
22
+ from corelp import test
23
+ test(__file__)
File without changes
File without changes
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-11-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : compress
8
+
9
+ """
10
+ Compresses an array between values by normalizing, with possibility to saturate extrema.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ import numpy as np
17
+
18
+
19
+
20
+ # %% Function
21
+ def compress(array, /, max=1, min=0, *, dtype=None, white=None, black=None, white_percent=None, black_percent=None, saturate=None) :
22
+ '''
23
+ Compresses an array between values by normalizing, with possibility to saturate extrema.
24
+
25
+ Parameters
26
+ ----------
27
+ array : np.ndarray
28
+ Array to normalize.
29
+ max : int or float or None
30
+ white value in output. None for no changing of white
31
+ min : int or float or None
32
+ black value in output. None for no changing of black
33
+ dtype : np.dtype or str or None
34
+ dtype of output, None for same as input
35
+ white : int or float or None
36
+ white value in input. None for maximum
37
+ black : int or float or None
38
+ black value in input. None for minimum
39
+ white_percent : int or None
40
+ white percentage distribution in input if white is None.
41
+ black_percent : int or None
42
+ black percentage distribution in input if black is None.
43
+ saturate : Any or bool or None
44
+ If True, will saturate values above white and black. If Any, will replace by this value. If None no saturation.
45
+
46
+ Returns
47
+ -------
48
+ array : np.ndarray
49
+ Normalized and saturated copy of array.
50
+
51
+ Examples
52
+ --------
53
+ >>> from arrlp import compress
54
+ >>> array = np.arange(100, dtype=np.float32)
55
+ ...
56
+ >>> compress(array, 10, 5) # compresses array between 10 and 5
57
+ >>> compress(array, white=50, black=40) # compresses array linearly so that 50 value is at 1 and 40 is at 0
58
+ >>> compress(array, white=50, black=40, saturate=np.nan) # compresses array linearly so that 50 value is at 1 and 40 is at 0, replace outside values by np.nan
59
+ >>> compress(array, white_percent=10, black=1) # compresses array linearly so that 10% of array will be white, and 1% black (without saturation)
60
+ >>> compress(array, white_percent=10, black=1, saturate=True) # compresses array linearly so that 10% of array will be white, and 1% black (with saturation)
61
+ '''
62
+
63
+ # init
64
+ array = np.asarray(array)
65
+ if dtype is None :
66
+ dtype = array.dtype
67
+
68
+ # Get white/black
69
+ if white is None :
70
+ white = np.nanmax(array) if white_percent is None else np.nanpercentile(array, 100-white_percent)
71
+ if black is None :
72
+ black = np.nanmin(array) if black_percent is None else np.nanpercentile(array, black_percent)
73
+ if white >= black :
74
+ raise ValueError('white >= black is not possible while compressing')
75
+
76
+ # Normalization
77
+ if max is not None and min is not None and min >= max :
78
+ raise ValueError('min >= max is not possible while compressing')
79
+ if max is not None :
80
+ array = normalization(array, value=max, norm=white, fix=black)
81
+ white = max
82
+ if min is not None :
83
+ array = normalization(array, value=min, norm=black, fix=white)
84
+ black = min
85
+ if max is None and min is None :
86
+ array = np.copy(array)
87
+
88
+ # Saturation
89
+ if saturate is not None :
90
+ if saturate is True :
91
+ sat_min, sat_max = min, max
92
+ else :
93
+ sat_min, sat_max = saturate, saturate
94
+ array[array>max] = sat_max
95
+ array[array<min] = sat_min
96
+
97
+ return array.astype(dtype)
98
+
99
+
100
+
101
+ def normalization(array, /, value:float=None, norm:float=None, fix:float=None):
102
+ '''Basic normalization process of array copy while keeping a fixed point'''
103
+
104
+ if fix is None : fix = 0
105
+ if norm is None : norm = max(np.nanmax(array),-np.nanmin(array))
106
+ if value is None : value = 1*np.sign(norm)
107
+ return (array-fix)/(norm-fix)*(value-fix) + fix
108
+
109
+
110
+
111
+ # %% Test function run
112
+ if __name__ == "__main__":
113
+ from corelp import test
114
+ test(__file__)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-11-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : compress
8
+
9
+ """
10
+ This file allows to test compress
11
+
12
+ compress : Compresses an array between values by normalizing, with possibility to saturate extrema.
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import print, debug
19
+ import pytest
20
+ from arrlp import compress
21
+ debug_folder = debug(__file__)
22
+
23
+
24
+
25
+ # %% Function test
26
+ def test_function() :
27
+ '''
28
+ Test compress function
29
+ '''
30
+ print('Hello world!')
31
+
32
+
33
+
34
+ # %% Instance fixture
35
+ @pytest.fixture()
36
+ def instance() :
37
+ '''
38
+ Create a new instance at each test function
39
+ '''
40
+ return compress()
41
+
42
+ def test_instance(instance) :
43
+ '''
44
+ Test on fixture
45
+ '''
46
+ pass
47
+
48
+
49
+ # %% Returns test
50
+ @pytest.mark.parametrize("args, kwargs, expected, message", [
51
+ #([], {}, None, ""),
52
+ ([], {}, None, ""),
53
+ ])
54
+ def test_returns(args, kwargs, expected, message) :
55
+ '''
56
+ Test compress return values
57
+ '''
58
+ assert compress(*args, **kwargs) == expected, message
59
+
60
+
61
+
62
+ # %% Error test
63
+ @pytest.mark.parametrize("args, kwargs, error, error_message", [
64
+ #([], {}, None, ""),
65
+ ([], {}, None, ""),
66
+ ])
67
+ def test_errors(args, kwargs, error, error_message) :
68
+ '''
69
+ Test compress error values
70
+ '''
71
+ with pytest.raises(error, match=error_message) :
72
+ compress(*args, **kwargs)
73
+
74
+
75
+
76
+ # %% Test function run
77
+ if __name__ == "__main__":
78
+ from corelp import test
79
+ test(__file__)
File without changes
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : convolve
8
+
9
+ """
10
+ This function convolves two numpy arrays (1D, 2D, 3D).
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ from arrlp import correlate
17
+ import numpy as np
18
+
19
+
20
+
21
+ # %% Function
22
+ def convolve(arr, k=None, /, out=None, **kwargs) :
23
+ '''
24
+ This function convolves two numpy arrays (1D, 2D, 3D).
25
+
26
+ Parameters
27
+ ----------
28
+ arr : numpy.ndarray
29
+ first array to correlate.
30
+ k : numpy.ndarray
31
+ second array to correlate, if is None is auto created.
32
+ out : numpy.ndarray
33
+ array where to put correlation, same shape as arr, if None is auto initialized.
34
+ kwargs : dict
35
+ Parameters to pass to arrlp.correlate function.
36
+
37
+ Returns
38
+ -------
39
+ out : numpy.ndarray
40
+ Correlated array.
41
+
42
+ Raises
43
+ ------
44
+ TypeError
45
+ if output shape does not match array shape.
46
+
47
+ Examples
48
+ --------
49
+ >>> from arrlp import correlate
50
+ ...
51
+ >>> convolve(img, sigma=5) # Gaussian convolve
52
+ >>> convolve(img, window=10) # Mask convolve
53
+ >>> convolve(img, wl=640, NA=1.5) # Airy convolve
54
+ '''
55
+
56
+ if k is not None :
57
+ for dim in range(np.ndim(k)) :
58
+ k = np.flip(k, axis=dim)
59
+ return correlate(arr, k, out, **kwargs)
60
+
61
+
62
+
63
+
64
+ # %% Test function run
65
+ if __name__ == "__main__":
66
+ from corelp import test
67
+ test(__file__)
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : convolve
8
+
9
+ """
10
+ This file allows to test convolve
11
+
12
+ convolve : This function convolves two numpy arrays (1D, 2D, 3D).
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import debug
19
+ import pytest
20
+ from matplotlib import pyplot as plt
21
+ from arrlp import kernel, convolve
22
+ debug_folder = debug(__file__)
23
+
24
+
25
+
26
+ # %% Function test
27
+ @pytest.mark.parametrize("kwargs, title", [
28
+ ({"window":10}, "Mask"),
29
+ ({"sigma":5}, "Gaussian"),
30
+ ({"wl":640, "NA":1.5}, "Airy"),
31
+ ])
32
+ def test_function(kwargs, title) :
33
+ '''
34
+ Test correlate values
35
+ '''
36
+
37
+ img = kernel(pixel=5, window=1000)
38
+
39
+ plt.figure()
40
+ plt.imshow(img, cmap="hot")
41
+ plt.tight_layout()
42
+ plt.colorbar()
43
+ plt.savefig(debug_folder / f"{title}_before.png")
44
+
45
+ out = convolve(img, pixel=5, **kwargs)
46
+
47
+ plt.figure()
48
+ plt.imshow(out, cmap="hot")
49
+ plt.tight_layout()
50
+ plt.colorbar()
51
+ plt.savefig(debug_folder / f"{title}.png")
52
+
53
+
54
+
55
+
56
+ # %% Test function run
57
+ if __name__ == "__main__":
58
+ from corelp import test
59
+ test(__file__)
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : coordinates
8
+
9
+ """
10
+ This function will return a ndarray corresponding to the coordinates array of the input.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ import numpy as np
17
+
18
+
19
+
20
+ # %% Function
21
+ def coordinates(shape, pixel=1., *, ndims=1, center=True, grid=False, origin=0.) :
22
+ '''
23
+ This function will return a ndarray corresponding to the coordinates array of the input.
24
+
25
+ Parameters
26
+ ----------
27
+ shape : int or tuple or np.ndarray
28
+ Describes the shape of the coordinates.
29
+ If int, all dimensions will have this value.
30
+ If tuple, corresponds to the shape.
31
+ If array, will take the shape attribute of the array
32
+ pixel : float, tuple(float)
33
+ Says what is the size of one bin, default is 1.
34
+ If tuple, corresponds to the pixel for each dimension.
35
+ ndims : int
36
+ Number of dimensions if input shape is int.
37
+ Will be overriden by any other input dimension.
38
+ center : bool or tuple(bool)
39
+ True to center origin, False to start by origin.
40
+ If tuple, defines how to place origin for each dimension.
41
+ grid : bool
42
+ True to return full meshgrid for each dimensions, False for broadcastable arrays.
43
+ origin : float or tuple(float)
44
+ Value of origin (default 0).
45
+ If tuple, value for each dimension.
46
+
47
+ Returns
48
+ -------
49
+ coords : np.ndarray or tuple(np.ndarray)
50
+ for 1 dimensional shape, returns coordinates array.
51
+ else, return a tuple of arrays for each dimensions.
52
+
53
+ Raises
54
+ ------
55
+ ValueError
56
+ if input or passed as tuples that do not have the good number of dimensions.
57
+
58
+ Examples
59
+ --------
60
+ >>> from arrlp import coordinates
61
+ ...
62
+ >>> array = np.ones((5,4))
63
+ >>> coordinates = coordinates(array, pixel=10, center=(True, False), grid=False, origin=(0,3))
64
+ ... array([[-20.], [-10.], [0.], [10.], [20.]]), array([[3., 13., 23., 33.]])
65
+ '''
66
+
67
+ # Manage shape argument
68
+ if isinstance(shape, int) :
69
+ shape = (shape,) * ndims
70
+ elif isinstance(shape, tuple) :
71
+ pass
72
+ else :
73
+ shape = np.shape(shape)
74
+
75
+ # Correct ndims
76
+ ndims = len(shape)
77
+
78
+ # Correct input dimensions
79
+ pixel = iterable(pixel, ndims)
80
+ center = iterable(center, ndims)
81
+ origin = iterable(origin, ndims)
82
+
83
+ #looping on shape
84
+ coords = []
85
+ for dim, (n, pix, cent, orig) in enumerate(zip(shape, pixel, center, origin)) :
86
+ coord = n2coord(n, pixel=pix, center=cent, origin=orig)
87
+ reshape = np.ones(ndims, dtype=int)
88
+ reshape[dim] = len(coord)
89
+ coord = coord.reshape(tuple(reshape))
90
+ coords.append(coord)
91
+
92
+ #Meshgrid
93
+ if ndims == 1 :
94
+ return coords[0]
95
+ elif grid :
96
+ return np.meshgrid(*coords, indexing='ij')
97
+ else :
98
+ return coords
99
+
100
+
101
+
102
+ def iterable(arg, ndims) :
103
+ try :
104
+ if len(arg) != ndims :
105
+ raise ValueError('Argument does not have good number of dimensions')
106
+ else :
107
+ return arg
108
+ except TypeError :
109
+ return (arg,) * ndims
110
+
111
+
112
+
113
+ def n2coord(n, pixel=1., center=False, origin=0.) :
114
+ if center :
115
+ start, stop = -(n - 1) / 2,(n - 1) / 2
116
+ else :
117
+ start,stop = 0, n - 1
118
+ return np.linspace(start, stop, n) * pixel + origin
119
+
120
+
121
+
122
+ # %% Test function run
123
+ if __name__ == "__main__":
124
+ from corelp import test
125
+ test(__file__)
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : coordinates
8
+
9
+ """
10
+ This file allows to test coordinates
11
+
12
+ coordinates : This function will return a ndarray corresponding to the coordinates array of the input.
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import debug
19
+ import pytest
20
+ import numpy as np
21
+ from arrlp import coordinates
22
+ debug_folder = debug(__file__)
23
+
24
+ print(coordinates())
25
+
26
+ # %% Returns test
27
+ @pytest.mark.parametrize("args, kwargs, expected, message", [
28
+ #([], {}, None, ""),
29
+ ([np.ones((5,4))], {"pixel":10, "center":(True, False), "grid":False, "origin":(0,3)}, (np.array([[-20.], [-10.], [0.], [10.], [20.]]), np.array([[3., 13., 23., 33.]])), ""),
30
+ ])
31
+ def test_returns(args, kwargs, expected, message) :
32
+ '''
33
+ Test coordinates return values
34
+ '''
35
+ assert (coordinates(*args, **kwargs)[0] == expected[0]).all(), message
36
+ assert (coordinates(*args, **kwargs)[1] == expected[1]).all(), message
37
+
38
+
39
+
40
+ # %% Test function run
41
+ if __name__ == "__main__":
42
+ from corelp import test
43
+ test(__file__)
File without changes
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : correlate
8
+
9
+ """
10
+ This function correlates two numpy arrays (1D, 2D, 3D).
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ from arrlp import kernel
17
+ from numba import njit, prange
18
+ import numpy as np
19
+ import astropy
20
+
21
+
22
+
23
+ # %% Function
24
+ def correlate(arr, k=None, /, out=None, *, method="mean", **kernel_kwargs) :
25
+ '''
26
+ This function correlates two numpy arrays (1D, 2D, 3D).
27
+
28
+ Parameters
29
+ ----------
30
+ arr : numpy.ndarray
31
+ first array to correlate.
32
+ k : numpy.ndarray
33
+ second array to correlate, if is None is auto created.
34
+ out : numpy.ndarray
35
+ array where to put correlation, same shape as arr, if None is auto initialized.
36
+ method : str
37
+ define function to use for correlation. Should be in ["astro", "mean", "std", "min", "max", "sum", "prod"].
38
+ kernel_kwargs : dict
39
+ Parameters passed to kernel function for auto-creation.
40
+
41
+ Returns
42
+ -------
43
+ out : numpy.ndarray
44
+ Correlated array.
45
+
46
+ Raises
47
+ ------
48
+ TypeError
49
+ if output shape does not match array shape.
50
+
51
+ Examples
52
+ --------
53
+ >>> from arrlp import correlate
54
+ ...
55
+ >>> correlate(img, sigma=5) # Gaussian correlation
56
+ >>> correlate(img, window=10) # Mask correlation
57
+ >>> correlate(img, wl=640, NA=1.5) # Airy correlation
58
+ '''
59
+
60
+ # Method
61
+ ndims = len(np.shape(arr))
62
+ method = f"{method}{ndims}"
63
+
64
+ # Kernel
65
+ if k is None :
66
+ k = kernel(ndims, **kernel_kwargs)
67
+
68
+ # Output
69
+ if out is None :
70
+ out = np.empty_like(arr)
71
+ if np.shape(out) != np.shape(arr) :
72
+ raise TypeError('Output shape does not match array shape')
73
+
74
+ # Function
75
+ function = function_factory(method)
76
+ function(arr, k, out)
77
+
78
+ return out
79
+
80
+
81
+
82
+ _cached = {}
83
+ funcs = {
84
+ 'mean' : np.nanmean,
85
+ 'std' : np.nanstd,
86
+ 'min' : np.nanmin,
87
+ 'max' : np.nanmax,
88
+ 'sum' : np.nansum,
89
+ 'prod' : np.nanprod,
90
+ }
91
+
92
+ def function_factory(method) :
93
+ function = _cached.get(method, None)
94
+ if function is not None :
95
+ return function
96
+
97
+ if method.startswith("astro") :
98
+ if method.endswith("1") :
99
+ def invert(k) :
100
+ return k[::-1]
101
+ elif method.endswith("2") :
102
+ def invert(k) :
103
+ return k[::-1, ::-1]
104
+ elif method.endswith("3") :
105
+ def invert(k) :
106
+ return k[::-1, ::-1, ::-1]
107
+ else :
108
+ raise SyntaxError(f'Correlation error cannot be {method}')
109
+
110
+ def function(arr, k, out, *, kernel_limit=2**12) :
111
+ k = invert(k)
112
+ if k.size > kernel_limit :
113
+ out[:] = astropy.convolution.convolve_fft(arr, k, normalize_kernel=False)
114
+ else :
115
+ out[:] = astropy.convolution.convolve(arr, k, boundary='extend', normalize_kernel=False)
116
+ _cached[method] = function
117
+ return function
118
+
119
+ func = funcs[method[:-1]]
120
+
121
+ if method.endswith("1") :
122
+ @njit(parallel=True)
123
+ def function(arr, k, out):
124
+ xlim, = np.shape(arr)
125
+ xp, = np.shape(k)
126
+ xp = int(xp / 2)
127
+ sum = np.sum(k)
128
+ sizep = k.size
129
+ for i in prange(xlim):
130
+ x = i
131
+ xmin, xmax = max(0, x - xp), min(xlim, x + xp + 1)
132
+ n = (xmax - xmin)
133
+ if n != sizep :
134
+ ksum = np.sum(k[xmin - x + xp: xmax - x + xp])
135
+ if ksum != 0 :
136
+ n = n * sum / ksum
137
+ crop = arr[xmin:xmax] * k[xmin - x + xp: xmax - x + xp] * n
138
+ out[x] = func(crop)
139
+
140
+ elif method.endswith("2") :
141
+
142
+ @njit(parallel=True)
143
+ def function(arr, k, out):
144
+ ylim, xlim = np.shape(arr)
145
+ yp, xp = np.shape(k)
146
+ yp, xp = int(yp / 2),int(xp / 2)
147
+ sum = np.sum(k)
148
+ sizep = k.size
149
+ for y in prange(ylim):
150
+ for x in range(xlim) :
151
+ ymin, ymax = max(0, y - yp), min(ylim,y + yp + 1)
152
+ xmin, xmax = max(0, x - xp), min(xlim,x + xp + 1)
153
+ n = (ymax - ymin) * (xmax - xmin)
154
+ if n != sizep :
155
+ ksum = np.sum(k[ymin - y + yp: ymax - y + yp, xmin - x + xp: xmax - x + xp])
156
+ if ksum != 0 :
157
+ n = n * sum / ksum
158
+ crop = arr[ymin:ymax, xmin:xmax] * k[ymin - y + yp: ymax - y + yp, xmin - x + xp: xmax - x + xp] * n
159
+ out[y, x] = func(crop)
160
+
161
+ elif method.endswith("3") :
162
+
163
+ @njit(parallel=True)
164
+ def function(arr, k, out):
165
+ zlim, ylim, xlim = np.shape(arr)
166
+ zp, yp, xp = np.shape(k)
167
+ zp, yp, xp = int(zp / 2), int(yp / 2), int(xp / 2)
168
+ sum = np.sum(k)
169
+ sizep = k.size
170
+ for z in prange(zlim) :
171
+ for y in range(ylim) :
172
+ for x in range(xlim) :
173
+ zmin, zmax = max(0, z - zp), min(zlim, z + zp + 1)
174
+ ymin, ymax = max(0, y - yp), min(ylim, y + yp + 1)
175
+ xmin, xmax = max(0, x - xp), min(xlim, x + xp + 1)
176
+ n = (zmax - zmin) * (ymax - ymin) * (xmax - xmin)
177
+ if n != sizep :
178
+ ksum = np.sum(k[zmin - z + zp: zmax - z + zp, ymin - y + yp: ymax - y + yp, xmin - x + xp: xmax - x + xp])
179
+ if ksum != 0 :
180
+ n = n * sum / ksum
181
+ crop = arr[zmin: zmax, ymin: ymax, xmin: xmax] * k[zmin - z + zp: zmax - z + zp, ymin - y + yp: ymax - y + yp, xmin - x + xp: xmax - x + xp] * n
182
+ out[z, y, x] = func(crop)
183
+
184
+ else :
185
+ raise SyntaxError(f'Correlation error cannot be {method}')
186
+ _cached[method] = function
187
+ return function
188
+
189
+
190
+
191
+ # %% Test function run
192
+ if __name__ == "__main__":
193
+ from corelp import test
194
+ test(__file__)
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : correlate
8
+
9
+ """
10
+ This file allows to test correlate
11
+
12
+ correlate : This function correlates two numpy arrays (1D, 2D, 3D).
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import debug
19
+ import pytest
20
+ from matplotlib import pyplot as plt
21
+ from arrlp import kernel, correlate
22
+ debug_folder = debug(__file__)
23
+
24
+
25
+
26
+ # %% Function test
27
+ @pytest.mark.parametrize("kwargs, title", [
28
+ ({"window":10}, "Mask"),
29
+ ({"sigma":5}, "Gaussian"),
30
+ ({"wl":640, "NA":1.5}, "Airy"),
31
+ ])
32
+ def test_function(kwargs, title) :
33
+ '''
34
+ Test correlate values
35
+ '''
36
+
37
+ img = kernel(pixel=5, window=1000)
38
+
39
+ plt.figure()
40
+ plt.imshow(img, cmap="hot")
41
+ plt.tight_layout()
42
+ plt.colorbar()
43
+ plt.savefig(debug_folder / f"{title}_before.png")
44
+
45
+ out = correlate(img, pixel=5, **kwargs)
46
+
47
+ plt.figure()
48
+ plt.imshow(out, cmap="hot")
49
+ plt.tight_layout()
50
+ plt.colorbar()
51
+ plt.savefig(debug_folder / f"{title}.png")
52
+
53
+
54
+
55
+ # %% Test function run
56
+ if __name__ == "__main__":
57
+ from corelp import test
58
+ test(__file__)
File without changes
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : kernel
8
+
9
+ """
10
+ This funtion creates kernels small arrays, mainly for convolutions.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ from arrlp import coordinates
17
+ import numpy as np
18
+ from scipy.special import erf, erfinv, j1
19
+
20
+
21
+
22
+ # %% Function
23
+ def kernel(ndims=2, pixel=1, *, dtype=np.float32, atrou=0, window=None, sigma=None, wl=None, NA=None) :
24
+ '''
25
+ This funtion creates kernels small arrays, mainly for convolutions.
26
+
27
+ Parameters
28
+ ----------
29
+ ndims : int
30
+ Number of dimensions.
31
+ pixel : int
32
+ Size of pixel for each dimension.
33
+ dtype : np.dtype
34
+ data type of kernel.
35
+ atrou : int
36
+ Number of trou to put for atrou algorithm, None for normal kernel.
37
+ window : float
38
+ Size of window for mask kernel.
39
+ sigma : float
40
+ Size of sigma for gaussian kernel.
41
+ wl : float
42
+ wavelength for Airy kernel.
43
+ NA : float
44
+ Numerical Aperture for Airy kernel.
45
+
46
+ Returns
47
+ -------
48
+ k : np.array
49
+ kernel generated.
50
+
51
+ Raises
52
+ ------
53
+ SyntaxError
54
+ One keyword input must be put that defines type of kernel, if not will raise Error.
55
+
56
+ Examples
57
+ --------
58
+ >>> from arrlp import kernel
59
+ ...
60
+ >>> kernel(window=10) # Mask
61
+ >>> kernel(sigma=5) # Gaussian
62
+ >>> kernel(wl=640, NA=1.5) # Airy
63
+ '''
64
+
65
+ #kernel
66
+ if sigma is not None :
67
+ k = kernel_gaus(sigma, pixel, ndims)
68
+ elif window is not None :
69
+ k = kernel_mask(window, pixel, ndims)
70
+ elif wl is not None and NA is not None :
71
+ k = kernel_airy(wl, NA, pixel, ndims)
72
+ else :
73
+ raise SyntaxError('Kernel was not recognized')
74
+
75
+ #"à trou" kernel
76
+ if atrou is not None and atrou != 0 :
77
+ k = kernel_atrou(k, atrou)
78
+
79
+ k = k.astype(dtype)
80
+ k /= k.sum()
81
+ return k
82
+
83
+
84
+
85
+ def kernel_gaus(sigma, pixel=1, ndims=1, tol_zero=1/100) :
86
+ '''gets a n dimensions gaussian kernel'''
87
+
88
+ # Coordinates
89
+ sigma = np.asarray(iterable(sigma, ndims))
90
+ pixel = np.asarray(iterable(pixel, ndims))
91
+ shape = np.ceil(sigma * 6 / pixel)
92
+ shape = tuple((shape + 1 - shape % 2).astype(int)) #odd number shape
93
+ coords = coordinates(shape, center=True, pixel=pixel)
94
+
95
+ # Apply Gaussian on each dimension and calculates distance from origin in sigmas
96
+ k = 1
97
+ r2 = 0 # (r/sigma)²
98
+ for coord, sig, pix in zip(coords, sigma, pixel) :
99
+ mini = (coord - pix/2) / np.sqrt(2) / sig
100
+ maxi = (coord + pix/2) / np.sqrt(2) / sig
101
+ k = k * ((erf(mini) - erf(maxi)) / 2)
102
+ r2 = r2 + (coord / sig)**2
103
+
104
+ # Apply mask
105
+ mask = r2 > 2 * erfinv((1 - tol_zero)**(1/ndims))**2 # r² > (n**(1/ndims)*sqrt(2))**2
106
+ k[mask] = 0
107
+ return k
108
+
109
+
110
+
111
+ def kernel_mask(window, pixel=1, ndims=1) :
112
+ '''gets a n dimensions mask kernel'''
113
+
114
+ # Coordinates
115
+ window = np.asarray(iterable(window, ndims))
116
+ pixel = np.asarray(iterable(pixel, ndims))
117
+ shape = np.ceil(window / pixel)
118
+ shape = tuple((shape + 1 - shape % 2).astype(int)) #odd number shape
119
+ coords = coordinates(shape, center=True, pixel=pixel)
120
+
121
+ # Calculates distance from origin in windows
122
+ k = np.ones(shape)
123
+ r2 = 0 # (r/window)²
124
+ for coord, win, pix in zip(coords, window, pixel) :
125
+ r2 = r2 + (coord / win)**2
126
+
127
+ # Apply mask
128
+ mask = r2 > 1/4 #r² > (1/2)**2
129
+ k[mask] = 0
130
+ return k
131
+
132
+
133
+
134
+ def kernel_airy(wl, NA, pixel=1, ndims=1) :
135
+ '''gets a n dimensions mask kernel'''
136
+
137
+ # Coordinates
138
+ pixel = np.asarray(iterable(pixel, ndims))
139
+ shape = np.ceil((2.44 * wl / NA) / pixel)
140
+ shape = tuple((shape + 1 - shape % 2).astype(int)) #odd number shape
141
+ coords = coordinates(shape, center=True, pixel=pixel)
142
+
143
+ # Calculates distance from origin in nm and pixel apply Airy function
144
+ r2 = 0 # r²
145
+ for coord in coords :
146
+ r2 = r2 + coord**2
147
+ r = np.sqrt(r2)
148
+ z = 2 * np.pi * r * NA / wl
149
+ ma = r < 1 # < 1 nm
150
+ k = np.empty(shape, dtype=np.float32)
151
+ k[~ma] = (2 * j1(z[~ma]) / z[~ma])**2
152
+ k[ma] = 1
153
+
154
+ # Apply mask
155
+ mask = r2 > (1.22 * wl / NA)**2 #r² > (1.22 * wl/NA)**2
156
+ k[mask] = 0
157
+ return k
158
+
159
+
160
+
161
+ def kernel_atrou(k, order) :
162
+ if order == 0 :
163
+ return k
164
+ shape = k.shape
165
+ newshape = tuple([s + (s - 1) * order for s in shape])
166
+ newkernel = np.zeros(newshape)
167
+ slices = [slice(None,None,order+1) for _ in range(k.ndim)]
168
+ newkernel[*slices] = k
169
+ return newkernel
170
+
171
+
172
+
173
+ def iterable(arg, ndims) :
174
+ try :
175
+ if len(arg) != ndims :
176
+ raise ValueError('Argument does not have good number of dimensions')
177
+ else :
178
+ return arg
179
+ except TypeError :
180
+ return (arg,) * ndims
181
+
182
+
183
+
184
+ # %% Test function run
185
+ if __name__ == "__main__":
186
+ from corelp import test
187
+ test(__file__)
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-30
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : kernel
8
+
9
+ """
10
+ This file allows to test kernel
11
+
12
+ kernel : This funtion creates kernels small arrays, mainly for convolutions.
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import debug
19
+ import pytest
20
+ from matplotlib import pyplot as plt
21
+ import numpy as np
22
+ from arrlp import kernel
23
+ debug_folder = debug(__file__)
24
+
25
+
26
+
27
+ # %% Function test
28
+ @pytest.mark.parametrize("kwargs, title", [
29
+ ({"window":500}, "Mask"),
30
+ ({"sigma":250}, "Gaussian"),
31
+ ({"wl":640, "NA":1.5}, "Airy"),
32
+ ])
33
+ def test_function(kwargs, title) :
34
+ '''
35
+ Test kernel values
36
+ '''
37
+ k = kernel(pixel=10, **kwargs)
38
+ assert np.isclose(k.sum(), np.float32(1.))
39
+ plt.figure()
40
+ plt.imshow(k, cmap="hot")
41
+ plt.tight_layout()
42
+ plt.colorbar()
43
+ plt.savefig(debug_folder / f"{title}.png")
44
+
45
+
46
+
47
+ # %% Test function run
48
+ if __name__ == "__main__":
49
+ from corelp import test
50
+ test(__file__)
File without changes
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-31
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : relabel
8
+
9
+ """
10
+ This function redefines a label array to fill potential holes inside.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+
17
+ import numpy as np
18
+ from numba import njit
19
+
20
+ @njit()
21
+ def relabel(array):
22
+ '''
23
+ This function redefines a label array to fill potential holes inside.
24
+
25
+ Parameters
26
+ ----------
27
+ labels : np.ndarray
28
+ array of labels (ints where each number > 0 corresponds to one object).
29
+
30
+ Returns
31
+ -------
32
+ labels : np.ndarray
33
+ 1D version of the corrected labels array.
34
+
35
+ Examples
36
+ --------
37
+ >>> from arrlp import relabel
38
+ ...
39
+ >>> relabel(labels)
40
+ '''
41
+
42
+ array = array.ravel() # Flatten array
43
+ unique_values = np.unique(array) # Get sorted unique values
44
+ mapping = np.zeros(unique_values[-1] + 1, dtype=np.uint32) # Create mapping array
45
+
46
+ # Assign new sequential labels
47
+ for new_label, old_value in enumerate(unique_values):
48
+ mapping[old_value] = new_label # Map old value to new one
49
+
50
+ # Apply relabeling
51
+ for i in range(len(array)):
52
+ array[i] = mapping[array[i]]
53
+
54
+ return array.reshape((-1,)) # Return as 1D array
55
+
56
+
57
+ # %% Test function run
58
+ if __name__ == "__main__":
59
+ from corelp import test
60
+ test(__file__)
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-31
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : arrLP
7
+ # Module : relabel
8
+
9
+ """
10
+ This file allows to test relabel
11
+
12
+ relabel : This function redefines a label array to fill potential holes inside.
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import print, debug
19
+ import pytest
20
+ from arrlp import relabel
21
+ debug_folder = debug(__file__)
22
+
23
+
24
+
25
+ # %% Function test
26
+ def test_function() :
27
+ '''
28
+ Test relabel function
29
+ '''
30
+ print('Hello world!')
31
+
32
+
33
+
34
+ # %% Test function run
35
+ if __name__ == "__main__":
36
+ from corelp import test
37
+ test(__file__)
@@ -0,0 +1,38 @@
1
+ {
2
+ "compress": {
3
+ "date": "2025-11-30",
4
+ "description": "Compresses an array between values by normalizing, with possibility to saturate extrema.",
5
+ "module": "modules/compress_LP/compress",
6
+ "object": "compress"
7
+ },
8
+ "convolve": {
9
+ "date": "2025-08-30",
10
+ "description": "This function convolves two numpy arrays (1D, 2D, 3D).",
11
+ "module": "modules/convolve_LP/convolve",
12
+ "object": "convolve"
13
+ },
14
+ "coordinates": {
15
+ "date": "2025-08-30",
16
+ "description": "This function will return a ndarray corresponding to the coordinates array of the input.",
17
+ "module": "modules/coordinates_LP/coordinates",
18
+ "object": "coordinates"
19
+ },
20
+ "correlate": {
21
+ "date": "2025-08-30",
22
+ "description": "This function correlates two numpy arrays (1D, 2D, 3D).",
23
+ "module": "modules/correlate_LP/correlate",
24
+ "object": "correlate"
25
+ },
26
+ "kernel": {
27
+ "date": "2025-08-30",
28
+ "description": "This funtion creates kernels small arrays, mainly for convolutions.",
29
+ "module": "modules/kernel_LP/kernel",
30
+ "object": "kernel"
31
+ },
32
+ "relabel": {
33
+ "date": "2025-08-31",
34
+ "description": "This function redefines a label array to fill potential holes inside.",
35
+ "module": "modules/relabel_LP/relabel",
36
+ "object": "relabel"
37
+ }
38
+ }
File without changes
File without changes
@@ -0,0 +1 @@
1
+ {}