うみゅ commited on
Commit
35fcf56
·
unverified ·
2 Parent(s): c3e9846 46c8ce2

Merge pull request #10 from umyuu/feature/0.0.8

Browse files
app.py CHANGED
@@ -2,7 +2,7 @@
2
  """
3
  SaliencyMapDemo
4
  """
5
- from argparse import ArgumentParser, BooleanOptionalAction
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
- 1. inputタブで画像を選択します。
127
- 2. Submitボタンを押します。
 
 
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=1)
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
- bool:
38
- true: SaliencyMap computed, false:NG
39
- np.ndarray: 顕著性マップ
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: 重ね合わせた画像(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")
 
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.7'
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
- end_time = time.perf_counter()
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
- end_time = time.perf_counter()
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