umyuu commited on
Commit
8ccf878
1 Parent(s): 232a35a
.vscode/extensions.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "recommendations": [
3
+ "ms-python.flake8"
4
+ ]
5
+ }
.vscode/settings.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cSpell.words": [
3
+ "astype",
4
+ "dtype",
5
+ "fromarray",
6
+ "Gradio",
7
+ "inbrowser",
8
+ "ndarray"
9
+ ],
10
+ "python.analysis.typeCheckingMode": "basic",
11
+ "flake8.args": [
12
+ "--ignore=E265,W291",
13
+ "--max-complexity=10",
14
+ "--max-line-length=127"
15
+ ]
16
+ }
app.py CHANGED
@@ -4,11 +4,12 @@
4
  """
5
  from argparse import ArgumentParser, BooleanOptionalAction
6
 
7
- import src.utils as utils
8
- from src.reporter import get_current_reporter
9
-
10
  PROGRAM_NAME = 'SaliencyMapDemo'
11
- __version__ = utils.get_package_version()
 
12
 
13
  def main():
14
  """
@@ -16,22 +17,20 @@ def main():
16
  1, コマンドライン引数の解析を行います
17
  2, アプリを起動します。
18
  """
19
- log = get_current_reporter()
20
- log.info("#アプリ起動中")
21
- watch = utils.Stopwatch.startNew()
22
-
23
- from src.myapp import runApp
24
-
25
  parser = ArgumentParser(prog=PROGRAM_NAME, description="SaliencyMapDemo")
26
- parser.add_argument('--server_port', type=int, default=7860, help="Gradio server port")
27
- parser.add_argument('--max_file_size', type=int, default=20 * 1024 * 1024, help="Gradio max file size")
28
- parser.add_argument('--inbrowser', action=BooleanOptionalAction, default=True, help="Gradio inbrowser")
29
- parser.add_argument('--share', action=BooleanOptionalAction, default=False, help="Gradio share")
 
 
 
 
30
  parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
31
-
32
  args = parser.parse_args()
33
-
34
- runApp(args, watch)
35
 
36
  if __name__ == "__main__":
37
  main()
 
4
  """
5
  from argparse import ArgumentParser, BooleanOptionalAction
6
 
7
+ from src.utils import get_package_version
8
+ from src.myapp import run_app
9
+
10
  PROGRAM_NAME = 'SaliencyMapDemo'
11
+ __version__ = get_package_version()
12
+
13
 
14
  def main():
15
  """
 
17
  1, コマンドライン引数の解析を行います
18
  2, アプリを起動します。
19
  """
 
 
 
 
 
 
20
  parser = ArgumentParser(prog=PROGRAM_NAME, description="SaliencyMapDemo")
21
+ parser.add_argument('--inbrowser',
22
+ action=BooleanOptionalAction, default=True, help="Gradio inbrowser")
23
+ parser.add_argument('--share',
24
+ action=BooleanOptionalAction, default=False, help="Gradio share")
25
+ parser.add_argument('--server_port',
26
+ type=int, default=7860, help="Gradio server port")
27
+ parser.add_argument('--max_file_size',
28
+ type=str, default="20MB", help="Gradio max file size")
29
  parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
30
+
31
  args = parser.parse_args()
32
+ run_app(args)
33
+
34
 
35
  if __name__ == "__main__":
36
  main()
src/myapp.py CHANGED
@@ -1,40 +1,54 @@
1
  # -*- coding: utf-8 -*-
 
2
  from typing import Literal
3
 
4
  import argparse
5
- from datetime import datetime
6
  import sys
7
 
8
  import gradio as gr
9
  import numpy as np
10
 
11
- import src.utils as utils
12
- from src.saliency import SaliencyMap, convertColorMap
13
  from src.reporter import get_current_reporter
14
 
15
  PROGRAM_NAME = 'SaliencyMapDemo'
16
- __version__ = utils.get_package_version()
17
  log = get_current_reporter()
18
-
19
- def jetTab_Selected(image: np.ndarray):
 
 
 
 
 
 
20
  #print(f"{datetime.now()}#jet")
21
  saliency = SaliencyMap("SpectralResidual")
22
- success, saliencyMap = saliency.computeSaliency(image)
23
- retval = convertColorMap(image, saliencyMap, "jet")
 
 
24
  #print(f"{datetime.now()}#jet")
25
-
26
  return retval
