聊聊那些专为算法设计的模式——模板方法模式
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)
算法核模板
垂直微分算子
定义
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)
算法核模板
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
引导篇
基础篇
进阶篇
经验篇
上一篇: vue实现单页面多标签页