File size: 8,366 Bytes
2d78c61
 
 
 
 
e9a3881
2d78c61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
from roboflow import Roboflow
import cv2
import json
import numpy as np
import gradio as gr
from sklearn.linear_model import LinearRegression

rf = Roboflow(api_key="5uMN18FScJQlsWbgIwrP")
project = rf.workspace().project("soil-phosphorus-analysis")
model = project.version(1).model

# Define the formulas
def formula_1(r, g, b):
    return r / (r + g + b)

def formula_2(r, g, b):
    return (r - g) / (r + g + b)

def formula_3(r, g, b):
    return (r - b) / (r + g + b)

def formula_4(r, g, b):
    return (r - b) / (r + b)

def formula_5(r, g, b):
    return (r - g) / (r + g)

def formula_6(r, g, b):
    return b / (r + g + b)

def formula_7(r, g, b):
    return (2*b) - g - r

def formula_8(r, g, b):
    return (2*r) - g - b 

formulas = {
    "R / (R + G + B)": formula_1,
    "(R - G) / (R + G + B)": formula_2,
    "(R - B) / (R + G + B)": formula_3,
    "(R - B) / (R + B)": formula_4,
    "(R - G) / (R + G)": formula_5,
    "B / (R + G + B)": formula_6,
    "(2*B) - G - R": formula_7,
    "(2*R) - G - B": formula_8
}

phosphorus_values = [0, 5, 10, 15, 20, 25]

def get_roboflow_predictions(image):
    # Save the image to a temporary file
    temp_image_path = "temp_image.jpg"
    cv2.imwrite(temp_image_path, image)

    # Get predictions from the Roboflow model
    predictions = model.predict(temp_image_path, confidence=45, overlap=30).json()

    return predictions

def get_flasks(pred):
    json_data = pred

    # Filter out only the flasks
    flasks = [obj for obj in json_data['predictions'] if obj['class'] == 'flask']

    # Sort flasks by 'y' in descending order
    flasks_sorted_by_y = sorted(flasks, key=lambda obj: -obj['y'])

    # Group flasks into rows of 6 each (considering the last row might have fewer than 6)
    rows = []
    current_row = []
    row_limit = 6
    for flask in flasks_sorted_by_y:
        current_row.append(flask)
        if len(current_row) == row_limit:
            rows.append(current_row)
            current_row = []
    if current_row:  # Add the last row if it's not empty
        rows.append(current_row)

    # Now sort each row by 'x' in ascending order
    rows_sorted_by_x = [sorted(row, key=lambda obj: -obj['x']) for row in rows]

    # Flatten the list of rows back into a single list and add the order
    flasks_ordered = []
    order = 1
    for row in rows_sorted_by_x:
        for flask in row:
            flask['order'] = order
            flasks_ordered.append(flask)
            order += 1

    # Output the sorted flasks in the desired format
    formatted_flasks = [
        {"order": flask['order'], "x": flask['x'], "y": flask['y'], "width": flask["width"],
        "height": flask["height"], "detection_id": flask['detection_id'], "class": "flask"}
        for flask in flasks_ordered
    ]

    # Output the formatted flasks
    flasks = formatted_flasks
    return flasks


