from java.awt import Color from java.awt.event import TextListener from javax.swing import JButton, JScrollPane, JPanel, JComboBox, JLabel, JFrame, JTextField, JCheckBox, JOptionPane from java.awt import BorderLayout, Choice, Color, Font, GridLayout, GridBagLayout, GridBagConstraints from java.awt.event import ActionListener import os import time import math from ij import Prefs from ij import IJ from ij import Menus from ij.plugin.filter import Info from ij.gui import GenericDialog #commands = [c for c in ij.Menus.getCommands().keySet()] # Above, equivalent list as below: #commands = Menus.getCommands().keySet().toArray() #gd = GenericDialog('Command Launcher 2') #gd.addStringField('Command: ', ''); #prompt = gd.getStringFields().get(0) #prompt.setForeground(Color.red) #gd.showDialog() #if not gd.wasCanceled(): IJ.doCommand(gd.getNextString()) # This python version does not encapsulate the values of the variables, so they are all global when defined outside the class definition. # In contrast, the lisp 'let' definitions encapsulates them in full # As an advantage, each python script executes within its own namespace, whereas clojure scripts run all within a unique static interpreter. class ConvertJob: def __init__(self): self.logMessages = [] self.projectName = "" self.tileSize = 512 def setScaleLevels(self, scaleLevels): if not self.image: print("ERR: image not set") return if not self.tileSize: print("ERR: tile size not set") return if scaleLevels == 0: self.scaleLevels = int( math.log( max( self.image.dimensions[0], self.image.dimensions[1] ) / self.tileSize, 2 ) ) + 1 else: self.scaleLevels = scaleLevels self.log("Number of scale levels: " + str(self.scaleLevels) + " (tilesize=" + str(self.tileSize) + ")") def log(self, message): self.logMessages.append(message) print(message) time.sleep(0.001) def saveLog(self, path): f = None try: f = open(path, 'w') for item in self.logMessages: f.write("%s\n" % item) except: print("ERR: Could not save log file!") finally: if f is not None: f.close() def saveProperties(self): self.pset("projectName", self.projectName) def saveYaml(self): f = None path = self.outputPathWithoutExtension + ".yaml" try: self.log("Saving YAML file") f = open( path, 'w') template_channels = """ - folder: "channel%(num)d" name: "%(name)s" metadata: "%(metadata)s" dimension: "(%(dimx)d,%(dimy)d,%(dimz)d)" resolution: "(%(resx)d,%(resy)d,%(resz)d)" zoomlevels: %(zoomlevels)d""" template_composite_channels = """ - stack: "channel%(num)d" color: "%(color)s\"""" channels_string = "" composite_channels_string = "" for c in range( 0, self.channels ): context_channels = { 'num' : c, 'name' : "Channel " + str( c ), 'metadata' : "...", 'dimx' : self.image.dimensions[0], 'dimy' : self.image.dimensions[1], 'dimz' : self.image.dimensions[3], 'resx' : 100, 'resy' : 100, 'resz' : 100, 'zoomlevels' : self.scaleLevels, 'color' : self.colors[c] } channels_string = channels_string + template_channels % context_channels composite_channels_string = composite_channels_string + template_composite_channels % context_channels # Add composite channel to YAML string channels_string = channels_string + """ - folder: "composite" name: "Composite" dimension: "(%(dimx)d,%(dimy)d,%(dimz)d)" resolution: "(%(resx)d,%(resy)d,%(resz)d)" channels:%(composite_channels)s""" % { 'dimx' : self.image.dimensions[0], 'dimy' : self.image.dimensions[1], 'dimz' : self.image.dimensions[3], 'resx' : 100, 'resy' : 100, 'resz' : 100, 'composite_channels' : composite_channels_string } # Generate main YAML string yaml_string = """project: name: "%(name)s" stacks:%(channels)s""" % { 'name' : self.projectName, 'channels' : channels_string } f.write( yaml_string ) except Exception, e: print("Error: Could not save YAML file: " + str(e)) finally: if f is not None: f.close() self.log("Saved YAML file to " + path ) def saveHDF5(self): self.log( "Creating HDF5 file" ) path = self.outputPathWithoutExtension + ".h5" self.imageClone = self.image.clone() self.log( "Saving scale level 0 (" + str( self.imageClone.width ) + "x" + str( self.imageClone.height ) + ")." ) IJ.run( self.imageClone, "Scriptable save HDF5 (new or replace)...", "save=" + path + " dsetnametemplate=/stacks/channel{c}/0 formattime=%d formatchannel=%d compressionlevel=0" ) scale_level = 1 while self.imageClone.width > self.tileSize or self.imageClone.height > self.tileSize: self.log( "Scale level " + str( scale_level - 1 ) + " saved. Scaling down." ) IJ.run( self.imageClone, "Bin...", "x=2 y=2 y=1 bin=Average") self.log( "Saving scale level " + str( scale_level ) + " (" + str( self.imageClone.width ) + "x" + str( self.imageClone.height ) + ")." ) IJ.run( self.imageClone, "Scriptable save HDF5 (append)...", "save=" + path + " dsetnametemplate=/stacks/channel{c}/" + str( scale_level ) + " formattime=%d formatchannel=%d compressionlevel=0" ) scale_level = scale_level + 1 self.log( "Scale level " + str( scale_level - 1 ) + " saved." ) self.log( "Smallest size reached. Saved " + str( scale_level ) + " scale levels.") self.log( "HDF5 file saved to " + path + "\n" ) self.imageClone.flush() def saveIJLog(path): f = None try: f = open(path, 'w') logText = IJ.getLog() if logText is not None: f.write(logText) except: print("ERR: Could not save IJ log file!") finally: if f is not None: f.close() def getDataPart( job, data, lineStart ): """ Get the end of line in data, starting with lineStart. """ posOne = data.find( lineStart ) if posOne != -1: posOne = posOne + len( lineStart ) posTwo = data.find( "\n", posOne ) substring = data[posOne:posTwo] return substring else: job.log("\tCould not find \"" + lineStart + "\"" ) return None class CancelHandler(ActionListener): def __init__(self, job, frame): self.job = job self.frame = frame def actionPerformed(self, event): # global currently_asking # self.job.log("Canceling stitching") # currently_asking = False self.frame.setVisible(False) self.frame.dispose() class StartHandler(ActionListener): def __init__(self, job, frame, yamlonly): self.job = job self.frame = frame self.yamlonly = yamlonly def actionPerformed(self, event): # global currently_asking self.job.projectName = projectNameTf.getText() scaleLevels = scaleLevelsTf.getText() if scaleLevels == "": self.job.setScaleLevels(0) else: try: self.job.setScaleLevels(int(scaleLevelsTf.getText())) except: print("ERR: invalid value for Scale Levels") self.job.metadata = [tf.getText() for tf in formMetadata] self.job.colors = [tf.getSelectedItem() for tf in formColors] #self.job. self.job.outputPathWithoutExtension = os.path.join( job.outputDir, job.projectName ) self.job.ready = True print("Starting job") # Save the job's properties # self.job.saveProperties() self.job.saveYaml( ) if not self.yamlonly: self.job.saveHDF5() # Stop input # currently_asking = False self.frame.setVisible(False) self.frame.dispose() # get Image imp = IJ.getImage() print( "Using open image: " + str(imp)) job = ConvertJob() job.image = imp job.width = imp.width job.height = imp.height job.slices = imp.getNSlices() job.channels = imp.getNChannels() job.projectName = imp.getShortTitle() job.outputDir = imp.getOriginalFileInfo().directory #imgInfo = Info() #p = imp.getChannelProcessor() #info = imgInfo.getImageInfo( imp, imp.getChannelProcessor() ) #job.metadata = info # Construct GUI # ------------- infoPanel = JPanel() infoPanel.setLayout(GridLayout(1,1)) infoPanel.add(JLabel("Convert the currently opened image to HDF5+YAML
The resulting files will be placed in the output directory specified.
Existing HDF5 and YAML files will be overwritten.")) frame = JFrame("Convert to HDF5+YAML") inputPanel = JPanel() #inputPanel.setLayout(GridLayout(4 + 4*job.channels, 2)) inputPanel.setLayout(GridLayout(0, 2)) inputPanel.add(JLabel("Image Dimensions")) inputPanel.add(JLabel( str(job.width) + " x " + str(job.height) + ", " + str(job.slices) + " slices, " + str(job.channels) + " stacks" )) projectNameTf = JTextField(job.projectName) inputPanel.add(JLabel("Image Name")) inputPanel.add(projectNameTf) scaleLevelsTf = JTextField() inputPanel.add(JLabel("Scale levels (empty=automatic)")) inputPanel.add(scaleLevelsTf) inputPanel.add(JLabel("Output Directory")) outputDirTf = JTextField(job.outputDir) inputPanel.add(outputDirTf) # Generate default metadata string # mdList = [] # offsetStr = "Image " + str(i) + " : AnalogPMTOffset = " # offsetData = getDataPart( job, job.metadata, offsetStr ) # if offsetData != None: # mdList.append( "PMT Offset: " + offsetData ) # powerStr = "Image " + str(i) + " : ExcitationOutPutLevel = " # powerData = getDataPart( job, job.metadata, powerStr ) # if powerData != None: # mdList.append( "Laser Power: " + powerData ) # gainStr = "Image " + str(i) + " : PMTVoltage = " # gainData = getDataPart( job, job.metadata, gainStr ) # if gainData != None: # mdList.append( "PMT Voltage: " + gainData ) # mdString = ", ".join( mdList ) # Channels / Stacks formChannelNames = [] formMetadata = [] formColors = [] for i in range( 0, job.channels ): inputPanel.add(JLabel("Stack " + str(i+1))) inputPanel.add(JLabel("")) inputPanel.add(JLabel(" Name")) formChannelNames.append(JTextField("Channel " + str(i+1) )) inputPanel.add(formChannelNames[i]) inputPanel.add(JLabel(" Metadata")) formMetadata.append(JTextField("")) inputPanel.add(formMetadata[i]) inputPanel.add(JLabel(" Color")) formColors.append(Choice()) formColors[i].add("Red") formColors[i].add("Green") formColors[i].add("Blue") formColors[i].add("Cyan") formColors[i].add("Magenta") formColors[i].add("Yellow") formColors[i].add("White") formColors[i].select(i) inputPanel.add(formColors[i]) controlPanel = JPanel() controlPanel.setLayout(GridLayout(1,3)) stopBtn = JButton("Cancel") stopBtn.addActionListener(CancelHandler(job, frame)) controlPanel.add(stopBtn) yamlBtn = JButton("Generate YAML only") yamlBtn.addActionListener(StartHandler(job, frame, True)) controlPanel.add(yamlBtn) startBtn = JButton("Generate HDF5+YAML") startBtn.addActionListener(StartHandler(job, frame, False)) controlPanel.add(startBtn) frame.getContentPane().add(infoPanel, BorderLayout.NORTH) frame.getContentPane().add(JScrollPane(inputPanel), BorderLayout.CENTER) frame.getContentPane().add(controlPanel, BorderLayout.SOUTH) frame.pack() frame.setVisible(True)