c# script support
python server support
This commit is contained in:
@@ -1,22 +1,43 @@
|
||||
|
||||
# app.py
|
||||
from fastapi import FastAPI, UploadFile, File, Body
|
||||
from fastapi import FastAPI, Response, UploadFile, File, Body, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
from fastapi.responses import JSONResponse
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import io
|
||||
import tensorflow as tf
|
||||
|
||||
import time
|
||||
app = FastAPI()
|
||||
|
||||
# ==== Schemas (show up in Swagger) ====
|
||||
class PredictOut(BaseModel):
|
||||
result: List[int]
|
||||
model_config = {"json_schema_extra": {"examples": [{"result": [0,1,2,3]}]}}
|
||||
|
||||
class StatusOut(BaseModel):
|
||||
status: str
|
||||
message: str
|
||||
|
||||
class AcceptSizeOut(BaseModel):
|
||||
status: str
|
||||
accept_size: List[int]
|
||||
|
||||
class LoadModelIn(BaseModel):
|
||||
path: str
|
||||
name: str
|
||||
|
||||
class ActivateModelIn(BaseModel):
|
||||
name: str
|
||||
|
||||
# ==== Service ====
|
||||
class Service:
|
||||
def __init__(self):
|
||||
self.model = None
|
||||
self.models={}
|
||||
self.models = {}
|
||||
self.active_model = "default"
|
||||
|
||||
def activate_model(self, name):
|
||||
|
||||
self.model = self.models[name]
|
||||
self.active_model = name
|
||||
return 0
|
||||
@@ -26,15 +47,13 @@ class Service:
|
||||
return 0
|
||||
|
||||
def get_accept_size(self):
|
||||
print(self.model.layers[0].input)
|
||||
|
||||
return list(self.model.input_shape)
|
||||
print(self.model.input_shape)
|
||||
return list(self.model.input_shape) # (B,H,W,C)
|
||||
|
||||
def get_output_size(self):
|
||||
return list(self.model.layers[-1].output[0].shape[1:4])
|
||||
return list(self.model.output_shape) # (B,H,W,C) or (B,H,W)
|
||||
|
||||
|
||||
def predict(self, data: bytes):
|
||||
def predict(self, data: bytes) -> List[int]:
|
||||
target_size = self.get_accept_size()
|
||||
image = np.reshape(
|
||||
np.frombuffer(data, dtype=np.uint8),
|
||||
@@ -48,21 +67,25 @@ class Service:
|
||||
else:
|
||||
classed = tf.argmax(predictions[0], axis=2)
|
||||
|
||||
return JSONResponse({"result": classed.numpy().reshape(-1).tolist()})
|
||||
return classed.numpy().reshape(-1).tolist()
|
||||
|
||||
def predict_raw(self, data):
|
||||
def predict_raw(self, data: bytes) -> np.ndarray:
|
||||
target_size = self.get_accept_size()
|
||||
image = np.reshape(np.frombuffer(data, dtype=np.uint8), (target_size[1], target_size[2], target_size[3])) / 255.
|
||||
image = np.reshape(
|
||||
np.frombuffer(data, dtype=np.uint8),
|
||||
(target_size[1], target_size[2], target_size[3])
|
||||
) / 255.0
|
||||
|
||||
predictions = self.model(np.array([image])).numpy()
|
||||
predictions = self.model({"input_layer": np.array([image], dtype=np.float32)}).numpy()
|
||||
|
||||
if predictions[0].ndim==2:
|
||||
classed = predictions[0][:,:,np.newaxis].clip(0,255)
|
||||
return (classed).astype(np.uint8).reshape(-1).tolist()
|
||||
if predictions[0].ndim == 2:
|
||||
classed = predictions[0][:, :, np.newaxis].clip(0, 255)
|
||||
return classed.astype(np.uint8).reshape(-1).tolist()
|
||||
else:
|
||||
classed = predictions[0][:,:,:]
|
||||
classed = predictions[0][:, :, :]
|
||||
|
||||
return (classed*255).astype(np.uint8).reshape(-1).tolist()
|
||||
res = (classed * 255).astype(np.uint8).reshape(-1)
|
||||
return np.asarray(res)
|
||||
|
||||
svc = Service()
|
||||
|
||||
@@ -75,33 +98,61 @@ def _prep_raw_bytes_for_predict(buf: bytes) -> bytes:
|
||||
arr = np.asarray(img, dtype=np.uint8) # HxWx3 uint8
|
||||
return arr.tobytes()
|
||||
|
||||
@app.post("/predict")
|
||||
# ==== Routes ====
|
||||
@app.post("/predict", response_model=bytes, summary="Predict (classified indices)")
|
||||
async def predict(file: UploadFile = File(...)):
|
||||
buf = await file.read()
|
||||
raw = _prep_raw_bytes_for_predict(buf)
|
||||
return svc.predict(raw)
|
||||
try:
|
||||
buf = await file.read()
|
||||
raw = _prep_raw_bytes_for_predict(buf)
|
||||
result = svc.predict(raw)
|
||||
return Response(content=result, media_type="application/octet-stream")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@app.post("/predict_raw")
|
||||
@app.post("/predictRaw", response_model=bytes, summary="Predict (raw/uint8 flattened)")
|
||||
async def predict_raw(file: UploadFile = File(...)):
|
||||
buf = await file.read()
|
||||
raw = _prep_raw_bytes_for_predict(buf)
|
||||
return svc.predict_raw(raw)
|
||||
|
||||
@app.post("/load_model")
|
||||
async def load_model(path: str = Body(...), name: str = Body(...)):
|
||||
try:
|
||||
svc.load_model(path, name)
|
||||
return JSONResponse({"status": "success", "message": f"Model {name} loaded successfully."})
|
||||
start_time = time.time()
|
||||
buf = await file.read()
|
||||
result = svc.predict_raw(buf)
|
||||
elapsed_time = time.time() - start_time
|
||||
print(f"Prediction took {elapsed_time:.2f} seconds")
|
||||
return Response(content=result.tobytes(), media_type="application/octet-stream")
|
||||
except Exception as e:
|
||||
return JSONResponse({"status": "error", "message": str(e)}, status_code=500)
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@app.post("/activate_model")
|
||||
async def activate_model(name: str = Body(...)):
|
||||
@app.post("/loadModel", response_model=StatusOut, summary="Load a model from path")
|
||||
async def load_model(payload: LoadModelIn):
|
||||
try:
|
||||
svc.activate_model(name)
|
||||
return JSONResponse({"status": "success", "message": f"Model {name} activated successfully."})
|
||||
svc.load_model(payload.path, payload.name)
|
||||
return StatusOut(status="success", message=f"Model {payload.name} loaded successfully.")
|
||||
except Exception as e:
|
||||
return JSONResponse({"status": "error", "message": str(e)}, status_code=500)
|
||||
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/activateModel", response_model=StatusOut, summary="Activate a loaded model")
|
||||
async def activate_model(payload: ActivateModelIn):
|
||||
try:
|
||||
svc.activate_model(payload.name)
|
||||
return StatusOut(status="success", message=f"Model {payload.name} activated successfully.")
|
||||
except KeyError:
|
||||
raise HTTPException(status_code=404, detail=f"Model {payload.name} not found.")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get("/getAcceptSize", response_model=AcceptSizeOut, summary="Get input tensor shape")
|
||||
async def get_accept_size():
|
||||
try:
|
||||
size = svc.get_accept_size()[1:] # Exclude batch size
|
||||
return AcceptSizeOut(status="success", accept_size=size)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get("/getOutputSize", response_model=AcceptSizeOut, summary="Get output tensor shape")
|
||||
async def get_output_size():
|
||||
try:
|
||||
size = svc.get_output_size()[1:]
|
||||
return AcceptSizeOut(status="success", accept_size=size)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>PythonModelAPI</Name>
|
||||
<RootNamespace>PythonModelAPI</RootNamespace>
|
||||
<PublishUrl>D:\Inspectron\Hawkeye\code\VisionBuilder5\VisionBuilder.UI\Hawkeye.VisionBuilder\bin\Debug\Data\Models</PublishUrl>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
||||
BIN
PythonModelAPI/office4_tr.h5
Normal file
BIN
PythonModelAPI/office4_tr.h5
Normal file
Binary file not shown.
Reference in New Issue
Block a user