Skip to content
167 changes: 163 additions & 4 deletions src/murfey/client/contexts/sxt.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,17 +157,176 @@ def post_transfer(
**kwargs,
)

data_suffixes = [".txrm"]
if transferred_file.suffix == ".xrm" and environment:
Comment thread
stephen-riggs marked this conversation as resolved.
# Make sure we have a dcg for this grid
dcg_tag = ensure_dcg_exists(
collection_type="sxt",
metadata_source=self._basepath,
environment=environment,
machine_config=self._machine_config,
token=self._token,
)

metadata: dict[str, Any] = {}
with OleFileIO(str(transferred_file)) as xrm_ole:
if xrm_ole.exists("ImageInfo/XPosition") and xrm_ole.exists(
"ImageInfo/YPosition"
):
x_tiles = np.frombuffer(
xrm_ole.openstream("ImageInfo/XPosition").getvalue(), np.float32
).tolist()
y_tiles = np.frombuffer(
xrm_ole.openstream("ImageInfo/YPosition").getvalue(), np.float32
).tolist()
metadata["x_position"] = x_tiles[int(len(x_tiles) / 2)]
metadata["y_position"] = y_tiles[int(len(y_tiles) / 2)]

if xrm_ole.exists("ImageInfo/PixelSize"):
metadata["pixel_size"] = np.frombuffer(
xrm_ole.openstream("ImageInfo/PixelSize").getvalue(), np.float32
).tolist()[0]
Comment thread
stephen-riggs marked this conversation as resolved.
Outdated

if xrm_ole.exists("ImageInfo/ImageHeight"):
metadata["height"] = np.frombuffer(
xrm_ole.openstream("ImageInfo/ImageHeight").getvalue(), np.int32
).tolist()[0]

if xrm_ole.exists("ImageInfo/ImageWidth"):
metadata["width"] = np.frombuffer(
xrm_ole.openstream("ImageInfo/ImageWidth").getvalue(), np.int32
).tolist()[0]

# Find images which are not mosaics (txrm spec typos this as mosiac)
if xrm_ole.exists("ImageInfo/MosiacRows") and xrm_ole.exists(
"ImageInfo/MosiacColumns"
):
metadata["mosaic_rows"] = np.frombuffer(
xrm_ole.openstream("ImageInfo/MosiacRows").getvalue(), np.int32
)[0]
metadata["mosaic_columns"] = np.frombuffer(
xrm_ole.openstream("ImageInfo/MosiacColumns").getvalue(),
np.int32,
)[0]
metadata["mosaic_size"] = int(
metadata["mosaic_rows"] * metadata["mosaic_columns"]
)

source = _get_source(transferred_file, environment=environment)
if source:
image_path = _file_transferred_to(
environment,
source,
transferred_file,
Path(self._machine_config.get("rsync_basepath", "")),
)
if (
environment.visit
in Path(environment.default_destinations[source]).parts
):
# Split either side of the raw directory
visit_idx = Path(
environment.default_destinations[source]
).parts.index(environment.visit)
Comment thread
stephen-riggs marked this conversation as resolved.
destination_base = "/".join(
Path(environment.default_destinations[source]).parts[
: visit_idx + 1
]
)
destination_extra = "/".join(
Path(environment.default_destinations[source]).parts[
visit_idx + 2 :
]
)
else:
destination_base = str(
Path(environment.default_destinations[source])
/ environment.visit
)
destination_extra = ""
converted_file_path = (
Path(self._machine_config.get("rsync_basepath", ""))
/ destination_base
/ self._machine_config.get("processed_directory_name", "")
/ self._machine_config.get("processed_extra_directory", "")
/ destination_extra
/ f"{transferred_file.relative_to(source).stem}_Annotated.tiff"
)
capture_post(
base_url=str(environment.url.geturl()),
router_name="workflow_sxt.router",
function_name="convert_xrm_to_tiff",
token=self._token,
instrument_name=environment.instrument_name,
data={
"xrm_path": str(image_path),
"tiff_path": str(converted_file_path),
},
)

