5

我正在尝试编写一小段代码,使用 matplotlib 以交互方式删除图像系列中的选定切片。我创建了一个“删除”按钮,它存储了在选择“更新”按钮时要删除的许多索引。但是,我目前无法重置滑块小部件的范围,即从 valmax 中删除已删除切片的数量。这个问题的pythonic解决方案是什么?

这是我的代码:

import dicom
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button

frame = 0
#store indices of slices to be deleted
delete_list = []


def main():

    data = np.random.rand(16,256,256)
    nframes = data.shape[0]

    raw_dicom_stack = []
    for x in range (nframes):
        raw_dicom_stack.append(data[x,:,:])

    #yframe = 0

    # Visualize it
    viewer = VolumeViewer(raw_dicom_stack, nframes)
    viewer.show()

class VolumeViewer(object):
    def __init__(self, raw_dicom_stack, nframes):

        global delete_list

        self.raw_dicom_stack = raw_dicom_stack
        self.nframes = nframes
        self.delete_list = delete_list

        # Setup the axes.
        self.fig, self.ax = plt.subplots()
        self.slider_ax = self.fig.add_axes([0.2, 0.03, 0.65, 0.03])
        self.delete_ax = self.fig.add_axes([0.85,0.84,0.1,0.04])
        self.update_ax = self.fig.add_axes([0.85,0.78,0.1,0.04])
        self.register_ax = self.fig.add_axes([0.85,0.72,0.1,0.04])
        self.add_ax = self.fig.add_axes([0.85,0.66,0.1,0.04])

        # Make the slider
        self.slider = Slider(self.slider_ax, 'Frame', 1, self.nframes, 
                            valinit=1, valfmt='%1d/{}'.format(self.nframes))
        self.slider.on_changed(self.update)

        #Make the buttons
        self.del_button = Button(self.delete_ax, 'Delete')
        self.del_button.on_clicked(self.delete)

        self.upd_button = Button(self.update_ax, 'Update')
        self.upd_button.on_clicked(self.img_update)

        self.reg_button = Button(self.register_ax, 'Register')

        self.add_button = Button(self.add_ax, "Add")

        # Plot the first slice of the image
        self.im = self.ax.imshow(np.array(raw_dicom_stack[0]))

    def update(self, value):
        global frame
        frame = int(np.round(value - 1))

        # Update the image data
        dat = np.array(self.raw_dicom_stack[frame])
        self.im.set_data(dat)

        # Reset the image scaling bounds (this may not be necessary for you)
        self.im.set_clim([dat.min(), dat.max()])

        # Redraw the plot
        self.fig.canvas.draw()

    def delete(self,event):
        global frame
        global delete_list

        delete_list.append(frame)
        print 'Frame %s has been added to list of slices to be deleted' %str(frame+1)
        print 'Please click update to delete these slices and show updated image series \n'

        #Remove duplicates from delete list

    def img_update(self,event):
        #function deletes image stacks and updates viewer
        global delete_list

        #Remove duplicates from list and sort into numerical order
        delete_list = list(set(delete_list))
        delete_list.sort()

        #Make sure delete_list is not empty
        if not delete_list:
            print "Delete list is empty, no slices to delete"
        #Loop through delete list in reverse numerical order and remove slices from series
        else:
            for i in reversed(delete_list):
                self.raw_dicom_stack.pop(i)
                print 'Slice %i removed from dicom series \n' %(i+1)

        #Can now remove contents from delete_list
        del delete_list[:]
        #Update slider range
        self.nframes =  len(self.raw_dicom_stack)


    def show(self):
        plt.show()

if __name__ == '__main__':
    main()
4

2 回答 2

12

为了更新滑块范围,您可以直接设置它的最小值和最大值,

slider.valmin = 3
slider.valmax = 7

为了反映滑块轴的这种变化,您需要设置轴的限制,

slider.ax.set_xlim(slider.valmin,slider.valmax)

一个完整的示例,输入任何数字会将valmin滑块的值更改为该值。

import matplotlib.pyplot as plt
import matplotlib.widgets

fig, (ax,sliderax) = plt.subplots(nrows=2,gridspec_kw=dict(height_ratios=[1,.05]))

ax.plot(range(11))
ax.set_xlim(5,None)
ax.set_title("Type number to set minimum slider value")
def update_range(val):
    ax.set_xlim(val,None)

def update_slider(evt):
    print(evt.key)
    try:
        val = int(evt.key)
        slider.valmin = val
        slider.ax.set_xlim(slider.valmin,None)
        if val > slider.val:
            slider.val=val
            update_range(val)
        fig.canvas.draw_idle()
    except:
        pass

slider=matplotlib.widgets.Slider(sliderax,"xlim",0,10,5)
slider.on_changed(update_range)

fig.canvas.mpl_connect('key_press_event', update_slider)

plt.show()
于 2017-11-28T17:37:22.697 回答
1

看起来滑块没有更新范围(api)的方法。我建议将滑块的范围设置为 [0,1] 并做

frame = int(self.nframes * value)

在一些相关的注释中,我会将frame 实例变量设为数据属性而不是全局变量(教程)。

于 2012-11-21T14:53:17.527 回答