-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathworkflow_fib.py
More file actions
135 lines (117 loc) · 4.21 KB
/
Copy pathworkflow_fib.py
File metadata and controls
135 lines (117 loc) · 4.21 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
import json
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 Session, 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 Transport Manager 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,
db: Session = murfey_db,
):
logger.debug(
"Received the following FIB metadata for registration:\n"
f"{json.dumps(site_info.model_dump(exclude_none=True), indent=2, default=str)}"
)
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
arr: list[np.ndarray] = []
for f in gif_params.images:
with PIL.Image.open(f) as im:
im.thumbnail((512, 512))
frame = np.array(im, dtype=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
try:
converted = [PIL.Image.fromarray(a, mode="L") for a in arr]
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)}
finally:
for im in converted:
im.close()