Face detection con scikit-learn
Versione integrata nel sito senza iframe. Il notebook ricostruito resta disponibile come file .ipynb.
Gabriele Iocco
Sistema di Face Detection per una fotocamera digitale con Scikit-Learn
Questo modello deve essere in grado, dopo aver ricevuto un'immagine in ingresso, di rilevare la presenza di un volto umano. Poiché il sistema su cui girerà dispone di poche risorse di calcolo, ho deciso di fissare il peso massimo di ogni immagine a 1 MB. Ho scelto questo valore rifacendomi alle prime macchine digitali economiche dei primi anni 2000, che generavano file con dimensioni comprese tra poche centinaia di kilobyte e un massimo di 1 MB.
Creazione del dataset
All'inizio avevo creato un dataset bilanciato di 6000 immagini, 3000 per la classe contenente volti e 3000 per la classe non contenente volti di cui 1000 immagini (paesaggio) erano mie, le altre 5000 le avevo scaricate dal sito https://unsplash.com/it
Le immagini prese da "unsplash.com" le ho caricate direttamente nella cartella di progetto in quanto avevano già un peso molto ridotto.
Le mie immagini essendo in formato .png e alla massima qualità invece le ho convertite in .jpg e ho diminuito la risoluzione.
Entrambi i due set di immagini sono stati rinominati per avere maggiore chiarezza.
Link alle cartelle che contengono le immaginihttps://drive.google.com/drive/folders/1M_CLph91T6W2tbwpuCyyIe4joEgBwLMh?usp=drive_link
Inizialmente, ho addestrato modelli usando la regressione logistica su dataset con centinaia o migliaia di immagini, ma l'accuratezza era sempre bassa, tra il 53% e il 60%. Sono quindi passato al modello Random Forest ottenendo un'accuratezza del 79,8% su specifiche immagini utilizzando Google Colab per gestire l'elevata richiesta di memoria ram sebbene questo abbia richiesto di ridurre la dimensione del dataset.
Durante i test eseguivo l'addestramento su Colab e poi passavo le immagini al modello su Jupyter, poiché riscontravo problemi nell'elaborazione diretta su Colab.
Più avanti una parte del progetto l'ho caricato sottoforma di file html per questo motivo.
Conversione delle immagini jpg in formato PPM
Ho pensato di convertire le immagini nel formato PPM in modo da memorizzare l’immagine in modo strutturato e optando per il formato ASCII il quale è facilmente interpretabile.
Con i file .PPM ottenuti ho calcolato il rapporto tra i canali R/G per la rilevazione delle zone di pelle presenti nelle immagini rifacendomi a questa tesi di laurea presente al seguente indirizzo http://www.cristinasegalin.com/research/ee/BachelorThesis.pdf dove viene discusso il lavoro di due ricercatori Brand e Mason i quali pubblicarono i loro studi su J. Brand, J. S. Mason. A Comparative Assessment of Three Approaches to Pixel-level Human Skin-Detection. Proc. Intl. Conf. Pattern Recognition, Pag. 1056-1059, 2000
Ho integrato anche l'apporto del canale B sulle zone di pelle calcolando il B_value come R/3 prendendo spunto da https://francismarionphoto.wordpress.com/wp-content/uploads/2012/01/art218_psexercise_skintones.pdf e https://digital-photography-school.com/skin-tones-using-lightrooms-color-curves/ e i valori di H(tinta), S(saturazione) e V(luminosità)
Codice
import os
import threading
import time
from tqdm import tqdm
import sys
import math
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from joblib import load
import seaborn as sns
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn import svm
from sklearn.metrics import classification_report, accuracy_score
from sklearn.utils import resample
import tkinter as tk
from tkinter import filedialog
from matplotlib.patches import Rectangle
Codice
from IPython.display import HTML
with open("face_detection_colab.html", "r", encoding="utf-8") as f:
html_content = f.read()
display(HTML(html_content))
Funzioni per l'animazione della cella di output
In [ ]:
class Animation:
def __init__(self, message="Sto convertendo", interval=0.5):
self.message = message
self.interval = interval
self.stop_event = threading.Event()
self.thread = threading.Thread(target=self.animate)
def animate(self):
dots = 0
while not self.stop_event.is_set():
dots = (dots + 1) % 4 # 0,1,2,3
if dots == 0:
display = self.message
else:
display = self.message + "." * dots
# Carriage return per sovrascrivere la linea precedente
sys.stdout.write("\r" + display)
sys.stdout.flush()
time.sleep(self.interval)
# Pulizia della linea dopo l'animazione
sys.stdout.write("\r" + " " * (len(self.message) + 3) + "\r")
sys.stdout.flush()
def start(self):
self.thread.start()
def stop(self):
self.stop_event.set()
self.thread.join()
In [ ]:
def measure_time(func, *args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
elapsed_time = end_time - start_time
return result, elapsed_time
Il codice effettua un ridimensionamento dell'immagine tramite binning, riducendo la risoluzione calcolando la media dei valori dei pixel in blocchi definiti dal fattore di scala.
In [ ]:
def resize_image_binning(img, scale_percent):
# Calcolo il fattore di ridimensionamento in un fattore decimale
scale_factor = scale_percent / 100.0
# Calcolo le nuove dimensioni, math.floor è per arrotondare
new_height = math.floor(img.shape[0] * scale_factor) # img.shape[0] = dimensione originale h
new_width = math.floor(img.shape[1] * scale_factor) # img.shape[1] = dimensione originale w
# Calcolo il fattore di binning, i fattori determinano quanti pixel devono essere combinati in un blocco per ridimensionare l’immagine tramite binning
# Le dimensioni originali vengono divise per quelle nuove ed il risultato combinato in un unico pixel nell’immagine finale
# Esempio: bin_factor_h = 2000 // 500
# bin_factor_w = 1000 // 250
# risultato: 4 e 4
# ogni blocco di 4x4 pixel verrà combinato in un unico pixel nell’immagine finale
bin_factor_h = img.shape[0] // new_height
bin_factor_w = img.shape[1] // new_width
if bin_factor_h < 1 or bin_factor_w < 1:
raise ValueError("Scale_percentage ha un valore troppo grande e l'immagine risulterà troppo piccola.")
# cropped_height e cropped_width sono il prodotto di new_height e new_width con i rispettivi fattori di binning (bin_factor_h e bin_factor_w)
# Facendo così l'immagine è divisibile in blocchi per eseguire il binning
# Rrappresentano la porzione dell’immagine che può essere divisa esattamente in blocchi di dimensioni bin_factor_h e bin_factor_w
cropped_height = bin_factor_h * new_height
cropped_width = bin_factor_w * new_width
if img.shape[0] < cropped_height or img.shape[1] < cropped_width:
raise ValueError("Dimensioni dell'immagine troppo piccole per il binning richiesto.")
# Se img.ndim == 3 vuol dire che l'immagine ha tre dimensioni,quindi a colori RGB
if img.ndim == 3:
# Questo ritaglio assicura che l’immagine sia esattamente della dimensione richiesta per il binning, evitando bordi non divisibili
img_cropped = img[:cropped_height, :cropped_width, :]
# img_cropped viene ridimensionato in una struttura a 5 dimensioni
# new_height e new_width sono le dimensioni finali che voglio ottenere
# bin_factor_h e bin_factor_w indicano il numero di pixel da combinare
# img.shape[2] mantiene i canali di colore
# calcola la media dei pixel lungo le dimensioni di binning riducendo così l'immagine alla risoluzione richiesta
# ogni pixel rappresenta la media dei blocchi originali
img_binned = img_cropped.reshape(new_height, bin_factor_h, new_width, bin_factor_w, img.shape[2]).mean(axis=(1,3))
# reshape applica il binning creando una struttura 5D contenente le nuove dimensioni, i fattori di bin e i canali colore
# continuando con l'esempio di prima 500 righe di blocchi, ciascuno alto 4 pixel
# 250 colonne di blocchi, ciascuno largo 4 pixel
# 3 canali per i colori (rosso, verde, blu)
else:
raise ValueError("Formato immagine non supportato.")
# Converto in uint8 se necessario
# uint8 rappresenta numeri interi con un range di valori compreso tra 0 e 255 senza valori negativi
if img_binned.dtype != np.uint8:
img_binned = (img_binned).astype(np.uint8)
return img_binned
Questa funzione legge ogni immagine, la elabora e la salva come un array NumPy.
Gli array NumPy permettono di avere efficienza computazionale e facilitano le manipolazioni delle immagini come ad esempio sui canali colore, sul crop o sulla rotazione
In [ ]:
def resize_images(input_folder, output_folder, scale_percent):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"Creata cartella di output per il ridimensionamento: {output_folder}")
# Elenco i file nella cartella di input
files = os.listdir(input_folder)
jpg_files = [f for f in files if f.lower().endswith(('.jpg', '.jpeg'))]
total_files = len(jpg_files)
print(f"Numero totale di immagini da ridimensionare: {total_files}")
converted_count = 0
# Istanza della classe Animation utilizzata durante la conversione delle immagini
animation = None
# Iterazione su ogni file contenuto su jpg_files
for filename in jpg_files:
img_path = os.path.join(input_folder, filename)
resized_filename = os.path.splitext(filename)[0] + '_resized.jpg'
resized_path = os.path.join(output_folder, resized_filename)
try:
# Funzione per il ridimensionamento
def process_image(path):
# Leggo l'immagine JPG
img = plt.imread(path)
return img #resituisce un array NUmPy
# Misura del tempo di lettura dell'immagine
# img_path viene passato a process_image tramite il parametro path
img, elapsed_time = measure_time(process_image, img_path)
# Ridimensiono l'immagine
if scale_percent != 100:
img_resized, resize_time = measure_time(resize_image_binning, img, scale_percent)
else:
img_resized = img
resize_time = 0.0 # Nessun ridimensionamento effettuato
# Salvo l'immagine ridimensionata come JPG nella cartella di output
def save_image(path, image):
plt.imsave(path, image)
# questa funzione chiama save_image per salvare l’immagine img_resized in resized_path e
# calcola il tempo necessario per farlo, assegnandolo alla variabile save_time
_, save_time = measure_time(save_image, resized_path, img_resized)
converted_count += 1
# Calcola il tempo totale per questa operazione
total_elapsed = elapsed_time + resize_time + save_time
# Se il conteggio delle immagini elaborate è inferiore o uguale a 5, stampa un messaggio con
# il numero di immagini elaborate, il percorso dell’immagine ridimensionata e il tempo totale impiegato
if converted_count <= 5:
print(f"[{converted_count}/{total_files}] Ridimensionata: {img_path} -> {resized_path} in {total_elapsed:.4f} secondi.")
# Avvio l'animazione dopo i primi 5 ridimensionamenti
if converted_count == 6:
animation = Animation(message="Sto ridimensionando")
animation.start()
except Exception as e:
print(f"Errore nel ridimensionamento di {img_path}: {e}")
# Segnala di fermare l'animazione
if converted_count > 5 and animation is not None:
animation.stop()
print("Ridimensionamento completato.")
else:
print("Ridimensionamento completato.")
In [ ]:
# Percorsi delle cartelle
faces_jpg = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_jpg'
faces_jpg_resized = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_jpg_resized'
no_faces_jpg = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_jpg'
no_faces_jpg_resized = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_jpg_resized'
In [ ]:
resize_images(
input_folder=faces_jpg,
output_folder=faces_jpg_resized,
scale_percent=50
)
resize_images(
input_folder=no_faces_jpg,
output_folder=no_faces_jpg_resized,
scale_percent=50
)
In [ ]:
def convert_jpg_to_ppm(input_folder, output_folder):
# Creazione della cartella di output se non esiste
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"Creata cartella di output: {output_folder}")
# Lista dei file nella cartella di input
files = os.listdir(input_folder)
jpg_files = [f for f in files if f.lower().endswith(('.jpg', '.jpeg'))]
total_files = len(jpg_files)
print(f"Numero totale di immagini da convertire: {total_files}")
# Contatore per le immagini convertite
converted_count = 0
# Istanza della classe Animation
animation = None
# Iterazione sulle immagini
for filename in jpg_files:
img_path = os.path.join(input_folder, filename)
ppm_filename = os.path.splitext(filename)[0] + '.ppm'
ppm_path = os.path.join(output_folder, ppm_filename)
try:
# Misurazione del tempo
start_time = time.perf_counter()
# Leggo l'immagine JPG
img = plt.imread(img_path)
# Salvo l'immagine in formato PPM
plt.imsave(ppm_path, img)
# Fine della misurazione del tempo
end_time = time.perf_counter()
elapsed_time = end_time - start_time
converted_count += 1
# Stampa del tempo per le prime 5 conversioni
if converted_count <= 5:
print(f"[{converted_count}/{total_files}] Convertita: {img_path} -> {ppm_path} in {elapsed_time:.4f} secondi.")
# Avvia l'animazione dopo le prime 5 conversioni
if converted_count == 6:
animation = Animation(message="Sto convertendo")
animation.start()
except Exception as e:
print(f"Errore nella conversione di {img_path}: {e}")
# Segnala di fermare l'animazione
if converted_count > 5 and animation is not None:
animation.stop()
print("Conversione completata.")
else:
print("Conversione completata.")
In [ ]:
faces_resized_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_jpg_resized'
faces_output_ppm = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_PPM'
no_faces_resized_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_jpg_resized'
no_faces_output_ppm = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_PPM'
In [ ]:
# Converto i fole da jpg a PPM
convert_jpg_to_ppm(faces_resized_folder, faces_output_ppm)
convert_jpg_to_ppm(no_faces_resized_folder, no_faces_output_ppm)
Leggo i file PPM
Con questa funzione leggo il contenuto dei file .ppm.
Ogni pixel dell'immagine è rappresentato da un tripletto di valori (R, G, B), ognuno dei quali varia tra 0 e 255 (per immagini a 8 bit per canale).
In [ ]:
def read_ppm(filename):
with open(filename, 'rb') as f:
magic_number = f.readline().strip()
if magic_number != b'P6':
raise ValueError(f"Formato PPM non supportato: {magic_number.decode()}. Solo il formato P6 è supportato.")
# Leggo le intestazioni: width, height, maxval
width, height, maxval = None, None, None
while True:
line = f.readline().strip()
if line.startswith(b'#') or len(line) == 0:
continue # Ignorare commenti e linee vuote
tokens = line.split()
if not width and len(tokens) >= 2:
width, height = int(tokens[0]), int(tokens[1])
if len(tokens) > 2:
maxval = int(tokens[2])
break
elif width and not maxval and len(tokens) >= 1:
maxval = int(tokens[0])
break
if width is None or height is None or maxval is None:
raise ValueError("Intestazione PPM incompleta o formattata in modo errato.")
# Leggo i dati dei pixel
pixel_data = f.read(width * height * 3)
if len(pixel_data) != width * height * 3:
raise ValueError("I dati dei pixel nel file PPM sono incompleti o corrotti.")
image = np.frombuffer(pixel_data, dtype=np.uint8).reshape((height, width, 3))
return image
Creo il dataset che include le immagini con volti, senza volti, il rapporto R/G, il B_value R/3 e HSV
In [ ]:
def rgb_to_hsv_manual(R, G, B):
# Normalizzo i valori RGB a [0, 1]
R_norm = R / 255.0
G_norm = G / 255.0
B_norm = B / 255.0
# Stack dei canali
rgb = np.stack((R_norm, G_norm, B_norm), axis=-1)
hsv = colors.rgb_to_hsv(rgb)
H = hsv[:, :, 0]
S = hsv[:, :, 1]
V = hsv[:, :, 2]
return H, S, V
Con flatten() ogni caratteristica dell’immagine (come il rapporto R/G, il valore B, e le componenti H, S, e V)
diventa un singolo vettore di valori facilitando la creazione di un array di caratteristiche omogeneo
https://eitca.org/artificial-intelligence/eitc-ai-dlpp-deep-learning-with-python-and-pytorch/neural-network/building-neural-network/examination-review-building-neural-network/why-do-we-need-to-flatten-images-before-passing-them-through-the-network/
https://machinelearningmastery.com/image-vector-representation-for-machine-learning-using-opencv/
In [ ]:
def create_dataset_from_folder(ppm_folder, label, csv_filename=None):
X = []
y = []
for file in os.listdir(ppm_folder):
if file.lower().endswith('.ppm'):
ppm_path = os.path.join(ppm_folder, file)
try:
image = read_ppm(ppm_path)
print(f"Lettura dell'immagine {ppm_path} riuscita.")
R = image[:, :, 0].astype(float)
G = image[:, :, 1].astype(float)
B = image[:, :, 2].astype(float)
# Evito divisioni per zero
G[G == 0] = 1.0
B[B == 0] = 1.0
# Calcolo il rapporto R/G
R_G_ratio = R / G
# Definisco B come R / 3
B = R / 3
# Assegno B a B_value
B_value = B
# Converto in HSV
H, S, V = rgb_to_hsv_manual(R, G, B)
print(f"Conversione HSV completata per {ppm_path}.")
tolerance = 0.2
# Verifico se B è circa uguale a R/3
if np.allclose(B, R / 3, atol=tolerance):
# Con flatten() ogni caratteristica dell’immagine (come il rapporto R/G, il valore B, e le componenti H, S, e V)
# diventa un singolo vettore di valori facilitando la creazione di un array di caratteristiche omogeneo
features = np.column_stack((
R_G_ratio.flatten(),
B_value.flatten(),
H.flatten(),
S.flatten(),
V.flatten()
))
X.append(features)
y.extend([label] * len(R.flatten()))
print(f"Dati aggiunti per {ppm_path}.")
else:
print(f"B non corrisponde a R/3 per il file {ppm_path}")
# Puoi decidere come gestire i casi non validi, ad esempio ignorare il file
except Exception as e:
print(f"Errore nel leggere o elaborare il file {ppm_path}: {e}")
# Verifica se X contiene dati prima di eseguire np.vstack
if X:
try:
# Converti X in un array NumPy
X = np.vstack(X) # Combina tutte le immagini in un unico array
except ValueError as ve:
print(f"Errore durante la concatenazione degli array: {ve}")
X = np.array([]) # In caso di errore, assegna un array vuoto
else:
X = np.array([]) # Se X è vuoto, assegna un array vuoto
# Salva in CSV se specificato e X non è vuoto
if csv_filename and X.size > 0:
df = pd.DataFrame(X, columns=['R_G_ratio', 'B_value', 'H', 'S', 'V'])
df['Label'] = y
df.to_csv(csv_filename, index=False)
print(f"Dataset salvato in {csv_filename}")
elif csv_filename:
print(f"Nessun dato valido da salvare in {csv_filename}")
return X, y
In [ ]:
# Definizione delle cartelle e dei percorsi
skin_ppm_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_PPM'
non_skin_ppm_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_PPM'
skin_csv_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/skin_dataset_rg_rb_hsv.csv'
non_skin_csv_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/non_skin_dataset_rg_rb_hsv.csv'
combined_csv_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/combined_skin_non_skin_dataset_rg_rb_hsv.csv'
count = 0
# Creo i dataset per skin e non-skin
print("Creazione del dataset per le immagini con volti...")
(X_skin, y_skin), time_skin = measure_time(lambda: create_dataset_from_folder(skin_ppm_folder, label=1, csv_filename=skin_csv_path))
print(f"Tempo per creare il dataset skin: {time_skin:.2f} secondi")
count += 1
# Stampa del tempo per le prime 5 conversioni
if count <= 5:
print(f"[{count}/...] Dataset skin creato.")
# L'animazione inizia dopo le prime 5 conversioni
if count == 6:
animation = Animation(message="Sto convertendo")
animation.start()
print("Creazione del dataset per le immagini senza volti...")
(X_non_skin, y_non_skin), time_non_skin = measure_time(lambda: create_dataset_from_folder(non_skin_ppm_folder, label=0, csv_filename=non_skin_csv_path))
print(f"Tempo per creare il dataset non-skin: {time_non_skin:.2f} secondi")
count +=1
# Stampa del tempo per le prime 5 conversioni
if count <= 5:
print(f"[{count}/...] Dataset non-skin creato.")
if count == 6:
animation = Animation(message="Sto convertendo")
animation.start()
# Combino i dataset e le etichette
if X_skin.size > 0 and X_non_skin.size > 0:
X = np.vstack((X_skin, X_non_skin)) # Caratteristiche combinate (R_G_ratio, B_value, H, S, V)
y = np.concatenate((y_skin, y_non_skin)) # Etichette combinate
# Salvo il dataset combinato in CSV
combined_df = pd.DataFrame(X, columns=['R_G_ratio'])
combined_df['Label'] = y
combined_df.to_csv(combined_csv_path, index=False)
print(f"Dataset combinato salvato in '{combined_csv_path}'")
print("Prime 5 righe del dataset combinato:")
print(combined_df.head())
else:
print("Uno dei dataset (skin o non-skin) è vuoto.")
In [ ]:
BASE_URL = "/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/"
In [ ]:
df_combined_rg_rb_hsv = pd.read_csv(BASE_URL + "combined_skin_non_skin_dataset_rg_rb_hsv.csv")
In [ ]:
df_combined_rg_rb_hsv.shape
Out[ ]:
(206195753, 6)
In [ ]:
df_combined_rg_rb_hsv.count()
Out[ ]:
R_G_ratio 206195753 B_value 206195753 H 206195753 S 206195753 V 206195753 Label 206195753 dtype: int64
In [ ]:
df_combined_rg_rb_hsv.head()
Out[ ]:
| R_G_ratio | B_value | H | S | V | Label | |
|---|---|---|---|---|---|---|
| 0 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 1 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 2 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 3 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 4 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
In [ ]:
df_combined_rg_rb_hsv.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 206195753 entries, 0 to 206195752 Data columns (total 6 columns): # Column Dtype --- ------ ----- 0 R_G_ratio float64 1 B_value float64 2 H float64 3 S float64 4 V float64 5 Label int64 dtypes: float64(5), int64(1) memory usage: 9.2 GB
In [ ]:
df_combined_rg_rb_hsv.describe
Out[ ]:
<bound method NDFrame.describe of R_G_ratio B_value H S V Label 0 0.026316 0.333333 0.330383 0.991228 0.149020 1 1 0.026316 0.333333 0.330383 0.991228 0.149020 1 2 0.026316 0.333333 0.330383 0.991228 0.149020 1 3 0.026316 0.333333 0.330383 0.991228 0.149020 1 4 0.026316 0.333333 0.330383 0.991228 0.149020 1 ... ... ... ... ... ... ... 206195748 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195749 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195750 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195751 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195752 1.341463 18.333333 0.103030 0.666667 0.215686 0 [206195753 rows x 6 columns]>
In [ ]:
df_combined_rg_rb_hsv.isna()
Out[ ]:
| R_G_ratio | B_value | H | S | V | Label | |
|---|---|---|---|---|---|---|
| 0 | False | False | False | False | False | False |
| 1 | False | False | False | False | False | False |
| 2 | False | False | False | False | False | False |
| 3 | False | False | False | False | False | False |
| 4 | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... |
| 206195748 | False | False | False | False | False | False |
| 206195749 | False | False | False | False | False | False |
| 206195750 | False | False | False | False | False | False |
| 206195751 | False | False | False | False | False | False |
| 206195752 | False | False | False | False | False | False |
206195753 rows × 6 columns
In [ ]:
df_combined_rg_rb_hsv.isnull()
Out[ ]:
| R_G_ratio | B_value | H | S | V | Label | |
|---|---|---|---|---|---|---|
| 0 | False | False | False | False | False | False |
| 1 | False | False | False | False | False | False |
| 2 | False | False | False | False | False | False |
| 3 | False | False | False | False | False | False |
| 4 | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... |
| 206195748 | False | False | False | False | False | False |
| 206195749 | False | False | False | False | False | False |
| 206195750 | False | False | False | False | False | False |
| 206195751 | False | False | False | False | False | False |
| 206195752 | False | False | False | False | False | False |
206195753 rows × 6 columns
In [ ]:
class_distribution=(df_combined_rg_rb_hsv['Label'].value_counts(normalize=True))
In [ ]:
plt.figure(figsize=(10, 6))
class_distribution.plot(kind='bar')
plt.title('Distribuzione delle classi (Response)')
plt.xlabel('Classi')
plt.ylabel('Percentuale')
plt.xticks(rotation=0)
plt.show()
In [ ]:
# Converto i dati in array NumPy per l'addestramento
X = np.array(X)
y = np.array(y)
# Suddivido il dataset in training (70%) e temporaneo (validation + test) (30%)
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# Suddivido il set temporaneo in validation (15%) e test (15%)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp)
print(f"Shape di X_train: {X_train.shape}")
print(f"Shape di X_val: {X_val.shape}")
print(f"Shape di X_test: {X_test.shape}")
print(f"Numero di dati nel Training set: {len(X_train)}")
print(f"Numero di dati nel Validation set: {len(X_val)}")
print(f"Numero di dati nel Test set: {len(X_test)}")
Normalizzazione delle Feature
È fondamentale normalizzare le feature per garantire che abbiano una scala comparabile, migliorando così le prestazioni del modello.
In [ ]:
animation = Animation(message="Normalizzazione delle feature", interval=0.5)
animation.start()
# Normalizzazione delle feature
scaler = StandardScaler()
X_train_scaled, time_train_scaler = measure_time(lambda: scaler.fit_transform(X_train))
X_val_scaled, time_val_scaler = measure_time(lambda: scaler.transform(X_val))
X_test_scaled, time_test_scaler = measure_time(lambda: scaler.transform(X_test))
animation.stop()
print(f"Tempo per normalizzare il training set: {time_train_scaler:.2f} secondi")
print(f"Tempo per normalizzare il validation set: {time_val_scaler:.2f} secondi")
print(f"Tempo per normalizzare il test set: {time_test_scaler:.2f} secondi")
Tempo per normalizzare il training set: 5.63 secondi Tempo per normalizzare il validation set: 0.33 secondi Tempo per normalizzare il test set: 0.34 secondi
Addestramento
Il modello usato poi con le immagini più avanti è il "colab_clf_rf_rg_rb_hsv.joblib" presente nella cartella di progetto
Ho fatto un sacco di prove cambiando il numero delle immagini e
quindi ricreando ogni volta il dataset. La massima Accuracy che ho ottenuto è stata del 79.3%
In [ ]:
animation = Animation(message="Addestramento del modello Random Forest", interval=0.5)
animation.start()
# Addestramento del modello Random Forest e misura del tempo
colab_clf_rf_rg_rb_hsv, time_rf = measure_time(RandomForestClassifier(
n_estimators=100,
max_depth=10,
random_state=42,
n_jobs=-1).fit, X_train_scaled, y_train)
# Ferma l'animazione
animation.stop()
# Salva il modello addestrato
dump(colab_clf_rf_rg_rb_hsv, '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/colab_clf_rf_rg_rb_hsv.joblib')
print(f"Tempo per addestrare il modello Random Forest (con max_depth=10 e n_estimators=100): {time_rf:.2f} secondi")
Tempo per addestrare il modello Random Forest (con max_depth=10 e n_estimators=100): 348.64 secondi
In [ ]:
# Inizializza l'animazione per la previsione
animation = Animation(message="Previsione sul test set", interval=0.5)
animation.start()
# Previsione sul test set e misura del tempo
y_test_pred, time_test_pred = measure_time(colab_clf_rf_rg_rb_hsv.predict, X_test_scaled)
# Ferma l'animazione
animation.stop()
# Valutazione del modello sul test set
print("Report di classificazione sul Test Set:")
print(classification_report(y_test, y_test_pred))
print(f"Accuratezza Test: {accuracy_score(y_test, y_test_pred)*100:.2f}%")
print(f"Tempo per prevedere sul test set: {time_test_pred:.2f} secondi")
Report di classificazione sul Test Set:
precision recall f1-score support
0 0.80 0.98 0.88 6212517
1 0.74 0.20 0.31 1921075
accuracy 0.79 8133592
macro avg 0.77 0.59 0.59 8133592
weighted avg 0.78 0.79 0.74 8133592
Accuratezza Test: 79.36%
Tempo per prevedere sul test set: 4.60 secondi
In [ ]:
# Inizializza l'animazione per la previsione sul validation set
animation = Animation(message="Previsione sul validation set", interval=0.5)
animation.start()
# Previsione sul validation set e misura del tempo
y_val_pred, time_val_pred = measure_time(colab_clf_rf_rg_rb_hsv.predict, X_val_scaled)
# Ferma l'animazione
animation.stop()
# Valutazione del modello sul validation set
print("Report di classificazione sul Validation Set:")
print(classification_report(y_val, y_val_pred))
print(f"Accuratezza Validation: {accuracy_score(y_val, y_val_pred)*100:.2f}%")
print(f"Tempo per prevedere sul validation set: {time_val_pred:.2f} secondi")
Report di classificazione sul Validation Set:
precision recall f1-score support
0 0.80 0.98 0.88 6212517
1 0.74 0.20 0.31 1921075
accuracy 0.79 8133592
macro avg 0.77 0.59 0.59 8133592
weighted avg 0.78 0.79 0.74 8133592
Accuratezza Validation: 79.36%
Tempo per prevedere sul validation set: 4.60 secondi
In [ ]:
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
def plot_confusion_matrix(y_true, y_pred, model_name):
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(6, 4))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False)
plt.title(f"Matrice di Confusione - {model_name}")
plt.xlabel('Previsione')
plt.ylabel('Reale')
plt.show()
print(f"\nMatrice di Confusione - {model_name}:")
print(cm)
In [ ]:
plot_confusion_matrix(y_test, y_test_pred, model_name="Random Forest Test Set")
Matrice di Confusione - Random Forest Test Set: [[6076718 135799] [1543253 377822]]
Ho provato ad ottimizzare gli iperparametri con GridSearchCV e RandomizedSearchCV ma purtroppo non ho avuto nessun miglioramneto.
GridSearchCV
In [ ]:
# Definizione della griglia degli iperparametri
param_grid = {
'n_estimators': [50, 80, 110],
'max_depth': [2, 3, 4],
'min_samples_split': [14, 18, 22],
'min_samples_leaf': [3, 4, 5],
'bootstrap': [True, False]
}
scoring = {
'f1': 'f1',
'accuracy': 'accuracy',
'precision': 'precision',
'recall': 'recall'
}
# Inizializzazione GridSearchCV con RandomForestClassifier
grid_search = GridSearchCV(
estimator=RandomForestClassifier(
random_state=42,
n_jobs=-1,
class_weight='balanced' # Manteniamo il bilanciamento delle classi
),
param_grid=param_grid,
cv=5, # Numero di fold per la cross-validazione
n_jobs=-1, # Utilizza tutti i core disponibili
verbose=2, # Livello di verbosità
scoring=scoring, # Specifica le metriche di scoring
refit='f1' # Indica quale metrica usare per rifitare il modello
)
In [ ]:
# Inizializzazione l'animazione per Grid Search
animation = Animation(message="Ottimizzazione con Grid Search", interval=0.5)
animation.start()
# Addestramento del modello con Grid Search e misura del tempo
grid_search, time_grid_search = measure_time(grid_search.fit, X_train_scaled, y_train)
animation.stop()
print(f"Tempo per Grid Search: {time_grid_search:.2f} secondi")
# Migliori parametri trovati
print("Migliori parametri trovati con Grid Search:")
print(grid_search.best_params_)
clf_rf_gridsearch = grid_search.best_estimator_
model_save_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/clf_rf_rg_rb_hsv_gridsearch.joblib'
dump(clf_rf_gridsearch, model_save_path)
print(f"Migliore modello Random Forest addestrato e salvato in: {model_save_path}")
RandomizedSearchCV
In [ ]:
param_dist = {
'n_estimators': [10, 30, 50],
'max_depth': [10, 20, None],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
'bootstrap': [True, False]
}
scoring = {
'f1': 'f1',
'accuracy': 'accuracy',
'precision': 'precision',
'recall': 'recall'
}
# Inizializza RandomizedSearchCV con RandomForestClassifier
random_search = RandomizedSearchCV(
estimator=RandomForestClassifier(
random_state=42,
n_jobs=-1,
class_weight='balanced' # Manteniamo il bilanciamento delle classi
),
param_distributions=param_dist,
n_iter=50, # Numero di combinazioni casuali da esplorare
cv=3, # Numero di fold per la cross-validazione (ridotto per risparmiare risorse)
n_jobs=-1, # Utilizza tutti i core disponibili
verbose=2, # Livello di verbosità
scoring=scoring, # Specifica le metriche di scoring
refit='f1', # Indica quale metrica usare per rifitare il modello
random_state=42
)
In [ ]:
animation = Animation(message="Ottimizzazione con Randomized Search", interval=0.5)
animation.start()
random_search, time_random_search = measure_time(random_search.fit, X_train_scaled, y_train)
animation.stop()
print(f"Tempo per Randomized Search: {time_random_search:.2f} secondi")
print("Migliori parametri trovati con Randomized Search:")
print(random_search.best_params_)
clf_rf_randomsearch = random_search.best_estimator_
model_save_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/clf_rf_rg_rb_hsv_randomizedsearch.joblib'
dump(clf_rf_randomsearch, model_save_path)
print(f"Migliore modello Random Forest addestrato e salvato in: {model_save_path}")
Funzioni per l'animazione della cella di output
Codice
class Animation:
def __init__(self, message="Sto convertendo", interval=0.5):
self.message = message
self.interval = interval
self.stop_event = threading.Event()
self.thread = threading.Thread(target=self.animate)
def animate(self):
dots = 0
while not self.stop_event.is_set():
dots = (dots + 1) % 4 # 0,1,2,3
if dots == 0:
display = self.message
else:
display = self.message + "." * dots
# Carriage return per sovrascrivere la linea precedente
sys.stdout.write("\r" + display)
sys.stdout.flush()
time.sleep(self.interval)
# Pulizia della linea dopo l'animazione
sys.stdout.write("\r" + " " * (len(self.message) + 3) + "\r")
sys.stdout.flush()
def start(self):
self.thread.start()
def stop(self):
self.stop_event.set()
self.thread.join()
Codice
def measure_time(func, *args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
elapsed_time = end_time - start_time
return result, elapsed_time
Il codice effettua un ridimensionamento dell'immagine tramite binning, riducendo la risoluzione calcolando la media dei valori dei pixel in blocchi definiti dal fattore di scala.
Codice
def resize_image_binning(img, scale_percent):
# Calcolo il fattore di ridimensionamento in un fattore decimale
scale_factor = scale_percent / 100.0
# Calcolo le nuove dimensioni, math.floor è per arrotondare
new_height = math.floor(img.shape[0] * scale_factor) # img.shape[0] = dimensione originale h
new_width = math.floor(img.shape[1] * scale_factor) # img.shape[1] = dimensione originale w
# Calcolo il fattore di binning, i fattori determinano quanti pixel devono essere combinati in un blocco per ridimensionare l’immagine tramite binning
# Le dimensioni originali vengono divise per quelle nuove ed il risultato combinato in un unico pixel nell’immagine finale
# Esempio: bin_factor_h = 2000 // 500
# bin_factor_w = 1000 // 250
# risultato: 4 e 4
# ogni blocco di 4x4 pixel verrà combinato in un unico pixel nell’immagine finale
bin_factor_h = img.shape[0] // new_height
bin_factor_w = img.shape[1] // new_width
if bin_factor_h < 1 or bin_factor_w < 1:
raise ValueError("Scale_percentage ha un valore troppo grande e l'immagine risulterà troppo piccola.")
# cropped_height e cropped_width sono il prodotto di new_height e new_width con i rispettivi fattori di binning (bin_factor_h e bin_factor_w)
# Facendo così l'immagine è divisibile in blocchi per eseguire il binning
# Rrappresentano la porzione dell’immagine che può essere divisa esattamente in blocchi di dimensioni bin_factor_h e bin_factor_w
cropped_height = bin_factor_h * new_height
cropped_width = bin_factor_w * new_width
if img.shape[0] < cropped_height or img.shape[1] < cropped_width:
raise ValueError("Dimensioni dell'immagine troppo piccole per il binning richiesto.")
# Se img.ndim == 3 vuol dire che l'immagine ha tre dimensioni,quindi a colori RGB
if img.ndim == 3:
# Questo ritaglio assicura che l’immagine sia esattamente della dimensione richiesta per il binning, evitando bordi non divisibili
img_cropped = img[:cropped_height, :cropped_width, :]
# img_cropped viene ridimensionato in una struttura a 5 dimensioni
# new_height e new_width sono le dimensioni finali che voglio ottenere
# bin_factor_h e bin_factor_w indicano il numero di pixel da combinare
# img.shape[2] mantiene i canali di colore
# calcola la media dei pixel lungo le dimensioni di binning riducendo così l'immagine alla risoluzione richiesta
# ogni pixel rappresenta la media dei blocchi originali
img_binned = img_cropped.reshape(new_height, bin_factor_h, new_width, bin_factor_w, img.shape[2]).mean(axis=(1,3))
# reshape applica il binning creando una struttura 5D contenente le nuove dimensioni, i fattori di bin e i canali colore
# continuando con l'esempio di prima 500 righe di blocchi, ciascuno alto 4 pixel
# 250 colonne di blocchi, ciascuno largo 4 pixel
# 3 canali per i colori (rosso, verde, blu)
else:
raise ValueError("Formato immagine non supportato.")
# Converto in uint8 se necessario
# uint8 rappresenta numeri interi con un range di valori compreso tra 0 e 255 senza valori negativi
if img_binned.dtype != np.uint8:
img_binned = (img_binned).astype(np.uint8)
return img_binned
Questa funzione legge ogni immagine, la elabora e la salva come un array NumPy.
Gli array NumPy permettono di avere efficienza computazionale e facilitano le manipolazioni delle immagini come ad esempio sui canali colore, sul crop o sulla rotazione
Codice
def resize_images(input_folder, output_folder, scale_percent):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"Creata cartella di output per il ridimensionamento: {output_folder}")
# Elenco i file nella cartella di input
files = os.listdir(input_folder)
jpg_files = [f for f in files if f.lower().endswith(('.jpg', '.jpeg'))]
total_files = len(jpg_files)
print(f"Numero totale di immagini da ridimensionare: {total_files}")
converted_count = 0
# Istanza della classe Animation utilizzata durante la conversione delle immagini
animation = None
# Iterazione su ogni file contenuto su jpg_files
for filename in jpg_files:
img_path = os.path.join(input_folder, filename)
resized_filename = os.path.splitext(filename)[0] + '_resized.jpg'
resized_path = os.path.join(output_folder, resized_filename)
try:
# Funzione per il ridimensionamento
def process_image(path):
# Leggo l'immagine JPG
img = plt.imread(path)
return img #resituisce un array NUmPy
# Misura del tempo di lettura dell'immagine
# img_path viene passato a process_image tramite il parametro path
img, elapsed_time = measure_time(process_image, img_path)
# Ridimensiono l'immagine
if scale_percent != 100:
img_resized, resize_time = measure_time(resize_image_binning, img, scale_percent)
else:
img_resized = img
resize_time = 0.0 # Nessun ridimensionamento effettuato
# Salvo l'immagine ridimensionata come JPG nella cartella di output
def save_image(path, image):
plt.imsave(path, image)
# questa funzione chiama save_image per salvare l’immagine img_resized in resized_path e
# calcola il tempo necessario per farlo, assegnandolo alla variabile save_time
_, save_time = measure_time(save_image, resized_path, img_resized)
converted_count += 1
# Calcola il tempo totale per questa operazione
total_elapsed = elapsed_time + resize_time + save_time
# Se il conteggio delle immagini elaborate è inferiore o uguale a 5, stampa un messaggio con
# il numero di immagini elaborate, il percorso dell’immagine ridimensionata e il tempo totale impiegato
if converted_count <= 5:
print(f"[{converted_count}/{total_files}] Ridimensionata: {img_path} -> {resized_path} in {total_elapsed:.4f} secondi.")
# Avvio l'animazione dopo i primi 5 ridimensionamenti
if converted_count == 6:
animation = Animation(message="Sto ridimensionando")
animation.start()
except Exception as e:
print(f"Errore nel ridimensionamento di {img_path}: {e}")
# Segnala di fermare l'animazione
if converted_count > 5 and animation is not None:
animation.stop()
print("Ridimensionamento completato.")
else:
print("Ridimensionamento completato.")
Codice
# Percorsi delle cartelle
faces_jpg = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_jpg'
faces_jpg_resized = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_jpg_resized'
no_faces_jpg = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_jpg'
no_faces_jpg_resized = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_jpg_resized'
Codice
resize_images(
input_folder=faces_jpg,
output_folder=faces_jpg_resized,
scale_percent=50
)
resize_images(
input_folder=no_faces_jpg,
output_folder=no_faces_jpg_resized,
scale_percent=50
)
Codice
def convert_jpg_to_ppm(input_folder, output_folder):
# Creazione della cartella di output se non esiste
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"Creata cartella di output: {output_folder}")
# Lista dei file nella cartella di input
files = os.listdir(input_folder)
jpg_files = [f for f in files if f.lower().endswith(('.jpg', '.jpeg'))]
total_files = len(jpg_files)
print(f"Numero totale di immagini da convertire: {total_files}")
# Contatore per le immagini convertite
converted_count = 0
# Istanza della classe Animation
animation = None
# Iterazione sulle immagini
for filename in jpg_files:
img_path = os.path.join(input_folder, filename)
ppm_filename = os.path.splitext(filename)[0] + '.ppm'
ppm_path = os.path.join(output_folder, ppm_filename)
try:
# Misurazione del tempo
start_time = time.perf_counter()
# Leggo l'immagine JPG
img = plt.imread(img_path)
# Salvo l'immagine in formato PPM
plt.imsave(ppm_path, img)
# Fine della misurazione del tempo
end_time = time.perf_counter()
elapsed_time = end_time - start_time
converted_count += 1
# Stampa del tempo per le prime 5 conversioni
if converted_count <= 5:
print(f"[{converted_count}/{total_files}] Convertita: {img_path} -> {ppm_path} in {elapsed_time:.4f} secondi.")
# Avvia l'animazione dopo le prime 5 conversioni
if converted_count == 6:
animation = Animation(message="Sto convertendo")
animation.start()
except Exception as e:
print(f"Errore nella conversione di {img_path}: {e}")
# Segnala di fermare l'animazione
if converted_count > 5 and animation is not None:
animation.stop()
print("Conversione completata.")
else:
print("Conversione completata.")
Codice
faces_resized_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_jpg_resized'
faces_output_ppm = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_PPM'
no_faces_resized_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_jpg_resized'
no_faces_output_ppm = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_PPM'
Codice
# Converto i fole da jpg a PPM
convert_jpg_to_ppm(faces_resized_folder, faces_output_ppm)
convert_jpg_to_ppm(no_faces_resized_folder, no_faces_output_ppm)
Leggo i file PPM
Con questa funzione leggo il contenuto dei file .ppm.
Ogni pixel dell'immagine è rappresentato da un tripletto di valori (R, G, B), ognuno dei quali varia tra 0 e 255 (per immagini a 8 bit per canale).
Codice
def read_ppm(filename):
with open(filename, 'rb') as f:
magic_number = f.readline().strip()
if magic_number != b'P6':
raise ValueError(f"Formato PPM non supportato: {magic_number.decode()}. Solo il formato P6 è supportato.")
# Leggo le intestazioni: width, height, maxval
width, height, maxval = None, None, None
while True:
line = f.readline().strip()
if line.startswith(b'#') or len(line) == 0:
continue # Ignorare commenti e linee vuote
tokens = line.split()
if not width and len(tokens) >= 2:
width, height = int(tokens[0]), int(tokens[1])
if len(tokens) > 2:
maxval = int(tokens[2])
break
elif width and not maxval and len(tokens) >= 1:
maxval = int(tokens[0])
break
if width is None or height is None or maxval is None:
raise ValueError("Intestazione PPM incompleta o formattata in modo errato.")
# Leggo i dati dei pixel
pixel_data = f.read(width * height * 3)
if len(pixel_data) != width * height * 3:
raise ValueError("I dati dei pixel nel file PPM sono incompleti o corrotti.")
image = np.frombuffer(pixel_data, dtype=np.uint8).reshape((height, width, 3))
return image
Creo il dataset che include le immagini con volti, senza volti, il rapporto R/G, il B_value R/3 e HSV
Codice
def rgb_to_hsv_manual(R, G, B):
# Normalizzo i valori RGB a [0, 1]
R_norm = R / 255.0
G_norm = G / 255.0
B_norm = B / 255.0
# Stack dei canali
rgb = np.stack((R_norm, G_norm, B_norm), axis=-1)
hsv = colors.rgb_to_hsv(rgb)
H = hsv[:, :, 0]
S = hsv[:, :, 1]
V = hsv[:, :, 2]
return H, S, V
Con flatten() ogni caratteristica dell’immagine (come il rapporto R/G, il valore B, e le componenti H, S, e V)
diventa un singolo vettore di valori facilitando la creazione di un array di caratteristiche omogeneo
https://eitca.org/artificial-intelligence/eitc-ai-dlpp-deep-learning-with-python-and-pytorch/neural-network/building-neural-network/examination-review-building-neural-network/why-do-we-need-to-flatten-images-before-passing-them-through-the-network/
https://machinelearningmastery.com/image-vector-representation-for-machine-learning-using-opencv/
Codice
def create_dataset_from_folder(ppm_folder, label, csv_filename=None):
X = []
y = []
for file in os.listdir(ppm_folder):
if file.lower().endswith('.ppm'):
ppm_path = os.path.join(ppm_folder, file)
try:
image = read_ppm(ppm_path)
print(f"Lettura dell'immagine {ppm_path} riuscita.")
R = image[:, :, 0].astype(float)
G = image[:, :, 1].astype(float)
B = image[:, :, 2].astype(float)
# Evito divisioni per zero
G[G == 0] = 1.0
B[B == 0] = 1.0
# Calcolo il rapporto R/G
R_G_ratio = R / G
# Definisco B come R / 3
B = R / 3
# Assegno B a B_value
B_value = B
# Converto in HSV
H, S, V = rgb_to_hsv_manual(R, G, B)
print(f"Conversione HSV completata per {ppm_path}.")
tolerance = 0.2
# Verifico se B è circa uguale a R/3
if np.allclose(B, R / 3, atol=tolerance):
# Con flatten() ogni caratteristica dell’immagine (come il rapporto R/G, il valore B, e le componenti H, S, e V)
# diventa un singolo vettore di valori facilitando la creazione di un array di caratteristiche omogeneo
features = np.column_stack((
R_G_ratio.flatten(),
B_value.flatten(),
H.flatten(),
S.flatten(),
V.flatten()
))
X.append(features)
y.extend([label] * len(R.flatten()))
print(f"Dati aggiunti per {ppm_path}.")
else:
print(f"B non corrisponde a R/3 per il file {ppm_path}")
# Puoi decidere come gestire i casi non validi, ad esempio ignorare il file
except Exception as e:
print(f"Errore nel leggere o elaborare il file {ppm_path}: {e}")
# Verifica se X contiene dati prima di eseguire np.vstack
if X:
try:
# Converti X in un array NumPy
X = np.vstack(X) # Combina tutte le immagini in un unico array
except ValueError as ve:
print(f"Errore durante la concatenazione degli array: {ve}")
X = np.array([]) # In caso di errore, assegna un array vuoto
else:
X = np.array([]) # Se X è vuoto, assegna un array vuoto
# Salva in CSV se specificato e X non è vuoto
if csv_filename and X.size > 0:
df = pd.DataFrame(X, columns=['R_G_ratio', 'B_value', 'H', 'S', 'V'])
df['Label'] = y
df.to_csv(csv_filename, index=False)
print(f"Dataset salvato in {csv_filename}")
elif csv_filename:
print(f"Nessun dato valido da salvare in {csv_filename}")
return X, y
Codice
# Definizione delle cartelle e dei percorsi
skin_ppm_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/face_PPM'
non_skin_ppm_folder = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/no_face_PPM'
skin_csv_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/skin_dataset_rg_rb_hsv.csv'
non_skin_csv_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/non_skin_dataset_rg_rb_hsv.csv'
combined_csv_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/combined_skin_non_skin_dataset_rg_rb_hsv.csv'
count = 0
# Creo i dataset per skin e non-skin
print("Creazione del dataset per le immagini con volti...")
(X_skin, y_skin), time_skin = measure_time(lambda: create_dataset_from_folder(skin_ppm_folder, label=1, csv_filename=skin_csv_path))
print(f"Tempo per creare il dataset skin: {time_skin:.2f} secondi")
count += 1
# Stampa del tempo per le prime 5 conversioni
if count <= 5:
print(f"[{count}/...] Dataset skin creato.")
# L'animazione inizia dopo le prime 5 conversioni
if count == 6:
animation = Animation(message="Sto convertendo")
animation.start()
print("Creazione del dataset per le immagini senza volti...")
(X_non_skin, y_non_skin), time_non_skin = measure_time(lambda: create_dataset_from_folder(non_skin_ppm_folder, label=0, csv_filename=non_skin_csv_path))
print(f"Tempo per creare il dataset non-skin: {time_non_skin:.2f} secondi")
count +=1
# Stampa del tempo per le prime 5 conversioni
if count <= 5:
print(f"[{count}/...] Dataset non-skin creato.")
if count == 6:
animation = Animation(message="Sto convertendo")
animation.start()
# Combino i dataset e le etichette
if X_skin.size > 0 and X_non_skin.size > 0:
X = np.vstack((X_skin, X_non_skin)) # Caratteristiche combinate (R_G_ratio, B_value, H, S, V)
y = np.concatenate((y_skin, y_non_skin)) # Etichette combinate
# Salvo il dataset combinato in CSV
combined_df = pd.DataFrame(X, columns=['R_G_ratio'])
combined_df['Label'] = y
combined_df.to_csv(combined_csv_path, index=False)
print(f"Dataset combinato salvato in '{combined_csv_path}'")
print("Prime 5 righe del dataset combinato:")
print(combined_df.head())
else:
print("Uno dei dataset (skin o non-skin) è vuoto.")
Codice
BASE_URL = "/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/"
Codice
df_combined_rg_rb_hsv = pd.read_csv(BASE_URL + "combined_skin_non_skin_dataset_rg_rb_hsv.csv")
Codice
df_combined_rg_rb_hsv.shape
Out[ ]:
(206195753, 6)
Codice
df_combined_rg_rb_hsv.count()
Out[ ]:
R_G_ratio 206195753 B_value 206195753 H 206195753 S 206195753 V 206195753 Label 206195753 dtype: int64
Codice
df_combined_rg_rb_hsv.head()
Out[ ]:
| R_G_ratio | B_value | H | S | V | Label | |
|---|---|---|---|---|---|---|
| 0 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 1 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 2 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 3 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
| 4 | 0.026316 | 0.333333 | 0.330383 | 0.991228 | 0.14902 | 1 |
Codice
df_combined_rg_rb_hsv.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 206195753 entries, 0 to 206195752 Data columns (total 6 columns): # Column Dtype --- ------ ----- 0 R_G_ratio float64 1 B_value float64 2 H float64 3 S float64 4 V float64 5 Label int64 dtypes: float64(5), int64(1) memory usage: 9.2 GB
Codice
df_combined_rg_rb_hsv.describe
Out[ ]:
<bound method NDFrame.describe of R_G_ratio B_value H S V Label 0 0.026316 0.333333 0.330383 0.991228 0.149020 1 1 0.026316 0.333333 0.330383 0.991228 0.149020 1 2 0.026316 0.333333 0.330383 0.991228 0.149020 1 3 0.026316 0.333333 0.330383 0.991228 0.149020 1 4 0.026316 0.333333 0.330383 0.991228 0.149020 1 ... ... ... ... ... ... ... 206195748 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195749 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195750 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195751 1.341463 18.333333 0.103030 0.666667 0.215686 0 206195752 1.341463 18.333333 0.103030 0.666667 0.215686 0 [206195753 rows x 6 columns]>
Codice
df_combined_rg_rb_hsv.isna()
Out[ ]:
| R_G_ratio | B_value | H | S | V | Label | |
|---|---|---|---|---|---|---|
| 0 | False | False | False | False | False | False |
| 1 | False | False | False | False | False | False |
| 2 | False | False | False | False | False | False |
| 3 | False | False | False | False | False | False |
| 4 | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... |
| 206195748 | False | False | False | False | False | False |
| 206195749 | False | False | False | False | False | False |
| 206195750 | False | False | False | False | False | False |
| 206195751 | False | False | False | False | False | False |
| 206195752 | False | False | False | False | False | False |
206195753 rows × 6 columns
Codice
df_combined_rg_rb_hsv.isnull()
Out[ ]:
| R_G_ratio | B_value | H | S | V | Label | |
|---|---|---|---|---|---|---|
| 0 | False | False | False | False | False | False |
| 1 | False | False | False | False | False | False |
| 2 | False | False | False | False | False | False |
| 3 | False | False | False | False | False | False |
| 4 | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... |
| 206195748 | False | False | False | False | False | False |
| 206195749 | False | False | False | False | False | False |
| 206195750 | False | False | False | False | False | False |
| 206195751 | False | False | False | False | False | False |
| 206195752 | False | False | False | False | False | False |
206195753 rows × 6 columns
Codice
class_distribution=(df_combined_rg_rb_hsv['Label'].value_counts(normalize=True))
Codice
plt.figure(figsize=(10, 6))
class_distribution.plot(kind='bar')
plt.title('Distribuzione delle classi (Response)')
plt.xlabel('Classi')
plt.ylabel('Percentuale')
plt.xticks(rotation=0)
plt.show()
Codice
# Converto i dati in array NumPy per l'addestramento
X = np.array(X)
y = np.array(y)
# Suddivido il dataset in training (70%) e temporaneo (validation + test) (30%)
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# Suddivido il set temporaneo in validation (15%) e test (15%)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp)
print(f"Shape di X_train: {X_train.shape}")
print(f"Shape di X_val: {X_val.shape}")
print(f"Shape di X_test: {X_test.shape}")
print(f"Numero di dati nel Training set: {len(X_train)}")
print(f"Numero di dati nel Validation set: {len(X_val)}")
print(f"Numero di dati nel Test set: {len(X_test)}")
Normalizzazione delle Feature
È fondamentale normalizzare le feature per garantire che abbiano una scala comparabile, migliorando così le prestazioni del modello.
Codice
animation = Animation(message="Normalizzazione delle feature", interval=0.5)
animation.start()
# Normalizzazione delle feature
scaler = StandardScaler()
X_train_scaled, time_train_scaler = measure_time(lambda: scaler.fit_transform(X_train))
X_val_scaled, time_val_scaler = measure_time(lambda: scaler.transform(X_val))
X_test_scaled, time_test_scaler = measure_time(lambda: scaler.transform(X_test))
animation.stop()
print(f"Tempo per normalizzare il training set: {time_train_scaler:.2f} secondi")
print(f"Tempo per normalizzare il validation set: {time_val_scaler:.2f} secondi")
print(f"Tempo per normalizzare il test set: {time_test_scaler:.2f} secondi")
Tempo per normalizzare il training set: 5.63 secondi Tempo per normalizzare il validation set: 0.33 secondi Tempo per normalizzare il test set: 0.34 secondi
Addestramento
Il modello usato poi con le immagini più avanti è il "colab_clf_rf_rg_rb_hsv.joblib" presente nella cartella di progetto
Ho fatto un sacco di prove cambiando il numero delle immagini e
quindi ricreando ogni volta il dataset. La massima Accuracy che ho ottenuto è stata del 79.3%
Codice
animation = Animation(message="Addestramento del modello Random Forest", interval=0.5)
animation.start()
# Addestramento del modello Random Forest e misura del tempo
colab_clf_rf_rg_rb_hsv, time_rf = measure_time(RandomForestClassifier(
n_estimators=100,
max_depth=10,
random_state=42,
n_jobs=-1).fit, X_train_scaled, y_train)
# Ferma l'animazione
animation.stop()
# Salva il modello addestrato
dump(colab_clf_rf_rg_rb_hsv, '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/colab_clf_rf_rg_rb_hsv.joblib')
print(f"Tempo per addestrare il modello Random Forest (con max_depth=10 e n_estimators=100): {time_rf:.2f} secondi")
Tempo per addestrare il modello Random Forest (con max_depth=10 e n_estimators=100): 348.64 secondi
Codice
# Inizializza l'animazione per la previsione
animation = Animation(message="Previsione sul test set", interval=0.5)
animation.start()
# Previsione sul test set e misura del tempo
y_test_pred, time_test_pred = measure_time(colab_clf_rf_rg_rb_hsv.predict, X_test_scaled)
# Ferma l'animazione
animation.stop()
# Valutazione del modello sul test set
print("Report di classificazione sul Test Set:")
print(classification_report(y_test, y_test_pred))
print(f"Accuratezza Test: {accuracy_score(y_test, y_test_pred)*100:.2f}%")
print(f"Tempo per prevedere sul test set: {time_test_pred:.2f} secondi")
Report di classificazione sul Test Set:
precision recall f1-score support
0 0.80 0.98 0.88 6212517
1 0.74 0.20 0.31 1921075
accuracy 0.79 8133592
macro avg 0.77 0.59 0.59 8133592
weighted avg 0.78 0.79 0.74 8133592
Accuratezza Test: 79.36%
Tempo per prevedere sul test set: 4.60 secondi
Codice
# Inizializza l'animazione per la previsione sul validation set
animation = Animation(message="Previsione sul validation set", interval=0.5)
animation.start()
# Previsione sul validation set e misura del tempo
y_val_pred, time_val_pred = measure_time(colab_clf_rf_rg_rb_hsv.predict, X_val_scaled)
# Ferma l'animazione
animation.stop()
# Valutazione del modello sul validation set
print("Report di classificazione sul Validation Set:")
print(classification_report(y_val, y_val_pred))
print(f"Accuratezza Validation: {accuracy_score(y_val, y_val_pred)*100:.2f}%")
print(f"Tempo per prevedere sul validation set: {time_val_pred:.2f} secondi")
Report di classificazione sul Validation Set:
precision recall f1-score support
0 0.80 0.98 0.88 6212517
1 0.74 0.20 0.31 1921075
accuracy 0.79 8133592
macro avg 0.77 0.59 0.59 8133592
weighted avg 0.78 0.79 0.74 8133592
Accuratezza Validation: 79.36%
Tempo per prevedere sul validation set: 4.60 secondi
Codice
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
def plot_confusion_matrix(y_true, y_pred, model_name):
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(6, 4))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False)
plt.title(f"Matrice di Confusione - {model_name}")
plt.xlabel('Previsione')
plt.ylabel('Reale')
plt.show()
print(f"\nMatrice di Confusione - {model_name}:")
print(cm)
Codice
plot_confusion_matrix(y_test, y_test_pred, model_name="Random Forest Test Set")
Matrice di Confusione - Random Forest Test Set: [[6076718 135799] [1543253 377822]]
Ho provato ad ottimizzare gli iperparametri con GridSearchCV e RandomizedSearchCV ma purtroppo non ho avuto nessun miglioramneto.
GridSearchCV
Codice
# Definizione della griglia degli iperparametri
param_grid = {
'n_estimators': [50, 80, 110],
'max_depth': [2, 3, 4],
'min_samples_split': [14, 18, 22],
'min_samples_leaf': [3, 4, 5],
'bootstrap': [True, False]
}
scoring = {
'f1': 'f1',
'accuracy': 'accuracy',
'precision': 'precision',
'recall': 'recall'
}
# Inizializzazione GridSearchCV con RandomForestClassifier
grid_search = GridSearchCV(
estimator=RandomForestClassifier(
random_state=42,
n_jobs=-1,
class_weight='balanced' # Manteniamo il bilanciamento delle classi
),
param_grid=param_grid,
cv=5, # Numero di fold per la cross-validazione
n_jobs=-1, # Utilizza tutti i core disponibili
verbose=2, # Livello di verbosità
scoring=scoring, # Specifica le metriche di scoring
refit='f1' # Indica quale metrica usare per rifitare il modello
)
Codice
# Inizializzazione l'animazione per Grid Search
animation = Animation(message="Ottimizzazione con Grid Search", interval=0.5)
animation.start()
# Addestramento del modello con Grid Search e misura del tempo
grid_search, time_grid_search = measure_time(grid_search.fit, X_train_scaled, y_train)
animation.stop()
print(f"Tempo per Grid Search: {time_grid_search:.2f} secondi")
# Migliori parametri trovati
print("Migliori parametri trovati con Grid Search:")
print(grid_search.best_params_)
clf_rf_gridsearch = grid_search.best_estimator_
model_save_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/clf_rf_rg_rb_hsv_gridsearch.joblib'
dump(clf_rf_gridsearch, model_save_path)
print(f"Migliore modello Random Forest addestrato e salvato in: {model_save_path}")
RandomizedSearchCV
Codice
param_dist = {
'n_estimators': [10, 30, 50],
'max_depth': [10, 20, None],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
'bootstrap': [True, False]
}
scoring = {
'f1': 'f1',
'accuracy': 'accuracy',
'precision': 'precision',
'recall': 'recall'
}
# Inizializza RandomizedSearchCV con RandomForestClassifier
random_search = RandomizedSearchCV(
estimator=RandomForestClassifier(
random_state=42,
n_jobs=-1,
class_weight='balanced' # Manteniamo il bilanciamento delle classi
),
param_distributions=param_dist,
n_iter=50, # Numero di combinazioni casuali da esplorare
cv=3, # Numero di fold per la cross-validazione (ridotto per risparmiare risorse)
n_jobs=-1, # Utilizza tutti i core disponibili
verbose=2, # Livello di verbosità
scoring=scoring, # Specifica le metriche di scoring
refit='f1', # Indica quale metrica usare per rifitare il modello
random_state=42
)
Codice
animation = Animation(message="Ottimizzazione con Randomized Search", interval=0.5)
animation.start()
random_search, time_random_search = measure_time(random_search.fit, X_train_scaled, y_train)
animation.stop()
print(f"Tempo per Randomized Search: {time_random_search:.2f} secondi")
print("Migliori parametri trovati con Randomized Search:")
print(random_search.best_params_)
clf_rf_randomsearch = random_search.best_estimator_
model_save_path = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/clf_rf_rg_rb_hsv_randomizedsearch.joblib'
dump(clf_rf_randomsearch, model_save_path)
print(f"Migliore modello Random Forest addestrato e salvato in: {model_save_path}")
Codice
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.utils.class_weight import compute_class_weight
from joblib import dump
import numpy as np
from tqdm import tqdm
import time
class ChunkedDataProcessor:
def __init__(self, chunk_size=1000):
self.chunk_size = chunk_size
self.scaler = StandardScaler()
def count_classes(self, y, chunk_size=10000):
"""Conta le classi in chunks per evitare di caricare tutto in memoria"""
unique_classes = set()
class_counts = {}
for i in range(0, len(y), chunk_size):
chunk = y[i:min(i + chunk_size, len(y))]
chunk_unique = np.unique(chunk)
for cls in chunk_unique:
unique_classes.add(cls)
if cls not in class_counts:
class_counts[cls] = 0
class_counts[cls] += np.sum(chunk == cls)
return sorted(list(unique_classes)), class_counts
def create_stratified_indices(self, y, test_size=0.3, chunk_size=10000):
"""Crea indici per split stratificato processando in chunks"""
print("Conteggio delle classi...")
classes, class_counts = self.count_classes(y, chunk_size)
# Inizializza dizionari per gli indici
train_indices = []
test_indices = []
print("Creazione indici stratificati...")
for i in tqdm(range(0, len(y), chunk_size)):
chunk_y = y[i:min(i + chunk_size, len(y))]
chunk_indices = np.arange(i, min(i + chunk_size, len(y)))
for cls in classes:
cls_indices = chunk_indices[chunk_y == cls]
n_samples = len(cls_indices)
if n_samples > 0:
n_test = int(test_size * n_samples)
np.random.shuffle(cls_indices)
test_indices.extend(cls_indices[:n_test])
train_indices.extend(cls_indices[n_test:])
return np.array(train_indices), np.array(test_indices)
def process_chunk(self, X_chunk, y_chunk, is_first_chunk=False):
"""Processa un singolo chunk di dati"""
if is_first_chunk:
X_chunk_scaled = self.scaler.fit_transform(X_chunk)
else:
X_chunk_scaled = self.scaler.transform(X_chunk)
return X_chunk_scaled, y_chunk
def split_data(self, X, y):
"""Split dei dati in train, validation e test"""
print("Creazione split train/val/test...")
# Primo split per separare il training set
train_idx, temp_idx = self.create_stratified_indices(y, test_size=0.3, chunk_size=self.chunk_size)
# Split dei dati rimanenti in validation e test
temp_y = y[temp_idx]
val_idx_local, test_idx_local = self.create_stratified_indices(temp_y, test_size=0.5, chunk_size=self.chunk_size)
# Converti gli indici locali in indici globali per validation e test
val_idx = temp_idx[val_idx_local]
test_idx = temp_idx[test_idx_local]
return train_idx, val_idx, test_idx
def train_random_forest(self, X, y, model_path):
"""Addestra il Random Forest usando chunks di dati"""
print("Iniziando il processamento dei dati...")
start_time = time.time()
# Split dei dati
train_indices, val_indices, test_indices = self.split_data(X, y)
# Inizializza il modello
rf_model = RandomForestClassifier(
n_estimators=50,
max_depth=5,
random_state=42,
n_jobs=-1,
verbose=1
)
# Processa e addestra in chunks
print("Processamento e addestramento in chunks...")
X_processed = []
y_processed = []
for i in tqdm(range(0, len(train_indices), self.chunk_size)):
chunk_indices = train_indices[i:min(i + self.chunk_size, len(train_indices))]
X_chunk = X[chunk_indices]
y_chunk = y[chunk_indices]
# Processa il chunk
X_chunk_scaled, y_chunk = self.process_chunk(
X_chunk, y_chunk, is_first_chunk=(i == 0)
)
X_processed.append(X_chunk_scaled)
y_processed.append(y_chunk)
# Concatena tutti i chunks processati
X_train_scaled = np.vstack(X_processed)
y_train = np.concatenate(y_processed)
# Addestra il modello
print("Addestramento del modello...")
rf_model.fit(X_train_scaled, y_train)
# Salva il modello
print("Salvando il modello...")
dump(rf_model, model_path)
total_time = time.time() - start_time
print(f"Tempo totale di esecuzione: {total_time:.2f} secondi")
return rf_model, self.scaler
# Uso del codice
CHUNK_SIZE = 1000 # Riduci se hai ancora problemi di memoria
MODEL_PATH = '/content/drive/Othercomputers/Il mio laptop/Desktop/face_detection/prova/colab_clf_rf_rg_rb_hsv_due.joblib'
processor = ChunkedDataProcessor(chunk_size=CHUNK_SIZE)
model, scaler = processor.train_random_forest(X, y, MODEL_PATH)
Visualizzazione delle Distribuzioni dei Canali R G B
https://en.wikipedia.org/wiki/Color_histogram
https://www.baeldung.com/cs/image-histograms
Codice
# Percorso della cartella delle immagini
skin_ppm_folder = 'C:/Users/gabri/Desktop/face_detection/prova/face_PPM'
# Seleziono tutti i file PPM nella cartella
sample_files = [file for file in os.listdir(skin_ppm_folder) if file.lower().endswith('.ppm')]
if sample_files:
# Inizializzo gli istogrammi accumulati per R, G e B
hist_R_accum = np.zeros(256)
hist_G_accum = np.zeros(256)
hist_B_accum = np.zeros(256)
# Itero su tutti i file PPM e accumula gli istogrammi
for file in sample_files:
sample_ppm_path = os.path.join(skin_ppm_folder, file)
try:
image = read_ppm(sample_ppm_path)
# Estrazione dei canali
R = image[:, :, 0].flatten()
G = image[:, :, 1].flatten()
B = image[:, :, 2].flatten()
# Calcolo gli istogrammi per R, G e B con 256 bin, normalizzati a unità
# np.histogram() calcola l'istogramma di un array di valori
# bins=256 indica il numero di intervalli in cui i valori del canale di colore vengono suddivisi, quante volte ciascun valore di intensità appare nell'immagine
# density=True serve per normalizzare l'istogramma
hist_R, _ = np.histogram(R, bins=256, range=(0, 255), density=True)
hist_G, _ = np.histogram(G, bins=256, range=(0, 255), density=True)
hist_B, _ = np.histogram(B, bins=256, range=(0, 255), density=True)
hist_R_accum += hist_R
hist_G_accum += hist_G
hist_B_accum += hist_B
except Exception as e:
print(f"Errore nel processare il file {sample_ppm_path}: {e}")
hist_R_avg = hist_R_accum / len(sample_files)
hist_G_avg = hist_G_accum / len(sample_files)
hist_B_avg = hist_B_accum / len(sample_files)
# Converto le distribuzioni in percentuali
hist_R_avg_percent = hist_R_avg * 100
hist_G_avg_percent = hist_G_avg * 100
hist_B_avg_percent = hist_B_avg * 100
plt.figure(figsize=(18, 12)) # Imposto la dimensione della figura per due righe
# Primo grafico per il canale R
plt.subplot(2, 2, 1)
plt.plot(hist_R_avg_percent, color='red')
plt.fill_between(range(256), hist_R_avg_percent, color='red', alpha=0.3)
plt.title('Distribuzione Media del Canale R')
plt.xlabel('Valore di Intensità')
plt.ylabel('Percentuale (%)')
plt.grid(True)
# Secondo grafico per il canale G
plt.subplot(2, 2, 2)
plt.plot(hist_G_avg_percent, color='green')
plt.fill_between(range(256), hist_G_avg_percent, color='green', alpha=0.3)
plt.title('Distribuzione Media del Canale G')
plt.xlabel('Valore di Intensità')
plt.ylabel('Percentuale (%)')
plt.grid(True)
# Terzo grafico per il canale B
plt.subplot(2, 2, 3)
plt.plot(hist_B_avg_percent, color='blue')
plt.fill_between(range(256), hist_B_avg_percent, color='blue', alpha=0.3)
plt.title('Distribuzione Media del Canale B')
plt.xlabel('Valore di Intensità')
plt.ylabel('Percentuale (%)')
plt.grid(True)
# Quarto grafico per i canali sovrapposti
plt.subplot(2, 2, 4)
plt.plot(hist_R_avg_percent, color='red', label='R', alpha=0.7)
plt.plot(hist_G_avg_percent, color='green', label='G', alpha=0.7)
plt.plot(hist_B_avg_percent, color='blue', label='B', alpha=0.7)
plt.fill_between(range(256), hist_R_avg_percent, color='red', alpha=0.2)
plt.fill_between(range(256), hist_G_avg_percent, color='green', alpha=0.2)
plt.fill_between(range(256), hist_B_avg_percent, color='blue', alpha=0.2)
plt.title('Distribuzione Media dei Canali R, G, B')
plt.xlabel('Valore di Intensità')
plt.ylabel('Percentuale (%)')
plt.legend()
plt.grid(True)
plt.tight_layout() # Ottimizza la disposizione dei grafici
plt.show()
Codice
model_colab_clf_rf_rg_rb_hsv_path = 'C:/Users/gabri/Desktop/face_detection/prova/colab_clf_rf_rg_rb_hsv.joblib'
colab_clf_rf_rg_rb_hsv = load(model_colab_clf_rf_rg_rb_hsv_path)
print("Parametri del modello caricato:")
print(colab_clf_rf_rg_rb_hsv.get_params())
Parametri del modello caricato:
{'bootstrap': True, 'ccp_alpha': 0.0, 'class_weight': None, 'criterion': 'gini', 'max_depth': 10, 'max_features': 'sqrt', 'max_leaf_nodes': None, 'max_samples': None, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100, 'n_jobs': -1, 'oob_score': False, 'random_state': 42, 'verbose': 0, 'warm_start': False}
Funzioni per selezionare e caricare le immagini dal pc
Codice
def select_image():
try:
root = tk.Tk()
root.withdraw() # Nasconde la finestra principale di Tkinter
file_path = filedialog.askopenfilename(
title="Seleziona un'immagine",
filetypes=[("Image Files", "*.jpg;*.jpeg;*.ppm;*.png;*.bmp;*.tiff")]
)
root.destroy() # Chiude la finestra di Tkinter dopo la selezione
if file_path:
# Normalizzo il percorso per evitare problemi con i separatori di slash
file_path = os.path.normpath(file_path)
print(f"Percorso immagine selezionato: {file_path}")
return file_path
else:
print("Nessuna immagine selezionata.")
return None
except Exception as e:
print(f"Errore nella selezione dell'immagine: {e}")
return None
Codice
def load_image(image_path):
image = plt.imread(image_path)
# Se l'immagine è in formato intero converto a uint8
if image.dtype != np.uint8:
image = (image * 255).astype(np.uint8)
# Se l'immagine ha un canale alpha viene rimosso
if image.shape[-1] == 4:
image = image[:, :, :3]
return image
Converto il singolo file caricato dal pc in .ppm
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imsave.html
Codice
def convert_single_jpg_to_ppm(input_image_path, output_ppm_path):
try:
# Verifico se il percorso del file di input esiste e sia un file
if not os.path.isfile(input_image_path):
print(f"Errore: Il percorso specificato non è un file valido: {input_image_path}")
return
img = plt.imread(input_image_path)
plt.imsave(output_ppm_path, img)
print(f"Convertita: {input_image_path} -> {output_ppm_path}")
except Exception as e:
print(f"Errore nella conversione dell'immagine a PPM: {e}")
K-Means
Questa funzione applica K-Means ai pixel classificati come 'volti' per raggrupparli in cluster e identificare ovali verticali.
https://www.geeksforgeeks.org/convex-hull-monotone-chain-algorithm/?ref=oin_asr2
Codice
def cluster_face_pixels(mask, n_clusters=5):
# mask (ndarray) maschera binaria con 1 per volti rilevati e 0 altrimenti
# n_clusters (int) numero di cluster (volti) da identificare
# Returns:
# list: Lista delle coordinate dei bounding box dei volti rilevati
try:
# Estraggo le coordinate dei pixel classificati come 'volti'
# np.column_stack trasforma le coordinate in un array di coordinate
# face_pixels[:, 1] inverte l'ordine delle colonne e righe
# questo perchè in molti framework di elaborazione delle immagini le coordinate degli pixel sono espresse
# nel formato - riga (y), colonna(x) -
# gli algoritmi di visione artificiale richiedono le coordinate in formato - colonna(x), riga(y) -
face_pixels = np.column_stack(np.where(mask == 1))
face_pixels = np.column_stack((face_pixels[:, 1], face_pixels[:, 0]))
if len(face_pixels) == 0:
print("Nessun pixel classificato come volto.")
return []
# Standardizzazione dei dati
scaler = StandardScaler()
face_pixels_scaled = scaler.fit_transform(face_pixels)
# Applico K-Means
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
labels = kmeans.fit_predict(face_pixels_scaled)
bounding_boxes = []
for cluster_label in range(n_clusters):
cluster_points = face_pixels[labels == cluster_label]
if len(cluster_points) < 5:
continue
# Analisi della forma del cluster
try:
hull = ConvexHull(cluster_points)
hull_points = cluster_points[hull.vertices]
# Calcolo l'area del Convex Hull
# Convex Hull è il più piccolo poligono convesso che contiene tutti i punti
hull_area = hull.volume
# Calcolo l'area rettangolare
min_x, min_y = np.min(cluster_points, axis=0)
max_x, max_y = np.max(cluster_points, axis=0)
rect_area = (max_x - min_x) * (max_y - min_y)
# Calcolo l'aspect ratio
aspect_ratio = (max_x - min_x) / (max_y - min_y) if (max_y - min_y) != 0 else 0
# Calcolo la copertura dell'area
coverage = hull_area / rect_area if rect_area != 0 else 0
# Criteri per identificare un ovale verticalmente orientato
if aspect_ratio > 1.1 and coverage > 0.5:
bounding_boxes.append((min_x, min_y, max_x, max_y))
print(f"Cluster {cluster_label}: bounding box (x_min={min_x}, y_min={min_y}, x_max={max_x}, y_max={max_y}), aspect_ratio={aspect_ratio:.2f}, coverage={coverage:.2f}")
except Exception as e:
print(f"Errore nell'analisi del cluster {cluster_label}: {e}")
continue
if not bounding_boxes:
print("Nessun volto ovale rilevato dopo clustering.")
return bounding_boxes
except Exception as e:
print(f"Errore nel clustering: {e}")
return []
Classificazione dei pixel
Classificazione dei pixel di un'immagine come pelle o non-pelle utilizzando il modello RandomForest
Codice
def classify_pixels(image, colab_clf_rf_rg_rb_hsv, L_i_rg=0.9, L_s_rg=1.4, b_onR=1.2):
# image (np.ndarray): Immagine in formato NumPy array
# colab_clf_rf_rg_rb_hsv (RandomForestClassifier), modello addestrato
# L_i_rg (float): Limite inferiore per il rapporto R/G
# L_s_rg (float): Limite superiore per il rapporto R/G
# b_onR (float): Tolleranza per il rapporto B_value = R / 3
# np.ndarray: Maschera binaria con 1 per pelle e 0 per non-pelle.
try:
# Estraggo i canali R, G e B
R = image[:, :, 0].astype(float)
G = image[:, :, 1].astype(float)
B = image[:, :, 2].astype(float)
# Evito divisioni per zero
G[G == 0] = 1.0
B[B == 0] = 1.0
# Calcolo i rapporti R/G e R/3
R_G_ratio = R / G
B_value = R / 3
# Calcolo i limiti basati su B_value con la tolleranza b_onR
L_i_b = B_value - b_onR
L_s_b = B_value + b_onR
# Applico i limiti sul rapporto R/G e R/B
mask_rg_limits = (R_G_ratio > L_i_rg) & (R_G_ratio < L_s_rg)
mask_rb_limits = (B_value > L_i_b) & (B_value < L_s_b)
valid_pixels = mask_rg_limits & mask_rb_limits
# Calcolo i valori H, S e V
H, S, V = rgb_to_hsv_manual(R, G, B)
# np.stack combina tutte le feature calcolate in un singolo array lungo l'ultimo asse axis=-1
# i singoli array (rapporti e componenti di colore) formano un array multidimensionale
features = np.stack((R_G_ratio, B_value, H, S, V), axis=-1).reshape(-1, 5)
# Seleziono solo i pixel validi per la classificazione
features_filtered = features[valid_pixels.flatten()]
# Verifico se ci sono pixel validi da classificare
if features_filtered.shape[0] == 0:
print("Nessun pixel valido da classificare.")
return np.zeros(R_G_ratio.shape, dtype=np.uint8)
# Classifico i pixel validi
predictions = colab_clf_rf_rg_rb_hsv.predict(features_filtered)
# Creo una maschera con 0 e 1 per rappresentare pelle e non-pelle
mask = np.zeros(R_G_ratio.shape, dtype=np.uint8)
mask[valid_pixels] = predictions
num_face_pixels = np.sum(mask == 1)
print(f"Numero di pixel classificati come volti: {num_face_pixels}")
return mask
except Exception as e:
print(f"Errore nella classificazione dei pixel: {e}")
return None
Sliding window
Lo sliding_window genera delle finestre mobili sull'immagine
https://www.geeksforgeeks.org/window-sliding-technique/
Codice
def sliding_window(image, step_size, window_size):
# image (ndarray) l'immagine su cui applicare lo sliding_window
# step_size (int) dimensione del passo per la finestra
# window_size (tuple) dimensioni della finestra (larghezza, altezza)
# Returns:
# list: un elenco di finestre mobili come tuple (x, y, window_size, image_window)
windows = []
for y in range(0, image.shape[0] - window_size[1] + 1, step_size):
for x in range(0, image.shape[1] - window_size[0] + 1, step_size):
image_window = image[y:y + window_size[1], x:x + window_size[0]]
windows.append((x, y, window_size, image_window))
return windows
Funzione per disegnare i rettangoli verdi intorno ai volti
Codice
def draw_bounding_boxes(image, bounding_boxes, save_path=None):
try:
fig, ax = plt.subplots(figsize=(10, 10))
ax.imshow(image)
for box in bounding_boxes:
x_min, y_min, x_max, y_max = box
width = x_max - x_min
height = y_max - y_min
# Crea un rettangolo verde
rect = Rectangle((x_min, y_min), width, height, linewidth=2, edgecolor='green', facecolor='none')
ax.add_patch(rect)
plt.axis('off')
plt.title('Immagine con Volti Rilevati')
if save_path:
# Normalizzo il percorso per evitare problemi con gli slash
save_path = os.path.normpath(save_path)
plt.savefig(save_path, bbox_inches='tight', pad_inches=0)
print(f"Immagine con bounding box salvata in: {save_path}")
plt.show()
except Exception as e:
print(f"Errore nel disegnare i bounding box: {e}")
Questa funzione processa l'immagine caricata dal pc, rileva i volti utilizzando il modello addestrato e applica K-Means per identificare gli ovali verticali
Visualizza e salva i risultati senza utilizzare lo sliding window.
Codice
def process_image(image_path=None, output_folder='C:/temp',
model_folder='C:/Users/gabri/Desktop/face_detection/prova',
scale_percent=15, n_clusters=3):
#image_path (str, optional) percorso dell'immagine caricata
#output_folder (str) cartella dove salvare i risultati
#model_folder (str) cartella dove si trova il modello addestrato
#scale_percent (float): Percentuale di ridimensionamento.
#n_clusters (int) numero di cluster (volti) da identificare
#Returns:
#list: Lista delle coordinate dei bounding box
if image_path is None:
image_path = select_image()
if not image_path:
print("Nessuna immagine selezionata.")
return []
# Definisco i percorsi per la conversione
base_name = os.path.splitext(os.path.basename(image_path))[0]
ppm_image_path = os.path.join(output_folder, f"{base_name}.ppm")
resized_image_path = os.path.join(output_folder, f"{base_name}_resized.ppm")
result_image_path = os.path.join(output_folder, f"{base_name}_result.png")
if not os.path.exists(output_folder):
try:
os.makedirs(output_folder)
print(f"Cartella di output creata: {output_folder}")
except Exception as e:
print(f"Errore nella creazione della cartella di output: {e}")
return []
# Converto JPG a PPM
if not image_path.lower().endswith('.ppm'):
try:
convert_single_jpg_to_ppm(image_path, ppm_image_path)
print(f"Convertita: {image_path} -> {ppm_image_path}")
except Exception as e:
print(f"Errore nella conversione dell'immagine a PPM: {e}")
return []
else:
ppm_image_path = image_path
# Leggo l'immagine PPM
try:
image = read_ppm(ppm_image_path)
except Exception as e:
print(f"Errore nella lettura dell'immagine PPM: {e}")
return []
if image is None:
print("Impossibile leggere l'immagine PPM.")
return []
# Effettuo binning e ridimensionamento per l'immagine caricata
resized_image = resize_image_binning(image, scale_percent)
if resized_image is None:
print("Errore nel ridimensionamento dell'immagine.")
return []
# Salvo l'immagine ridimensionata come PPM
try:
save_ppm(resized_image_path, resized_image)
print(f"Immagine ridimensionata salvata in: {resized_image_path}")
except Exception as e:
print(f"Errore nel salvataggio dell'immagine ridimensionata: {e}")
return []
model_filename = 'colab_clf_rf_rg_rb_hsv.joblib'
model_path = os.path.join(model_folder, model_filename)
if not os.path.exists(model_path):
print(f"Il modello '{model_path}' non esiste.")
return []
try:
colab_clf_rf_rg_rb_hsv = load(model_path)
print(f"Modello '{model_path}' caricato con successo.")
except Exception as e:
print(f"Errore nel caricamento del modello: {e}")
return []
# Classifico i pixel usando le 4 features
mask = classify_pixels(resized_image, colab_clf_rf_rg_rb_hsv)
if mask is None:
print("Impossibile classificare i pixel.")
return []
# Visualizzo la maschera binaria
plt.figure(figsize=(10, 10))
plt.imshow(mask, cmap='gray')
plt.title('Maschera Binaria dei Pixel Classificati')
plt.axis('off')
plt.show()
# Applico il clustering con K-Means per identificare ovali verticali
refined_bounding_boxes = cluster_face_pixels(mask, n_clusters=n_clusters)
if not refined_bounding_boxes:
print("Nessun volto ovale rilevato dopo clustering.")
plt.figure(figsize=(10, 10))
plt.imshow(resized_image)
plt.axis('off')
plt.title('Immagine Senza Volti Rilevati')
plt.show()
return []
# Stampo le coordinate dei bounding box
print("Volti ovali rilevati:")
for idx, box in enumerate(refined_bounding_boxes, start=1):
x_min, y_min, x_max, y_max = box
print(f"Volto {idx}: (x_min={x_min}, y_min={y_min}, x_max={x_max}, y_max={y_max})")
# Disegno i bounding box sull'immagine e salvalo
try:
draw_bounding_boxes(resized_image, refined_bounding_boxes, save_path=result_image_path)
print(f"Immagine con bounding box salvata in: {result_image_path}")
except Exception as e:
print(f"Errore nel disegnare o salvare i bounding box: {e}")
return []
return refined_bounding_boxes
La funzione "non_maximum_suppression" rimuove rilevamenti duplicati o sovrapposti.
La funzione "iou" prende le coordinate di due box, calcola l'area di intersezione e la divide per l'area di unione.
https://kikaben.com/object-detection-non-maximum-suppression/
Codice
def non_maximum_suppression(detections, threshold=0.5):
# detections è un elenco di riquadri di delimitazione, ciascuno associato a un punteggio
# threshold è la soglia iou per il filtraggio delle caselle sovrapposte
if len(detections) == 0:
return []
# Ordino le finestre basate sul punteggio
detections = sorted(detections, key=lambda x: x[2], reverse=True)
final_detections = []
while detections:
chosen_box = detections.pop(0)# seleziono la casella con il punteggio più alto e la rimuovo
final_detections.append(chosen_box)
# Applico la funzione IoU a ogni rilevamento rimanente filtrando le caselle
detections = [box for box in detections if iou(chosen_box, box) < threshold]
return final_detections
def iou(box1, box2):
# calcolo l'intersezione tra due bounding box
x1, y1, w1, h1 = box1[:4]
x2, y2, w2, h2 = box2[:4]
#Trovo il rettangolo di intersezione calcolando il massimo delle coordinate in alto a sinistra ( xi1, yi1)
# e il minimo delle coordinate in basso a destra ( xi2, yi2).
xi1 = max(x1, x2)
yi1 = max(y1, y2)
xi2 = min(x1 + w1, x2 + w2)
yi2 = min(y1 + h1, y2 + h2)
# area dell'intersezione
inter_area = max(0, xi2 - xi1) * max(0, yi2 - yi1)
box1_area = w1 * h1
box2_area = w2 * h2
union_area = box1_area + box2_area - inter_area
return inter_area / union_area
Visualizzazione e salvataggio dei risultati utilizzando lo sliding window
Codice
def process_image_sliding_window(image_path=None, output_folder='C:/temp',
model_folder='C:/Users/gabri/Desktop/face_detection/prova',
scale_percent=15, n_clusters=1, iou_threshold=0.5,
step_size=128, window_size=(128, 128)):
if image_path is None:
image_path = select_image()
if not image_path:
print("Nessuna immagine selezionata.")
return []
# Percorsi per la conversione
base_name = os.path.splitext(os.path.basename(image_path))[0]
ppm_image_path = os.path.join(output_folder, f"{base_name}.ppm")
resized_image_path = os.path.join(output_folder, f"{base_name}_resized.ppm")
result_image_path = os.path.join(output_folder, f"{base_name}_result.png")
if not os.path.exists(output_folder):
try:
os.makedirs(output_folder)
print(f"Cartella di output creata: {output_folder}")
except Exception as e:
print(f"Errore nella creazione della cartella di output: {e}")
return []
# Converto JPG a PPM se necessario
if not image_path.lower().endswith('.ppm'):
try:
convert_single_jpg_to_ppm(image_path, ppm_image_path)
print(f"Convertita: {image_path} -> {ppm_image_path}")
except Exception as e:
print(f"Errore nella conversione dell'immagine a PPM: {e}")
return []
else:
ppm_image_path = image_path
try:
image = read_ppm(ppm_image_path)
except Exception as e:
print(f"Errore nella lettura dell'immagine PPM: {e}")
return []
if image is None:
print("Impossibile leggere l'immagine PPM.")
return []
# Effettuo binning e ridimensionamento per l'immagine corrente
resized_image = resize_image_binning(image, scale_percent)
if resized_image is None:
print("Errore nel ridimensionamento dell'immagine.")
return []
try:
save_ppm(resized_image_path, resized_image)
print(f"Immagine ridimensionata salvata in: {resized_image_path}")
except Exception as e:
print(f"Errore nel salvataggio dell'immagine ridimensionata: {e}")
return []
model_filename = 'colab_clf_rf_rg_rb_hsv.joblib'
model_path = os.path.join(model_folder, model_filename)
if not os.path.exists(model_path):
print(f"Il modello '{model_path}' non esiste.")
return []
try:
colab_clf_rf_rg_rb_hsv = load(model_path)
print(f"Modello '{model_path}' caricato con successo.")
except Exception as e:
print(f"Errore nel caricamento del modello: {e}")
return []
# Applico sliding window per ottenere le regioni di interesse
sliding_windows = sliding_window(resized_image, step_size, window_size)
# Lista per memorizzare i bounding box
all_bounding_boxes = []
for window in sliding_windows:
# window è una tupla: (x, y, window_size, image_window)
x, y, window_size, image_window = window
# Classifico i pixel per ogni finestra
mask = classify_pixels(image_window, colab_clf_rf_rg_rb_hsv)
bounding_boxes = cluster_face_pixels(mask, n_clusters=n_clusters)
all_bounding_boxes.extend(bounding_boxes)
# Applico Non-Maximum Suppression
final_bounding_boxes = non_maximum_suppression(all_bounding_boxes, iou_threshold)
if not final_bounding_boxes:
print("Nessun volto ovale rilevato dopo clustering e non-maximum suppression.")
plt.figure(figsize=(10, 10))
plt.imshow(resized_image)
plt.axis('off')
plt.title('Immagine Senza Volti Rilevati')
plt.show()
return []
# Stampo le coordinate dei bounding box
print("Volti ovali rilevati:")
for idx, box in enumerate(final_bounding_boxes, start=1):
x_min, y_min, x_max, y_max = box
print(f"Volto {idx}: (x_min={x_min}, y_min={y_min}, x_max={x_max}, y_max={y_max})")
try:
draw_bounding_boxes(resized_image, final_bounding_boxes, save_path=result_image_path)
print(f"Immagine con bounding box salvata in: {result_image_path}")
except Exception as e:
print(f"Errore nel disegnare o salvare i bounding box: {e}")
return []
return final_bounding_boxes
Codice
def save_ppm(ppm_path, img):
try:
height, width, channels = img.shape
if channels != 3:
raise ValueError("L'immagine deve avere 3 canali (RGB).")
with open(ppm_path, 'wb') as f:
# Scrivi l'header PPM
header = f"P6\n{width} {height}\n255\n"
f.write(header.encode())
# Scrivi i dati RGB
f.write(img.tobytes())
print(f"Immagine PPM salvata in: {ppm_path}")
except Exception as e:
print(f"Errore nel salvataggio dell'immagine PPM: {e}")
Codice
process_image()
Percorso immagine selezionato: H:\gap\pp\foto\IMG_20210204_114843.jpg Convertita: H:\gap\pp\foto\IMG_20210204_114843.jpg -> C:/temp\IMG_20210204_114843.ppm Convertita: H:\gap\pp\foto\IMG_20210204_114843.jpg -> C:/temp\IMG_20210204_114843.ppm Immagine PPM salvata in: C:/temp\IMG_20210204_114843_resized.ppm Immagine ridimensionata salvata in: C:/temp\IMG_20210204_114843_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo.
Cluster 2: bounding box (x_min=464, y_min=0, x_max=983, y_max=251), aspect_ratio=2.07, coverage=0.75 Cluster 4: bounding box (x_min=503, y_min=248, x_max=983, y_max=613), aspect_ratio=1.32, coverage=0.75 Volti ovali rilevati: Volto 1: (x_min=464, y_min=0, x_max=983, y_max=251) Volto 2: (x_min=503, y_min=248, x_max=983, y_max=613) Immagine con bounding box salvata in: C:\temp\IMG_20210204_114843_result.png
Immagine con bounding box salvata in: C:/temp\IMG_20210204_114843_result.png
Out[ ]:
[(np.int64(464), np.int64(0), np.int64(983), np.int64(251)), (np.int64(503), np.int64(248), np.int64(983), np.int64(613))]
Codice
process_image()
Percorso immagine selezionato: H:\gap\pp\foto\People\_MG_5726.jpg Convertita: H:\gap\pp\foto\People\_MG_5726.jpg -> C:/temp\_MG_5726.ppm Convertita: H:\gap\pp\foto\People\_MG_5726.jpg -> C:/temp\_MG_5726.ppm Immagine PPM salvata in: C:/temp\_MG_5726_resized.ppm Immagine ridimensionata salvata in: C:/temp\_MG_5726_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo.
Cluster 4: bounding box (x_min=173, y_min=207, x_max=611, y_max=546), aspect_ratio=1.29, coverage=0.70 Volti ovali rilevati: Volto 1: (x_min=173, y_min=207, x_max=611, y_max=546) Immagine con bounding box salvata in: C:\temp\_MG_5726_result.png
Immagine con bounding box salvata in: C:/temp\_MG_5726_result.png
Out[ ]:
[(np.int64(173), np.int64(207), np.int64(611), np.int64(546))]
Codice
process_image()
Percorso immagine selezionato: H:\gap\pp\foto\People\_MG_5701.jpg Convertita: H:\gap\pp\foto\People\_MG_5701.jpg -> C:/temp\_MG_5701.ppm Convertita: H:\gap\pp\foto\People\_MG_5701.jpg -> C:/temp\_MG_5701.ppm Immagine PPM salvata in: C:/temp\_MG_5701_resized.ppm Immagine ridimensionata salvata in: C:/temp\_MG_5701_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo.
Cluster 1: bounding box (x_min=0, y_min=219, x_max=803, y_max=546), aspect_ratio=2.46, coverage=0.74 Cluster 2: bounding box (x_min=0, y_min=0, x_max=459, y_max=325), aspect_ratio=1.41, coverage=0.84 Volti ovali rilevati: Volto 1: (x_min=0, y_min=219, x_max=803, y_max=546) Volto 2: (x_min=0, y_min=0, x_max=459, y_max=325) Immagine con bounding box salvata in: C:\temp\_MG_5701_result.png
Immagine con bounding box salvata in: C:/temp\_MG_5701_result.png
Out[ ]:
[(np.int64(0), np.int64(219), np.int64(803), np.int64(546)), (np.int64(0), np.int64(0), np.int64(459), np.int64(325))]
Codice
process_image()
Percorso immagine selezionato: H:\gap\pp\foto\People\IMG_1227.jpg Convertita: H:\gap\pp\foto\People\IMG_1227.jpg -> C:/temp\IMG_1227.ppm Convertita: H:\gap\pp\foto\People\IMG_1227.jpg -> C:/temp\IMG_1227.ppm Immagine PPM salvata in: C:/temp\IMG_1227_resized.ppm Immagine ridimensionata salvata in: C:/temp\IMG_1227_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo.
Cluster 0: bounding box (x_min=0, y_min=235, x_max=609, y_max=546), aspect_ratio=1.96, coverage=0.76 Cluster 2: bounding box (x_min=0, y_min=0, x_max=629, y_max=274), aspect_ratio=2.30, coverage=0.77 Volti ovali rilevati: Volto 1: (x_min=0, y_min=235, x_max=609, y_max=546) Volto 2: (x_min=0, y_min=0, x_max=629, y_max=274) Immagine con bounding box salvata in: C:\temp\IMG_1227_result.png
Immagine con bounding box salvata in: C:/temp\IMG_1227_result.png
Out[ ]:
[(np.int64(0), np.int64(235), np.int64(609), np.int64(546)), (np.int64(0), np.int64(0), np.int64(629), np.int64(274))]
Codice
process_image()
Percorso immagine selezionato: H:\gap\pp\foto\People\PSX_20180702_202231.jpg Convertita: H:\gap\pp\foto\People\PSX_20180702_202231.jpg -> C:/temp\PSX_20180702_202231.ppm Convertita: H:\gap\pp\foto\People\PSX_20180702_202231.jpg -> C:/temp\PSX_20180702_202231.ppm Immagine PPM salvata in: C:/temp\PSX_20180702_202231_resized.ppm Immagine ridimensionata salvata in: C:/temp\PSX_20180702_202231_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 59596
Cluster 0: bounding box (x_min=0, y_min=133, x_max=199, y_max=274), aspect_ratio=1.41, coverage=0.82 Cluster 2: bounding box (x_min=0, y_min=0, x_max=215, y_max=137), aspect_ratio=1.57, coverage=0.80 Volti ovali rilevati: Volto 1: (x_min=0, y_min=133, x_max=199, y_max=274) Volto 2: (x_min=0, y_min=0, x_max=215, y_max=137) Immagine con bounding box salvata in: C:\temp\PSX_20180702_202231_result.png
Immagine con bounding box salvata in: C:/temp\PSX_20180702_202231_result.png
Out[ ]:
[(np.int64(0), np.int64(133), np.int64(199), np.int64(274)), (np.int64(0), np.int64(0), np.int64(215), np.int64(137))]
Codice
process_image()
Percorso immagine selezionato: E:\gap\pp\foto\Startrail e luci Urbane\startrail faro.png Convertita: E:\gap\pp\foto\Startrail e luci Urbane\startrail faro.png -> C:/temp\startrail faro.ppm Convertita: E:\gap\pp\foto\Startrail e luci Urbane\startrail faro.png -> C:/temp\startrail faro.ppm Immagine PPM salvata in: C:/temp\startrail faro_resized.ppm Immagine ridimensionata salvata in: C:/temp\startrail faro_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 2261 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 8674 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 5928 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 4192 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 9435 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 3909 Nessun volto ovale rilevato dopo clustering. Nessun volto ovale rilevato dopo clustering e non-maximum suppression.
Out[ ]:
[]
Codice
process_image()
Percorso immagine selezionato: E:\gap\pp\foto\creare cartelle\ascignopng.jpg Convertita: E:\gap\pp\foto\creare cartelle\ascignopng.jpg -> C:/temp\ascignopng.ppm Convertita: E:\gap\pp\foto\creare cartelle\ascignopng.jpg -> C:/temp\ascignopng.ppm Immagine PPM salvata in: C:/temp\ascignopng_resized.ppm Immagine ridimensionata salvata in: C:/temp\ascignopng_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 16381 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16374 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16377 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16376 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16377 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16353 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16369 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16382 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16383 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 14052 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13979 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13000 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13168 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12990 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12360 Nessun volto ovale rilevato dopo clustering. Nessun volto ovale rilevato dopo clustering e non-maximum suppression.
Out[ ]:
[]
Codice
process_image_sliding_window()
Percorso immagine selezionato: H:\gap\pp\foto\creare cartelle\_MG_3911.jpg Convertita: H:\gap\pp\foto\creare cartelle\_MG_3911.jpg -> C:/temp\_MG_3911.ppm Convertita: H:\gap\pp\foto\creare cartelle\_MG_3911.jpg -> C:/temp\_MG_3911.ppm Immagine PPM salvata in: C:/temp\_MG_3911_resized.ppm Immagine ridimensionata salvata in: C:/temp\_MG_3911_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16226 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16370 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16381 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16353 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12893 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 4013 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13423 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 8951 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 9666 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12236 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13503 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16370 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12389 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13359 Nessun volto ovale rilevato dopo clustering. Nessun volto ovale rilevato dopo clustering e non-maximum suppression.
Out[ ]:
[]
Codice
process_image_sliding_window()
Percorso immagine selezionato: H:\gap\pp\foto\Astrofotografia reflex + 135mm\Andromeda galaxy.jpg Convertita: H:\gap\pp\foto\Astrofotografia reflex + 135mm\Andromeda galaxy.jpg -> C:/temp\Andromeda galaxy.ppm Convertita: H:\gap\pp\foto\Astrofotografia reflex + 135mm\Andromeda galaxy.jpg -> C:/temp\Andromeda galaxy.ppm Immagine PPM salvata in: C:/temp\Andromeda galaxy_resized.ppm Immagine ridimensionata salvata in: C:/temp\Andromeda galaxy_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 5631 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12094 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13795 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 11423 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 4751 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12645 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16106 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 15263 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 5487 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 11259 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 15107 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12326 Nessun volto ovale rilevato dopo clustering. Nessun volto ovale rilevato dopo clustering e non-maximum suppression.
Out[ ]:
[]
Codice
process_image_sliding_window()
Percorso immagine selezionato: H:\gap\pp\foto\People\_MG_5722.jpg Convertita: H:\gap\pp\foto\People\_MG_5722.jpg -> C:/temp\_MG_5722.ppm Convertita: H:\gap\pp\foto\People\_MG_5722.jpg -> C:/temp\_MG_5722.ppm Immagine PPM salvata in: C:/temp\_MG_5722_resized.ppm Immagine ridimensionata salvata in: C:/temp\_MG_5722_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 343 Cluster 0: bounding box (x_min=0, y_min=0, x_max=111, y_max=96), aspect_ratio=1.16, coverage=0.59 Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Numero di pixel classificati come volti: 2 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 208 Nessun volto ovale rilevato dopo clustering. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Numero di pixel classificati come volti: 222 Nessun volto ovale rilevato dopo clustering. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Numero di pixel classificati come volti: 81 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 319 Nessun volto ovale rilevato dopo clustering. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Numero di pixel classificati come volti: 296 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 30 Cluster 0: bounding box (x_min=1, y_min=85, x_max=53, y_max=127), aspect_ratio=1.24, coverage=0.78 Nessun pixel valido da classificare. Nessun pixel classificato come volto. Numero di pixel classificati come volti: 234 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 151 Cluster 0: bounding box (x_min=5, y_min=0, x_max=126, y_max=62), aspect_ratio=1.95, coverage=0.72 Numero di pixel classificati come volti: 12 Nessun volto ovale rilevato dopo clustering. Nessun pixel valido da classificare. Nessun pixel classificato come volto. Volti ovali rilevati: Volto 1: (x_min=5, y_min=0, x_max=126, y_max=62) Volto 2: (x_min=1, y_min=85, x_max=53, y_max=127) Immagine con bounding box salvata in: C:\temp\_MG_5722_result.png
Immagine con bounding box salvata in: C:/temp\_MG_5722_result.png
Out[ ]:
[(np.int64(5), np.int64(0), np.int64(126), np.int64(62)), (np.int64(1), np.int64(85), np.int64(53), np.int64(127))]
Codice
process_image_sliding_window()
Percorso immagine selezionato: H:\gap\pp\foto\Astrofotografia reflex + 135mm\slh12.png Convertita: H:\gap\pp\foto\Astrofotografia reflex + 135mm\slh12.png -> C:/temp\slh12.ppm Convertita: H:\gap\pp\foto\Astrofotografia reflex + 135mm\slh12.png -> C:/temp\slh12.ppm Immagine PPM salvata in: C:/temp\slh12_resized.ppm Immagine ridimensionata salvata in: C:/temp\slh12_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 5828 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 2472 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 965 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 926 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 1060 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 1194 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 619 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 232 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 260 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 722 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 265 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 253 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 906 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 1526 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 1882 Nessun volto ovale rilevato dopo clustering. Nessun volto ovale rilevato dopo clustering e non-maximum suppression.
Out[ ]:
[]
Codice
process_image_sliding_window()
Percorso immagine selezionato: H:\gap\pp\foto\Macro\Insetti\preda ragno _2.jpg Convertita: H:\gap\pp\foto\Macro\Insetti\preda ragno _2.jpg -> C:/temp\preda ragno _2.ppm Convertita: H:\gap\pp\foto\Macro\Insetti\preda ragno _2.jpg -> C:/temp\preda ragno _2.ppm Immagine PPM salvata in: C:/temp\preda ragno _2_resized.ppm Immagine ridimensionata salvata in: C:/temp\preda ragno _2_resized.ppm Modello 'C:/Users/gabri/Desktop/face_detection\colab_clf_rf_rg_rb_hsv.joblib' caricato con successo. Numero di pixel classificati come volti: 3970 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 15937 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 14231 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 115 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 1179 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 664 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 13204 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16355 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 4155 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 7401 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 6 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16384 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16338 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 8496 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 4223 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 7288 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 528 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16163 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 16383 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 12383 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 5902 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 3982 Nessun volto ovale rilevato dopo clustering. Numero di pixel classificati come volti: 2376 Nessun volto ovale rilevato dopo clustering. Nessun volto ovale rilevato dopo clustering e non-maximum suppression.
Out[ ]:
[]