Spaces:
Running
Running
Upload 3 files
Browse files- Dockerfile +19 -0
- predict.py +110 -0
- requirements.txt +10 -0
Dockerfile
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9-slim
|
2 |
+
|
3 |
+
# Set the working directory to /app
|
4 |
+
WORKDIR /app
|
5 |
+
|
6 |
+
# Copy the requirements file
|
7 |
+
COPY requirements.txt .
|
8 |
+
|
9 |
+
# Install the dependencies
|
10 |
+
RUN pip install -r requirements.txt
|
11 |
+
|
12 |
+
# Copy the application code
|
13 |
+
COPY . .
|
14 |
+
|
15 |
+
# Expose the port
|
16 |
+
EXPOSE 8000
|
17 |
+
|
18 |
+
# Run the command to start the development server
|
19 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
predict.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, File, UploadFile, Response
|
2 |
+
from fastapi.responses import FileResponse
|
3 |
+
from tensorflow.keras.preprocessing.image import img_to_array
|
4 |
+
import tensorflow as tf
|
5 |
+
import cv2
|
6 |
+
import numpy as np
|
7 |
+
import os
|
8 |
+
from scipy import fftpack
|
9 |
+
from scipy import ndimage
|
10 |
+
from ultralytics import YOLO
|
11 |
+
from PIL import Image
|
12 |
+
import io
|
13 |
+
|
14 |
+
app = FastAPI()
|
15 |
+
uploads_dir = 'uploads'
|
16 |
+
|
17 |
+
if not os.path.exists(uploads_dir):
|
18 |
+
os.makedirs(uploads_dir)
|
19 |
+
|
20 |
+
# Load the saved models
|
21 |
+
segmentation_model_path = 'segmentation_model.h5'
|
22 |
+
segmentation_model = tf.keras.models.load_model(segmentation_model_path)
|
23 |
+
yolo_model_path = 'best.pt'
|
24 |
+
yolo_model = YOLO(yolo_model_path)
|
25 |
+
|
26 |
+
def calculate_moisture_and_texture(image):
|
27 |
+
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
28 |
+
fft_image = fftpack.fft2(gray_image)
|
29 |
+
fft_shifted = fftpack.fftshift(fft_image)
|
30 |
+
magnitude_spectrum = 20 * np.log(np.abs(fft_shifted))
|
31 |
+
height, width = magnitude_spectrum.shape
|
32 |
+
center_x, center_y = width // 2, height // 2
|
33 |
+
radius = min(center_x, center_y) // 2
|
34 |
+
moisture_region = magnitude_spectrum[center_y - radius:center_y + radius, center_x - radius:center_x + radius]
|
35 |
+
moisture_level = np.mean(moisture_region)
|
36 |
+
return moisture_level
|
37 |
+
|
38 |
+
def calculate_wound_dimensions(mask):
|
39 |
+
labeled_mask, num_labels = ndimage.label(mask > 0.5)
|
40 |
+
label_count = np.bincount(labeled_mask.ravel())
|
41 |
+
wound_label = np.argmax(label_count[1:]) + 1
|
42 |
+
wound_region = labeled_mask == wound_label
|
43 |
+
rows = np.any(wound_region, axis=1)
|
44 |
+
cols = np.any(wound_region, axis=0)
|
45 |
+
rmin, rmax = np.where(rows)[0][[0, -1]]
|
46 |
+
cmin, cmax = np.where(cols)[0][[0, -1]]
|
47 |
+
length_pixels = rmax - rmin
|
48 |
+
breadth_pixels = cmax - cmin
|
49 |
+
pixel_to_cm_ratio = 0.1
|
50 |
+
length_cm = length_pixels * pixel_to_cm_ratio
|
51 |
+
breadth_cm = breadth_pixels * pixel_to_cm_ratio
|
52 |
+
depth_cm = np.mean(mask[wound_region]) * pixel_to_cm_ratio
|
53 |
+
length_cm = round(length_cm, 3)
|
54 |
+
breadth_cm = round(breadth_cm, 3)
|
55 |
+
depth_cm = round(depth_cm, 3)
|
56 |
+
area_cm2 = length_cm * breadth_cm
|
57 |
+
return length_cm, breadth_cm, depth_cm, area_cm2
|
58 |
+
|
59 |
+
@app.post("/analyze_wound")
|
60 |
+
async def analyze_wounds(file: UploadFile = File(...)):
|
61 |
+
if file.filename.lower().endswith(('.png', '.jpg', '.jpeg')):
|
62 |
+
contents = await file.read()
|
63 |
+
nparr = np.fromstring(contents, np.uint8)
|
64 |
+
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
65 |
+
results = yolo_model(img)
|
66 |
+
combined_xmin = float('inf')
|
67 |
+
combined_ymin = float('inf')
|
68 |
+
combined_xmax = float('-inf')
|
69 |
+
combined_ymax = float('-inf')
|
70 |
+
for detection in results[0].boxes.xyxy.tolist():
|
71 |
+
xmin, ymin, xmax, ymax = detection
|
72 |
+
combined_xmin = min(combined_xmin, xmin)
|
73 |
+
combined_ymin = min(combined_ymin, ymin)
|
74 |
+
combined_xmax = max(combined_xmax, xmax)
|
75 |
+
combined_ymax = max(combined_ymax, ymax)
|
76 |
+
combined_xmin = int(combined_xmin)
|
77 |
+
combined_ymin = int(combined_ymin)
|
78 |
+
combined_xmax = int(combined_xmax)
|
79 |
+
combined_ymax = int(combined_ymax)
|
80 |
+
combined_img = img[combined_ymin:combined_ymax, combined_xmin:combined_xmax]
|
81 |
+
combined_img_resized = cv2.resize(combined_img, (224, 224))
|
82 |
+
img_array = img_to_array(combined_img_resized) / 255.0
|
83 |
+
img_array = np.expand_dims(img_array, axis=0)
|
84 |
+
output = segmentation_model.predict(img_array)
|
85 |
+
predicted_mask = output[0]
|
86 |
+
mask_overlay = (predicted_mask.squeeze() * 255).astype(np.uint8)
|
87 |
+
mask_overlay_colored = np.zeros((mask_overlay.shape[0], mask_overlay.shape[1], 3), dtype=np.uint8)
|
88 |
+
mask_overlay_colored[mask_overlay > 200] = [255, 0, 0] # Red
|
89 |
+
mask_overlay_colored[(mask_overlay > 100) & (mask_overlay <= 200)] = [0, 255, 0] # Green
|
90 |
+
mask_overlay_colored[mask_overlay <= 100] = [0, 0, 255] # Blue
|
91 |
+
mask_overlay_colored = cv2.resize(mask_overlay_colored, (224, 224))
|
92 |
+
blended_image = cv2.addWeighted(combined_img_resized.astype(np.uint8), 0.6, mask_overlay_colored, 0.4, 0)
|
93 |
+
segmented_image = Image.fromarray(cv2.cvtColor(blended_image, cv2.COLOR_BGR2RGB))
|
94 |
+
img_byte_arr = io.BytesIO()
|
95 |
+
segmented_image.save(img_byte_arr, format='PNG')
|
96 |
+
img_byte_arr.seek(0)
|
97 |
+
length_cm, breadth_cm, depth_cm, area_cm2 = calculate_wound_dimensions(predicted_mask)
|
98 |
+
moisture = calculate_moisture_and_texture(combined_img)
|
99 |
+
response = Response(img_byte_arr.getvalue(), media_type='image/png')
|
100 |
+
response.headers['X-Length-Cm'] = str(length_cm)
|
101 |
+
response.headers['X-Breadth-Cm'] = str(breadth_cm)
|
102 |
+
response.headers['X-Depth-Cm'] = str(depth_cm)
|
103 |
+
response.headers['X-Area-Cm2'] = str(area_cm2)
|
104 |
+
response.headers['X-Moisture'] = str(moisture)
|
105 |
+
return response
|
106 |
+
return {'error': 'Invalid file format'}
|
107 |
+
|
108 |
+
if __name__ == '__main__':
|
109 |
+
import uvicorn
|
110 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
fastapi
|
2 |
+
uvicorn
|
3 |
+
starlette
|
4 |
+
anyio
|
5 |
+
aiofiles
|
6 |
+
python-multipart
|
7 |
+
pydantic
|
8 |
+
numpy
|
9 |
+
opencv-python
|
10 |
+
tensorflow
|