Rilevazione "fiume" nel testo


175

Nell'ambito dello stackexchange di TeX, abbiamo discusso su come rilevare i "fiumi" nei paragrafi di questa domanda .

In questo contesto, i fiumi sono fasce di spazio bianco che risultano dall'allineamento accidentale di spazi tra parole nel testo. Dal momento che questo può distrarre un lettore, i cattivi fiumi sono considerati un sintomo di una scarsa tipografia. Un esempio di testo con i fiumi è questo, dove ci sono due fiumi che scorrono in diagonale.

inserisci qui la descrizione dell'immagine

È interessante rilevare automaticamente questi fiumi, in modo che possano essere evitati (probabilmente mediante la modifica manuale del testo). Raphink sta facendo progressi a livello di TeX (che conosce solo le posizioni dei glifi e i riquadri di delimitazione), ma sono fiducioso che il modo migliore per rilevare i fiumi sia con una certa elaborazione delle immagini (poiché le forme dei glifi sono molto importanti e non disponibili per TeX) . Ho provato vari modi per estrarre i fiumi dall'immagine sopra, ma la mia semplice idea di applicare una piccola quantità di sfocatura ellissoidale non sembra essere abbastanza buona. Ho anche provato un po 'di RadonHough trasforma il filtraggio basato, ma non sono arrivato da nessuna parte con quelli. I fiumi sono molto visibili ai circuiti di rilevamento delle caratteristiche dell'occhio umano / retina / cervello e in qualche modo penso che questo possa essere tradotto in una sorta di operazione di filtraggio, ma non sono in grado di farlo funzionare. Qualche idea?

Per essere precisi, sto cercando un'operazione che rileverà i 2 fiumi nell'immagine sopra, ma non ha troppe altre rilevazioni di falsi positivi.

EDIT: endolith mi ha chiesto perché sto perseguendo un approccio basato sull'elaborazione delle immagini dato che in TeX abbiamo accesso alle posizioni dei glifi, alle spaziature, ecc. E potrebbe essere molto più veloce e più affidabile utilizzare un algoritmo che esamina il testo reale. La mia ragione per fare le cose nell'altro modo è che la formadei glifi può influire sulla rilevanza di un fiume e, a livello di testo, è molto difficile considerare questa forma (che dipende dal carattere, dalla legatura, ecc.). Per un esempio di come la forma dei glifi può essere importante, considera i seguenti due esempi, in cui la differenza tra loro è che ho sostituito alcuni glifi con altri della stessa larghezza, in modo che un'analisi testuale prenderebbe in considerazione ugualmente buoni / cattivi. Si noti, tuttavia, che i fiumi nel primo esempio sono molto peggio che nel secondo.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


5
+1 Mi piace questa domanda. Il mio primo pensiero è una trasformazione di Hough , ma probabilmente avrebbe bisogno di qualche pre-elaborazione. Forse prima un filtro di dilatazione .
datageist

Sono sorpreso che la trasformazione di Radon non abbia funzionato, in realtà. Come hai fatto?
endolith

@endolith: niente di sofisticato. Ho usato ImageLines[]da Mathematica, con e senza pre-elaborazione. Immagino che tecnicamente stia usando una trasformazione di Hough piuttosto che quella di Radon. Non mi sorprenderò se la corretta preelaborazione (non ho provato il filtro di dilatazione suggerito da datageist) e / o le impostazioni dei parametri possano farlo funzionare.
Lev Bishop,

Google Image Search per fiumi mostra anche fiumi "tortuosi". Vuoi trovare quelli? cdn.ilovetypography.com/img/text-river1.gif
endolith

@endolith Immagino che alla fine voglio replicare l'elaborazione del sistema visivo umano che distrae alcune configurazioni degli spazi. Dal momento che ciò può accadere anche per i fiumi tortuosi, allora vorrei catturarli, anche se quelli diritti sembrano essere più un problema in generale. Ancora meglio sarebbe un modo per quantificare la "cattiveria" dei fiumi in un modo che corrisponda a quanto sono chiaramente visibili quando leggono il testo. Ma è tutto molto soggettivo e difficile da quantificare. In primo luogo, semplicemente catturare davvero tutti i fiumi cattivi senza troppi falsi positivi.
Lev Bishop,