27
-
28
- def hotTab_Selected(image: np.ndarray):
 
 
 
 
29
  #print(f"{datetime.now()}#hot")
30
  saliency = SaliencyMap("SpectralResidual")
31
- success, saliencyMap = saliency.computeSaliency(image)
32
- retval = convertColorMap(image, saliencyMap, "turbo")
 
 
33
  #print(f"{datetime.now()}#hot")
34
-
35
  return retval
36
 
37
- def submit_Clicked(image: np.ndarray, algorithm: str):
 
38
  """
39
  入力画像を元に顕著マップを計算します。
40
 
@@ -46,27 +60,26 @@ def submit_Clicked(image: np.ndarray, algorithm: str):
46
  np.ndarray: HOT画像
47
  """
48
  #log.info(f"#submit_Clicked")
49
- watch = utils.Stopwatch.startNew()
50
-
51
  saliency = SaliencyMap(algorithm)
52
- success, saliencyMap = saliency.computeSaliency(image)
53
- #log.info(f"#SaliencyMap computeSaliency()")
54
 
55
  if not success:
56
- return image, image # エラーが発生した場合は入力画像を返します。
57
-
58
- #log.info(f"#jet")
59
- jet = convertColorMap(image, saliencyMap, "jet")
60
- #jet = None
61
- #log.info(f"#hot")
62
- hot = convertColorMap(image, saliencyMap, "hot")
63
-
64
  saliency = None
65
  #log.info(f"#submit_Clicked End{watch.stop():.3f}")
66
-
67
  return jet, hot
68
 
69
- def runApp(args: argparse.Namespace, watch: utils.Stopwatch) -> None:
 
70
  """
71
  アプリの画面を作成し、Gradioサービスを起動します。
72
 
@@ -76,59 +89,68 @@ def runApp(args: argparse.Namespace, watch: utils.Stopwatch) -> None:
76
  """
77
  # analytics_enabled=False
78
  # https://github.com/gradio-app/gradio/issues/4226
79
- with gr.Blocks(analytics_enabled=False, \
80
- title=f"{PROGRAM_NAME} {__version__}", \
 
81
  head="""
82
  <meta name="format-detection" content="telephone=no">
83
  <meta name="robots" content="noindex, nofollow, noarchive">
84
  <meta name="referrer" content="no-referrer" />
85
- """) as demo:
86
-
87
- gr.Markdown(
88
  """
89
- # Saliency Map demo.
90
- """)
 
 
 
91
  with gr.Accordion("取り扱い説明書", open=False):
92
- gr.Markdown(
93
- """
94
- 1. inputタブで画像を選択します。
95
- 2. Submitボタンを押します。
96
- ※画像は外部送信していません。ローカルで処理が完結します。
97
- 3. 結果は、JETタブとHOTタブに表示します。
98
  """)
99
-
100
- algorithmType = gr.Radio(["SpectralResidual", "FineGrained"], label="Saliency", value="SpectralResidual", interactive=True)
 
 
 
 
101
 
102
  submit_button = gr.Button("submit", variant="primary")
103
 
104
  with gr.Row():
105
  with gr.Tab("input", id="input"):
106
 
107
- image_input = gr.Image(sources = ["upload", "clipboard"], interactive=True)
 
108
  with gr.Tab("overlay(JET)"):
109
  image_overlay_jet = gr.Image(interactive=False)
110
- #tab_jet.select(jetTab_Selected, inputs=[image_input], outputs=image_overlay_jet)
111
  with gr.Tab("overlay(HOT)"):
112
  image_overlay_hot = gr.Image(interactive=False)
113
- #tab_hot.select(hotTab_Selected, inputs=[image_input], outputs=image_overlay_hot, api_name=False)
114
-
115
- submit_button.click(submit_Clicked, inputs=[image_input, algorithmType], outputs=[image_overlay_jet, image_overlay_hot])
116
-
117
- gr.Markdown(
118
- f"""
119
- Python {sys.version}
120
- App {__version__}
 
 
 
 
 
 
121
  """)
122
-
123
  demo.queue(default_concurrency_limit=5)
124
-
125
  log.info(f"#アプリ起動完了({watch.stop():.3f}s)")
126
  # https://www.gradio.app/docs/gradio/blocks#blocks-launch
127
-
128
  demo.launch(
129
- max_file_size=args.max_file_size,
130
- server_port=args.server_port,
131
  inbrowser=args.inbrowser,
132
  share=args.share,
 
 
133
  )