def find_rgb_values(image, flasks):
    flask_rgbs = []
    
    for i, prediction in enumerate(flasks):
        if prediction.get("class") == "flask":
            try:
                # Calculate the center point of the flask using width and height
                center_x = int(prediction["x"])
                center_y = int(prediction["y"])

                width = int(prediction["width"])
                height = int(prediction["height"])
                
                # Define three points around the center
                points = [
                    (center_x, center_y - int(height / 8)),
                    (center_x, center_y ),
                    (center_x, center_y + int(height / 8))
                ]

                # Extract RGB values from the three points
                rgb_values = []
                for point in points:
                    x, y = point
                    if 0 <= y < image.shape[0] and 0 <= x < image.shape[1]:
                        rgb_value = image[y, x]
                        rgb_values.append(rgb_value)
                    else:
                        raise IndexError("Point is out of image bounds")

                # Calculate the average RGB values
                avg_rgb = np.mean(rgb_values, axis=0)
                
                flask_rgbs.append(avg_rgb)
                
                # Draw the bounding box and points on the image
                top_left = (center_x - width // 2, center_y - height // 2)
                bottom_right = (center_x + width // 2, center_y + height // 2)
                cv2.rectangle(image, top_left, bottom_right, (0, 255, 0), 2)  # Green bounding box

                for point in points:
                    cv2.circle(image, point, 5, (0, 0, 255), -1)  # Red points
                
            except IndexError:
                # Skip processing faulty coordinates
                print(f"Ignore flask {i+1} due to faulty coordinates.")
    
    return flask_rgbs

def predict(image, formula_choice):
    pred = get_roboflow_predictions(image)
    flasks = get_flasks(pred)

    image_path = "temp_image.jpg"
    cv2.imwrite(image_path, image)
    
    image = cv2.imread(image_path)

    # Check if the image is loaded properly
    if image is None:
        raise ValueError(f"Image at path {image_path} could not be loaded.")

    # Get original dimensions
    original_height, original_width = image.shape[:2]

    # Define new dimensions
    new_width = 600
    new_height = 800

    # Calculate scale factors
    scale_x = new_width / original_width
    scale_y = new_height / original_height

    # Resize the image
    image = cv2.resize(image, (new_width, new_height))

    # Rescale predictions
    rescaled_predictions = []
    for prediction in flasks:
        rescaled_prediction = prediction.copy()
        rescaled_prediction["x"] = int(prediction["x"] * scale_x)
        rescaled_prediction["y"] = int(prediction["y"] * scale_y)
        rescaled_prediction["width"] = int(prediction["width"] * scale_x)
        rescaled_prediction["height"] = int(prediction["height"] * scale_y)
        rescaled_predictions.append(rescaled_prediction)

    
    # Find RGB values from three points in the center of each flask
    flask_rgbs = find_rgb_values(image, rescaled_predictions)

    flask_outputs = []
    for i, rgb in enumerate(flask_rgbs):
        flask_outputs.append({"flask_number": i + 1, "rgb_values": rgb})

    # Get the chosen formula
    chosen_formula = formulas.get(formula_choice)

    if not chosen_formula:
        raise ValueError("Invalid formula choice.")
    
    # Apply the chosen formula to each flask
    results = []
    for flask in flask_outputs:
        r, g, b = flask["rgb_values"]
        result = chosen_formula(r, g, b)
        results.append({"flask_number": flask["flask_number"], "rgb_value": result})

    # Separate the first 6 flasks for training
    training_flasks = results[:6]
    training_rgb_values = [flask["rgb_value"] for flask in training_flasks]
    
    # Create the linear regression model
    model = LinearRegression()
    X_train = np.array(training_rgb_values).reshape(-1, 1)
    y_train = np.array(phosphorus_values)
    model.fit(X_train, y_train)

    # Apply the model to the remaining flasks
    remaining_flasks = results[6:]
    X_test = np.array([flask["rgb_value"] for flask in remaining_flasks]).reshape(-1, 1)
    predicted_phosphorus = model.predict(X_test)

    # Combine the results
    output_data = []
    for flask in training_flasks:
        output_data.append([
            flask["flask_number"],
            flask["rgb_value"],
            phosphorus_values[flask["flask_number"] - 1]
        ])

    for i, flask in enumerate(remaining_flasks):
        output_data.append([
            flask["flask_number"],
            flask["rgb_value"],
            predicted_phosphorus[i]
        ])

    return image, output_data

iface = gr.Interface(
    fn=predict,
    inputs=[
        gr.Image(type="numpy", label="Upload an image"),
        gr.Dropdown(choices=list(formulas.keys()), label="Choose a formula"),
    ],
    outputs=[
        gr.Image(type="numpy", label="Predicted Image"),
        gr.Dataframe(headers=["Flask Number", "Regressions", "Phosphorus"], label="Predictions"),
    ],
    title="Digital Spectrophotometer",
    description="Upload an image to detect flasks and calculate the Phosphorus Value for each flask.",
)

# Add a button for exporting CSV
iface.launch(share=True)