Source code for guibot.imagelogger

# Copyright 2013-2018 Intranet AG and contributors
#
# guibot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# guibot 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with guibot.  If not, see <http://www.gnu.org/licenses/>.

"""

SUMMARY
------------------------------------------------------
Image logging for enhanced debugging and verbosity of guibot's operation.


INTERFACE
------------------------------------------------------

"""

import os
import shutil
import PIL.Image

from .config import GlobalConfig


[docs]class ImageLogger(object): """ Logger for the image matching process with the help of images. It always contains the current match case: the needle and haystack images/targets being matched and the hotmap (an image with additional drawn information on it), the matched similarity and the matched coordinates. Generally, each finder class takes care of its own image logging, performing drawing or similar operations on the spot and deciding which hotmaps (also their names and order) to dump. """ #: number of the current step step = 1 #: switch to stop logging and later on log all accumulated dumps at once accumulate_logging = False #: level for the image logging logging_level = GlobalConfig.image_logging_level #: destination for the image logging in order to dump images #: (the executing code decides when to clean this directory) logging_destination = GlobalConfig.image_logging_destination #: number of digits for the counter of logged steps step_width = GlobalConfig.image_logging_step_width
[docs] def __init__(self): """Build an imagelogger object.""" self.needle = None self.haystack = None self.hotmaps = [] self.similarities = [] self.locations = [] # sync these static methods with the general settings at each use ImageLogger.logging_level = GlobalConfig.image_logging_level # NOTE: the executing code decides when to clean this directory ImageLogger.logging_destination = GlobalConfig.image_logging_destination ImageLogger.step_width = GlobalConfig.image_logging_step_width
def get_printable_step(self): """ Getter for readonly attribute. :returns: step number prepended with zeroes to obtain a fixed length enumeration :rtype: str """ return ("%0" + str(ImageLogger.step_width) + "d") % ImageLogger.step printable_step = property(fget=get_printable_step)
[docs] def debug(self): """Log images with a DEBUG logging level.""" self.log(10)
[docs] def info(self): """Log images with an INFO logging level.""" self.log(20)
[docs] def warning(self): """Log images with a WARNING logging level.""" self.log(30)
[docs] def error(self): """Log images with an ERROR logging level.""" self.log(40)
[docs] def critical(self): """Log images with a CRITICAL logging level.""" self.log(50)
[docs] def dump_matched_images(self): """ Write file with the current needle and haystack. The current needle and haystack (matched images) are stored as `needle` and `haystack` attributes. """ if ImageLogger.logging_level > 30: return if not os.path.exists(ImageLogger.logging_destination): os.mkdir(ImageLogger.logging_destination) elif ImageLogger.step == 1: shutil.rmtree(ImageLogger.logging_destination) os.mkdir(ImageLogger.logging_destination) needle_name = "imglog%s-1needle-%s" % (self.printable_step, str(self.needle)) needle_path = os.path.join(ImageLogger.logging_destination, needle_name) self.needle.save(needle_path) haystack_name = "imglog%s-2haystack-%s" % (self.printable_step, str(self.haystack)) haystack_path = os.path.join(ImageLogger.logging_destination, haystack_name) self.haystack.save(haystack_path)
[docs] def dump_hotmap(self, name, hotmap): """ Write a file the given hotmap. :param str name: filename to use for the image :param hotmap: image (with matching results) to write :type hotmap: :py:class:`PIL.Image` or :py:class:`numpy.ndarray` """ if ImageLogger.logging_level > 30: return if not os.path.exists(ImageLogger.logging_destination): os.mkdir(ImageLogger.logging_destination) path = os.path.join(ImageLogger.logging_destination, name) if isinstance(hotmap, PIL.Image.Image): pil_image = hotmap else: # numpy or other array pil_image = PIL.Image.fromarray(hotmap) # NOTE: some modes cannot be saved unless converted to RGB if pil_image.mode != 'RGB': pil_image = pil_image.convert('RGB') pil_image.save(path, compress_level=GlobalConfig.image_quality)
[docs] def clear(self): """Clear all accumulated logging including hotmaps, similarities, and locations.""" self.needle = None self.haystack = None self.hotmaps = [] self.similarities = [] self.locations = []