深度学习

3 从零开始设计计算机视觉软件

2019-10-18  本文已影响0人  水之心

本章导航:

  1. 说明如何使用 GitHub 创建一个项目。
  2. 使用 Python 如何创建一个 bbox 处理的工具。

本章需要的背景知识:

  1. 了解 Git 与 vscode。
  2. 熟悉 Python。
  3. Shell(命令行命令) 基本命令,本书均以 $ 开头用于标识。

6.1 创建一个项目

本章尽可能地从零基础说明如何在 https://github.com 创建一个项目,并在本地进行项目开发。

创建项目的步骤很简单:

第 1 步:在 GitHub 注册一个账号。
第 2 步:基于您自己的 GitHub 账户,创建一个项目。

  1. 进入您自己的 GitHub 主页,比如:https://github.com/xinetzone,点击页面右上角的 +,选择 New repository
图6.1 选择 New repository
  1. 填写必需项目信息:
图6.2 填写必需项目信息

表单上的 Initialize this repository with a README 需要✔,.gitignore 选择您在项目中需要使用的编程语言,比如,Pythonlicense 选择一个您需要的即可。

如果您想要将此项目打造为一个社区,可以参考我的博客:构建属于自己的项目[1]。至此,您完成了项目的创建工作。

6.2 项目的准备工作

