Calcolare Indice Di Jaccard Tra Due Immagini Matlab

Calcolatore Indice di Jaccard tra Immagini (MATLAB)

Calcola l’indice di similarità di Jaccard tra due immagini binarie utilizzando la metodologia MATLAB

Indice di Jaccard: 0.0000
Intersezione: 0
Unione: 0
Distanza di Jaccard: 1.0000

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:

  1. 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.

  2. 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);
  3. Calcolo dell’indice di Jaccard:
    intersection = sum(bw1(:) & bw2(:));
    union = sum(bw1(:) | bw2(:));
    jaccard_index = intersection / union;
  4. 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:

  1. 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(:));
  2. 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
  3. 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:

  1. 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.

  2. Dimensioni diverse:

    Confrontare immagini con dimensioni diverse causa errori. Soluzione: ridimensionare con imresize mantenendo le proporzioni.

  3. 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
  4. Formato dei dati:

    Caricare immagini come logical invece di double può causare errori. Soluzione: convertire esplicitamente con logical(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:

  1. Ottenere 50 immagini MRI con segmentazione manuale (ground truth) da esperti
  2. Eseguire l’algoritmo automatico su tutte le immagini
  3. 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 parfor per 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):

  1. 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;
  2. Compressione:

    Usare im2col per 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.

Leave a Reply

Your email address will not be published. Required fields are marked *