source: sasview/src/sans/perspectives/calculator/image_viewer.py @ eacf6a8c

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 eacf6a8c was 5777106, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Moving things around. Will definitely not build.

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