File size: 5,399 Bytes
4c7e762
5a16251
4c7e762
 
 
 
5a16251
f9c687f
5a16251
4c7e762
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d71b96
5a16251
4c7e762
 
 
d00e3b8
 
 
 
 
 
 
4c7e762
 
 
 
 
5a16251
4c7e762
 
 
 
 
5a16251
 
 
 
67e7167
 
 
745c768
67e7167
 
 
 
 
 
 
 
 
 
 
2b8c717
2ea67b5
3ed1d32
e450495
a8e25be
 
67e7167
 
 
 
 
1bd1e60
 
67e7167
 
 
 
 
 
 
 
 
 
 
 
 
 
aec0b85
67e7167
 
1bd1e60
67e7167
 
 
 
 
d00e3b8
 
 
 
 
 
 
67e7167
 
 
 
 
 
1bd1e60
 
aec0b85
 
1bd1e60
 
 
 
 
67e7167
 
 
 
 
1bd1e60
 
67e7167
 
 
 
 
 
 
 
1bd1e60
67e7167
 
 
 
 
 
 
 
 
 
 
 
 
1bd1e60
 
 
67e7167
1bd1e60
 
 
 
 
 
 
67e7167
1bd1e60
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from fastapi import FastAPI, UploadFile, status
from fastapi.responses import JSONResponse
from ultralytics import YOLO
import cv2
import numpy as np
from fastapi.middleware.cors import CORSMiddleware
import base64
import math

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["POST"],
    allow_headers=["content-type", "accept"],
)

model = YOLO("best.pt")

@app.post("/inference")
async def inference(file: UploadFile):
    if file.content_type != "image/jpeg" and file.content_type != "image/png" and file.content_type != "image/jpg":
        return JSONResponse(
            status_code=status.HTTP_400_BAD_REQUEST,
            content={"error": "Invalid file format"}
        )
    image_bytes = await file.read()
    img = np.frombuffer(image_bytes, dtype=np.uint8)
    img = cv2.imdecode(img, cv2.IMREAD_COLOR)
    results = model.predict(source=img, conf=0.3)
    detections = []
    for r in results: 
        boxes = r.boxes
    for box in boxes: 
        label = box.cls[0].item()  
        label_name = model.names[label]  
        
        # Skip NoTumor class - don't draw bounding box
        if label_name.lower() == "notumor":
            continue
        
        x1, y1, x2, y2 = box.xyxy[0]
        x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
    
        cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 255), 2)

        detections.append(label_name)
        (text_width, text_height), baseline = cv2.getTextSize(label_name, cv2.FONT_HERSHEY_SIMPLEX, 0.9, 2)
        cv2.rectangle(img, (x1, y1 - text_height - baseline - 5), (x1 + text_width, y1), (255, 0, 255), -1)
        cv2.putText(img, label_name, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 0), 2)
    
    resp_img_bytes = cv2.imencode('.jpg', img)[1].tobytes()
    img_base64 = base64.b64encode(resp_img_bytes).decode('utf-8')
    return JSONResponse({
        "image": img_base64,
        "detections": detections
    })


AVERAGE_TUMOR_VOLUME = 5751.46 

def calculate_sphere_volume(width, height):
    """
    Menghitung volume tumor menggunakan rumus bola
    Volume = (4/3) * π * r³
    Diameter = rata-rata dari width dan height
    """
    try:
        diameter = (width + height) / 2
        radius = diameter / 2
        volume = (4/3) * math.pi * (radius ** 3)
        result = round(volume, 2) 
        result = result * 0.00757132152125087872521790538421
        if result < 523: result = 523
        return result
    except Exception as e:
        print(f"Terjadi error: {e}")
        return None

@app.post("/inference_volume")
async def inference_volume(file: UploadFile):
    """
    Endpoint sederhana untuk deteksi tumor dengan volume
    Return: JSON dengan volume_mm3, class, dan image_bytes
    """
    if file.content_type not in ["image/jpeg", "image/png", "image/jpg"]:
        return JSONResponse(
            status_code=status.HTTP_400_BAD_REQUEST,
            content={"error": "Invalid file format"}
        )
    
    image_bytes = await file.read()
    img = np.frombuffer(image_bytes, dtype=np.uint8)
    img = cv2.imdecode(img, cv2.IMREAD_COLOR)
    
    results = model.predict(
        source=img, 
        conf=0.5,
        iou=0.2
    )
    
    detections = []
    
    for r in results: 
        boxes = r.boxes
        
        for box in boxes: 
            label = int(box.cls[0].item())
            label_name = model.names[label]
            
            # Skip NoTumor class - don't draw bounding box or include in detections
            if label_name.lower() == "notumor":
                continue
            
            x1, y1, x2, y2 = box.xyxy[0]
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            
            width = x2 - x1
            height = y2 - y1
            
            # Hitung volume
            volume_mm3 = calculate_sphere_volume(width, height)
            print("VOLUME")
            print(volume_mm3)
            
            detections.append({
                "class": label_name,
                "volume_mm3": volume_mm3
            })
            
            # Draw bounding box
            cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 255), 2)
            
            # Label dengan volume
            text = f"{label_name}"
            vol_text = f"{volume_mm3} mm3"
            
            (text_width, text_height), baseline = cv2.getTextSize(
                text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2
            )
            
            cv2.rectangle(
                img, 
                (x1, y1 - text_height - baseline - 25), 
                (x1 + max(text_width, 100), y1), 
                (255, 0, 255), 
                -1
            )
            
            cv2.putText(
                img, text, (x1, y1 - 20), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2
            )
            cv2.putText(
                img, vol_text, (x1, y1 - 5), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1
            )
    
    # Encode image ke bytes
    _, buffer = cv2.imencode('.jpg', img)
    img_bytes = buffer.tobytes()
    
    # Convert ke base64 untuk JSON
    img_base64 = base64.b64encode(img_bytes).decode('utf-8')
    
    response = {
        "detections": detections,
        "image_bytes": img_base64
    }
    
    return JSONResponse(content=response)