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);
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.
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)
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