Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit251d333

Browse files
authored
Merge pull request#242 from p-j-smith/feat/hist-bin-params
Add widgets for setting histogram bins
2 parents0501fab +8fe7c7f commit251d333

File tree

4 files changed

+96
-11
lines changed

4 files changed

+96
-11
lines changed

‎docs/changelog.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
=========
33

4+
2.1.0
5+
-----
6+
New features
7+
~~~~~~~~~~~~
8+
- Added a GUI element to manually set the number of bins in the histogram widgets.
9+
410
2.0.3
511
-----
612
Bug fixes

‎src/napari_matplotlib/histogram.py‎

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
fromnapari.layers._multiscale_dataimportMultiScaleData
99
fromqtpy.QtWidgetsimport (
1010
QComboBox,
11+
QFormLayout,
12+
QGroupBox,
1113
QLabel,
14+
QSpinBox,
1215
QVBoxLayout,
1316
QWidget,
1417
)
@@ -22,15 +25,32 @@
2225
_COLORS= {"r":"tab:red","g":"tab:green","b":"tab:blue"}
2326

2427

25-
def_get_bins(data:npt.NDArray[Any])->npt.NDArray[Any]:
28+
def_get_bins(
29+
data:npt.NDArray[Any],
30+
num_bins:int=100,
31+
)->npt.NDArray[Any]:
32+
"""Create evenly spaced bins with a given interval.
33+
34+
Parameters
35+
----------
36+
data : napari.layers.Layer.data
37+
Napari layer data.
38+
num_bins : integer, optional
39+
Number of evenly-spaced bins to create. Defaults to 100.
40+
41+
Returns
42+
-------
43+
bin_edges : numpy.ndarray
44+
Array of evenly spaced bin edges.
45+
"""
2646
ifdata.dtype.kindin {"i","u"}:
2747
# Make sure integer data types have integer sized bins
28-
step=np.ceil(np.ptp(data)/100)
48+
step=np.ceil(np.ptp(data)/num_bins)
2949
returnnp.arange(np.min(data),np.max(data)+step,step)
3050
else:
31-
# For other data types, just have 100 evenly spaced bins
32-
# (and101 bin edges)
33-
returnnp.linspace(np.min(data),np.max(data),101)
51+
# For other data types we can use exactly `num_bins` bins
52+
# (and`num_bins` + 1 bin edges)
53+
returnnp.linspace(np.min(data),np.max(data),num_bins+1)
3454

3555

3656
classHistogramWidget(SingleAxesWidget):
@@ -47,6 +67,30 @@ def __init__(
4767
parent:QWidget|None=None,
4868
):
4969
super().__init__(napari_viewer,parent=parent)
70+
71+
num_bins_widget=QSpinBox()
72+
num_bins_widget.setRange(1,100_000)
73+
num_bins_widget.setValue(101)
74+
num_bins_widget.setWrapping(False)
75+
num_bins_widget.setKeyboardTracking(False)
76+
77+
# Set bins widget layout
78+
bins_selection_layout=QFormLayout()
79+
bins_selection_layout.addRow("num bins",num_bins_widget)
80+
81+
# Group the widgets and add to main layout
82+
params_widget_group=QGroupBox("Params")
83+
params_widget_group_layout=QVBoxLayout()
84+
params_widget_group_layout.addLayout(bins_selection_layout)
85+
params_widget_group.setLayout(params_widget_group_layout)
86+
self.layout().addWidget(params_widget_group)
87+
88+
# Add callbacks
89+
num_bins_widget.valueChanged.connect(self._draw)
90+
91+
# Store widgets for later usage
92+
self.num_bins_widget=num_bins_widget
93+
5094
self._update_layers(None)
5195
self.viewer.events.theme.connect(self._on_napari_theme_changed)
5296

@@ -60,6 +104,13 @@ def on_update_layers(self) -> None:
60104
self._update_contrast_lims
61105
)
62106

107+
ifnotself.layers:
108+
return
109+
110+
# Reset the num bins based on new layer data
111+
layer_data=self._get_layer_data(self.layers[0])
112+
self._set_widget_nums_bins(data=layer_data)
113+
63114
def_update_contrast_lims(self)->None:
64115
forlim,lineinzip(
65116
self.layers[0].contrast_limits,self._contrast_lines,strict=False
@@ -68,11 +119,13 @@ def _update_contrast_lims(self) -> None:
68119

69120
self.figure.canvas.draw()
70121

71-
defdraw(self)->None:
72-
"""
73-
Clear the axes and histogram the currently selected layer/slice.
74-
"""
75-
layer:Image=self.layers[0]
122+
def_set_widget_nums_bins(self,data:npt.NDArray[Any])->None:
123+
"""Update num_bins widget with bins determined from the image data"""
124+
bins=_get_bins(data)
125+
self.num_bins_widget.setValue(bins.size-1)
126+
127+
def_get_layer_data(self,layer:napari.layers.Layer)->npt.NDArray[Any]:
128+
"""Get the data associated with a given layer"""
76129
data=layer.data
77130

78131
ifisinstance(layer.data,MultiScaleData):
@@ -87,9 +140,21 @@ def draw(self) -> None:
87140
# Read data into memory if it's a dask array
88141
data=np.asarray(data)
89142

143+
returndata
144+
145+
defdraw(self)->None:
146+
"""
147+
Clear the axes and histogram the currently selected layer/slice.
148+
"""
149+
layer:Image=self.layers[0]
150+
data=self._get_layer_data(layer)
151+
90152
# Important to calculate bins after slicing 3D data, to avoid reading
91153
# whole cube into memory.
92-
bins=_get_bins(data)
154+
bins=_get_bins(
155+
data,
156+
num_bins=self.num_bins_widget.value(),
157+
)
93158

94159
iflayer.rgb:
95160
# Histogram RGB channels independently
19.4 KB
Loading

‎src/napari_matplotlib/tests/test_histogram.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@
1010
)
1111

1212

13+
@pytest.mark.mpl_image_compare
14+
deftest_histogram_2D_bins(make_napari_viewer,astronaut_data):
15+
viewer=make_napari_viewer()
16+
viewer.theme="light"
17+
viewer.add_image(astronaut_data[0],**astronaut_data[1])
18+
widget=HistogramWidget(viewer)
19+
viewer.window.add_dock_widget(widget)
20+
widget.num_bins_widget.setValue(25)
21+
fig=widget.figure
22+
# Need to return a copy, as original figure is too eagerley garbage
23+
# collected by the widget
24+
returndeepcopy(fig)
25+
26+
1327
@pytest.mark.mpl_image_compare
1428
deftest_histogram_2D(make_napari_viewer,astronaut_data):
1529
viewer=make_napari_viewer()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp