Calcolatore Indice di Jaccard tra Immagini (MATLAB)
Calcola l’indice di similarità di Jaccard tra due immagini binarie utilizzando la metodologia MATLAB
Guida Completa: Calcolare l’Indice di Jaccard tra Due Immagini in MATLAB
L’indice di Jaccard (o coefficiente di Jaccard) è una metrica fondamentale per valutare la similarità tra due insiemi, ampiamente utilizzata nel campo dell’elaborazione delle immagini per confrontare immagini binarie. Questo articolo fornisce una guida dettagliata su come calcolare l’indice di Jaccard tra due immagini utilizzando MATLAB, con esempi pratici e considerazioni teoriche.
1. Fondamenti Teorici dell’Indice di Jaccard
L’indice di Jaccard (J) tra due insiemi A e B è definito come:
J(A,B) = |A ∩ B| / |A ∪ B|
Dove:
- |A ∩ B| rappresenta la cardinalità dell’intersezione tra A e B
- |A ∪ B| rappresenta la cardinalità dell’unione tra A e B
- Il valore risultante varia tra 0 (nessuna similarità) e 1 (identità perfetta)
Per le immagini binarie, gli insiemi A e B rappresentano i pixel con valore 1 (tipicamente il foreground) in ciascuna immagine.
2. Implementazione in MATLAB: Passo per Passo
Di seguito è riportato il processo dettagliato per calcolare l’indice di Jaccard tra due immagini in MATLAB:
-
Caricamento delle immagini:
img1 = imread('immagine1.png'); img2 = imread('immagine2.png');Assicurarsi che le immagini siano in formato binario (0 e 1) o applicare una soglia se necessario.
-
Conversione in binario (se necessario):
% Metodo di Otsu per immagini in scala di grigi level = graythresh(img1); bw1 = imbinarize(img1, level); level = graythresh(img2); bw2 = imbinarize(img2, level);
-
Calcolo dell’indice di Jaccard:
intersection = sum(bw1(:) & bw2(:)); union = sum(bw1(:) | bw2(:)); jaccard_index = intersection / union;
-
Visualizzazione dei risultati:
fprintf('Indice di Jaccard: %.4f\n', jaccard_index); fprintf('Distanza di Jaccard: %.4f\n', 1 - jaccard_index);
3. Esempio Pratico con Dati Realistici
Consideriamo due immagini binarie 8×8 pixel:
| Immagine 1 | Immagine 2 |
|---|---|
1 0 1 1 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 0 1 0 1 0 0 0 1 1 0 |
1 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 0 0 1 0 1 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 1 0 0 0 1 1 0 |
Calcoli:
- Intersezione: 24 pixel
- Unione: 40 pixel
- Indice di Jaccard: 24/40 = 0.6000
- Distanza di Jaccard: 1 – 0.6000 = 0.4000
4. Confronto con Altre Metriche di Similarità
L’indice di Jaccard è una delle molte metriche utilizzate per confrontare immagini binarie. La tabella seguente confronta le prestazioni di diverse metriche in scenari tipici:
| Metrica | Formula | Sensibilità al Rumore | Intervallo | Applicazioni Tipiche |
|---|---|---|---|---|
| Indice di Jaccard | |A∩B| / |A∪B| | Moderata | [0, 1] | Segmentazione immagini, retrieval |
| Dice Coefficient | 2|A∩B| / (|A| + |B|) | Bassa | [0, 1] | Analisi medica, NLP |
| Accuracy | (TP + TN) / (TP + TN + FP + FN) | Alta | [0, 1] | Classificazione generale |
| Hamming Distance | Numero di posizioni diverse | Molto alta | [0, ∞) | Codici correzione errori |
5. Ottimizzazione delle Prestazioni in MATLAB
Per immagini di grandi dimensioni, il calcolo dell’indice di Jaccard può essere ottimizzato:
-
Vettorizzazione:
Utilizzare operazioni vettoriali invece di cicli
for:% Metodo non ottimizzato (lento) jaccard = 0; for i = 1:size(bw1,1) for j = 1:size(bw1,2) if bw1(i,j) && bw2(i,j) jaccard = jaccard + 1; end end end % Metodo ottimizzato (vettoriale) jaccard = sum(bw1(:) & bw2(:)); -
Preallocazione della memoria:
Per operazioni ripetute, preallocare gli array:
results = zeros(1, num_experiments); for k = 1:num_experiments results(k) = sum(bw1(:) & bw2(:)) / sum(bw1(:) | bw2(:)); end -
Utilizzo di GPU:
Per dataset molto grandi, considerare l’uso di
gpuArray:bw1_gpu = gpuArray(bw1); bw2_gpu = gpuArray(bw2); intersection = sum(bw1_gpu & bw2_gpu); union = sum(bw1_gpu | bw2_gpu); jaccard = gather(intersection / union);
6. Applicazioni Pratiche nell’Elaborazione delle Immagini
L’indice di Jaccard trova applicazione in numerosi campi:
-
Segmentazione delle immagini mediche:
Confrontare mascheramenti di organi in immagini MRI/CT per valutare l’accuratezza degli algoritmi di segmentazione automatica.
-
Retrieval di immagini (CBIR):
Misurare la similarità tra immagini binarie in database per sistemi di ricerca basati sul contenuto.
-
Analisi di documenti:
Confrontare layout di documenti scansionati per rilevare modifiche o plagio visivo.
-
Visione artificiale:
Valutare le prestazioni degli algoritmi di object detection confrontando le bounding box previste con quelle reali.
7. Errori Comuni e Come Evitarli
Durante l’implementazione dell’indice di Jaccard in MATLAB, è facile incorrere in errori:
-
Immagini non binarizzate:
Dimenticare di convertire le immagini in binario prima del calcolo. Soluzione: sempre verificare con
unique(img)che i valori siano solo 0 e 1. -
Dimensioni diverse:
Confrontare immagini con dimensioni diverse causa errori. Soluzione: ridimensionare con
imresizemantenendo le proporzioni. -
Divisione per zero:
Se entrambe le immagini sono completamente nere (tutti 0), l’unione sarà 0. Soluzione: aggiungere una verifica:
union = sum(bw1(:) | bw2(:)); if union == 0 jaccard_index = 1; % Entrambe le immagini sono identicamente nere else jaccard_index = intersection / union; end -
Formato dei dati:
Caricare immagini come
logicalinvece didoublepuò causare errori. Soluzione: convertire esplicitamente conlogical(img).
8. Estensioni e Variazioni dell’Indice di Jaccard
Esistono diverse varianti e estensioni dell’indice di Jaccard:
-
Jaccard pesato:
Assegna pesi diversi a diverse regioni dell’immagine:
weights = ...; % Matrice dei pesi weighted_intersection = sum((bw1(:) & bw2(:)) .* weights(:)); weighted_union = sum((bw1(:) | bw2(:)) .* weights(:));
-
Jaccard multiclasse:
Estensione per immagini con più di due classi:
% Per ogni classe c jaccard_c = sum((label1==c) & (label2==c)) / sum((label1==c) | (label2==c)); overall_jaccard = mean(jaccard_c);
-
Jaccard spaziale:
Considera anche la prossimità spaziale dei pixel:
% Usa una funzione di decadimento della distanza D = pdist2(...); % Matrice delle distanze spatial_weights = exp(-D.^2 / (2*sigma^2)); spatial_jaccard = ...; % Calcolo pesato
9. Implementazione Avanzata con MATLAB Image Processing Toolbox
La Image Processing Toolbox di MATLAB offre funzioni specializzate che possono semplificare il calcolo:
% Caricamento e binarizzazione
img1 = imbinarize(imread('image1.tif'));
img2 = imbinarize(imread('image2.tif'));
% Calcolo usando bwprop
stats1 = regionprops(img1, 'Area');
stats2 = regionprops(img2, 'Area');
union_area = stats1.Area + stats2.Area;
% Intersezione usando AND logico
intersection = nnz(img1 & img2);
jaccard_index = intersection / (union_area - intersection);
Questo approccio è particolarmente utile quando si lavorano con regioni connesse invece che con singoli pixel.
10. Confronto con Altre Piattaforme
La tabella seguente confronta l’implementazione dell’indice di Jaccard in diverse piattaforme:
| Piattaforma | Funzione/Tecnica | Vantaggi | Svantaggi |
|---|---|---|---|
| MATLAB | sum(A & B)/sum(A | B) |
Sintassi chiara, ottimizzato per matrici | Costo della licenza |
| Python (scikit-image) | jaccard_score |
Gratuito, vasta comunità | Meno ottimizzato per operazioni vettoriali |
| OpenCV (C++) | bitwise_and, bitwise_or |
Prestazioni elevate | Sintassi più verbosa |
| R | Pacco jaccard |
Ideale per analisi statistica | Meno efficiente per immagini grandi |
11. Caso Studio: Valutazione di un Algoritmo di Segmentazione
Supponiamo di voler valutare un algoritmo di segmentazione di lesioni cerebrali in immagini MRI. Il processo sarebbe:
- Ottenere 50 immagini MRI con segmentazione manuale (ground truth) da esperti
- Eseguire l’algoritmo automatico su tutte le immagini
- Calcolare l’indice di Jaccard per ogni immagine:
jaccard_scores = zeros(1, 50);
for i = 1:50
ground_truth = imread(sprintf('gt_%02d.png', i));
auto_segment = imread(sprintf('auto_%02d.png', i));
intersection = sum(ground_truth(:) & auto_segment(:));
union = sum(ground_truth(:) | auto_segment(:));
jaccard_scores(i) = intersection / union;
end
mean_jaccard = mean(jaccard_scores);
std_jaccard = std(jaccard_scores);
Risultati tipici per algoritmi state-of-the-art:
- Segmentazione manuale vs manuale: Jaccard = 0.85-0.95
- Algoritmi automatici (U-Net): Jaccard = 0.70-0.85
- Algoritmi tradizionali (thresholding): Jaccard = 0.50-0.70
12. Visualizzazione dei Risultati
Una buona pratica è visualizzare i risultati per una valutazione qualitativa:
figure;
subplot(1,3,1); imshow(ground_truth); title('Ground Truth');
subplot(1,3,2); imshow(auto_segment); title('Segmentazione Automatica');
subplot(1,3,3); imshow(ground_truth & auto_segment); title('Intersezione');
colormap('jet');
colorbar;
Questo aiuta a identificare sistematicamente dove l’algoritmo fallisce (falsi positivi/negativi).
13. Considerazioni Computazionali
Per dataset molto grandi (es. immagini 3D medicali):
-
Memoria:
Immagini 3D (es. 512×512×200) occupano ~50MB ciascuna in single precision. Soluzione: processare in blocchi o usare
memmapfile. -
Parallelizzazione:
Usare
parforper processare multiple immagini in parallelo:parpool('local', 4); % Crea un pool di 4 workers parfor i = 1:num_images % Calcoli paralleli end -
Approssimazione:
Per stime rapide, campionare casualmentre i pixel:
sample_idx = randperm(numel(bw1), 10000); % Campione di 10k pixel sample_jaccard = sum(bw1(sample_idx) & bw2(sample_idx)) / ... sum(bw1(sample_idx) | bw2(sample_idx));
14. Validazione Statistica dei Risultati
Quando si confrontano multiple configurazioni di algoritmi, è importante validare statisticamente i risultati:
% Test t di Student per confrontare due algoritmi
[h, p] = ttest2(jaccard_scores_algo1, jaccard_scores_algo2);
if h == 1
fprintf('Differenza significativa (p = %.4f)\n', p);
else
fprintf('Nessuna differenza significativa (p = %.4f)\n', p);
end
Tipici valori soglia per p-value:
- p < 0.05: differenza significativa
- p < 0.01: differenza altamente significativa
- p < 0.001: differenza estremamente significativa
15. Integrazione con Altri Indici di Prestazione
L’indice di Jaccard dovrebbe essere usato insieme ad altre metriche per una valutazione completa:
% Calcolo di multiple metriche TP = sum((ground_truth == 1) & (auto_segment == 1)); FP = sum((ground_truth == 0) & (auto_segment == 1)); FN = sum((ground_truth == 1) & (auto_segment == 0)); jaccard = TP / (TP + FP + FN); dice = 2*TP / (2*TP + FP + FN); precision = TP / (TP + FP); recall = TP / (TP + FN); accuracy = (TP + TN) / (TP + TN + FP + FN); % TN = sum((ground_truth == 0) & (auto_segment == 0))
Una buona pratica è riportare:
- Media e devianza standard di tutte le metriche
- Boxplot per visualizzare la distribuzione
- Matrice di confusione aggregata
16. Applicazione a Immagini a Livelli di Grigio
Per immagini non binarie, è necessario prima applicare una soglia:
% Metodo di Otsu (automatico) level = graythresh(gray_img); binary_img = imbinarize(gray_img, level); % Soglia manuale binary_img = imbinarize(gray_img, 0.7); % Soglia a 0.7 % Soglia multi-livello multi_level = multithresh(gray_img, 2); segmented = imquantize(gray_img, multi_level);
Per immagini a colori, convertire prima in scala di grigi o lavorare su singoli canali.
17. Estensione a Video
Per valutare la similarità tra frame di video:
video = VideoReader('video.mp4');
frame1 = rgb2gray(readFrame(video));
frame2 = rgb2gray(readFrame(video));
% Binarizzare (es. usando edge detection)
bw1 = edge(frame1, 'canny');
bw2 = edge(frame2, 'canny');
jaccard = sum(bw1(:) & bw2(:)) / sum(bw1(:) | bw2(:));
Per analisi temporale, calcolare l’indice di Jaccard tra frame consecutivi per rilevare cambi di scena.
18. Implementazione in MATLAB con Interfaccia Grafica
Per un’applicazione user-friendly, creare una GUI:
function jaccard_gui
fig = uifigure('Name', 'Jaccard Index Calculator');
gl = uigridlayout(fig, [3 3]);
% Componenti UI
btn1 = uibutton(gl, 'Text', 'Carica Immagine 1');
btn1.Layout.Row = 1; btn1.Layout.Column = 1;
btn1.ButtonPushedFcn = @(btn,event) loadImage(1);
btn2 = uibutton(gl, 'Text', 'Carica Immagine 2');
btn2.Layout.Row = 1; btn2.Layout.Column = 2;
btn2.ButtonPushedFcn = @(btn,event) loadImage(2);
img1 = uiimage(gl);
img1.Layout.Row = 2; img1.Layout.Column = 1;
img2 = uiimage(gl);
img2.Layout.Row = 2; img2.Layout.Column = 2;
result = uilabel(gl, 'Text', 'Indice di Jaccard: ');
result.Layout.Row = 3; result.Layout.Column = [1 2];
function loadImage(num)
[file, path] = uigetfile('*.*');
if file ~= 0
img = imread(fullfile(path, file));
if num == 1
img1.ImageSource = img;
global img1_global;
img1_global = imbinarize(rgb2gray(img));
else
img2.ImageSource = img;
global img2_global;
img2_global = imbinarize(rgb2gray(img));
end
calculateJaccard();
end
end
function calculateJaccard()
if exist('img1_global', 'var') && exist('img2_global', 'var')
intersection = sum(img1_global(:) & img2_global(:));
union = sum(img1_global(:) | img2_global(:));
jaccard = intersection / union;
result.Text = sprintf('Indice di Jaccard: %.4f', jaccard);
end
end
end
19. Ottimizzazione per Immagini di Grandi Dimensioni
Per immagini molto grandi (es. 4000×4000 pixel):
-
Processamento a blocchi:
block_size = [1000 1000]; jaccard_total = 0; count = 0; for i = 1:block_size(1):size(bw1,1) for j = 1:block_size(2):size(bw1,2) i_end = min(i+block_size(1)-1, size(bw1,1)); j_end = min(j+block_size(2)-1, size(bw1,2)); block1 = bw1(i:i_end, j:j_end); block2 = bw2(i:i_end, j:j_end); intersection = sum(block1(:) & block2(:)); union = sum(block1(:) | block2(:)); jaccard_total = jaccard_total + intersection/union; count = count + 1; end end avg_jaccard = jaccard_total / count; -
Compressione:
Usare
im2colper ridurre la ridondanza:cols1 = im2col(bw1, [8 8], 'sliding'); cols2 = im2col(bw2, [8 8], 'sliding'); % Calcolare Jaccard su blocchi 8x8
20. Conclusione e Best Practices
Per implementare correttamente il calcolo dell’indice di Jaccard tra immagini in MATLAB:
- Sempre verificare che le immagini siano binarie (solo 0 e 1)
- Usare operazioni vettoriali invece di cicli
for - Considerare la normalizzazione per dimensioni diverse
- Combinare con altre metriche (Dice, precision, recall) per una valutazione completa
- Visualizzare i risultati per un’analisi qualitativa
- Per dataset grandi, implementare soluzioni di parallelizzazione o campionamento
- Documentare sempre il metodo di binarizzazione utilizzato
L’indice di Jaccard rimane una delle metriche più robuste e interpretabili per valutare la similarità tra immagini binarie, con applicazioni che spaziano dalla diagnostica medica alla visione artificiale.