Determinazione dello soglie locali

Nel caso di immagini con uno sfondo avente una illuminazione variabile un modo per avere una migliore determinazione della mappa binaria che separa gli oggetti nell'immagine dallo sfondo si può suddividere l'immagine in NxM sottoimmagini e determinare la soglia per ognuna di esse. La dimensione di queste sottoimmagini deve essere sufficientemente piccola da avere un'intensità luminosa relativamente costante. L'immagine binaria finale si ottiene ricomponendo le sottoimmagini con operazioni di composizione di matrici.

Iniziamo con la lettura del file di prova e la sua trasformazione in un'immagine a scala di grigi (rgb2gray ) rappresentata in virgola mobile a doppia precisione nell'intervallo [0-1] (mat2gray ), copiamo l'immagine in una nuova variabile imgriso (la ragione di questo apparirà chiara in seguito) e stampiamo la dimensione dell'immagine.

% Con Octave si deve prima di tutto
% caricare il package 'image', quindi
% si passa a caricare l'immagine in memoria 

pkg load image
			
riso = imread('grani-riso.png');
riso = mat2gray(rgb2gray(riso));

size(riso)

Iniziamo con programmare la suddivisione dell'immagine. Data la dimensione ridotta tentiamo inizialmente con una suddivisione in una griglia di 4 sottoimmagini (2 righe e 2 colonne). Andiamo a memorizzare queste sottoimmagini in un cell array un contenitore di dati che ammette, come le matrici, indici per accedere agli elementi contenuti in esso. A differenza delle matrici, i cui elementi sono scalari, gli elementi di un cell array può essere una qualsiasi istanza di matrici, anche di differenti dimensioni, scalari ed altri tipi di dati esistenti in Octave/Matlab. Il nostro cell array avrà  due indici per memorizzare le 4 immagini che vengono dalla suddivisione in 2 righe e 2 colonne. La notazione per assegnare le sottomatrici al cell array è (prima sottoimmagine in alto a sinistra, quindi prima riga e prima colonna)

riso_griglia{1}{1}=imgriso(1:127,1:128);
Assegnate gli altri 3 elementi mancanti al cell array di dimensione 2x2 usando i range di indici corretti per selezionare sottoimmagini

Si puo' cercare di determinare le soglie per ogni sottoimmagine, calcolare le immagini binarie corrispondenti e salvarle in un altro cell array. Per esempio per la sottoimmagine di indici 1,1

imbw{1}{1}=im2bw(riso_griglia{1}{1},graythresh(riso_griglia{1}{1}));

E così anche per le altre sottomatrici fino ad avere 4 elementi in imbw che corrispondono alle sottoimmagini binarie. La composizione dell'immagine binaria complessiva si fa usando la notazione di composizione delle matrici tipica della sintassi di Octave/Matlab.

Usate la notazione per la costruzione di matrici (spiegata alle pagine precedenti) per costruire una nuova matrice binaria bw composta dalle 4 sottomatrice salvate nel cell array e valutate il risultato

Un ulteriore miglioramento può essere ottenuto notando che molti dei falsi positivi sono di piccole dimensioni, prodotti da pochi pixel che hanno superato la soglia. Una tentativo per eliminare queste deviazioni dalla media locale di intensità luminosa può essere quello di filtrare l'immagine con un filtro di smoothing . Riprendiamo l'esericizio dall'inizio e alla matrice imgriso assegnamo l'output della funzione imfilter che applica un filtro lineare ad un immagine. La funzione ammette un numero variabile di argomenti, ma il primo deve essere la matrice dell'immagine da filtrare e il secondo la matrice del filtro. La funzione fspecial genera matrici che implementano diversi tipi di filtri. Per esempio con un filtro gaussiano di ordine 3

gauss = fspecial("gaussian",3)

Dove il primo argomento è una stringa che identifica il tipo di filtro e il secondo argomento l'ordine del filtro. Nel caso di filtro gaussiano è importante anche il terzo argomento che è la dispersione della funzione a campana di Gauss (più grande questo numero più ampia la campana)

Provate a filtrare l'immagine originaria con un filtro di media ( ) o un filtro gaussiano modificando i parametri di controllo

Per esempio, nel caso del filtro gaussiano non solo cambiate l'ordine del filtro, ma aumentate anche la dispersione (se l'argomento della dispersione non viene specificato il suo valore viene posto a 0.5). L'effetto di questi parametri sulle matrici che rappresentano il filtro lineare è visibile provando a chiamare direttamente fspecial dalla linea di comando di Octave

fspecial("gaussian",5,0.8)
ans =

