The bug regarding novoapi for Python 3.6 is solved. The detail can be found in novoapi_for_python3x/readme.txt
This commit is contained in:
6
novoapi_for_python3x/asr/__init__.py
Normal file
6
novoapi_for_python3x/asr/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#import segments
|
||||
#import spraaklab
|
||||
from . import segments
|
||||
from . import spraaklab
|
4
novoapi_for_python3x/asr/segments/__init__.py
Normal file
4
novoapi_for_python3x/asr/segments/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from .segments import Segmentation
|
||||
from .praat import seg2tg
|
79
novoapi_for_python3x/asr/segments/praat.py
Normal file
79
novoapi_for_python3x/asr/segments/praat.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
# (c) 2015--2018 NovoLanguage, author: David A. van Leeuwen
|
||||
|
||||
import codecs
|
||||
|
||||
def print_header(output, begin, end, nr_tiers):
|
||||
print >> output, 'File type = "ooTextFile"'
|
||||
print >> output, 'Object class = "TextGrid"'
|
||||
print >> output, ''
|
||||
print >> output, 'xmin = %s' % begin
|
||||
print >> output, 'xmax = %s' % end
|
||||
print >> output, 'tiers? <exists>'
|
||||
print >> output, 'size = %d' % nr_tiers
|
||||
print >> output, 'item []:'
|
||||
|
||||
|
||||
def print_info_tier(output, title, begin, end, label):
|
||||
print >> output, '\titem [%d]:' % 0
|
||||
print >> output, '\t\tclass = "IntervalTier"'
|
||||
print >> output, '\t\tname = "%s"' % title
|
||||
print >> output, '\t\txmin = %s' % begin
|
||||
print >> output, '\t\txmax = %s' % end
|
||||
print >> output, '\t\tintervals: size = %d' % 1
|
||||
|
||||
print >> output, '\t\tintervals [1]:'
|
||||
print >> output, '\t\t\txmin = %s' % begin
|
||||
print >> output, '\t\t\txmax = %s' % end
|
||||
print >> output, '\t\t\ttext = "%s"' % label
|
||||
|
||||
|
||||
def print_tier(output, title, begin, end, segs, format, formatter):
|
||||
print >> output, '\titem [%d]:' % 0
|
||||
print >> output, '\t\tclass = "IntervalTier"'
|
||||
print >> output, '\t\tname = "%s"' % title
|
||||
print >> output, '\t\txmin = %s' % begin
|
||||
print >> output, '\t\txmax = %s' % end
|
||||
print >> output, '\t\tintervals: size = %d' % len(segs)
|
||||
|
||||
count = 1
|
||||
for seg in segs:
|
||||
#print seg
|
||||
print >> output, '\t\tintervals [%d]:' % count
|
||||
print >> output, '\t\t\txmin = %s' % repr(int(seg['begin']) / 100.0)
|
||||
print >> output, '\t\t\txmax = %s' % repr(int(seg['end']) / 100.0)
|
||||
string = '\t\t\ttext = "' + format + '"'
|
||||
print >> output, string % formatter(seg['label'])
|
||||
count += 1
|
||||
|
||||
|
||||
def seg2tg(fname, segments):
|
||||
if not segments:
|
||||
return
|
||||
output = codecs.open(fname, "w", encoding="utf-8")
|
||||
|
||||
confidences = []
|
||||
word_labels = []
|
||||
phones = []
|
||||
|
||||
for s in segments:
|
||||
conf = s.llh if hasattr(s, "llh") else s.score
|
||||
confidences.append({'begin': s.begin, 'end': s.end, 'label': conf})
|
||||
word_labels.append({'begin': s.begin, 'end': s.end, 'label': s.label})
|
||||
for p in s.phones:
|
||||
phones.append({'begin': p.begin, 'end': p.end, 'label': p.label})
|
||||
|
||||
|
||||
begin = repr(int(segments[0].begin) / 100.0)
|
||||
end = repr(int(segments[-1].end) / 100.0)
|
||||
|
||||
nr_tiers = 3
|
||||
print_header(output, begin, end, nr_tiers)
|
||||
#print_tier(output, "confidence", begin, end, confidences, ('%.3f', lambda x: x))
|
||||
#print_tier(output, "words", begin, end, word_labels, ('%s', lambda x: x))
|
||||
#print_tier(output, "phones", begin, end, phones, ('%s', lambda x: x))
|
||||
print_tier(output, "confidence", begin, end, confidences, '%.3f', lambda x: x)
|
||||
print_tier(output, "words", begin, end, word_labels, '%s', lambda x: x)
|
||||
print_tier(output, "phones", begin, end, phones, '%s', lambda x: x)
|
||||
|
||||
output.close()
|
99
novoapi_for_python3x/asr/segments/segments.py
Normal file
99
novoapi_for_python3x/asr/segments/segments.py
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python
|
||||
# (c) 2015--2018 NovoLanguage, author: David A. van Leeuwen
|
||||
|
||||
## These classes can be initialized with dictionaries, as they are returned by the python spraaklab recognition system.
|
||||
|
||||
class Segment(object):
|
||||
def __init__(self, segment):
|
||||
self.begin = segment["begin"]
|
||||
self.end = segment["end"]
|
||||
self.begintime = segment.get("beginTime", self.begin / 100.0)
|
||||
self.endtime = segment.get("endTime", self.end / 100.0)
|
||||
self.label = segment["label"]
|
||||
self.score = segment["score"]
|
||||
if "llh" in segment:
|
||||
self.llh = segment["llh"]
|
||||
if "phones" in segment:
|
||||
self.type = "word"
|
||||
self.phones = Segmentation(segment["phones"], ["sil"])
|
||||
if hasattr(self.phones[0], "llh"):
|
||||
self.minllh = min([s.llh for s in self.phones]) ## the current word llh for error detection
|
||||
else:
|
||||
self.type = "phone"
|
||||
|
||||
def __repr__(self):
|
||||
res = "%8.3f -- %8.3f score %8.3f " % (self.begintime, self.endtime, self.score)
|
||||
if hasattr(self, "llh"):
|
||||
res += "llh %8.3f " % self.llh
|
||||
res += self.label.encode("utf8")
|
||||
return res
|
||||
|
||||
def export(self):
|
||||
r = {"begin": self.begin, "end": self.end, "label": self.label, "score": self.score, "type": self.type}
|
||||
if hasattr(self, "llh"):
|
||||
r["llh"] = self.llh
|
||||
if hasattr(self, "phones"):
|
||||
r["phones"] = self.phones.export()
|
||||
return r
|
||||
|
||||
class Segmentation(object):
|
||||
def __init__(self, segments, sils=["<s>", "</s>", "!sil"]):
|
||||
"""Create a segmentation from a spraaklab recognition structure.
|
||||
segments: an array of words (or phones), represented by a dict with
|
||||
"begin", "end", "label", "score", and "llh" keys. Words can also have
|
||||
"phones" which is another array of segments."""
|
||||
self.segments = [Segment(s) for s in segments]
|
||||
if self.segments:
|
||||
self.type = self.segments[0].type
|
||||
else:
|
||||
self.type = None
|
||||
self.sils = sils
|
||||
self.orig = segments ## in case we want to have access to the original recognition structure
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.segments[item]
|
||||
|
||||
def __repr__(self):
|
||||
ns = len(self.segments)
|
||||
res = "Segmentation with %d %s%s" % (ns, self.type, "" if ns==1 else "s")
|
||||
for seg in self.segments:
|
||||
res += "\n " + repr(seg)
|
||||
return res
|
||||
|
||||
def __len__(self):
|
||||
return len(self.segments)
|
||||
|
||||
def score(self, skip=None):
|
||||
if not skip:
|
||||
skip = self.sils
|
||||
s = 0.0
|
||||
for seg in self.segments:
|
||||
if seg.label not in skip:
|
||||
s += seg.score
|
||||
return s
|
||||
|
||||
def llhs(self, skip=None):
|
||||
if not skip:
|
||||
skip = self.sils
|
||||
return [seg.llh for seg in self.segments if hasattr(seg, "llh") and seg.label not in skip]
|
||||
|
||||
def llh(self, skip=None):
|
||||
return sum(self.llhs(skip))
|
||||
|
||||
def minllh(self, skip=None):
|
||||
llhs = self.llhs(skip)
|
||||
if llhs:
|
||||
return min(llhs)
|
||||
else:
|
||||
return None
|
||||
|
||||
def labels(self, skip=None):
|
||||
if not skip:
|
||||
skip = self.sils
|
||||
return [seg.label for seg in self.segments if seg.label not in skip]
|
||||
|
||||
def sentence(self, skip=None):
|
||||
return " ".join(self.labels(skip))
|
||||
|
||||
def export(self):
|
||||
return [seg.export() for seg in self.segments]
|
4
novoapi_for_python3x/asr/spraaklab/__init__.py
Normal file
4
novoapi_for_python3x/asr/spraaklab/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#import schema
|
||||
from . import schema
|
273
novoapi_for_python3x/asr/spraaklab/schema.py
Normal file
273
novoapi_for_python3x/asr/spraaklab/schema.py
Normal file
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python
|
||||
## (c) 2017 NovoLanguage, author: David A. van Leeuwen
|
||||
|
||||
## The purpose of this to define the grammar structure in a json schema, so that it can be validated,
|
||||
## (de)serialized, and perhaps even automatically converted to a Python class structure.
|
||||
|
||||
import json
|
||||
import jsonschema
|
||||
|
||||
grammar_schema_v10 = {
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"title": "NovoLanguage grammar",
|
||||
"description": "A grammar specification for the NovoLanguage Automatic Speech Recognition",
|
||||
"$ref": "#/definitions/group",
|
||||
"definitions": {
|
||||
"phones": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"pronunciation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"phones": {
|
||||
"$ref": "#/definitions/phones"
|
||||
},
|
||||
"syllables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/syllable"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"description": "ID to distinguish this pronunciation from other variants"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": ["phones"]
|
||||
},
|
||||
"syllable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"begin": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"end": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"stress": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"tone": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": ["begin", "end"]
|
||||
},
|
||||
"word": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["word"]
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"pronunciation": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/pronunciation"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/pronunciation"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/phones"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/phones"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
"syllables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/syllable"
|
||||
}
|
||||
},
|
||||
"graphemes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"description": "ID to distinguish this word from other words (with possibly the same label)"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": ["label"]
|
||||
},
|
||||
"element": {
|
||||
"title": "element",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/word"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/group"
|
||||
},
|
||||
{
|
||||
"type": ["string", "null"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"group": {
|
||||
"title": "element group",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["sequence", "alternatives", "order"]
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/element"
|
||||
},
|
||||
"minItems": 1,
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": ["kind", "elements"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grammar_schema_v01 = {
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"title": "NovoLanguage grammar v0.1",
|
||||
"description": "A grammar specification for the NovoLanguage Automatic Speech Recognition",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["multiple_choice", "word_order"]
|
||||
},
|
||||
"parts": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 5,
|
||||
"items": {
|
||||
"type": ["string", "array"],
|
||||
"items": {
|
||||
"type": ["string"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grammar_rpc_schema = {
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"title": "NovoLanguage RPC grammar",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["confusion_network"]
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "v0.1"
|
||||
},
|
||||
"data": {
|
||||
"type": "object"
|
||||
},
|
||||
"return_dict": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"return_objects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["dict", "grammar"]
|
||||
}
|
||||
},
|
||||
"phoneset": {
|
||||
"type": "string",
|
||||
"enum": ["cmu69", "novo70", "mdbg115"]
|
||||
},
|
||||
"parallel_silence": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["type", "data"]
|
||||
}
|
||||
|
||||
def validate(object, schema=grammar_schema_v10):
|
||||
#if isinstance(object, basestring):
|
||||
if isinstance(object, str):
|
||||
object = json.loads(object)
|
||||
if not isinstance(object, dict):
|
||||
raise TypeError("Expected dict or json string")
|
||||
try:
|
||||
jsonschema.validate(object, schema)
|
||||
except jsonschema.ValidationError:
|
||||
return False
|
||||
except Exception:
|
||||
raise
|
||||
else:
|
||||
return True
|
||||
|
||||
def validate_rpc_grammar(message):
|
||||
"""validate an rpc grammar message"""
|
||||
if not validate(message, grammar_rpc_schema):
|
||||
raise ValueError("Not a valid RPC grammar")
|
||||
version = message.get("version", "0.1")
|
||||
data = message["data"]
|
||||
if version == "0.1":
|
||||
if not validate(data, grammar_schema_v01):
|
||||
raise ValueError("Not a valid grammar v0.1")
|
||||
elif version == "1.0":
|
||||
if not validate(data, grammar_schema_v10):
|
||||
raise ValueError("Not a valid grammar v1.0")
|
||||
else:
|
||||
raise ValueError("Unsupported schema version")
|
||||
|
||||
|
||||
## test
|
||||
def test(data=None):
|
||||
if not data:
|
||||
data = {"kind": "sequence", "elements": [
|
||||
{"kind": "alternatives", "elements": ["a plain string", "an alternative string"]},
|
||||
{"kind": "word", "label": "a word", "pronunciation": {"phones": ["ah", "w", "er", "d"]}},
|
||||
{"kind": "order", "elements": [{"kind": "word", "label": "another word", "visible": False}, "last word"]}]}
|
||||
try:
|
||||
jsonschema.validate(data, schema)
|
||||
except jsonschema.ValidationError as e:
|
||||
#print data, "validated not OK", e.message
|
||||
print("{0} validated not OK {1}".format(data, e.message))
|
||||
else:
|
||||
#print data, "validated OK"
|
||||
print("{0} validated OK".format(data))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
Reference in New Issue
Block a user