376 lines
16 KiB
Python
376 lines
16 KiB
Python
# String(label="Please enter your name",description="Name field") name
|
|
# OUTPUT String greeting
|
|
|
|
# A Jython script with parameters.
|
|
# It is the duty of the scripting framework to harvest
|
|
# the 'name' parameter from the user, and then display
|
|
# the 'greeting' output parameter, based on its type.
|
|
from ij import IJ # pylint: disable=import-error
|
|
from ij import ImagePlus # pylint: disable=import-error
|
|
from ij.process import ByteProcessor # pylint: disable=import-error
|
|
from ij.process import ImageStatistics
|
|
from ij.plugin import ImageCalculator
|
|
from ij.plugin import ZProjector
|
|
from ij import WindowManager
|
|
|
|
# greeting = "Hello, " + name + "!"
|
|
|
|
|
|
# image prefix :
|
|
# AF
|
|
# blé : coupes de blé
|
|
# CA : coupe d'amande
|
|
# FE : feuille d'épinard
|
|
# GGH : globule gras humain
|
|
# CRF chloroplastes de feuille d'épinard
|
|
# OL : oléosome
|
|
# DARK : dark
|
|
# white :
|
|
#
|
|
# - cin1 : cinétique 1
|
|
# phiG_40x_1 : cinétique avant et après injection enzyme gastrique
|
|
# phiG_40x_Zstack20um_1 : stack
|
|
# 0mn : on commence à enregistrer et on attend 10mn (pour le bleaching) -> phiG_40x_1
|
|
# 10mn : debut injection phase gastrique (poussée)
|
|
# 13mn : la phase gastrique (le petit tuyau contient 20ul) arrive dans la cellule d'un coup (1 nanol)
|
|
# 15mn : on arrête l'injection
|
|
# 50mn : on fait un stack -> phiG_40x_Zstack20um_1
|
|
# 51mn : début d'injection phase intestinale (poussée) -> phiG_I_40x_1
|
|
# x mn : on arrête l'injection
|
|
# 90mn : on fait un stack -> phiG_I_40x_Zstack20um_1
|
|
# - cin2 : autre échantillon similaire à cin1
|
|
# - cond[5678] : condition non réalistes
|
|
import json
|
|
|
|
|
|
class Sequence(object):
|
|
def __init__(self, catalog, sequence_id, micro_manager_metadata_file_path):
|
|
self.catalog = catalog
|
|
self.sequence_id = sequence_id
|
|
self.micro_manager_metadata_file_path = micro_manager_metadata_file_path
|
|
print(micro_manager_metadata_file_path)
|
|
|
|
with open(micro_manager_metadata_file_path, "r") as mmm_file:
|
|
self.mmm = json.load(mmm_file, encoding='latin-1') # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments)
|
|
|
|
@property
|
|
def num_frames(self):
|
|
summary = self.mmm['Summary']
|
|
return int(summary['Frames'])
|
|
|
|
@property
|
|
def width(self):
|
|
summary = self.mmm['Summary']
|
|
return int(summary['Width'])
|
|
|
|
@property
|
|
def height(self):
|
|
summary = self.mmm['Summary']
|
|
return int(summary['Height'])
|
|
|
|
@property
|
|
def num_channels(self):
|
|
summary = self.mmm['Summary']
|
|
return int(summary['Channels'])
|
|
|
|
@property
|
|
def num_slices(self):
|
|
summary = self.mmm['Summary']
|
|
return int(summary['Slices'])
|
|
|
|
@property
|
|
def num_bits_per_pixels(self):
|
|
summary = self.mmm['Summary']
|
|
return int(summary['BitDepth'])
|
|
|
|
def get_root_path(self):
|
|
return '/'.join(self.micro_manager_metadata_file_path.split('/')[:-1])
|
|
|
|
def get_image_file_path(self, channel_index, frame_index, z_index=0):
|
|
'''
|
|
:param int channel_index:
|
|
:param int frame_index:
|
|
:param int z_index:
|
|
'''
|
|
assert frame_index < self.num_frames
|
|
assert channel_index < self.num_channels
|
|
frame_info = self.mmm['FrameKey-%d-%d-%d' % (frame_index, channel_index, z_index)]
|
|
rel_file_path = frame_info['FileName']
|
|
return self.get_root_path() + '/' + rel_file_path
|
|
|
|
def get_channel_index(self, channel_id):
|
|
'''
|
|
:param str channel_id:
|
|
'''
|
|
summary = self.mmm['Summary']
|
|
channel_index = summary['ChNames'].index(channel_id)
|
|
return channel_index
|
|
|
|
def get_black(self):
|
|
''' returns the black sequence related to the the sequence self
|
|
|
|
:return Sequence:
|
|
'''
|
|
seqid_to_black = {
|
|
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0',
|
|
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0',
|
|
}
|
|
white_sequence = seqid_to_black[self.sequence_id]
|
|
return self.catalog.sequences[white_sequence]
|
|
|
|
def get_white(self):
|
|
''' returns the white sequence related to the the sequence self
|
|
|
|
:return Sequence:
|
|
'''
|
|
# assert fixme : res_soleil2018/white/white_24112018_2/Pos0 is visible
|
|
seqid_to_white = {
|
|
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/white/white_24112018_2/Pos0',
|
|
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2': 'res_soleil2018/white/white_24112018_2/Pos0',
|
|
}
|
|
white_sequence = seqid_to_white[self.sequence_id]
|
|
return self.catalog.sequences[white_sequence]
|
|
|
|
def as_hyperstack(self):
|
|
hyperstack = IJ.createHyperStack(self.sequence_id, self.width, self.height, self.num_channels, self.num_slices, self.num_frames, self.num_bits_per_pixels)
|
|
for channel_index in range(self.num_channels):
|
|
for frame_index in range(self.num_frames):
|
|
slice_index = 0
|
|
src_image_file_path = self.get_image_file_path(channel_index=channel_index, frame_index=frame_index)
|
|
# print(src_image_file_path)
|
|
src_image = IJ.openImage(src_image_file_path)
|
|
# print(src_image.getProperties())
|
|
hyperstack.setPositionWithoutUpdate(channel_index + 1, slice_index + 1, frame_index + 1)
|
|
hyperstack.setProcessor(src_image.getProcessor())
|
|
return hyperstack
|
|
|
|
def as_stack(self, channel_id):
|
|
'''
|
|
:param str channel_id: eg 'DM300_327-353_fluo'
|
|
'''
|
|
channel_index = self.get_channel_index(channel_id)
|
|
hyperstack = IJ.createHyperStack(self.sequence_id, self.width, self.height, 1, self.num_slices, self.num_frames, self.num_bits_per_pixels)
|
|
for frame_index in range(self.num_frames):
|
|
slice_index = 0
|
|
src_image_file_path = self.get_image_file_path(channel_index=channel_index, frame_index=frame_index)
|
|
# print(src_image_file_path)
|
|
src_image = IJ.openImage(src_image_file_path)
|
|
# print(src_image.getProperties())
|
|
hyperstack.setPositionWithoutUpdate(channel_index + 1, slice_index + 1, frame_index + 1)
|
|
hyperstack.setProcessor(src_image.getProcessor())
|
|
return hyperstack
|
|
|
|
def open_in_imagej(self):
|
|
# ip = IJ.createHyperStack(title=self.sequence_id, width=self.width, height= self.height, channels=1, slices=1, frames=self.get_num_frames(), bitdepth=16)
|
|
hyperstack = self.as_hyperstack()
|
|
hyperstack.show()
|
|
for channel_index in range(self.num_channels):
|
|
hyperstack.setPositionWithoutUpdate(channel_index + 1, 1, 1)
|
|
IJ.run("Enhance Contrast", "saturated=0.35")
|
|
return hyperstack
|
|
|
|
|
|
class ImageCatalog(object):
|
|
def __init__(self, raw_images_root):
|
|
self.raw_images_root = raw_images_root
|
|
self.sequences = {}
|
|
|
|
# nb : we use the path as sequence id because the "Comment" field in the summary section of the metadata file is not guaranteed to be unique (eg they are the same in res_soleil2018/white/white_24112018_1/Pos0 and in res_soleil2018/white/white_24112018_2/Pos0)
|
|
sequence_ids = []
|
|
sequence_ids.append('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0')
|
|
sequence_ids.append('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2')
|
|
|
|
sequence_ids.append('res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0')
|
|
sequence_ids.append('res_soleil2018/DARK/DARK_40X_zstack_vis_327-353_1/Pos0')
|
|
|
|
# sequence_ids.append('res_soleil2018/white/white_24112018_1/Pos0') # this sequence seems broken (only 5 images while there's supposed to be 201 frames)
|
|
sequence_ids.append('res_soleil2018/white/white_24112018_2/Pos0')
|
|
|
|
for sequence_id in sequence_ids:
|
|
micro_manager_metadata_file_path = raw_images_root + '/' + sequence_id + '/metadata.txt'
|
|
# micro_manager_metadata_file_path = '/tmp/toto.json'
|
|
self.sequences[sequence_id] = Sequence(self, sequence_id, micro_manager_metadata_file_path)
|
|
|
|
def __str__(self):
|
|
for sequence_id, sequence in self.sequences.iteritems():
|
|
return str(sequence_id) + ':' + str(sequence)
|
|
# self.add_micromanager_metadata(raw_images_root + '/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/metadata.txt')
|
|
|
|
# def add_micromanager_metadata(self, micro_manager_metadata_file_path):
|
|
# self.sequences[ micro_manager_metadata_file_path ] = Sequence(self, micro_manager_metadata_file_path)
|
|
|
|
|
|
class IImageProcessingDebugger(object):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def on_image(self, image, image_id):
|
|
'''
|
|
:param ImagePlus image:
|
|
:param str image_id:
|
|
'''
|
|
pass
|
|
|
|
|
|
class NullDebugger(IImageProcessingDebugger):
|
|
|
|
def __init__(self):
|
|
IImageProcessingDebugger.__init__(self)
|
|
|
|
def on_image(self, image, image_id):
|
|
pass
|
|
|
|
|
|
class ImageLogger(IImageProcessingDebugger):
|
|
|
|
def __init__(self):
|
|
IImageProcessingDebugger.__init__(self)
|
|
|
|
def on_image(self, image, image_id):
|
|
# todo : WindowManager
|
|
image.show()
|
|
|
|
|
|
class Lipase(object):
|
|
|
|
def __init__(self, debugger=NullDebugger()):
|
|
'''
|
|
:param IImageProcessingDebugger im_proc_debugger:
|
|
'''
|
|
self.debugger = debugger
|
|
raw_images_root = '/Users/graffy/ownCloud/ipr/lipase/raw-images'
|
|
self.catalog = ImageCatalog(raw_images_root)
|
|
print(self.catalog)
|
|
# catalog.sequences['GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0'].open_in_imagej()
|
|
# catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2'].open_in_imagej()
|
|
# catalog.sequences['DARK_40X_60min_1 im pae min_1/Pos0'].open_in_imagej()
|
|
# catalog.sequences['white_24112018_1/Pos0'].open_in_imagej()
|
|
|
|
def get_image_median_value(self, src_image):
|
|
'''
|
|
:param ImagePlus src_image:
|
|
'''
|
|
# https://imagej.nih.gov/ij/developer/api/ij/process/ImageStatistics.html
|
|
stats = ImageStatistics.getStatistics(src_image.getProcessor(), ImageStatistics.MEDIAN, None)
|
|
# print(stats)
|
|
# print(stats.pixelCount)
|
|
return stats.median
|
|
|
|
def get_image_mean_value(self, src_image):
|
|
'''
|
|
:param ImagePlus src_image:
|
|
'''
|
|
# https://imagej.nih.gov/ij/developer/api/ij/process/ImageStatistics.html
|
|
stats = ImageStatistics.getStatistics(src_image.getProcessor(), ImageStatistics.MEAN, None)
|
|
# print(stats)
|
|
# print(stats.pixelCount)
|
|
return stats.mean
|
|
|
|
def test_get_image_median_value(self):
|
|
image_file_path = '/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/img_000000000_DM300_327-353_fluo_000.tif'
|
|
image = IJ.openImage(image_file_path)
|
|
median_value = self.get_image_median_value(image)
|
|
print('median value : %d' % median_value)
|
|
|
|
def find_depth_index(self, src_image, white_sequence):
|
|
''' finds in the image sequence white_sequence the image that correlates the best to src_image
|
|
|
|
:param ImagePlus src_image:
|
|
:param Sequence white_sequence:
|
|
'''
|
|
self.debugger.on_image(src_image, 'src_image')
|
|
|
|
white_sequence.open_in_imagej()
|
|
|
|
print('image type : ', src_image.getType())
|
|
src_median_value = self.get_image_median_value(src_image)
|
|
normalized_src_processor = src_image.getProcessor().convertToFloat()
|
|
normalized_src_processor.multiply(1.0 / src_median_value)
|
|
for z_index in range(white_sequence.num_slices):
|
|
white_image_file_path = white_sequence.get_image_file_path(channel_index=0, frame_index=0, z_index=z_index)
|
|
white_image = IJ.openImage(white_image_file_path)
|
|
# self.debugger.on_image(white_image, 'white_image')
|
|
white_median_value = self.get_image_median_value(white_image)
|
|
# white_to_src_factor = 1.0 / float(white_median_value)
|
|
normalized_white_processor = white_image.getProcessor().convertToFloat()
|
|
normalized_white_processor.multiply(1.0 / float(white_median_value))
|
|
|
|
normalized_src = ImagePlus() # IJ.openImage("http://imagej.nih.gov/ij/images/boats.gif")
|
|
normalized_src.setProcessor(normalized_src_processor)
|
|
# print(imp1)
|
|
normalized_white = ImagePlus() # IJ.openImage("http://imagej.nih.gov/ij/images/bridge.gif")
|
|
normalized_white.setProcessor(normalized_white_processor)
|
|
# print(imp2)
|
|
ic = ImageCalculator()
|
|
difference_image = ic.run("sub create float", normalized_src, normalized_white)
|
|
# self.debugger.on_image(imp3, 'diff')
|
|
|
|
self.debugger.on_image(normalized_white, 'normalized_white')
|
|
self.debugger.on_image(difference_image, 'difference_image')
|
|
|
|
# imp2mat = ImagePlusMatConverter()
|
|
# white_mat = imp2mat.toMat(white_image.getProcessor())
|
|
# white_mat = imread(white_image_file_path)
|
|
# print(white_image)
|
|
sqdiff_image = ic.run("mul create float", difference_image, difference_image)
|
|
mean_sqdiff = self.get_image_mean_value(sqdiff_image)
|
|
print('difference : ', mean_sqdiff)
|
|
|
|
def compute_sequence_median_image(self, sequence, channel_id='DM300_nofilter_vis'):
|
|
''' computes for each pixel its median value along time, so that in the end we have a single image that only shows structures (things that don't move such as the traps)
|
|
|
|
:param Sequence sequence:
|
|
:param str channel_id:
|
|
'''
|
|
stack = sequence.as_stack(channel_id)
|
|
projector = ZProjector()
|
|
median_image = projector.run(stack, 'median')
|
|
# pixel_value_along_time = ByteProcessor.createProcessor(stack.getDepth(), 1)
|
|
return median_image
|
|
|
|
def extract_background(self, sequence):
|
|
channel_id = 'DM300_nofilter_vis'
|
|
sequence.as_stack(channel_id).show()
|
|
background_image = self.compute_sequence_median_image(sequence, channel_id)
|
|
background_image.show()
|
|
return background_image
|
|
|
|
def test_uniformization(self, sequence):
|
|
white_seq = sequence.get_white()
|
|
channel_index = sequence.get_channel_index('DM300_327-353_fluo')
|
|
src_image_file_path = sequence.get_image_file_path(channel_index=channel_index, frame_index=0, z_index=0)
|
|
src_image = IJ.openImage(src_image_file_path)
|
|
# src_image = IJ.openImage(src_image_file_path)
|
|
self.find_depth_index(src_image, white_seq)
|
|
|
|
def process_sequence(self, sequence_id):
|
|
'''
|
|
:param str sequence_id:
|
|
'''
|
|
sequence = self.catalog.sequences[sequence_id]
|
|
self.extract_background(sequence)
|
|
|
|
self.test_uniformization(sequence)
|
|
|
|
|
|
def run_script():
|
|
lipase = Lipase(debugger=ImageLogger())
|
|
lipase.process_sequence('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2')
|
|
|
|
if False:
|
|
sequence = lipase.catalog.sequences['GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0']
|
|
src_image_file_path = sequence.get_image_file_path(channel_index=sequence.get_channel_index('DM300_327-353_fluo'), frame_index=3)
|
|
src_image = IJ.openImage(src_image_file_path) # pylint: disable=unused-variable
|
|
|
|
# dark_image = IJ.openImage(raw_images_root + '/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/img_000000000_DM300_327-353_fluo_000.tif')
|
|
# src_image.show()
|
|
# assert src_image
|
|
|
|
|
|
# If a Jython script is run, the variable __name__ contains the string '__main__'.
|
|
# If a script is loaded as module, __name__ has a different value.
|
|
if __name__ in ['__builtin__', '__main__']:
|
|
run_script()
|
|
# test_get_image_median_value()
|