如何貢獻至 scikit-image#

作為社群的一份子開發開源軟體很有趣,而且通常也很有教育意義!

我們使用 GitHub 協調我們的工作,您可以在其中找到 開啟的問題新功能請求的清單。

若要追蹤討論,或與開發團隊聯繫,請加入我們的 scikit-image 開發人員論壇Zulip 聊天

請將問題發佈到這些公開論壇 (而不是直接聯繫開發人員);這樣一來,每個人都可以從答案中獲益,而且開發人員可以根據他們的時間回覆。別害羞,團隊非常友善!

開發流程#

以下是如何將原始碼和文件變更貢獻至 scikit-image 的簡要概述。

  1. 如果您是第一次貢獻者

    • 前往 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
      
  2. 開發您的貢獻

    • 從上游提取最新的變更

      git checkout main
      git pull upstream main
      
    • 為您要處理的功能建立分支。使用合理名稱,例如「transform-speedups」

      git checkout -b transform-speedups
      
    • 在您進展時在本機提交 (使用 git addgit commit)。請撰寫 良好的提交訊息

  3. 若要提交您的貢獻

    • 將您的變更推送回您在 GitHub 上的 fork

      git push origin transform-speedups
      
    • 輸入您的 GitHub 使用者名稱和密碼 (重複貢獻者或進階使用者可以透過 使用 SSH 連接至 GitHub 來移除此步驟)。

    • 前往 GitHub。新的分支將會顯示綠色的「pull request」按鈕,按一下它。

    • 如果您願意,請在 開發人員論壇上發文,說明您的變更或要求審閱。

如需更詳細的討論,請閱讀這些關於如何搭配 scikit-image 使用 Git 的 詳細文件 (使用 scikit-image 原始碼)。

  1. 審閱流程

    • 審閱者 (其他開發人員和感興趣的社群成員) 將在您的 pull request (PR) 上撰寫內嵌和/或一般意見,以協助您改善其實作、文件和風格。專案中工作的每一位開發人員都會審閱其程式碼,而且我們已經將其視為友善的對話,從中我們都可以學習,並讓整體程式碼品質受益。因此,請不要讓審閱阻礙您貢獻:其唯一目的是改善專案的品質,而不是批評 (畢竟,我們非常感謝您捐贈的時間!)。

    • 若要更新您的 pull request,請在您的本機儲存庫中進行變更並提交。只要將這些變更推送上來 (與之前相同的分支),pull request 就會自動更新。

    • 每次提交 pull request 後,都會觸發持續整合 (CI) 服務,以建置套件、執行單元測試、測量程式碼覆蓋率,並檢查您分支的程式碼風格 (PEP8)。測試必須通過,您的 PR 才能合併。如果 CI 失敗,您可以按一下「失敗」圖示 (紅色叉號) 並檢查建置和測試記錄,找出失敗的原因。

    • pull request 必須在合併前由兩位核心團隊成員核准。

  1. 文件變更

    如果您的變更引入了棄用,請在 TODO.txt 中新增提醒,讓團隊在未來移除棄用的功能。

    scikit-image 使用 changelist 從 pull request 自動產生發行說明清單。根據預設,changelist 將使用 pull request 的標題及其 GitHub 標籤,將其排序到適當的章節中。但是,對於更複雜的變更,我們鼓勵您使用 pull request 說明中的 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 使用者使用 rebase 而非 merge,但我們無論如何都會 squash 並合併大多數 PR。

準則#

  • 所有程式碼都應有測試 (如需更多詳細資訊,請參閱下方的 測試覆蓋率)。

  • 所有程式碼都應記錄在案,其 標準與 NumPy 和 SciPy 相同。

  • 對於新功能,務必將範例新增至展示區 (如需更多詳細資訊,請參閱下方的 展示區)。

  • 未經兩位核心團隊成員的審閱和核准,任何變更都不得合併。此規則有兩個例外。首先,在大多數情況下,僅影響文件的 pull request 只需要一位核心團隊成員的審閱和核准。如果維護者認為變更很大或可能引起爭議,仍應鼓勵進行兩次審閱。第二種情況是修復次要錯誤,使其恢復 CI 的工作狀態,因為這些錯誤應盡快合併。如果您沒有收到 pull request 的回覆,請在 開發人員論壇上發文。永遠不要合併您自己的 pull request。

風格指南#

  • 設定您的編輯器以移除尾隨空白。遵循 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,然後在 docstring 中參照 MN (如果必要)。

  • 將陣列維度參照為 (平面)、列、欄,而不是 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 程式碼包裝在純 Python 函式中,以定義 API。這可改善與程式碼內省工具的相容性,這些工具通常不了解 Cython 程式碼。

  • 對於 Cython 函式,請盡可能使用 with nogil: 來釋放 GIL。

測試#

在合併 Pull Request 之前,測試套件必須通過,並且應加入測試以涵蓋所有行為上的修改。

我們使用 pytest 測試框架,測試位於各個 skimage/submodule/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

  • 執行所有測試和 doctestspin 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 標記以從頭開始建置,刪除任何快取的輸出。

修正警告#

  • 「找不到引用:R###」docstring 第一行中的參照後方可能有一個底線 (例如 [1]_)。使用此方法尋找來源檔案:$ cd doc/build; grep -rin R####

  • 「重複引用 R###,其他實例位於…」其中一個 docstring 中可能有一個 [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 版本棄用週期的流程

  • 將預設值設定為 None,並修改 docstring 以指定預設值為 True

  • 在函式中,如果 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 引發 FutureWarnings 來強調其 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_parameterskimage._shared.utils.deprecate_func 公用程式函式來執行上述程序。

新增資料#

程式碼託管於 github 上,範例資料集則位於 gitlab 上。存取 skimage.data.* 時,會使用 pooch 提取這些資料集。

新的資料集會在 gitlab 上提交,合併後,可以更新主要 GitHub 儲存庫中的資料登錄 skimage/data/_registry.py

效能基準#

雖然並非大多數 Pull Request 的強制要求,但我們要求與效能相關的 PR 包含效能基準,以便清楚描述正在優化的使用案例。我們快照的歷史檢視可以在以下網站上找到。

在本節中,我們將回顧如何設定效能基準,以及三個命令 spin asv -- devspin asv -- runspin 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

將結果與主分支比較#

通常,PR 的目標是將修改在速度方面的結果與 scikit-image 儲存庫主分支中的程式碼快照進行比較。命令 asv continuous 在此處會有所幫助

spin asv -- continuous main -b TransformSuite

此呼叫將會建置在 asv.conf.json 檔案中指定的環境,並比較您目前 commit 和主分支中程式碼之間的效能評測效能。

輸出看起來可能像這樣

$ 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

最後,也可以僅針對特定 commit hash 或發佈標籤執行 ASV 效能評測,方法是將 ^! 附加到 commit 或標籤名稱。例如,要在發佈版本 v0.17.2 上執行 skimage.filter 模組的效能評測

spin asv -- run -b Filter v0.17.2^!