Risposte:


135

Ci ho pensato un po 'di più e penso che quanto segue dovrebbe essere abbastanza stabile. Nota che mi sono limitato alle operazioni morfologiche, perché dovrebbero essere disponibili in qualsiasi libreria standard di elaborazione delle immagini.

(1) Apri l'immagine con una maschera nPix per 1, dove nPix è circa la distanza verticale tra le lettere

#% read image
img = rgb2gray('http://i.stack.imgur.com/4ShOW.png');

%# threshold and open with a rectangle
%# that is roughly letter sized
bwImg = img > 200; %# threshold of 200 is better than 128

opImg = imopen(bwImg,ones(13,1));

inserisci qui la descrizione dell'immagine

(2) Apri l'immagine con una maschera 1 per mPix per eliminare tutto ciò che è troppo stretto per essere un fiume.

opImg = imopen(opImg,ones(1,5));

inserisci qui la descrizione dell'immagine

(3) Rimuovere "fiumi e laghi" orizzontali dovuti allo spazio tra i paragrafi o al rientro. Per questo, rimuoviamo tutte le righe che sono tutte vere e apriamo con la maschera nPix-by-1 che sappiamo che non influenzerà i fiumi che abbiamo trovato in precedenza.

Per rimuovere i laghi, possiamo usare una maschera di apertura leggermente più grande di nPix-by-nPix.

A questo punto, possiamo anche buttare via tutto ciò che è troppo piccolo per essere un vero fiume, vale a dire tutto ciò che copre meno area di (nPix + 2) * (mPix + 2) * 4 (che ci darà ~ 3 linee). Il +2 è lì perché sappiamo che tutti gli oggetti sono almeno nPix in altezza e mPix in larghezza e vogliamo andare un po 'oltre.

%# horizontal river: just look for rows that are all true
opImg(all(opImg,2),:) = false;
%# open with line spacing (nPix)
opImg = imopen(opImg,ones(13,1));

%# remove lakes with nPix+2
opImg = opImg & ~imopen(opImg,ones(15,15)); 

%# remove small fry
opImg = bwareaopen(opImg,7*15*4);

inserisci qui la descrizione dell'immagine

(4) Se siamo interessati non solo alla lunghezza, ma anche alla larghezza del fiume, possiamo combinare la trasformazione della distanza con lo scheletro.

   dt = bwdist(~opImg);
   sk = bwmorph(opImg,'skel',inf);
   %# prune the skeleton a bit to remove branches
   sk = bwmorph(sk,'spur',7);

   riversWithWidth = dt.*sk;

inserisci qui la descrizione dell'immagine (i colori corrispondono alla larghezza del fiume (sebbene la barra dei colori sia disattivata di un fattore 2)

Ora puoi ottenere la lunghezza approssimativa dei fiumi contando il numero di pixel in ciascun componente collegato e la larghezza media calcolando la media dei loro valori di pixel.


Ecco la stessa identica analisi applicata alla seconda immagine "no-river":

inserisci qui la descrizione dell'immagine


Grazie. Ho Matlab, quindi lo proverò su alcuni altri testi per vedere quanto sarà robusto.
Lev Bishop,

Integrarlo nuovamente in TeX potrebbe essere un altro problema, a meno che non possiamo portarlo su Lua in qualche modo.
ℝaphink,

@LevBishop: penso di capire un po 'meglio il problema. La nuova soluzione dovrebbe essere abbastanza solida.
Jonas,

@levBishop: un altro aggiornamento.
Jonas,

1
@LevBishop: ho appena notato la seconda immagine. Risulta che l'analisi basata sulla morfologia fa il suo lavoro.
Jonas,

56

In Mathematica, usando l'erosione e la trasformazione di Hough:

