2023-05-17 17:07:17 +00:00
|
|
|
import os
|
2023-06-25 03:18:09 +00:00
|
|
|
from io import BytesIO
|
2023-06-05 14:40:48 +00:00
|
|
|
from typing import Any
|
2023-06-07 01:48:51 +00:00
|
|
|
|
2023-06-25 03:18:09 +00:00
|
|
|
import cv2
|
|
|
|
import numpy as np
|
|
|
|
import uvicorn
|
|
|
|
from fastapi import Body, Depends, FastAPI
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
from .config import settings
|
|
|
|
from .models.base import InferenceModel
|
|
|
|
from .models.cache import ModelCache
|
|
|
|
from .schemas import (
|
2023-06-05 14:40:48 +00:00
|
|
|
EmbeddingResponse,
|
|
|
|
FaceResponse,
|
|
|
|
MessageResponse,
|
2023-06-25 03:18:09 +00:00
|
|
|
ModelType,
|
|
|
|
TagResponse,
|
2023-06-05 14:40:48 +00:00
|
|
|
TextModelRequest,
|
|
|
|
TextResponse,
|
|
|
|
)
|
2023-06-18 03:49:19 +00:00
|
|
|
|
2023-05-17 17:07:17 +00:00
|
|
|
app = FastAPI()
|
|
|
|
|
2023-04-26 10:39:24 +00:00
|
|
|
|
2023-05-20 03:37:01 +00:00
|
|
|
@app.on_event("startup")
|
2023-06-05 14:40:48 +00:00
|
|
|
async def startup_event() -> None:
|
2023-06-25 03:18:09 +00:00
|
|
|
app.state.model_cache = ModelCache(ttl=settings.model_ttl, revalidate=True)
|
2023-06-03 02:42:47 +00:00
|
|
|
models = [
|
2023-06-25 03:18:09 +00:00
|
|
|
(settings.classification_model, ModelType.IMAGE_CLASSIFICATION),
|
2023-06-27 17:21:50 +00:00
|
|
|
(settings.clip_image_model, ModelType.CLIP),
|
|
|
|
(settings.clip_text_model, ModelType.CLIP),
|
2023-06-25 03:18:09 +00:00
|
|
|
(settings.facial_recognition_model, ModelType.FACIAL_RECOGNITION),
|
2023-06-03 02:42:47 +00:00
|
|
|
]
|
|
|
|
|
2023-05-20 03:37:01 +00:00
|
|
|
# Get all models
|
2023-06-03 02:42:47 +00:00
|
|
|
for model_name, model_type in models:
|
2023-06-18 03:49:19 +00:00
|
|
|
if settings.eager_startup:
|
2023-06-25 03:18:09 +00:00
|
|
|
await app.state.model_cache.get(model_name, model_type)
|
2023-06-03 02:42:47 +00:00
|
|
|
else:
|
2023-06-25 03:18:09 +00:00
|
|
|
InferenceModel.from_model_type(model_type, model_name)
|
|
|
|
|
2023-05-20 03:37:01 +00:00
|
|
|
|
2023-06-25 03:18:09 +00:00
|
|
|
def dep_pil_image(byte_image: bytes = Body(...)) -> Image.Image:
|
|
|
|
return Image.open(BytesIO(byte_image))
|
2023-05-20 03:37:01 +00:00
|
|
|
|
2023-06-18 03:49:19 +00:00
|
|
|
|
2023-06-25 03:18:09 +00:00
|
|
|
def dep_cv_image(byte_image: bytes = Body(...)) -> cv2.Mat:
|
|
|
|
byte_image_np = np.frombuffer(byte_image, np.uint8)
|
|
|
|
return cv2.imdecode(byte_image_np, cv2.IMREAD_COLOR)
|
|
|
|
|
2023-06-18 03:49:19 +00:00
|
|
|
|
2023-06-05 14:40:48 +00:00
|
|
|
@app.get("/", response_model=MessageResponse)
|
|
|
|
async def root() -> dict[str, str]:
|
2023-04-26 10:39:24 +00:00
|
|
|
return {"message": "Immich ML"}
|
|
|
|
|
|
|
|
|
2023-06-05 14:40:48 +00:00
|
|
|
@app.get("/ping", response_model=TextResponse)
|
|
|
|
def ping() -> str:
|
2023-02-18 15:13:37 +00:00
|
|
|
return "pong"
|
|
|
|
|
2023-06-03 02:42:47 +00:00
|
|
|
|
2023-06-18 03:49:19 +00:00
|
|
|
@app.post(
|
|
|
|
"/image-classifier/tag-image",
|
|
|
|
response_model=TagResponse,
|
|
|
|
status_code=200,
|
|
|
|
)
|
|
|
|
async def image_classification(
|
2023-06-25 03:18:09 +00:00
|
|
|
image: Image.Image = Depends(dep_pil_image),
|
2023-06-18 03:49:19 +00:00
|
|
|
) -> list[str]:
|
2023-06-25 03:18:09 +00:00
|
|
|
model = await app.state.model_cache.get(
|
|
|
|
settings.classification_model, ModelType.IMAGE_CLASSIFICATION
|
|
|
|
)
|
|
|
|
labels = model.predict(image)
|
|
|
|
return labels
|
2023-04-26 10:39:24 +00:00
|
|
|
|
2023-03-18 13:44:42 +00:00
|
|
|
|
2023-06-05 14:40:48 +00:00
|
|
|
@app.post(
|
|
|
|
"/sentence-transformer/encode-image",
|
|
|
|
response_model=EmbeddingResponse,
|
|
|
|
status_code=200,
|
|
|
|
)
|
2023-06-18 03:49:19 +00:00
|
|
|
async def clip_encode_image(
|
2023-06-25 03:18:09 +00:00
|
|
|
image: Image.Image = Depends(dep_pil_image),
|
2023-06-18 03:49:19 +00:00
|
|
|
) -> list[float]:
|
2023-06-27 17:21:50 +00:00
|
|
|
model = await app.state.model_cache.get(settings.clip_image_model, ModelType.CLIP)
|
2023-06-25 03:18:09 +00:00
|
|
|
embedding = model.predict(image)
|
2023-06-07 01:48:51 +00:00
|
|
|
return embedding
|
2023-02-18 15:13:37 +00:00
|
|
|
|
2023-04-26 10:39:24 +00:00
|
|
|
|
2023-06-05 14:40:48 +00:00
|
|
|
@app.post(
|
|
|
|
"/sentence-transformer/encode-text",
|
|
|
|
response_model=EmbeddingResponse,
|
|
|
|
status_code=200,
|
|
|
|
)
|
2023-06-25 03:18:09 +00:00
|
|
|
async def clip_encode_text(payload: TextModelRequest) -> list[float]:
|
2023-06-27 17:21:50 +00:00
|
|
|
model = await app.state.model_cache.get(settings.clip_text_model, ModelType.CLIP)
|
2023-06-25 03:18:09 +00:00
|
|
|
embedding = model.predict(payload.text)
|
2023-06-07 01:48:51 +00:00
|
|
|
return embedding
|
2023-04-26 10:39:24 +00:00
|
|
|
|
2023-02-18 15:13:37 +00:00
|
|
|
|
2023-06-05 14:40:48 +00:00
|
|
|
@app.post(
|
2023-06-18 03:49:19 +00:00
|
|
|
"/facial-recognition/detect-faces",
|
|
|
|
response_model=FaceResponse,
|
|
|
|
status_code=200,
|
2023-06-05 14:40:48 +00:00
|
|
|
)
|
2023-06-18 03:49:19 +00:00
|
|
|
async def facial_recognition(
|
2023-06-25 03:18:09 +00:00
|
|
|
image: cv2.Mat = Depends(dep_cv_image),
|
2023-06-18 03:49:19 +00:00
|
|
|
) -> list[dict[str, Any]]:
|
2023-06-25 03:18:09 +00:00
|
|
|
model = await app.state.model_cache.get(
|
|
|
|
settings.facial_recognition_model, ModelType.FACIAL_RECOGNITION
|
2023-06-07 01:48:51 +00:00
|
|
|
)
|
2023-06-25 03:18:09 +00:00
|
|
|
faces = model.predict(image)
|
2023-06-07 01:48:51 +00:00
|
|
|
return faces
|
2023-06-03 02:42:47 +00:00
|
|
|
|
|
|
|
|
2023-02-18 15:13:37 +00:00
|
|
|
if __name__ == "__main__":
|
2023-05-20 03:37:01 +00:00
|
|
|
is_dev = os.getenv("NODE_ENV") == "development"
|
2023-06-18 03:49:19 +00:00
|
|
|
uvicorn.run(
|
2023-06-25 03:18:09 +00:00
|
|
|
"app.main:app",
|
2023-06-18 03:49:19 +00:00
|
|
|
host=settings.host,
|
|
|
|
port=settings.port,
|
|
|
|
reload=is_dev,
|
|
|
|
workers=settings.workers,
|
|
|
|
)
|