dct-toolkit 0.4.0__py3-none-any.whl

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.
@@ -0,0 +1,63 @@
1
+ """Public API for DCT-based convolution and statistical operations.
2
+
3
+ The public top-level surface is intentionally scoped to smoothing and
4
+ normalized-convolution statistics for the publication-focused release track.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ import numpy as np
10
+
11
+ from .core import dct_convolve_1d, get_dct_transfer_function
12
+ from .cartesian import smooth_cartesian
13
+ from .polar import smooth_polar
14
+ from .stats import dct_count, dct_mean, dct_prefill, dct_std, dct_variance
15
+
16
+ __version__ = "0.4.0"
17
+
18
+
19
+ __all__ = [
20
+ "__version__",
21
+ "get_dct_transfer_function",
22
+ "dct_convolve_1d",
23
+ "smooth_cartesian",
24
+ "smooth_polar",
25
+ "dct_smooth",
26
+ "dct_count",
27
+ "dct_mean",
28
+ "dct_prefill",
29
+ "dct_variance",
30
+ "dct_std",
31
+ ]
32
+
33
+
34
+ def dct_smooth(
35
+ data: np.ndarray,
36
+ width: float,
37
+ coordinates: str = "cartesian",
38
+ **kwargs: Any,
39
+ ) -> np.ndarray:
40
+ """
41
+ Apply DCT-based smoothing.
42
+
43
+ Parameters
44
+ ----------
45
+ data : np.ndarray
46
+ Input data.
47
+ width : float
48
+ Smoothing width.
49
+ coordinates : str, default='cartesian'
50
+ 'cartesian' or 'polar'.
51
+ **kwargs
52
+ Additional arguments (kernel_type, az_res_deg, etc.)
53
+
54
+ Returns
55
+ -------
56
+ smoothed : np.ndarray
57
+ Smoothed data.
58
+ """
59
+ if coordinates == "cartesian":
60
+ return smooth_cartesian(data, width, **kwargs)
61
+ if coordinates == "polar":
62
+ return smooth_polar(data, width_pixels=width, **kwargs)
63
+ raise ValueError(f"Unknown coordinates: {coordinates}")
@@ -0,0 +1,41 @@
1
+ """
2
+ 2D Cartesian Smoothing.
3
+
4
+ This module provides separable smoothing for N-D Cartesian data.
5
+ It applies 1D smoothing sequentially along each dimension.
6
+ """
7
+
8
+ import numpy as np
9
+ from .core import get_dct_transfer_function, dct_convolve_1d
10
+
11
+ def smooth_cartesian(data: np.ndarray, width: float, kernel_type: str = 'boxcar') -> np.ndarray:
12
+ """
13
+ Apply separable DCT smoothing to N-D Cartesian data.
14
+
15
+ The smoothing is applied sequentially along each axis using the same
16
+ kernel type and width (isotropic smoothing).
17
+
18
+ Parameters
19
+ ----------
20
+ data : np.ndarray
21
+ Input data array (any dimension).
22
+ width : float
23
+ Smoothing width.
24
+ kernel_type : str, default='boxcar'
25
+ Kernel type ('boxcar', 'boxcar_discrete', 'gaussian').
26
+
27
+ Returns
28
+ -------
29
+ smoothed : np.ndarray
30
+ Smoothed data with same shape as input.
31
+ """
32
+ result = data.copy()
33
+ ndim = data.ndim
34
+
35
+ # Apply 1D smoothing along each axis
36
+ for axis in range(ndim):
37
+ n = data.shape[axis]
38
+ H = get_dct_transfer_function(n, kernel_type, width)
39
+ result = dct_convolve_1d(result, H, axis=axis)
40
+
41
+ return result
dct_toolkit/core.py ADDED
@@ -0,0 +1,123 @@
1
+ """
2
+ Core DCT Primitives and Transfer Functions.
3
+
4
+ This module provides the low-level building blocks for DCT-based smoothing:
5
+ 1. Analytical and discrete transfer functions (H[k])
6
+ 2. 1D convolution primitive using DCT-II/Ortho
7
+ """
8
+
9
+ import numpy as np
10
+ import scipy.fft
11
+
12
+ def get_dct_transfer_function(n: int, kernel_type: str, width: float) -> np.ndarray:
13
+ """
14
+ Generate the 1D DCT Transfer Function H[k].
15
+
16
+ Parameters
17
+ ----------
18
+ n : int
19
+ Number of points.
20
+ kernel_type : str
21
+ 'boxcar', 'boxcar_discrete', or 'gaussian'.
22
+ width : float
23
+ Kernel width.
24
+ - boxcar: full width
25
+ - gaussian: equivalent boxcar width (sigma = width / sqrt(12))
26
+
27
+ Returns
28
+ -------
29
+ H : np.ndarray
30
+ Transfer function of shape (n,).
31
+ """
32
+ k = np.arange(n)
33
+
34
+ if kernel_type == 'boxcar':
35
+ # Analytical Boxcar: H = sin(W * theta) / (W * sin(theta))
36
+ # where theta = pi * k / (2*n)
37
+ theta_half = (np.pi * k) / (2 * n)
38
+
39
+ H = np.zeros(n)
40
+ H[0] = 1.0
41
+
42
+ mask = k > 0
43
+ if np.any(mask):
44
+ numerator = np.sin(width * theta_half[mask])
45
+ denominator = np.sin(theta_half[mask])
46
+
47
+ # Avoid division by zero
48
+ valid = np.abs(denominator) > 1e-15
49
+ safe_mask = np.zeros_like(mask)
50
+ safe_mask[mask] = valid
51
+
52
+ if np.any(safe_mask):
53
+ H[safe_mask] = numerator[safe_mask[mask]] / (denominator[safe_mask[mask]] * width)
54
+
55
+ return H
56
+
57
+ elif kernel_type == 'boxcar_discrete':
58
+ # Discrete Boxcar Sum
59
+ # H[k] = (1 + 2*sum_{j=1}^{m} cos(j*w_k)) / w_int
60
+ w_int = int(np.round(width))
61
+ if w_int < 1: w_int = 1
62
+ if w_int % 2 == 0: w_int += 1
63
+
64
+ w_k = (np.pi * k) / n
65
+ H = np.ones(n)
66
+ half_width = (w_int - 1) // 2
67
+
68
+ for j in range(1, half_width + 1):
69
+ H += 2 * np.cos(j * w_k)
70
+
71
+ H /= w_int
72
+ return H
73
+
74
+ elif kernel_type == 'gaussian':
75
+ # Gaussian: sigma = width / sqrt(12)
76
+ sigma = width / np.sqrt(12)
77
+ if sigma < 1e-9:
78
+ return np.ones(n)
79
+
80
+ # omega_k = pi * k / n
81
+ omega_k = (np.pi * k) / n
82
+ return np.exp(-0.5 * (omega_k * sigma)**2)
83
+
84
+ else:
85
+ raise ValueError(f"Unknown kernel type: {kernel_type}")
86
+
87
+ def dct_convolve_1d(data: np.ndarray, H: np.ndarray, axis: int = -1) -> np.ndarray:
88
+ """
89
+ Apply 1D DCT-based convolution.
90
+
91
+ Parameters
92
+ ----------
93
+ data : np.ndarray
94
+ Input data.
95
+ H : np.ndarray
96
+ Transfer function (must match data.shape[axis]).
97
+ axis : int
98
+ Axis to smooth along.
99
+
100
+ Returns
101
+ -------
102
+ smoothed : np.ndarray
103
+ Smoothed data.
104
+ """
105
+ # Validate shapes
106
+ if data.shape[axis] != len(H):
107
+ raise ValueError(f"Transfer function length {len(H)} does not match data dimension {data.shape[axis]}")
108
+
109
+ # Broadcast H to data shape
110
+ shape = [1] * data.ndim
111
+ shape[axis] = len(H)
112
+ H_reshaped = H.reshape(shape)
113
+
114
+ # Forward DCT (Type-II, Ortho)
115
+ X = scipy.fft.dct(data, axis=axis, type=2, norm='ortho')
116
+
117
+ # Apply transfer function
118
+ Y = X * H_reshaped
119
+
120
+ # Inverse DCT (Type-II, Ortho)
121
+ y = scipy.fft.idct(Y, axis=axis, type=2, norm='ortho')
122
+
123
+ return y