scikit-imageへの貢献方法#

コミュニティの一員としてオープンソースソフトウェアを開発することは、楽しく、そして多くの場合、非常に教育的です!

GitHubを使用して作業を調整しています。そこでは、未解決の問題新しい機能リクエストのリストを見つけることができます。

ディスカッションに参加したり、開発チームと連絡を取り合ったりするには、scikit-image開発者フォーラムZulipチャットに参加してください。

質問はこれらの公開フォーラムに投稿してください(開発者に直接連絡するのではなく)。そうすることで、誰もが回答から恩恵を受けることができ、開発者は自分の空き時間に回答できます。遠慮しないでください、チームはとてもフレンドリーです!

開発プロセス#

以下は、ソースコードとドキュメントへの変更をscikit-imageに貢献する方法についての簡単な概要です。

  1. 初めて貢献する場合

    • scikit-image/scikit-imageにアクセスし、「フォーク」ボタンをクリックしてプロジェクトの独自の複製を作成します。

    • プロジェクトソースを含むリポジトリをローカルコンピュータに複製(ダウンロード)します。

      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
      
    • これで、次のように名前が付けられたリモートリポジトリができました。

      • upstreamscikit-imageリポジトリを参照し、

      • originはあなたの個人用フォークを参照します。

    • 次に、ビルド環境を設定します。

    • 最後に、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のあなたのフォークにプッシュバックします。

      git push origin transform-speedups
      
    • GitHubのユーザー名とパスワードを入力します(繰り返し貢献者または上級ユーザーは、SSHを使用してGitHubに接続することでこのステップを削除できます)。

    • GitHubにアクセスします。新しいブランチが緑色の「プルリクエスト」ボタンとともに表示されます。それをクリックします。

    • 必要に応じて、開発者フォーラムに投稿して、変更内容を説明したり、レビューを依頼したりできます。

より詳細な説明については、scikit-imageでGitを使用する方法に関するこれらの詳細なドキュメントscikit-imageソースコードの操作)をお読みください。

  1. レビュープロセス

    • レビューアー(他の開発者や関心のあるコミュニティメンバー)は、実装、ドキュメント、スタイルの改善に役立つように、プルリクエスト(PR)にインラインコメントや一般的なコメントを書き込みます。プロジェクトで作業しているすべての開発者はコードレビューを受けており、私たちはそれを、全員が学び、全体的なコード品質が向上するフレンドリーな会話と考えています。そのため、レビューによって貢献をためらわないでください。その目的はプロジェクトの品質を向上させることであり、批判することではありません(結局のところ、あなたの寄付してくれた時間には非常に感謝しています!)。

    • プルリクエストを更新するには、ローカルリポジトリに変更を加えてコミットします。それらの変更が(以前と同じブランチに)プッシュされるとすぐに、プルリクエストは自動的に更新されます。

    • 各プルリクエストの送信後、継続的インテグレーション(CI)サービスがトリガーされ、パッケージのビルド、単体テストの実行、コードカバレッジの測定、ブランチのコーディングスタイル(PEP8)のチェックが行われます。PRをマージするには、テストに合格する必要があります。CIが失敗した場合は、「失敗」アイコン(赤いバツ印)をクリックしてビルドとテストのログを検査することで、原因を特定できます。

    • プルリクエストは、マージされる前に2人のコアチームメンバーによって承認される必要があります。

  1. ドキュメントの変更

    変更によって非推奨化が導入された場合は、将来チームが非推奨になった機能を削除するためのリマインダーを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ユーザーはマージではなくrebaseを行うことをお勧めしますが、いずれにしてもほとんどのPRはsquash and mergeします。

ガイドライン#

  • すべてのコードにはテストが必要です(詳細については、以下のテストカバレッジを参照してください)。

  • すべてのコードは、NumPyとSciPyと同じ標準で文書化される必要があります。

  • 新しい機能については、常にギャラリーに例を追加してください(詳細については、以下のギャラリーを参照してください)。

  • 2人のコアチームメンバーによるレビューと承認なしで変更がマージされることはありません。このルールには2つの例外があります。まず、ドキュメントのみに影響を与えるプルリクエストは、ほとんどの場合、1人のコアチームメンバーによるレビューと承認のみが必要です。メンテナが変更が大きい、または論争の的になる可能性があると判断した場合は、2つのレビューを依然として推奨する必要があります。2番目のケースは、CIを動作状態に復元するマイナーな修正です。これはかなり迅速にマージする必要があります。開発者フォーラムで、プルリクエストへの応答がない場合はご連絡ください。**決して自分のプルリクエストをマージしないでください。**

