聊聊那些专为算法设计的模式——模板方法模式

AI越来越火热,人工智能已然成风!而人工智能最重要是各种算法,因此机器学习越来越受到追捧,算法越来越被重视。

作为一个算法的研究者,写出一手高级算法当然是令人兴奋的一件事!但你是否有时会有这种感觉:
1. 写的算法很难通用于所有的数据类型!每来一个新类型的数据,又得改一下算法,或新加一个方法来支持这种类型。
2. 有时候多个算法需要灵活组合,甚至每个算法的顺序不一样都会产生不一样的效果;每一种组合都要为其构建一个新算法,即累又麻烦。
3. 算法越来越多,自建的算法库也越来越庞大而难于管理;

这个时候,让你的算法具有更好通用性、拓展性就显得极为重要!因此,你必须要掌握几个重要的设计模式来优化你的代码,解决这些问题。今天就来聊聊那些专为算法设计的模式:策略模式、模板方法模式、访问模式。

模板方法模式

Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

定义一个操作中的算法的框(骨)架,而将算法中用到的某些具体的步骤放到子类中去实现,使得子类可以在不改变算法结构的情况下重新定义该算法的某些特定步骤。这个定义算法骨架的方法就叫模板方法

对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。即一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。

应用案例

模板方法模式非常简单,以至于我都不觉得它是一个模式。因为只要是在使用面向对象的语言进行开发,你就有意无意之中已经在使用它了,举一个例子。

在图形图像的处理中,对图像像素进行微分求导,进行图像的锐化处理,是一个非常基础而又重要的算法。在对图像的一阶微分求导算法中,有两个非常重要的算法:水平微分算子垂直微分算子。另一个非常著名的算法Sobel微分算子,也是基于这两个算法来实现的。

水平微分算子

定义

Gx=f(x-1,y-1) + 2f(x-1,y) + f(x-1,y+1) - f(x+1,y-1) - 2f(x+1,y) - f(x+1, y+1)

算法核模板
Alt text

垂直微分算子

定义

Gy=f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1) - f(x-1,y+1) - 2f(x,y+1) - f(x+1,y+1)

算法核模板
Alt text

Sobel微分算子

定义

Gx=f(x-1,y-1) + 2f(x-1,y) + f(x-1,y+1) - f(x+1,y-1) - 2f(x+1,y) - f(x+1, y+1)
Gy=f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1) - f(x-1,y+1) - 2f(x,y+1) - f(x+1,y+1)
G=sqrt(Gx^2 + Gy^2)

也就是对水平微分和垂直微分的两个计算结果,再进行算术平方计算。

自己实现这个算法

虽然像OpenCv等这些成熟的图形图像算法库都提供了这一基础的算法,但作为一个图形图像算法的研究者,你有没有想过自己去实现一下这个简单的算法!我是有的,你呢?

水平微分算子和垂直微分算子这两个算法非常的相似,不同之处在于:一个是在水平方向上处理,一个是在垂直方向上处理。

这两个算法既然如此的相似,那肯定会有一些共同的部分,如像素的遍历:也有不同的部分,如算法的核模板不同。这个时候,我们就可以考虑使用模板方法的设计了。来看一下我们实现(下面这段代码应用到的拓展库:opencv-3.4.1,numpy-1.14.5):

算法实现:

from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法
import numpy as np
# 引入numpy模块进行矩阵的计算

class DifferentialDerivative(metaclass=ABCMeta):
    """微分求导算法"""

    def imgProcessing(self, img, width, height):
        """模板方法,进行图像处理"""
        # 这里特别需要注意:OpenCv for Python中,(x, y)坐标点的像素用img[y, x]表示
        newImg = np.zeros([height, width], dtype=np.uint8)
        for y in range(0, height):
            for x in range(0, width):
                # 因为是采用(3*3)的核进行处理,所以最边上一圈的像素无法处理,需保留原值
                if (y != 0 and y != height-1 and x != 0 and x != width-1):
                    value = self.derivation(img, x, y)
                    # 小于0的值置为0,大于255的值置为255
                    value = 0 if value < 0 else (255 if value > 255 else value)
                    newImg[y, x] = value
                else:
                    newImg[y, x] = img[y, x]
        return newImg

    @abstractmethod
    def derivation(self, img, x, y):
        """具体的步骤由子类实现"""
        pass