134
-
 
1
  # -*- coding: utf-8 -*-
2
+ """myapp Widget"""
3
  from typing import Literal
4
 
5
  import argparse
6
+ #from datetime import datetime
7
  import sys
8
 
9
  import gradio as gr
10
  import numpy as np
11
 
12
+ from src.utils import Stopwatch, get_package_version
13
+ from src.saliency import SaliencyMap, convert_colormap
14
  from src.reporter import get_current_reporter
15
 
16
  PROGRAM_NAME = 'SaliencyMapDemo'
17
+ __version__ = get_package_version()
18
  log = get_current_reporter()
19
+ log.info("#アプリ起動中")
20
+ watch = Stopwatch.start_new()
21
+
22
+
23
+ def jettab_selected(image: np.ndarray):
24
+ """
25
+ JETタブを選択時
26
+ """
27
  #print(f"{datetime.now()}#jet")
28
  saliency = SaliencyMap("SpectralResidual")
29
+ success, saliency_map = saliency.compute(image)
30
+ if not success:
31
+ return image # エラーが発生した場合は入力画像を返します。
32
+ retval = convert_colormap(image, saliency_map, "jet")
33
  #print(f"{datetime.now()}#jet")
 
34
  return retval
35
+
36
+
37
+ def hottab_selected(image: np.ndarray):
38
+ """
39
+ HOTタブを選択時
40
+ """
41
  #print(f"{datetime.now()}#hot")
42
  saliency = SaliencyMap("SpectralResidual")
43
+ success, saliency_map = saliency.compute(image)
44
+ if not success:
45
+ return image # エラーが発生した場合は入力画像を返します。
46
+ retval = convert_colormap(image, saliency_map, "turbo")
47
  #print(f"{datetime.now()}#hot")
 
48
  return retval
49
 
50
+
51
+ def submit_clicked(image: np.ndarray, algorithm: Literal["SpectralResidual", "FineGrained"]):
52
  """
53
  入力画像を元に顕著マップを計算します。
54
 
 
60
  np.ndarray: HOT画像
61
  """
62
  #log.info(f"#submit_Clicked")
63
+ #watch = utils.Stopwatch.startNew()
64
+ #
65
  saliency = SaliencyMap(algorithm)
66
+ success, saliency_map = saliency.compute(image)
67
+ # log.info(f"#SaliencyMap compute()")
68
 
69
  if not success:
70
+ return image, image # エラーが発生した場合は入力画像を返します。
71
+
72
+ # log.info(f"#jet")
73
+ jet = convert_colormap(image, saliency_map, "jet")
74
+ # jet = None
75
+ # log.info(f"#hot")
76
+ hot = convert_colormap(image, saliency_map, "hot")
 
77
  saliency = None
78
  #log.info(f"#submit_Clicked End{watch.stop():.3f}")
 
79
  return jet, hot
80
 
81
+
82
+ def run_app(args: argparse.Namespace) -> None:
83
  """
84
  アプリの画面を作成し、Gradioサービスを起動します。
85
 
 
89
  """
90
  # analytics_enabled=False
91
  # https://github.com/gradio-app/gradio/issues/4226
92
+ with gr.Blocks(
93
+ analytics_enabled=False,
94
+ title=f"{PROGRAM_NAME} {__version__}",
95
  head="""
96
  <meta name="format-detection" content="telephone=no">
97
  <meta name="robots" content="noindex, nofollow, noarchive">
98
  <meta name="referrer" content="no-referrer" />
 
 
 
99
  """
100
+ ) as demo:
101
+
102
+ gr.Markdown("""
103
+ # Saliency Map demo.
104
+ """)
105
  with gr.Accordion("取り扱い説明書", open=False):
106
+ gr.Markdown("""
107
+ 1. inputタブで画像を選択します。
108
+ 2. Submitボタンを押します。
109
+ ※画像は外部送信していません。ローカルで処理が完結します。
110
+ 3. 結果は、JETタブとHOTタブに表示します。
 
111
  """)
112
+ algorithm_type = gr.Radio(
113
+ ["SpectralResidual", "FineGrained"],
114
+ label="Saliency",
115
+ value="SpectralResidual",
116
+ interactive=True
117
+ )
118
 
119
  submit_button = gr.Button("submit", variant="primary")
