c# script support

python server support
This commit is contained in:
meelstorm
2025-08-25 17:02:33 +02:00
parent af8ee449a0
commit 07002dd875
22 changed files with 1710 additions and 95 deletions

View File

@@ -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))

View File

@@ -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>

Binary file not shown.