if (
metadata.get("mosaic_size", 1) > 0
and metadata.get("pixel_size", 0) > 0.1
):
# Large pixel size, this is an atlas
dcg_data = {
"experiment_type_id": 44, # Atlas
Comment thread
stephen-riggs marked this conversation as resolved.
"tag": dcg_tag,
"atlas": str(converted_file_path),
"atlas_pixel_size": round(metadata.get("pixel_size", 0), 2),
"atlas_x_stage_position": metadata.get("x_position", None),
"atlas_y_stage_position": metadata.get("y_position", None),
"atlas_height": int(
metadata.get("height", 0) * metadata["mosaic_rows"]
),
"atlas_width": int(
metadata.get("width", 0) * metadata["mosaic_columns"]
),
}
capture_post(
base_url=str(environment.url.geturl()),
router_name="workflow.router",
function_name="register_dc_group",
token=self._token,
instrument_name=environment.instrument_name,
visit_name=environment.visit,
session_id=environment.murfey_session,
data=dcg_data,
)
elif metadata.get("mosaic_size", 1) > 0:
# Other mosaic images are of grid squares
capture_post(
base_url=str(environment.url.geturl()),
router_name="workflow_sxt.router",
function_name="register_sxt_roi",
token=self._token,
instrument_name=environment.instrument_name,
visit_name=environment.visit,
session_id=environment.murfey_session,
data={
"tag": dcg_tag,
"name": transferred_file.stem,
"x_stage_position": metadata.get("x_position", None),
"y_stage_position": metadata.get("y_position", None),
"pixel_size": round(metadata.get("pixel_size", 0), 2),
"height": int(
metadata.get("height", 0) * metadata["mosaic_rows"]
),
"width": int(
metadata.get("width", 0) * metadata["mosaic_columns"]
),
"image": str(converted_file_path),
},
)

if transferred_file.suffix in data_suffixes and environment:
elif transferred_file.suffix == ".txrm" and environment:
source = _get_source(transferred_file, environment)
if not source:
logger.warning(f"No source found for file {transferred_file}")
return False

