diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/application/camera-backend/main.py b/application/camera-backend/main.py new file mode 100644 index 0000000..e745e9a --- /dev/null +++ b/application/camera-backend/main.py @@ -0,0 +1,89 @@ +import io + +from fastapi import FastAPI, Response +import uvicorn +from picamera2 import Picamera2, Preview +from threading import Condition +import logging + +from picamera2.encoders import MJPEGEncoder, Quality +from picamera2.outputs import FileOutput +from starlette.background import BackgroundTask +from starlette.responses import StreamingResponse + +app = FastAPI() + +@app.get("/image") +def get_image(): + """ + Obtain a still image from the camera + :return: + """ + picam2 = Picamera2() + capture_config = picam2.create_still_configuration(main={"size": (1920,1080)}) + picam2.configure(capture_config) + + data = io.BytesIO() + picam2.start() + picam2.capture_image(data, format="jpeg") + + picam2.stop() + picam2.close() + + return Response(content=data.getvalue(), media_type="image/jpeg") + +class StreamingOutput(io.BufferedIOBase): + def __init(self): + self.frame = None + self.condition = Condition() + + def write(self, buffer): + with self.condition: + self.frame = buffer + self.condition.notify_all() + + def read(self): + with self.condition: + self.condition.wait() + return self.frame + +def generate_frames(output): + while True: + try: + frame = output.read() + yield (b"--frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + frame + b"\r\n") + except Exception as e: + logging.error(f"Error in generate_frames: {str(e)}") + break + print("done") + +@app.get("/mjpeg") +async def mjpeg(): + picam2 = Picamera2() + capture_config = picam2.create_still_configuration(main={"size": (1920,1080)}) + picam2.configure(capture_config) + + output = StreamingOutput() + picam2.start_recording(MJPEGEncoder(), FileOutput(output), Quality.VERY_HIGH) + def stop(): + logging.info("Stopping recording") + picam2.stop_recording() + picam2.close() + + return StreamingResponse( + generate_frames(output), + media_type="multipare/x-mixed-replace; boundry=frame", + background=BackgroundTask(stop) + ) + + +def main(): + uvicorn.run( + "main:app", + host="0.0.0.0", + port=8080 + ) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2b2784f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,29 @@ +annotated-types==0.7.0 +anyio==4.9.0 +attrs==25.3.0 +av==14.4.0 +click==8.2.1 +fastapi==0.115.14 +h11==0.16.0 +idna==3.10 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +libarchive-c==5.3 +numpy==2.3.1 +picamera2==0.3.27 +pidng==4.0.9 +piexif==1.1.3 +pillow==11.2.1 +pydantic==2.11.7 +pydantic_core==2.33.2 +python-prctl==1.8.1 +referencing==0.36.2 +rpds-py==0.25.1 +simplejpeg==1.8.2 +sniffio==1.3.1 +starlette==0.46.2 +tqdm==4.67.1 +typing-inspection==0.4.1 +typing_extensions==4.14.0 +uvicorn==0.35.0 +v4l2-python3==0.3.5