Reputation: 141
I get an image, change it, then it is classified using a neural network, should return a new image and json with a response. How to do it with one endpoint? image is returned with Streaming Response but how to add json to it?
import io
from starlette.responses import StreamingResponse
app = FastAPI()
@app.post("/predict")
def predict(file: UploadFile = File(...)):
img = file.read()
new_image = prepare_image(img)
result = predict(new_image)
return StreamingResponse(io.BytesIO(new_image.tobytes()), media_type="image/png")
Upvotes: 8
Views: 11837
Reputation: 3
You can use this code to return an Image and json altogether when using yolov8 model, hope this work with other models too, please modify the image generation part
@app.post("/detect")
async def detect_and_return_image(image_file: UploadFile = File(...)):
"""
Handler of /detect POST endpoint
Receives uploaded file with a name "image_file",
passes it through YOLOv8 object detection
network and returns an array of bounding boxes.
:return: a JSON array of objects bounding
boxes in format
[[x1,y1,x2,y2,object_type,probability],..]
"""
buf = await image_file.read()
boxes, class_prob = detect_objects_on_image(Image.open(BytesIO(buf)))
print(f'class proba {class_prob}')
annotated_image = annotate_image(Image.open(BytesIO(buf)), boxes)
return {
"annotated_image": image_to_base64(annotated_image),
"class_prob": class_prob
}
def detect_objects_on_image(image):
"""
Function receives an image,
passes it through YOLOv8 neural network
and returns an array of detected objects
and their bounding boxes
:param image: Input image
:return: Array of bounding boxes in format
[[x1,y1,x2,y2,object_type,probability],..]
"""
model = YOLO('best.pt')
results = model.predict(image)
result = results[0]
output = []
class_prob = []
for box in result.boxes:
x1, y1, x2, y2 = [round(x) for x in box.xyxy[0].tolist()]
class_id = box.cls[0].item()
prob = round(box.conf[0].item(), 2)
output.append([x1, y1, x2, y2, result.names[class_id], prob])
class_prob.append([result.names[class_id], prob])
return output, class_prob
def annotate_image(image, boxes):
"""
Function annotates the image with bounding boxes.
:param image: Input image
:param boxes: Array of bounding boxes in format
[[x1,y1,x2,y2,object_type,probability],..]
:return: Annotated image
"""
# Draw bounding boxes on the image
draw = ImageDraw.Draw(image)
for box in boxes:
x1, y1, x2, y2, object_type, probability = box
draw.rectangle([(x1, y1), (x2, y2)], outline="red", width=3)
draw.text((x1, y1 - 10), f"{object_type} ({probability})", fill="red")
return image
def save_annotated_image(image):
"""
Function saves the annotated image and returns
the image as a response.
:param image: Annotated image
:return: StreamingResponse with the image
"""
output_buffer = BytesIO()
image.save(output_buffer, format="PNG")
output_buffer.seek(0)
return StreamingResponse(output_buffer, media_type="image/png")
def image_to_base64(image):
buffered = BytesIO()
image.save(buffered, format="PNG")
return base64.b64encode(buffered.getvalue()).decode('utf-8')
To get full code please visit github here
Upvotes: 0
Reputation: 1019
I was having the same issue, although, my file was stored locally but still I have to return JSON, and Image in a single response.
This worked for me, much neater and shorter:
@app.post("/ImgAndJSON")
# Postmsg is a Pydantic model having 1 str field
def ImgAndJSON(message:PostMsg):
results={"message":"This is just test message"}
return FileResponse('path/to/file.png',headers=results)
Upvotes: 3
Reputation: 141
I added json to response headers. change from:
@app.post("/predict")
def predict(file: UploadFile = File(...)):
img = file.read()
new_image = prepare_image(img)
result = predict(new_image)
return StreamingResponse(io.BytesIO(new_image.tobytes()), media_type="image/png")
to
@app.post("/predict/")
def predict(file: UploadFile = File(...)):
file_bytes = file.file.read()
image = Image.open(io.BytesIO(file_bytes))
new_image = prepare_image(image)
result = predict(image)
bytes_image = io.BytesIO()
new_image.save(bytes_image, format='PNG')
return Response(content = bytes_image.getvalue(), headers = result, media_type="image/png")
Upvotes: 6