# Read the tilt angles and pixel size from the txrm
angles: list = []
metadata: dict[str, Any] = {
metadata = {
"source": str(self._basepath),
"tilt_series_tag": transferred_file.stem,
}
Expand Down Expand Up @@ -302,7 +461,7 @@ def post_transfer(
reference_file_transferred_to = None
capture_post(
base_url=str(environment.url.geturl()),
router_name="workflow.sxt_router",
router_name="workflow_sxt.router",
function_name="process_sxt_tilt_series",
token=self._token,
instrument_name=environment.instrument_name,
Expand Down
33 changes: 4 additions & 29 deletions src/murfey/server/api/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@
motion_corrected_mrc,
)
from murfey.util.tomo import midpoint
from murfey.workflows.sxt.process_sxt_tilt_series import (
SXTTiltSeriesInfo,
)
from murfey.workflows.tomo.tomo_metadata import register_search_map_in_database

logger = getLogger("murfey.server.api.workflow")
Expand All @@ -100,6 +97,10 @@ class DCGroupParameters(BaseModel):
atlas: str = ""
sample: Optional[int] = None
atlas_pixel_size: float = 0
atlas_x_stage_position: float | None = None
atlas_y_stage_position: float | None = None
atlas_width: int | None = None
atlas_height: int | None = None
create_smartem_grid: bool = False
acquisition_uuid: Optional[str] = None

Expand Down Expand Up @@ -1071,32 +1072,6 @@ def _add_tilt():
_add_tilt()


sxt_router = APIRouter(
prefix="/workflow/sxt",
dependencies=[Depends(validate_instrument_token)],
tags=["Workflows: Soft x-ray tomography"],
)


@sxt_router.post("/visits/{visit_name}/sessions/{session_id}/sxt_tilt_series")
def process_sxt_tilt_series(
visit_name: str,
session_id: MurfeySessionID,
tilt_series_info: SXTTiltSeriesInfo,
db=murfey_db,
):
if _transport_object:
_transport_object.send(
_transport_object.feedback_queue,
{
"register": "sxt.process_tilt_series",
"session_id": session_id,
"visit_name": visit_name,
"tilt_series_info": tilt_series_info.model_dump(mode="json"),
},
)


correlative_router = APIRouter(
prefix="/workflow/correlative",
dependencies=[Depends(validate_instrument_token)],
Expand Down
84 changes: 84 additions & 0 deletions src/murfey/server/api/workflow_sxt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from logging import getLogger
from pathlib import Path

from fastapi import APIRouter, Depends
from pydantic import BaseModel

from murfey.server import _transport_object
from murfey.server.api.auth import (
MurfeySessionIDInstrument as MurfeySessionID,
validate_instrument_token,
)
from murfey.server.murfey_db import murfey_db
from murfey.workflows.sxt.process_sxt_tilt_series import SXTTiltSeriesInfo

logger = getLogger("murfey.server.api.workflow_sxt")


router = APIRouter(
prefix="/workflow/sxt",
dependencies=[Depends(validate_instrument_token)],
tags=["Workflows: Soft x-ray tomography"],
)


@router.post("/visits/{visit_name}/sessions/{session_id}/sxt_tilt_series")
def process_sxt_tilt_series(
visit_name: str,
session_id: MurfeySessionID,
tilt_series_info: SXTTiltSeriesInfo,
db=murfey_db,
):
if _transport_object:
_transport_object.send(
_transport_object.feedback_queue,
{
"register": "sxt.process_tilt_series",
"session_id": session_id,
"visit_name": visit_name,
"tilt_series_info": tilt_series_info.model_dump(mode="json"),
},
)


class XrmFile(BaseModel):
xrm_path: Path
tiff_path: Path


@router.post("/convert_xrm_to_tiff")
def convert_xrm_to_tiff(xrm_file: XrmFile, db=murfey_db):
if _transport_object:
logger.info("Sending xrm conversion to images service")
_transport_object.send(
"images",
{
"image_command": "xrm_to_jpeg",
"xrm_file": str(xrm_file.xrm_path),
"tiff_destination": str(xrm_file.tiff_path),
"annotate": True,
},
new_connection=True,
)


class SxtRoiInfo(BaseModel):
tag: str
name: str
x_stage_position: float
y_stage_position: float
pixel_size: float
height: int
width: int
image: Path


@router.post("/visits/{visit_name}/sessions/{session_id}/register_sxt_roi")
def register_sxt_roi(
visit_name: str,
session_id: MurfeySessionID,
tilt_series_info: SXTTiltSeriesInfo,
db=murfey_db,
):
# TODO
return
3 changes: 2 additions & 1 deletion src/murfey/server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import murfey.server.api.workflow
import murfey.server.api.workflow_clem
import murfey.server.api.workflow_fib
import murfey.server.api.workflow_sxt
from murfey.server import template_files
from murfey.util.config import get_security_config

Expand Down Expand Up @@ -96,10 +97,10 @@ class Settings(BaseSettings):
app.include_router(murfey.server.api.workflow.router)
app.include_router(murfey.server.api.workflow.correlative_router)
app.include_router(murfey.server.api.workflow.spa_router)
app.include_router(murfey.server.api.workflow.sxt_router)
app.include_router(murfey.server.api.workflow.tomo_router)
app.include_router(murfey.server.api.workflow_clem.router)
app.include_router(murfey.server.api.workflow_fib.router)
app.include_router(murfey.server.api.workflow_sxt.router)

app.include_router(murfey.server.api.prometheus.router)

Expand Down
34 changes: 24 additions & 10 deletions src/murfey/util/route_manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1336,16 +1336,6 @@ murfey.server.api.workflow.spa_router:
type: int
methods:
- POST
murfey.server.api.workflow.sxt_router:
- path: /workflow/sxt/visits/{visit_name}/sessions/{session_id}/sxt_tilt_series
function: process_sxt_tilt_series
path_params:
- name: visit_name
type: str
- name: session_id
type: int
methods:
- POST
murfey.server.api.workflow.tomo_router:
- path: /workflow/tomo/sessions/{session_id}/tomography_processing_parameters
function: register_tomo_proc_params
Expand Down Expand Up @@ -1455,3 +1445,27 @@ murfey.server.api.workflow_fib.router:
type: int
methods:
- POST
murfey.server.api.workflow_sxt.router:
- path: /workflow/sxt/visits/{visit_name}/sessions/{session_id}/sxt_tilt_series
function: process_sxt_tilt_series
path_params:
- name: visit_name
type: str
- name: session_id
type: int
methods:
- POST
- path: /workflow/sxt/convert_xrm_to_tiff
function: convert_xrm_to_tiff
path_params: []
methods:
- POST
- path: /workflow/sxt/visits/{visit_name}/sessions/{session_id}/register_sxt_roi
function: register_sxt_roi
path_params:
- name: visit_name
type: str
- name: session_id
type: int
methods:
- POST
Loading