在您的电脑磁盘上创建一个名为 projects 的文件夹,然后使用 vscode 打开该文件夹。接着,创建一个终端(快捷键 Ctrl+shift+`),并使用 git clone URL 克隆您的项目:

图6.3 克隆项目到本地

转到项目目录,编辑 README.md 文件,通过网站 https://img.shields.io 为您的项目添加图标信息,比如:

## 数字图像处理

[![GitHub issues](https://img.shields.io/github/issues/xinetzone/image)](https://github.com/xinetzone/image/issues) [![GitHub forks](https://img.shields.io/github/forks/xinetzone/image)](https://github.com/xinetzone/image/network) [![GitHub stars](https://img.shields.io/github/stars/xinetzone/image)](https://github.com/xinetzone/image/stargazers) [![GitHub license](https://img.shields.io/github/license/xinetzone/image)](https://github.com/xinetzone/image/blob/master/LICENSE) ![GitHub repo size](https://img.shields.io/github/repo-size/xinetzone/image)

显示的效果如下:

图6.4 创建图标

下面就以我自己的项目 image[2] 为例展示如何开发项目。

6.3 开发一个小工具:bbox

对于目标检测任务,总是会涉及到对边界框(Bounding Box,简称 bbox)的处理,因而,开发一个专门处理边界框的 API 是十分有必要的。在创建小工具 bbox 之前,我们需要了解一些 Python 的基础知识并创建一个数学的向量实例。

6.3.1 创建数学中的“向量”

Python 中存在一个十分强大的标准库:dataclassdataclass 的定义位于 PEP-557,一个 dataclass 是指“一个带有默认值的可变的 namedtuple”,广义的定义就是有一个类,它的属性均可公开访问,可以带有默认值并能被修改,而且类中含有与这些属性相关的类方法,那么这个类就可以称为 dataclass,再通俗点讲,dataclass 就是一个含有数据及操作数据方法的容器。

关于 dataclass 的更多精彩内容可参阅 dataclasses[3]typing[4](详细的介绍可参考:Python3 之类型注解[5])。

为了更好的说明 dataclass 的魅力。先从“整点”开始的定义开始。即定义 AA = x, \; x \in Z。先看看 typing 的方法如何定义?

from typing import Any
class Point:
    def __init__(self, name: str, x: int) -> Any:
        self.x = x  # 坐标值
        self.name = name  # 名称

    def __repr__(self) -> Any:
        print("name={self.name}, x={self.x}")

再看看 dataclass 如何定义?

from dataclasses import dataclass

@dataclass
class Point:
    name: str
    x: int

是不是清爽很多?

在数学中一般使用向量来表示。即设 x_i \in \mathbb{R}^n, \; 0 \leq i \leq n,则可以使用 A = (x_1,\cdots,x_n) 代表点 A。这里的 n 被称为点或者向量的纬度。我们使用 Python 实现向量的构建工作。

from typing import Sequence
from dataclasses import dataclass
import numpy as np


@dataclass
class Vector:
    '''
    数学中的向量
    '''
    name: str
    # 矢量的分量
    components: Sequence[float]

    @property
    def toArray(self):
        return np.asanyarray(self.components)

    def __len__(self):
        '''
        向量的维度
        '''
        return len(self.components)

    def __add__(self, other):
        '''
        向量加法运算
        '''
        assert len(self) == len(other), "向量的维度不相同"
        out = self.toArray + other.toArray
        return out

    def __sub__(self, other):
        '''
        向量减法运算
        '''
        assert len(self) == len(other), "向量的维度不相同"
        out = self.toArray - other.toArray
        return out

    def scale(self, scalar):
        '''
        向量的数乘运算
        '''
        return scalar * self.toArray

    def __mul__(self, other):
        '''
        向量的内积运算
        '''
        assert len(self) == len(other), "向量的维度不相同"
        return np.dot(self.toArray, other.toArray.T)

    @property
    def norm(self):
        '''
        计算模长
        '''
        # S = sum(component**2 for component in self.components)
        mod = self * self
        return np.sqrt(mod)

    def __getitem__(self, index):
        '''
        向量的索引与切片
        '''
        return self.components[index]

将该代码保存到 app/vector.py 文件之中。

下面看一个实例:

a = Vector('a', range(2,7))
a1 = Vector('a1', range(5,10))
a2 = Vector('a2', range(7,12))

a + a1, a - a1, a.scalar(4), a.norm

创建了 3 个向量 a, a_1, a_2,接着,分别计算其运算:向量加法,减法,数乘以及模。输出结果:

(array([ 7,  9, 11, 13, 15]),
 array([-3, -3, -3, -3, -3]),
 array([ 8, 12, 16, 20, 24]),
 9.486832980505138)

6.3.2 编写 box.py 代码

Vector 定义了“点”,下面我们需要定义:有向线段 [x_1,x_2] = x_2 -x_1。使用 Python 定义很简单:

x1 = Vector('x1', [1, 2])
x2 = Vector('x2', [3, 4])
LS = Vector('x2 - x1', x2 - x1) # 线段 x2 - x1

则点 x_2x_1 之间的线段,可以使用 x_1 + t[x_1, x_2], \; 0\leq t \leq 1 进行表示。这样一来,以 x_2x_1 连接的线段为对角线的矩形框便由 x_2x_1 唯一确定。

由于大多数情况,矩形框是二维平面图形,所以,接下来我们仅仅考虑二维向量生成的矩形框。使用 Python 可以这样定义:

from dataclasses import dataclass
from typing import Sequence
import numpy as np

from vector import Vector

@dataclass
class Rectangle:
    box: Sequence[Vector]

    def __post_init__(self):
        self.w, self.h = np.abs(self.box[1] - self.box[0])

    def __lt__(self, size):
        '''
        判断矩形的尺寸是否是小于 size
        '''
        w_cond = self.w < size  # 宽是否小于 size
        h_cond = self.h < size  # 高是否小于 size
        cond = w_cond or h_cond  # 宽或者高是否均小于 size
        return cond

    def __ge__(self, size):
        min_size = self < size
        return not min_size

    @property
    def area(self):
        '''
        计算矩形面积
        '''
        return self.w * self.h

该代码保存在 /app/image/box.py 之中。Rectangle 完成了矩形边界框的基础构建工作。同时提供了宽、高、面积计算以及判断是否为最小边界框的实现。

我们依然看一个例子:

设存在一个矩形框 a = (x_1,y_1,x_2,y_2),其中,(x_1,y_1)(x_2, y_2) 分别表示矩形框的左上角、右下角坐标。这样,利用 Rectangle 便可定义此矩形框:

a1 = Vector('a_1', [2, 3])
a2 = Vector('a_1', [7, 13])
bbox = Rectangle([a1, a2])

接着,可以计算 bbox 的面积、高、
以及宽:

bbox.area, bbox.h, bbox.w

输出结果是:

(66, 10, 5)

至此,一个简单的边界框模型搭建完成了。


  1. https://xinetzone.github.io/zh-CN/e6d6f9e7.html

  2. https://github.com/xinetzone/image

  3. https://docs.python.org/zh-cn/3.7/library/dataclasses.html

  4. https://docs.python.org/zh-cn/3.7/library/typing.html

  5. https://xinetzone.github.io/zh-CN/55e01cf0.html

上一篇 下一篇

猜你喜欢

热点阅读