Skip to content

Commit

Permalink
adds well centroid algorithm selection
Browse files Browse the repository at this point in the history
  • Loading branch information
David Erb committed May 22, 2023
1 parent 1126ebf commit cbc835d
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 10 deletions.
6 changes: 6 additions & 0 deletions src/chimpflow_api/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class WELL_CENTROID_ALGORITHMS:
MIDDLE_PIXEL = "middle_pixel"
# Adapted from matlab code used by Texrank?
# Requires prepared background images on plate type.
# Doesn't work very well, sensitive to crystal density.
TEXRANK_LIKE = "textrank_like"
49 changes: 43 additions & 6 deletions src/chimpflow_lib/chimp_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
)
from xchembku_api.models.crystal_well_model import CrystalWellModel

from chimpflow_api.constants import WELL_CENTROID_ALGORITHMS

with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
from xchem_chimp.detector.chimp_detector import ChimpDetector
Expand Down Expand Up @@ -47,6 +49,21 @@ def __init__(self, specification: Dict):
"num_classes",
)

# Caller specifies the well centroid algorithm they want to use.
# None means don't calculate well centroid.
self.__well_centroid_algorithm = specification.get("well_centroid_algorithm")

if self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE:
pass
elif self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.MIDDLE_PIXEL:
pass
elif self.__well_centroid_algorithm is None:
pass
else:
raise RuntimeError(
"configuration error: invalid well_centroid_algorithm '{self.__well_centroid_algorithm}'"
)

self.__is_activated = False
self.__detector: Optional[ChimpDetector] = None

Expand Down Expand Up @@ -110,9 +127,10 @@ def detect(
with self.__profiler.context("coord_generator.extract_coordinates()"):
coord_generator.extract_coordinates()

# Calculate well centers.
with self.__profiler.context("coord_generator.calculate_well_centres()"):
coord_generator.calculate_well_centres()
if self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE:
# Calculate well centers.
with self.__profiler.context("coord_generator.calculate_well_centres()"):
coord_generator.calculate_well_centres()

# Get the output stucture for the first (only) image.
# TODO: Store the chimp detector output structure as json in the database.
Expand All @@ -123,17 +141,36 @@ def detect(
crystal_well_uuid=crystal_well_model.uuid,
)
model.drop_detected = output_dict["drop_detected"]
target_position = output_dict["echo_coordinate"]
target_position = require(
"coord_generator output_dict",
output_dict,
"echo_coordinate",
)
if len(target_position) > 0:
# The target position is a list of (np.int64, np.int64), so have to convert to int.
# Coordinate pairs are vertical-first.
# TODO: Change the CrystalWellAutolocationModel to do type checking on field assignment.
model.auto_target_x = int(target_position[0][1])
model.auto_target_y = int(target_position[0][0])
well_centroid = output_dict["well_centroid"]
if well_centroid is not None:

if self.__well_centroid_algorithm == WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE:
well_centroid = require(
"coord_generator output_dict",
output_dict,
"well_centroid",
)
model.well_centroid_x = int(well_centroid[1])
model.well_centroid_y = int(well_centroid[0])
# Anything else is assumed MIDDLE_PIXEL.
else:
original_image_shape = require(
"coord_generator output_dict",
output_dict,
"original_image_shape",
)
model.well_centroid_x = int(original_image_shape[1] / 2.0)
model.well_centroid_y = int(original_image_shape[0] / 2.0)

model.number_of_crystals = len(output_dict["xtal_coordinates"])

# TODO: Store the chimp detected crystal coordinates in the model too.
Expand Down
38 changes: 34 additions & 4 deletions tests/test_chimp_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
)
from xchembku_api.models.crystal_well_model import CrystalWellModel

from chimpflow_api.constants import WELL_CENTROID_ALGORITHMS

# Base class for the tester.
from tests.base import Base

Expand Down Expand Up @@ -45,22 +47,49 @@ async def _main_coroutine(self, constants, output_directory):
self.__specification = {
"model_path": constants["model_path"],
"num_classes": 3,
"well_centroid_algorithm": WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE,
}

# Do the work in a separate process since the torchvision won't release unless its process quits.
# If it doesn't release, then subsequent pytest cases wait forever.
# TODO: Figure out how to release resources from torchvision within a process.
p = multiprocessing.Process(target=self.__process, args=[self.__run_97wo_01A_1])
p = multiprocessing.Process(
target=self.__process1,
)
p.start()
p.join()
assert p.exitcode == 0

# ------------------------------------------------------------------
# Make a specification for the chimp adapter, this time with no centroid algorithm.
self.__specification = {
"model_path": constants["model_path"],
"num_classes": 3,
# No specified algorithm.
# "well_centroid_algorithm": WELL_CENTROID_ALGORITHMS.TEXRANK_LIKE,
}

p = multiprocessing.Process(
target=self.__process2,
)
p.start()
p.join()
assert p.exitcode == 0

# ----------------------------------------------------------------------------------------
def __process(self, run):
def __process1(self):
chimp_adapter = ChimpAdapter(self.__specification)

self.__run_97wo_01A_1(chimp_adapter)
self.__run_97wo_01A_2(chimp_adapter)

# Display the profiler's end results.
logger.debug(f"profile\n{dls_utilpack_global_profiler()}")

# ----------------------------------------------------------------------------------------
def __process2(self):
chimp_adapter = ChimpAdapter(self.__specification)

self.__run_97wo_01A_3(chimp_adapter)

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

assert well_model_autolocation.well_centroid_x == 638
assert well_model_autolocation.well_centroid_y == 494
# Centroid in this test comes from image central pixel.
assert well_model_autolocation.well_centroid_x == 612
assert well_model_autolocation.well_centroid_y == 512

0 comments on commit cbc835d

Please sign in to comment.