-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathworkflow_fib.py
More file actions
139 lines (120 loc) · 4.32 KB
/
Copy pathworkflow_fib.py
File metadata and controls
139 lines (120 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import logging
import os
from pathlib import Path
import numpy as np
import PIL.Image
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from sqlmodel import select
import murfey.util.db as MurfeyDB
from murfey.server import _transport_object
from murfey.server.api.auth import validate_instrument_token
from murfey.server.murfey_db import murfey_db
from murfey.util import sanitise_path
from murfey.util.config import get_machine_config
from murfey.util.models import LamellaSiteInfo
logger = logging.getLogger("murfey.server.api.workflow_fib")
router = APIRouter(
prefix="/workflow/fib",
dependencies=[Depends(validate_instrument_token)],
tags=["Workflows: FIB milling"],
)
class FIBAtlasFile(BaseModel):
file: Path
@router.post("/sessions/{session_id}/register_atlas")
def register_fib_atlas(
session_id: int,
fib_atlas: FIBAtlasFile,
):
if _transport_object is None:
logger.error("No TransportManager object was set up")
return None
_transport_object.send(
_transport_object.feedback_queue,
{
"register": "fib.register_atlas",
"session_id": session_id,
"atlas_file": str(fib_atlas.file),
},
)
@router.post("/sessions/{session_id}/register_milling_progress")
def register_fib_milling_progress(
session_id: int,
site_info: LamellaSiteInfo,
):
if _transport_object is None:
logger.error("No TransportManager object was set up")
return None
_transport_object.send(
_transport_object.feedback_queue,
{
"register": "fib.register_milling_progress",
"session_id": session_id,
"site_info": site_info.model_dump(exclude_none=True),
},
)
class FIBGIFParameters(BaseModel):
lamella_number: int
images: list[Path]
output_file: Path
@router.post("/sessions/{session_id}/make_gif")
async def make_gif(
session_id: int,
gif_params: FIBGIFParameters,
db=murfey_db,
):
# Load machine config and session info
session_entry = db.exec(
select(MurfeyDB.Session).where(MurfeyDB.Session.id == session_id)
).one()
instrument_name = session_entry.instrument_name
visit_name = session_entry.visit
machine_config = get_machine_config(instrument_name=instrument_name)[
instrument_name
]
rsync_basepath = machine_config.rsync_basepath or Path(".").resolve()
# Sanitise and verify that the output directory is relative to rsync basepath
output_file = sanitise_path(gif_params.output_file)
if not output_file.is_relative_to(rsync_basepath):
logger.error("Output file path is not permitted")
raise ValueError
# Create folders in the visit directory and onwards and change permissions
visit_index = output_file.parts.index(visit_name)
for current_path in list(reversed(output_file.parents))[visit_index + 1 :]:
if not current_path.exists():
current_path.mkdir(parents=True)
logger.debug(f"Created output directory {current_path}")
try:
os.chmod(current_path, mode=machine_config.mkdir_chmod)
except PermissionError:
logger.warning(
f"Insufficient permissions to modify directory {current_path}"
)
continue
# Load the images as PIL Image objects
images = [PIL.Image.open(f) for f in gif_params.images]
for im in images:
im.thumbnail((512, 512))
# Normalize and convert individual frames to 8-bit
arr: list[np.ndarray] = []
for im in images:
frame = np.array(im).astype(np.float32)
vmin, vmax = np.percentile(frame, (0.5, 99.5))
scale = 255 / ((vmax - vmin) or 1)
np.clip(frame, a_min=vmin, a_max=vmax, out=frame)
np.subtract(frame, vmin, out=frame)
np.multiply(frame, scale, out=frame)
arr.append(frame.astype(np.uint8))
arr = np.array(arr).astype(np.uint8)
# Convert back to PIL.Image objects and save as GIF
converted = [PIL.Image.fromarray(arr[f], mode="L") for f in range(len(images))]
converted[0].save(
output_file,
format="GIF",
append_images=converted[1:],
save_all=True,
duration=30,
loop=0,
)
logger.info(f"Created GIF file {output_file}")
return {"output_gif": str(output_file)}