スタイルガイドライン#

  • エディターを設定して、末尾の空白を削除します。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 skimage._shared import xyzではなくfrom .._shared import xyzなど、相対モジュールインポートを使用します。

  • APIを定義する純粋なPython関数でCythonコードをラップします。これにより、多くの場合Cythonコードを認識しないコードイントロスペクションツールとの互換性が向上します。

  • Cython関数は、可能な限り`with nogil:`を使ってGILを解放してください。

テスト#

プルリクエストをマージするには、テストスイートがパスする必要があります。また、動作の変更をすべて網羅するテストを追加する必要があります。

テストフレームワークには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 -- 任意の pytest 引数

  • すべてのテストとdoctestの実行: spin test -- --doctest-plus skimage

テストフェーズでの警告#

デフォルトでは、テストスイートによって発生した警告はエラーになります。環境変数SKIMAGE_TEST_STRICT_WARNINGS0に設定することで、この動作を無効にすることができます。

テストカバレッジ#

モジュールのテストは、理想的にはそのモジュールのすべてのコードを網羅する必要があります。つまり、ステートメントカバレッジは100%である必要があります。

テストカバレッジを測定するには、以下のコマンドを実行します。

$ spin test --coverage

これにより、テストが実行され、skimage内の各ファイルについて1行のレポートが表示され、テストカバレッジの詳細が示されます。

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フラグを追加すると、キャッシュされた出力を削除して最初からビルドできます。

警告の修正#

  • “citation not found: R###” ドキュメント文字列の最初の行で参照の後にアンダースコアが付いている可能性があります(例:[1]_)。ソースファイルを見つけるには、この方法を使用します。`$ cd doc/build; grep -rin R####`

  • “Duplicate citation R###, other instance in…”” ドキュメント文字列のいずれかに[1]がないのに[2]がある可能性があります。

  • 画像には、sphinx処理前のパス(_imagesディレクトリではない)を使用してください。

非推奨化サイクル#

関数の呼び出し方法を変更する必要がある場合は、ユーザーに警告するために非推奨化サイクルに従う必要があります。

非推奨化サイクルは、以下の場合に必要ありません。

  • 新しい関数の追加、または

  • 関数のシグネチャの末尾に新しいキーワード引数を追加する場合、または

  • 予期しない動作または誤った動作を修正する場合。

非推奨化サイクルは、以下の場合に必要です。

  • キーワード引数の名前変更、または

  • 引数またはキーワードの順序の変更、または

  • 関数の引数の追加、または

  • 関数の名前または場所の変更、または

  • 関数引数またはキーワードのデフォルト値の変更。

通常、非推奨化警告は変更が行われる前に2つのリリースの間有効です。

例として、関数シグネチャのデフォルト値の変更を考えてみましょう。バージョン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に設定し、ドキュメント文字列を変更してデフォルトが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は、APIの変更を強調するためにFutureWarningを発生させます。例:

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はやや技術的な問題ですが、警告がユーザーが呼び出した関数ではなく、内部のユーティリティ関数ではなく、ユーザーが呼び出した関数に関連付けられるようにします。

ほとんどの場合、stacklevel2に設定します。警告が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 -- devspin asv -- runspin asv -- continuousの3つのコマンドについて説明します。

前提条件#

まず、開発環境に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 ディレクトリにファイルを追加します。このファイルには、1つのsetupメソッドと、time_で始まるメソッドを少なくとも1つ含むクラスが含まれている必要があります。

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メソッド自体がNotImplementedErrorを発生させることができます。登録モジュールに関する次の例を参照してください。

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ファイルで指定された環境を構築し、現在のコミットとメインブランチのコードとの間のベンチマークのパフォーマンスを比較します。

出力は次のようになる場合があります。

$ 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とメインブランチの違いは、airspeed velocityが報告するには不十分です。

asv compareコマンドを使用すると、以前にベンチマーク結果が実行された2つの特定のリビジョンについて、結果を比較することもできます。

spin asv -- compare v0.14.5 v0.17.2

最後に、コミットハッシュまたはリリースタグに^!を追加することで、特定のコミットハッシュまたはリリースタグに対してのみASVベンチマークを実行することもできます。たとえば、リリースv0.17.2でskimage.filterモジュールのベンチマークを実行するには、

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