Edinburgh Speech Tools  2.1-release
align_main.cc
Go to the documentation of this file.
1 /*************************************************************************/
2 /* */
3 /* Centre for Speech Technology Research */
4 /* University of Edinburgh, UK */
5 /* Copyright (c) 1996,1997 */
6 /* All Rights Reserved. */
7 /* */
8 /* Permission is hereby granted, free of charge, to use and distribute */
9 /* this software and its documentation without restriction, including */
10 /* without limitation the rights to use, copy, modify, merge, publish, */
11 /* distribute, sublicense, and/or sell copies of this work, and to */
12 /* permit persons to whom this work is furnished to do so, subject to */
13 /* the following conditions: */
14 /* 1. The code must retain the above copyright notice, this list of */
15 /* conditions and the following disclaimer. */
16 /* 2. Any modifications must be clearly marked as such. */
17 /* 3. Original authors' names are not deleted. */
18 /* 4. The authors' names are not used to endorse or promote products */
19 /* derived from this software without specific prior written */
20 /* permission. */
21 /* */
22 /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */
23 /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
24 /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
25 /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */
26 /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
27 /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
28 /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
29 /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
30 /* THIS SOFTWARE. */
31 /* */
32 /*************************************************************************/
33 /* Author : Alan W Black */
34 /* Date : September 1999 */
35 /*-----------------------------------------------------------------------*/
36 /* Simple alignment scoring program to give number of insertions, */
37 /* deletions, errors between a string of symbols and a reference string */
38 /* of symbols */
39 /* */
40 /*=======================================================================*/
41 #include <cstdlib>
42 #include <cstdio>
43 #include <iostream>
44 #include <fstream>
45 #include <cstring>
46 #include "EST.h"
47 #include "EST_WFST.h"
48 
49 using namespace std;
50 
51 static int align_main(int argc, char **argv);
52 static void nisttool_align(EST_Option &al);
53 static void string_align(EST_Option &al);
54 static void align_score(EST_Utterance &u, const EST_String &refrel,
55  const EST_String &hyporel,
56  const EST_String &alignrel,
57  int &total,int &ins,int &del,int &sub,int &correct);
58 static int name_distance(EST_Item *r,EST_Item *h);
59 void align(EST_Utterance &utt,
60  const EST_String &refrel,
61  const EST_String &hyporel,
62  const EST_String &alignrel);
63 static void load_sentence(EST_Utterance &u, const EST_String &relname,
64  EST_TokenStream &ts);
65 static void load_sentence(EST_Utterance &u, const EST_String &relname,
66  EST_String &relval);
67 
68 
69 int main(int argc, char **argv)
70 {
71 
72  align_main(argc,argv);
73 
74  exit(0);
75  return 0;
76 }
77 
78 static int align_main(int argc, char **argv)
79 {
80  // Top level function generates a WFST from rules
81  EST_Option al;
82  EST_StrList files;
84  EST_String format;
85 
87  (argc, argv,
88  EST_String("[options] ...\n")+
89  "Summary: align an hypothesis with a reference string\n"+
90  "-rfile <ifile> Reference file\n"+
91  "-hfile <ifile> Hypothesis file\n"+
92  "-rstring <string> Reference string\n"+
93  "-hstring <string> Hypothesis string\n"+
94  "-format <string>\n"+
95  " Supported formats: strings, nisttool\n",
96  files, al);
97 
98  if (al.present("-o"))
99  outfile = al.val("-o");
100  else
101  outfile = "-";
102 
103  if (al.present("-format"))
104  format = al.val("-format");
105  else
106  format = "strings";
107 
108  if (format == "strings")
109  string_align(al);
110  else if (format == "nisttool")
111  nisttool_align(al);
112  else
113  cout << "Unknown or unhandled format: " << format << endl;
114 
115  return 0;
116 }
117 
118 bool dp_match(const EST_Relation &lexical,
119  const EST_Relation &surface,
120  EST_Relation &match,
121  float ins, float del, float sub);
122 
123 static void string_align(EST_Option &al)
124 {
125  EST_String refStr = al.val("-rstring");
126  EST_String hypStr = al.val("-hstring");
127  EST_Utterance u;
128  int total,ins,del,sub,correct;
129 
130  load_sentence(u,"ref",refStr);
131  load_sentence(u,"hypo",hypStr);
132  align(u,"ref","hypo","align");
133  align_score(u,"ref","hypo","align",total,ins,del,sub,correct);
134  fprintf(stdout,"words %d\n",total);
135  fprintf(stdout,"insertions %d\n",ins);
136  fprintf(stdout,"deletions %d\n",del);
137  fprintf(stdout,"substitutions %d\n",sub);
138  fprintf(stdout,"correct %d\n",correct);
139  fprintf(stdout,"WER %f\n",(100.0 * (float)(ins+del+sub))/total);
140 }
141 
142 static void nisttool_align(EST_Option &al)
143 {
144  // Using the format used by the NIST tools for alignment
145  // Sentence per line with parenthesized id at end
146  EST_String reffile = al.val("-rfile");
147  EST_String hypofile = al.val("-hfile");
148  EST_TokenStream rts,hts;
149  EST_Item *r, *h;
150  static EST_Regex id("^(.*)$");
151  int sents=0;
152  int total,ins,del,sub,correct;
153  int s_total,s_ins,s_del,s_sub,s_correct;
154 
155  if (rts.open(reffile) != 0) {
156  exit(-1);
157  }
158  if (hts.open(hypofile) != 0) {
159  exit(-1);
160  }
161  s_total=s_ins=s_del=s_sub=s_correct=0;
162 
163  while (!rts.eof())
164  {
165  EST_Utterance u;
166 
167  load_sentence(u,"ref",rts);
168  load_sentence(u,"hypo",hts);
169  r = u.relation("ref")->last();
170  h = u.relation("hypo")->last();
171  if ((!r->name().matches(id)) ||
172  (r->name() != h->name()))
173  {
174  cerr << "Align: failed to match sentence " <<
175  sents << " at id " << r->name() << endl;
176  }
177  else
178  {
179  // Ids aren't counted as words
180  r->unref_all();
181  h->unref_all();
182  align(u,"ref","hypo","align");
183  // This doesn't give exactly the same as the NIST tools
184  // even though it should (actually I think its better)
185 // dp_match(*u.relation("ref"),
186 // *u.relation("hypo"),
187 // *u.create_relation("align"),
188 // 3,3,4);
189  align_score(u,"ref","hypo","align",
190  total,ins,del,sub,correct);
191  s_total += total;
192  s_ins += ins;
193  s_del += del;
194  s_sub += sub;
195  s_correct += correct;
196  }
197  sents++;
198  }
199 
200  rts.close();
201  hts.close();
202  fprintf(stdout,"sentences %d\n",sents);
203  fprintf(stdout,"words %d\n",s_total);
204  fprintf(stdout,"insertions %d\n",s_ins);
205  fprintf(stdout,"deletions %d\n",s_del);
206  fprintf(stdout,"substitutions %d\n",s_sub);
207  fprintf(stdout,"correct %d\n",s_correct);
208  fprintf(stdout,"WER %f\n",(100.0 * (float)(s_ins+s_del+s_sub))/s_total);
209 }
210 
211 static void load_sentence(EST_Utterance &u,
212  const EST_String &relname,
213  EST_TokenStream &ts)
214 {
215  EST_Relation *r = u.create_relation(relname);
216 
217  do
218  {
219  EST_Item *i = r->append();
220  i->set_name(ts.get());
221  }
222  while ((!ts.eoln()) && (!ts.eof()));
223 }
224 
225 static void load_sentence(EST_Utterance &u,
226  const EST_String &relname,
227  EST_String &relval)
228 {
229  EST_Relation *r = u.create_relation(relname);
230  EST_StrList strlist;
231  StringtoStrList(relval, strlist, " ");
233 
234  for (iter.begin(strlist); iter; ++iter)
235  {
236  EST_Item *i = r->append();
237  i->set_name(*iter);
238  }
239 }
240 
241 static void align_score(EST_Utterance &u, const EST_String &refrel,
242  const EST_String &hyporel,
243  const EST_String &alignrel,
244  int &total,int &ins,int &del,int &sub,int &correct)
245 {
246  // Score alignment
247  EST_Item *ri,*hi;
248  total=ins=del=correct=sub=0;
249 
250  for (ri=u.relation(refrel)->first(),
251  hi=u.relation(hyporel)->first();
252  ri;
253  ri=ri->next(),hi=hi->next())
254  {
255  for ( ; (as(hi,alignrel) == 0) && hi ; hi=hi->next())
256  {
257  fprintf(stdout,"inserted: %s\n",(const char *)hi->name());
258  ins++;
259  }
260  for ( ; (daughter1(ri,alignrel) == 0) && ri; ri=ri->next())
261  {
262  fprintf(stdout,"deleted: %s\n",(const char *)ri->name());
263  del++;
264  }
265  if (!ri)
266  break;
267  if (name_distance(ri,daughter1(ri,alignrel)) == 0)
268  {
269  fprintf(stdout,"correct: %s\n",(const char *)ri->name());
270  correct++;
271  }
272  else
273  {
274  fprintf(stdout,"substituted: %s\n",(const char *)ri->name());
275  sub++;
276  }
277  }
278  // For trailing hypothesized (or ref is nil)
279  for ( ; hi ; hi=hi->next())
280  {
281  fprintf(stdout,"inserted: %s\n",(const char *)hi->name());
282  ins++;
283  }
284 
285  total = u.relation(refrel)->length();
286 
287 
288 // fprintf(stdout,"total %d ins %d del %d subs %d correct %d\n",
289 // total, ins, del, sub, correct);
290 }
291 
292 static int name_distance(EST_Item *r,EST_Item *h)
293 {
294  EST_String rname = r->name();
295  EST_String hname = h->name();
296  if ((rname == hname) ||
297  (downcase(rname) == downcase(hname)))
298  return 0;
299  else
300  return 1;
301 }
302 
304  const EST_String &refrel,
305  const EST_String &hyporel,
306  const EST_String &alignrel)
307 {
308  // Align refrel to hyporel by alignrel
309  int r_size = utt.relation(refrel)->length();
310  int h_size = utt.relation(hyporel)->length();
311  EST_Item *ri = utt.relation(refrel)->first();
312  EST_Item *hi = utt.relation(hyporel)->first();
313  int i,j;
314  int insdel_cost = 3;
315  int subs_cost = 4;
316  float to_insert,to_del,to_subs;
317  float cost;
318 
319  EST_Relation *ar = utt.create_relation(alignrel);
320 
321  EST_FMatrix dpt(r_size+1,h_size+1);
322  EST_IMatrix dpp(r_size+1,h_size+1);
323 
324  // Initialise first row and column
325  dpt(0,0) = subs_cost * name_distance(ri,hi);
326  dpp(0,0) = 0;
327  for (i=1; i<r_size+1; i++)
328  {
329  dpt(i,0) = insdel_cost + dpt(i-1,0);
330  dpp(i,0) = -1; // deletion
331  }
332  for (j=1; j < h_size+1; j++)
333  {
334  dpt(0,j) = insdel_cost + dpt(0,j-1);
335  dpp(0,j) = 1; // insertion
336  }
337 
338  ri = utt.relation(refrel)->first();
339  for (i=1; ri; ri=ri->next(),i++)
340  {
341  ar->append(ri); // for use later
342  hi = utt.relation(hyporel)->first();
343  for (j=1; hi; hi=hi->next(),j++)
344  {
345  cost = name_distance(ri,hi);
346  to_insert = insdel_cost + dpt(i,j-1);
347  to_del = insdel_cost + dpt(i-1,j);
348  to_subs = (cost * subs_cost) + dpt(i-1,j-1);
349  if (to_insert < to_del)
350  {
351  if (to_insert < to_subs)
352  {
353  dpt(i,j) = to_insert;
354  dpp(i,j) = 1;
355  }
356  else
357  {
358  dpt(i,j) = to_subs;
359  dpp(i,j) = 0;
360  }
361  }
362  else
363  {
364  if (to_del < to_subs)
365  {
366  dpt(i,j) = to_del;
367  dpp(i,j) = -1;
368  }
369  else
370  {
371  dpt(i,j) = to_subs;
372  dpp(i,j) = 0;
373  }
374  }
375  }
376  }
377 
378 // for (i=1,ri=utt.relation(refrel)->first(); i < r_size+1; i++,ri=ri->next())
379 // {
380 // fprintf(stdout,"%10s ",(const char *)ri->name());
381 // for (j=1,hi=utt.relation(hyporel)->first(); j<h_size+1; j++,hi=hi->next())
382 // fprintf(stdout,"%3d/%2d ",(int)dpt(i,j),dpp(i,j));
383 // fprintf(stdout,"\n");
384 // }
385 
386  for (i=r_size,j=h_size,
387  ri=utt.relation(refrel)->last(),
388  hi=utt.relation(hyporel)->last();
389  ri; i--,ri=ri->prev())
390  {
391  while (dpp(i,j) == 1)
392  {
393  j--;
394 // fprintf(stdout,"skipping hi %s\n",
395 // (const char *)hi->name());
396  hi=hi->prev();
397  }
398  if (dpp(i,j) == 0)
399  {
400 // fprintf(stdout,"linking %s %s\n",
401 // (const char *)ri->name(),
402 // (const char *)hi->name());
403  append_daughter(ri,alignrel,hi);
404  j--;
405  hi=hi->prev();
406  }
407 // else
408 // fprintf(stdout,"skipping ri %s\n",
409 // (const char *)ri->name());
410  }
411 }
EST_TokenStream & get(EST_Token &t)
get next token in stream
Definition: EST_Token.cc:499
EST_Item * first() const
Definition: EST_Relation.h:130
const EST_String name() const
Definition: EST_Item.h:250
EST_Item * append_daughter(EST_Item *n, EST_Item *p=0)
Definition: EST_Item.cc:594
EST_Item * as(const EST_Item *n, const char *relname)
Definition: EST_Item.h:419
EST_Relation * create_relation(const EST_String &relname)
create a new relation called n.
A Regular expression class to go with the CSTR EST_String class.
Definition: EST_Regex.h:56
void close(void)
Close stream.
Definition: EST_Token.cc:419
int length() const
EST_Item * last() const
Definition: EST_Relation.h:132
void unref_all()
Definition: EST_Item.cc:189
int open(const EST_String &filename)
open a EST_TokenStream for a file.
Definition: EST_Token.cc:213
int eof()
end of file
Definition: EST_Token.h:362
int main(int argc, char **argv)
Definition: align_main.cc:69
void set_name(const EST_String &name) const
Definition: EST_Item.h:254
EST_String downcase(const EST_String &s)
Definition: EST_String.cc:942
EST_FMatrix sub(const EST_FMatrix &a, ssize_t row, ssize_t col)
Definition: vec_mat_aux.cc:187
EST_Item * prev() const
Definition: EST_Item.h:350
void StringtoStrList(EST_String s, EST_StrList &l, EST_String sep)
Convert a EST_String to a EST_StrList by separating tokens in s delimited by the separator sep...
void align(EST_Utterance &utt, const EST_String &refrel, const EST_String &hyporel, const EST_String &alignrel)
Definition: align_main.cc:303
EST_String outfile
bool dp_match(const EST_Relation &lexical, const EST_Relation &surface, EST_Relation &match, float ins, float del, float sub)
int matches(const char *e, ssize_t pos=0) const
Exactly match this string?
Definition: EST_String.cc:651
const V & val(const K &rkey, bool m=0) const
return value according to key (const)
Definition: EST_TKVL.cc:145
EST_Item * next() const
Definition: EST_Item.h:348
void begin(const Container &over)
Set the iterator ready to run over this container.
int present(const K &rkey) const
Returns true if key is present.
Definition: EST_TKVL.cc:222
EST_Relation * relation(const char *name, int err_on_not_found=1) const
get relation by name
EST_Item * append(EST_Item *si)
Definition: EST_Relation.cc:88
EST_String
int parse_command_line(int argc, char *argv[], const EST_String &usage, EST_StrList &files, EST_Option &al, int make_stdio=1)
Definition: cmd_line.cc:101
EST_Item * daughter1(const EST_Item *n)
return first daughter of n
int eoln()
end of line
Definition: EST_Token.cc:832