import json
from .region import ROI, Residual
from .annotate_ROI.utils import is_approximately_square
ROI_TYPES = {'h': 'Handwritten',
'c': 'Checkbox',
'b': 'Barcode',
'n': 'Number'}
[docs]
class LogsheetConfig:
"""
Class to store and represent the whole config.
"""
def __init__(self, regions, residuals, height=None, width=None):
self.regions = regions
self.residuals = residuals
self.height = height
self.width = width
[docs]
def add_roi(self, start_x, start_y, end_x, end_y, varname=None, content_type=None):
"""Append a new ROI rectangle to ``regions``.
Args:
start_x: Left edge (inclusive).
start_y: Top edge (inclusive).
end_x: Right edge.
end_y: Bottom edge.
varname: Optional variable label.
content_type: Optional type string (e.g. ``Handwritten``).
Returns:
``None``.
"""
self.regions.append(ROI(start_x, start_y, end_x, end_y, varname, content_type))
[docs]
def delete_last_region(self):
"""Remove the most recently added ROI, if any.
Returns:
``None``.
"""
if self.regions:
self.regions.pop()
[docs]
def update(self, index, attribute, value):
"""
Update content type of particular region
Args:
index (int): region identifier
attribute (str): attribute to be set
value (str): desired value
Returns:
``None``.
"""
if attribute == 'content_type' and value is not None:
value = ROI_TYPES[value]
setattr(self.regions[index], attribute, value)
[docs]
def announce_status(self, index, clean_len=20):
"""
Print current region status to command line
Args:
index (int): region identifier
clean_len (int, optional): length of text to clear. Defaults to 20.
Returns:
``None``.
"""
print(str(self.regions[index]) + ' ' * clean_len, end='\r')
[docs]
def export_to_json(self, output_file, remove_unannotated=False):
"""
Output logsheet config to JSON file
Args:
output_file (str): location of output file.
remove_unannotated (bool, optional): Remove ROIs without any content type specified. Defaults to False.
Returns:
``None``.
"""
output = {'to_ignore': [], 'content': [], 'height': self.height, 'width': self.width}
if remove_unannotated:
for region in self.regions:
if region.content_type is not None:
output['content'].append({'coords': region.get_coords(), 'varname': region.varname, 'type': region.content_type})
else:
for region in self.regions:
output['content'].append({'coords': region.get_coords(), 'varname': region.varname, 'type': region.content_type})
for residual in self.residuals:
output['to_ignore'].append({'coords': residual.get_coords(), 'content': residual.expected_content})
with open(output_file, 'w') as f:
json.dump(output, f, sort_keys=True, indent=4)
[docs]
def import_from_json(self, input_file):
"""
Import losheet config from a JSON file
Args:
input_file (str): path to JSON file
Returns:
``None``; mutates ``self`` regions, residuals, height, and width.
"""
with open(input_file, 'r') as f:
data = json.load(f)
self.height = int(data['height'])
self.width = int(data['width'])
for residual in data['to_ignore']:
self.residuals.append(Residual(*residual['coords'],
expected_content=residual['content']))
index = 0
for region in data['content']:
varname = region['varname']
if varname is None:
varname = str(index)
index += 1
content_type = region['type']
if content_type is None:
if is_approximately_square(*region['coords'], self.width, self.height):
content_type = 'Checkbox'
else:
content_type = 'Handwritten'
self.regions.append(ROI(*region['coords'],
varname=varname,
content_type=content_type))