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

本章导航:

  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容