Jaysum commited on
Commit
2f372a2
·
1 Parent(s): 47c3434

Added Dockerfile and necessary scripts

Browse files
Files changed (4) hide show
  1. Dockerfile +53 -0
  2. compose.yaml +49 -0
  3. main.py +77 -0
  4. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1
2
+
3
+ # Comments are provided throughout this file to help you get started.
4
+ # If you need more help, visit the Dockerfile reference guide at
5
+ # https://docs.docker.com/go/dockerfile-reference/
6
+
7
+ # Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
8
+
9
+ ARG PYTHON_VERSION=3.10.5
10
+ FROM python:${PYTHON_VERSION} as base
11
+
12
+ # Prevents Python from writing pyc files.
13
+ ENV PYTHONDONTWRITEBYTECODE=1
14
+
15
+ # Keeps Python from buffering stdout and stderr to avoid situations where
16
+ # the application crashes without emitting any logs due to buffering.
17
+ ENV PYTHONUNBUFFERED=1
18
+
19
+ WORKDIR /app
20
+
21
+ RUN python -m pip install --upgrade pip
22
+
23
+ # Create a non-privileged user that the app will run under.
24
+ # See https://docs.docker.com/go/dockerfile-user-best-practices/
25
+ ARG UID=10001
26
+ RUN adduser \
27
+ --disabled-password \
28
+ --gecos "" \
29
+ --home "/nonexistent" \
30
+ --shell "/sbin/nologin" \
31
+ --no-create-home \
32
+ --uid "${UID}" \
33
+ appuser
34
+
35
+ # Download dependencies as a separate step to take advantage of Docker's caching.
36
+ # Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
37
+ # Leverage a bind mount to requirements.txt to avoid having to copy them into
38
+ # into this layer.
39
+ RUN --mount=type=cache,target=/root/.cache/pip \
40
+ --mount=type=bind,source=requirements.txt,target=requirements.txt \
41
+ python -m pip install -r requirements.txt
42
+
43
+ # Switch to the non-privileged user to run the application.
44
+ USER appuser
45
+
46
+ # Copy the source code into the container.
47
+ COPY . .
48
+
49
+ # Expose the port that the application listens on.
50
+ EXPOSE 7860
51
+
52
+ # Run the application.
53
+ CMD uvicorn 'main:app' --host=0.0.0.0 --port=7860
compose.yaml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Comments are provided throughout this file to help you get started.
2
+ # If you need more help, visit the Docker Compose reference guide at
3
+ # https://docs.docker.com/go/compose-spec-reference/
4
+
5
+ # Here the instructions define your application as a service called "server".
6
+ # This service is built from the Dockerfile in the current directory.
7
+ # You can add other services your application may depend on here, such as a
8
+ # database or a cache. For examples, see the Awesome Compose repository:
9
+ # https://github.com/docker/awesome-compose
10
+ services:
11
+ server:
12
+ build:
13
+ context: .
14
+ ports:
15
+ - 8000:8000
16
+
17
+ # The commented out section below is an example of how to define a PostgreSQL
18
+ # database that your application can use. `depends_on` tells Docker Compose to
19
+ # start the database before your application. The `db-data` volume persists the
20
+ # database data between container restarts. The `db-password` secret is used
21
+ # to set the database password. You must create `db/password.txt` and add
22
+ # a password of your choosing to it before running `docker compose up`.
23
+ # depends_on:
24
+ # db:
25
+ # condition: service_healthy
26
+ # db:
27
+ # image: postgres
28
+ # restart: always
29
+ # user: postgres
30
+ # secrets:
31
+ # - db-password
32
+ # volumes:
33
+ # - db-data:/var/lib/postgresql/data
34
+ # environment:
35
+ # - POSTGRES_DB=example
36
+ # - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
37
+ # expose:
38
+ # - 5432
39
+ # healthcheck:
40
+ # test: [ "CMD", "pg_isready" ]
41
+ # interval: 10s
42
+ # timeout: 5s
43
+ # retries: 5
44
+ # volumes:
45
+ # db-data:
46
+ # secrets:
47
+ # db-password:
48
+ # file: db/password.txt
49
+
main.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile
2
+ import io
3
+ import tensorflow as tf
4
+ import numpy as np
5
+ from PIL import Image
6
+ import os
7
+ import requests
8
+
9
+ # Google Drive file ID of the model
10
+ DRIVE_FILE_ID = "1HBzc72rm8NpJZoMQwLA4SJOBR_FzMjRe" # Replace with your file ID
11
+ MODEL_PATH = "./efficientnet_poultry_disease_model.keras"
12
+
13
+ # Function to download the model from Google Drive
14
+ def download_model():
15
+ if not os.path.exists(MODEL_PATH): # Check if the model already exists
16
+ print("Downloading model from Google Drive...")
17
+ # Construct the download URL
18
+ url = f"https://drive.google.com/uc?export=download&id={DRIVE_FILE_ID}"
19
+ with requests.Session() as session:
20
+ response = session.get(url, stream=True)
21
+ # Handle confirmation for large files
22
+ for key, value in response.cookies.items():
23
+ if key.startswith("download_warning"):
24
+ url = f"https://drive.google.com/uc?export=download&id={DRIVE_FILE_ID}&confirm={value}"
25
+ response = session.get(url, stream=True)
26
+ break
27
+ # Write the content to a file
28
+ with open(MODEL_PATH, "wb") as f:
29
+ for chunk in response.iter_content(chunk_size=32768):
30
+ f.write(chunk)
31
+
32
+ # Download the model if not already downloaded
33
+ download_model()
34
+
35
+ # Load the pre-trained model
36
+ model = tf.keras.models.load_model(MODEL_PATH)
37
+
38
+ # List of classes the model predicts
39
+ CLASSES = ['coccidiosis', 'healthy', 'newcastle disease', 'salmo']
40
+ IMAGE_SIZE = (360, 360)
41
+
42
+ # Function to preprocess and predict a single image
43
+ def predict_image(image_stream: io.BytesIO):
44
+ # Load the image from the BytesIO stream
45
+ img = Image.open(image_stream)
46
+ img = img.resize(IMAGE_SIZE) # Resize the image
47
+ img_array = np.array(img, dtype=np.float32) # Convert to float32 for proper scaling
48
+ img_array = np.expand_dims(img_array, axis=0) # Expand dims to make it (1, IMAGE_SIZE, IMAGE_SIZE, 3)
49
+
50
+ # Normalize the image by dividing by 255.0
51
+ img_array /= 255.0
52
+
53
+ # Make prediction
54
+ predictions = model.predict(img_array)
55
+ predicted_class = np.argmax(predictions, axis=1)[0]
56
+ confidence = predictions[0][predicted_class]
57
+
58
+ # Return prediction results as a dictionary
59
+ return {
60
+ "class": CLASSES[predicted_class],
61
+ "confidence": confidence.item() # Convert numpy.float32 to native float
62
+ }
63
+
64
+ app = FastAPI()
65
+
66
+ @app.get("/")
67
+ def read_root():
68
+ return {"Hello": "World"}
69
+
70
+ @app.post("/predict")
71
+ async def predict(image: UploadFile):
72
+ content = await image.read() # Read the uploaded image content
73
+
74
+ # Pass the byte content as a BytesIO object to predict_image
75
+ result = predict_image(io.BytesIO(content)) # Pass the byte stream instead of the PIL image
76
+
77
+ return result # FastAPI will automatically convert the dictionary to JSON
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi==0.115.5
2
+ matplotlib==3.9.2
3
+ numpy>=1.26.0,<2.1.0
4
+ Pillow==11.0.0
5
+ tensorflow==2.18.0
6
+ uvicorn
7
+ python-multipart