Source code for pyplis.forms
# -*- coding: utf-8 -*-
#
# Pyplis is a Python library for the analysis of UV SO2 camera data
# Copyright (C) 2017 Jonas Gliss (jonasgliss@gmail.com)
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License a
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division)
from collections import OrderedDict as od
from pyplis import logger
import six
[docs]class FormCollectionBase(object):
"""Abtract base class representing collection of geometrical forms.
Abstract class providing basic functionality for object collections.
Note that the basic management functions for adding / deleting forms
:func:`add` , :func:`remove` create the objects based on start (x,y)
and stop (x,y) points, i.e. [x0, y0, x1, y1]
This class and classes inheriting from it show large similarities to
dictionaries
"""
[docs] def __init__(self, forms_dict=None):
"""Class initialisation."""
if forms_dict is None:
forms_dict = {}
self._forms = od()
self.id_count = 0
self.type = ""
for key, val in six.iteritems(forms_dict):
self[key] = val
[docs] def add(self, x0, y0, x1, y1, id=None):
"""Create a new form from input coordinates.
:param int x0: x coordinate of start point
:param int y0: y coordinate of start point
:param int x1: x coordinate of stop point
:param int y1: y coordinate of stop point
:param str id: name of form (if None, it will be set automatically
based on current object counter value)
"""
if not self.check_input(x0, y0, x1, y1):
raise ValueError("Check coordinates...")
if id in self.keys():
raise KeyError("A form with name %s already exists" % id)
if x1 < x0:
x0, y0, x1, y1 = x1, y1, x0, y0
if id is None:
id = self.type + str(self.id_count)
self._forms[id] = [x0, y0, x1, y1]
self.id_count += 1
return True
@property
def tot_num(self):
"""Return current number of forms in collection."""
return len(self._forms.keys())
[docs] def update(self, x0, y0, x1, y1, id):
"""Update an existing form (or create new if it does not exist)."""
if id in self._forms.keys():
logger.info("Form with ID " + str(id) + " could not be updated because "
"it does not exist, creating new form instead")
self.remove(id)
if self.add(x0, y0, x1, y1, id):
return 1
return 0
[docs] def check_input(self, x0, y0, x1, y1):
"""Check if input for adding a form has the right format."""
items = [x0, y0, x1, y1]
for item in items:
if not any([isinstance(item, tp) for tp in [int, float]]):
return False
return True
[docs] def remove(self, id):
"""Remove one form from collection.
:param str id: string id of the form to be deleted
"""
if id not in self._forms.keys():
logger.info("Error: could not delete form " + id + " from "
"collection, no such form in collection")
return 0
logger.info("Delete form " + id + " from collection")
del self._forms[id]
[docs] def rename(self, current_id, new_id):
"""Rename one form."""
if new_id in self._forms.keys():
raise Exception("Error renaming form %s to %s. Such a form already"
" exists in collection, delete the old one first "
% (current_id, new_id))
l = self._forms[current_id]
self.add(l[0], l[1], l[2], l[3], new_id)
del self._forms[current_id]
"""
Helpers
"""
[docs] def has_form(self, form_id):
"""Check if collection has a form with input ID, returns bool."""
if form_id in self._forms.keys():
return True
return False
[docs] def form_info(self, form_id):
"""Print information about one of the forms in this collection.
:param str form_id: string ID of form
"""
f = self[form_id]
add_str = ""
if f is None:
add_str = "(Form does not exist in collection)"
f = ["undefined", "undefined", "undefined", "undefined"]
s = ("ID: %s %s\nStart (x,y): %s, %s\nStop (x,y): %s, %s\n"
% (form_id, add_str, f[0], f[1], f[2], f[3]))
return s
[docs] def get(self, form_id):
"""Get one form.
:param str form_id: name of the form within collection
"""
return self[form_id]
"""
Magic methods
"""
def __call__(self, key):
"""Make object callable.
Returns form based on input key (see also :func:`__getitem__`)
"""
if key in self._forms.keys():
return self._forms[key]
else:
# Default behaviour
raise AttributeError
def __setitem__(self, key, val):
"""Set item method.
Adds one form to this collection
:param str key: string ID of new form
:param list val: list with start / stop coords ``[x0,y0, x1, y1]``
"""
try:
self.add(val[0], val[1], val[2], val[3], key)
except Exception as e:
logger.info("Adding form failed_ %s" % repr(e))
def __getitem__(self, name):
"""Get item.
Returns form with corresponding to input name (if it exists)
"""
try:
return self._forms[name]
except BaseException:
logger.info("No such form: " + str(name))
def __str__(self):
s = ("\nForm collection %s\n-----------------------\n" % self.type)
if not bool(self.keys()):
return s + "No forms available"
for key, val in six.iteritems(self._forms):
s = (s + "ID: %s\nStart (x,y): %s, %s\n"
"Stop (x,y): %s, %s\n"
% (key, val[0], val[1], val[2], val[3]))
return s
[docs]class LineCollection(FormCollectionBase):
"""Class specifying line objects on images."""
[docs] def __init__(self, forms_dict=None):
super(LineCollection, self).__init__(forms_dict)
if forms_dict is None:
forms_dict = {}
self.type = "line"
[docs]class RectCollection(FormCollectionBase):
"""Class specifying rectangle objects on images."""
[docs] def __init__(self, forms_dict=None):
super(RectCollection, self).__init__(forms_dict)
if forms_dict is None:
forms_dict = {}
self.type = "rect"
[docs] def add(self, x0, y0, x1, y1, id=None):
"""Create a new form from input coordinates.
:param int x0: x coordinate of start point
:param int y0: y coordinate of start point
:param int x1: x coordinate of stop point
:param int y1: y coordinate of stop point
:param str id: identification string of object (if None, the id
will be set automatically based on current object counter value)
"""
tl_x, br_x = min([x0, x1]), max(
[x0, x1]) # top left / bottom right x coorindates
# top left / bottom right y coorindates
tl_y, br_y = min([y0, y1]), max([y0, y1])
return super(RectCollection, self).add(tl_x, tl_y, br_x, br_y, id)
if __name__ == "__main__":
rects = {"bla": [1, 2, 3, 4],
"blub": [10, 20, 30, 40]}
rectColl = RectCollection(rects)
rectColl["bla"] = [3, 4, 5, 6]