Newer
Older
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import cv2
import random
import os
from PIL import Image
import csv
AVERAGE_SIZE = (32, 32) # Thanks to the stats, we know that size of bbox will be (127, 145) -> Average size of labels
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
# Dictionary for mapping class names to integers
CLASSE_TO_INT = {
"danger": 0,
"interdiction": 1,
"obligation": 2,
"stop": 3,
"ceder": 4,
"frouge": 5,
"forange": 6,
"fvert": 7,
"ff": 8,
"empty": 9
}
# Dictionary for mapping integers to class names
INT_TO_CLASSE = {
0: "danger",
1: "interdiction",
2: "obligation",
3: "stop",
4: "ceder",
5: "frouge",
6: "forange",
7: "fvert",
8: "ff",
9: "empty"
}
# Data labels key
CLASSES = ["danger", "interdiction", "obligation", "stop", "ceder", "frouge", "forange", "fvert", "ff", "empty"]
# Number of classes
NB_CLASSES = len(CLASSES)
def load_dataset(image_dir, label_dir):
# Initialize empty lists to store images (X) and labels (Y)
for label_file in os.listdir(label_dir):
label_path = os.path.join(label_dir, label_file)
file_name = int(label_file.split('.')[0]) # Extract the file name to find corresponding image file
image_path = os.path.join(image_dir, str(file_name).zfill(4) + ".jpg")
image = Image.open(image_path).convert("RGB") # Open the image
# Read bounding boxes from the label file
with open(label_path, "r") as file:
reader = csv.reader(file)
bboxes = list(reader)
# Check if there are any bounding boxes in the label file
# Iterate over each bounding box
# Convert class label from string to integer using CLASSE_TO_INT dictionary
# Convert all elements in the bounding box to integers
# Extract Region of Interest (ROI) from the image based on the bounding box
roi = image.crop((box[0], box[1], box[2], box[3]))
# Resize the ROI to a predefined average size
roi_resized = roi.resize(AVERAGE_SIZE)
# Append the resized ROI to X and its corresponding class label to Y
X.append(np.array(roi_resized))
Y.append(box[4])
# If no bounding boxes are present, generate empty bounding boxes
for _ in range(3):
box = list(generate_empty_bbox(image_width=image.size[1], image_height=image.size[0]))
# Extract ROI from image based on empty bounding box
roi = image.crop((box[0], box[1], box[2], box[3]))
# Resize the ROI to a predefined average size
roi_resized = roi.resize(AVERAGE_SIZE)
# Append the resized ROI to X and the class label for empty to Y
X.append(np.array(roi_resized))
Y.append(CLASSE_TO_INT["empty"])
except FileNotFoundError:
print(f"Image file not found for {file_name}")
except Exception as e:
print(f"Error when processing index {file_name}: {e}")
# Convert the lists X and Y to numpy arrays and return them
return np.array(X), np.array(Y)
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
# Function to calculate Intersection over Union (IoU)
def iou(box1, box2):
"""
Calcule l'Intersection over Union (IoU) entre deux boîtes englobantes.
Parameters:
box1 (tuple): Une boîte englobante sous la forme (x1, y1, x2, y2) où (x1, y1) est le coin supérieur gauche et (x2, y2) est le coin inférieur droit.
box2 (tuple): Une deuxième boîte englobante sous la même forme (x1, y1, x2, y2).
Returns:
float: La valeur IoU entre les deux boîtes englobantes.
"""
# Coordonnées des coins des boîtes ([Axe][corner_idx]_[boxe_idx])
x0_1, y0_1, x1_1, y1_1 = box1
x0_2, y0_2, x1_2, y1_2 = box2
# Calcul des coordonnées de l'intersection
x1_inter = max(x0_1, x0_2)
y1_inter = max(y0_1, y0_2)
x2_inter = min(x1_1, x1_2)
y2_inter = min(y1_1, y1_2)
# Calcul de l'aire de l'intersection
inter_area = max(0, x2_inter - x1_inter) * max(0, y2_inter - y1_inter)
# Calcul de l'aire des deux boîtes
box1_area = (x1_1 - x0_1) * (y1_1 - y0_1)
box2_area = (x1_2 - x0_2) * (y1_2 - y0_2)
# Calcul de l'aire de l'union
union_area = box1_area + box2_area - inter_area
# Calcul de l'IoU
iou = inter_area / union_area if union_area > 0 else 0
return iou
# Function to calculate Non Maximum Suppression (NMS)
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
def non_maximum_suppression(bboxes, threshold=0.5):
"""
Apply non-maximum suppression to filter overlapping bounding boxes
:param bboxes: List of proposed bounding boxes with their scores
:param threshold: IoU threshold for suppression
:return: List of final bounding boxes
"""
if len(bboxes) == 0:
return []
# Extract the coordinates and scores
x1 = torch.tensor([bbox[0] for bbox in bboxes])
y1 = torch.tensor([bbox[1] for bbox in bboxes])
x2 = torch.tensor([bbox[2] for bbox in bboxes])
y2 = torch.tensor([bbox[3] for bbox in bboxes])
scores = torch.tensor([bbox[4] for bbox in bboxes])
# Compute the area of the bounding boxes and sort by score
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
_, order = scores.sort(0, descending=True)
keep = []
while order.numel() > 0:
i = order[0]
keep.append(i.item())
if order.numel() == 1:
break
xx1 = x1[order[1:]].clamp(min=x1[i])
yy1 = y1[order[1:]].clamp(min=y1[i])
xx2 = x2[order[1:]].clamp(max=x2[i])
yy2 = y2[order[1:]].clamp(max=y2[i])
w = (xx2 - xx1 + 1).clamp(min=0)
h = (yy2 - yy1 + 1).clamp(min=0)
inter = w * h
ovr = inter / (areas[i] + areas[order[1:]] - inter)
# Keep only elements with an overlap less than the threshold
inds = (ovr <= threshold).nonzero(as_tuple=False).squeeze()
order = order[inds + 1]
final_bboxes = [bboxes[idx] for idx in keep]
return final_bboxes
# Function to plot images with bounding boxes and class labels
def plot_bbox_image(image, boxes):
# Getting the color map from matplotlib
colour_map = plt.get_cmap("tab20b")
# Getting different colors from the color map for 20 different classes
colors = [colour_map(i) for i in np.linspace(0, 1, NB_CLASSES)]
# Getting the height and width of the image
h, w, _ = image.shape
# Create figure and axes
fig, ax = plt.subplots(1)
# Add image to plot
ax.imshow(image)
# Plotting the bounding boxes and labels over the image
for box in boxes:
# Get the class from the box
try:
class_pred = box[4]
except:
class_pred=1 # No classe (maybe because of selective search) set at 1 randomly
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
if class_pred != CLASSE_TO_INT["empty"]:
x = box[0]
y = box[1]
width = box[2] - x
height = box[3] - y
# Create a Rectangle patch with the bounding box
rect = patches.Rectangle(
(x, y), width, height,
linewidth=2,
edgecolor=colors[int(class_pred)],
facecolor="none",
)
# Add the patch to the Axes
ax.add_patch(rect)
# Add class name to the patch
plt.text(
x,
y,
s=INT_TO_CLASSE[int(class_pred)],
color="white",
verticalalignment="top",
bbox={"color": colors[int(class_pred)], "pad": 0},
)
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# Display the plot
plt.show()
def selective_search(image, visualize=False, visulize_count=100):
# Convert image to BGR format for OpenCV
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# Initialiser la recherche sélective
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
ss.setBaseImage(image)
# Utiliser la recherche sélective en mode rapide (ou en mode qualité)
ss.switchToSelectiveSearchFast() # Pour la recherche rapide
# ss.switchToSelectiveSearchQuality() # Pour une recherche plus précise
# Obtenir les régions candidates
roi = ss.process()
if visualize:
# Dessiner les régions candidates sur l'image
for (x, y, w, h) in roi[:visulize_count]: # Limiter à 100 régions pour la visualisation
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
# Afficher l'image avec les régions candidates
plt.figure(figsize=(10, 10))
plt.imshow(image)
plt.axis('off')
plt.show()
return roi
# Generate an empty box for images without label
def generate_empty_bbox(image_width, image_height):
# Generating random coords for the bbox
x_min = random.randint(0, image_width - AVERAGE_SIZE[0])
y_min = random.randint(0, image_height - AVERAGE_SIZE[1])
# Compute complete coords of the bbox
x_max = x_min + AVERAGE_SIZE[0]
y_max = y_min + AVERAGE_SIZE[1]
return (x_min, y_min, x_max, y_max)