Skip to content

Commit cbc835d

Browse files
author
David Erb
committed
adds well centroid algorithm selection
1 parent 1126ebf commit cbc835d

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed

src/chimpflow_api/constants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class WELL_CENTROID_ALGORITHMS:
2+
MIDDLE_PIXEL = "middle_pixel"
3+
# Adapted from matlab code used by Texrank?
4+
# Requires prepared background images on plate type.
5+
# Doesn't work very well, sensitive to crystal density.
6+
TEXRANK_LIKE = "textrank_like"

src/chimpflow_lib/chimp_adapter.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
)
1212
from xchembku_api.models.crystal_well_model import CrystalWellModel
1313

14+
from chimpflow_api.constants import WELL_CENTROID_ALGORITHMS
15+
1416
with warnings.catch_warnings():
1517
warnings.filterwarnings("ignore", category=DeprecationWarning)
1618
from xchem_chimp.detector.chimp_detector import ChimpDetector
@@ -47,6 +49,21 @@ def __init__(self, specification: Dict):
4749
"num_classes",
4850
)
4951

52+
# Caller specifies the well centroid algorithm they want to use.
53+
# None means don't calculate well centroid.
54+
self.__well_centroid_algorithm = specification.get("well_centroid_algorithm")
55+
56+
if self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE:
57+
pass
58+
elif self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.MIDDLE_PIXEL:
59+
pass
60+
elif self.__well_centroid_algorithm is None:
61+
pass
62+
else:
63+
raise RuntimeError(
64+
"configuration error: invalid well_centroid_algorithm '{self.__well_centroid_algorithm}'"
65+
)
66+
5067
self.__is_activated = False
5168
self.__detector: Optional[ChimpDetector] = None
5269

@@ -110,9 +127,10 @@ def detect(
110127
with self.__profiler.context("coord_generator.extract_coordinates()"):
111128
coord_generator.extract_coordinates()
112129

113-
# Calculate well centers.
114-
with self.__profiler.context("coord_generator.calculate_well_centres()"):
115-
coord_generator.calculate_well_centres()
130+
if self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE:
131+
# Calculate well centers.
132+
with self.__profiler.context("coord_generator.calculate_well_centres()"):
133+
coord_generator.calculate_well_centres()
116134

117135
# Get the output stucture for the first (only) image.
118136
# TODO: Store the chimp detector output structure as json in the database.
@@ -123,17 +141,36 @@ def detect(
123141
crystal_well_uuid=crystal_well_model.uuid,
124142
)
125143
model.drop_detected = output_dict["drop_detected"]
126-
target_position = output_dict["echo_coordinate"]
144+
target_position = require(
145+
"coord_generator output_dict",
146+
output_dict,
147+
"echo_coordinate",
148+
)
127149
if len(target_position) > 0:
128150
# The target position is a list of (np.int64, np.int64), so have to convert to int.
129151
# Coordinate pairs are vertical-first.
130152
# TODO: Change the CrystalWellAutolocationModel to do type checking on field assignment.
131153
model.auto_target_x = int(target_position[0][1])
132154
model.auto_target_y = int(target_position[0][0])
133-
well_centroid = output_dict["well_centroid"]
134-
if well_centroid is not None:
155+
156+
if self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE:
157+
well_centroid = require(
158+
"coord_generator output_dict",
159+
output_dict,
160+
"well_centroid",
161+
)
135162
model.well_centroid_x = int(well_centroid[1])
136163
model.well_centroid_y = int(well_centroid[0])
164+
# Anything else is assumed MIDDLE_PIXEL.
165+
else:
166+
original_image_shape = require(
167+
"coord_generator output_dict",
168+
output_dict,
169+
"original_image_shape",
170+
)
171+
model.well_centroid_x = int(original_image_shape[1] / 2.0)
172+
model.well_centroid_y = int(original_image_shape[0] / 2.0)
173+
137174
model.number_of_crystals = len(output_dict["xtal_coordinates"])
138175

139176
# TODO: Store the chimp detected crystal coordinates in the model too.

tests/test_chimp_adapter.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
)
1111
from xchembku_api.models.crystal_well_model import CrystalWellModel
1212

13+
from chimpflow_api.constants import WELL_CENTROID_ALGORITHMS
14+
1315
# Base class for the tester.
1416
from tests.base import Base
1517

@@ -45,22 +47,49 @@ async def _main_coroutine(self, constants, output_directory):
4547
self.__specification = {
4648
"model_path": constants["model_path"],
4749
"num_classes": 3,
50+
"well_centroid_algorithm": WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE,
4851
}
4952

5053
# Do the work in a separate process since the torchvision won't release unless its process quits.
5154
# If it doesn't release, then subsequent pytest cases wait forever.
5255
# TODO: Figure out how to release resources from torchvision within a process.
53-
p = multiprocessing.Process(target=self.__process, args=[self.__run_97wo_01A_1])
56+
p = multiprocessing.Process(
57+
target=self.__process1,
58+
)
59+
p.start()
60+
p.join()
61+
assert p.exitcode == 0
62+
63+
# ------------------------------------------------------------------
64+
# Make a specification for the chimp adapter, this time with no centroid algorithm.
65+
self.__specification = {
66+
"model_path": constants["model_path"],
67+
"num_classes": 3,
68+
# No specified algorithm.
69+
# "well_centroid_algorithm": WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE,
70+
}
71+
72+
p = multiprocessing.Process(
73+
target=self.__process2,
74+
)
5475
p.start()
5576
p.join()
5677
assert p.exitcode == 0
5778

5879
# ----------------------------------------------------------------------------------------
59-
def __process(self, run):
80+
def __process1(self):
6081
chimp_adapter = ChimpAdapter(self.__specification)
6182

6283
self.__run_97wo_01A_1(chimp_adapter)
6384
self.__run_97wo_01A_2(chimp_adapter)
85+
86+
# Display the profiler's end results.
87+
logger.debug(f"profile\n{dls_utilpack_global_profiler()}")
88+
89+
# ----------------------------------------------------------------------------------------
90+
def __process2(self):
91+
chimp_adapter = ChimpAdapter(self.__specification)
92+
6493
self.__run_97wo_01A_3(chimp_adapter)
6594

6695
# Display the profiler's end results.
@@ -138,5 +167,6 @@ def __run_97wo_01A_3(self, chimp_adapter: ChimpAdapter) -> None:
138167
assert well_model_autolocation.auto_target_x == pytest.approx(417, 3)
139168
assert well_model_autolocation.auto_target_y == pytest.approx(672, 3)
140169

141-
assert well_model_autolocation.well_centroid_x == 638
142-
assert well_model_autolocation.well_centroid_y == 494
170+
# Centroid in this test comes from image central pixel.
171+
assert well_model_autolocation.well_centroid_x == 612
172+
assert well_model_autolocation.well_centroid_y == 512

0 commit comments

Comments
 (0)