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

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


需求:

图像处理方法可以分为几个大类,比如图像平滑、轮廓提取、角点检测、形态学处理等,每一个大类下又有很多小类,比如图像平滑有高斯平滑、中值平滑和均值平滑等,我希望用户选择某个小类方法,然后得到相应的图像处理结果。因此在设计的时候给每个方法一个编号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"
        }
    ]
}

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Y大宽

转录因子详细介绍(motif)

TF: transcription factor转录因子 TFBS: transcription factor binding site转录因子结合位点 T...

1.2K30
来自专栏机器人网

机器人A*寻路算法详解

A*(A-star)算法是一种静态网路中求解最短路径最有效的直接搜索算法。在电子游戏中最主要的应用是寻找地图上两点间的最佳路线。在机器人领域中,A*算法...

43240
来自专栏落影的专栏

OpenGL ES不容错过的实战-碰碰车

教程 OpenGLES入门教程1-Tutorial01-GLKit OpenGLES入门教程2-Tutorial02-shader入门 OpenGLES入门...

29450
来自专栏数据科学与人工智能

【Python环境】R vs Python:硬碰硬的数据分析

我们将在已有的数十篇从主观角度对比Python和R的文章中加入自己的观点,但是这篇文章旨在更客观地看待这两门语言。我们会平行使用Python和R分析一个数据集,...

31490
来自专栏Coding迪斯尼

神经网络实战:快速构建一个基于神经网络的手写数字识别系统

13520
来自专栏Python攻城狮

Python数据科学(九)- 使用Pandas绘制统计图表1.信息可视化

因为人对图像信息的解析效率比文字更高,所以可视化可以使数据更为直观,便于理解,使决策变得高效,所以信息可视化就显得尤为重要。

12630
来自专栏深度学习与数据挖掘实战

【今日热门&优秀资源】深度学习&Kaggle竞赛

18230
来自专栏数据小魔方

如何处理地图投影转换

最近学习地理信息可视化总是遇到投影的麻烦,包括前段时间输出两篇关于simple features的分享中,其中没有特别处理投影的问题,老司机一看就能看出其中存在...

19630
来自专栏Petrichor的专栏

深度学习: 局部响应归一化 (Local Response Normalization,LRN)

局部响应归一化(Local Response Normalization,LRN):

1K40
来自专栏数据结构与算法

Noip 2016 Day1 题解

老师让我们刷历年真题, 然后漫不经心的说了一句:“你们就先做做noip2016 day1 吧” 。。。。。。 我还能说什么,,,,,老师你这是明摆着伤害我们啊2...

597120

扫码关注云+社区

领取腾讯云代金券