Spaces:
Sleeping
Sleeping
Merge pull request #10 from umyuu/feature/0.0.8
Browse files- app.py +53 -23
- assets/DSC_0108.webp +0 -0
- assets/DSC_0297.webp +0 -0
- assets/black_256x256.webp +0 -0
- assets/grayscale_256x256.webp +0 -0
- docs/ThirdPartyNotices.txt +8 -0
- src/__init__.py +1 -1
- src/args_parser.py +23 -0
- src/reporter.py +1 -1
- src/saliency.py +23 -9
- src/utils.py +18 -11
app.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
"""
|
| 3 |
SaliencyMapDemo
|
| 4 |
"""
|
| 5 |
-
|
| 6 |
#from datetime import datetime
|
| 7 |
import sys
|
| 8 |
from typing import Literal
|
|
@@ -11,6 +11,7 @@ import gradio as gr
|
|
| 11 |
import numpy as np
|
| 12 |
|
| 13 |
from src import PROGRAM_NAME, get_package_version
|
|
|
|
| 14 |
from src.reporter import log
|
| 15 |
from src.saliency import SaliencyMap, convert_colormap
|
| 16 |
from src.utils import Stopwatch
|
|
@@ -20,25 +21,6 @@ log.info("#アプリ起動中")
|
|
| 20 |
watch = Stopwatch.start_new()
|
| 21 |
|
| 22 |
|
| 23 |
-
def parse_args():
|
| 24 |
-
"""
|
| 25 |
-
コマンドライン引数の解析を行います
|
| 26 |
-
"""
|
| 27 |
-
parser = ArgumentParser(prog=PROGRAM_NAME, description=PROGRAM_NAME)
|
| 28 |
-
parser.add_argument('--inbrowser',
|
| 29 |
-
action=BooleanOptionalAction, default=True, help="Gradio inbrowser")
|
| 30 |
-
parser.add_argument('--share',
|
| 31 |
-
action=BooleanOptionalAction, default=False, help="Gradio share")
|
| 32 |
-
parser.add_argument('--server_port',
|
| 33 |
-
type=int, default=7860, help="Gradio server port")
|
| 34 |
-
parser.add_argument('--max_file_size',
|
| 35 |
-
type=str, default="20MB", help="Gradio max file size")
|
| 36 |
-
parser.add_argument('--version',
|
| 37 |
-
action='version', version=f'%(prog)s {__version__}')
|
| 38 |
-
|
| 39 |
-
return parser.parse_args()
|
| 40 |
-
|
| 41 |
-
|
| 42 |
def jet_tab_selected(image: np.ndarray):
|
| 43 |
"""
|
| 44 |
JETタブを選択時
|
|
@@ -101,6 +83,21 @@ def submit_clicked(image: np.ndarray, algorithm: Literal["SpectralResidual", "Fi
|
|
| 101 |
return jet, hot
|
| 102 |
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
args = parse_args()
|
| 105 |
"""
|
| 106 |
アプリの画面を作成し、Gradioサービスを起動します。
|
|
@@ -123,9 +120,15 @@ with gr.Blocks(
|
|
| 123 |
""")
|
| 124 |
with gr.Accordion("取り扱い説明書", open=False):
|
| 125 |
gr.Markdown("""
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
| 128 |
3. 結果は、JETタブとHOTタブに表示します。
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
""")
|
| 130 |
algorithm_type = gr.Radio(
|
| 131 |
["SpectralResidual", "FineGrained"],
|
|
@@ -150,6 +153,32 @@ with gr.Blocks(
|
|
| 150 |
# inputs=[image_input],
|
| 151 |
# outputs=image_overlay_hot, api_name=False)
|
| 152 |
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
submit_button.click(
|
| 154 |
submit_clicked,
|
| 155 |
inputs=[image_input, algorithm_type],
|
|
@@ -160,9 +189,10 @@ with gr.Blocks(
|
|
| 160 |
App {get_package_version()}
|
| 161 |
""")
|
| 162 |
|
| 163 |
-
demo.queue(default_concurrency_limit=
|
| 164 |
|
| 165 |
log.info(f"#アプリ起動完了({watch.elapsed:.3f}s)アプリを終了するにはCtrl+Cキーを入力してください。")
|
|
|
|
| 166 |
|
| 167 |
|
| 168 |
if __name__ == "__main__":
|
|
|
|
| 2 |
"""
|
| 3 |
SaliencyMapDemo
|
| 4 |
"""
|
| 5 |
+
|
| 6 |
#from datetime import datetime
|
| 7 |
import sys
|
| 8 |
from typing import Literal
|
|
|
|
| 11 |
import numpy as np
|
| 12 |
|
| 13 |
from src import PROGRAM_NAME, get_package_version
|
| 14 |
+
from src.args_parser import parse_args
|
| 15 |
from src.reporter import log
|
| 16 |
from src.saliency import SaliencyMap, convert_colormap
|
| 17 |
from src.utils import Stopwatch
|
|
|
|
| 21 |
watch = Stopwatch.start_new()
|
| 22 |
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
def jet_tab_selected(image: np.ndarray):
|
| 25 |
"""
|
| 26 |
JETタブを選択時
|
|
|
|
| 83 |
return jet, hot
|
| 84 |
|
| 85 |
|
| 86 |
+
def gallery_selected(_, evt: gr.SelectData):
|
| 87 |
+
"""
|
| 88 |
+
ギャラリーの画像が選択されたときに呼び出されるコールバック関数。
|
| 89 |
+
|
| 90 |
+
Parameters:
|
| 91 |
+
_ (Unused): 使用されない引数。
|
| 92 |
+
evt (gr.SelectData): Gradioのギャラリー選択イベントデータ。
|
| 93 |
+
Returns:
|
| 94 |
+
str: 選択されたギャラリー画像のパス。
|
| 95 |
+
"""
|
| 96 |
+
image_path = evt.value['image']['path']
|
| 97 |
+
|
| 98 |
+
return image_path
|
| 99 |
+
|
| 100 |
+
|
| 101 |
args = parse_args()
|
| 102 |
"""
|
| 103 |
アプリの画面を作成し、Gradioサービスを起動します。
|
|
|
|
| 120 |
""")
|
| 121 |
with gr.Accordion("取り扱い説明書", open=False):
|
| 122 |
gr.Markdown("""
|
| 123 |
+
### 操作説明
|
| 124 |
+
顕著性マップデモを使用する手順は以下の通りです:
|
| 125 |
+
1. inputタブで画像を選択します。下部の📋下部のクリップボードアイコン(コピー&ペーストアイコン)よりクリップボートから入力することも出来ます。
|
| 126 |
+
2. Submitボタンを押すと、入力された画像が処理されます。
|
| 127 |
3. 結果は、JETタブとHOTタブに表示します。
|
| 128 |
+
### 活用アイデア🎨
|
| 129 |
+
このデモは、創作活動の際に注目するポイントを視覚化するために役立ちます。視覚化された結果を基に、どの部分に加筆が必要かを判断することができます。
|
| 130 |
+
例えば、目に注目するポイントが少ない場合は、目を重点的に加筆することで、作品全体の魅力を高めることができるかもしれません。
|
| 131 |
+
ご利用いただき、ありがとうございます。
|
| 132 |
""")
|
| 133 |
algorithm_type = gr.Radio(
|
| 134 |
["SpectralResidual", "FineGrained"],
|
|
|
|
| 153 |
# inputs=[image_input],
|
| 154 |
# outputs=image_overlay_hot, api_name=False)
|
| 155 |
#
|
| 156 |
+
with gr.Accordion("Sample Image Gallery", open=False):
|
| 157 |
+
gr.Markdown("""
|
| 158 |
+
### 画像のライセンス表示
|
| 159 |
+
画像のライセンスはすべてCC0(パブリックドメイン)です。
|
| 160 |
+
""")
|
| 161 |
+
gallery = gr.Gallery(type="filepath",
|
| 162 |
+
value=["assets/black_256x256.webp",
|
| 163 |
+
"assets/grayscale_256x256.webp",
|
| 164 |
+
"assets/DSC_0108.webp",
|
| 165 |
+
"assets/DSC_0297.webp"],
|
| 166 |
+
label="Sample Gallery",
|
| 167 |
+
interactive=False,
|
| 168 |
+
#height=156,
|
| 169 |
+
columns=5,
|
| 170 |
+
allow_preview=False,
|
| 171 |
+
selected_index=0,
|
| 172 |
+
preview=False,
|
| 173 |
+
show_download_button=False,
|
| 174 |
+
show_share_button=False
|
| 175 |
+
)
|
| 176 |
+
# ギャラリー内の画像を選択時
|
| 177 |
+
gallery.select(gallery_selected,
|
| 178 |
+
inputs=[gallery],
|
| 179 |
+
outputs=[image_input],
|
| 180 |
+
show_api=False
|
| 181 |
+
)
|
| 182 |
submit_button.click(
|
| 183 |
submit_clicked,
|
| 184 |
inputs=[image_input, algorithm_type],
|
|
|
|
| 189 |
App {get_package_version()}
|
| 190 |
""")
|
| 191 |
|
| 192 |
+
demo.queue(default_concurrency_limit=5)
|
| 193 |
|
| 194 |
log.info(f"#アプリ起動完了({watch.elapsed:.3f}s)アプリを終了するにはCtrl+Cキーを入力してください。")
|
| 195 |
+
log.debug("reload")
|
| 196 |
|
| 197 |
|
| 198 |
if __name__ == "__main__":
|
assets/DSC_0108.webp
ADDED
|
assets/DSC_0297.webp
ADDED
|
assets/black_256x256.webp
ADDED
|
assets/grayscale_256x256.webp
ADDED
|
docs/ThirdPartyNotices.txt
CHANGED
|
@@ -4,6 +4,14 @@ This repository uses the materials listed or described below.
|
|
| 4 |
|
| 5 |
---------------------------------------------------------
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
Gradio - Apache-2.0
|
| 8 |
https://github.com/gradio-app/gradio
|
| 9 |
---------------------------------------------------------
|
|
|
|
| 4 |
|
| 5 |
---------------------------------------------------------
|
| 6 |
|
| 7 |
+
DSC_0297 - CC0
|
| 8 |
+
https://www.flickr.com/photos/layout_nu/14445520741/
|
| 9 |
+
---------------------------------------------------------
|
| 10 |
+
|
| 11 |
+
DSC_0108 - CC0
|
| 12 |
+
https://www.flickr.com/photos/layout_nu/14239881090/
|
| 13 |
+
---------------------------------------------------------
|
| 14 |
+
|
| 15 |
Gradio - Apache-2.0
|
| 16 |
https://github.com/gradio-app/gradio
|
| 17 |
---------------------------------------------------------
|
src/__init__.py
CHANGED
|
@@ -9,7 +9,7 @@ from src.utils import get_package_version
|
|
| 9 |
|
| 10 |
__all__ = ["LocalTimeFormatter"]
|
| 11 |
|
| 12 |
-
PROGRAM_NAME = 'SaliencyMapDemo'
|
| 13 |
__version__ = get_package_version()
|
| 14 |
|
| 15 |
|
|
|
|
| 9 |
|
| 10 |
__all__ = ["LocalTimeFormatter"]
|
| 11 |
|
| 12 |
+
PROGRAM_NAME: str = 'SaliencyMapDemo'
|
| 13 |
__version__ = get_package_version()
|
| 14 |
|
| 15 |
|
src/args_parser.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""コマンドライン引数の解析"""
|
| 3 |
+
from argparse import ArgumentParser, BooleanOptionalAction
|
| 4 |
+
from . import PROGRAM_NAME, get_package_version
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def parse_args():
|
| 8 |
+
"""
|
| 9 |
+
コマンドライン引数の解析を行います
|
| 10 |
+
"""
|
| 11 |
+
parser = ArgumentParser(prog=PROGRAM_NAME, description=PROGRAM_NAME)
|
| 12 |
+
parser.add_argument('--inbrowser',
|
| 13 |
+
action=BooleanOptionalAction, default=True, help="Gradio inbrowser")
|
| 14 |
+
parser.add_argument('--share',
|
| 15 |
+
action=BooleanOptionalAction, default=False, help="Gradio share")
|
| 16 |
+
parser.add_argument('--server_port',
|
| 17 |
+
type=int, default=7860, help="Gradio server port")
|
| 18 |
+
parser.add_argument('--max_file_size',
|
| 19 |
+
type=str, default="20MB", help="Gradio max file size")
|
| 20 |
+
parser.add_argument('--version',
|
| 21 |
+
action='version', version=f'%(prog)s {get_package_version()}')
|
| 22 |
+
|
| 23 |
+
return parser.parse_args()
|
src/reporter.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""
|
| 3 |
-
Reporter
|
| 4 |
ログハンドラーが重複登録されるのを防ぐために1箇所で生成してログハンドラーを返します。
|
| 5 |
Example:
|
| 6 |
from src.reporter import log
|
|
@@ -27,6 +26,7 @@ class Reporter:
|
|
| 27 |
|
| 28 |
def __new__(cls):
|
| 29 |
"""
|
|
|
|
| 30 |
"""
|
| 31 |
# インスタンスがまだ存在しない場合は新たに作成します。
|
| 32 |
if not cls._instance:
|
|
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""
|
|
|
|
| 3 |
ログハンドラーが重複登録されるのを防ぐために1箇所で生成してログハンドラーを返します。
|
| 4 |
Example:
|
| 5 |
from src.reporter import log
|
|
|
|
| 26 |
|
| 27 |
def __new__(cls):
|
| 28 |
"""
|
| 29 |
+
インスタンスの生成を制御します。
|
| 30 |
"""
|
| 31 |
# インスタンスがまだ存在しない場合は新たに作成します。
|
| 32 |
if not cls._instance:
|
src/saliency.py
CHANGED
|
@@ -8,7 +8,8 @@ import cv2
|
|
| 8 |
|
| 9 |
class SaliencyMap:
|
| 10 |
"""
|
| 11 |
-
|
|
|
|
| 12 |
Example:
|
| 13 |
from src.saliency import SaliencyMap
|
| 14 |
|
|
@@ -19,6 +20,15 @@ class SaliencyMap:
|
|
| 19 |
self,
|
| 20 |
algorithm: Literal["SpectralResidual", "FineGrained"] = "SpectralResidual",
|
| 21 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
self.algorithm = algorithm
|
| 23 |
# OpenCVのsaliencyを作成します。
|
| 24 |
if algorithm == "SpectralResidual":
|
|
@@ -28,17 +38,16 @@ class SaliencyMap:
|
|
| 28 |
|
| 29 |
def compute(self, image: np.ndarray) -> Tuple[bool, Any]:
|
| 30 |
"""
|
| 31 |
-
|
| 32 |
|
| 33 |
Parameters:
|
| 34 |
image: 入力画像
|
| 35 |
|
| 36 |
Returns:
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
"""
|
| 41 |
-
# 画像の顕著性を計算します。
|
| 42 |
return self.saliency.computeSaliency(image)
|
| 43 |
|
| 44 |
|
|
@@ -48,19 +57,24 @@ def convert_colormap(
|
|
| 48 |
colormap_name: Literal["jet", "hot", "turbo"] = "jet"
|
| 49 |
):
|
| 50 |
"""
|
| 51 |
-
|
| 52 |
|
| 53 |
Parameters:
|
| 54 |
image: 入力画像
|
| 55 |
saliency_map: 顕著性マップ
|
| 56 |
colormap_name: カラーマップの種類
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
Returns:
|
| 59 |
-
np.ndarray:
|
| 60 |
"""
|
| 61 |
maps = {"jet": cv2.COLORMAP_JET, "hot": cv2.COLORMAP_HOT, "turbo": cv2.COLORMAP_TURBO}
|
|
|
|
|
|
|
| 62 |
if colormap_name not in maps:
|
| 63 |
-
raise ValueError(colormap_name)
|
| 64 |
|
| 65 |
# 顕著性マップをカラーマップに変換
|
| 66 |
saliency_map = (saliency_map * 255).astype("uint8")
|
|
|
|
| 8 |
|
| 9 |
class SaliencyMap:
|
| 10 |
"""
|
| 11 |
+
画像から顕著性マップを計算するクラス。
|
| 12 |
+
|
| 13 |
Example:
|
| 14 |
from src.saliency import SaliencyMap
|
| 15 |
|
|
|
|
| 20 |
self,
|
| 21 |
algorithm: Literal["SpectralResidual", "FineGrained"] = "SpectralResidual",
|
| 22 |
):
|
| 23 |
+
"""
|
| 24 |
+
SaliencyMapオブジェクトを初期化します。
|
| 25 |
+
|
| 26 |
+
Parameters:
|
| 27 |
+
algorithm: 使用する顕著性マップアルゴリズムの種類。
|
| 28 |
+
有効なアルゴリズムについてはOpenCVのドキュメントを参照してください。
|
| 29 |
+
https://docs.opencv.org/4.9.0/d8/d65/group__saliency.html
|
| 30 |
+
|
| 31 |
+
"""
|
| 32 |
self.algorithm = algorithm
|
| 33 |
# OpenCVのsaliencyを作成します。
|
| 34 |
if algorithm == "SpectralResidual":
|
|
|
|
| 38 |
|
| 39 |
def compute(self, image: np.ndarray) -> Tuple[bool, Any]:
|
| 40 |
"""
|
| 41 |
+
入力画像から顕著性マップを計算します。
|
| 42 |
|
| 43 |
Parameters:
|
| 44 |
image: 入力画像
|
| 45 |
|
| 46 |
Returns:
|
| 47 |
+
Tuple[bool, Any]: 顕著性マップの計算結果。
|
| 48 |
+
bool値がTrueの場合は計算成功、Falseの場合は失敗。
|
| 49 |
+
顕著性マップのデータ。
|
| 50 |
"""
|
|
|
|
| 51 |
return self.saliency.computeSaliency(image)
|
| 52 |
|
| 53 |
|
|
|
|
| 57 |
colormap_name: Literal["jet", "hot", "turbo"] = "jet"
|
| 58 |
):
|
| 59 |
"""
|
| 60 |
+
入力画像と顕著性マップを合成し、指定されたカラーマップを適用します。
|
| 61 |
|
| 62 |
Parameters:
|
| 63 |
image: 入力画像
|
| 64 |
saliency_map: 顕著性マップ
|
| 65 |
colormap_name: カラーマップの種類
|
| 66 |
+
"jet": Jetカラーマップ
|
| 67 |
+
"hot": Hotカラーマップ
|
| 68 |
+
"turbo": Turboカラーマップ
|
| 69 |
|
| 70 |
Returns:
|
| 71 |
+
np.ndarray: 合成された画像 (RGBA形式)
|
| 72 |
"""
|
| 73 |
maps = {"jet": cv2.COLORMAP_JET, "hot": cv2.COLORMAP_HOT, "turbo": cv2.COLORMAP_TURBO}
|
| 74 |
+
|
| 75 |
+
# colormap_nameが有効かどうかをチェック
|
| 76 |
if colormap_name not in maps:
|
| 77 |
+
raise ValueError(f"Invalid colormap name: {colormap_name}")
|
| 78 |
|
| 79 |
# 顕著性マップをカラーマップに変換
|
| 80 |
saliency_map = (saliency_map * 255).astype("uint8")
|
src/utils.py
CHANGED
|
@@ -6,9 +6,9 @@ import time
|
|
| 6 |
|
| 7 |
def get_package_version() -> str:
|
| 8 |
"""
|
| 9 |
-
|
| 10 |
"""
|
| 11 |
-
return '0.0.
|
| 12 |
|
| 13 |
|
| 14 |
@dataclass
|
|
@@ -29,18 +29,20 @@ class Stopwatch:
|
|
| 29 |
@property
|
| 30 |
def elapsed(self) -> float:
|
| 31 |
"""
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
| 33 |
"""
|
| 34 |
if self._is_running:
|
| 35 |
-
|
| 36 |
-
self._elapsed = end_time - self._start_time
|
| 37 |
|
| 38 |
return self._elapsed
|
| 39 |
|
| 40 |
@property
|
| 41 |
def is_running(self) -> bool:
|
| 42 |
"""
|
| 43 |
-
|
| 44 |
"""
|
| 45 |
return self._is_running
|
| 46 |
|
|
@@ -53,9 +55,12 @@ class Stopwatch:
|
|
| 53 |
self._is_running = True
|
| 54 |
|
| 55 |
@classmethod
|
| 56 |
-
def start_new(cls):
|
| 57 |
"""
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
| 59 |
"""
|
| 60 |
stopwatch = Stopwatch()
|
| 61 |
stopwatch.start()
|
|
@@ -63,10 +68,12 @@ class Stopwatch:
|
|
| 63 |
|
| 64 |
def stop(self) -> float:
|
| 65 |
"""
|
| 66 |
-
|
|
|
|
|
|
|
|
|
|
| 67 |
"""
|
| 68 |
if self._is_running:
|
| 69 |
-
|
| 70 |
-
self._elapsed = end_time - self._start_time
|
| 71 |
self._is_running = False
|
| 72 |
return self._elapsed
|
|
|
|
| 6 |
|
| 7 |
def get_package_version() -> str:
|
| 8 |
"""
|
| 9 |
+
バージョン情報を取得します。
|
| 10 |
"""
|
| 11 |
+
return '0.0.8'
|
| 12 |
|
| 13 |
|
| 14 |
@dataclass
|
|
|
|
| 29 |
@property
|
| 30 |
def elapsed(self) -> float:
|
| 31 |
"""
|
| 32 |
+
計測中の経過時間を取得します。
|
| 33 |
+
|
| 34 |
+
Returns:
|
| 35 |
+
float: 計測中の経過時間(小数秒)
|
| 36 |
"""
|
| 37 |
if self._is_running:
|
| 38 |
+
self._elapsed = time.perf_counter() - self._start_time
|
|
|
|
| 39 |
|
| 40 |
return self._elapsed
|
| 41 |
|
| 42 |
@property
|
| 43 |
def is_running(self) -> bool:
|
| 44 |
"""
|
| 45 |
+
計測が実行中であるかどうかを取得します
|
| 46 |
"""
|
| 47 |
return self._is_running
|
| 48 |
|
|
|
|
| 55 |
self._is_running = True
|
| 56 |
|
| 57 |
@classmethod
|
| 58 |
+
def start_new(cls) -> 'Stopwatch':
|
| 59 |
"""
|
| 60 |
+
新しいストップウォッチを生成し、計測を開始します。
|
| 61 |
+
|
| 62 |
+
Returns:
|
| 63 |
+
Stopwatch: 新しいストップウォッチオブジェクト
|
| 64 |
"""
|
| 65 |
stopwatch = Stopwatch()
|
| 66 |
stopwatch.start()
|
|
|
|
| 68 |
|
| 69 |
def stop(self) -> float:
|
| 70 |
"""
|
| 71 |
+
計測を終了し、経過時間を返します。
|
| 72 |
+
|
| 73 |
+
Returns:
|
| 74 |
+
float: 計測中の経過時間
|
| 75 |
"""
|
| 76 |
if self._is_running:
|
| 77 |
+
self._elapsed = time.perf_counter() - self._start_time
|
|
|
|
| 78 |
self._is_running = False
|
| 79 |
return self._elapsed
|