source: sasview/src/sas/perspectives/calculator/image_viewer.py @ 5ac0a7a

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 5ac0a7a was 49ab5d7, checked in by Doucet, Mathieu <doucetm@…>, 10 years ago

Take care of white spaces (pylint)

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