initial (non working) code and structure

This commit is contained in:
2021-07-09 12:38:08 +02:00
parent 43c4f026a7
commit 9fb8436ea3
5 changed files with 208 additions and 0 deletions

132
situr/image/situ_image.py Normal file
View File

@@ -0,0 +1,132 @@
def extend_dim(array):
ones = np.ones((array.shape[0], 1))
return np.append(array, ones, axis=1)
def remove_dim(array):
return array[:,:-1]
class SituImage:
"""
A class to representing one situ image with different focus levels.
...
Attributes
----------
data : numpy.array
the image data containing all the channels of shape (channels, focus_levels, image_size_y, image_size_x)
files: (list(list(str)))
A list of lists. Each inner list corresponds to one focus level. Its contents correspons to a file for each channel.
nucleaus_channel : int
tells which channel is used for showing where the cell nucleuses are.
"""
def __init__(self, file_list, nucleaus_channel=4):
self.files = file_list
self.data = None
self.nucleaus_channel = nucleaus_channel
def get_data(self):
if self.data is None:
self._load_image()
return self.data
def get_channel(self, channel):
'''
Loads and returns the specified channel for all focus_levels.
Returns:
numpy.array: The loaded image of shape (focus_level, width, height)
'''
return self.get_data()[channel,:,:,:]
def _load_image(self):
'''
Loads the channels of an image from seperate files and returns them as numpy array.
Parameters:
channel (int):
The channel that should be used
Returns:
numpy.array: The loaded image of shape (channels, focus_level, width, height)
'''
image_list = []
for focus_level_list in self.files:
channels = []
for file in focus_level_list:
channels.append(np.array(Image.open(file)))
image_list.append(channels)
self.data = np.array(image_list)
def unload_image(self):
'''
Unloads the image data to free up memory
'''
self.data = None
def show_channel(self, channel, focus_level=0):
'''
Prints and returns the specified channel and focus_level of the image.
Parameters:
channel (int):
The channel that should be used when printing
focus_level (int) default: 0:
The focus level that should be used
Returns:
image: The image of the specified focus level and channel
'''
img = Image.fromarray(self.get_data()[0,0,:,:])
img.show()
return img
def get_channel_peaks(self, channel, focus_level=0, min_sigma=0.75, max_sigma=3, threshold=0.1):
'''
Returns the coordinates of peaks (local maxima) in the specified channel and focus_level.
This method uses skimage blob_dog, therefore using difference of gaussian.
Parameters:
channel (int):
The channel that should be used when printing
focus_level (int) default: 0:
The focus level that should be used
min_sigma (float) default: 0.75:
The minimum standard deviation for Gaussian kernel. Keep this low to detect smaller blobs. The standard deviations of the Gaussian filter are given for each axis as a sequence, or as a single number, in which case it is equal for all axes.
max_sigma (float) default: 3:
The maximum standard deviation for Gaussian kernel. Keep this high to detect larger blobs. The standard deviations of the Gaussian filter are given for each axis as a sequence, or as a single number, in which case it is equal for all axes.
threshold (float) default: 0.1:
The absolute lower bound for scale space maxima. Local maxima smaller than thresh are ignored. Reduce this to detect blobs with less intensities.
Returns:
np.array: The peaks found by this method as np.array of shape (n, 2)
'''
img = img_as_float(self.get_data()[channel, focus_level, :, :])
peaks = blob_dog(img, min_sigma=min_sigma, max_sigma=max_sigma, threshold=threshold)
return peaks[:, 0:2]
def show_channel_peaks(self, channel, focus_level=0, min_sigma=0.75, max_sigma=3, threshold=0.1):
'''
Returns and shows the found. Uses get_channel_peaks internally.
Parameters:
channel (int):
The channel that should be used when printing
focus_level (int) default: 0:
The focus level that should be used
min_sigma (float) default: 0.75:
The minimum standard deviation for Gaussian kernel. Keep this low to detect smaller blobs. The standard deviations of the Gaussian filter are given for each axis as a sequence, or as a single number, in which case it is equal for all axes.
max_sigma (float) default: 3:
The maximum standard deviation for Gaussian kernel. Keep this high to detect larger blobs. The standard deviations of the Gaussian filter are given for each axis as a sequence, or as a single number, in which case it is equal for all axes.
threshold (float) default: 0.1:
The absolute lower bound for scale space maxima. Local maxima smaller than thresh are ignored. Reduce this to detect blobs with less intensities.
Returns:
image: The image of the specified focus level and channel with encircled peaks.
'''
peaks = self.get_channel_peaks(channel, focus_level, min_sigma, max_sigma, threshold)
img = Image.fromarray(self.get_data()[channel, focus_level, :, :])
draw = ImageDraw.Draw(img)
for x, y in zip(peaks[:,0], peaks[:,1]):
draw.ellipse((x - 5, y - 5, x + 5, y + 5), outline ='white', width = 3)
img.show()
return img

