用责任链模式实现图像处理方法的选择(python)

结合我们822实验室开源的图像处理平台(http://822lab.top)介绍用责任链模式实现图像处理方法的选择(python),供后续学弟学妹参考,整个平台的从零搭建记录在这里,后端仓库在这里,前端仓库在这里,欢迎大家为平台做贡献。


需求:

图像处理方法可以分为几个大类,比如图像平滑、轮廓提取、角点检测、形态学处理等,每一个大类下又有很多小类,比如图像平滑有高斯平滑、中值平滑和均值平滑等,我希望用户选择某个小类方法,然后得到相应的图像处理结果。因此在设计的时候给每个方法一个编号code,字符串类型,是一个三位数,第一位代表大类,后两位代表小类,客户请求时带着图片和code进行请求,服务器返回处理后的图片。

设计思路:

大类与小类的code记在一个枚举类中,如下:

# coding=utf-8
from enum import Enum


class ProcCodeEnum(Enum):
    # 图像平滑
    GAUSSIAN_BLUR = '101'
    MEAN_BLUR = '102'
    MEDIA_BLUR = '103'
    B_F_BLUR = '104'
    JOINT_B_F_BLUR = '105'

    # 阈值分割
    AUTO_THRESHOLD = '201'
    MANUAL_THRESHOLD = '202'
    ADAPTIVE_THRESHOLD = '203'

    # 轮廓提取
    ACCURATE_CONTOUR = '301'
    FITTING_CONTOUR = '302'

    # 角点提取
    HARRIS_CORNER = '401'
    SUBPIX_CORNER = '402'
    R_J_CORNER = '403'

    # 形态学处理
    ERODE = '501'
    DILATE = '502'
    MORPH_OPERATIONS_CAL = '503'

在进行设计的时候,最容易产生的思路是:根据传入的code,用if else判断是哪个算法,然后进行处理。因为我不喜欢代码中有很长的if else语句,尤其这么多的算法,可能很长一大段if else,这是一件恐怖的事情,因此自然想到了责任链模式,用责任链有两个好处:

  • 免掉很多if else语句。
  • 扩展时候容易,只需要以同样模式新增代码就好,不需要改动已有逻辑。

最初设计责任链时候,我设想的是所有小算法都在一个链下,对应的画面是:图像处理算法由一个manager管,这个manager负责所有算法,code来了只要交给他就能出结果。但是很快就面临一个问题:

  • 随着图像处理算法的增加,manager的负担太大,虽然他不需要进行具体处理,但是他没接到一个任务需要挨家挨户敲门去问手下的工人能不能进行处理,显然不是很好。

因此把责任链粒度缩小到图像处理算法的每一个大类都使用一个责任链,对应的画面是:有n个manager负责不同类的图像处理算法,是哪个类的就交给哪个manager,每个manager管的工人都不多,因此会合理一些。(注意我说的是合理,不是快,对于工程项目来说,拉慢的一点点效率,对整体影响不大,提高的一点点效率,对整体影响也不大,因此合理是最重要的)。

详细设计:

责任链模式的关键,在java里是每个类要实现的接口,在python是每个类要继承的父类,里面包含to_next方法和handle方法,to_next是链条里的下一个人,handle是具体的处理方法,从第一个人起,每个人会先尝试handle,如果handle不了,再调用to_next的handle,也就是让下一个人处理,下一个人处理不了再交给下下个人,直到链条最后。

handler.py

class Handler(object):

    def __init__(self, _to_next=None):
        self._to_next = _to_next

    def to_next(self, _to_next):
        self._to_next = _to_next

    def handle(self, code, params, image):
        pass

每个的小算法类都继承Handler,实现handle方法。

如高斯平滑gaussian_blur.py

import cv2

from img_algorithms.algorithms_base import Handler
from img_algorithms.algorithms_base import get_border_type
from myenums.proc_code_enum import ProcCodeEnum


def gaussian_blur(image, k_size_w, k_size_h, sigma_x, sigma_y, border_type):
    return cv2.GaussianBlur(image,
                            ksize=(k_size_w, k_size_h),
                            sigmaX=sigma_x,
                            sigmaY=sigma_y,
                            borderType=get_border_type(border_type))

# 继承Handle,如果自己能处理,则处理,处理不了,则交给下一个(调用to_next的handle)
class GaussianBlurHandler(Handler):
    def handle(self, code, params, image):
        if code == ProcCodeEnum.GAUSSIAN_BLUR:
            return gaussian_blur(image,
                                 int(params['kSizeW']),
                                 int(params['kSizeH']),
                                 float(params['sigmaX']),
                                 float(params['sigmaY']),
                                 int(params['borderType']))
        else:
            return self._to_next.handle(code, params, image)

链条的最后一个将不会调用to_next,而是抛出异常(这里只是用异常机制控制了业务逻辑)。

我们设计所有的manager最后一个链条元素都是no_process.py

# coding=utf-8
from img_algorithms.algorithms_base import Handler


class NoProcHandler(Handler):
    def handle(self, code, params, image):
        raise Exception('哎呀~{}号图像处理方法尚未完成,请静候佳音'.format(code))

最后是链条的组织者,也就是每类算法的manager,还是以图像平滑为例:

from img_algorithms.algorithms_base import NoProcHandler
from img_algorithms.algorithms_smooth import BFBlurHandler
from img_algorithms.algorithms_smooth import GaussianBlurHandler
from img_algorithms.algorithms_smooth import JointBFBlurHandler
from img_algorithms.algorithms_smooth import MeanBlurHandler
from img_algorithms.algorithms_smooth import MediaBlurHandler


def process(code, params, image):
    # 实例化每个小算法
    gaussian_blur = GaussianBlurHandler()
    mean_blur = MeanBlurHandler()
    media_blur = MediaBlurHandler()
    b_f_blur = BFBlurHandler()
    joint_b_f_blur = JointBFBlurHandler()
    no_proc_handler = NoProcHandler()
    # 拼成链条
    gaussian_blur.to_next(mean_blur)
    mean_blur.to_next(media_blur)
    media_blur.to_next(b_f_blur)
    b_f_blur.to_next(joint_b_f_blur)
    joint_b_f_blur.to_next(no_proc_handler)

    return gaussian_blur.handle(code, params, image)

最外层调用的时候只需要:

...
if int(o_code) < 200:
    processed_img_arr = smooth_manager.process(o_code, o_params, img_arr)
...

PR新增算法

平台仓库在这里,欢迎大家进行完善。

新增图像处理大类

  1. 在proc_code_enum.py中添加大类以及code,注释与命名风格请一定统一。
  2. 在img_algorithms中新建大类的package,并在img_algorithms的__init__.py文件中import manager。
  3. 在新建的package中写用责任链模式写新的小类算法。
  4. 在img_lab.py的判断code中,调用新类manager的process方法,注意,这里读入的图都是彩色BGR图,如需要灰度图,请在算法中自行转换。

新增图像处理小类

  1. 在proc_code_enum.py中添加小类code,注释与命名风格请一定统一。
  2. 从img_algorithms中找到大类,在里面新建小类模块,并在大类的__init__.py中import 方法。

数据库添加

由于数据库添加接口还没做,所以需要联系我(QQ或微信644306737,备注server-gitPR+姓名),我来进行数据库更新。组织成json发给我即可,形如:

{
    "name" : "形态学运算",
    "code" : "503",
    "type" : [ 
        {
            "name" : "形态学处理"
        }
    ],
    "params" : [ 
        {
            "type" : "input",
            "name" : "结构元大小",
            "value" : [],
            "limit" : "odd",
            "pName" : "kSize"
        }, 
        {
            "type" : "select",
            "name" : "结构元形状",
            "value" : [ 
                "矩形", 
                "椭圆形", 
                "十字交叉形"
            ],
            "limit" : "",
            "pName" : "shape"
        }, 
        {
            "type" : "input",
            "name" : "迭代次数",
            "value" : [],
            "limit" : "int >0",
            "pName" : "iterations"
        }, 
        {
            "type" : "select",
            "name" : "运算",
            "value" : [ 
                "开运算", 
                "闭运算", 
                "形态梯度", 
                "顶帽运算", 
                "底帽运算"
            ],
            "valueDesc" : [ 
                "先腐蚀再膨胀,可以消除白色小物体", 
                "先膨胀再腐蚀,可以消除黑色小物体", 
                "膨胀图与腐蚀图之差,对二值图可以出现中空效果,类似华文彩云字体", 
                "原图像与开运算做差,可以用来分离比邻近点亮的斑块", 
                "原图像和闭运算做差,可以用来分离比邻近点暗的斑块"
            ],
            "limit" : "",
            "pName" : "op"
        }, 
        {
            "type" : "select",
            "name" : "边界扩充方式",
            "value" : [ 
                "边界复制", 
                "常数扩充", 
                "反射扩充", 
                "边界为中心反射扩充", 
                "平铺扩充"
            ],
            "valueDesc" : [ 
                "边界复制:BORDER_REPLICATE,复制最外围像素实现扩充", 
                "常数扩充:BORDER_CONSTANT,用指定常数扩充边界,这里全部用0扩充", 
                "反射扩充:BORDER_REFLECT,对图片进行镜像扩充", 
                "边界为中心反射扩充:BORDER_REFLECT_101,以四个边界分别为对称轴镜像扩充,比反括扩充少了一圈边界值的复制", 
                "平铺扩充:BORDER_WRAP,同桌面平铺效果"
            ],
            "limit" : "",
            "pName" : "borderType"
        }
    ]
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容