4.8091e-04   5.0112e-03   1.0945e-02   5.0112e-03   4.8091e-04
5.0112e-03   5.2218e-02   1.1405e-01   5.2218e-02   5.0112e-03
1.0945e-02   1.1405e-01   2.4912e-01   1.1405e-01   1.0945e-02
5.0112e-03   5.2218e-02   1.1405e-01   5.2218e-02   5.0112e-03
4.8091e-04   5.0112e-03   1.0945e-02   5.0112e-03   4.8091e-04

Tutta la procedura, una volta messa a punto può essere semplificata salvando i comandi in un file .m , un file che contiene comandi di Octave che vengono eseguiti in sequenza quando si digita il nome del file sulla linea di comando. Nella vostra cartella di lavoro create con un editor di testo il file soglialocale.m e scrivete in esso i comandi a partire dalla costruzione del cell array e proseguendo con la determinazione delle soglie e delle immagini binarie di base, fino alla loro composizione nell'immagine binaria composta. I comandi verranno eseguiti quando digitate sulla linea di comando

soglialocale

Funzione di ripartizione di un'immagine

La funzione ritaglia semplifica l'intero processo di costruzione di un cell array con le sottomatrici. La funzione accetta un numero variabile di argomenti.

  • Il primo argomento è obbligatorio ed è l'immagine da ripartire in sotto matrici. Se questo è l'unico argomento allora la procedura ripartisce l'immagine in una griglia di 3x3
  • Il secondo argomento è il numero di righe della griglia. Se gli argomenti sono due allora il secondo argomento è anche il numero delle colonne della griglia
  • Il terzo argomento è il numero delle colonne della griglia
function griglia=ritaglia(img,rows,columns)
%
% ritaglia(img,n,m) ritorna un cell array contenente le sotto-matrici
% ottenute ritagliando in l'immagine 'img' in una griglia rettangolare
% di N x M elementi. La griglia viene restituita dalla funzione in
% un cell array
%
%  Argomenti:
%
%    img - immagine da ritagliare (obbligatorio). Se questo è l'unico
%          argomento allora il cell array è formato da una griglia 3 x 3
%
%    rows - numero di righe del cell array.
%
%    columns - numero di colonne del cell array
%
% Se vengono passati solo 2 argomenti allora il numero di colonne
% viene posto uguale al numero delle righe (secondo argomento)
% 
%

    imgsz = size(img);

% Il seguente blocco di condizioni realizza il comportamento dichiarato
% mella dichiarazione della funzione per quanto riguarda gli argomenti
% di input. La variabile 'nargin' è una variabile 'embedded', viene creata
% da Octave o Matlab durante l'esecuzione di ogni funzione e contiene
% il numero di argomenti usati nel chiamare la funzione stessa
        
    if (nargin == 1)
        rows = 3;
        columns = 3;
    elseif (nargin == 2)
        columns = rows;
    end

    griglia = cell(rows,columns);

% Determiniamo in numero di pixel l'altezza di una riga e l'ampiezza di
% una colonna della griglia. Questi devono essere numeri interi

    altezza_riga     = floor(imgsz(1)/rows);
    ampiezza_colonna = floor(imgsz(2)/columns);

% gli array 'righe' e 'colonne' contengono gli indici alle righe e colonne
% dell'immagine che determinano la griglia 
    
    righe = [1:altezza_riga:imgsz(1)];
    colonne = [1:ampiezza_colonna:imgsz(2)];

    imgsz
    righe
    colonne

% viene composto il cell array

    for c = [1:length(colonne)-1]
        for r = [1:length(righe)-1]
            griglia{r}{c} = ...
                img(righe(r):righe(r+1)-1,colonne(c):colonne(c+1)-1);
        end
    end

end

% -- ritaglia.m

Esempio e risultati sull'immagine di prova grani-riso.png

img=imread('grani-riso.png');
img=rgb2gray(img);
img=mat2gray(img);
griglia=ritaglia(img,4,4);
for r = [1:4]; for c = [1:4]; subplot(4,4,4*(r-1)+c); imshow(griglia{r}{c}); end; end

Le soglie possono essere determinate individualmente per ogni immagine e quindi una immagine binaria completa costruita a partire dalle singole sotto-immagini binarie

for r = [1:4]
   for c = [1:4]
      soglia=graythresh(griglia{r}{c});
      imbinaria{r}{c}=im2bw(griglia{r}{c},soglia)
   end
end

% creiamo una matrice vuota per l'immagine binaria totale

immagine_binaria = [];
for r = [1:4]
   riga_matrici = []; 
   for c = [1:4]
       riga_matrici = [riga_matrici imbinaria{r}{c}];
   end
   immagine_binaria = [immagine_binaria; riga_matrici]
end