(*Get Your Images*)
i = Import /@ {"http://i.stack.imgur.com/4ShOW.png", 
               "http://i.stack.imgur.com/5UQwb.png"};

(*Erode and binarize*)
i1 = Binarize /@ (Erosion[#, 2] & /@ i);

(*Hough transform*)
lines = ImageLines[#, .5, "Segmented" -> True] & /@ i1;

(*Ready, show them*)
Show[#[[1]],Graphics[{Thick,Orange, Line /@ #[[2]]}]] & /@ Transpose[{i, lines}]

inserisci qui la descrizione dell'immagine

Modifica rispondendo al commento di Mr. Wizard

Se vuoi sbarazzarti delle linee orizzontali, fai semplicemente una cosa del genere (probabilmente qualcuno potrebbe renderlo più semplice):

Show[#[[1]], Graphics[{Thick, Orange, Line /@ #[[2]]}]] & /@ 
 Transpose[{i, Select[Flatten[#, 1], Chop@Last@(Subtract @@ #) != 0 &] & /@ lines}]

inserisci qui la descrizione dell'immagine


1
Perché non sbarazzarsi di tutte le linee orizzontali? (+1)
Mr.Wizard,

@Sig. Solo per mostrare che tutte le linee vengono rilevate ...
Dr. belisarius,

1
Questo non fa parte del problema, vero?
Mr.Wizard,

@Sig. Modificato come richiesto
Dr. belisarius,

4
@belisarius Il sistema di coordinate utilizzato nella trasformazione di Hough è cambiato dopo 8.0.0 per corrispondere a quello della trasformazione di Radon. Questo a sua volta ha cambiato il comportamento di ImageLines. Nel complesso si tratta di un miglioramento, anche se in questo caso si preferirebbe il comportamento precedente. Se non si vuole sperimentare con i rilevamenti di picco, è possibile modificare il rapporto di formato dell'immagine in ingresso per essere più vicini a 1 e ottenere un risultato simile a 8.0.0: lines = ImageLines[ImageResize[#, {300, 300}], .6, "Segmented" -> True] & /@ i1;. Detto questo, per questo problema un approccio morfologico sembra più solido.
Matthias Odisio,

29

Hmmm ... Immagino che la trasformazione del Radon non sia così facile da estrarre. (La trasformazione del radon fondamentalmente ruota l'immagine mentre "guarda attraverso" il bordo. È il principio alla base delle scansioni CAT.) La trasformazione della tua immagine produce questo sinogramma, con i "fiumi" che formano picchi luminosi, che sono cerchiati:

inserisci qui la descrizione dell'immagine

Quello con una rotazione di 70 gradi può essere visto abbastanza chiaramente come il picco a sinistra di questo diagramma di una fetta lungo l'asse orizzontale:

inserisci qui la descrizione dell'immagine

Soprattutto se il testo era gaussiano prima sfocato:

inserisci qui la descrizione dell'immagine

Ma non sono sicuro di come estrarre in modo affidabile questi picchi dal resto del rumore. Le estremità superiore e inferiore luminose del sinogramma rappresentano i "fiumi" tra le linee orizzontali di testo, che ovviamente non ti interessano. Forse una funzione di ponderazione rispetto all'angolo che enfatizza più linee verticali e minimizza quelle orizzontali?

Una semplice funzione di ponderazione del coseno funziona bene su questa immagine:

inserisci qui la descrizione dell'immagine

trovare il fiume verticale a 90 gradi, che è il massimo globale nel sinogramma:

inserisci qui la descrizione dell'immagine

e su questa immagine trovare quella a 104 gradi, sebbene la sfocatura prima la renda più accurata:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

(La radon()funzione di SciPy è un po 'stupida , o mapperei questo picco sull'immagine originale come una linea che attraversa il mezzo del fiume.)

Ma non trova nessuno dei due picchi principali nel sinogramma per la tua immagine, dopo la sfocatura e la ponderazione:

inserisci qui la descrizione dell'immagine

Sono lì, ma sono sopraffatti dalle cose vicino al picco medio della funzione di ponderazione. Con la giusta ponderazione e ottimizzazione questo metodo probabilmente potrebbe funzionare, ma non sono sicuro di quali siano le giuste modifiche. Probabilmente dipende anche dalle proprietà delle scansioni della pagina. Forse la ponderazione deve essere derivata dall'energia complessiva nella fetta o qualcosa del genere, come una normalizzazione.

from pylab import *
from scipy.misc import radon
import Image

filename = 'rivers.png'
I = asarray(Image.open(filename).convert('L').rotate(90))

# Do the radon transform and display the result
a = radon(I, theta = mgrid[0:180])

# Remove offset
a = a - min(a.flat)

# Weight it to emphasize vertical lines
b = arange(shape(a)[1]) #
d = (0.5-0.5*cos(b*pi/90))*a

figure()
imshow(d.T)
gray()
show()

# Find the global maximum, plot it, print it
peak_x, peak_y = unravel_index(argmax(d),shape(d))
plot(peak_x, peak_y,'ro')
print len(d)- peak_x, 'pixels', peak_y, 'degrees'

E se dovessi sfocare prima con un gaussiano asimmetrico? Vale a dire stretto in direzione orizzontale, largo in direzione verticale.
Jonas,

@Jonas: Questo probabilmente aiuterebbe. Il problema principale è selezionare automaticamente i picchi dallo sfondo quando lo sfondo varia così tanto con la rotazione. La sfocatura asimmetrica potrebbe appianare le strisce orizzontali da una riga all'altra.
endolith,

Questo metodo funziona bene per rilevare la rotazione delle linee del testo, almeno: gist.github.com/endolith/334196bac1cac45a4893
endolith

16

Ho addestrato un classificatore discriminante sui pixel usando funzioni derivate (fino al 2 ° ordine) su scale diverse.

Le mie etichette:

etichettatura

Pronostico sull'immagine di allenamento:

inserisci qui la descrizione dell'immagine

Pronostico sulle altre due immagini:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Immagino che questo appaia promettente e potrebbe produrre risultati utilizzabili dati più di addestramento e forse funzioni più intelligenti. D'altra parte mi ci sono voluti solo pochi minuti per ottenere questi risultati. Puoi riprodurre tu stesso i risultati usando il software open source ilastik . [Dichiarazione di non responsabilità: sono uno dei principali sviluppatori.]


2

(Mi dispiace, questo post non include dimostrazioni fantastiche.)

Se si desidera lavorare con le informazioni già presenti in TeX (lettere e posizioni), è possibile classificare manualmente le lettere e le coppie di lettere come "inclinate" in una direzione o nell'altra. Ad esempio, "w" ha pendenze angolari SW e SE, la combo "al" ha una pendenza angolare NW, "k" ha una pendenza angolare NE. (Non dimenticare la punteggiatura: una citazione seguita da una lettera che riempie la metà inferiore del glifo stabilisce una bella pendenza; la citazione seguita da q è particolarmente forte.)

Quindi, cerca le occorrenze delle pendenze corrispondenti sui lati opposti di uno spazio - "w al" per un fiume da SW a NE o "k T" per un fiume da NW a SE. Quando ne trovi uno su una linea, vedi se si verifica uno simile, spostato in modo appropriato a sinistra o a destra, sulle linee sopra / sotto; quando ne trovi una, probabilmente c'è un fiume.

Inoltre, ovviamente, cerca solo gli spazi accatastati quasi verticalmente, per i semplici fiumi verticali.

Puoi diventare un po 'più sofisticato misurando la "forza" della pendenza: quanta parte dell'anticipo è "vuota" a causa della pendenza e contribuendo così alla larghezza del fiume. "w" è abbastanza piccolo, in quanto ha solo un piccolo angolo della sua casella di avanzamento per contribuire al fiume, ma "V" è molto forte. "b" è leggermente più forte di "k"; la curva più delicata dà un bordo del fiume più visivamente continuo, rendendolo più forte e visivamente più largo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.