Spaces:
Runtime error
Runtime error
Ziad Meligy commited on
Commit ·
32458a9
1
Parent(s): 0682281
Adding files for docker build
Browse files- Dockerfile +22 -0
- app/main.py +54 -0
- app/maira2Service.py +43 -0
- app/readimageService.py +8 -0
- requirements.txt +0 -0
Dockerfile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11.4-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Create cache directory and set permissions as root
|
| 6 |
+
RUN mkdir -p /app/cache && \
|
| 7 |
+
useradd -m -u 1000 user && \
|
| 8 |
+
chown user:user /app/cache
|
| 9 |
+
|
| 10 |
+
# Switch to non-root user
|
| 11 |
+
USER user
|
| 12 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 13 |
+
|
| 14 |
+
# Copy and install requirements
|
| 15 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 16 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 17 |
+
|
| 18 |
+
# Copy application code
|
| 19 |
+
COPY --chown=user . /app
|
| 20 |
+
|
| 21 |
+
# Run FastAPI app
|
| 22 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
app/main.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, UploadFile, File, Form
|
| 2 |
+
from fastapi.responses import JSONResponse
|
| 3 |
+
|
| 4 |
+
from maira2Service import generate_report
|
| 5 |
+
from readimageService import read_image
|
| 6 |
+
|
| 7 |
+
app = FastAPI()
|
| 8 |
+
|
| 9 |
+
@app.post("/generate/frontal")
|
| 10 |
+
async def generate_frontal_report(frontal: UploadFile = File(...)):
|
| 11 |
+
try:
|
| 12 |
+
frontal_img = read_image(frontal)
|
| 13 |
+
report = generate_report(frontal_image=frontal_img)
|
| 14 |
+
return JSONResponse(content={"report": report})
|
| 15 |
+
except Exception as e:
|
| 16 |
+
return JSONResponse(status_code=500, content={"error": str(e)})
|
| 17 |
+
|
| 18 |
+
@app.post("/generate/lateral")
|
| 19 |
+
async def generate_lateral_report(
|
| 20 |
+
frontal: UploadFile = File(...),
|
| 21 |
+
lateral: UploadFile = File(...),
|
| 22 |
+
indication: str = Form(...),
|
| 23 |
+
technique: str = Form(...),
|
| 24 |
+
comparison: str = Form(...)
|
| 25 |
+
):
|
| 26 |
+
try:
|
| 27 |
+
frontal_img = read_image(frontal)
|
| 28 |
+
lateral_img = read_image(lateral)
|
| 29 |
+
report = generate_report(
|
| 30 |
+
frontal_image=frontal_img,
|
| 31 |
+
lateral_image=lateral_img,
|
| 32 |
+
indication=indication,
|
| 33 |
+
technique=technique,
|
| 34 |
+
comparison=comparison
|
| 35 |
+
)
|
| 36 |
+
return JSONResponse(content={"report": report})
|
| 37 |
+
except Exception as e:
|
| 38 |
+
return JSONResponse(status_code=500, content={"error": str(e)})
|
| 39 |
+
|
| 40 |
+
@app.post("/generate/lateral/both")
|
| 41 |
+
async def generate_lateral_both_report(
|
| 42 |
+
frontal: UploadFile = File(...),
|
| 43 |
+
lateral: UploadFile = File(...),
|
| 44 |
+
):
|
| 45 |
+
try:
|
| 46 |
+
frontal_img = read_image(frontal)
|
| 47 |
+
lateral_img = read_image(lateral)
|
| 48 |
+
report = generate_report(
|
| 49 |
+
frontal_image=frontal_img,
|
| 50 |
+
lateral_image=lateral_img,
|
| 51 |
+
)
|
| 52 |
+
return JSONResponse(content={"report": report})
|
| 53 |
+
except Exception as e:
|
| 54 |
+
return JSONResponse(status_code=500, content={"error": str(e)})
|
app/maira2Service.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import AutoModelForCausalLM, AutoProcessor
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
model = AutoModelForCausalLM.from_pretrained("microsoft/maira-2", trust_remote_code=True)
|
| 6 |
+
processor = AutoProcessor.from_pretrained("microsoft/maira-2", trust_remote_code=True)
|
| 7 |
+
|
| 8 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 9 |
+
if torch.cuda.is_available():
|
| 10 |
+
print("Using GPU for inference")
|
| 11 |
+
model = model.eval()
|
| 12 |
+
model = model.to(device)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def generate_report(frontal_image = None, lateral_image = None, indication="", technique="", comparison="" ):
|
| 17 |
+
inputs = processor.format_and_preprocess_reporting_input(
|
| 18 |
+
current_frontal=frontal_image,
|
| 19 |
+
current_lateral=lateral_image,
|
| 20 |
+
prior_frontal=None,
|
| 21 |
+
indication=indication,
|
| 22 |
+
technique=technique,
|
| 23 |
+
comparison=comparison,
|
| 24 |
+
prior_report=None,
|
| 25 |
+
return_tensors="pt",
|
| 26 |
+
get_grounding=False,
|
| 27 |
+
)
|
| 28 |
+
inputs = inputs.to(device)
|
| 29 |
+
with torch.no_grad():
|
| 30 |
+
output_decoding = model.generate(
|
| 31 |
+
**inputs,
|
| 32 |
+
max_new_tokens=300, # Set to 450 for grounded reporting
|
| 33 |
+
use_cache=True,
|
| 34 |
+
)
|
| 35 |
+
prompt_length = inputs["input_ids"].shape[-1]
|
| 36 |
+
decoded_text = processor.decode(output_decoding[0][prompt_length:], skip_special_tokens=True)
|
| 37 |
+
decoded_text = decoded_text.lstrip()
|
| 38 |
+
prediction = processor.convert_output_to_plaintext_or_grounded_sequence(decoded_text)
|
| 39 |
+
print("Parsed prediction:", prediction)
|
| 40 |
+
return prediction
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
print("Maira-2 service is ready to generate reports.")
|
app/readimageService.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, UploadFile, File, Form
|
| 2 |
+
from fastapi.responses import JSONResponse
|
| 3 |
+
import io
|
| 4 |
+
from PIL import Image
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def read_image(upload: UploadFile) -> Image.Image:
|
| 8 |
+
return Image.open(io.BytesIO(upload.file.read()))
|
requirements.txt
ADDED
|
Binary file (1.51 kB). View file
|
|
|