120
 
121
  with gr.Row():
122
  with gr.Tab("input", id="input"):
123
 
124
+ image_input = gr.Image(sources=["upload", "clipboard"],
125
+ interactive=True)
126
  with gr.Tab("overlay(JET)"):
127
  image_overlay_jet = gr.Image(interactive=False)
128
+ # tab_jet.select(jetTab_Selected, inputs=[image_input], outputs=image_overlay_jet)
129
  with gr.Tab("overlay(HOT)"):
130
  image_overlay_hot = gr.Image(interactive=False)
131
+ # tab_hot.select(hotTab_Selected,
132
+ # inputs=[image_input],
133
+ # outputs=image_overlay_hot, api_name=False)
134
+ #
135
+ submit_button.click(
136
+ submit_clicked,
137
+ inputs=[image_input, algorithm_type],
138
+ outputs=[image_overlay_jet,
139
+ image_overlay_hot]
140
+ )
141
+
142
+ gr.Markdown(f"""
143
+ Python {sys.version}
144
+ App {__version__}
145
  """)
146
+
147
  demo.queue(default_concurrency_limit=5)
148
+
149
  log.info(f"#アプリ起動完了({watch.stop():.3f}s)")
150
  # https://www.gradio.app/docs/gradio/blocks#blocks-launch
 
151
  demo.launch(
 
 
152
  inbrowser=args.inbrowser,
153
  share=args.share,
154
+ server_port=args.server_port,
155
+ max_file_size=args.max_file_size,
156
  )
 
src/reporter.py CHANGED
@@ -4,7 +4,7 @@
4
  ログハンドラーが重複登録されるのを防ぐために1箇所で生成してログハンドラーを返します。
5
  Example:
6
  from reporter import get_current_reporter
7
-
8
  logger = get_current_reporter()
9
  logger.info("message");
10
  """
@@ -13,37 +13,45 @@ from logging import DEBUG
13
 
14
  _reporters = []
15
 
 
16
  def get_current_reporter() -> Logger:
 
 
 
17
  return _reporters[-1]
18
 
19
- def __make_reporter(name: str='SaliencyMapDemo') -> None:
 
20
  """
21
  ログハンドラーを生成します。
22
- @see https://docs.python.jp/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes
23
-
24
  Parameters:
25
  name: アプリ名
26
  """
27
- handler = StreamHandler() # コンソールに出力します。
28
  formatter = Formatter('%(asctime)s%(message)s')
29
  handler.setFormatter(formatter)
30
  handler.setLevel(DEBUG)
31
-
32
  logger = getLogger(name)
33
  logger.setLevel(DEBUG)
34
  logger.addHandler(handler)
35
  _reporters.append(logger)
36
 
 
37
  __make_reporter()
38
 
 
39
  def main():
40
  """
41
  Entry Point
42
  """
43
  assert len(_reporters) == 1
44
-
45
  logger = get_current_reporter()
46
  logger.debug("main")
47
 
 
48
  if __name__ == "__main__":
49
  main()
 
4
  ログハンドラーが重複登録されるのを防ぐために1箇所で生成してログハンドラーを返します。
5
  Example:
6
  from reporter import get_current_reporter
7
+
8
  logger = get_current_reporter()
9
  logger.info("message");
10
  """
 
13
 
14
  _reporters = []
15
 
16
+
17
  def get_current_reporter() -> Logger:
18
+ """
19
+ シングルトン
20
+ """
21
  return _reporters[-1]
22
 
23
+
24
+ def __make_reporter(name: str = 'SaliencyMapDemo') -> None:
25
  """
26
  ログハンドラーを生成します。
27
+ @see https://docs.python.jp/3/howto/logging-cookbook.html
28
+
29
  Parameters:
30
  name: アプリ名
31
  """
32
+ handler = StreamHandler() # コンソールに出力します。
33
  formatter = Formatter('%(asctime)s%(message)s')
34
  handler.setFormatter(formatter)
35
  handler.setLevel(DEBUG)
36
+
37
  logger = getLogger(name)
38
  logger.setLevel(DEBUG)
39
  logger.addHandler(handler)
40
  _reporters.append(logger)
41
 
42
+
43
  __make_reporter()
44
 
45
+
46
  def main():
47
  """
48
  Entry Point
49
  """
50
  assert len(_reporters) == 1
51
+
52
  logger = get_current_reporter()
