Převod titulků z DVD - The Final Countdown
Tímto navazuji na můj předchozí článek o převodu titulků z DVD to TXT. Zůstala
tam totiž nedokončena pasáž týkající se 'automatických' oprav chyb, které bohužel vždy
vznikají při aplikaci OCR software. I zde si na pomoc vezmu proudový editor sed, který je součástí snad každé
distribuce GNU/Linuxu.
sed je velmi šikovný program používaný z příkazové řádky a z toho vyplývá jeho síla - je neskutečně
rychlý a efektivní. Jeho možnosti jsou rozsáhlé, my využijeme pouze funkci nahrazování za použití rugulárních výrazů. To
jsou hrůzostrašně vyhlížející konstrukce, změť normálních a zpěrných lomítek. Myslel jsem si, že je nikdy neovládnu. Ale
při psaní toho článku jsem se je učil "za pochodu" a radikálně jsem změnil názor. Najednou jsem se snažil vytvořit co nejkrkolomnější
a nejsložitější regulární výraz, který by ale byl pochopitelně funkční. Kolikrát mě překvapilo, že skript postupně
umí i věci, které jsem původně ani nezamýšlel :o))
Můžete si
stáhnout skript (sed_cz_iso2) pro sed, ať to nemusíte pořád vypisovat.
Jeho použití je pak:
sed -f sed_cz_iso2 < Puvodni_titulky > Nove_titulky
Formát souboru Puvodni_titulky musí být v kódování ISO-8859-2 a nejlépe ve formátu SRT tedy tak, jak vzejdou
po použití programu dvdsub z projektu Martina Kačera.
Doporučuju používat pouze na české titulky, je tam
několik specifik pro češtinu. Inspiraci jsem čerpal z projektu Martina Kačera, ze zdrojáků SubRipu a něco jsem
si dovolil vymyslet sám. Taky jsem si v průběhu vymýšlení a ladění formy výrazů a jejich pořadí vytvořil
testovací soubor s různými "chuťovkami".
POZOR! Skript se rovněž pokouší řešit nepříjemný problém, který vzniká proto, že velké I vypadá u DVD titulků
stejně jako malé L. Aby to bylo jednodušší, provádí se jen kontrola a nahrazování ve směru z malého L na velké I. Proto
už při převodu titulků-obrázků na ASCII znaky (např. pomocí dvdsub) je nutno zajistit, aby všude bylo malé L. Tzn. že pokud
se vás OCR program nejdříve zeptá na rozpoznané velké I, odpovězte mu, že je to malé L, jinak budete mít všude jen velké I a to
se na malé L převádí o poznání hůře (resp. byste nejdříve museli změnit všechna velká I na malá L - což je jeden příkaz
v sedu - a teprve pak použít můj skript). Velké I s čárkou samozřejmě při OCR převodu nečiní potíže.
A jdeme na to!!! (sed -e 's/d/eb/g')
Teď si probereme jednotlivé položky skriptu pro sed - využijeme zde síly regulárních výrazů
(více o nich se dozvíte např. v článcích Pavla Satrapy). Jedna
z možností využití programu sed je nahrazování, jež má tvar např. s/hledej/nahraď/g, kde
/g znamená, že se má prohledávat celý vstup (global) od začátku do konce, nikoli jen jediný výskyt.
- s/''/"/g
nahrazení dvou apostrofů jedněmi uvozovkami; při běhu programu dvdsub si dávejte pozor, abyste používali
vždy apostrof anglické klávesnice (na klávese pod uvozovkami), nikoli čárku nad písmeny či zpětný apostrof nebo český apostrof,
ušetříte si tak spoustu práce
- s/'"/"/g
odstranění drobných problémů s uvozovkami a apostrofy
- s/"'/"/g
odstranění drobných problémů s uvozovkami a apostrofy
- s/\ \+"/"/g
hledáme mezery před uvozovkami a tyto dva a více znaků nahradíme jediným - jedněmi uvozovkami;
jelikož znak mezera (ASCII 32) má svůj specifický význam - oddělovač argumentů, musíme tento její
význam potlačit opačným lomítkem \ a v případě symbolu + je tomu naopak - abychom zapnuli jeho specifický
význam (opakování předcházejícího znaku - zde mezery - alespoň jednou), musíme použít zpětné lomítko; některé
programy používající klasické regulární výrazy (grep, sed, vi) zpětným lomítkem zapínají speciální významy
některých znaků, jiné programy pracující s rozšířenými regulárními výrazy naopak zpětným lomítkem speciální
významy některých znaků vypínají - takže Perl, awk, egrep používají
jen + a sed aj. zase vyžadují \+ pro jejich speciální význam. Ach ta standardizace :o)
- s/"\ \+/"/g
vynechání všech mezer za uvozovkami, resp. nahrazení uvozovek a všech mezer za nimi jedním znakem uvozovek
- s/\("[^"]*"\)/\ \1\ /g
tahle konstrukce se mi povedla :o) dělá to, že před a za výraz_v_uvozovkách vloží mezery; konstrukce \( a \) se
používá pro zapamatování obsahu v závorkách - sed opět požaduje uvození závorek zpětným lomítkem kdežto
Perl nikoli; na zapamatovaný obsah se pak odkazujeme pomocí \číslo zde \1. Zapamatování lze řetězit či
vnořovat a číslo odkazu se počítá podle pořadí levé závorky zleva. Konstrukce [] označuje výčet - [ast] znamená
právě jedno z písmen a, s nebo t. Lze i negovat pomocí stříšky ^ jako v tomto případě - [^"] znamená cokoli kromě
uvozovek. Opakování znaků zajišťuje hvězdička, tečka bez zpětného lomítka znamená jeden libovolný znak. Regulární
výrazy se vyznačují svou nenasytností, tzn. že se snaží najít co možná nejdelší výraz, který odpovídá vzoru v rámci
jednoho řádku. Kdybychom hledali výraz "*." tak by výsledek mohl být i "text1","text2" tedy uvozovky v uvozovkách, což
však nechceme. Proto hledáme uvozovky následované ostatními znaky kromě uvozovek a uvozovkami. Tento nalezený
a zapamatovaný řetězec pak doplníme před a za jednou mezerou.
- s/^\ \+//g
vynechání všech mezer na začátku řádku; stříška, pokud není ve výčtu jako negace, označuje začátek řádku
- s/""/"/g
odstranění zdvojených uvozovek
- s/\ \+,/,/g
odstranění všech mezer před čárkou
- s/\ \+\./\./g
odstranění všech mezer před tečkou; tečka má v regulárních výrazech svůj speciální význam - jeden libovolný znak - a
tak ho musíme vypnout opačným lomítkem
- s/\ \+?/?/g
vynechání všech mezer před otazníkem; otazník má v regulárních výrazech také svůj specifický význam - označuje
nejvýše jeden výskyt předcházejícího znaku; v sedu bychom museli tuto vlastnost aktivovat zpětným lomítkem obdobně jako u +
- s/\ \+!/!/g
vynechání všech mezer před vykřičníkem
- s/\([[:digit:]]\)\ \([[:digit:]]\)/\1\2/g
vynechání mezery mezi číslicemi - nenapadá mě případ, kdy by číslice měly být odděleny mezerou (kromě řádů a tel. čísel)
- s/\([[:digit:]]\)\ \([[:digit:]]\)/\1\2/g
zopakování předchozího kroku - v případě vzorku 1 2 3 4 by totiž napoprvé došlo k odstranění jen každé druhé mezery, protože
sed prochází soubor neprůnikově, tzn. že nejprve vyhoví 1 2 a pak se posune na mezeru a další vyhoví 3 4. Až v druhém
kole najde 2 3
- s/,\.\.\./\.\.\./g
záměna ,... za ...
- s/?\./?/g
odstranění tečky za otazníkem
- s/!\./!/g
odstranění tečky za vykřičníkem
- s/,\([^[:digit:]"|]\)/,\ \1/g
doplnění mezery za čárku, pokud za ní nenásleduje číslice, uvozovky nebo svislá čára - hlavně z důvodu lepšího automatického zalamování dlouhých titulků na více řádků; pokud
za čárkou už mezera je, pak přibude další - to pak vyřeší jeden z následujících příkazů (odstranění duplicitních mezer);
svislou čáru používá formát titulků MicroDVD SUB v rámci jednoho řádku k rozdělení víceřádkových titulků pro zobrazení
- s/;/;\ /g
doplnění mezery za středník - viz výše; zde mohou vyvstat potíže u SUB formátu titulků - jejich rozšířený formát totiž
umožňuje označovat, které titulky mají být kurzívou či tučné, a při tom používá jako oddělovač parametrů ve složených
závorkách právě středník; to se ovšem týká jen některých případů, kdy pustíte skript dodatečně na titulky vytvořené jinak, než popisuji já
- s/:\([^[:digit:]]\)/:\ \1/g
doplnění mezery za dvojtečku, pokud nenásleduje číslice
- s/?\([^?!"|)]\)/?\ \1/g
doplnění mezery za otazník, pokud nenásleduje otazník, vykřičník, uvozovky, svislá čára nebo závorka
- s/!\([^?!"|)]\)/!\ \1/g
doplnění mezery za vykřičník, pokud nenásleduje otazník, vykřičník, uvozovky, svislá čára nebo závorka
- s/"\ "\([^[:alnum:]]\)/"\1/g
nahrazení mezery v uvozovkách (zdvojených uvozovek oddělených mezerou) jedněmi uvozovkami, pokud za nimi bezprostředně
nenásleduje písmeno nebo číslo - [:alnum:]
- s/\ \+/\ /g
náhrada více mezer za sebou jen jednou mezerou
- s/d'/ď/g
změna d a apostrofu na d s háčkem
- s/t'/ť/g
změna t a apostrofu na t s háčkem
- s/\<lhned/Ihned/g
Nyní následuje řada úprav pro češtinu, kdy se malé L převádí na velké I; zde slovo 'ihned' na začátku věty
- s/\<lk\([^aá]\)/Ik\1/g
všechna slova začínající na 'lk' vyjma těch začínajících 'lka' a 'lká' budou změněna na 'Ik'
- s/\<lkar/Ikar/g
z předchozí výjimky pro 'lka' vyjmeme slova Ikar- (Ikasor, Ikarus, Ikarie aj.)
- s/\<ln\([^ouě]\)/In\1/g
slova začínající na 'ln' kromě těch na 'lno', 'lnu', 'lně' budou změněny na 'In'
- s/\<lnov/Inov/g
slova začínající na 'lnov' změnit na 'Inov' (např. slovo 'inovace' na začátku věty)
- s/\<lnui/Inui/g
slova 'inulin' a 'inundace' na začátku věty :o)
- s/\<lont/Iont/g
různé tvary kořene 'iont' na začátku věty; na žádné slovo na 'lont' jsem si nevzpomněl :o/
- s/\<loniz/Ioniz/g
např. 'ionizační' na začátku věty
- s/\<lono/Iono/g
např. 'ionosféra' na začátku věty
- s/\<lpsa/Ipsa/g
slovo 'ipsace' na začátku věty :o) (todle si fakt najděte ve slovníku...)
- s/\<lpeka/Ipeka/g
slovo 'ipekakuanha' na začátku věty :o)
- s/\<lQ/IQ/g
každý máme nějaké to IQ...
- s/\<ls\([^t]\)/Is\1/g
slova začínající na 'ls' kromě těch na 'lst' (lstivý, lstivě, aj.) změnit na 'Is'
- s/\<l\([bcdfgjmrtwxz]\)/I\1/g
předpokládám, že nejsou slova, která by začínala na lb, lc, ld, lf, ...
- s/\<Ize\>/lze/g
oprava předchozího příkazu, kdy jediné slovo na 'lz' - lze - bylo mylně změněno
- s/\<lan\>/Ian/g
oprava počátečního velkého I ve jménu Ian - pády nelze jednoduše testovat, neboť má smysl i 'lana'
- s/\<lvan/Ivan/g
oprava počátečního velkého I ve jménu Ivan a všech jeho tvarech
- s/\<lvet/Ivet/g
oprava počátečního velkého I ve jménu Iveta a všech jeho tvarech
- s/\<lvo\>/Ivo/g
oprava počátečního velkého I ve jménu Ivo; tvary kolidují se slovy lev/lví
- s/\<lvon/Ivon/g
oprava počátečního velkého I ve jménu Ivon(a) a všech jeho tvarech
- s/\<lvoš/Ivoš/g
oprava počátečního velkého I ve jménu Ivoš a všech jeho tvarech
- s/\<l\>/I/g
kontrola osamoceného velkého I nebo římské 1; výraz \< označuje začátek slova, výraz \> pak konec slova; slovo je ohraničeno
mezerou, čárkou, tečkou, vykřičníkem, otazníkem...
- s/<Iž/lž/g
oprava předchozího příkazu, kdy 'ž' se považovalo za hranici slova (chyba v lokalizačních proměnných?) a u slov
lže, lžíce aj. bylo počáteční malé L nahrazeno velkým I
- s/n\.I\./n\.l\./g
předchozí řádek také špatně opravil zkratku n.l. (př.n.l.) na n.I. tak je to třeba vrátit zpět na n.l.
- s/\<ll\>/II/g
kontrola osamocené římské číslice 2
- s/\<lll\>/III/g
kontrola osamocené římské číslice 3; pořadí řádků pro záměnu malého L za velké I není libovolná, alespoň při podobě příkazů, jak
je uvádím zde - trojité malé L je totiž nejdříve testováno, zda je to římská 3, až pokud tomu tak není, pak dojde ke
změně pouze prvního malého L na velké I; pokud by byly řádky přehozeny, pak by došlo k záměně lll za Ill a test na
římskou trojku by už byl neúspěšný; samozřejmě by se pořadí dalo 'znecitlivit' složitější konstrukcí některých výrazů
- s/Vll\>/VII/g
kontrola římské číslice 7 ať už osamocené či na konci většího čísla
- s/Vlll\>/VIII/g
kontrola římské číslice 8 ať už osamocené či na konci většího čísla
- s/Xll\>/VII/g
kontrola římské číslice 12 ať už osamocené či na konci většího čísla
- s/Xlll\>/XIII/g
kontrola římské číslice 13 ať už osamocené či na konci většího čísla
- s/l\([[:upper:]]\)/I\1/g
konstrukci [[:upper:]] vyhoví jakékoli velké písmeno - podle nastavení lokalizačních proměnných též velká česká písmena
s diakritikou; tento příkaz nahradí malé L velkým I všude tam, kde je malé L následováno libovolným velkým znakem; pokud
máte problémy s tím, že nejsou "vnímána" velká písmena s diakritikou, zkuste konstrukci [[:upper:]ÁČĎÉĚÍÓŘŠŤÚŮÝŽ]
- s/\([[:upper:]]\)l\>/\1I/g
tímto příkazem se nahradí malé L velkým I, pokud je malé L na konci slova - konstrukce /> - a před ním je velké písmeno
- s/\<ll/Il/g
jestliže slovo začíná na 'll', je první písmeno změněno na velké I
- s/\.\ l/\.\ I/g
velké I na začátku věty (tečka, mezera, malé L -> velké I)
- Ručně je nutno projít slova lva/lvu/lana, jestli nemají být spíše Iva/Ivu/Iana
Jsem si jist, že tento skript nemůže stoprocentně opravit chyby po OCR software, ale myslím si, že po něm bude kvalita
titulků již tak vysoká, že není třeba je procházet "ručně". Samozřejmě vítám každý návrh na vylepšení - jistě
se setkáte s titulky, na které bude jakýkoli natož tento automatizovaný skript krátký.
LINUX
Počítadlo z http://pocitadlo.netway.cz/