source: sasview/calculatorview/src/sans/perspectives/calculator/image_viewer.py @ 3e001f9

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 3e001f9 was 3e001f9, checked in by Jae Cho <jhjcho@…>, 12 years ago

Added tools, imageviwer and qrange cursors

  • Property mode set to 100644
File size: 16.6 KB
Line 
1import os
2import sys
3import wx
4import numpy as np
5import matplotlib
6matplotlib.interactive(False)
7#Use the WxAgg back end. The Wx one takes too long to render
8matplotlib.use('WXAgg')
9from sans.guiframe.local_perspectives.plotting.SimplePlot import PlotFrame
10#import matplotlib.pyplot as plt
11import matplotlib.image as mpimg
12import matplotlib.colors as colors
13from sans.guiframe.events import StatusEvent
14from sans.perspectives.calculator.calculator_widgets import InputTextCtrl
15from sans.dataloader.data_info import Data2D
16from sans.dataloader.data_info import Detector
17from sans.dataloader.manipulations import reader2D_converter
18_BOX_WIDTH = 60
19if sys.platform.count("win32") > 0:
20    _DIALOG_WIDTH = 400
21else:
22    _DIALOG_WIDTH = 480
23
24class ImageView:
25    """
26    Open a file dialog to allow the user to select a given file.
27    Display the loaded data if available.
28    """
29    def __init__(self, parent=None):
30        """
31        Init
32        """
33        self.parent = parent
34       
35    def load(self):
36        """
37        load image files
38        """
39        parent = self.parent
40        if parent == None:
41            location = os.getcwd()
42        else:
43            location = parent._default_save_location
44        path_list = self.choose_data_file(location=location)
45        if path_list == None:
46            return
47        if len(path_list) >= 0 and not(path_list[0]is None):
48            if parent != None:
49                parent._default_save_location = os.path.dirname(path_list[0])
50        err_msg = ''
51        for file_path in path_list:
52            basename = os.path.basename(file_path)
53            _, extension = os.path.splitext(basename)
54            try:
55                img = mpimg.imread(file_path)
56                is_png = extension.lower() == '.png'
57                plot_frame = ImageFrame(parent, -1, basename, img)
58                plot_frame.Show(False)
59                #plot_frame.im_show(img)
60                ax = plot_frame.plotpanel
61                if not is_png:
62                    ax.subplot.set_ylim(ax.subplot.get_ylim()[::-1])
63                ax.subplot.set_xlabel('x [pixel]')
64                ax.subplot.set_ylabel('y [pixel]')
65                ax.figure.subplots_adjust(left=0.15, bottom=0.1, 
66                                          right=0.95, top=0.95)
67                plot_frame.SetTitle('Picture -- %s --'% basename)
68                plot_frame.Show(True)
69                if parent != None:
70                    parent.put_icon(plot_frame)
71            except:
72                err_msg += "Failed to load '%s'.\n"% basename
73        if err_msg:
74            if parent is not None:
75                wx.PostEvent(parent, StatusEvent(status=err_msg, info="error"))
76            else:
77                print err_msg
78
79       
80    def choose_data_file(self, location=None):
81        """
82        Open a file dialog to allow loading a file
83        """
84        parent = self.parent
85        path = None
86        if location == None:
87            location = os.getcwd()
88        wlist = ''
89        elist = ["All images (*.png, *.bmp, *.gif, *.jpg, *.tif, *.tiff) | \
90                *.png; *.bmp; *.gif; *.jpg; *.tif; *.tiff", 
91                "PNG files (*.png) | *.png", 
92                "BMP files (*.bmp) | *.bmp", 
93                "GIF files (*.gif) | *.gif",
94                "JPG files (*.jpg) | *.jpg",
95                "TIF files (*.tif) | *.tif",
96                "TIFF files (*.tiff) | *.tiff"]
97        wlist = '|'.join(elist)       
98        style = wx.OPEN|wx.FD_MULTIPLE
99        dlg = wx.FileDialog(parent, "Image Viewer: Choose a image file", 
100                            location, "", wlist, style=style)
101        if dlg.ShowModal() == wx.ID_OK:
102            path = dlg.GetPaths()
103        else:
104            return None
105        dlg.Destroy()
106        return path
107
108class ImageFrame(PlotFrame):
109    """
110    Frame for simple plot
111    """
112    def __init__(self, parent, id, title, image=None, scale='log_{10}', 
113                 size=wx.Size(550, 470)): 
114        """
115        comment
116        :Param data: image array got from imread() of matplotlib [narray]
117        :param parent: parent panel/container
118        """
119        # Initialize the Frame object
120        PlotFrame.__init__(self, parent, id, title, scale, size)
121        self.parent = parent
122        self.data = image
123        self.file_name = title
124                   
125        menu = wx.Menu()
126        id = wx.NewId()
127        item = wx.MenuItem(menu, id, "&Convert to Data")
128        menu.AppendItem(item)
129        wx.EVT_MENU(self, id, self.on_set_data)
130        self.menu_bar.Append(menu, "&Image")
131
132        menu_help = wx.Menu()
133        id = wx.NewId()
134        item = wx.MenuItem(menu_help, id, "&HowTo")
135        menu_help.AppendItem(item)
136        wx.EVT_MENU(self, id, self.on_help)
137        self.menu_bar.Append(menu_help, "&Help")
138       
139        self.SetMenuBar(self.menu_bar)
140        self.im_show(image)
141   
142    def on_set_data(self, event):
143        """
144        Rescale the x y range, make 2D data and send it to data explore
145        """
146        title = self.file_name
147        self.panel = SetDialog(parent=self, title=title, image=self.data)
148        self.panel.ShowModal()
149
150    def on_help(self, event):   
151        """
152        Image Viewer help panel
153        """
154        from sans.perspectives.calculator.help_panel import  HelpWindow
155        # Get models help model_function path
156        import sans.perspectives.calculator as calmedia
157
158        media = calmedia.get_data_path(media='media')
159        path = os.path.join(media,"load_image_help.html") 
160        name = "Image Viewer"
161        frame = HelpWindow(self, -1, title=' Help: Image Viewer', 
162                           pageToOpen=path, size=(640, 450))   
163        try: 
164            frame.splitter.DetachWindow(frame.lpanel)
165            # Display only the right side one
166            frame.lpanel.Hide() 
167            frame.Show(True)
168        except:
169            frame.Destroy() 
170            msg = 'Display Error\n'
171            info = "Info"
172            wx.MessageBox(msg, info)
173           
174class SetDialog(wx.Dialog):
175    """
176    Dialog for Data Set
177    """
178    def __init__(self, parent, id=-1, title="Convert to Data", image=None, 
179                 size=(_DIALOG_WIDTH, 270)):
180        wx.Dialog.__init__(self, parent, id, title, size)
181        # parent
182        self.parent = parent
183        self.base = parent.parent
184        self.title = title
185        self.image = np.array(image)
186        self.z_ctrl = None
187        self.xy_ctrls = []
188        self.is_png = self._get_is_png()
189        self._build_layout()
190        my_title = "Convert Image to Data - %s -"% self.title
191        self.SetTitle(my_title)
192        self.SetSize(size)
193   
194    def _get_is_png(self):
195        """
196        Get if the image file is png
197        """
198        _, extension = os.path.splitext(self.title)
199        return extension.lower() == '.png'
200           
201    def _build_layout(self):
202        """
203        Layout
204        """
205        vbox = wx.BoxSizer(wx.VERTICAL)
206        zbox = wx.BoxSizer(wx.HORIZONTAL)
207        xbox = wx.BoxSizer(wx.HORIZONTAL)
208        ybox = wx.BoxSizer(wx.HORIZONTAL)
209        btnbox = wx.BoxSizer(wx.VERTICAL)
210       
211        sb_title = wx.StaticBox(self, -1, 'Transform Axes')
212        boxsizer = wx.StaticBoxSizer(sb_title, wx.VERTICAL)
213        z_title = wx.StaticText(self, -1, 'z values (range: 0 - 255) to:')
214        ztime_title = wx.StaticText(self, -1, 'z *')
215        x_title = wx.StaticText(self, -1, 'x values from pixel # to:')
216        xmin_title = wx.StaticText(self, -1, 'xmin:')
217        xmax_title = wx.StaticText(self, -1, 'xmax:')
218        y_title = wx.StaticText(self, -1, 'y values from pixel # to:')
219        ymin_title = wx.StaticText(self, -1, 'ymin: ')
220        ymax_title = wx.StaticText(self, -1, 'ymax:')
221        z_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH , 20),
222                                style=wx.TE_PROCESS_ENTER)
223       
224        xmin_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
225                                style=wx.TE_PROCESS_ENTER)
226        xmax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
227                                style=wx.TE_PROCESS_ENTER)
228        ymin_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
229                                style=wx.TE_PROCESS_ENTER)
230        ymax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
231                                style=wx.TE_PROCESS_ENTER)
232        z_ctl.SetValue('1.0')
233        xmin_ctl.SetValue('-0.3')
234        xmax_ctl.SetValue('0.3')
235        ymin_ctl.SetValue('-0.3')
236        ymax_ctl.SetValue('0.3')
237        z_ctl.Bind(wx.EVT_TEXT, self._on_z_enter)
238        xmin_ctl.Bind(wx.EVT_TEXT, self._onparam)
239        xmax_ctl.Bind(wx.EVT_TEXT, self._onparam)
240        ymin_ctl.Bind(wx.EVT_TEXT, self._onparam)
241        ymax_ctl.Bind(wx.EVT_TEXT, self._onparam)
242        xbox.AddMany([(x_title , 0, wx.LEFT, 0), 
243                      (xmin_title , 0, wx.LEFT, 10), 
244                      (xmin_ctl , 0, wx.LEFT, 10), 
245                      (xmax_title , 0, wx.LEFT, 10), 
246                      (xmax_ctl , 0, wx.LEFT, 10)])
247        ybox.AddMany([(y_title , 0, wx.LEFT, 0), 
248                      (ymin_title , 0, wx.LEFT, 10), 
249                      (ymin_ctl , 0, wx.LEFT, 10), 
250                      (ymax_title , 0, wx.LEFT, 10), 
251                      (ymax_ctl , 0, wx.LEFT, 10)])     
252        zbox.AddMany([(z_title , 0, wx.LEFT, 0), 
253                      (ztime_title, 0, wx.LEFT, 10),
254                      (z_ctl , 0, wx.LEFT, 7), 
255                      ])   
256        msg = "The data rescaled will show up in the Data Explorer. \n"
257        msg += "*Note: Recommend to use an image with 8 bit Grey \n"
258        msg += "  scale (and with No. of pixels < 300 x 300).\n"
259        msg += "  Otherwise, z = 0.299R + 0.587G + 0.114B."
260        note_txt = wx.StaticText(self, -1, msg)
261        note_txt.SetForegroundColour("black")
262        hbox = wx.BoxSizer(wx.HORIZONTAL)
263        okButton = wx.Button(self, -1, 'OK')
264        okButton.Bind(wx.EVT_BUTTON, self.on_set)
265        cancelButton = wx.Button(self, -1, 'Cancel')
266        cancelButton.Bind(wx.EVT_BUTTON, self.OnClose)
267        btnbox.Add(okButton, 0, wx.LEFT | wx.BOTTOM, 5)
268        btnbox.Add(cancelButton, 0, wx.LEFT | wx.TOP, 5)
269        hbox.Add(note_txt, 0, wx.LEFT, 5)
270        hbox.Add(btnbox, 0, wx.LEFT, 15)
271        vbox.Add((10, 15))
272        boxsizer.Add(xbox, 1, wx.LEFT | wx.BOTTOM, 5)
273        boxsizer.Add(ybox, 1, wx.LEFT | wx.BOTTOM, 5)
274        boxsizer.Add(zbox, 1, wx.LEFT | wx.BOTTOM, 5)
275        vbox.Add(boxsizer, 0, wx.LEFT, 20)
276        vbox.Add(hbox, 0, wx.LEFT | wx.TOP, 15)
277        okButton.SetFocus()
278        # set sizer
279        self.SetSizer(vbox)
280        #pos = self.parent.GetPosition()
281        #self.SetPosition(pos)
282        self.z_ctrl = z_ctl
283        self.xy_ctrls = [[xmin_ctl, xmax_ctl], [ymin_ctl, ymax_ctl]]
284
285    def _onparamEnter(self, event=None):
286        """
287        By pass original txtcrl binding
288        """
289        pass   
290       
291    def _onparam(self, event=None):
292        """
293        Set to default
294        """
295        item = event.GetEventObject()
296        self._check_ctrls(item)
297
298    def _check_ctrls(self, item, is_button=False):
299        """
300        """
301        flag = True
302        item.SetBackgroundColour("white")
303        try:
304            val = float(item.GetValue()) 
305            if val < -10.0 or val > 10.0:
306                item.SetBackgroundColour("pink")
307                item.Refresh()
308                flag = False
309        except:
310            item.SetBackgroundColour("pink")
311            item.Refresh()
312            flag = False
313        if not flag and is_button:
314            err_msg = "The allowed range of the min and max values are \n"
315            err_msg += "between -10 and 10."
316            if self.base is not None:
317                wx.PostEvent(self.base, StatusEvent(status=err_msg, 
318                                                    info="error"))
319            else:
320                print err_msg
321        return flag
322
323    def _on_z_enter(self, event= None): 
324        """
325        On z factor enter
326        """
327        item = event.GetEventObject()
328        self._check_z_ctrl(item)
329       
330    def _check_z_ctrl(self, item, is_button=False):
331        """
332        """
333        flag = True
334        item.SetBackgroundColour("white")
335        try:
336            val = float(item.GetValue()) 
337            if val <= 0:
338                item.SetBackgroundColour("pink")
339                item.Refresh()
340                flag = False
341        except:
342            item.SetBackgroundColour("pink")
343            item.Refresh() 
344            flag = False
345        if not flag and is_button:
346            err_msg = "The z scale value should be larger than 0."
347            if self.base is not None:
348                wx.PostEvent(self.base, StatusEvent(status=err_msg, 
349                                                    info="error"))
350            else:
351                print err_msg
352        return flag
353   
354    def on_set(self, event):
355        """
356        Set image as data   
357        """
358        event.Skip()
359        # Check the textctrl values
360        for item_list in self.xy_ctrls:
361            for item in item_list:
362                 if not self._check_ctrls(item, True):
363                     return
364        if not self._check_z_ctrl(self.z_ctrl, True):
365            return
366        try:
367            image = self.image
368            xmin = float(self.xy_ctrls[0][0].GetValue())
369            xmax = float(self.xy_ctrls[0][1].GetValue())
370            ymin = float(self.xy_ctrls[1][0].GetValue())
371            ymax = float(self.xy_ctrls[1][1].GetValue())
372            zscale = float(self.z_ctrl.GetValue())
373            self.convert_image(image, xmin, xmax, ymin, ymax, zscale)
374        except:
375            err_msg = "Error occurred while converting Image to Data."
376            if self.base is not None:
377                wx.PostEvent(self.base, StatusEvent(status=err_msg, 
378                                                    info="error"))
379            else:
380                print err_msg
381           
382        self.OnClose(event)
383   
384    def convert_image(self, rgb, xmin, xmax, ymin, ymax, zscale):
385        """
386        Convert image to data2D
387        """
388        x_len = len(rgb[0])
389        y_len = len(rgb)
390        x_vals = np.linspace(xmin, xmax, num=x_len)
391        y_vals = np.linspace(ymin, ymax, num=y_len)
392        # Instantiate data object
393        output = Data2D()
394        output.filename = os.path.basename(self.title)
395        output.id = output.filename
396        detector = Detector()
397        detector.pixel_size.x = None
398        detector.pixel_size.y = None
399        # Store the sample to detector distance
400        detector.distance = None
401        output.detector.append(detector)
402        # Initiazed the output data object
403        output.data = zscale * self.rgb2gray(rgb)
404        output.err_data = np.zeros([x_len, y_len])
405        output.mask = np.ones([x_len, y_len], dtype=bool)
406        output.xbins = x_len
407        output.ybins = y_len
408        output.x_bins = x_vals
409        output.y_bins = y_vals
410        output.qx_data = np.array(x_vals)
411        output.qy_data = np.array(y_vals)
412        output.xmin = xmin
413        output.xmax = xmax
414        output.ymin = ymin
415        output.ymax = ymax
416        output.xaxis('\\rm{Q_{x}}', '\AA^{-1}')
417        output.yaxis('\\rm{Q_{y}}', '\AA^{-1}')
418        # Store loading process information
419        output.meta_data['loader'] = self.title.split('.')[-1] + "Reader"
420        output.is_data = True
421        output = reader2D_converter(output)
422        if self.base != None:
423            data = self.base.create_gui_data(output, self.title)
424            self.base.add_data({data.id:data})
425           
426    def rgb2gray(self, rgb):
427        """
428        RGB to Grey
429        """
430        if self.is_png:
431            # png image limits: 0 to 1, others 0 to 255
432            #factor = 255.0
433            rgb = rgb[::-1]
434        if rgb.ndim == 2:
435            grey = np.rollaxis(rgb, axis = 0)
436        else:
437            red, green, blue = np.rollaxis(rgb[...,:3], axis = -1)
438            grey = 0.299 * red + 0.587 * green + 0.114 * blue
439        max_i = rgb.max()
440        factor = 255.0 / max_i
441        grey *= factor
442        return np.array(grey)
443                     
444    def OnClose(self, event):
445        """
446        Close event
447        """
448        # clear event
449        event.Skip()
450        self.Destroy()
451                       
452if __name__ == "__main__":
453    app  = wx.App()
454    ImageView(None).load()
455    app.MainLoop()   
456   
457 
Note: See TracBrowser for help on using the repository browser.