|
@@ -1,58 +1,77 @@
|
|
-import os
|
|
|
|
-from flask import Flask, request
|
|
|
|
from transformers import pipeline
|
|
from transformers import pipeline
|
|
from sentence_transformers import SentenceTransformer, util
|
|
from sentence_transformers import SentenceTransformer, util
|
|
from PIL import Image
|
|
from PIL import Image
|
|
|
|
+from fastapi import FastAPI
|
|
|
|
+import uvicorn
|
|
|
|
+import os
|
|
|
|
+from pydantic import BaseModel
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class MlRequestBody(BaseModel):
|
|
|
|
+ thumbnailPath: str
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class ClipRequestBody(BaseModel):
|
|
|
|
+ text: str
|
|
|
|
+
|
|
|
|
|
|
is_dev = os.getenv('NODE_ENV') == 'development'
|
|
is_dev = os.getenv('NODE_ENV') == 'development'
|
|
server_port = os.getenv('MACHINE_LEARNING_PORT', 3003)
|
|
server_port = os.getenv('MACHINE_LEARNING_PORT', 3003)
|
|
server_host = os.getenv('MACHINE_LEARNING_HOST', '0.0.0.0')
|
|
server_host = os.getenv('MACHINE_LEARNING_HOST', '0.0.0.0')
|
|
|
|
|
|
-classification_model = os.getenv('MACHINE_LEARNING_CLASSIFICATION_MODEL', 'microsoft/resnet-50')
|
|
|
|
|
|
+app = FastAPI()
|
|
|
|
+
|
|
|
|
+"""
|
|
|
|
+Model Initialization
|
|
|
|
+"""
|
|
|
|
+classification_model = os.getenv(
|
|
|
|
+ 'MACHINE_LEARNING_CLASSIFICATION_MODEL', 'microsoft/resnet-50')
|
|
object_model = os.getenv('MACHINE_LEARNING_OBJECT_MODEL', 'hustvl/yolos-tiny')
|
|
object_model = os.getenv('MACHINE_LEARNING_OBJECT_MODEL', 'hustvl/yolos-tiny')
|
|
-clip_image_model = os.getenv('MACHINE_LEARNING_CLIP_IMAGE_MODEL', 'clip-ViT-B-32')
|
|
|
|
-clip_text_model = os.getenv('MACHINE_LEARNING_CLIP_TEXT_MODEL', 'clip-ViT-B-32')
|
|
|
|
|
|
+clip_image_model = os.getenv(
|
|
|
|
+ 'MACHINE_LEARNING_CLIP_IMAGE_MODEL', 'clip-ViT-B-32')
|
|
|
|
+clip_text_model = os.getenv(
|
|
|
|
+ 'MACHINE_LEARNING_CLIP_TEXT_MODEL', 'clip-ViT-B-32')
|
|
|
|
|
|
_model_cache = {}
|
|
_model_cache = {}
|
|
-def _get_model(model, task=None):
|
|
|
|
- global _model_cache
|
|
|
|
- key = '|'.join([model, str(task)])
|
|
|
|
- if key not in _model_cache:
|
|
|
|
- if task:
|
|
|
|
- _model_cache[key] = pipeline(model=model, task=task)
|
|
|
|
- else:
|
|
|
|
- _model_cache[key] = SentenceTransformer(model)
|
|
|
|
- return _model_cache[key]
|
|
|
|
-
|
|
|
|
-server = Flask(__name__)
|
|
|
|
-
|
|
|
|
-@server.route("/ping")
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@app.get("/")
|
|
|
|
+async def root():
|
|
|
|
+ return {"message": "Immich ML"}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@app.get("/ping")
|
|
def ping():
|
|
def ping():
|
|
return "pong"
|
|
return "pong"
|
|
|
|
|
|
-@server.route("/object-detection/detect-object", methods=['POST'])
|
|
|
|
-def object_detection():
|
|
|
|
|
|
+
|
|
|
|
+@app.post("/object-detection/detect-object", status_code=200)
|
|
|
|
+def object_detection(payload: MlRequestBody):
|
|
model = _get_model(object_model, 'object-detection')
|
|
model = _get_model(object_model, 'object-detection')
|
|
- assetPath = request.json['thumbnailPath']
|
|
|
|
- return run_engine(model, assetPath), 200
|
|
|
|
|
|
+ assetPath = payload.thumbnailPath
|
|
|
|
+ return run_engine(model, assetPath)
|
|
|
|
|
|
-@server.route("/image-classifier/tag-image", methods=['POST'])
|
|
|
|
-def image_classification():
|
|
|
|
|
|
+
|
|
|
|
+@app.post("/image-classifier/tag-image", status_code=200)
|
|
|
|
+def image_classification(payload: MlRequestBody):
|
|
model = _get_model(classification_model, 'image-classification')
|
|
model = _get_model(classification_model, 'image-classification')
|
|
- assetPath = request.json['thumbnailPath']
|
|
|
|
- return run_engine(model, assetPath), 200
|
|
|
|
|
|
+ assetPath = payload.thumbnailPath
|
|
|
|
+ return run_engine(model, assetPath)
|
|
|
|
+
|
|
|
|
|
|
-@server.route("/sentence-transformer/encode-image", methods=['POST'])
|
|
|
|
-def clip_encode_image():
|
|
|
|
|
|
+@app.post("/sentence-transformer/encode-image", status_code=200)
|
|
|
|
+def clip_encode_image(payload: MlRequestBody):
|
|
model = _get_model(clip_image_model)
|
|
model = _get_model(clip_image_model)
|
|
- assetPath = request.json['thumbnailPath']
|
|
|
|
- return model.encode(Image.open(assetPath)).tolist(), 200
|
|
|
|
|
|
+ assetPath = payload.thumbnailPath
|
|
|
|
+ return model.encode(Image.open(assetPath)).tolist()
|
|
|
|
|
|
-@server.route("/sentence-transformer/encode-text", methods=['POST'])
|
|
|
|
-def clip_encode_text():
|
|
|
|
|
|
+
|
|
|
|
+@app.post("/sentence-transformer/encode-text", status_code=200)
|
|
|
|
+def clip_encode_text(payload: ClipRequestBody):
|
|
model = _get_model(clip_text_model)
|
|
model = _get_model(clip_text_model)
|
|
- text = request.json['text']
|
|
|
|
- return model.encode(text).tolist(), 200
|
|
|
|
|
|
+ text = payload.text
|
|
|
|
+ return model.encode(text).tolist()
|
|
|
|
+
|
|
|
|
|
|
def run_engine(engine, path):
|
|
def run_engine(engine, path):
|
|
result = []
|
|
result = []
|
|
@@ -69,5 +88,17 @@ def run_engine(engine, path):
|
|
return result
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
+def _get_model(model, task=None):
|
|
|
|
+ global _model_cache
|
|
|
|
+ key = '|'.join([model, str(task)])
|
|
|
|
+ if key not in _model_cache:
|
|
|
|
+ if task:
|
|
|
|
+ _model_cache[key] = pipeline(model=model, task=task)
|
|
|
|
+ else:
|
|
|
|
+ _model_cache[key] = SentenceTransformer(model)
|
|
|
|
+ return _model_cache[key]
|
|
|
|
+
|
|
|
|
+
|
|
if __name__ == "__main__":
|
|
if __name__ == "__main__":
|
|
- server.run(debug=is_dev, host=server_host, port=server_port)
|
|
|
|
|
|
+ uvicorn.run("main:app", host=server_host,
|
|
|
|
+ port=int(server_port), reload=is_dev, workers=1)
|