Skip to content

Commit

Permalink
Merge pull request #245 from NIEHS/stable
Browse files Browse the repository at this point in the history
merge stable into latest
  • Loading branch information
JoQCcoz authored Feb 2, 2024
2 parents d36ac2e + f37b81c commit 45f6fd2
Show file tree
Hide file tree
Showing 32 changed files with 2,104 additions and 827 deletions.
5 changes: 3 additions & 2 deletions Smartscope/core/autoscreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ def autoscreen(session_id:str):
logger.info(f'Process: {process}')
logger.info(f'Session: {session}')
logger.info(f"Grids: {', '.join([grid.__str__() for grid in grids])}")
scopeInterface = select_microscope_interface(microscope)
scopeInterface, additional_settings = select_microscope_interface(microscope)

with scopeInterface(
microscope = Microscope.model_validate(session.microscope_id),
detector= Detector.model_validate(session.detector_id) ,
atlas_settings= AtlasSettings.model_validate(session.detector_id)
atlas_settings= AtlasSettings.model_validate(session.detector_id),
additional_settings=additional_settings
) as scope:

logger.debug(f'Main Log handlers:{logger.handlers}')
Expand Down
2 changes: 1 addition & 1 deletion Smartscope/core/db_manipulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def viewer_only(user):

def group_holes_for_BIS(hole_models, max_radius=4, min_group_size=1, queue_all=False, iterations=500, score_weight=2):
if len(hole_models) == 0:
return
return hole_models
logger.debug(
f'grouping params, max radius = {max_radius}, min group size = {min_group_size}, queue all = {queue_all}, max iterations = {iterations}, score_weight = {score_weight}')
# Extract coordinated for the holes
Expand Down
3 changes: 2 additions & 1 deletion Smartscope/core/grid/finders.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def find_targets(montage: Montage, methods: list):
targets, success, additional_outputs = method.run(montage=montage)
except Exception as err:
logger.exception(err)
success = False
continue
if success:
logger.debug(f"{method} was successful: {success}, '+ \
Expand All @@ -22,4 +23,4 @@ def find_targets(montage: Montage, methods: list):
return targets, method.name, method.name, additional_outputs
else:
return targets, method.name, None, additional_outputs
return [], '', None, dict()
return [], '', None, dict()
12 changes: 8 additions & 4 deletions Smartscope/core/interfaces/jeolserialem_interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import serialem as sem
from typing import Callable
from pydantic import BaseModel
import time
import logging
from .serialem_interface import SerialemInterface
Expand All @@ -14,9 +15,12 @@ def wrapper(*args, **kwargs):
sem.ReInsertAperture(0)
return wrapper

class JEOLadditionalSettings(BaseModel):
transfer_cartridge_path: str = 'C:\Program Data\SerialEM\PyTool\Transfer_Cartridge.bat'

class JEOLSerialemInterface(SerialemInterface):

additional_settings: JEOLadditionalSettings = JEOLadditionalSettings()

def checkPump(self, wait=30):
pass

Expand All @@ -31,16 +35,16 @@ def atlas(self, *args, **kwargs):
if not self.microscope.apertureControl:
return super().atlas(*args,**kwargs)
remove_condenser_aperture(
super().atlas(*args,**kwargs)
)
super().atlas)(*args,**kwargs)


def loadGrid(self, position):
if self.microscope.loaderSize > 1:
sem.Delay(5)
sem.SetColumnOrGunValve(0)
sem.Delay(5)
## HARCODED FOR NOW SINCE THE EXECUTABLE SHOULD BE THERE IN ALL SCOPES
sem.RunInShell(f'C:\Program Data\SerialEM\PyTool\Transfer_Cartridge.bat {position}')
sem.RunInShell(f"""{self.additional_settings.transfer_cartridge_path} "{position} 3 0" """)
sem.Delay(5)
sem.SetColumnOrGunValve(1)

Expand Down
2 changes: 2 additions & 0 deletions Smartscope/core/interfaces/microscope_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ class MicroscopeInterface(ABC):
detector: Detector
atlas_settings:AtlasSettings
state: MicroscopeState = MicroscopeState()
additional_settings: dict = None
has_hole_ref: bool = False
hole_crop_size: int = 0
focus_position_set: bool = False

def __enter__(self):
logger.debug(f'Additional settings set: {self.additional_settings}')
self.connect()
return self

Expand Down
28 changes: 22 additions & 6 deletions Smartscope/core/interfaces/microscope_methods.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import yaml
import logging
from Smartscope.core.settings.worker import SMARTSCOPE_CUSTOM_CONFIG
from .fakescope_interface import FakeScopeInterface
from .tfsserialem_interface import TFSSerialemInterface
from .jeolserialem_interface import JEOLSerialemInterface
from .jeolserialem_interface import JEOLSerialemInterface, JEOLadditionalSettings

logger = logging.getLogger(__name__)

def select_microscope_interface(microscope):
additional_settings_file = SMARTSCOPE_CUSTOM_CONFIG / f'{microscope.microscope_id}.yaml'
additional_settings = dict()
logger.debug(f'Additional settings file: {additional_settings_file}')
if additional_settings_file.exists():
with open(additional_settings_file, 'r') as yaml_file:
logger.info(f'Loading additional settings from {additional_settings_file}')
additional_settings = yaml.safe_load(yaml_file)

