Skip to content
Snippets Groups Projects
Commit 9fa881ac authored by Victor Demessance's avatar Victor Demessance
Browse files

[+] Add NMS process & saving predicted label process

parent 1fc7d520
No related branches found
No related tags found
No related merge requests found
Showing
with 183 additions and 109 deletions
No preview for this file type
No preview for this file type
File added
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
......@@ -4,5 +4,7 @@ TRAINING_IMAGE_FOLDER_PATH = "./data/train/images/"
TRAINING_LABEL_FOLDER_PATH = "./data/train/labels/"
VAL_IMAGE_FOLDER_PATH = "./data/val/images/"
VAL_LABEL_FOLDER_PATH = "./data/val/labels/"
PREDICTION_LABEL_FOLDER_PATH = "./data/train/predicted_labels/"
CLASSIFIERS_FOLDER_PATH = "./supervised_learning/classifiers/saves/"
WINDOW_SIZES = [(64, 64), (128, 128), (256, 256),(512,512)] # Window sizes during slidding window process
\ No newline at end of file
WINDOW_SIZES = [(64, 64), (128, 128), (256, 256),(512,512)] # Window sizes during slidding window process
STEP_SIZE = 16
\ No newline at end of file
......@@ -25,98 +25,43 @@ for classe in classifiers.keys():
# ------------- LOAD DATAS -----------------
X = []
X = {}
print("Loading validation datas...")
for filename in os.listdir(VAL_IMAGE_FOLDER_PATH):
filepath = os.path.join(VAL_IMAGE_FOLDER_PATH, filename)
image = Image.open(filepath)
X.append(image)
X[filepath] = image
# ------------- DETECTION -----------------
# Parse image with the slidding window and classify to detect labels
def sliding_window(image, step_size, window_size):
# Slide a window across the image
for y in range(0, image.shape[0] - window_size[1], step_size):
for x in range(0, image.shape[1] - window_size[0], step_size):
yield (x, y, image[y:y + window_size[1], x:x + window_size[0]])
# Check if predicted_label folder exists
if not os.path.exists(PREDICTION_LABEL_FOLDER_PATH):
# Create folder if doesn't exist
os.makedirs(PREDICTION_LABEL_FOLDER_PATH)
print(f"Folder '{PREDICTION_LABEL_FOLDER_PATH}' created with success.")
def pyramid(image, scale=1.5, min_size=(30, 30)):
# Yield the original image
yield image
# Keep looping over the pyramid
while True:
# Compute the new dimensions of the image and resize it
w = int(image.shape[1] / scale)
h = int(image.shape[0] / scale)
image = cv2.resize(image, (w, h))
# If the resized image does not meet the supplied minimum size, then stop constructing the pyramid
if image.shape[0] < min_size[1] or image.shape[1] < min_size[0]:
break
# Yield the next image in the pyramid
yield image
for filepath, image in X.items():
image = np.array(image)
name = filepath.split('/')[-1].split(".")[0]
step_size = 16
# Start detection process
print(f"[DETECTION] Processing image {name}")
# ------------- DETECTION -----------------
# Parse image with the slidding window and classify to detect labels
for image in X:
image = np.array(image)
# Loop over the image pyramid
for resized in pyramid(image):
# Loop over the sliding window for each layer of the pyramid
for (x, y, window) in sliding_window(resized, step_size=step_size, window_size=AVERAGE_SIZE):
# If the window does not meet our desired window size, ignore it
if window.shape[0] != AVERAGE_SIZE[1] or window.shape[1] != AVERAGE_SIZE[0]:
continue
# HOG features
hog_features = np.array(hog(rgb2gray(window), pixels_per_cell=(16, 16), cells_per_block=(2, 2), block_norm='L2-Hys')).flatten()
# HUE features
color_features = np.histogram(rgb2hsv(window)[:,:,0], bins=10, range=(0, 1), density=True)[0]
# Concatenate ROI features
roi_features = np.concatenate((hog_features, color_features)).reshape(1, -1)
probas = {
"danger" : None,
"interdiction": None,
"obligation": None,
"stop": None,
"ceder": None,
"frouge": None,
"forange": None,
"fvert": None
}
# Extract rois from images with dynamic slidding window process
rois = extract_rois_from_image(image, classifiers)
for classe, classifier in classifiers.items():
proba = classifier.predict_proba(roi_features)[0][1]
if proba > 0.6:
probas[classe] = proba
else:
probas[classe] = 0
max_proba = 0
max_classe = "empty"
for classe, proba in probas.items():
if proba > max_proba:
max_proba = proba
max_classe = classe
if max_classe not in ["empty", "frouge", "fvert", "forange"]:
plt.imshow(window)
plt.show()
print(max_classe)
# Filter rois with Non Maximum Suppression process
rois = non_max_suppression(rois, iou_threshold=0.1)
#display_rois(image, rois) -- UNCOMMENT TO DISPLAY
# TO DO : NMS + FAUX NEGATIS TRAINING
# Write preticted labels into prediction files
prediction_file_path = os.path.join(PREDICTION_LABEL_FOLDER_PATH, f"{name}.csv")
with open(prediction_file_path, "w") as f:
for roi in rois:
x0, y0, x1, y1, classe, score = roi
row = f"{x0},{y0},{x1},{y1},{classe},{score}\n"
f.write(row)
# RAPPEL :
# STOP : BON
# OBLIGATION : OK
# DANGER : NUL
# FEUX : NUL
# INTERDICTION : CONFOND AVEC STOP (MAIS PAS DEGEUX NON PLUS)
# CEDER : PAS FOU CONFOND AVEC INTERDICTION ET ARBRES PARFOIS
\ No newline at end of file
......@@ -76,36 +76,163 @@ def create_binary_classification_dataset(datas, key):
return np.array(X), np.array(Y)
# Function to extract all regions of interest from an image
def extract_rois_from_image(image, classifiers_dict):
rois = []
# Loop over the image pyramid
for resized, scale in pyramid(image):
# Loop over the sliding window for each layer of the pyramid
for (x, y, window) in sliding_window(resized, step_size=STEP_SIZE, window_size=AVERAGE_SIZE):
# If the window does not meet our desired window size, ignore it
if window.shape[0] != AVERAGE_SIZE[1] or window.shape[1] != AVERAGE_SIZE[0]:
continue
# HOG features
hog_features = np.array(hog(rgb2gray(window), pixels_per_cell=(16, 16), cells_per_block=(2, 2), block_norm='L2-Hys')).flatten()
# HUE features
color_features = np.histogram(rgb2hsv(window)[:,:,0], bins=10, range=(0, 1), density=True)[0]
# Concatenate ROI features
roi_features = np.concatenate((hog_features, color_features)).reshape(1, -1)
probas = {
"danger" : None,
"interdiction": None,
"obligation": None,
"stop": None,
"ceder": None,
"frouge": None,
"forange": None,
"fvert": None
}
for classe, classifier in classifiers_dict.items():
proba = classifier.predict_proba(roi_features)[0][1]
if proba > 0.7:
probas[classe] = proba
else:
probas[classe] = 0
max_proba = 0
max_classe = "empty"
for classe, proba in probas.items():
if proba > max_proba:
max_proba = proba
max_classe = classe
if max_classe not in ["empty", "frouge", "fvert", "forange"]:
x0 = int(x * scale)
y0 = int(y * scale)
x1 = int((x + AVERAGE_SIZE[0]) * scale)
y1 = int((y + AVERAGE_SIZE[1]) * scale)
rois.append([x0, y0, x1, y1, max_classe, max_proba])
return rois
# Function to compute a slidding window process
def sliding_window(image, step_size, window_size):
def sliding_window(image, step_size=STEP_SIZE, window_size=AVERAGE_SIZE):
# Slide a window across the image
for y in range(0, image.shape[0] - window_size[1], step_size):
for x in range(0, image.shape[1] - window_size[0], step_size):
yield (x, y, image[y:y + window_size[1], x:x + window_size[0]])
# Function to change scale of the image to compute dynamic slidding window process
def pyramid(image, scale=1.5, min_size=(30, 30)):
# Yield the original image
yield image, 1
current_scale = 1
# Keep looping over the pyramid
while True:
# Compute the new dimensions of the image and resize it
w = int(image.shape[1] / scale)
h = int(image.shape[0] / scale)
image = cv2.resize(image, (w, h))
# Update the current scale
current_scale *= scale
# If the resized image does not meet the supplied minimum size, then stop constructing the pyramid
if image.shape[0] < min_size[1] or image.shape[1] < min_size[0]:
break
# Yield the next image in the pyramid
yield image, current_scale
# Function to compute Non Maximum Suppression process (NMS)
def non_max_suppression(boxes, overlap_thresh=0.3):
if len(boxes) == 0:
def non_max_suppression(rois, iou_threshold=0.1):
"""
Apply Non-Maximum Suppression to avoid multiple detections of the same object.
Parameters:
- rois: List of ROIs where each ROI is a list [x0, y0, x1, y1, classe, score]
- iou_threshold: Threshold for Intersection over Union (IoU) to suppress overlapping boxes
Returns:
- List of ROIs after NMS
"""
if len(rois) == 0:
return []
boxes = np.array(boxes)
if boxes.dtype.kind == "i":
boxes = boxes.astype("float")
pick = []
x1 = boxes[:,0]
y1 = boxes[:,1]
x2 = boxes[:,2]
y2 = boxes[:,3]
area = (x2 - x1 + 1) * (y2 - y1 + 1)
idxs = np.argsort(y2)
while len(idxs) > 0:
last = len(idxs) - 1
i = idxs[last]
pick.append(i)
xx1 = np.maximum(x1[i], x1[idxs[:last]])
yy1 = np.maximum(y1[i], y1[idxs[:last]])
xx2 = np.minimum(x2[i], x2[idxs[:last]])
yy2 = np.minimum(y2[i], y2[idxs[:last]])
w = np.maximum(0, xx2 - xx1 + 1)
h = np.maximum(0, yy2 - yy1 + 1)
overlap = (w * h) / area[idxs[:last]]
idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlap_thresh)[0])))
return boxes[pick].astype("int")
\ No newline at end of file
rois = np.array(rois)
# Coordinates of bounding boxes
x0 = rois[:, 0].astype(int)
y0 = rois[:, 1].astype(int)
x1 = rois[:, 2].astype(int)
y1 = rois[:, 3].astype(int)
scores = rois[:, 5].astype(float)
# Compute the area of the bounding boxes and sort the bounding boxes by the score
areas = (x1 - x0 + 1) * (y1 - y0 + 1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
# Compute the intersection area
xx0 = np.maximum(x0[i], x0[order[1:]])
yy0 = np.maximum(y0[i], y0[order[1:]])
xx1 = np.minimum(x1[i], x1[order[1:]])
yy1 = np.minimum(y1[i], y1[order[1:]])
w = np.maximum(0, xx1 - xx0 + 1)
h = np.maximum(0, yy1 - yy0 + 1)
intersection = w * h
# Compute the IoU
iou = intersection / (areas[i] + areas[order[1:]] - intersection)
# Keep only the boxes with IoU less than the threshold
inds = np.where(iou <= iou_threshold)[0]
order = order[inds + 1]
return rois[keep].tolist()
# Function that allows to display multiples rois on an image
def display_rois(image, rois):
# Convert the image to RGB (from BGR, which is the format used by cv2)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Draw rectangles around the ROIs
for roi in rois:
x0 = int(roi[0])
y0 = int(roi[1])
x1 = int(roi[2])
y1 = int(roi[3])
classe = str(roi[4])
proba = float(roi[5])
cv2.rectangle(image_rgb, (x0, y0), (x1, y1), (0, 255, 0), 2)
label = f"{classe}: {proba:.2f}"
cv2.putText(image_rgb, label, (x0, y0 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# Display the image with the ROIs
plt.figure(figsize=(12, 8))
plt.imshow(image_rgb)
plt.axis('off')
plt.show()
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment