Skip to content

Commit

Permalink
Use multiple gradients on a single ingredient (#270)
Browse files Browse the repository at this point in the history
* Add methods and recipe to use multiple gradients on a single ingredient

* Add check for list of gradients while creating plots

* Move point picking logic to Gradient.py

* Update colormap for displaying grid data

* Increase gradient strength in example recipe

* formatting

* Add check for gradient list with single element
  • Loading branch information
mogres authored Jul 8, 2024
1 parent d2bcfbc commit 12276f5
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 34 deletions.
14 changes: 11 additions & 3 deletions cellpack/autopack/Analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,9 +828,17 @@ def update_distance_distribution_dictionaries(

get_angles = False
if ingr.packing_mode == "gradient" and self.env.use_gradient:
self.center = center = self.env.gradients[ingr.gradient].mode_settings.get(
"center", center
)
if isinstance(ingr.gradient, list):
if len(ingr.gradient) > 1 or len(ingr.gradient) == 0:
self.center = center
else:
self.center = center = self.env.gradients[
ingr.gradient[0]
].mode_settings.get("center", center)
else:
self.center = center = self.env.gradients[
ingr.gradient
].mode_settings.get("center", center)
get_angles = True

# get angles wrt gradient
Expand Down
4 changes: 3 additions & 1 deletion cellpack/autopack/Environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -1700,7 +1700,9 @@ def getPointToDrop(
# get the most probable point using the gradient
# use the gradient weighted map and get mot probabl point
self.log.info("pick point from gradients %d", (len(allIngrPts)))
ptInd = self.gradients[ingr.gradient].pickPoint(allIngrPts)
ptInd = Gradient.pick_point_for_ingredient(
ingr, allIngrPts, self.gradients
)
else:
# pick a point randomly among free points
# random or uniform?
Expand Down
119 changes: 108 additions & 11 deletions cellpack/autopack/Gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,111 @@ def __init__(self, gradient_data):

self.function = self.defaultFunction # lambda ?

@staticmethod
def scale_between_0_and_1(values):
"""
Scale values between 0 and 1
"""
max_value = numpy.nanmax(values)
min_value = numpy.nanmin(values)
return (values - min_value) / (max_value - min_value)

@staticmethod
def get_combined_gradient_weight(gradient_list):
"""
Combine the gradient weights
Parameters
----------
gradient_list: list
list of gradient objects
Returns
----------
numpy.ndarray
the combined gradient weight
"""
weight_list = numpy.zeros((len(gradient_list), len(gradient_list[0].weight)))
for i in range(len(gradient_list)):
weight_list[i] = Gradient.scale_between_0_and_1(gradient_list[i].weight)

combined_weight = numpy.mean(weight_list, axis=0)
combined_weight = Gradient.scale_between_0_and_1(combined_weight)

return combined_weight

@staticmethod
def pick_point_from_weight(weight, points):
"""
Picks a point from a list of points according to the given weight
Parameters
----------
weight: numpy.ndarray
the weight of each point
points: numpy.ndarray
list of grid point indices
Returns
----------
int
the index of the picked point
"""
weights_to_use = numpy.take(weight, points)
weights_to_use = Gradient.scale_between_0_and_1(weights_to_use)
weights_to_use[numpy.isnan(weights_to_use)] = 0

point_probabilities = weights_to_use / numpy.sum(weights_to_use)

point = numpy.random.choice(points, p=point_probabilities)

return point

@staticmethod
def pick_point_for_ingredient(ingr, allIngrPts, all_gradients):
"""
Picks a point for an ingredient according to the gradient
Parameters
----------
ingr: Ingredient
the ingredient object
allIngrPts: numpy.ndarray
list of grid point indices
all_gradients: dict
dictionary of all gradient objects
Returns
----------
int
the index of the picked point
"""
if isinstance(ingr.gradient, list):
if len(ingr.gradient) > 1:
if not hasattr(ingr, "combined_weight"):
gradient_list = [
gradient
for gradient_name, gradient in all_gradients.items()
if gradient_name in ingr.gradient
]
combined_weight = Gradient.get_combined_gradient_weight(
gradient_list
)
ingr.combined_weight = combined_weight

ptInd = Gradient.pick_point_from_weight(
ingr.combined_weight, allIngrPts
)
else:
ptInd = all_gradients[ingr.gradient[0]].pickPoint(allIngrPts)
else:
ptInd = all_gradients[ingr.gradient].pickPoint(allIngrPts)

return ptInd

def get_center(self):
"""get the center of the gradient grid"""
center = [0.0, 0.0, 0.0]
Expand All @@ -113,14 +218,6 @@ def normalize_vector(self, vector):
"""
return vector / numpy.linalg.norm(vector)

def get_normalized_values(self, values):
"""
Scale values between 0 and 1
"""
max_value = numpy.nanmax(values)
min_value = numpy.nanmin(values)
return (values - min_value) / (max_value - min_value)

def pickPoint(self, listPts):
"""
pick next random point according to the chosen function
Expand Down Expand Up @@ -205,7 +302,7 @@ def build_axis_weight_map(self, bb, master_grid_positions):

def set_weights_by_mode(self):

self.scaled_distances = self.get_normalized_values(self.distances)
self.scaled_distances = Gradient.scale_between_0_and_1(self.distances)

if (numpy.nanmax(self.scaled_distances) > 1.0) or (
numpy.nanmin(self.scaled_distances) < 0.0
Expand All @@ -232,7 +329,7 @@ def set_weights_by_mode(self):
-self.scaled_distances / self.weight_mode_settings["decay_length"]
)
# normalize the weight
self.weight = self.get_normalized_values(self.weight)
self.weight = Gradient.scale_between_0_and_1(self.weight)

if (numpy.nanmax(self.weight) > 1.0) or (numpy.nanmin(self.weight) < 0.0):
raise ValueError(
Expand Down Expand Up @@ -377,7 +474,7 @@ def create_voxelization(self, image_writer):
)
if channel_values is None:
continue
normalized_values = self.get_normalized_values(channel_values)
normalized_values = Gradient.scale_between_0_and_1(channel_values)
reshaped_values = numpy.reshape(
normalized_values, image_writer.image_size, order="F"
)
Expand Down
5 changes: 4 additions & 1 deletion cellpack/autopack/upy/simularium/simularium_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,10 @@ def add_grid_data_to_scene(self, incoming_name, positions, values, radius=0.5):

positions, values = self.sort_values(positions, values)

colormap = matplotlib.cm.Reds(values)
normalized_values = (values - np.min(values)) / (
np.max(values) - np.min(values)
)
colormap = matplotlib.cm.Reds(normalized_values)

for index, value in enumerate(values):
name = f"{incoming_name}#{value:.3f}"
Expand Down
9 changes: 9 additions & 0 deletions cellpack/tests/packing-configs/test_grid_plot_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "show_grid_plot",
"out": "cellpack/tests/outputs/",
"load_from_grid_file": false,
"overwrite_place_method": true,
"place_method": "spheresSST",
"save_analyze_result": true,
"show_grid_plot": true
}
18 changes: 0 additions & 18 deletions cellpack/tests/packing-configs/test_mesh_config.json

This file was deleted.

107 changes: 107 additions & 0 deletions cellpack/tests/recipes/v2/test_combined_gradient.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"version": "1.0.0",
"format_version": "2.0",
"name": "test_combined_gradient",
"bounding_box": [
[
0,
0,
0
],
[
1000,
1000,
1
]
],
"gradients": {
"X_gradient": {
"description": "X gradient",
"mode": "X",
"pick_mode": "rnd",
"weight_mode": "exponential",
"weight_mode_settings": {
"decay_length": 0.1
}
},
"Y_gradient": {
"description": "Y gradient",
"mode": "Y",
"pick_mode": "rnd",
"weight_mode": "exponential",
"weight_mode_settings": {
"decay_length": 0.1
}
}
},
"objects": {
"base": {
"jitter_attempts": 10,
"orient_bias_range": [
-3.1415927,
3.1415927
],
"rotation_range": 6.2831,
"cutoff_boundary": 0,
"max_jitter": [
1,
1,
0
],
"perturb_axis_amplitude": 0.1,
"packing_mode": "random",
"principal_vector": [
0,
0,
1
],
"rejection_threshold": 50,
"place_method": "spheresSST",
"cutoff_surface": 42,
"rotation_axis": [
0,
0,
1
],
"available_regions": {
"interior": {},
"surface": {},
"outer_leaflet": {},
"inner_leaflet": {}
}
},
"sphere_25": {
"type": "single_sphere",
"inherit": "base",
"color": [
0.5,
0.5,
0.5
],
"radius": 25,
"max_jitter": [
1,
1,
0
],
"packing_mode": "gradient",
"gradient": [
"X_gradient",
"Y_gradient"
]
}
},
"composition": {
"space": {
"regions": {
"interior": [
"A"
]
}
},
"A": {
"object": "sphere_25",
"count": 500
}
}
}

0 comments on commit 12276f5

Please sign in to comment.