From 768734fc066592e42cea6850025ca55cff09cc78 Mon Sep 17 00:00:00 2001 From: ChristianNitzsche Date: Tue, 24 Jan 2017 11:41:12 +0100 Subject: [PATCH 01/13] Add files via upload --- button.png | Bin 0 -> 2937 bytes cif2tif.py | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++++ icon.png | Bin 0 -> 12095 bytes 3 files changed, 354 insertions(+) create mode 100644 button.png create mode 100644 cif2tif.py create mode 100644 icon.png diff --git a/button.png b/button.png new file mode 100644 index 0000000000000000000000000000000000000000..a911c15ff4d0b31ad0474220fa503dde9e11442b GIT binary patch literal 2937 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 bfY6YTx_w802E}-nTnDt(^$?Y+c2EBU{px5r literal 0 HcmV?d00001 diff --git a/cif2tif.py b/cif2tif.py new file mode 100644 index 0000000..f16b9cd --- /dev/null +++ b/cif2tif.py @@ -0,0 +1,354 @@ +from Tkinter import * +import tkFileDialog +import tkMessageBox +import bioformats +import bioformats.formatreader +import javabridge +import skimage.io +import skimage.util.montage +import math +import os +import os.path +import click +import numpy +import numpy.random +import matplotlib.pyplot as plt + + +class Stitching(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.initUI() + + def initUI(self): + self.parent.title("Stitching") + self.config(bg = '#F0F0F0') + self.pack(fill = BOTH, expand = 1) + self.photo=PhotoImage(file="button.png") + + self.filename = ' ' + self.dirPath = ' ' + self.n_images = 0 + self.n_channels = 0 + self.reader = 0 + + #create canvas1 + self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) + self.canvas1.place(x=20,y=20) + + self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) + self.button1.configure(width = 18, height = 4, background = "#33B5E5") + self.button1.place(x=30,y=60) + + self.label1 = Label(self.canvas1, text="Filename", fg='blue') + self.label1.configure(width = 23, height = 2) + self.label1.place(x=288,y=20) + + self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") + self.Text1.config(state=DISABLED) + self.Text1.place(x=288,y=40) + + self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') + self.label2.configure(width = 23, height = 2) + self.label2.place(x=288,y=70) + + self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text2.config(state=DISABLED) + self.Text2.place(x=288,y=90) + + self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') + self.label3.configure(width = 23, height = 2) + self.label3.place(x=288,y=120) + + self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text3.config(state=DISABLED) + self.Text3.place(x=288,y=140) + + + #create canvas2 + self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas2.place(x=280,y=210) + + self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) + self.button4.configure(width = 20, height = 2, background = "#33B5E5") + self.button4.place(x=28,y=10) + + self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") + self.Text4.config(state=DISABLED) + self.Text4.place(x=28,y=50) + + + + self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') + self.label5.configure(width = 13) + self.label5.place(x=60,y=90) + + self.entry1 = Entry(self.canvas2,width = 13) + self.entry1.place(x = 60,y=108) + + self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) + self.button5.place(x=180,y=90) + + self.label6 = Label(self.canvas2, text="Grid size", fg='blue') + self.label6.configure(width = 13) + self.label6.place(x=60,y=135) + + self.entry2 = Entry(self.canvas2,width = 13) + self.entry2.place(x = 60,y=153) + + self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) + self.button6.place(x=180,y=135) + + self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) + self.button2.configure(width = 20, height = 3, background = "#33B5E5") + self.button2.place(x=30,y=180) + + #create canvas3 + self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas3.place(x=20,y=210) + + self.label8 = Label(self.canvas3, text="Select cell", fg='blue') + self.label8.configure(width = 13) + self.label8.place(x=70,y=40) + + self.label9 = Label(self.canvas3, text="Select channel", fg='blue') + self.label9.configure(width = 13) + self.label9.place(x=70,y=120) + + self.entry4 = Entry(self.canvas3,width = 13) + self.entry4.place(x = 70,y=58) + + self.entry5 = Entry(self.canvas3,width= 13) + self.entry5.place(x = 70,y=138) + + self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) + self.button3.configure(width = 20, height = 3, background = "#33B5E5") + self.button3.place(x=30,y=180) + + + def messageBox1(self): + text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." + tkMessageBox.showinfo("", text) + + def messageBox2(self): + text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 32 and will yield output images with 32x32 cells per montage. The maximum possible value is 100." + tkMessageBox.showinfo("", text) + + def chooseDir(self): + self.dirPath = tkFileDialog.askdirectory() + self.Text4.config(state=NORMAL) + self.Text4.delete(1.0, END) + self.Text4.insert(END,self.dirPath) + self.Text4.config(state=DISABLED) + + def read_cif(self): + old_file = self.filename + self.filename = tkFileDialog.askopenfilename() + if type(self.filename) != tuple: + if self.filename.lower().endswith('.cif'): + try: + self.Text1.config(state=NORMAL) + self.Text2.config(state=NORMAL) + self.Text3.config(state=NORMAL) + self.Text1.delete(1.0, END) + self.Text2.delete(1.0, END) + self.Text3.delete(1.0, END) + self.entry1.delete(0,END) + self.entry2.delete(0,END) + + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) + self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) + self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) + + #check, whether the .cif file is ok + if self.n_images == 0 or self.n_channels == 0: + raise ValueError('There is nothing in the file') + self.Text1.insert(END,self.filename) + self.Text1.config(state=DISABLED) + self.Text2.insert(END,self.n_images) + self.Text2.config(state=DISABLED) + self.Text3.insert(END,self.n_channels) + self.Text3.config(state=DISABLED) + self.entry1.insert(0,'1-'+str(self.n_channels)) + self.entry2.insert(0,32) + except: + tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") + if self.filename and not self.filename.lower().endswith('.cif'): + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def generate_tifs(self): + if self.reader != 0: + if type(self.dirPath) != tuple: + if os.path.isdir(self.dirPath): + output = self.dirPath + + try: + chooseChannels = self.entry1.get() + channels = [] + t = chooseChannels.split(',') + for k in t: + r = k.split('-') + for e in range(int(r[0]),int(r[-1])+1): + channels.append(e-1) + channels = list(set(channels)) + n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) + if max(channels) < n_channels and min(channels) >= 0: + try: + grid_size = int(self.entry2.get()) + if grid_size < 1 or grid_size > 100: + raise ValueError + + self.stitch(self.reader, output,grid_size, 55, channels) + tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") + + except ValueError: + tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") + + else: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + except: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def display_cell(self): + flag = False + if self.reader != 0: + try: + selected_cell = int(self.entry4.get()) + selected_chan = int(self.entry5.get()) + if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: + raise ValueError + image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) + maxi = numpy.amax(image) + mini = numpy.amin(image) + ''' + f: [min,max] -> [0,255], f = m*x+n + ''' + image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) + flag = True + except: + tkMessageBox.showinfo("Error", "Please select an available cell and channel.") + if flag: + plt.imshow(image, cmap='gray') + plt.show() + + + + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def stitch(self,reader, output, montage_size, image_size, channels = []): + + n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) + n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) + + chunk_size = montage_size**2 + n_chunks = self.__compute_chunks(n_images/2,montage_size) + + if len(channels) == 0: + channels = range(n_channels) + + for channel in channels: + for i in range(n_chunks): + try: + images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] + except javabridge.jutil.JavaException: + break + + images = [self.__pad_or_crop(image, image_size) for image in images] + montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) + + if i == (n_chunks-1): + montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) + + skimage.io.imsave(os.path.join(output, "ch{:d}Im{:d}.tif".format(channel + 1,i+1)), montage) + + def __pad_or_crop(self,image, image_size): + bigger = max(image.shape[0], image.shape[1], image_size) + + pad_x = float(bigger - image.shape[0]) + pad_y = float(bigger - image.shape[1]) + + pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) + pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) + sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] + + std = numpy.std(sample) + + mean = numpy.mean(sample) + + def normal(vector, pad_width, iaxis, kwargs): + vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) + vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) + return vector + + if bigger == image_size: + return numpy.pad(image, (pad_width_x, pad_width_y), normal) + else: + if bigger == image.shape[0]: + temp_image = numpy.pad(image, (pad_width_y), normal) + else: + temp_image = numpy.pad(image, (pad_width_x), normal) + return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] + + def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): + pad_x = float(montage_size*image_size - small_montage.shape[0]) + pad_y = float(montage_size*image_size - small_montage.shape[1]) + npad = ((0,int(pad_y)), (0,int(pad_x))) + return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) + + def __compute_chunks(self,n_images, montage_size): + def remainder(images, groups): + return (images - groups * (montage_size ** 2)) + + n_groups = 1 + while remainder(n_images, n_groups) > 0: + n_groups += 1 + return n_groups + + +@click.command() +@click.option("-image", type=click.Path(exists=True)) +@click.option("-output", type=click.Path(exists=False)) +@click.option("--image-size", default=55) +@click.option("--montage-size", default=30) + +def main(image,output,image_size, montage_size): + root = Tk() + root.geometry('540x470+10+50') + app = Stitching(root) + if image == None: + + root.protocol("WM_DELETE_WINDOW", on_closing) + img = PhotoImage(file='icon.png') + root.tk.call('wm', 'iconphoto', root._w, img) + app.mainloop() + else: + try: + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + try: + os.mkdir(output) + except: + pass + reader = bioformats.formatreader.get_image_reader("tmp", path=image) + app.stitch(reader, output, montage_size,image_size) + javabridge.kill_vm() + except: + pass + +def on_closing(): + if tkMessageBox.askokcancel("Quit", "Do you want to quit?"): + try: + javabridge.kill_vm() + finally: + quit() + +if __name__ == '__main__': + main() diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b549426ea8c58cbb1fc9afd07d8cc7c0e87e18e9 GIT binary patch literal 12095 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 z zVHKhehQ!8PpVtdE5&O|oUT?#QP2Y>%49##BZE?vl9<)dmjK3;d{SRFL-M^{Xjkne4 zBFo44nJbv3UUQE}sKM$cXe-k_xbhUfIB_F-SMEF~mgT|lWVEVl{msO2`JJY`8-4*o z!*={A6zkRXrZQ9ycR{OSTDifjmh<6nQjy^)%4UkxFHG)cJmw)xG4$?XIJ~b3YYrP# zKcjuU^St9VzRs^u*`jEfG!v6ia_Hqe+eA&dwB75ys@0*_0$W9)`whR#UOybf9pBLD z?F5v+n@0+&bmaA%J6l^&`}|)}6Pg%w470SDLPSbYULb}g1M(`uAM;bM!(9yY@xK`k zUllW72U}l9NqcX{#~VOc@UdVB3IT?K=t1Wo(@iuCXU`dP!D&2zOpJ%=!GZ?jwO-AqaC23qN`k|X!GWO0A7It AssI20 literal 0 HcmV?d00001 From d8d18e90708f429b2d7e70d72c24e90e30059cef Mon Sep 17 00:00:00 2001 From: ChristianNitzsche Date: Tue, 24 Jan 2017 18:35:02 +0100 Subject: [PATCH 02/13] Delete cif2tif.py --- cif2tif.py | 354 ----------------------------------------------------- 1 file changed, 354 deletions(-) delete mode 100644 cif2tif.py diff --git a/cif2tif.py b/cif2tif.py deleted file mode 100644 index f16b9cd..0000000 --- a/cif2tif.py +++ /dev/null @@ -1,354 +0,0 @@ -from Tkinter import * -import tkFileDialog -import tkMessageBox -import bioformats -import bioformats.formatreader -import javabridge -import skimage.io -import skimage.util.montage -import math -import os -import os.path -import click -import numpy -import numpy.random -import matplotlib.pyplot as plt - - -class Stitching(Frame): - def __init__(self, parent): - Frame.__init__(self, parent) - self.parent = parent - self.initUI() - - def initUI(self): - self.parent.title("Stitching") - self.config(bg = '#F0F0F0') - self.pack(fill = BOTH, expand = 1) - self.photo=PhotoImage(file="button.png") - - self.filename = ' ' - self.dirPath = ' ' - self.n_images = 0 - self.n_channels = 0 - self.reader = 0 - - #create canvas1 - self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) - self.canvas1.place(x=20,y=20) - - self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) - self.button1.configure(width = 18, height = 4, background = "#33B5E5") - self.button1.place(x=30,y=60) - - self.label1 = Label(self.canvas1, text="Filename", fg='blue') - self.label1.configure(width = 23, height = 2) - self.label1.place(x=288,y=20) - - self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") - self.Text1.config(state=DISABLED) - self.Text1.place(x=288,y=40) - - self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') - self.label2.configure(width = 23, height = 2) - self.label2.place(x=288,y=70) - - self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") - self.Text2.config(state=DISABLED) - self.Text2.place(x=288,y=90) - - self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') - self.label3.configure(width = 23, height = 2) - self.label3.place(x=288,y=120) - - self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") - self.Text3.config(state=DISABLED) - self.Text3.place(x=288,y=140) - - - #create canvas2 - self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) - self.canvas2.place(x=280,y=210) - - self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) - self.button4.configure(width = 20, height = 2, background = "#33B5E5") - self.button4.place(x=28,y=10) - - self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") - self.Text4.config(state=DISABLED) - self.Text4.place(x=28,y=50) - - - - self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') - self.label5.configure(width = 13) - self.label5.place(x=60,y=90) - - self.entry1 = Entry(self.canvas2,width = 13) - self.entry1.place(x = 60,y=108) - - self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) - self.button5.place(x=180,y=90) - - self.label6 = Label(self.canvas2, text="Grid size", fg='blue') - self.label6.configure(width = 13) - self.label6.place(x=60,y=135) - - self.entry2 = Entry(self.canvas2,width = 13) - self.entry2.place(x = 60,y=153) - - self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) - self.button6.place(x=180,y=135) - - self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) - self.button2.configure(width = 20, height = 3, background = "#33B5E5") - self.button2.place(x=30,y=180) - - #create canvas3 - self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) - self.canvas3.place(x=20,y=210) - - self.label8 = Label(self.canvas3, text="Select cell", fg='blue') - self.label8.configure(width = 13) - self.label8.place(x=70,y=40) - - self.label9 = Label(self.canvas3, text="Select channel", fg='blue') - self.label9.configure(width = 13) - self.label9.place(x=70,y=120) - - self.entry4 = Entry(self.canvas3,width = 13) - self.entry4.place(x = 70,y=58) - - self.entry5 = Entry(self.canvas3,width= 13) - self.entry5.place(x = 70,y=138) - - self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) - self.button3.configure(width = 20, height = 3, background = "#33B5E5") - self.button3.place(x=30,y=180) - - - def messageBox1(self): - text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." - tkMessageBox.showinfo("", text) - - def messageBox2(self): - text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 32 and will yield output images with 32x32 cells per montage. The maximum possible value is 100." - tkMessageBox.showinfo("", text) - - def chooseDir(self): - self.dirPath = tkFileDialog.askdirectory() - self.Text4.config(state=NORMAL) - self.Text4.delete(1.0, END) - self.Text4.insert(END,self.dirPath) - self.Text4.config(state=DISABLED) - - def read_cif(self): - old_file = self.filename - self.filename = tkFileDialog.askopenfilename() - if type(self.filename) != tuple: - if self.filename.lower().endswith('.cif'): - try: - self.Text1.config(state=NORMAL) - self.Text2.config(state=NORMAL) - self.Text3.config(state=NORMAL) - self.Text1.delete(1.0, END) - self.Text2.delete(1.0, END) - self.Text3.delete(1.0, END) - self.entry1.delete(0,END) - self.entry2.delete(0,END) - - javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') - self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) - self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) - self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) - - #check, whether the .cif file is ok - if self.n_images == 0 or self.n_channels == 0: - raise ValueError('There is nothing in the file') - self.Text1.insert(END,self.filename) - self.Text1.config(state=DISABLED) - self.Text2.insert(END,self.n_images) - self.Text2.config(state=DISABLED) - self.Text3.insert(END,self.n_channels) - self.Text3.config(state=DISABLED) - self.entry1.insert(0,'1-'+str(self.n_channels)) - self.entry2.insert(0,32) - except: - tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") - if self.filename and not self.filename.lower().endswith('.cif'): - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def generate_tifs(self): - if self.reader != 0: - if type(self.dirPath) != tuple: - if os.path.isdir(self.dirPath): - output = self.dirPath - - try: - chooseChannels = self.entry1.get() - channels = [] - t = chooseChannels.split(',') - for k in t: - r = k.split('-') - for e in range(int(r[0]),int(r[-1])+1): - channels.append(e-1) - channels = list(set(channels)) - n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) - if max(channels) < n_channels and min(channels) >= 0: - try: - grid_size = int(self.entry2.get()) - if grid_size < 1 or grid_size > 100: - raise ValueError - - self.stitch(self.reader, output,grid_size, 55, channels) - tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") - - except ValueError: - tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") - - else: - tkMessageBox.showinfo("Error", "Please choose correct channels.") - except: - tkMessageBox.showinfo("Error", "Please choose correct channels.") - else: - tkMessageBox.showinfo("Error", "Please choose an output directory.") - else: - tkMessageBox.showinfo("Error", "Please choose an output directory.") - else: - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def display_cell(self): - flag = False - if self.reader != 0: - try: - selected_cell = int(self.entry4.get()) - selected_chan = int(self.entry5.get()) - if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: - raise ValueError - image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) - maxi = numpy.amax(image) - mini = numpy.amin(image) - ''' - f: [min,max] -> [0,255], f = m*x+n - ''' - image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) - flag = True - except: - tkMessageBox.showinfo("Error", "Please select an available cell and channel.") - if flag: - plt.imshow(image, cmap='gray') - plt.show() - - - - else: - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def stitch(self,reader, output, montage_size, image_size, channels = []): - - n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) - n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) - - chunk_size = montage_size**2 - n_chunks = self.__compute_chunks(n_images/2,montage_size) - - if len(channels) == 0: - channels = range(n_channels) - - for channel in channels: - for i in range(n_chunks): - try: - images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] - except javabridge.jutil.JavaException: - break - - images = [self.__pad_or_crop(image, image_size) for image in images] - montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) - - if i == (n_chunks-1): - montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) - - skimage.io.imsave(os.path.join(output, "ch{:d}Im{:d}.tif".format(channel + 1,i+1)), montage) - - def __pad_or_crop(self,image, image_size): - bigger = max(image.shape[0], image.shape[1], image_size) - - pad_x = float(bigger - image.shape[0]) - pad_y = float(bigger - image.shape[1]) - - pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) - pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) - sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] - - std = numpy.std(sample) - - mean = numpy.mean(sample) - - def normal(vector, pad_width, iaxis, kwargs): - vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) - vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) - return vector - - if bigger == image_size: - return numpy.pad(image, (pad_width_x, pad_width_y), normal) - else: - if bigger == image.shape[0]: - temp_image = numpy.pad(image, (pad_width_y), normal) - else: - temp_image = numpy.pad(image, (pad_width_x), normal) - return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] - - def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): - pad_x = float(montage_size*image_size - small_montage.shape[0]) - pad_y = float(montage_size*image_size - small_montage.shape[1]) - npad = ((0,int(pad_y)), (0,int(pad_x))) - return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) - - def __compute_chunks(self,n_images, montage_size): - def remainder(images, groups): - return (images - groups * (montage_size ** 2)) - - n_groups = 1 - while remainder(n_images, n_groups) > 0: - n_groups += 1 - return n_groups - - -@click.command() -@click.option("-image", type=click.Path(exists=True)) -@click.option("-output", type=click.Path(exists=False)) -@click.option("--image-size", default=55) -@click.option("--montage-size", default=30) - -def main(image,output,image_size, montage_size): - root = Tk() - root.geometry('540x470+10+50') - app = Stitching(root) - if image == None: - - root.protocol("WM_DELETE_WINDOW", on_closing) - img = PhotoImage(file='icon.png') - root.tk.call('wm', 'iconphoto', root._w, img) - app.mainloop() - else: - try: - javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') - try: - os.mkdir(output) - except: - pass - reader = bioformats.formatreader.get_image_reader("tmp", path=image) - app.stitch(reader, output, montage_size,image_size) - javabridge.kill_vm() - except: - pass - -def on_closing(): - if tkMessageBox.askokcancel("Quit", "Do you want to quit?"): - try: - javabridge.kill_vm() - finally: - quit() - -if __name__ == '__main__': - main() From c8ef8005a9577b9e3f297997b975c2b9180fc3a6 Mon Sep 17 00:00:00 2001 From: ChristianNitzsche Date: Tue, 24 Jan 2017 18:41:46 +0100 Subject: [PATCH 03/13] Add files via upload --- button.png | Bin 2937 -> 2955 bytes stitching.py | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 stitching.py diff --git a/button.png b/button.png index a911c15ff4d0b31ad0474220fa503dde9e11442b..3863e4dea3ea24411c51726bf97b33e50bcce7d0 100644 GIT binary patch delta 376 zcmV-;0f+wi7K<0KtqC@K(*UH?0J75nxYGcu(*T~+0Cv+3cGCct(*V=c0L;?>gVO-U z(*VEI0E*L$vC{wl|6$~)Vw2+uEq@phB`4_|n2Z1b0NF`IK~yNueUjS}gCGnC8BFfm*+L=QIpxHh|oMyoDTXVg$HiS1j zGqU9T3AZj3!{s`+y(Y^QQ>kFfx3^)nm2ZQC?6uL{R(*6HvSv%NR^~u&T@(+7H(mrx z0000bbVXQnWMOn=I%9HWVUzp{3=}j}F*rIiH###pD=;xSFfiBFSD}*}3mp_RR53U@ WGdDUjIV&(RIxsM%i|-wiO$#jM^QYDT delta 341 zcmeAc|0%X%EvMOp%M42{Gjv^MD7(y1bD3fFWrqEi`GD-{ml@7nX4rI@;n-z{9hVs{ zUuI~&+_Ul0|Ns93vliT${Fqa>UYtu;OFYHs9|Hr!aZeY=5RLP-lU@onEAX^1yVxA) zaNm0Uw|%;|k z95LO9B`+p!Mr+;yn>V-YC&;wtgdGu<;oarL%CvbBgBCaIlE1H?Z&^2g=1GVB+)ux3 z&e*&5r1fOA3#+fIWuH!O7QUZ7_umP#<#X8+Yt?(N2-Y%imGTK4eKW&&@?S1l5#ta; jLn{+gD-$Da14AnVgN|*siIerXF(rP!ZoNA>o?8b1O!Sn& diff --git a/stitching.py b/stitching.py new file mode 100644 index 0000000..f16b9cd --- /dev/null +++ b/stitching.py @@ -0,0 +1,354 @@ +from Tkinter import * +import tkFileDialog +import tkMessageBox +import bioformats +import bioformats.formatreader +import javabridge +import skimage.io +import skimage.util.montage +import math +import os +import os.path +import click +import numpy +import numpy.random +import matplotlib.pyplot as plt + + +class Stitching(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.initUI() + + def initUI(self): + self.parent.title("Stitching") + self.config(bg = '#F0F0F0') + self.pack(fill = BOTH, expand = 1) + self.photo=PhotoImage(file="button.png") + + self.filename = ' ' + self.dirPath = ' ' + self.n_images = 0 + self.n_channels = 0 + self.reader = 0 + + #create canvas1 + self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) + self.canvas1.place(x=20,y=20) + + self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) + self.button1.configure(width = 18, height = 4, background = "#33B5E5") + self.button1.place(x=30,y=60) + + self.label1 = Label(self.canvas1, text="Filename", fg='blue') + self.label1.configure(width = 23, height = 2) + self.label1.place(x=288,y=20) + + self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") + self.Text1.config(state=DISABLED) + self.Text1.place(x=288,y=40) + + self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') + self.label2.configure(width = 23, height = 2) + self.label2.place(x=288,y=70) + + self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text2.config(state=DISABLED) + self.Text2.place(x=288,y=90) + + self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') + self.label3.configure(width = 23, height = 2) + self.label3.place(x=288,y=120) + + self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text3.config(state=DISABLED) + self.Text3.place(x=288,y=140) + + + #create canvas2 + self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas2.place(x=280,y=210) + + self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) + self.button4.configure(width = 20, height = 2, background = "#33B5E5") + self.button4.place(x=28,y=10) + + self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") + self.Text4.config(state=DISABLED) + self.Text4.place(x=28,y=50) + + + + self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') + self.label5.configure(width = 13) + self.label5.place(x=60,y=90) + + self.entry1 = Entry(self.canvas2,width = 13) + self.entry1.place(x = 60,y=108) + + self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) + self.button5.place(x=180,y=90) + + self.label6 = Label(self.canvas2, text="Grid size", fg='blue') + self.label6.configure(width = 13) + self.label6.place(x=60,y=135) + + self.entry2 = Entry(self.canvas2,width = 13) + self.entry2.place(x = 60,y=153) + + self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) + self.button6.place(x=180,y=135) + + self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) + self.button2.configure(width = 20, height = 3, background = "#33B5E5") + self.button2.place(x=30,y=180) + + #create canvas3 + self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas3.place(x=20,y=210) + + self.label8 = Label(self.canvas3, text="Select cell", fg='blue') + self.label8.configure(width = 13) + self.label8.place(x=70,y=40) + + self.label9 = Label(self.canvas3, text="Select channel", fg='blue') + self.label9.configure(width = 13) + self.label9.place(x=70,y=120) + + self.entry4 = Entry(self.canvas3,width = 13) + self.entry4.place(x = 70,y=58) + + self.entry5 = Entry(self.canvas3,width= 13) + self.entry5.place(x = 70,y=138) + + self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) + self.button3.configure(width = 20, height = 3, background = "#33B5E5") + self.button3.place(x=30,y=180) + + + def messageBox1(self): + text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." + tkMessageBox.showinfo("", text) + + def messageBox2(self): + text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 32 and will yield output images with 32x32 cells per montage. The maximum possible value is 100." + tkMessageBox.showinfo("", text) + + def chooseDir(self): + self.dirPath = tkFileDialog.askdirectory() + self.Text4.config(state=NORMAL) + self.Text4.delete(1.0, END) + self.Text4.insert(END,self.dirPath) + self.Text4.config(state=DISABLED) + + def read_cif(self): + old_file = self.filename + self.filename = tkFileDialog.askopenfilename() + if type(self.filename) != tuple: + if self.filename.lower().endswith('.cif'): + try: + self.Text1.config(state=NORMAL) + self.Text2.config(state=NORMAL) + self.Text3.config(state=NORMAL) + self.Text1.delete(1.0, END) + self.Text2.delete(1.0, END) + self.Text3.delete(1.0, END) + self.entry1.delete(0,END) + self.entry2.delete(0,END) + + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) + self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) + self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) + + #check, whether the .cif file is ok + if self.n_images == 0 or self.n_channels == 0: + raise ValueError('There is nothing in the file') + self.Text1.insert(END,self.filename) + self.Text1.config(state=DISABLED) + self.Text2.insert(END,self.n_images) + self.Text2.config(state=DISABLED) + self.Text3.insert(END,self.n_channels) + self.Text3.config(state=DISABLED) + self.entry1.insert(0,'1-'+str(self.n_channels)) + self.entry2.insert(0,32) + except: + tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") + if self.filename and not self.filename.lower().endswith('.cif'): + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def generate_tifs(self): + if self.reader != 0: + if type(self.dirPath) != tuple: + if os.path.isdir(self.dirPath): + output = self.dirPath + + try: + chooseChannels = self.entry1.get() + channels = [] + t = chooseChannels.split(',') + for k in t: + r = k.split('-') + for e in range(int(r[0]),int(r[-1])+1): + channels.append(e-1) + channels = list(set(channels)) + n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) + if max(channels) < n_channels and min(channels) >= 0: + try: + grid_size = int(self.entry2.get()) + if grid_size < 1 or grid_size > 100: + raise ValueError + + self.stitch(self.reader, output,grid_size, 55, channels) + tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") + + except ValueError: + tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") + + else: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + except: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def display_cell(self): + flag = False + if self.reader != 0: + try: + selected_cell = int(self.entry4.get()) + selected_chan = int(self.entry5.get()) + if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: + raise ValueError + image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) + maxi = numpy.amax(image) + mini = numpy.amin(image) + ''' + f: [min,max] -> [0,255], f = m*x+n + ''' + image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) + flag = True + except: + tkMessageBox.showinfo("Error", "Please select an available cell and channel.") + if flag: + plt.imshow(image, cmap='gray') + plt.show() + + + + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def stitch(self,reader, output, montage_size, image_size, channels = []): + + n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) + n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) + + chunk_size = montage_size**2 + n_chunks = self.__compute_chunks(n_images/2,montage_size) + + if len(channels) == 0: + channels = range(n_channels) + + for channel in channels: + for i in range(n_chunks): + try: + images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] + except javabridge.jutil.JavaException: + break + + images = [self.__pad_or_crop(image, image_size) for image in images] + montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) + + if i == (n_chunks-1): + montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) + + skimage.io.imsave(os.path.join(output, "ch{:d}Im{:d}.tif".format(channel + 1,i+1)), montage) + + def __pad_or_crop(self,image, image_size): + bigger = max(image.shape[0], image.shape[1], image_size) + + pad_x = float(bigger - image.shape[0]) + pad_y = float(bigger - image.shape[1]) + + pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) + pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) + sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] + + std = numpy.std(sample) + + mean = numpy.mean(sample) + + def normal(vector, pad_width, iaxis, kwargs): + vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) + vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) + return vector + + if bigger == image_size: + return numpy.pad(image, (pad_width_x, pad_width_y), normal) + else: + if bigger == image.shape[0]: + temp_image = numpy.pad(image, (pad_width_y), normal) + else: + temp_image = numpy.pad(image, (pad_width_x), normal) + return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] + + def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): + pad_x = float(montage_size*image_size - small_montage.shape[0]) + pad_y = float(montage_size*image_size - small_montage.shape[1]) + npad = ((0,int(pad_y)), (0,int(pad_x))) + return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) + + def __compute_chunks(self,n_images, montage_size): + def remainder(images, groups): + return (images - groups * (montage_size ** 2)) + + n_groups = 1 + while remainder(n_images, n_groups) > 0: + n_groups += 1 + return n_groups + + +@click.command() +@click.option("-image", type=click.Path(exists=True)) +@click.option("-output", type=click.Path(exists=False)) +@click.option("--image-size", default=55) +@click.option("--montage-size", default=30) + +def main(image,output,image_size, montage_size): + root = Tk() + root.geometry('540x470+10+50') + app = Stitching(root) + if image == None: + + root.protocol("WM_DELETE_WINDOW", on_closing) + img = PhotoImage(file='icon.png') + root.tk.call('wm', 'iconphoto', root._w, img) + app.mainloop() + else: + try: + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + try: + os.mkdir(output) + except: + pass + reader = bioformats.formatreader.get_image_reader("tmp", path=image) + app.stitch(reader, output, montage_size,image_size) + javabridge.kill_vm() + except: + pass + +def on_closing(): + if tkMessageBox.askokcancel("Quit", "Do you want to quit?"): + try: + javabridge.kill_vm() + finally: + quit() + +if __name__ == '__main__': + main() From cf370b218d0b4fd2a3d7462b16ecc32fc83c9c15 Mon Sep 17 00:00:00 2001 From: ChristianNitzsche Date: Tue, 24 Jan 2017 18:43:35 +0100 Subject: [PATCH 04/13] Add files via upload --- README.rst | 797 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 754 insertions(+), 43 deletions(-) diff --git a/README.rst b/README.rst index 5f52cbb..c311c25 100755 --- a/README.rst +++ b/README.rst @@ -1,67 +1,778 @@ -Stitching -========= -Stitch images to form a montage. -Requirements -============ -Java development kit -Python 2.7.12 + + + + + -pip + + + + + + -numpy - $ pip install numpy + + + + + stitching/README.rst at master ยท holgerhennig/stitching + + + + + + + + + + + + + -scipy - $ pip install scipy + + + + + + + + + -click - $ pip install click + + + -skimage - $ pip install skimage + + + -python-bioformats - $ pip install python-bioformats + + -matplotlib - $ pip install matplotlib -Additional in Windows OS: Visual C++ 9.0 -Installation -============ + - $ git clone https://github.com/CellProfiler/stitching.git - $ cd /path/to/stitching - $ pip install -e . + + -Use -=== + + - $ python stitching -o path/to/OUTPUT_DIRECTORY path/to/IMAGE -Generates per-channel tiled images from IMAGE saved to OUTPUT_DIRECTORY. Each image in IMAGE has shape 55px by 55px and -is padded with random noise. Files are named "ch1.tif", "ch2.tif", ..., one for each channel. + + -Optional: + ---image-size - Set the window size of the tile images (default: --image-size 55). - If user sets a size bigger than the original images, each of the tile images will be padded with its own background. - If user sets a size smaller than the original images, each of the tile images will be cropped toward its center. ---montage-size - Set the size of the final montage (default: --montage-size 30). - If the total number of original images could not fill up the desired montage size, the montage will be padded with black background. - For example: + + - Number of total images: 1000 + + + - User setting: --image-size 20 --montage-size 30 + + + + + + + + + +
+ Skip to content + + + + + + + + + + + + + +
+ +
+
+ + +
+
+
+ +
+
+ + + +
    + +
  • +
    + +
    + + + + Watch + + + + +
    + +
    +
    +
    +
  • + +
  • +
    +
    + + +
    +
    + + +
    + +
  • + +
  • +
    + +
    + +
  • +
+ +

+ + /stitching + + + forked from CellProfiler/stitching + +

+ +
+ +
+ +
+
+ + + +Permalink + + + +
+ +
+ + + +
+ +
+ + Find file + + +
+ +
+ + +
+ + + 8cd6636 + + Jan 24, 2017 + + + +
+ + @minh-doan + @holgerhennig + @0x00b1 + @mcquin + + +
+ + +
+ + +
+
+
+ +
+ Raw + Blame + History +
+ + +
+ +
+ +
+ +
+ 75 lines (48 sloc) + + 2.4 KB +
+
+ + +
+
+

Stitching

+

Stitch images to form a montage. A montage contains of a n-by-n grid, where in each grid compartment an image can be placed, e.g., a 5-by-5 montage contains 25 images in total. Visit http://cellprofiler.org/imagingflowcytometry/ for more information on the imaging flow cytometry workflow and the image stitching (also referred to as image tiling).

+ +

Requirements

+

Java development kit

+

Python 2.7.12

+

pip

+
+
numpy
+
$ pip install numpy
+
scipy
+
$ pip install scipy
+
click
+
$ pip install click
+
skimage
+
$ pip install skimage
+
python-bioformats
+
$ pip install python-bioformats
+
matplotlib
+
$ pip install matplotlib
+
+

Additional in Windows OS: Visual C++ 9.0

+ +

Installation

+
+

$ git clone https://github.com/CellProfiler/stitching.git

+

$ cd /path/to/stitching

+

$ pip install -e .

+
+ +

Use

+

Generates per-channel montages from IMAGE saved to OUTPUT_DIRECTORY. Each image in IMAGE has shape 55px by 55px and is padded with random noise. Montage files are named "ch1.tif", "ch2.tif", ..., one for each channel.

+

Open a new command line window (also called command prompt window in Windows OS or terminal in MAC OS). Now, to generate the montages, you can either (1) start the stitching GUI or (2) use the command line.

+
    +
  1. To use the GUI, type
  2. +
+
+$ python stitching.py
+

The GUI window will open where you can select a .cif file to generate the montages. Optionally you can also display selected images from the .cif file.

+

OR

+
    +
  1. To use the command line, type
  2. +
+
+$ python stitching -image='path/to/IMAGE' -output path/to/OUTPUT_DIRECTORY
+

Optional:

+ + + + + + + + + +
+--image-sizeSet the window size of the tile images (default: --image-size 55). +If user sets a size bigger than the original images, each of the tile images will be padded with its own background. +If user sets a size smaller than the original images, each of the tile images will be cropped toward its center.
+--montage-size

Set the size of the final montage (default: --montage-size 30). +If the total number of original images could not fill up the desired montage size, the montage will be padded with black background.

+
+

For example:

+

Number of total images: 1000

+

User setting: --image-size 20 --montage-size 30

+

The result would be 2 montages at the size of 600x600; one montage is filled up with 900 (30x30) tile images, one montage contains 100 tile images and remaining space is filled up by black background.

+
+
+ +
+
+ +
+ + + + +
+ +
+ + +
+
+ +
+ + + + + + + +
+ + + You can't perform that action at this time. +
+ + + + + + + + + +
+ + You signed in with another tab or window. Reload to refresh your session. + You signed out in another tab or window. Reload to refresh your session. +
+ + + + - The result would be 2 montages at the size of 600x600; one montage is filled up with 900 (30x30) tile images, one montage contains 100 tile images and remaining space is filled up by black background. From 02ef07b0d631293ca33da289a6eaadb8aecaac22 Mon Sep 17 00:00:00 2001 From: Christian Nitzsche Date: Wed, 25 Jan 2017 14:32:44 +0100 Subject: [PATCH 05/13] update Readme --- README.rst | 800 ++++------------------------------------------------- 1 file changed, 50 insertions(+), 750 deletions(-) diff --git a/README.rst b/README.rst index c311c25..9362419 100755 --- a/README.rst +++ b/README.rst @@ -1,778 +1,78 @@ +Stitching +========= +Stitch images to form a montage. A montage contains of a n-by-n grid, where in each grid compartment an image can be placed, e.g., a 5-by-5 montage contains 25 images in total. Visit http://cellprofiler.org/imagingflowcytometry/ for more information on the imaging flow cytometry workflow and the image stitching (also referred to as image tiling). +Requirements +============ +Java development kit - - - - - +Python 2.7.12 - - - - - - +pip - - - - - stitching/README.rst at master ยท holgerhennig/stitching - - - - - - - - - - - - - +numpy + $ pip install numpy - - - - - - - - - +scipy + $ pip install scipy - - - +click + $ pip install click - - - +skimage + $ pip install skimage - - +Tkinter + typically included with your python distribution +python-bioformats + $ pip install python-bioformats +matplotlib + $ pip install matplotlib - +Additional in Windows OS: Visual C++ 9.0 +Installation +============ + $ git clone https://github.com/CellProfiler/stitching.git - - + $ cd /path/to/stitching - - + $ pip install -e . +Use +=== +Generates per-channel montages from IMAGE saved to OUTPUT_DIRECTORY. Each image in IMAGE has shape 55px by 55px and is padded with random noise. Montage files are named "ch1.tif", "ch2.tif", ..., one for each channel. - - +Open a new command line window (also called command prompt window in Windows OS or terminal in MAC OS). Now, to generate the montages, you can either (1) start the stitching GUI or (2) use the command line. - +1. To use the GUI, type + $ python stitching.py +The GUI window will open where you can select a .cif file to generate the montages. Optionally you can also display selected images from the .cif file. - - +OR - - - +2. To use the command line, type + $ python stitching -image='path/to/IMAGE' -output path/to/OUTPUT_DIRECTORY - - +Optional: - - +--image-size + Set the window size of the tile images (default: --image-size 55). + If user sets a size bigger than the original images, each of the tile images will be padded with its own background. + If user sets a size smaller than the original images, each of the tile images will be cropped toward its center. +--montage-size + Set the size of the final montage (default: --montage-size 30). + If the total number of original images could not fill up the desired montage size, the montage will be padded with black background. + For example: + Number of total images: 1000 - -
- Skip to content - - - - - - - - - - - - - -
- -
-
- - -
-
-
- -
-
- - - -
    - -
  • -
    - -
    - - - - Watch - - - - -
    - -
    -
    -
    -
  • - -
  • -
    -
    - - -
    -
    - - -
    - -
  • - -
  • -
    - -
    - -
  • -
- -

- - /stitching - - - forked from CellProfiler/stitching - -

- -
- -
- -
-
- - - -Permalink - - - -
- -
- - - -
- -
- - Find file - - -
- -
- - -
- - - 8cd6636 - - Jan 24, 2017 - - - -
- - @minh-doan - @holgerhennig - @0x00b1 - @mcquin - - -
- - -
- - -
-
-
- -
- Raw - Blame - History -
- - -
- -
- -
- -
- 75 lines (48 sloc) - - 2.4 KB -
-
- - -
-
-

Stitching

-

Stitch images to form a montage. A montage contains of a n-by-n grid, where in each grid compartment an image can be placed, e.g., a 5-by-5 montage contains 25 images in total. Visit http://cellprofiler.org/imagingflowcytometry/ for more information on the imaging flow cytometry workflow and the image stitching (also referred to as image tiling).

- -

Requirements

-

Java development kit

-

Python 2.7.12

-

pip

-
-
numpy
-
$ pip install numpy
-
scipy
-
$ pip install scipy
-
click
-
$ pip install click
-
skimage
-
$ pip install skimage
-
python-bioformats
-
$ pip install python-bioformats
-
matplotlib
-
$ pip install matplotlib
-
-

Additional in Windows OS: Visual C++ 9.0

- -

Installation

-
-

$ git clone https://github.com/CellProfiler/stitching.git

-

$ cd /path/to/stitching

-

$ pip install -e .

-
- -

Use

-

Generates per-channel montages from IMAGE saved to OUTPUT_DIRECTORY. Each image in IMAGE has shape 55px by 55px and is padded with random noise. Montage files are named "ch1.tif", "ch2.tif", ..., one for each channel.

-

Open a new command line window (also called command prompt window in Windows OS or terminal in MAC OS). Now, to generate the montages, you can either (1) start the stitching GUI or (2) use the command line.

-
    -
  1. To use the GUI, type
  2. -
-
-$ python stitching.py
-

The GUI window will open where you can select a .cif file to generate the montages. Optionally you can also display selected images from the .cif file.

-

OR

-
    -
  1. To use the command line, type
  2. -
-
-$ python stitching -image='path/to/IMAGE' -output path/to/OUTPUT_DIRECTORY
-

Optional:

- - - - - - - - - -
---image-sizeSet the window size of the tile images (default: --image-size 55). -If user sets a size bigger than the original images, each of the tile images will be padded with its own background. -If user sets a size smaller than the original images, each of the tile images will be cropped toward its center.
---montage-size

Set the size of the final montage (default: --montage-size 30). -If the total number of original images could not fill up the desired montage size, the montage will be padded with black background.

-
-

For example:

-

Number of total images: 1000

-

User setting: --image-size 20 --montage-size 30

-

The result would be 2 montages at the size of 600x600; one montage is filled up with 900 (30x30) tile images, one montage contains 100 tile images and remaining space is filled up by black background.

-
-
- -
-
- -
- - - - -
- -
- - -
-
- -
- - - - - - - -
- - - You can't perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - + User setting: --image-size 20 --montage-size 30 + The result would be 2 montages at the size of 600x600; one montage is filled up with 900 (30x30) tile images, one montage contains 100 tile images and remaining space is filled up by black background. From 2d4b2404d6fabe365601058e335ba3b7368abb9a Mon Sep 17 00:00:00 2001 From: Christian Nitzsche Date: Wed, 25 Jan 2017 15:03:02 +0100 Subject: [PATCH 06/13] update Main.py --- stitching.py | 354 --------------------------------- stitching/__main__.py | 449 ++++++++++++++++++++++++++++++++---------- 2 files changed, 344 insertions(+), 459 deletions(-) delete mode 100644 stitching.py mode change 100755 => 100644 stitching/__main__.py diff --git a/stitching.py b/stitching.py deleted file mode 100644 index f16b9cd..0000000 --- a/stitching.py +++ /dev/null @@ -1,354 +0,0 @@ -from Tkinter import * -import tkFileDialog -import tkMessageBox -import bioformats -import bioformats.formatreader -import javabridge -import skimage.io -import skimage.util.montage -import math -import os -import os.path -import click -import numpy -import numpy.random -import matplotlib.pyplot as plt - - -class Stitching(Frame): - def __init__(self, parent): - Frame.__init__(self, parent) - self.parent = parent - self.initUI() - - def initUI(self): - self.parent.title("Stitching") - self.config(bg = '#F0F0F0') - self.pack(fill = BOTH, expand = 1) - self.photo=PhotoImage(file="button.png") - - self.filename = ' ' - self.dirPath = ' ' - self.n_images = 0 - self.n_channels = 0 - self.reader = 0 - - #create canvas1 - self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) - self.canvas1.place(x=20,y=20) - - self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) - self.button1.configure(width = 18, height = 4, background = "#33B5E5") - self.button1.place(x=30,y=60) - - self.label1 = Label(self.canvas1, text="Filename", fg='blue') - self.label1.configure(width = 23, height = 2) - self.label1.place(x=288,y=20) - - self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") - self.Text1.config(state=DISABLED) - self.Text1.place(x=288,y=40) - - self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') - self.label2.configure(width = 23, height = 2) - self.label2.place(x=288,y=70) - - self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") - self.Text2.config(state=DISABLED) - self.Text2.place(x=288,y=90) - - self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') - self.label3.configure(width = 23, height = 2) - self.label3.place(x=288,y=120) - - self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") - self.Text3.config(state=DISABLED) - self.Text3.place(x=288,y=140) - - - #create canvas2 - self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) - self.canvas2.place(x=280,y=210) - - self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) - self.button4.configure(width = 20, height = 2, background = "#33B5E5") - self.button4.place(x=28,y=10) - - self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") - self.Text4.config(state=DISABLED) - self.Text4.place(x=28,y=50) - - - - self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') - self.label5.configure(width = 13) - self.label5.place(x=60,y=90) - - self.entry1 = Entry(self.canvas2,width = 13) - self.entry1.place(x = 60,y=108) - - self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) - self.button5.place(x=180,y=90) - - self.label6 = Label(self.canvas2, text="Grid size", fg='blue') - self.label6.configure(width = 13) - self.label6.place(x=60,y=135) - - self.entry2 = Entry(self.canvas2,width = 13) - self.entry2.place(x = 60,y=153) - - self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) - self.button6.place(x=180,y=135) - - self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) - self.button2.configure(width = 20, height = 3, background = "#33B5E5") - self.button2.place(x=30,y=180) - - #create canvas3 - self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) - self.canvas3.place(x=20,y=210) - - self.label8 = Label(self.canvas3, text="Select cell", fg='blue') - self.label8.configure(width = 13) - self.label8.place(x=70,y=40) - - self.label9 = Label(self.canvas3, text="Select channel", fg='blue') - self.label9.configure(width = 13) - self.label9.place(x=70,y=120) - - self.entry4 = Entry(self.canvas3,width = 13) - self.entry4.place(x = 70,y=58) - - self.entry5 = Entry(self.canvas3,width= 13) - self.entry5.place(x = 70,y=138) - - self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) - self.button3.configure(width = 20, height = 3, background = "#33B5E5") - self.button3.place(x=30,y=180) - - - def messageBox1(self): - text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." - tkMessageBox.showinfo("", text) - - def messageBox2(self): - text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 32 and will yield output images with 32x32 cells per montage. The maximum possible value is 100." - tkMessageBox.showinfo("", text) - - def chooseDir(self): - self.dirPath = tkFileDialog.askdirectory() - self.Text4.config(state=NORMAL) - self.Text4.delete(1.0, END) - self.Text4.insert(END,self.dirPath) - self.Text4.config(state=DISABLED) - - def read_cif(self): - old_file = self.filename - self.filename = tkFileDialog.askopenfilename() - if type(self.filename) != tuple: - if self.filename.lower().endswith('.cif'): - try: - self.Text1.config(state=NORMAL) - self.Text2.config(state=NORMAL) - self.Text3.config(state=NORMAL) - self.Text1.delete(1.0, END) - self.Text2.delete(1.0, END) - self.Text3.delete(1.0, END) - self.entry1.delete(0,END) - self.entry2.delete(0,END) - - javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') - self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) - self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) - self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) - - #check, whether the .cif file is ok - if self.n_images == 0 or self.n_channels == 0: - raise ValueError('There is nothing in the file') - self.Text1.insert(END,self.filename) - self.Text1.config(state=DISABLED) - self.Text2.insert(END,self.n_images) - self.Text2.config(state=DISABLED) - self.Text3.insert(END,self.n_channels) - self.Text3.config(state=DISABLED) - self.entry1.insert(0,'1-'+str(self.n_channels)) - self.entry2.insert(0,32) - except: - tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") - if self.filename and not self.filename.lower().endswith('.cif'): - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def generate_tifs(self): - if self.reader != 0: - if type(self.dirPath) != tuple: - if os.path.isdir(self.dirPath): - output = self.dirPath - - try: - chooseChannels = self.entry1.get() - channels = [] - t = chooseChannels.split(',') - for k in t: - r = k.split('-') - for e in range(int(r[0]),int(r[-1])+1): - channels.append(e-1) - channels = list(set(channels)) - n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) - if max(channels) < n_channels and min(channels) >= 0: - try: - grid_size = int(self.entry2.get()) - if grid_size < 1 or grid_size > 100: - raise ValueError - - self.stitch(self.reader, output,grid_size, 55, channels) - tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") - - except ValueError: - tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") - - else: - tkMessageBox.showinfo("Error", "Please choose correct channels.") - except: - tkMessageBox.showinfo("Error", "Please choose correct channels.") - else: - tkMessageBox.showinfo("Error", "Please choose an output directory.") - else: - tkMessageBox.showinfo("Error", "Please choose an output directory.") - else: - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def display_cell(self): - flag = False - if self.reader != 0: - try: - selected_cell = int(self.entry4.get()) - selected_chan = int(self.entry5.get()) - if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: - raise ValueError - image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) - maxi = numpy.amax(image) - mini = numpy.amin(image) - ''' - f: [min,max] -> [0,255], f = m*x+n - ''' - image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) - flag = True - except: - tkMessageBox.showinfo("Error", "Please select an available cell and channel.") - if flag: - plt.imshow(image, cmap='gray') - plt.show() - - - - else: - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def stitch(self,reader, output, montage_size, image_size, channels = []): - - n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) - n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) - - chunk_size = montage_size**2 - n_chunks = self.__compute_chunks(n_images/2,montage_size) - - if len(channels) == 0: - channels = range(n_channels) - - for channel in channels: - for i in range(n_chunks): - try: - images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] - except javabridge.jutil.JavaException: - break - - images = [self.__pad_or_crop(image, image_size) for image in images] - montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) - - if i == (n_chunks-1): - montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) - - skimage.io.imsave(os.path.join(output, "ch{:d}Im{:d}.tif".format(channel + 1,i+1)), montage) - - def __pad_or_crop(self,image, image_size): - bigger = max(image.shape[0], image.shape[1], image_size) - - pad_x = float(bigger - image.shape[0]) - pad_y = float(bigger - image.shape[1]) - - pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) - pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) - sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] - - std = numpy.std(sample) - - mean = numpy.mean(sample) - - def normal(vector, pad_width, iaxis, kwargs): - vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) - vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) - return vector - - if bigger == image_size: - return numpy.pad(image, (pad_width_x, pad_width_y), normal) - else: - if bigger == image.shape[0]: - temp_image = numpy.pad(image, (pad_width_y), normal) - else: - temp_image = numpy.pad(image, (pad_width_x), normal) - return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] - - def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): - pad_x = float(montage_size*image_size - small_montage.shape[0]) - pad_y = float(montage_size*image_size - small_montage.shape[1]) - npad = ((0,int(pad_y)), (0,int(pad_x))) - return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) - - def __compute_chunks(self,n_images, montage_size): - def remainder(images, groups): - return (images - groups * (montage_size ** 2)) - - n_groups = 1 - while remainder(n_images, n_groups) > 0: - n_groups += 1 - return n_groups - - -@click.command() -@click.option("-image", type=click.Path(exists=True)) -@click.option("-output", type=click.Path(exists=False)) -@click.option("--image-size", default=55) -@click.option("--montage-size", default=30) - -def main(image,output,image_size, montage_size): - root = Tk() - root.geometry('540x470+10+50') - app = Stitching(root) - if image == None: - - root.protocol("WM_DELETE_WINDOW", on_closing) - img = PhotoImage(file='icon.png') - root.tk.call('wm', 'iconphoto', root._w, img) - app.mainloop() - else: - try: - javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') - try: - os.mkdir(output) - except: - pass - reader = bioformats.formatreader.get_image_reader("tmp", path=image) - app.stitch(reader, output, montage_size,image_size) - javabridge.kill_vm() - except: - pass - -def on_closing(): - if tkMessageBox.askokcancel("Quit", "Do you want to quit?"): - try: - javabridge.kill_vm() - finally: - quit() - -if __name__ == '__main__': - main() diff --git a/stitching/__main__.py b/stitching/__main__.py old mode 100755 new mode 100644 index 2b04743..f16b9cd --- a/stitching/__main__.py +++ b/stitching/__main__.py @@ -1,115 +1,354 @@ +from Tkinter import * +import tkFileDialog +import tkMessageBox +import bioformats +import bioformats.formatreader +import javabridge +import skimage.io +import skimage.util.montage import math import os import os.path - -import bioformats -import bioformats.formatreader import click -import javabridge -import javabridge.jutil import numpy import numpy.random -import skimage.io -import skimage.util.montage - +import matplotlib.pyplot as plt + + +class Stitching(Frame): + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.initUI() + + def initUI(self): + self.parent.title("Stitching") + self.config(bg = '#F0F0F0') + self.pack(fill = BOTH, expand = 1) + self.photo=PhotoImage(file="button.png") + + self.filename = ' ' + self.dirPath = ' ' + self.n_images = 0 + self.n_channels = 0 + self.reader = 0 + + #create canvas1 + self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) + self.canvas1.place(x=20,y=20) + + self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) + self.button1.configure(width = 18, height = 4, background = "#33B5E5") + self.button1.place(x=30,y=60) + + self.label1 = Label(self.canvas1, text="Filename", fg='blue') + self.label1.configure(width = 23, height = 2) + self.label1.place(x=288,y=20) + + self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") + self.Text1.config(state=DISABLED) + self.Text1.place(x=288,y=40) + + self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') + self.label2.configure(width = 23, height = 2) + self.label2.place(x=288,y=70) + + self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text2.config(state=DISABLED) + self.Text2.place(x=288,y=90) + + self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') + self.label3.configure(width = 23, height = 2) + self.label3.place(x=288,y=120) + + self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text3.config(state=DISABLED) + self.Text3.place(x=288,y=140) + + + #create canvas2 + self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas2.place(x=280,y=210) + + self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) + self.button4.configure(width = 20, height = 2, background = "#33B5E5") + self.button4.place(x=28,y=10) + + self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") + self.Text4.config(state=DISABLED) + self.Text4.place(x=28,y=50) + + + + self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') + self.label5.configure(width = 13) + self.label5.place(x=60,y=90) + + self.entry1 = Entry(self.canvas2,width = 13) + self.entry1.place(x = 60,y=108) + + self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) + self.button5.place(x=180,y=90) + + self.label6 = Label(self.canvas2, text="Grid size", fg='blue') + self.label6.configure(width = 13) + self.label6.place(x=60,y=135) + + self.entry2 = Entry(self.canvas2,width = 13) + self.entry2.place(x = 60,y=153) + + self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) + self.button6.place(x=180,y=135) + + self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) + self.button2.configure(width = 20, height = 3, background = "#33B5E5") + self.button2.place(x=30,y=180) + + #create canvas3 + self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas3.place(x=20,y=210) + + self.label8 = Label(self.canvas3, text="Select cell", fg='blue') + self.label8.configure(width = 13) + self.label8.place(x=70,y=40) + + self.label9 = Label(self.canvas3, text="Select channel", fg='blue') + self.label9.configure(width = 13) + self.label9.place(x=70,y=120) + + self.entry4 = Entry(self.canvas3,width = 13) + self.entry4.place(x = 70,y=58) + + self.entry5 = Entry(self.canvas3,width= 13) + self.entry5.place(x = 70,y=138) + + self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) + self.button3.configure(width = 20, height = 3, background = "#33B5E5") + self.button3.place(x=30,y=180) + + + def messageBox1(self): + text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." + tkMessageBox.showinfo("", text) + + def messageBox2(self): + text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 32 and will yield output images with 32x32 cells per montage. The maximum possible value is 100." + tkMessageBox.showinfo("", text) + + def chooseDir(self): + self.dirPath = tkFileDialog.askdirectory() + self.Text4.config(state=NORMAL) + self.Text4.delete(1.0, END) + self.Text4.insert(END,self.dirPath) + self.Text4.config(state=DISABLED) + + def read_cif(self): + old_file = self.filename + self.filename = tkFileDialog.askopenfilename() + if type(self.filename) != tuple: + if self.filename.lower().endswith('.cif'): + try: + self.Text1.config(state=NORMAL) + self.Text2.config(state=NORMAL) + self.Text3.config(state=NORMAL) + self.Text1.delete(1.0, END) + self.Text2.delete(1.0, END) + self.Text3.delete(1.0, END) + self.entry1.delete(0,END) + self.entry2.delete(0,END) + + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) + self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) + self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) + + #check, whether the .cif file is ok + if self.n_images == 0 or self.n_channels == 0: + raise ValueError('There is nothing in the file') + self.Text1.insert(END,self.filename) + self.Text1.config(state=DISABLED) + self.Text2.insert(END,self.n_images) + self.Text2.config(state=DISABLED) + self.Text3.insert(END,self.n_channels) + self.Text3.config(state=DISABLED) + self.entry1.insert(0,'1-'+str(self.n_channels)) + self.entry2.insert(0,32) + except: + tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") + if self.filename and not self.filename.lower().endswith('.cif'): + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def generate_tifs(self): + if self.reader != 0: + if type(self.dirPath) != tuple: + if os.path.isdir(self.dirPath): + output = self.dirPath + + try: + chooseChannels = self.entry1.get() + channels = [] + t = chooseChannels.split(',') + for k in t: + r = k.split('-') + for e in range(int(r[0]),int(r[-1])+1): + channels.append(e-1) + channels = list(set(channels)) + n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) + if max(channels) < n_channels and min(channels) >= 0: + try: + grid_size = int(self.entry2.get()) + if grid_size < 1 or grid_size > 100: + raise ValueError + + self.stitch(self.reader, output,grid_size, 55, channels) + tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") + + except ValueError: + tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") + + else: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + except: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def display_cell(self): + flag = False + if self.reader != 0: + try: + selected_cell = int(self.entry4.get()) + selected_chan = int(self.entry5.get()) + if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: + raise ValueError + image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) + maxi = numpy.amax(image) + mini = numpy.amin(image) + ''' + f: [min,max] -> [0,255], f = m*x+n + ''' + image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) + flag = True + except: + tkMessageBox.showinfo("Error", "Please select an available cell and channel.") + if flag: + plt.imshow(image, cmap='gray') + plt.show() + + + + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + + def stitch(self,reader, output, montage_size, image_size, channels = []): + + n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) + n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) + + chunk_size = montage_size**2 + n_chunks = self.__compute_chunks(n_images/2,montage_size) + + if len(channels) == 0: + channels = range(n_channels) + + for channel in channels: + for i in range(n_chunks): + try: + images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] + except javabridge.jutil.JavaException: + break + + images = [self.__pad_or_crop(image, image_size) for image in images] + montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) + + if i == (n_chunks-1): + montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) + + skimage.io.imsave(os.path.join(output, "ch{:d}Im{:d}.tif".format(channel + 1,i+1)), montage) + + def __pad_or_crop(self,image, image_size): + bigger = max(image.shape[0], image.shape[1], image_size) + + pad_x = float(bigger - image.shape[0]) + pad_y = float(bigger - image.shape[1]) + + pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) + pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) + sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] + + std = numpy.std(sample) + + mean = numpy.mean(sample) + + def normal(vector, pad_width, iaxis, kwargs): + vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) + vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) + return vector + + if bigger == image_size: + return numpy.pad(image, (pad_width_x, pad_width_y), normal) + else: + if bigger == image.shape[0]: + temp_image = numpy.pad(image, (pad_width_y), normal) + else: + temp_image = numpy.pad(image, (pad_width_x), normal) + return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] + + def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): + pad_x = float(montage_size*image_size - small_montage.shape[0]) + pad_y = float(montage_size*image_size - small_montage.shape[1]) + npad = ((0,int(pad_y)), (0,int(pad_x))) + return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) + + def __compute_chunks(self,n_images, montage_size): + def remainder(images, groups): + return (images - groups * (montage_size ** 2)) + + n_groups = 1 + while remainder(n_images, n_groups) > 0: + n_groups += 1 + return n_groups + @click.command() -@click.argument("image", type=click.Path(exists=True)) -@click.option("-o", "--output", type=click.Path(exists=False)) +@click.option("-image", type=click.Path(exists=True)) +@click.option("-output", type=click.Path(exists=False)) @click.option("--image-size", default=55) @click.option("--montage-size", default=30) - - -def __main__(image, output, image_size, montage_size): - try: - javabridge.start_vm(class_path=bioformats.JARS) - - os.mkdir(output) - - __stitch(image, output, image_size, montage_size) - finally: - javabridge.kill_vm() - - -def __stitch(filename, output, image_size, montage_size): - reader = bioformats.formatreader.get_image_reader("tmp", path=filename) - - image_count = javabridge.call(reader.metadata, "getImageCount", "()I") - - channel_count = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) - - n_chunks = __compute_chunks(image_count/2,montage_size) - chunk_size = montage_size**2 - - for channel in range(channel_count): - for chunk in range(n_chunks): - try: - images = [ - reader.read(c=channel, series=image) for image in range(image_count)[::2][chunk*chunk_size:(chunk+1)*chunk_size] - ] - except javabridge.jutil.JavaException: - break - - images = [__pad_or_crop(image, image_size) for image in images] - - montage = skimage.util.montage.montage2d(numpy.asarray(images), 0) - - if chunk == (n_chunks-1): - montage = __pad_to_same_chunk_size(montage, image_size, montage_size) - - skimage.io.imsave(os.path.join(output, "ch{:d}_{:d}.tif".format(channel + 1, chunk + 1)), montage) - - -def __pad_or_crop(image, image_size): - bigger = max(image.shape[0], image.shape[1], image_size) - - pad_x = float(bigger - image.shape[0]) - pad_y = float(bigger - image.shape[1]) - - pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) - pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) - sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] - - std = numpy.std(sample) - - mean = numpy.mean(sample) - - def normal(vector, pad_width, iaxis, kwargs): - vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) - vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) - return vector - - if bigger == image_size: - return numpy.pad(image, (pad_width_x, pad_width_y), normal) - else: - if bigger == image.shape[0]: - temp_image = numpy.pad(image, (pad_width_y), normal) - else: - temp_image = numpy.pad(image, (pad_width_x), normal) - return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] - - -def __pad_to_same_chunk_size(small_montage, image_size, montage_size): - pad_x = float(montage_size*image_size - small_montage.shape[0]) - - pad_y = float(montage_size*image_size - small_montage.shape[1]) - - npad = ((0,int(pad_y)), (0,int(pad_x))) - - return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) - - -def __compute_chunks(n_images, montage_size): - - def remainder(images, groups): - return (images - groups * (montage_size ** 2)) - - n_groups = 1 - - while remainder(n_images, n_groups) > 0: - n_groups += 1 - - return n_groups - -if __name__ == "__main__": - __main__() + +def main(image,output,image_size, montage_size): + root = Tk() + root.geometry('540x470+10+50') + app = Stitching(root) + if image == None: + + root.protocol("WM_DELETE_WINDOW", on_closing) + img = PhotoImage(file='icon.png') + root.tk.call('wm', 'iconphoto', root._w, img) + app.mainloop() + else: + try: + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + try: + os.mkdir(output) + except: + pass + reader = bioformats.formatreader.get_image_reader("tmp", path=image) + app.stitch(reader, output, montage_size,image_size) + javabridge.kill_vm() + except: + pass + +def on_closing(): + if tkMessageBox.askokcancel("Quit", "Do you want to quit?"): + try: + javabridge.kill_vm() + finally: + quit() + +if __name__ == '__main__': + main() From 25e02a76e1ba74878b53b1f5059cce0dc288971d Mon Sep 17 00:00:00 2001 From: Christian Nitzsche Date: Wed, 25 Jan 2017 15:14:22 +0100 Subject: [PATCH 07/13] update Readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9362419..d1ffa0f 100755 --- a/README.rst +++ b/README.rst @@ -57,7 +57,7 @@ The GUI window will open where you can select a .cif file to generate the montag OR 2. To use the command line, type - $ python stitching -image='path/to/IMAGE' -output path/to/OUTPUT_DIRECTORY + $ python stitching -image='path/to/IMAGE' -output='path/to/OUTPUT_DIRECTORY' Optional: From 106b541c4b3c0fd889f664ff64abf204a151dad0 Mon Sep 17 00:00:00 2001 From: Christian Nitzsche Date: Wed, 25 Jan 2017 15:18:12 +0100 Subject: [PATCH 08/13] update Readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d1ffa0f..d4073b3 100755 --- a/README.rst +++ b/README.rst @@ -51,7 +51,7 @@ Generates per-channel montages from IMAGE saved to OUTPUT_DIRECTORY. Each image Open a new command line window (also called command prompt window in Windows OS or terminal in MAC OS). Now, to generate the montages, you can either (1) start the stitching GUI or (2) use the command line. 1. To use the GUI, type - $ python stitching.py + $ python stitching The GUI window will open where you can select a .cif file to generate the montages. Optionally you can also display selected images from the .cif file. OR From 25e2f2f0043ce81ee8bcfe1dc4a9e0b3d1e511b0 Mon Sep 17 00:00:00 2001 From: Christian Nitzsche Date: Wed, 25 Jan 2017 15:34:38 +0100 Subject: [PATCH 09/13] update main.py --- stitching/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stitching/__main__.py b/stitching/__main__.py index f16b9cd..c3c059a 100644 --- a/stitching/__main__.py +++ b/stitching/__main__.py @@ -132,7 +132,7 @@ def messageBox1(self): tkMessageBox.showinfo("", text) def messageBox2(self): - text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 32 and will yield output images with 32x32 cells per montage. The maximum possible value is 100." + text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 30 and will yield output images with 30x30 cells per montage. The maximum possible value is 100." tkMessageBox.showinfo("", text) def chooseDir(self): @@ -172,7 +172,7 @@ def read_cif(self): self.Text3.insert(END,self.n_channels) self.Text3.config(state=DISABLED) self.entry1.insert(0,'1-'+str(self.n_channels)) - self.entry2.insert(0,32) + self.entry2.insert(0,30) except: tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") if self.filename and not self.filename.lower().endswith('.cif'): From bba6567ac247bc05e38166e190fd8d471f14637c Mon Sep 17 00:00:00 2001 From: ChristianNitzsche Date: Fri, 24 Feb 2017 14:37:05 +0100 Subject: [PATCH 10/13] Delete icon.png --- icon.png | Bin 12095 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 icon.png diff --git a/icon.png b/icon.png deleted file mode 100644 index b549426ea8c58cbb1fc9afd07d8cc7c0e87e18e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12095 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 z zVHKhehQ!8PpVtdE5&O|oUT?#QP2Y>%49##BZE?vl9<)dmjK3;d{SRFL-M^{Xjkne4 zBFo44nJbv3UUQE}sKM$cXe-k_xbhUfIB_F-SMEF~mgT|lWVEVl{msO2`JJY`8-4*o z!*={A6zkRXrZQ9ycR{OSTDifjmh<6nQjy^)%4UkxFHG)cJmw)xG4$?XIJ~b3YYrP# zKcjuU^St9VzRs^u*`jEfG!v6ia_Hqe+eA&dwB75ys@0*_0$W9)`whR#UOybf9pBLD z?F5v+n@0+&bmaA%J6l^&`}|)}6Pg%w470SDLPSbYULb}g1M(`uAM;bM!(9yY@xK`k zUllW72U}l9NqcX{#~VOc@UdVB3IT?K=t1Wo(@iuCXU`dP!D&2zOpJ%=!GZ?jwO-AqaC23qN`k|X!GWO0A7It AssI20 From 69bc0a06ffcdff5a0aa6c4ca0a300e7bc5233199 Mon Sep 17 00:00:00 2001 From: ChristianNitzsche Date: Fri, 24 Feb 2017 14:37:17 +0100 Subject: [PATCH 11/13] Delete button.png --- button.png | Bin 2955 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 button.png diff --git a/button.png b/button.png deleted file mode 100644 index 3863e4dea3ea24411c51726bf97b33e50bcce7d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2955 zcmZ`*X*ARi7yb#^*JP3!cFwqm@hK}0KjHw07Xzy;NM`RrDk1< z91Z}Ou6gR{z&)Lv0U&fdGt(XZ0txC~-%?+S)8fAK1g`TOEQZP8w2>qe%Cr_=!*zWU zn$AfjB`E1~nrU@fUME^X-_}SS_BQ;!;wO}sPpMHN&1PaA%X3cbw zd*|?sWEu=H>z<<2ZPy$QIfdKmr&9TLZk>o&qe$nR;*QP5(OiG&!`nGwl>3bv3P z^!9lCj!n8cG-~qaK?-{dcj2u`5Ff1EDyD(!VZ6qfx zcP!u%i>hJc!HCo_tTI>@@G4=%2bfK;CILbyn$b?*01zuyLW@!Sy_E2|^-gsIhfjxlsC)2)$K&43D$` zdns02vW$Z~Th`hsS}Xhn7a0O6@yVo)XxkQqEoz~aAS`wmS$&nFAA_RP>o6Gkk#LisY?~ zpkBUS!QNz5dz4PDl#qBkveo7|cRyjjlFqmb-lZdAR&=ijIfMJabJ1wH00I{+I~Woj zA`AsyzGGe*=ovX4Y(x9!vhSm3g|&<7;R0<5(dimmAVY*$ir9%*tr$T}&VZSZQ3u=U zd`>==L`#WHX?)3i$y|wVsc0!n>5q~prJ6nRt^zJIuDiYHUiD1f z9>luebS6~W5`EFJZp z7kUsb;qT!>%>r!_aV}Ln1PdJ6a7oNOy~j~pUMjl{_MF&CtPP3}di@vkx0TuGqp6r_ zsqtJnSUa=oa;Xh&ycmoyM?A3Zv-CELsC;JaPUtSX0$(@Jr#N`hd8`gC4rMzT)`nIJ zSh09pyr}dn&Z$7Y3A@iy{Ba~{6jJukntw!j@Y!JXNP98uO7`-HWq~0+RII&YyNIXm zNfMV`I`y8+8+Aiqsf7!;Ec|Hsp3Qw@&3iF#qVrEdv=^{+*z;J4cncj6NuA_J>PV?e z+1DG^+p|u$hEz`wiQaSk&VsWBvpSzRj6KBH#MO+$VH&Sq<}kx-jqzWHzTElh^+j(C z-5}AD9YqrG^>c6kc-(rR&tm=*{glERgIu=xWUFjzQsq>Ygc`2#nz;KB{H3D*uE*Cp z_IeE;nL4uurrPqxFh7D9zvB-NwVuJ54}m5D_CNT>m{!OC%s25)7*F)=CbYeXDm=l^ z4tIVl?48ajPI9huep>vw=YwZ4#nrv8mat{Nz?F9{^_%xhBI*lDxq16tIw}f9K;^f1 zwlGb+`{@`0+KXB%CJb9kRIP0TLh7keLZ-KodF6Q>;f}59Z53_yq5RCsu!O^!|fzxgaR9FlCD=a(7$m|F#}@$>KxT})=%NWcS15Ub$HC$Ex6#SQu&^&MX_W@>u5s=IlR6d z#|==FlgCW+H7`UY!k_3A6#UmA$>gqflKim9!=d^~0Snk>lY zoHLR$oO6YET-)zrn4y#7nbp3 z`R9YHCM=s*j2xlURU+7;zeRCQ(VJEIY%$gQVSyn-Yq8vvbe#zKi1c0cG4I;F&~m7! z$7&thu2s)`7Rsvy(3JC?BFfO@-!wkeTct6w%_ zxfq_Xe;4wqvMiISI6!I9n7cmHi;Fx9zs7Swn+Rj4X$5q7^znnPcn%$=fm5dav%0#Db@Ih{+e%f zd6t^C6ccZ`RSZNKod9u$#Mx=vVHZ&SJ80YB#d&y>lrtFx&O+0c#!$)oQZM37PH$R)!LQlM|;Md7)1pWnon+f%hr%{BhhNVBx zqF(wu60^sK~H?*t0!tZLQXW+Pyb90ldv4eLh1rcd|VpI zp{hHQ(}SM=L^uf*)=k6&TA za3J?tu2&*c{1q-4*Kew3G8xr8Klg!s!>m-MOK zvDN&aw;`X_!`*JX=eL_1HiHa06?+<02Gk>&P<(3*uY^H zx@0o>WfR#Viy{z70sJX?X_V*+a#=fBHIWifLjKT424g8suPFePEujEJ3eZFW2oxZX z0t8dogDF531)xxXFBAYv0X|WH4hoP&Nq$Q{J3DjyUFJk}p>Zf{4ccDD8?dRNuQ6XKavu-7i8FdP#hSKJbGf-2`kNfX-t2Iy`(`P-fOp`z z>jg$-3T;eRb@yaI?x|KfLFa(O9+xN5J2o_)qeEZtT?3ly_I}IuL0hfzV-+<;;_qXo zMp+)x9Hg;LyzioUxkF!Lqho!hKNh=MKR96(g%%>Z84<1KwfyaPIO0|8>0s0)#n8I< z&@PT>SBP_vD;0nn@(L<4@(MCHlq?ifA&M#x#amMH3J`hu(Glyy{}K55yLh^V{eJ=O v>AE0Q!1J#|fQuW%$~EYLr@tQnhw8~F$lesDN3q Date: Fri, 24 Feb 2017 14:59:25 +0100 Subject: [PATCH 12/13] update Readme, main.py --- README.rst | 15 +++++ button.gif | Bin 0 -> 337 bytes icon.gif | Bin 0 -> 12592 bytes stitching/__main__.py | 139 ++++++++++++++++++++++-------------------- 4 files changed, 88 insertions(+), 66 deletions(-) create mode 100644 button.gif create mode 100644 icon.gif diff --git a/README.rst b/README.rst index d4073b3..089b9f4 100755 --- a/README.rst +++ b/README.rst @@ -35,6 +35,21 @@ matplotlib Additional in Windows OS: Visual C++ 9.0 +For MacOSX users +================ + +The error " libc++abi.dylib: terminating with uncaught exception of type ..." should be noticed to MacOSX users. To solve this: + +If users have installed "pip matplotlib", there is a directory in root called ~/.matplotlib + +Create an empty file ~/.matplotlib/matplotlibrc +add the following code: +backend: TkAgg + +Further see: + +https://www.python.org/download/mac/tcltk/ + Installation ============ diff --git a/button.gif b/button.gif new file mode 100644 index 0000000000000000000000000000000000000000..08c3263f0618e842b7ba05b154aedc9a0ff744c7 GIT binary patch literal 337 zcmZ?wbhEHblwpuzXpv>Obm`Kp9hX9;UzVx4%#eTCZNlZcRhL%myJXRMxnRkqBd0HE zHD6Aedzk?!yW}#%rppXFE;FpY%rNgVL)m4%vdaw9FEd=e%y8y1L-S>ZW0x8BUuNjK z+_Ul0e;82wC+J+1npl#WqEMb$lA+-4=^MbH!vF*z2QaW6R8Z(kQs8k}CDiJ;?|tK% zEmk`WW?tFTrD55yz+FX$&%kZt4u(FSJ#CK}8(ma{<&u^xxS}=9BkR$I7z3L*o60su zguUa*X9)Nrm$+khdV^lDM4o^xD{DQAu#}iohAnryfEZu9x3rk(6mR~dnXc(`UGv+G zy%*JIFDPd84w9~CV$g8O6QQ(r}~Nv)&R-Yg|+|y literal 0 HcmV?d00001 diff --git a/icon.gif b/icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..8e120d75d7479357751e2cb336234a317b5fc99e GIT binary patch literal 12592 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�@l)diRxV$uR`s8ZDXT`b7mV|tK?rJ2MU;r=G5Tc!l2OWMw`~Ik0Jq5NJx{n^ zr?bcg9u^tnupkc2ob!4WXF47s%jZ6Or@YfeQXYNLs@Swq5~$AU;ocd)Q94aLji}Bd z4g$~!Df;Z63ogcS@@dt0o_lF$0a#~IVk8Ylr^NY94!bukKTvM};rZC@PEosNq_dWy z6Q|JnfU&$5fT_l;5^Q?mJHMrUmPgaZD)Q@HJT}L362V12y*wZW8o}VXgWqQ(lw3R< zTK>HU8w-5AM62hQ(mwgR_jTyN*3v(^{DRJ&d7`u4kyR#t>zWUPZ>(cYh2L0JCSbYR zpFXmuQRl+(AWMQs`uhLu_0q_$^4=e+@=O(a5q~i;@Bj>o>R*~%0ERAL)0xN&#+TFn8M*udaDtZ$z;Kj6HiE5Yx0phtz>$!}F|m6C zYOBSgPA9u1XG(2+D3DDLUuD=%R?V5P05LoAgJ5v6JvtJyD>vLdkNdaWa0sgiw*uQ2 zi@Y`flQRGfC3@CX+4EM8Q1Dh-s1>kWd%$l%Sfr^YBJX#2AntOzys`j|IV^hx7ERlbTX z6Ns6IcOFV-<``dE8R7_m1@0tZsutY)%%7AQ%!~8`*T>J=jIU7w{Yq?(xY`|!CO@Z# z%5!1>zn;+u5ymjxXhgyw5Um2E@S8g%gVXs74l`~p3xnZI#~3OFJFrBkvttfu2x?>} zncn;)njQ*c$^t>q9=0P;=?j=tD2ZbKa}7pAU0{xdPJ(aNwb8Eg3DmV&6(UDsLc&h& z=>vc<-0a+~mfpVK=50G*pgGLr!=GBpxer34?5hVu@4I*WmAUWrN-50g{=$PW$G|Ux z7v`a7f4hw%A;9{@osHN|~ubV?*t!*Lmg#`%Frf@X)hp2EoLUu>-WQ;Xd@pM9XSn+H| zVOA_kYgYG;($6K^SfyVdNpLqW)}sC?U2Z 100: raise ValueError - self.stitch(self.reader, output,grid_size, 55, channels) tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") @@ -219,28 +222,26 @@ def generate_tifs(self): def display_cell(self): flag = False - if self.reader != 0: - try: - selected_cell = int(self.entry4.get()) - selected_chan = int(self.entry5.get()) - if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: - raise ValueError - image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) - maxi = numpy.amax(image) - mini = numpy.amin(image) - ''' - f: [min,max] -> [0,255], f = m*x+n - ''' - image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) - flag = True - except: - tkMessageBox.showinfo("Error", "Please select an available cell and channel.") - if flag: - plt.imshow(image, cmap='gray') - plt.show() - - - + if self.reader == 0: + return + try: + selected_cell = int(self.entry4.get()) + selected_chan = int(self.entry5.get()) + if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: + raise ValueError + image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) + maxi = numpy.amax(image) + mini = numpy.amin(image) + ''' + f: [min,max] -> [0,255], f = m*x+n + ''' + image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) + flag = True + except: + tkMessageBox.showinfo("Error", "Please select an available cell and channel.") + if flag: + plt.imshow(image, cmap='gray') + plt.show() else: tkMessageBox.showinfo("Error", "Please choose a .cif file.") @@ -268,8 +269,16 @@ def stitch(self,reader, output, montage_size, image_size, channels = []): if i == (n_chunks-1): montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) - skimage.io.imsave(os.path.join(output, "ch{:d}Im{:d}.tif".format(channel + 1,i+1)), montage) + skimage.io.imsave(os.path.join(output, "ch{:d}_{:d}.tif".format(channel + 1,i+1)), montage) + + def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): + pad_x = float(montage_size*image_size - small_montage.shape[0]) + pad_y = float(montage_size*image_size - small_montage.shape[1]) + npad = ((0,int(pad_y)), (0,int(pad_x))) + return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) + + def __pad_or_crop(self,image, image_size): bigger = max(image.shape[0], image.shape[1], image_size) @@ -289,20 +298,18 @@ def normal(vector, pad_width, iaxis, kwargs): vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) return vector - if bigger == image_size: + if (image_size > image.shape[0]) & (image_size > image.shape[1]): return numpy.pad(image, (pad_width_x, pad_width_y), normal) else: - if bigger == image.shape[0]: + if bigger > image.shape[1]: temp_image = numpy.pad(image, (pad_width_y), normal) else: - temp_image = numpy.pad(image, (pad_width_x), normal) - return temp_image[(bigger - image_size)/2:(bigger + image_size)/2,(bigger - image_size)/2:(bigger + image_size)/2] - - def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): - pad_x = float(montage_size*image_size - small_montage.shape[0]) - pad_y = float(montage_size*image_size - small_montage.shape[1]) - npad = ((0,int(pad_y)), (0,int(pad_x))) - return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) + if bigger > image.shape[0]: + temp_image = numpy.pad(image, (pad_width_x), normal) + else: + temp_image = image + return temp_image[(temp_image.shape[0] - image_size)/2:(temp_image.shape[0] + image_size)/2,(temp_image.shape[1] - image_size)/2:(temp_image.shape[1] + image_size)/2] + def __compute_chunks(self,n_images, montage_size): def remainder(images, groups): @@ -327,7 +334,7 @@ def main(image,output,image_size, montage_size): if image == None: root.protocol("WM_DELETE_WINDOW", on_closing) - img = PhotoImage(file='icon.png') + img = PhotoImage(file='icon.gif') root.tk.call('wm', 'iconphoto', root._w, img) app.mainloop() else: From 0414f0589d109d3e2e8191170ff4361274bc1678 Mon Sep 17 00:00:00 2001 From: Christian Nitzsche Date: Wed, 8 Mar 2017 10:39:21 +0100 Subject: [PATCH 13/13] update main.py --- stitching/__main__.py | 510 +++++++++++++++++++++--------------------- 1 file changed, 259 insertions(+), 251 deletions(-) diff --git a/stitching/__main__.py b/stitching/__main__.py index f1c54de..ffa22a1 100644 --- a/stitching/__main__.py +++ b/stitching/__main__.py @@ -16,309 +16,317 @@ class Stitching(Frame): - def __init__(self, parent): - Frame.__init__(self, parent) - self.parent = parent - self.initUI() + def __init__(self, parent): + Frame.__init__(self, parent) + self.parent = parent + self.initUI() - def initUI(self): - self.parent.title("Stitching") - self.config(bg = '#F0F0F0') - self.pack(fill = BOTH, expand = 1) - self.photo=PhotoImage(file="button.gif") - - self.filename = ' ' - self.dirPath = ' ' - self.n_images = 0 - self.n_channels = 0 - self.reader = 0 + def initUI(self): + self.parent.title("Stitching") + self.config(bg = '#F0F0F0') + self.pack(fill = BOTH, expand = 1) + self.photo=PhotoImage(file="button.gif") + + self.filename = ' ' + self.dirPath = ' ' + self.n_images = 0 + self.n_channels = 0 + self.reader = 0 #create canvas1 - self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) - self.canvas1.place(x=20,y=20) + self.canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 500, height = 180) + self.canvas1.place(x=20,y=20) - self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) - self.button1.configure(width = 18, height = 4, background = "#33B5E5") - self.button1.place(x=30,y=60) + self.button1 = Button(self.canvas1, text = "Open .cif file", command = self.read_cif) + self.button1.configure(width = 18, height = 4, background = "#33B5E5") + self.button1.place(x=30,y=60) - self.label1 = Label(self.canvas1, text="Filename", fg='blue') - self.label1.configure(width = 23, height = 2) - self.label1.place(x=288,y=20) + self.label1 = Label(self.canvas1, text="Filename", fg='blue') + self.label1.configure(width = 23, height = 2) + self.label1.place(x=288,y=20) - self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") - self.Text1.config(state=DISABLED) - self.Text1.place(x=288,y=40) + self.Text1 = Text(self.canvas1, height = 2,width=26, background = "#D2D2D2") + self.Text1.config(state=DISABLED) + self.Text1.place(x=288,y=40) - self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') - self.label2.configure(width = 23, height = 2) - self.label2.place(x=288,y=70) + self.label2 = Label(self.canvas1, text="Number of cells", fg='blue') + self.label2.configure(width = 23, height = 2) + self.label2.place(x=288,y=70) - self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") - self.Text2.config(state=DISABLED) - self.Text2.place(x=288,y=90) + self.Text2 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text2.config(state=DISABLED) + self.Text2.place(x=288,y=90) - self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') - self.label3.configure(width = 23, height = 2) - self.label3.place(x=288,y=120) + self.label3 = Label(self.canvas1, text="Number of channels", fg='blue') + self.label3.configure(width = 23, height = 2) + self.label3.place(x=288,y=120) - self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") - self.Text3.config(state=DISABLED) - self.Text3.place(x=288,y=140) + self.Text3 = Text(self.canvas1, height=2, width=26, background = "#D2D2D2") + self.Text3.config(state=DISABLED) + self.Text3.place(x=288,y=140) #create canvas2 - self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) - self.canvas2.place(x=280,y=210) + self.canvas2 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas2.place(x=280,y=210) - self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) - self.button4.configure(width = 20, height = 2, background = "#33B5E5") - self.button4.place(x=28,y=10) + self.button4 = Button(self.canvas2, text = "Choose output directory", command = self.chooseDir) + self.button4.configure(width = 20, height = 2, background = "#33B5E5") + self.button4.place(x=28,y=10) - self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") - self.Text4.config(state=DISABLED) - self.Text4.place(x=28,y=50) + self.Text4 = Text(self.canvas2, height = 2,width=26,background = "#D2D2D2") + self.Text4.config(state=DISABLED) + self.Text4.place(x=28,y=50) - self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') - self.label5.configure(width = 13) - self.label5.place(x=60,y=90) + self.label5 = Label(self.canvas2, text="Choose channels", fg='blue') + self.label5.configure(width = 13) + self.label5.place(x=60,y=90) - self.entry1 = Entry(self.canvas2,width = 13) - self.entry1.place(x = 60,y=108) + self.entry1 = Entry(self.canvas2,width = 13) + self.entry1.place(x = 60,y=108) - self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) - self.button5.place(x=180,y=90) + self.button5 = Button(self.canvas2, image = self.photo, command = self.messageBox1, height = 30,width = 30) + self.button5.place(x=180,y=90) - self.label6 = Label(self.canvas2, text="Grid size", fg='blue') - self.label6.configure(width = 13) - self.label6.place(x=60,y=135) + self.label6 = Label(self.canvas2, text="Grid size", fg='blue') + self.label6.configure(width = 13) + self.label6.place(x=60,y=135) - self.entry2 = Entry(self.canvas2,width = 13) - self.entry2.place(x = 60,y=153) + self.entry2 = Entry(self.canvas2,width = 13) + self.entry2.place(x = 60,y=153) - self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) - self.button6.place(x=180,y=135) + self.button6 = Button(self.canvas2, image = self.photo, command = self.messageBox2, height = 30,width = 30) + self.button6.place(x=180,y=135) - self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) - self.button2.configure(width = 20, height = 3, background = "#33B5E5") - self.button2.place(x=30,y=180) + self.button2 = Button(self.canvas2, text = "Generate tiled .tif", command = self.generate_tifs) + self.button2.configure(width = 20, height = 3, background = "#33B5E5") + self.button2.place(x=30,y=180) #create canvas3 - self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) - self.canvas3.place(x=20,y=210) - - self.label8 = Label(self.canvas3, text="Select cell", fg='blue') - self.label8.configure(width = 13) - self.label8.place(x=70,y=40) - - self.label9 = Label(self.canvas3, text="Select channel", fg='blue') - self.label9.configure(width = 13) - self.label9.place(x=70,y=120) - - self.entry4 = Entry(self.canvas3,width = 13) - self.entry4.place(x = 70,y=58) - - self.entry5 = Entry(self.canvas3,width= 13) - self.entry5.place(x = 70,y=138) - - self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) - self.button3.configure(width = 20, height = 3, background = "#33B5E5") - self.button3.place(x=30,y=180) - - - def messageBox1(self): - text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." - tkMessageBox.showinfo("", text) - - def messageBox2(self): - text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 30 and will yield output images with 30x30 cells per montage. The maximum possible value is 100." - tkMessageBox.showinfo("", text) - - def chooseDir(self): - self.dirPath = tkFileDialog.askdirectory() - self.Text4.config(state=NORMAL) - self.Text4.delete(1.0, END) - self.Text4.insert(END,self.dirPath) - self.Text4.config(state=DISABLED) - - def read_cif(self): - old_file = self.filename - self.filename = tkFileDialog.askopenfilename() - - if type(self.filename) == tuple: - return - - if self.filename.lower().endswith('.cif'): - try: - self.Text1.config(state=NORMAL) - self.Text2.config(state=NORMAL) - self.Text3.config(state=NORMAL) - self.Text1.delete(1.0, END) - self.Text2.delete(1.0, END) - self.Text3.delete(1.0, END) - self.entry1.delete(0,END) - self.entry2.delete(0,END) + self.canvas3 = Canvas(self, relief = FLAT, background = "#D2D2D2",width = 240, height = 240) + self.canvas3.place(x=20,y=210) + + self.label8 = Label(self.canvas3, text="Select cell", fg='blue') + self.label8.configure(width = 13) + self.label8.place(x=70,y=40) + + self.label9 = Label(self.canvas3, text="Select channel", fg='blue') + self.label9.configure(width = 13) + self.label9.place(x=70,y=120) + + self.entry4 = Entry(self.canvas3,width = 13) + self.entry4.place(x = 70,y=58) + + self.entry5 = Entry(self.canvas3,width= 13) + self.entry5.place(x = 70,y=138) + + self.button3 = Button(self.canvas3, text = "Display image", command = self.display_cell) + self.button3.configure(width = 20, height = 3, background = "#33B5E5") + self.button3.place(x=30,y=180) + + + def messageBox1(self): + text = "Select channel numbers. Separate each channel or range with a comma (such as 1,3,5-7). Per default all channels will be used." + tkMessageBox.showinfo("", text) + + def messageBox2(self): + text = "Enter the number of cells per row and per column in the output image. The default value for the grid size is 30 and will yield output images with 30x30 cells per montage. The maximum possible value is 100." + tkMessageBox.showinfo("", text) + + def chooseDir(self): + self.dirPath = tkFileDialog.askdirectory() + self.Text4.config(state=NORMAL) + self.Text4.delete(1.0, END) + self.Text4.insert(END,self.dirPath) + self.Text4.config(state=DISABLED) + + def read_cif(self): + old_file = self.filename + self.filename = tkFileDialog.askopenfilename() + + if type(self.filename) == tuple: + return + + if self.filename.lower().endswith('.cif'): + try: + self.Text1.config(state=NORMAL) + self.Text2.config(state=NORMAL) + self.Text3.config(state=NORMAL) + self.Text1.delete(1.0, END) + self.Text2.delete(1.0, END) + self.Text3.delete(1.0, END) + self.entry1.delete(0,END) + self.entry2.delete(0,END) - javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') - self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) - self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) - self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) + javabridge.start_vm(class_path=bioformats.JARS, max_heap_size='8G') + self.reader = bioformats.formatreader.get_image_reader("tmp", path=self.filename) + self.n_images = int(0.5*javabridge.call(self.reader.metadata, "getImageCount", "()I")) + self.n_channels = javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0) - #check, whether the .cif file is ok - if self.n_images == 0 or self.n_channels == 0: - raise ValueError('There is nothing in the file') - self.Text1.insert(END,self.filename) - self.Text1.config(state=DISABLED) - self.Text2.insert(END,self.n_images) - self.Text2.config(state=DISABLED) - self.Text3.insert(END,self.n_channels) - self.Text3.config(state=DISABLED) - self.entry1.insert(0,'1-'+str(self.n_channels)) - self.entry2.insert(0,30) - except: - tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") - - if self.filename and not self.filename.lower().endswith('.cif'): - tkMessageBox.showinfo("Error", "Please choose a .cif file.") + if self.n_images == 0 or self.n_channels == 0: + raise ValueError('There is nothing in the file') + self.Text1.insert(END,self.filename) + self.Text1.config(state=DISABLED) + self.Text2.insert(END,self.n_images) + self.Text2.config(state=DISABLED) + self.Text3.insert(END,self.n_channels) + self.Text3.config(state=DISABLED) + self.entry1.insert(0,'1-'+str(self.n_channels)) + self.entry2.insert(0,30) + except: + tkMessageBox.showinfo("Error", "Please choose a correct .cif file.") + + if self.filename and not self.filename.lower().endswith('.cif'): + tkMessageBox.showinfo("Error", "Please choose a .cif file.") - def generate_tifs(self): - if self.reader != 0: - if type(self.dirPath) != tuple: - if os.path.isdir(self.dirPath): - output = self.dirPath + def generate_tifs(self): + if self.reader == 0: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + return + + if type(self.dirPath) == tuple: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + return + + + if os.path.isdir(self.dirPath): + output = self.dirPath + else: + tkMessageBox.showinfo("Error", "Please choose an output directory.") + return - try: - chooseChannels = self.entry1.get() - channels = [] - t = chooseChannels.split(',') - for k in t: - r = k.split('-') - for e in range(int(r[0]),int(r[-1])+1): - channels.append(e-1) - channels = list(set(channels)) - n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) - if max(channels) < n_channels and min(channels) >= 0: - try: - grid_size = int(self.entry2.get()) - if grid_size < 1 or grid_size > 100: - raise ValueError - self.stitch(self.reader, output,grid_size, 55, channels) - tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") - - except ValueError: - tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") + try: + chooseChannels = self.entry1.get() + channels = [] + t = chooseChannels.split(',') + for k in t: + r = k.split('-') + for e in range(int(r[0]),int(r[-1])+1): + channels.append(e-1) + channels = list(set(channels)) + n_channels = int(javabridge.call(self.reader.metadata, "getChannelCount", "(I)I", 0)) + if max(channels) < n_channels and min(channels) >= 0: + try: + grid_size = int(self.entry2.get()) + if grid_size < 1 or grid_size > 100: + raise ValueError + self.stitch(self.reader, output,grid_size, 55, channels) + tkMessageBox.showinfo("", "Tiled .tif images successfully generated.") + except ValueError: + tkMessageBox.showinfo("Error", "Please enter a positive integer number between 1 and 100 as grid size.") + return - else: - tkMessageBox.showinfo("Error", "Please choose correct channels.") - except: - tkMessageBox.showinfo("Error", "Please choose correct channels.") - else: - tkMessageBox.showinfo("Error", "Please choose an output directory.") - else: - tkMessageBox.showinfo("Error", "Please choose an output directory.") - else: - tkMessageBox.showinfo("Error", "Please choose a .cif file.") - - def display_cell(self): - flag = False - if self.reader == 0: - return - try: - selected_cell = int(self.entry4.get()) - selected_chan = int(self.entry5.get()) - if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: - raise ValueError - image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) - maxi = numpy.amax(image) - mini = numpy.amin(image) - ''' - f: [min,max] -> [0,255], f = m*x+n - ''' - image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) - flag = True - except: - tkMessageBox.showinfo("Error", "Please select an available cell and channel.") - if flag: - plt.imshow(image, cmap='gray') - plt.show() - else: - tkMessageBox.showinfo("Error", "Please choose a .cif file.") + else: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + return + except: + tkMessageBox.showinfo("Error", "Please choose correct channels.") + + + + def display_cell(self): + flag = False + if self.reader == 0: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") + return + try: + selected_cell = int(self.entry4.get()) + selected_chan = int(self.entry5.get()) + if selected_cell < 1 or selected_cell > self.n_images or selected_chan < 1 or selected_chan > self.n_channels: + raise ValueError + image = self.__pad_or_crop(self.reader.read(c=selected_chan-1, series=2*(selected_cell-1)), 55) + maxi = numpy.amax(image) + mini = numpy.amin(image) + ''' + f: [min,max] -> [0,255], f = m*x+n + ''' + image = (255.0/(maxi-mini))*image - 255*mini/(maxi-mini) + flag = True + except: + tkMessageBox.showinfo("Error", "Please select an available cell and channel.") + return + if flag: + plt.imshow(image, cmap='gray') + plt.show() + else: + tkMessageBox.showinfo("Error", "Please choose a .cif file.") - def stitch(self,reader, output, montage_size, image_size, channels = []): + def stitch(self,reader, output, montage_size, image_size, channels = []): - n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) - n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) + n_images = int(0.5*javabridge.call(reader.metadata, "getImageCount", "()I")) + n_channels = javabridge.call(reader.metadata, "getChannelCount", "(I)I", 0) - chunk_size = montage_size**2 - n_chunks = self.__compute_chunks(n_images/2,montage_size) + chunk_size = montage_size**2 + n_chunks = self.__compute_chunks(n_images/2,montage_size) - if len(channels) == 0: - channels = range(n_channels) + if len(channels) == 0: + channels = range(n_channels) - for channel in channels: - for i in range(n_chunks): - try: - images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] - except javabridge.jutil.JavaException: - break + for channel in channels: + for i in range(n_chunks): + try: + images = [reader.read(c=channel, series=image) for image in range(n_images)[::2][i*chunk_size:(i+1)*chunk_size]] + except javabridge.jutil.JavaException: + break - images = [self.__pad_or_crop(image, image_size) for image in images] - montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) + images = [self.__pad_or_crop(image, image_size) for image in images] + montage = skimage.util.montage.montage2d(numpy.asarray(images), 0, grid_shape = (montage_size,montage_size)) - if i == (n_chunks-1): - montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) + if i == (n_chunks-1): + montage = self.__pad_to_same_chunk_size(montage, image_size, montage_size) - skimage.io.imsave(os.path.join(output, "ch{:d}_{:d}.tif".format(channel + 1,i+1)), montage) + skimage.io.imsave(os.path.join(output, "ch{:d}_{:d}.tif".format(channel + 1,i+1)), montage) - def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): - pad_x = float(montage_size*image_size - small_montage.shape[0]) - pad_y = float(montage_size*image_size - small_montage.shape[1]) - npad = ((0,int(pad_y)), (0,int(pad_x))) - return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) + def __pad_to_same_chunk_size(self,small_montage, image_size, montage_size): + pad_x = float(montage_size*image_size - small_montage.shape[0]) + pad_y = float(montage_size*image_size - small_montage.shape[1]) + npad = ((0,int(pad_y)), (0,int(pad_x))) + return numpy.pad(small_montage, pad_width=npad, mode='constant', constant_values=0) - def __pad_or_crop(self,image, image_size): - bigger = max(image.shape[0], image.shape[1], image_size) + def __pad_or_crop(self,image, image_size): + bigger = max(image.shape[0], image.shape[1], image_size) - pad_x = float(bigger - image.shape[0]) - pad_y = float(bigger - image.shape[1]) + pad_x = float(bigger - image.shape[0]) + pad_y = float(bigger - image.shape[1]) - pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) - pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) - sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] + pad_width_x = (int(math.floor(pad_x / 2)), int(math.ceil(pad_x / 2))) + pad_width_y = (int(math.floor(pad_y / 2)), int(math.ceil(pad_y / 2))) + sample = image[image.shape[0]/2-4:image.shape[0]/2+4, :8] - std = numpy.std(sample) + std = numpy.std(sample) - mean = numpy.mean(sample) + mean = numpy.mean(sample) - def normal(vector, pad_width, iaxis, kwargs): - vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) - vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) - return vector + def normal(vector, pad_width, iaxis, kwargs): + vector[:pad_width[0]] = numpy.random.normal(mean, std, vector[:pad_width[0]].shape) + vector[-pad_width[1]:] = numpy.random.normal(mean, std, vector[-pad_width[1]:].shape) + return vector - if (image_size > image.shape[0]) & (image_size > image.shape[1]): - return numpy.pad(image, (pad_width_x, pad_width_y), normal) - else: - if bigger > image.shape[1]: - temp_image = numpy.pad(image, (pad_width_y), normal) - else: - if bigger > image.shape[0]: - temp_image = numpy.pad(image, (pad_width_x), normal) - else: - temp_image = image - return temp_image[(temp_image.shape[0] - image_size)/2:(temp_image.shape[0] + image_size)/2,(temp_image.shape[1] - image_size)/2:(temp_image.shape[1] + image_size)/2] + if (image_size > image.shape[0]) & (image_size > image.shape[1]): + return numpy.pad(image, (pad_width_x, pad_width_y), normal) + else: + if bigger > image.shape[1]: + temp_image = numpy.pad(image, (pad_width_y), normal) + else: + if bigger > image.shape[0]: + temp_image = numpy.pad(image, (pad_width_x), normal) + else: + temp_image = image + return temp_image[(temp_image.shape[0] - image_size)/2:(temp_image.shape[0] + image_size)/2,(temp_image.shape[1] - image_size)/2:(temp_image.shape[1] + image_size)/2] - def __compute_chunks(self,n_images, montage_size): - def remainder(images, groups): - return (images - groups * (montage_size ** 2)) + def __compute_chunks(self,n_images, montage_size): + def remainder(images, groups): + return (images - groups * (montage_size ** 2)) - n_groups = 1 - while remainder(n_images, n_groups) > 0: - n_groups += 1 - return n_groups + n_groups = 1 + while remainder(n_images, n_groups) > 0: + n_groups += 1 + return n_groups @click.command()