ndslice 0.1.0__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.
- ndslice-0.1.0/LICENSE +21 -0
- ndslice-0.1.0/PKG-INFO +113 -0
- ndslice-0.1.0/README.md +80 -0
- ndslice-0.1.0/ndslice/__init__.py +9 -0
- ndslice-0.1.0/ndslice/imageview2d.py +266 -0
- ndslice-0.1.0/ndslice/ndslice.py +791 -0
- ndslice-0.1.0/ndslice.egg-info/PKG-INFO +113 -0
- ndslice-0.1.0/ndslice.egg-info/SOURCES.txt +11 -0
- ndslice-0.1.0/ndslice.egg-info/dependency_links.txt +1 -0
- ndslice-0.1.0/ndslice.egg-info/requires.txt +7 -0
- ndslice-0.1.0/ndslice.egg-info/top_level.txt +1 -0
- ndslice-0.1.0/pyproject.toml +48 -0
- ndslice-0.1.0/setup.cfg +4 -0
ndslice-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 [Your Name]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ndslice-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ndslice
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive N-dimensional numpy array viewer with FFT support
|
|
5
|
+
Author-email: Henric Rydén <henric.ryden@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/henricryden/ndslice
|
|
8
|
+
Project-URL: Documentation, https://github.com/henricryden/ndslice#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/henricryden/ndslice
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/henricryden/ndslice/issues
|
|
11
|
+
Keywords: visualization,numpy,fft,image-viewer,data-visualization
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: numpy>=1.20.0
|
|
27
|
+
Requires-Dist: pyqtgraph>=0.12.0
|
|
28
|
+
Requires-Dist: PyQt5>=5.15.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# ndslice
|
|
35
|
+
|
|
36
|
+
Interactive N-dimensional array viewer with FFT support for NumPy arrays.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **N-dimensional slicing**: View any 2D slice of your N-dimensional data
|
|
41
|
+
- **Multiple view modes**: Image view and line plot modes
|
|
42
|
+
- **FFT/IFFT support**: Apply Fourier transforms along any dimension with a click
|
|
43
|
+
- **Complex data support**: View real, imaginary, magnitude, or phase components
|
|
44
|
+
- **Scale transformations**: Linear and symmetric logarithmic scaling
|
|
45
|
+
- **Interactive controls**: Mouse hover for pixel values, dynamic zooming, and panning
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
### From PyPI
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install ndslice
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### From source
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/henricryden/ndslice.git
|
|
59
|
+
cd ndslice
|
|
60
|
+
pip install -e .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
### Basic Usage
|
|
66
|
+
|
|
67
|
+
The `ndslice()` function opens an interactive window to explore your N-dimensional arrays:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from ndslice import ndslice
|
|
71
|
+
import numpy as np
|
|
72
|
+
|
|
73
|
+
# View a 4D array
|
|
74
|
+
data_4d = np.random.randn(10, 20, 30, 40)
|
|
75
|
+
ndslice(data_4d)
|
|
76
|
+
|
|
77
|
+
# View complex FFT data
|
|
78
|
+
fft_data = np.fft.fftn(data_4d)
|
|
79
|
+
ndslice(fft_data, title='FFT Data')
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Interactive Features
|
|
83
|
+
|
|
84
|
+
- **Dimension Selection**: Click Y/X buttons to choose which dimensions to display
|
|
85
|
+
- **Slicing**: Use spinboxes to select the slice index for other dimensions
|
|
86
|
+
- **FFT Transforms**:
|
|
87
|
+
- Left-click dimension labels to apply FFT
|
|
88
|
+
- Right-click to apply inverse FFT
|
|
89
|
+
- Click again to return to native domain
|
|
90
|
+
- **Channel Selection** (for complex data): Real, Imaginary, Magnitude, or Phase
|
|
91
|
+
- **Scale Options**: Linear or Symmetric Log scaling
|
|
92
|
+
- **Display Modes**: Square pixels, square FOV, or fit to window
|
|
93
|
+
- **View Modes**: Switch between 2D image view and 1D line plot
|
|
94
|
+
- **Complex Data**: View real, imaginary, magnitude, or phase components
|
|
95
|
+
|
|
96
|
+
## Requirements
|
|
97
|
+
|
|
98
|
+
- Python >= 3.8
|
|
99
|
+
- NumPy >= 1.20.0
|
|
100
|
+
- PyQtGraph >= 0.12.0
|
|
101
|
+
- PyQt5 >= 5.15.0
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
MIT License - see LICENSE file for details.
|
|
106
|
+
|
|
107
|
+
## Contributing
|
|
108
|
+
|
|
109
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
110
|
+
|
|
111
|
+
## Acknowledgments
|
|
112
|
+
|
|
113
|
+
Built with [PyQtGraph](https://www.pyqtgraph.org/) for high-performance visualization.
|
ndslice-0.1.0/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ndslice
|
|
2
|
+
|
|
3
|
+
Interactive N-dimensional array viewer with FFT support for NumPy arrays.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **N-dimensional slicing**: View any 2D slice of your N-dimensional data
|
|
8
|
+
- **Multiple view modes**: Image view and line plot modes
|
|
9
|
+
- **FFT/IFFT support**: Apply Fourier transforms along any dimension with a click
|
|
10
|
+
- **Complex data support**: View real, imaginary, magnitude, or phase components
|
|
11
|
+
- **Scale transformations**: Linear and symmetric logarithmic scaling
|
|
12
|
+
- **Interactive controls**: Mouse hover for pixel values, dynamic zooming, and panning
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### From PyPI
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install ndslice
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### From source
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/henricryden/ndslice.git
|
|
26
|
+
cd ndslice
|
|
27
|
+
pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Basic Usage
|
|
33
|
+
|
|
34
|
+
The `ndslice()` function opens an interactive window to explore your N-dimensional arrays:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from ndslice import ndslice
|
|
38
|
+
import numpy as np
|
|
39
|
+
|
|
40
|
+
# View a 4D array
|
|
41
|
+
data_4d = np.random.randn(10, 20, 30, 40)
|
|
42
|
+
ndslice(data_4d)
|
|
43
|
+
|
|
44
|
+
# View complex FFT data
|
|
45
|
+
fft_data = np.fft.fftn(data_4d)
|
|
46
|
+
ndslice(fft_data, title='FFT Data')
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Interactive Features
|
|
50
|
+
|
|
51
|
+
- **Dimension Selection**: Click Y/X buttons to choose which dimensions to display
|
|
52
|
+
- **Slicing**: Use spinboxes to select the slice index for other dimensions
|
|
53
|
+
- **FFT Transforms**:
|
|
54
|
+
- Left-click dimension labels to apply FFT
|
|
55
|
+
- Right-click to apply inverse FFT
|
|
56
|
+
- Click again to return to native domain
|
|
57
|
+
- **Channel Selection** (for complex data): Real, Imaginary, Magnitude, or Phase
|
|
58
|
+
- **Scale Options**: Linear or Symmetric Log scaling
|
|
59
|
+
- **Display Modes**: Square pixels, square FOV, or fit to window
|
|
60
|
+
- **View Modes**: Switch between 2D image view and 1D line plot
|
|
61
|
+
- **Complex Data**: View real, imaginary, magnitude, or phase components
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- Python >= 3.8
|
|
66
|
+
- NumPy >= 1.20.0
|
|
67
|
+
- PyQtGraph >= 0.12.0
|
|
68
|
+
- PyQt5 >= 5.15.0
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT License - see LICENSE file for details.
|
|
73
|
+
|
|
74
|
+
## Contributing
|
|
75
|
+
|
|
76
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
77
|
+
|
|
78
|
+
## Acknowledgments
|
|
79
|
+
|
|
80
|
+
Built with [PyQtGraph](https://www.pyqtgraph.org/) for high-performance visualization.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ndslice - Interactive N-dimensional array viewer with FFT support
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .ndslice import ndslice, NDSliceWindow, Domain
|
|
6
|
+
from .imageview2d import ImageView2D
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
__all__ = ["ndslice", "NDSliceWindow", "ImageView2D", "Domain"]
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pyqtgraph.Qt import QtGui, QtWidgets
|
|
3
|
+
import pyqtgraph as pg
|
|
4
|
+
from pyqtgraph.graphicsItems.ImageItem import ImageItem
|
|
5
|
+
from pyqtgraph.graphicsItems.ViewBox import ViewBox
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ImageView2D(QtWidgets.QWidget):
|
|
9
|
+
"""
|
|
10
|
+
Simplified widget for displaying 2D image data.
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- 2D image display via ImageItem
|
|
14
|
+
- Zoom/pan via ViewBox
|
|
15
|
+
- Histogram with level controls
|
|
16
|
+
- Auto-ranging and level adjustment
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, parent=None, view=None, imageItem=None):
|
|
20
|
+
"""
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
parent : QWidget
|
|
24
|
+
Parent widget
|
|
25
|
+
view : ViewBox
|
|
26
|
+
If specified, this ViewBox will be used for display
|
|
27
|
+
imageItem : ImageItem
|
|
28
|
+
If specified, this ImageItem will be used for display
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(parent)
|
|
31
|
+
|
|
32
|
+
self.image = None
|
|
33
|
+
self.imageDisp = None
|
|
34
|
+
self.levelMin = None
|
|
35
|
+
self.levelMax = None
|
|
36
|
+
self.displayMode = 'square_pixels' # Default to square pixels
|
|
37
|
+
|
|
38
|
+
# Create the UI layout
|
|
39
|
+
self.setupUI()
|
|
40
|
+
|
|
41
|
+
# Create view if not provided
|
|
42
|
+
if view is None:
|
|
43
|
+
self.view = ViewBox()
|
|
44
|
+
else:
|
|
45
|
+
self.view = view
|
|
46
|
+
self.graphicsView.setCentralItem(self.view)
|
|
47
|
+
self.view.setAspectLocked(True)
|
|
48
|
+
self.view.invertY()
|
|
49
|
+
|
|
50
|
+
# Create image item if not provided
|
|
51
|
+
if imageItem is None:
|
|
52
|
+
self.imageItem = ImageItem()
|
|
53
|
+
else:
|
|
54
|
+
self.imageItem = imageItem
|
|
55
|
+
self.view.addItem(self.imageItem)
|
|
56
|
+
|
|
57
|
+
# Setup histogram
|
|
58
|
+
self.histogram.setImageItem(self.imageItem)
|
|
59
|
+
self.histogram.setLevelMode('mono') # Force mono mode for scalar values
|
|
60
|
+
|
|
61
|
+
# Initialize levels
|
|
62
|
+
self.levelMin = 0.0
|
|
63
|
+
self.levelMax = 1.0
|
|
64
|
+
|
|
65
|
+
def setupUI(self):
|
|
66
|
+
"""Create the user interface"""
|
|
67
|
+
# Main layout
|
|
68
|
+
self.layout = QtWidgets.QHBoxLayout(self)
|
|
69
|
+
self.layout.setContentsMargins(0, 0, 0, 0)
|
|
70
|
+
self.layout.setSpacing(0)
|
|
71
|
+
|
|
72
|
+
# Graphics view for image display
|
|
73
|
+
self.graphicsView = pg.GraphicsView()
|
|
74
|
+
self.layout.addWidget(self.graphicsView, 1) # Give it most of the space
|
|
75
|
+
|
|
76
|
+
# Histogram widget
|
|
77
|
+
self.histogram = pg.HistogramLUTWidget()
|
|
78
|
+
self.layout.addWidget(self.histogram)
|
|
79
|
+
|
|
80
|
+
def setImage(self, img, autoRange=True, autoLevels=True, levels=None,
|
|
81
|
+
pos=None, scale=None, transform=None, autoHistogramRange=True):
|
|
82
|
+
"""
|
|
83
|
+
Set the image to be displayed.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
img : np.ndarray
|
|
88
|
+
2D image data to display
|
|
89
|
+
autoRange : bool
|
|
90
|
+
Whether to auto-scale the view to fit the image
|
|
91
|
+
autoLevels : bool
|
|
92
|
+
Whether to auto-adjust the histogram levels
|
|
93
|
+
levels : tuple
|
|
94
|
+
(min, max) levels for the histogram
|
|
95
|
+
pos : tuple
|
|
96
|
+
Position offset for the image
|
|
97
|
+
scale : tuple
|
|
98
|
+
Scale factors for the image
|
|
99
|
+
transform : QTransform
|
|
100
|
+
Transform to apply to the image
|
|
101
|
+
autoHistogramRange : bool
|
|
102
|
+
Whether to auto-scale the histogram range
|
|
103
|
+
"""
|
|
104
|
+
if not isinstance(img, np.ndarray):
|
|
105
|
+
raise TypeError("Image must be a numpy array")
|
|
106
|
+
|
|
107
|
+
if img.ndim != 2:
|
|
108
|
+
raise ValueError("ImageView2D only supports 2D images")
|
|
109
|
+
|
|
110
|
+
self.image = img
|
|
111
|
+
self.imageDisp = None
|
|
112
|
+
|
|
113
|
+
# Update the image display
|
|
114
|
+
self.updateImage(autoHistogramRange=autoHistogramRange)
|
|
115
|
+
|
|
116
|
+
# Set levels
|
|
117
|
+
if levels is None and autoLevels:
|
|
118
|
+
self.autoLevels()
|
|
119
|
+
elif levels is not None:
|
|
120
|
+
if isinstance(levels, (list, tuple)) and len(levels) == 2:
|
|
121
|
+
self.setLevels(levels[0], levels[1])
|
|
122
|
+
else:
|
|
123
|
+
self.setLevels(*levels)
|
|
124
|
+
|
|
125
|
+
# Set transform
|
|
126
|
+
if transform is None:
|
|
127
|
+
if pos is not None or scale is not None:
|
|
128
|
+
if pos is None:
|
|
129
|
+
pos = (0, 0)
|
|
130
|
+
if scale is None:
|
|
131
|
+
scale = (1, 1)
|
|
132
|
+
transform = QtGui.QTransform()
|
|
133
|
+
transform.translate(pos[0], pos[1])
|
|
134
|
+
transform.scale(scale[0], scale[1])
|
|
135
|
+
|
|
136
|
+
if transform is not None:
|
|
137
|
+
self.imageItem.setTransform(transform)
|
|
138
|
+
|
|
139
|
+
# Update aspect ratio based on display mode
|
|
140
|
+
self._updateAspectRatio()
|
|
141
|
+
|
|
142
|
+
# Auto range the view
|
|
143
|
+
if autoRange:
|
|
144
|
+
self.autoRange()
|
|
145
|
+
|
|
146
|
+
def updateImage(self, autoHistogramRange=True):
|
|
147
|
+
"""Update the displayed image"""
|
|
148
|
+
if self.image is None:
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
# For 2D images, we can display directly
|
|
152
|
+
self.imageDisp = self.image
|
|
153
|
+
|
|
154
|
+
# Calculate min/max levels from the image data for histogram
|
|
155
|
+
self._updateImageLevels()
|
|
156
|
+
|
|
157
|
+
# Set the image data
|
|
158
|
+
self.imageItem.setImage(self.imageDisp, autoLevels=False)
|
|
159
|
+
|
|
160
|
+
# Update histogram range if requested
|
|
161
|
+
if autoHistogramRange:
|
|
162
|
+
self.histogram.setHistogramRange(self.levelMin, self.levelMax)
|
|
163
|
+
|
|
164
|
+
def autoRange(self):
|
|
165
|
+
"""Auto scale and pan the view to fit the image"""
|
|
166
|
+
if self.imageDisp is not None:
|
|
167
|
+
self.view.autoRange()
|
|
168
|
+
|
|
169
|
+
def _updateImageLevels(self):
|
|
170
|
+
"""Update the min/max levels from the current image data"""
|
|
171
|
+
if self.imageDisp is not None:
|
|
172
|
+
# Use the same approach as the original ImageView
|
|
173
|
+
finite_data = self.imageDisp[np.isfinite(self.imageDisp)]
|
|
174
|
+
if len(finite_data) > 0:
|
|
175
|
+
self.levelMin = float(np.min(finite_data))
|
|
176
|
+
self.levelMax = float(np.max(finite_data))
|
|
177
|
+
else:
|
|
178
|
+
self.levelMin = 0.0
|
|
179
|
+
self.levelMax = 1.0
|
|
180
|
+
|
|
181
|
+
def autoLevels(self):
|
|
182
|
+
"""Automatically set the histogram levels based on image data"""
|
|
183
|
+
if self.imageDisp is not None:
|
|
184
|
+
self._updateImageLevels()
|
|
185
|
+
self.setLevels(self.levelMin, self.levelMax)
|
|
186
|
+
|
|
187
|
+
def setLevels(self, min_level, max_level):
|
|
188
|
+
"""Set the histogram levels"""
|
|
189
|
+
self.histogram.setLevels(min_level, max_level)
|
|
190
|
+
|
|
191
|
+
def getLevels(self):
|
|
192
|
+
"""Get the current histogram levels"""
|
|
193
|
+
return self.histogram.getLevels()
|
|
194
|
+
|
|
195
|
+
def setHistogramRange(self, min_val, max_val):
|
|
196
|
+
"""Set the range of the histogram"""
|
|
197
|
+
self.histogram.setHistogramRange(min_val, max_val)
|
|
198
|
+
|
|
199
|
+
def getProcessedImage(self):
|
|
200
|
+
"""Get the processed image data"""
|
|
201
|
+
return self.imageDisp
|
|
202
|
+
|
|
203
|
+
def getView(self):
|
|
204
|
+
"""Get the ViewBox containing the image"""
|
|
205
|
+
return self.view
|
|
206
|
+
|
|
207
|
+
def getImageItem(self):
|
|
208
|
+
"""Get the ImageItem"""
|
|
209
|
+
return self.imageItem
|
|
210
|
+
|
|
211
|
+
def getHistogramWidget(self):
|
|
212
|
+
"""Get the histogram widget"""
|
|
213
|
+
return self.histogram
|
|
214
|
+
|
|
215
|
+
def clear(self):
|
|
216
|
+
"""Clear the displayed image"""
|
|
217
|
+
self.image = None
|
|
218
|
+
self.imageDisp = None
|
|
219
|
+
self.imageItem.clear()
|
|
220
|
+
|
|
221
|
+
def setColorMap(self, colormap):
|
|
222
|
+
"""Set the color map for the histogram"""
|
|
223
|
+
self.histogram.gradient.setColorMap(colormap)
|
|
224
|
+
|
|
225
|
+
def setDisplayMode(self, mode):
|
|
226
|
+
"""Set the display mode.
|
|
227
|
+
|
|
228
|
+
Modes:
|
|
229
|
+
- 'square_pixels': force square pixel display (aspect ratio 1.0)
|
|
230
|
+
- 'square_fov' : lock aspect ratio to image width/height (field of view square)
|
|
231
|
+
- 'fit' : allow non-uniform scaling so the entire image fits viewport
|
|
232
|
+
"""
|
|
233
|
+
if mode not in ('square_pixels', 'square_fov', 'fit'):
|
|
234
|
+
raise ValueError(f"Unknown display mode: {mode}")
|
|
235
|
+
self.displayMode = mode
|
|
236
|
+
self._updateAspectRatio()
|
|
237
|
+
|
|
238
|
+
def _updateAspectRatio(self):
|
|
239
|
+
"""Update the aspect ratio based on display mode"""
|
|
240
|
+
if self.image is None:
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
if self.displayMode == 'square_pixels':
|
|
244
|
+
# Square pixels: maintain 1:1 aspect ratio
|
|
245
|
+
self.view.setAspectLocked(True, ratio=1.0)
|
|
246
|
+
elif self.displayMode == 'square_fov':
|
|
247
|
+
# Square FOV: adjust aspect ratio based on image dimensions
|
|
248
|
+
height, width = self.image.shape
|
|
249
|
+
aspect_ratio = width / height
|
|
250
|
+
self.view.setAspectLocked(True, ratio=aspect_ratio)
|
|
251
|
+
elif self.displayMode == 'fit':
|
|
252
|
+
# Fit: allow free aspect so the whole image fits inside the view box
|
|
253
|
+
self.view.setAspectLocked(False)
|
|
254
|
+
# Ensure view box ranges cover the image exactly
|
|
255
|
+
self.view.autoRange()
|
|
256
|
+
|
|
257
|
+
# Trigger a refresh of the view
|
|
258
|
+
if hasattr(self, 'imageItem') and self.imageItem is not None:
|
|
259
|
+
self.view.autoRange()
|
|
260
|
+
|
|
261
|
+
# --- Qt Events -----------------------------------------------------
|
|
262
|
+
def resizeEvent(self, event):
|
|
263
|
+
"""On resize, if in 'fit' mode keep the image fully visible."""
|
|
264
|
+
super().resizeEvent(event)
|
|
265
|
+
if self.displayMode == 'fit' and self.image is not None:
|
|
266
|
+
self.view.autoRange()
|