Python:检查经常使用组合的对象层次结构的调用图

时间:2019-03-19 18:27:43

标签: python image-processing python-imaging-library introspection call-graph

好的,所以:我有一个基于PIL / Pillow(以下简称为“ Pillow”)的图像处理器类的程序包。基本思想是图像处理器是一类,其实例具有process(…)方法,该方法采用一个Pillow图像实例并返回一个(可能是变异的,可能是不同的)Pillow图像实例。

包装中的所有图像处理器均基于ABC。根ABC看起来像这样:

from abc import ABC, abstractmethod

class Processor(ABC):

    @abstractmethod
    def process(self, image):
        """ Process an image instance, per the processor instance,
            returning the processed image data.
        """
        ...

(N.B。,the actual working published ABC in question还有很多东西,但是为了这个问题,我现在并将继续简化。)

许多更简单的图像处理器实现如下所示:

class Brightness(Processor):
    # __init__() sets some parameters on self

    def process(self, image):
        # … adjust brightness …
        return brightened

class Hue(Processor):
    # __init__() sets some parameters on self

    def process(self, image):
        # … adjust hue …
        return recolorized

...首先将它们实例化(processor = Hue(2.7)),然后将其应用于一个或多个图像实例(recolorized = processor.process(image))。

不过,并非所有处理器都这么简单:软件包中最常用的处理器之一是Mode处理器,它基于enum(使用enum34在Python 2.7上):

from enum import Enum, unique, auto

@unique
class Mode(Enum, Processor):

    MONO    = auto()
    GRAY    = auto()
    RGB     = auto()
    CMYK    = auto()
    YCbCr   = auto() # …

    def process(self, image):
        if image.mode == self.to_string():
            return image
        return image.convert(self.to_string())

(再次,the working version of this one涉及更多)。

一个人通过简单地访问枚举的单个实例之一来使用基于enum的处理器,例如:rgbimage = Mode.RGB.process(image)

此外,还有一些处理器容器(或容器处理器,以不太尴尬的形式出现),它们将Python容器类型与处理器逻辑结合在一起。两个(简化的)示例是基于list的{​​{1}}处理器和基于Pipeline的{​​{1}}处理器:

defaultdict

...简而言之。这些都允许使用类层次结构和组合快速建立图像处理数据流。这是使用上述所有示例的示例实现:

ChannelFork

现在:尽管我的示例很抽象,但这全都是非理论性的。这是我的笔记本电脑上现在使用处理器生成的两张图像,与上面的from collections import defaultdict class Pipeline(Processor): def __init__(self, *processors): self.list = [*processors] for processor in self.list: assert callable(getattr(processor, 'process', None)) # ITS A DUCK def process(self, image): for processor in self.list: image = processor.process(image) return image class ChannelFork(defaultdict): # … lots of implementation stuff skipped … def __init__(self, processor_factory, *args, **kwargs): self.mode = kwargs.pop('mode', Mode.CMYK) super(ChannelFork, self).__init__(processor_factory, *args, **kwargs) def process(self, image): # … possibly pre-process “image” e.g. GCR bands = [] for channel_label, channel in zip(self.mode.bands, mode.process(image).split()): # R, G, B; C, M, Y, K; etc. bands.append(self[channel_label].process(channel)) recomposed = Image.merge(self.mode.to_string(), bands) # … possibly post-process “recomposed” return recomposed 示例不同(单击其中任一以查看像素级细节):

...所以是的!到目前为止,一切工作都很好。但是,正如您在我的冗长的介绍性示例代码发布期间所收集的那样,这些处理器可能成为子处理器和子子处理器的复杂嵌套。

因此,根据帖子标题,我现在要讨论一个实际的问题:我感兴趣的是开发一种方法来检查所有这些class Dither(Processor): def process(self, image): brightened = Brightness(self.brightness).process(image) monochrome = Mode.MONO.process(brightened) # … do the dithering … return dithered def ColorHalftone(Processor): def __init__(self, gcr_percentage=20, OutputMode=None, Ditherer=Dither): # Using a Pipeline in leu of instance variables: # self.channelfork = ChannelFork(Ditherer) # self.gcr = BasicGCR(percentage=gcr_percentage) # self.output_mode = output_mode self.pipeline = Pipeline(ChannelFork(Ditherer), BasicGCR(percentage=gcr_percentage), OutputMode or Mode.RGB) def process(self, image): return self.pipeline.process(image) 子类的调用图,它们之间存在ColorHalftone个呼叫的相互依存的网络。

我个人从未写过任何东西来生成任何类型的调用图。我只熟悉将utilities provided用作parts of other software packages的短语“调用图” –尽管我对编程并不陌生,但这只是我拥有的众多壁one之一尚未探索。

这是问:我如何绘制所有“ process(…)”调用的图形?这样我就可以看到一个这样的呼叫何时包含其他呼叫,以及何时由其他被叫者串行进行呼叫?

我假设我想加入根抽象基类及其对“ process(...)”的实现(或缺少它),但是我不确定从那里去哪里

  • 已经存在哪些工具可能对此提供帮助?
  • 执行此类自省操作可能会遇到哪些算法问题(大O或其他)?
  • 应为调用图考虑哪种数据表示格式,应该避免哪种格式?
  • 为此,Python标准库中可能包含哪些基本实用程序?

我知道背景和示例都相当长,但是如果您已经读了这么多,我会很感激您在这一领域中所拥有的实践知识。

0 个答案:

没有答案