Skip to content

Commit ef75404

Browse files
authored
Merge pull request #4 from clamsproject/normalization
Added naive name normalization functionality
2 parents 934b38e + fc072ea commit ef75404

4 files changed

Lines changed: 64 additions & 19 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ annotations generated by an OCR app such as [doctr-wrapper](https://github.com/c
2323
or [Tesseract](https://github.com/clamsproject/app-tesseractocr-wrapper).
2424

2525
The app outputs a [`TextDocument`]('https://mmif.clams.ai/vocabulary/TextDocument/v1/') annotation corresponding to each
26-
of the input annotations, containing the original `text` split into `name-as-written` and `attributes` fields in an escaped
27-
JSON string. Each annotation also contains identifying information for the new annotation, source annotation, and source [`VideoDocument`]('https://mmif.clams.ai/vocabulary/VideoDocument/v1/').
26+
of the input annotations, containing the original `text` split into `name-as-written` and `attributes` fields
27+
in an escaped JSON string; optionally, the output may also include a `name-normalized` field.
28+
Each annotation also contains identifying information for the new annotation,
29+
source annotation, and source [`VideoDocument`]('https://mmif.clams.ai/vocabulary/VideoDocument/v1/').
2830
For more details, see the `output` section of the app metadata.

app.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import interpreter
1414

15+
1516
class HeuristicChyronInterpreter(ClamsApp):
1617

1718
def __init__(self):
@@ -23,27 +24,27 @@ def _appmetadata(self):
2324
pass
2425

2526
def _annotate(self, mmif: Mmif, **parameters) -> Mmif:
26-
2727
self.mmif = mmif if isinstance(mmif, Mmif) else Mmif(mmif)
2828

2929
new_view = self.mmif.new_view()
3030
self.sign_view(new_view, parameters)
3131
new_view.new_contain(DocumentTypes.TextDocument)
3232

3333
for doc in self.mmif.get_documents_by_type(DocumentTypes.TextDocument):
34-
self._run_interpreter(doc, new_view)
34+
self._run_interpreter(doc, new_view, not parameters.get('note4mode'))
3535

3636
return self.mmif
3737

38-
def _run_interpreter(self, doc, new_view):
38+
def _run_interpreter(self, doc, new_view, do_normalize):
3939
"""
4040
Run the chyron interpreter over the document and add annotations to the view.
4141
"""
42-
text = doc.text_value
43-
content = interpreter.split_text(text)
42+
content = interpreter.split_text(doc.text_value, do_normalize)
4443
mmif_vids = self.mmif.get_documents_by_type(DocumentTypes.VideoDocument)
4544
vid_id = mmif_vids[0].long_id
46-
out_doc = new_view.new_textdocument(text=content, document=vid_id, origin=doc.long_id, provenance='derived', mime='application/json')
45+
out_doc = new_view.new_textdocument(text=content, document=vid_id, origin=doc.long_id, provenance='derived',
46+
mime='application/json')
47+
4748

4849
def get_app():
4950
"""

interpreter.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,57 @@
44
Reformats chyron text output from an OCR app as a name and list of attributes
55
presented as an escaped json string.
66
"""
7-
8-
__VERSION__ = 'v0.1'
9-
107
import json
118

12-
def split_text(text: str) -> str:
9+
10+
def split_text(text: str, normalize=False) -> str:
1311
"""
1412
Splits input string on newline and creates a dictionary with 'name-as-written'
15-
and 'attributes' keys, with string content as values.
13+
and 'attributes' keys, with string content as values. If normalization parameter
14+
is True, also adds a 'name-normalized' item to the dictionary.
1615
Returns escaped json string of dictionary.
1716
"""
1817
content = {}
19-
lines = text.split("\n")
18+
lines = list(filter(lambda x: x is not None and len(x) > 0, text.split("\n")))
2019
if lines:
2120
content["name-as-written"] = lines[0]
22-
content["attributes"] = [x for x in lines[1:] if x != '']
21+
if normalize:
22+
content["name-normalized"] = normalize_text(lines[0])
23+
last_line_processed = 0
24+
else:
25+
if len(lines) > 1:
26+
content["name-normalized"] = lines[1]
27+
last_line_processed = 1
28+
else:
29+
content["name-normalized"] = ''
30+
last_line_processed = 0
31+
content["attributes"] = lines[(last_line_processed+1):]
2332
content = json.dumps(content)
2433

25-
return content
34+
return content
35+
36+
37+
def normalize_text(text: str) -> str:
38+
"""
39+
Converts input string to a 'normalized' name form.
40+
Returns text in a 'Lastname, Firstname' format.
41+
"""
42+
# Depending on the quality of the OCR, this may not represent the original chyron as well as desired.
43+
44+
if text.find('.') != -1: # Will only strip 1 abbreviated title, not multiple or full-word titles.
45+
text = text[text.find('.') + 1:]
46+
if text.find('(') != -1:
47+
text = text[:text.find('(')]
48+
if text.find(', ') != -1:
49+
text = text[:text.find(', ')]
50+
parts = text.split()
51+
normal_parts = []
52+
for word in parts:
53+
word = ''.join([word[0].upper(), word[1:].lower()])
54+
normal_parts.append(word)
55+
if len(normal_parts) == 1:
56+
normal_name = normal_parts[0]
57+
else:
58+
normal_name = ' '.join(normal_parts[1:]) + ', ' + normal_parts[0] # to handle names with more than two words
59+
60+
return normal_name

metadata.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def appmetadata() -> AppMetadata:
1818
- https://sdk.clams.ai/appmetadata.html metadata specification.
1919
- https://sdk.clams.ai/autodoc/clams.appmetadata.html python API
2020
21+
2122
:return: AppMetadata object holding all necessary information.
2223
"""
2324
metadata = AppMetadata(
@@ -32,12 +33,18 @@ def appmetadata() -> AppMetadata:
3233
# I/O Spec
3334
in_doc = metadata.add_input(DocumentTypes.TextDocument)
3435
in_doc.add_description('Text content transcribed from video input by docTR/Tesseract/LLAVA.')
35-
out_doc = metadata.add_output(DocumentTypes.TextDocument, **{'document': '*', 'origin': '*'})
36+
out_doc = metadata.add_output(DocumentTypes.TextDocument,
37+
document='*', origin='*', provenance='derived', mime='application/json')
3638
out_doc.add_description('Reformatted chyron text. `document` property stores the ID of the original source '
3739
'`VideoDocument`. `origin` property stores the ID of the original OCR `TextDocument` '
38-
'annotation. ')
40+
'annotation. Reformatted text is escaped JSON string with three fields: '
41+
'`name-as-written`, `name-normalized`, and `attributes`. ')
3942

40-
# No runtime parameters besides universals.
43+
metadata.add_parameter(name='note4mode', default=False, type='boolean',
44+
description='Boolean to set the app to run in "note-4" mode and to take the second line '
45+
'(if available) from the input text to be the `name-normalized` value. The '
46+
'default is false, which means the app will try to generate normalization from'
47+
'`name-as-written` (from the first line) value. ')
4148

4249
return metadata
4350

0 commit comments

Comments
 (0)