import os
import sys
import wx
import numpy as np
import matplotlib
matplotlib.interactive(False)
#Use the WxAgg back end. The Wx one takes too long to render
matplotlib.use('WXAgg')
from sas.guiframe.local_perspectives.plotting.SimplePlot import PlotFrame
#import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import matplotlib.colors as colors
from sas.guiframe.events import StatusEvent
from sas.perspectives.calculator.calculator_widgets import InputTextCtrl
from sas.dataloader.data_info import Data2D
from sas.dataloader.data_info import Detector
from sas.dataloader.manipulations import reader2D_converter
_BOX_WIDTH = 60
IS_WIN = True
if sys.platform.count("win32") > 0:
_DIALOG_WIDTH = 400
else:
_DIALOG_WIDTH = 480
IS_WIN = False
[docs]class ImageView:
"""
Open a file dialog to allow the user to select a given file.
Display the loaded data if available.
"""
def __init__(self, parent=None):
"""
Init
"""
self.parent = parent
[docs] def load(self):
"""
load image files
"""
parent = self.parent
if parent == None:
location = os.getcwd()
else:
location = parent._default_save_location
path_list = self.choose_data_file(location=location)
if path_list == None:
return
if len(path_list) >= 0 and not(path_list[0]is None):
if parent != None:
parent._default_save_location = os.path.dirname(path_list[0])
err_msg = ''
for file_path in path_list:
basename = os.path.basename(file_path)
_, extension = os.path.splitext(basename)
try:
img = mpimg.imread(file_path)
is_png = extension.lower() == '.png'
plot_frame = ImageFrame(parent, -1, basename, img)
plot_frame.Show(False)
#plot_frame.im_show(img)
ax = plot_frame.plotpanel
if not is_png:
ax.subplot.set_ylim(ax.subplot.get_ylim()[::-1])
ax.subplot.set_xlabel('x [pixel]')
ax.subplot.set_ylabel('y [pixel]')
ax.figure.subplots_adjust(left=0.15, bottom=0.1,
right=0.95, top=0.95)
plot_frame.SetTitle('Picture -- %s --'% basename)
plot_frame.Show(True)
if parent != None:
parent.put_icon(plot_frame)
except:
print "parent", parent
raise
err_msg += "Failed to load '%s'.\n"% basename
if err_msg:
if parent is not None:
wx.PostEvent(parent, StatusEvent(status=err_msg, info="error"))
else:
print err_msg
[docs] def choose_data_file(self, location=None):
"""
Open a file dialog to allow loading a file
"""
parent = self.parent
path = None
if location == None:
location = os.getcwd()
wlist = ''
elist = ["All images (*.png, *.bmp, *.gif, *.jpg, *.tif, *.tiff) | \
*.png; *.bmp; *.gif; *.jpg; *.tif; *.tiff",
"PNG files (*.PNG, *.png) | *.png",
"BMP files (*.BMP, *.bmp) | *.bmp",
"GIF files (*.GIF, *.gif) | *.gif",
"JPG files (*.JPG, *.jpg) | *.jpg",
"TIF files (*.TIF, *.tif) | *.tif",
"TIFF files (*.TIFF, *.tiff) | *.tiff"]
if not IS_WIN:
del elist[0]
elist.append("All files (*.*) | *.*")
wlist = '|'.join(elist)
style = wx.OPEN|wx.FD_MULTIPLE
dlg = wx.FileDialog(parent, "Image Viewer: Choose a image file",
location, "", wlist, style=style)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPaths()
else:
return None
dlg.Destroy()
return path
[docs]class ImageFrame(PlotFrame):
"""
Frame for simple plot
"""
def __init__(self, parent, id, title, image=None, scale='log_{10}',
size=wx.Size(550, 470)):
"""
comment
:Param data: image array got from imread() of matplotlib [narray]
:param parent: parent panel/container
"""
# Initialize the Frame object
PlotFrame.__init__(self, parent, id, title, scale, size)
self.parent = parent
self.data = image
self.file_name = title
menu = wx.Menu()
id = wx.NewId()
item = wx.MenuItem(menu, id, "&Convert to Data")
menu.AppendItem(item)
wx.EVT_MENU(self, id, self.on_set_data)
self.menu_bar.Append(menu, "&Image")
menu_help = wx.Menu()
id = wx.NewId()
item = wx.MenuItem(menu_help, id, "&HowTo")
menu_help.AppendItem(item)
wx.EVT_MENU(self, id, self.on_help)
self.menu_bar.Append(menu_help, "&Help")
self.SetMenuBar(self.menu_bar)
self.im_show(image)
[docs] def on_set_data(self, event):
"""
Rescale the x y range, make 2D data and send it to data explore
"""
title = self.file_name
self.panel = SetDialog(parent=self, title=title, image=self.data)
self.panel.ShowModal()
[docs] def on_help(self, event):
"""
Image Viewer help panel
"""
from sas.perspectives.calculator.help_panel import HelpWindow
# Get models help model_function path
import sas.perspectives.calculator as calmedia
media = calmedia.get_data_path(media='media')
path = os.path.join(media,"load_image_help.html")
name = "Image Viewer"
frame = HelpWindow(self, -1, title=' Help: Image Viewer',
pageToOpen=path, size=(640, 450))
try:
frame.splitter.DetachWindow(frame.lpanel)
# Display only the right side one
frame.lpanel.Hide()
frame.Show(True)
except:
frame.Destroy()
msg = 'Display Error\n'
info = "Info"
wx.MessageBox(msg, info)
[docs]class SetDialog(wx.Dialog):
"""
Dialog for Data Set
"""
def __init__(self, parent, id=-1, title="Convert to Data", image=None,
size=(_DIALOG_WIDTH, 270)):
wx.Dialog.__init__(self, parent, id, title, size)
# parent
self.parent = parent
self.base = parent.parent
self.title = title
self.image = np.array(image)
self.z_ctrl = None
self.xy_ctrls = []
self.is_png = self._get_is_png()
self._build_layout()
my_title = "Convert Image to Data - %s -"% self.title
self.SetTitle(my_title)
self.SetSize(size)
def _get_is_png(self):
"""
Get if the image file is png
"""
_, extension = os.path.splitext(self.title)
return extension.lower() == '.png'
def _build_layout(self):
"""
Layout
"""
vbox = wx.BoxSizer(wx.VERTICAL)
zbox = wx.BoxSizer(wx.HORIZONTAL)
xbox = wx.BoxSizer(wx.HORIZONTAL)
ybox = wx.BoxSizer(wx.HORIZONTAL)
btnbox = wx.BoxSizer(wx.VERTICAL)
sb_title = wx.StaticBox(self, -1, 'Transform Axes')
boxsizer = wx.StaticBoxSizer(sb_title, wx.VERTICAL)
z_title = wx.StaticText(self, -1, 'z values (range: 0 - 255) to:')
ztime_title = wx.StaticText(self, -1, 'z *')
x_title = wx.StaticText(self, -1, 'x values from pixel # to:')
xmin_title = wx.StaticText(self, -1, 'xmin:')
xmax_title = wx.StaticText(self, -1, 'xmax:')
y_title = wx.StaticText(self, -1, 'y values from pixel # to:')
ymin_title = wx.StaticText(self, -1, 'ymin: ')
ymax_title = wx.StaticText(self, -1, 'ymax:')
z_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH , 20),
style=wx.TE_PROCESS_ENTER)
xmin_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER)
xmax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER)
ymin_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER)
ymax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER)
z_ctl.SetValue('1.0')
xmin_ctl.SetValue('-0.3')
xmax_ctl.SetValue('0.3')
ymin_ctl.SetValue('-0.3')
ymax_ctl.SetValue('0.3')
z_ctl.Bind(wx.EVT_TEXT, self._on_z_enter)
xmin_ctl.Bind(wx.EVT_TEXT, self._onparam)
xmax_ctl.Bind(wx.EVT_TEXT, self._onparam)
ymin_ctl.Bind(wx.EVT_TEXT, self._onparam)
ymax_ctl.Bind(wx.EVT_TEXT, self._onparam)
xbox.AddMany([(x_title , 0, wx.LEFT, 0),
(xmin_title , 0, wx.LEFT, 10),
(xmin_ctl , 0, wx.LEFT, 10),
(xmax_title , 0, wx.LEFT, 10),
(xmax_ctl , 0, wx.LEFT, 10)])
ybox.AddMany([(y_title , 0, wx.LEFT, 0),
(ymin_title , 0, wx.LEFT, 10),
(ymin_ctl , 0, wx.LEFT, 10),
(ymax_title , 0, wx.LEFT, 10),
(ymax_ctl , 0, wx.LEFT, 10)])
zbox.AddMany([(z_title , 0, wx.LEFT, 0),
(ztime_title, 0, wx.LEFT, 10),
(z_ctl , 0, wx.LEFT, 7),
])
msg = "The data rescaled will show up in the Data Explorer. \n"
msg += "*Note: Recommend to use an image with 8 bit Grey \n"
msg += " scale (and with No. of pixels < 300 x 300).\n"
msg += " Otherwise, z = 0.299R + 0.587G + 0.114B."
note_txt = wx.StaticText(self, -1, msg)
note_txt.SetForegroundColour("black")
hbox = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(self, -1, 'OK')
okButton.Bind(wx.EVT_BUTTON, self.on_set)
cancelButton = wx.Button(self, -1, 'Cancel')
cancelButton.Bind(wx.EVT_BUTTON, self.OnClose)
btnbox.Add(okButton, 0, wx.LEFT | wx.BOTTOM, 5)
btnbox.Add(cancelButton, 0, wx.LEFT | wx.TOP, 5)
hbox.Add(note_txt, 0, wx.LEFT, 5)
hbox.Add(btnbox, 0, wx.LEFT, 15)
vbox.Add((10, 15))
boxsizer.Add(xbox, 1, wx.LEFT | wx.BOTTOM, 5)
boxsizer.Add(ybox, 1, wx.LEFT | wx.BOTTOM, 5)
boxsizer.Add(zbox, 1, wx.LEFT | wx.BOTTOM, 5)
vbox.Add(boxsizer, 0, wx.LEFT, 20)
vbox.Add(hbox, 0, wx.LEFT | wx.TOP, 15)
okButton.SetFocus()
# set sizer
self.SetSizer(vbox)
#pos = self.parent.GetPosition()
#self.SetPosition(pos)
self.z_ctrl = z_ctl
self.xy_ctrls = [[xmin_ctl, xmax_ctl], [ymin_ctl, ymax_ctl]]
def _onparamEnter(self, event=None):
"""
By pass original txtcrl binding
"""
pass
def _onparam(self, event=None):
"""
Set to default
"""
item = event.GetEventObject()
self._check_ctrls(item)
def _check_ctrls(self, item, is_button=False):
"""
"""
flag = True
item.SetBackgroundColour("white")
try:
val = float(item.GetValue())
if val < -10.0 or val > 10.0:
item.SetBackgroundColour("pink")
item.Refresh()
flag = False
except:
item.SetBackgroundColour("pink")
item.Refresh()
flag = False
if not flag and is_button:
err_msg = "The allowed range of the min and max values are \n"
err_msg += "between -10 and 10."
if self.base is not None:
wx.PostEvent(self.base, StatusEvent(status=err_msg,
info="error"))
else:
print err_msg
return flag
def _on_z_enter(self, event= None):
"""
On z factor enter
"""
item = event.GetEventObject()
self._check_z_ctrl(item)
def _check_z_ctrl(self, item, is_button=False):
"""
"""
flag = True
item.SetBackgroundColour("white")
try:
val = float(item.GetValue())
if val <= 0:
item.SetBackgroundColour("pink")
item.Refresh()
flag = False
except:
item.SetBackgroundColour("pink")
item.Refresh()
flag = False
if not flag and is_button:
err_msg = "The z scale value should be larger than 0."
if self.base is not None:
wx.PostEvent(self.base, StatusEvent(status=err_msg,
info="error"))
else:
print err_msg
return flag
[docs] def on_set(self, event):
"""
Set image as data
"""
event.Skip()
# Check the textctrl values
for item_list in self.xy_ctrls:
for item in item_list:
if not self._check_ctrls(item, True):
return
if not self._check_z_ctrl(self.z_ctrl, True):
return
try:
image = self.image
xmin = float(self.xy_ctrls[0][0].GetValue())
xmax = float(self.xy_ctrls[0][1].GetValue())
ymin = float(self.xy_ctrls[1][0].GetValue())
ymax = float(self.xy_ctrls[1][1].GetValue())
zscale = float(self.z_ctrl.GetValue())
self.convert_image(image, xmin, xmax, ymin, ymax, zscale)
except:
err_msg = "Error occurred while converting Image to Data."
if self.base is not None:
wx.PostEvent(self.base, StatusEvent(status=err_msg,
info="error"))
else:
print err_msg
self.OnClose(event)
[docs] def convert_image(self, rgb, xmin, xmax, ymin, ymax, zscale):
"""
Convert image to data2D
"""
x_len = len(rgb[0])
y_len = len(rgb)
x_vals = np.linspace(xmin, xmax, num=x_len)
y_vals = np.linspace(ymin, ymax, num=y_len)
# Instantiate data object
output = Data2D()
output.filename = os.path.basename(self.title)
output.id = output.filename
detector = Detector()
detector.pixel_size.x = None
detector.pixel_size.y = None
# Store the sample to detector distance
detector.distance = None
output.detector.append(detector)
# Initiazed the output data object
output.data = zscale * self.rgb2gray(rgb)
output.err_data = np.zeros([x_len, y_len])
output.mask = np.ones([x_len, y_len], dtype=bool)
output.xbins = x_len
output.ybins = y_len
output.x_bins = x_vals
output.y_bins = y_vals
output.qx_data = np.array(x_vals)
output.qy_data = np.array(y_vals)
output.xmin = xmin
output.xmax = xmax
output.ymin = ymin
output.ymax = ymax
output.xaxis('\\rm{Q_{x}}', '\AA^{-1}')
output.yaxis('\\rm{Q_{y}}', '\AA^{-1}')
# Store loading process information
output.meta_data['loader'] = self.title.split('.')[-1] + "Reader"
output.is_data = True
output = reader2D_converter(output)
if self.base != None:
data = self.base.create_gui_data(output, self.title)
self.base.add_data({data.id:data})
[docs] def rgb2gray(self, rgb):
"""
RGB to Grey
"""
if self.is_png:
# png image limits: 0 to 1, others 0 to 255
#factor = 255.0
rgb = rgb[::-1]
if rgb.ndim == 2:
grey = np.rollaxis(rgb, axis = 0)
else:
red, green, blue = np.rollaxis(rgb[...,:3], axis = -1)
grey = 0.299 * red + 0.587 * green + 0.114 * blue
max_i = rgb.max()
factor = 255.0 / max_i
grey *= factor
return np.array(grey)
[docs] def OnClose(self, event):
"""
Close event
"""
# clear event
event.Skip()
self.Destroy()
if __name__ == "__main__":
app = wx.App()
ImageView(None).load()
app.MainLoop()