class DifferentialDerivativeX(DifferentialDerivative):
    """水平微分求导算法"""

    def derivation(self, img, x, y):
        """Gx=f(x-1,y-1) + 2f(x-1,y) + f(x-1,y+1) - f(x+1,y-1) - 2f(x+1,y) - f(x+1, y+1)"""
        pix = img[y-1, x-1] + 2 * img[y, x-1] + img[y+1, x-1] - img[y-1, x+1] - 2 *img[y, x+1] - img[y+1, x+1]
        return pix


class DifferentialDerivativeY(DifferentialDerivative):
    """垂直微分求导算法"""

    def derivation(self, img, x, y):
        """Gy=f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1) - f(x-1,y+1) - 2f(x,y+1) - f(x+1,y+1)"""
        pix = img[y-1, x-1] + 2*img[y-1, x] + img[y-1, x+1] - img[y+1, x-1] - 2*img[y+1, x] - img[y+1, x+1]
        return pix

测试代码:

def differentialDerivative():
    img = cv2.imread("E:\\TestImages\\person.jpg")

    # 转换成单通道的灰度图
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 均值滤波
    # img = cv2.blur(img, (3, 3))

    # 获取图片的宽和高
    width = img.shape[1]
    height = img.shape[0]
    # 进行微分求导
    derivativeX = DifferentialDerivativeX()
    imgX = derivativeX.imgProcessing(img, width, height)
    derivativeY = DifferentialDerivativeY()
    imgY = derivativeY.imgProcessing(img, width, height)
    # 实现Sobel微分算子
    imgScobel = cv2.addWeighted(imgX, 0.5, imgY, 0.5, 0)

    cv2.imshow("First order differential X", imgX)
    cv2.imshow("First order differential Y", imgY)
    cv2.imshow("First order differential Scobel", imgScobel)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

实现的效果与OpenCv的效果一模一样,我们一起来看一下。

原图:
这里写图片描述

水平微分求导后:
这里写图片描述

垂直微分求导后:
这里写图片描述

Sobel微分计算后:
这里写图片描述

更多更有趣的文章

想获得更多更有趣的设计模式吗?一起来阅读以下系列文章吧!

程序源码

https://github.com/luoweifu/PyDesignPattern

引导篇

生活中的设计模式——启程之前,请不要错过我【试读】

基础篇

生活中的监听模式——一坑爹的热水器

生活中的适配模式——身高不够鞋来凑

生活中的状态模式——人有少、壮、老, 水之冰、液、汽

生活中的单例模式——你是我生命的唯一

生活中的职责模式——我的假条去哪了

生活中的中介模式——找房子问中介

生活中的代理模式——帮我拿一下快递

生活中的装饰模式——你想怎么穿就怎么穿

生活中的工厂模式——你要拿铁还是摩卡

生活中的迭代模式——下一个就是你了

生活中的组合模式——自己电脑组装,价格再降三折

生活中的构建模式——你想要一辆车还是一座房

生活中的克隆模式——给你一个分身术

生活中的策略模式——怎么来不重要,人到就行

生活中的命令模式——大闸蟹,走起!

生活中的备忘模式——好记性不如烂笔头

生活中的享元模式——颜料很贵必须充分利用

生活中的外观模式——学妹别慌,学长帮你

生活中的访问模式——一千个读者一千个哈姆雷特

生活中的设计模式——与经典23种设计模式的不解渊源

生活中的设计模式——那些未完待续的设计模式

进阶篇

深入解读过滤器模式——制作一杯鲜纯细腻的豆浆

深入解读对象池技术——共享让生活更便捷

深入解读回调机制——把你技能亮出来

经验篇

谈谈我对设计模式的理解

谈谈我对设计原则的思考

谈谈我对项目重构的看法

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值