10
situr/image/tile.py Normal file
View File

@@ -0,0 +1,10 @@
class Tile:
'''
* Rounds 5
* Channels 4+1 - spot colours + nuclei
* Z 1 to 30 - focus level
* Y 2048
* X 2048
'''
def __init__(self):
pass

View File

@@ -0,0 +1,15 @@
class TileCollection:
'''
The idea here is about a class that knows where to find all the images and then being able to load the tile that is wanted on demand.
Therefore it also needs to know about the metadata.
We need to keep track off transformatons for each channel and tile.
* Tiles ~100
* Rounds 5
* Channels 4+1 - spot colours + nuclei
* Z 1 to 30 - focus level
* Y 2048
* X 2048
'''
def __init__(self):
pass

View File

@@ -0,0 +1,23 @@
class ChannelRegistration:
__metaclass__ = abc.ABCMeta
def do_registration(self, situ_img , reference_channel=0):
# For each channel (except nucleus) compute transform compared to reference_channel
# Add Channel transformation to Channel
pass
@abc.abstractmethod
def register_single_channel(self, peaks_data, reference_peaks):
"""Performs the channel registration on an image. Expects the peaks in each image as input."""
raise NotImplementedError(self.__class__.__name__ + '.register_single_channel')
class FilterregChannelRegistration(ChannelRegistration):
def register_single_channel(self, data_peaks, reference_peaks):
source = o3.geometry.PointCloud()
source.points = o3.utility.Vector3dVector(extend_dim(data_peaks))
target = o3.geometry.PointCloud()
target.points = o3.utility.Vector3dVector(extend_dim(reference_peaks))
registration_method=filterreg.registration_filterreg
tf_param, _, _ = filterreg.registration_filterreg(source, target)
return ScaleRotateTranslateChannelTransform(transform_matrix=tf_param.rot[0:2, 0:2], scale=tf_param.scale, offset=tf_param.t[0:2])

View File

@@ -0,0 +1,28 @@
class ChannelTransform:
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def apply_transformation(self, situ_img , channel):
"""Performs a transformation on one channel, all focus_levels are transformed the same way"""
raise NotImplementedError(self.__class__.__name__ + '.apply_transformation')
class ScaleRotateTranslateChannelTransform(ChannelTransform):
def __init__(self, transform_matrix, scale=1, offset=np.array([0, 0])):
# TODO: check
# * transform matrix is 2x2
# * offset is array (2,)
self.transform_matrix = transform_matrix
self.offset = offset
self.scale = scale
def apply_tranformation(self, situ_img , channel):
channel_img = situ_img.get_channel(channel)
focus_levels = channel_img.shape[0]
for focus_level in range(focus_levels):
img = channel_img [focus_level, :, :]
img [:, :] = scipy.ndimage.affine_transform(img, self.transform_matrix)
img [:, :] = scipy.ndimage.zoom(img, self.scale)
img [:, :] = scipy.ndimage.shift(img, self.offset)