如何为 scikit-image 做贡献#
作为社区的一部分开发开源软件是很有趣的,而且通常也很有教育意义!
我们使用 GitHub 来协调我们的工作,您可以在其中找到未解决的问题和新功能请求的列表。
要跟进讨论或与开发团队取得联系,请加入我们的scikit-image 开发论坛和Zulip 聊天。
请将问题发布到这些公共论坛(而不是直接联系开发人员);这样,每个人都可以从答案中受益,开发人员可以根据自己的空闲时间来回答。不要感到害羞,团队非常友好!
开发过程#
以下是关于如何将对源代码和文档的更改贡献给 scikit-image 的简要概述。
如果您是第一次贡献者
转到scikit-image/scikit-image,然后单击“fork”按钮以创建您自己的项目副本。
在您的本地计算机上使用项目源代码克隆(下载)存储库
git clone https://github.com/your-username/scikit-image.git
更改到克隆存储库的根目录
cd scikit-image
添加上游存储库
git remote add upstream https://github.com/scikit-image/scikit-image.git
现在,您有名为的远程存储库
upstream
,它指的是scikit-image
存储库,以及origin
,它指的是您的个人 fork。
接下来,设置您的构建环境。
最后,我们建议您使用预提交钩子,它会在您每次执行
git commit
时运行代码检查器和格式化程序pip install pre-commit pre-commit install
开发您的贡献
从上游拉取最新更改
git checkout main git pull upstream main
为您要处理的功能创建一个分支。使用一个有意义的名称,例如“transform-speedups”
git checkout -b transform-speedups
在您进行时本地提交(使用
git add
和git commit
)。请编写良好的提交消息。
要提交您的贡献
将您的更改推送回您在 GitHub 上的 fork
git push origin transform-speedups
输入您的 GitHub 用户名和密码(重复贡献者或高级用户可以通过使用 SSH 连接到 GitHub 来删除此步骤)。
转到 GitHub。新分支将显示一个绿色的“pull request”按钮 – 单击它。
如果需要,请在开发者论坛上发帖,以解释您的更改或请求审核。
有关更详细的讨论,请阅读有关如何使用 Git 和 scikit-image
的这些详细文档(使用 scikit-image 源代码)。
审查过程
审查者(其他开发人员和感兴趣的社区成员)将对您的拉取请求 (PR) 编写内联和/或一般评论,以帮助您改进其实现、文档和样式。在项目上工作的每一位开发人员的代码都会被审查,我们已经把它看作是一场友好的对话,我们所有人都可以从中学习,并且整体代码质量也会受益。因此,请不要让审查阻止您做出贡献:它的唯一目的是提高项目的质量,而不是批评(毕竟,我们非常感谢您捐赠的时间!)。
要更新您的拉取请求,请在您的本地存储库上进行更改并提交。一旦这些更改被推送上去(与之前相同的分支),拉取请求将自动更新。
每次提交拉取请求后,都会触发持续集成 (CI) 服务来构建软件包、运行单元测试、测量代码覆盖率并检查您分支的编码风格 (PEP8)。测试必须通过才能合并您的 PR。如果 CI 失败,您可以通过单击“失败”图标(红色叉号)并检查构建和测试日志来找出原因。
拉取请求必须在合并前获得两个核心团队成员的批准。
记录更改
如果您的更改引入了弃用,请在
TODO.txt
中添加提醒,以便团队在将来删除已弃用的功能。scikit-image 使用 changelist 从拉取请求自动生成发布说明列表。默认情况下,changelist 将使用拉取请求的标题及其 GitHub 标签将其排序到适当的部分。但是,对于更复杂的更改,我们鼓励您使用拉取请求描述中的
release-note
代码块更详细地描述它们;例如```release-note Remove the deprecated function `skimage.color.blue`. Blend `skimage.color.cyan` and `skimage.color.magenta` instead. ```
您可以参考发布说明以获取示例,并参考changelist 的文档以获取更多详细信息。
注意
对于审查者:如果从 PR 描述中不明显,请确保在合并消息中描述更改的原因和上下文。
upstream main
和您的特性分支之间的差异#
如果 GitHub 指示您的 PR 分支不再可以自动合并,请将 main 分支合并到您的分支中
git fetch upstream main
git merge upstream/main
如果发生任何冲突,则需要在继续之前修复它们。使用以下命令查看哪些文件存在冲突
git status
这会显示如下消息
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: file_with_conflict.txt
在冲突的文件中,您会发现如下部分
The way the text looks in your branch
选择应该保留的一个版本的文本,然后删除其余部分
The way the text looks in your branch
现在,添加修复后的文件
git add file_with_conflict.txt
修复所有合并冲突后,执行
git commit
注意
我们鼓励高级 Git 用户变基而不是合并,但无论如何我们都会压缩和合并大多数 PR。
指南#
样式指南#
设置您的编辑器以删除尾随空格。遵循 PEP08。
使用 numpy 数据类型而不是字符串(
np.uint8
而不是"uint8"
)。使用以下导入约定
import numpy as np import matplotlib.pyplot as plt import scipy as sp import skimage as ski sp.ndimage.label(...) ski.measure.label(...) # only in Cython code cimport numpy as cnp cnp.import_array()
在记录数组参数时,请使用
image : (M, N) ndarray
,然后根据需要,在文档字符串中引用M
和N
。将数组维度称为(平面)、行、列,而不是 x、y、z。有关更多信息,请参阅用户指南中的坐标约定。
函数应支持所有输入图像 dtype。使用诸如
img_as_float
之类的实用程序函数来帮助转换为适当的类型。输出格式可以是任何最有效的格式。这允许我们将多个函数链接到管道中,例如hough(canny(my_image))
在 C/C++ 和 Cython 代码中,将
Py_ssize_t
用作所有索引、形状和大小变量的数据类型。使用相对模块导入,即
from .._shared import xyz
而不是from skimage._shared import xyz
。将 Cython 代码包装在定义 API 的纯 Python 函数中。这提高了与代码自省工具的兼容性,这些工具通常不知道 Cython 代码。
对于 Cython 函数,请尽可能使用
with nogil:
来释放 GIL。
测试#
在合并拉取请求之前,测试套件必须通过,并且应添加测试以覆盖所有行为的修改。
我们使用 pytest 测试框架,测试位于各个 skimage/子模块/tests
文件夹中。
测试要求在 requirements/test.txt
中列出。运行
所有测试:
spin test
子模块的测试:
spin test skimage/morphology
从特定文件运行测试:
spin test skimage/morphology/tests/test_gray.py
运行文件中的一个测试:
spin test skimage/morphology/tests/test_gray.py::test_3d_fallback_black_tophat
使用任意 ``pytest`` 选项运行测试:
spin test -- any pytest args you want
。运行所有测试和 doctest:
spin test -- --doctest-plus skimage
测试阶段的警告#
默认情况下,测试套件引发的警告会导致错误。您可以通过将环境变量 SKIMAGE_TEST_STRICT_WARNINGS
设置为 0
来关闭此行为。
测试覆盖率#
模块的测试理想情况下应覆盖该模块中的所有代码,即语句覆盖率应为 100%。
要衡量测试覆盖率,请运行
$ spin test --coverage
这将运行测试并打印报告,其中包含 skimage
中每个文件的一行,详细说明测试覆盖率
Name Stmts Exec Cover Missing
------------------------------------------------------------------------------
skimage/color/colorconv 77 77 100%
skimage/filter/__init__ 1 1 100%
...
构建文档#
要构建 HTML 文档,请运行
spin docs
输出位于 scikit-image/doc/build/html/
中。添加 --clean
标志以从头开始构建,删除任何缓存的输出。
示例库#
示例库是使用 Sphinx-Gallery 构建的。有关完整的使用说明,请参阅其文档,以及 doc/examples
中的现有示例。
示例库示例的最大图形宽度应为 8 英寸。您还可以更改图库条目的缩略图。
修复警告#
“找不到引用:R###” 文档字符串的第一行中引用后可能有一个下划线 (例如 [1]_)。使用此方法查找源文件:$ cd doc/build; grep -rin R####
“重复引用 R###,其他实例在...”” 其中一个文档字符串中可能有一个 [2] 而没有 [1]
请确保使用 pre-sphinxification 图像路径(而不是 _images 目录)
弃用周期#
如果必须更改函数的调用方式,则必须遵循弃用周期以警告用户。
在以下情况下,不需要弃用周期
添加新函数,或
在函数签名的末尾添加新的关键字参数,或
修复意外或不正确的行为。
在以下情况下,需要弃用周期
重命名关键字参数,或
更改参数或关键字的顺序,或
向函数添加参数,或
更改函数的名称或位置,或
更改函数参数或关键字的默认值。
通常,弃用警告在更改之前会保留两个版本。
例如,考虑在函数签名中修改默认值。在 N 版本中,我们有
def some_function(image, rescale=True):
"""Do something.
Parameters
----------
image : ndarray
Input image.
rescale : bool, optional
Rescale the image unless ``False`` is given.
Returns
-------
out : ndarray
The resulting image.
"""
out = do_something(image, rescale=rescale)
return out
在 N+1 版本中,我们将更改为
def some_function(image, rescale=None):
"""Do something.
Parameters
----------
image : ndarray
Input image.
rescale : bool, optional
Rescale the image unless ``False`` is given.
.. warning:: The default value will change from ``True`` to
``False`` in skimage N+3.
Returns
-------
out : ndarray
The resulting image.
"""
if rescale is None:
warn('The default value of rescale will change '
'to `False` in version N+3.', stacklevel=2)
rescale = True
out = do_something(image, rescale=rescale)
return out
并且,在 N+3 版本中
def some_function(image, rescale=False):
"""Do something.
Parameters
----------
image : ndarray
Input image.
rescale : bool, optional
Rescale the image if ``True`` is given.
Returns
-------
out : ndarray
The resulting image.
"""
out = do_something(image, rescale=rescale)
return out
这是 3 个版本弃用周期的过程
在函数中,如果 rescale 为
None
,则将其设置为True
并警告默认值将在 N+3 版本中更改为False
。在
doc/release/release_dev.rst
中,在弃用下,添加 “在some_function
中,rescale
参数在 N+3 中将默认为False
。”在
TODO.txt
中,在与 N+3 版本相关的部分中创建一个项目,并写入 “在 some_function 中将 rescale 默认值更改为 False”。
请注意,3 个版本的弃用周期不是严格的规则,在某些情况下,开发人员可以就不同的程序达成一致。
引发警告#
skimage
引发 FutureWarning
来突出显示其 API 中的更改,例如
from warnings import warn
warn(
"Automatic detection of the color channel was deprecated in "
"v0.19, and `channel_axis=None` will be the new default in "
"v0.22. Set `channel_axis=-1` explicitly to silence this "
"warning.",
FutureWarning,
stacklevel=2,
)
stacklevel 有点技术性,但可确保警告指向用户调用的函数,而不是指向内部的实用程序函数。
在大多数情况下,将 stacklevel
设置为 2
。当警告源于 scikit-image 库内部的辅助例程时,将其设置为 3
。
要测试是否正确发出警告,请尝试从 IPython 控制台调用该函数。它应该将您指向控制台输入本身,而不是由 scikit-image 库中的文件发出
良好:
ipython:1: UserWarning: ...
不良:
scikit-image/skimage/measure/_structural_similarity.py:155: UserWarning:
弃用关键字和函数#
当删除关键字或整个函数时,可以使用 skimage._shared.utils.deprecate_parameter
和 skimage._shared.utils.deprecate_func
实用程序函数来执行上述过程。
添加数据#
虽然代码托管在 github 上,但示例数据集位于 gitlab 上。在访问 skimage.data.*
时,这些数据集通过 pooch 获取。
新的数据集在 gitlab 上提交,一旦合并,就可以更新主 GitHub 存储库中的数据注册表 skimage/data/_registry.py
。
基准测试#
虽然对于大多数拉取请求不是强制性的,但我们要求与性能相关的 PR 包括基准测试,以便清楚地描述正在优化的用例。我们快照的历史视图可以在以下网站上找到。
在本节中,我们将回顾如何设置基准测试,以及三个命令 spin asv -- dev
、spin asv -- run
和 spin asv -- continuous
。
先决条件#
首先,在您的开发环境中安装 airspeed velocity。在安装之前,请务必激活您的开发环境,然后如果使用 venv
,您可以使用以下命令安装依赖项:
source skimage-dev/bin/activate
pip install asv
如果您使用 conda,则以下命令更合适:
conda activate skimage-dev
conda install asv
安装完成后,运行以下命令会很有用:
spin asv -- machine
让 airspeed velocity 了解更多关于您机器的信息。
编写基准测试#
要编写基准测试,请在 benchmarks
目录中添加一个文件,该文件包含一个类,该类具有一个 setup
方法和至少一个以 time_
为前缀的方法。
time_
方法应仅包含您希望进行基准测试的代码。因此,将准备基准测试场景的所有内容移至 setup
方法中会很有用。此函数在调用 time_
方法之前调用,并且其执行时间不计入基准测试中。
以 TransformSuite
基准测试为例:
import numpy as np
from skimage import transform
class TransformSuite:
"""Benchmark for transform routines in scikit-image."""
def setup(self):
self.image = np.zeros((2000, 2000))
idx = np.arange(500, 1500)
self.image[idx[::-1], idx] = 255
self.image[idx, idx] = 255
def time_hough_line(self):
result1, result2, result3 = transform.hough_line(self.image)
这里,图像的创建在 setup
方法中完成,并且不包括在基准测试的报告时间中。
还可以对峰值内存使用率等功能进行基准测试。要了解更多关于这些功能的信息,请参阅官方的 airspeed velocity 文档。
此外,在对旧版本的 scikit-image 进行基准测试时,基准测试文件需要是可导入的。因此,如果从 scikit-image 导入任何内容在顶层,则应按如下方式进行:
try:
from skimage import metrics
except ImportError:
pass
基准测试本身不需要针对缺失的功能进行任何保护,只需要顶层导入即可。
为了允许将较新函数的测试标记为“n/a”(不可用),而不是旧版本的“失败”,setup 方法本身可以引发 NotImplemented 错误。请参阅以下注册模块的示例:
try:
from skimage import registration
except ImportError:
raise NotImplementedError("registration module not available")
在本地测试基准测试#
在运行真正的基准测试之前,通常值得测试代码是否没有拼写错误。为此,您可以使用以下命令:
spin asv -- dev -b TransformSuite
上面的 TransformSuite
将在您当前的环境中运行一次,以测试一切是否正常。
运行您的基准测试#
上面的命令很快,但不足以测试代码的性能。为此,您可能希望在当前环境中运行基准测试,以查看您在开发新功能时所做更改的性能。命令 asv run -E existing
将指定您希望在现有环境中运行基准测试。这将节省大量时间,因为构建 scikit-image 可能是一项耗时的任务。
spin asv -- run -E existing -b TransformSuite
将结果与 main 分支进行比较#
通常,PR 的目标是将修改的速度结果与 scikit-image
存储库 main 分支中的代码快照进行比较。命令 asv continuous
在这里很有帮助。
spin asv -- continuous main -b TransformSuite
此调用将构建 asv.conf.json
文件中指定的环境,并比较当前提交和 main 分支中代码之间的基准测试性能。
输出可能如下所示:
$ spin asv -- continuous main -b TransformSuite
· Creating environments
· Discovering benchmarks
·· Uninstalling from conda-py3.7-cython-numpy1.15-scipy
·· Installing 544c0fe3 <benchmark_docs> into conda-py3.7-cython-numpy1.15-scipy.
· Running 4 total benchmarks (2 commits * 2 environments * 1 benchmarks)
[ 0.00%] · For scikit-image commit 37c764cb <benchmark_docs~1> (round 1/2):
[...]
[100.00%] ··· ...ansform.TransformSuite.time_hough_line 33.2±2ms
BENCHMARKS NOT SIGNIFICANTLY CHANGED.
在这种情况下,HEAD 和 main 之间的差异不足以让 airspeed velocity 报告。
还可以通过 asv compare
命令获取先前运行过基准测试的两个特定修订版本的结果比较。
spin asv -- compare v0.14.5 v0.17.2
最后,还可以通过在提交或标签名称后附加 ^!
来仅为特定的提交哈希或发布标签运行 ASV 基准测试。例如,要在发布版本 v0.17.2 上运行 skimage.filter 模块基准测试:
spin asv -- run -b Filter v0.17.2^!