好的,所以:我有一个基于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(...)”的实现(或缺少它),但是我不确定从那里去哪里>
我知道背景和示例都相当长,但是如果您已经读了这么多,我会很感激您在这一领域中所拥有的实践知识。