if microscope.serialem_IP == 'xxx.xxx.xxx.xxx':
# logger.info('Setting scope into test mode')
return FakeScopeInterface
logger.info('Setting scope into test mode')
return FakeScopeInterface, additional_settings

if microscope.vendor == 'JEOL':
# logger.info('Using the JEOL interface')
return JEOLSerialemInterface
logger.info('Using the JEOL interface')

return JEOLSerialemInterface, JEOLadditionalSettings.model_validate(additional_settings)

logger.info('Using the TFS interface')

return TFSSerialemInterface
return TFSSerialemInterface, additional_settings
9 changes: 9 additions & 0 deletions Smartscope/core/interfaces/tfsserialem_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ def wrapper(*args, **kwargs):
logger.info(msg)
sem.ReInsertAperture(Aperture.OBJECTIVE)
time.sleep(wait)

def no_wrap(*args,**kwargs):
msg = 'Objective aperture already out. Skipping removal and reinstertion.'
sem.Echo(msg)
logger.info(msg)
function(*args, **kwargs)

if sem.ReportApertureSize(Aperture.OBJECTIVE) == 0:
return no_wrap
return wrapper

class TFSSerialemInterface(SerialemInterface):
Expand Down
34 changes: 29 additions & 5 deletions Smartscope/core/main_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import json
from django.db import transaction
from django.conf import settings
from django.core.cache import cache

from Smartscope.core.models import *
from Smartscope.core.test_commands import *
Expand Down Expand Up @@ -52,7 +53,7 @@ def add_holes(id, targets):
instance = SquareModel.objects.get(pk=id)
montage = Montage(name=instance.name, working_dir=instance.grid_id.directory)
montage.load_or_process()
targets = np.array(targets) + np.array([0, instance.shape_x])
targets = np.array(targets)
hole_size = holeSize if (holeSize := instance.grid_id.holeType.hole_size) is not None else 1.2
targets = list(map(lambda t: convert_centers_to_boxes(
t, montage.pixel_size, montage.shape_x, montage.shape_y, hole_size), targets))
Expand All @@ -76,18 +77,21 @@ def add_holes(id, targets):
return holes


def add_single_targets(id, *args):
logger.info(f'Adding {len(args)} targets to square {id}')
def add_single_targets(id_, *args):
logger.info(f'Adding {len(args)} targets to square {id_}')
targets = []
for i in args:
j = i.split(',')
j = [float(k) for k in j]

targets.append(j)
logger.debug(targets)
holes = add_holes(id=id, targets=targets)
holes = add_holes(id=id_, targets=targets)

logger.debug(f'Successfully added {len(holes)} targets.')
cache_key = f'{id_}_targets_methods'
cache.delete(cache_key)

logger.debug(holes)


def check_pause(microscope_id: str, session_id: str):
Expand Down Expand Up @@ -241,4 +245,24 @@ def get_atlas_to_search_offset(detector_name,maximum=0):
return
print('Skip setting values')


def export_grid(grid_id, export_to=''):
from Smartscope.core.utils.export_import import export_grid
if export_to == '':
export_to = os.path.join(grid.directory, 'export.yaml')
print(f'Export path not specified. Exporting to default loacation: {export_to}')
grid = AutoloaderGrid.objects.get(grid_id=grid_id)
export_grid(grid, export_to=export_to)
print('Done.')

def import_grid(file:str):
from Smartscope.core.utils.export_import import import_grid
if not Path(file).exists():
print(f'File {file} does not exist.')
return
print(f'Importing {file} into the smartscope database.')
import_grid(file)
print('Done.')



2 changes: 1 addition & 1 deletion Smartscope/core/models/screening_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def root_directories(session):
root_directories.append(custom_group_path.path)

if settings.USE_STORAGE:
root_directories.append(settings.AUTOSCREENDIR)
# root_directories.append(settings.AUTOSCREENDIR)
if (groupname:=session.group.name) is not None:
root_directories.append(os.path.join(settings.AUTOSCREENDIR,groupname))
else:
Expand Down
2 changes: 2 additions & 0 deletions Smartscope/core/svg_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def drawAtlas(atlas, targets, display_type, method) -> draw.Drawing:
labels_list = []
for i in targets:
color, label, prefix = css_color(i, display_type, method)

if color is not None:
sz = floor(sqrt(i.area))
finder = list(i.finders.all())[0]
Expand Down Expand Up @@ -132,6 +133,7 @@ def drawSquare(square, targets, display_type, method) -> draw.Drawing:
for i in targets:

color, label, prefix = css_color(i, display_type, method)
# logger.debug(f'{i.number} -> {color}')
if color is not None:
finder = list(i.finders.all())[0]
if not finder.is_position_within_stage_limits():
Expand Down
2 changes: 1 addition & 1 deletion Smartscope/core/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def refine_pixel_size_from_targets(instances, spacings) -> Tuple[float, float]:


def test_finder(plugin_name: str, raw_image_path: str, output_dir: str, repeats=1): # output_dir='/mnt/data/testing/'
from build_smartscope.SmartScope.Smartscope.lib.image.montage import Montage
from Smartscope.lib.image.montage import Montage
from Smartscope.lib.image_manipulations import auto_contrast, save_image
import cv2
import math
Expand Down
29 changes: 29 additions & 0 deletions Smartscope/lib/Finders/AIFinder/detectors/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2022, Bartesaghi Lab (Duke University)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Loading

0 comments on commit 45f6fd2

Please sign in to comment.