53
  logger.debug("main")
54
 
55
+
56
  if __name__ == "__main__":
57
  main()
src/saliency.py CHANGED
@@ -1,5 +1,6 @@
1
  # -*- coding: utf-8 -*-
2
- from typing import Literal
 
3
 
4
  import numpy as np
5
  import cv2
@@ -10,23 +11,22 @@ class SaliencyMap:
10
  SaliencyMap 顕著性マップを計算するクラスです。
11
  Example:
12
  from lib.saliency import SaliencyMap
13
-
14
  saliency = SaliencyMap("SpectralResidual")
15
- success, saliencyMap = saliency.computeSaliency(image)
16
  """
17
  def __init__(
18
  self,
19
  algorithm: Literal["SpectralResidual", "FineGrained"] = "SpectralResidual",
20
- ):
21
  self.algorithm = algorithm
22
  # OpenCVのsaliencyを作成します。
23
  if algorithm == "SpectralResidual":
24
  self.saliency = cv2.saliency.StaticSaliencySpectralResidual_create()
25
  else:
26
  self.saliency = cv2.saliency.StaticSaliencyFineGrained_create()
27
-
28
 
29
- def computeSaliency(self, image: np.ndarray):
30
  """
31
  入力画像から顕著性マップを作成します。
32
 
@@ -41,31 +41,32 @@ class SaliencyMap:
41
  # 画像の顕著性を計算します。
42
  return self.saliency.computeSaliency(image)
43
 
44
- def convertColorMap(
 
45
  image: np.ndarray,
46
- saliencyMap: np.ndarray,
47
- colormap_name: Literal["jet", "hot", "turbo"] = "jet"):
 
48
  """
49
  顕著性マップをカラーマップに変換後に、入力画像に重ね合わせします。
50
-
51
  Parameters:
52
  image: 入力画像
53
- saliencyMap: 顕著性マップ
54
  colormap_name: カラーマップの種類
55
 
56
  Returns:
57
  np.ndarray: 重ね合わせた画像(RGBA形式)
58
  """
59
- colormaps = {"jet": cv2.COLORMAP_JET, "hot": cv2.COLORMAP_HOT, "turbo": cv2.COLORMAP_TURBO}
60
- if colormap_name not in colormaps:
61
- raise ValueError(colormap_name)
62
 
63
  # 顕著性マップをカラーマップに変換
64
- saliencyMap = (saliencyMap * 255).astype("uint8")
65
- saliencyMap = cv2.applyColorMap(saliencyMap, colormaps[colormap_name])
66
  #return saliencyMap
67
  # 入力画像とカラーマップを重ね合わせ
68
- overlay = cv2.addWeighted(image, 0.5, saliencyMap, 0.5, 0)
69
  #return overlay
70
-
71
  return cv2.cvtColor(overlay, cv2.COLOR_BGR2RGBA)
 
1
  # -*- coding: utf-8 -*-
2
+ """SaliencyMapを計算する"""
3
+ from typing import Any, Tuple, Literal
4
 
5
  import numpy as np
6
  import cv2
 
11
  SaliencyMap 顕著性マップを計算するクラスです。
12
  Example:
13
  from lib.saliency import SaliencyMap
14
+
15
  saliency = SaliencyMap("SpectralResidual")
16
+ success, saliencyMap = saliency.compute(image)
17
  """
18
  def __init__(
19
  self,
20
  algorithm: Literal["SpectralResidual", "FineGrained"] = "SpectralResidual",
21
+ ):
22
  self.algorithm = algorithm
23
  # OpenCVのsaliencyを作成します。
24
  if algorithm == "SpectralResidual":
25
  self.saliency = cv2.saliency.StaticSaliencySpectralResidual_create()
26
  else:
27
  self.saliency = cv2.saliency.StaticSaliencyFineGrained_create()
 
28
 
29
+ def compute(self, image: np.ndarray) -> Tuple[bool, Any]:
30
  """
31
  入力画像から顕著性マップを作成します。
32
 
 
41
  # 画像の顕著性を計算します。
42
  return self.saliency.computeSaliency(image)
43
 
44
+
45
+ def convert_colormap(
46
  image: np.ndarray,
47
+ saliency_map: np.ndarray,
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: 重ね合わせた画像(RGBA形式)
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")
67
+ saliency_map = cv2.applyColorMap(saliency_map, maps[colormap_name])
68
  #return saliencyMap
69
  # 入力画像とカラーマップを重ね合わせ
70
+ overlay = cv2.addWeighted(image, 0.5, saliency_map, 0.5, 0)
71
  #return overlay
 
72
  return cv2.cvtColor(overlay, cv2.COLOR_BGR2RGBA)
src/utils.py CHANGED
@@ -1,39 +1,57 @@
1
  # -*- coding: utf-8 -*-
 
2
  import time
3
 
 
4
  def get_package_version() -> str:
 
 
 
5
  return '0.0.4'
6
 
 
7
  class Stopwatch:
8
  """
9
  Stopwatch 経過時間を計測するためのクラスです。
10
  Example:
11
- from utils import Stopwatch
12
-
13
- watch = Stopwatch.startNew()
14
  # 計測する処理
15
  print(f"{watch.stop():.3f}")
16
  """
17
 
18
  def __init__(self):
19
- self._start_time = None
20
- self._elapsed = 0;
21
 
22
  @property
23
- def Elapsed(self):
 
 
 
24
  return self._elapsed
25
 
26
  def start(self) -> None:
 
 
 
27
  self._start_time = time.perf_counter()
28
- self._elapsed = 0;
29
 
30
  @classmethod
31
- def startNew(cls):
 
 
 
32
  stopwatch = Stopwatch()
33
  stopwatch.start()
34
  return stopwatch
35
 
36
  def stop(self):
 
 
 
37
  end_time = time.perf_counter()
38
  self._elapsed = end_time - self._start_time
39
  return self._elapsed
 
1
  # -*- coding: utf-8 -*-
2
+ """ユーティリティ"""
3
  import time
4
 
5
+
6
  def get_package_version() -> str:
7
+ """
8
+ バージョン情報
9
+ """
10
  return '0.0.4'
11
 
12
+
13
  class Stopwatch:
14
  """
15
  Stopwatch 経過時間を計測するためのクラスです。
16
  Example:
17
+ from src.utils import Stopwatch
18
+
19
+ watch = Stopwatch.start_new()
20
  # 計測する処理
21
  print(f"{watch.stop():.3f}")
22
  """
23
 
24
  def __init__(self):
25
+ self._start_time = 0
26
+ self._elapsed = 0
27
 
28
  @property
29
+ def elapsed(self):
30
+ """
31
+ 経過時間
32
+ """
33
  return self._elapsed
34
 
35
  def start(self) -> None:
36
+ """
37
+ 計測を開始します。
38
+ """
39
  self._start_time = time.perf_counter()
40
+ self._elapsed = 0
41
 
42
  @classmethod
43
+ def start_new(cls):
44
+ """
45
+ ストップウォッチを生成し計測を開始します。
46
+ """
47
  stopwatch = Stopwatch()
48
  stopwatch.start()
49
  return stopwatch
50
 
51
  def stop(self):
52
+ """
53
+ 計測を終了します。
54
+ """
55
  end_time = time.perf_counter()
56
  self._elapsed = end_time - self._start_time
57
  return self._elapsed
test/test_create_grayscale_gradation.py CHANGED
@@ -1,14 +1,21 @@
1
  # -*- coding: utf-8 -*-
 
2
  import unittest
3
 
4
  import numpy as np
5
  from PIL import Image
6
 
 
7
  class TestGrayscale(unittest.TestCase):
8
- pass
 
 
9
 
10
 
11
  def main():
 
 
 
12
  # グラデーションのサイズを定義します
13
  width = 256
14
  height = 256
@@ -25,6 +32,7 @@ def main():
25
  # 画像を表示します
26
  image.show()
27
 
 
28
  if __name__ == "__main__":
29
  #main()
30
  unittest.main()
 
1
  # -*- coding: utf-8 -*-
2
+ """単体テストモジュール"""
3
  import unittest
4
 
5
  import numpy as np
6
  from PIL import Image
7
 
8
+
9
  class TestGrayscale(unittest.TestCase):
10
+ """
11
+ グレースケールのテスト
12
+ """
13
 
14
 
15
  def main():
16
+ """
17
+ 単体テストのエントリーポイント
18
+ """
19
  # グラデーションのサイズを定義します
20
  width = 256
21
  height = 256
 
32
  # 画像を表示します
33
  image.show()
34
 
35
+
36
  if __name__ == "__main__":
37
  #main()
38
  unittest.main()