MakiAi commited on
Commit
2a6ac51
2 Parent(s): 9958a7c 65455b9

Merge branch 'develop'

Browse files
pic_to_header/app.py CHANGED
@@ -1,65 +1,111 @@
1
  import streamlit as st
2
- import os
3
- from core import process_header_image
 
 
 
4
 
5
  def main():
6
- st.markdown("""
7
-
8
- <div align="center">
9
-
10
- # Pic-to-Header
11
 
12
- ![Pic-to-Header Result](https://raw.githubusercontent.com/Sunwood-ai-labs/pic-to-header/refs/heads/main/assets/result.png)
13
-
14
- [![GitHub license](https://img.shields.io/github/license/Sunwood-ai-labs/pic-to-header)](https://github.com/Sunwood-ai-labs/pic-to-header/blob/main/LICENSE)
15
- [![GitHub stars](https://img.shields.io/github/stars/Sunwood-ai-labs/pic-to-header)](https://github.com/Sunwood-ai-labs/pic-to-header/stargazers)
16
- [![GitHub issues](https://img.shields.io/github/issues/Sunwood-ai-labs/pic-to-header)](https://github.com/Sunwood-ai-labs/pic-to-header/issues)
17
 
18
- ![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)
19
- ![Streamlit](https://img.shields.io/badge/Streamlit-FF4B4B?style=for-the-badge&logo=Streamlit&logoColor=white)
20
- ![OpenCV](https://img.shields.io/badge/opencv-%23white.svg?style=for-the-badge&logo=opencv&logoColor=white)
21
 
22
- </div>
 
 
23
 
24
- Pic-to-Headerは、マスク画像と入力画像を使用してヘッダー画像を生成するPythonアプリケーションです。
 
 
 
 
25
 
26
- """, unsafe_allow_html=True)
27
  st.write("マスク画像と入力画像をアップロードして、ヘッダー画像を生成します。")
28
 
29
- input_image = st.file_uploader("入力画像をアップロード", type=["png", "jpg", "jpeg"])
30
- mask_image = st.file_uploader("マスク画像をアップロード", type=["png", "jpg", "jpeg"])
31
-
32
- if input_image is not None and mask_image is not None:
33
- if st.button("ヘッダー画像を生成"):
34
- # 一時ファイルとして保存
35
- input_path = f"temp_input.{input_image.name.split('.')[-1]}"
36
- mask_path = f"temp_mask.{mask_image.name.split('.')[-1]}"
37
- output_path = "output_header.png"
38
-
39
- with open(input_path, "wb") as f:
40
- f.write(input_image.getbuffer())
41
- with open(mask_path, "wb") as f:
42
- f.write(mask_image.getbuffer())
43
-
44
- # 画像処理
45
- process_header_image(input_path, mask_path, output_path)
46
-
47
- # 結果を表示
48
- st.image(output_path, caption="生成されたヘッダー画像")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- # ダウンロードボタンを追加
51
- with open(output_path, "rb") as file:
52
- btn = st.download_button(
53
- label="ヘッダー画像をダウンロード",
54
- data=file,
55
- file_name="header_image.png",
56
- mime="image/png"
57
- )
58
-
59
- # 一時ファイルを削除
60
- os.remove(input_path)
61
- os.remove(mask_path)
62
- os.remove(output_path)
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  if __name__ == "__main__":
65
  main()
 
1
  import streamlit as st
2
+ from PIL import Image
3
+ import numpy as np
4
+ from modules.image_processor import process_image, prepare_image, convert_to_pil
5
+ from modules.mask_manager import MaskManager
6
+ from modules.utils import create_download_button
7
 
8
  def main():
9
+ # ワイドモードの設定
10
+ st.set_page_config(layout="wide")
 
 
 
11
 
12
+ st.markdown("""
13
+ <div align="center">
14
+
15
+ # Pic-to-Header
 
16
 
17
+ <img src="https://raw.githubusercontent.com/Sunwood-ai-labs/pic-to-header/refs/heads/main/assets/result.png" width="50%">
 
 
18
 
19
+ [![GitHub license](https://img.shields.io/github/license/Sunwood-ai-labs/pic-to-header)](https://github.com/Sunwood-ai-labs/pic-to-header/blob/main/LICENSE)
20
+ [![GitHub stars](https://img.shields.io/github/stars/Sunwood-ai-labs/pic-to-header)](https://github.com/Sunwood-ai-labs/pic-to-header/stargazers)
21
+ [![GitHub issues](https://img.shields.io/github/issues/Sunwood-ai-labs/pic-to-header)](https://github.com/Sunwood-ai-labs/pic-to-header/issues)
22
 
23
+ ![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)
24
+ ![Streamlit](https://img.shields.io/badge/Streamlit-FF4B4B?style=for-the-badge&logo=Streamlit&logoColor=white)
25
+ ![OpenCV](https://img.shields.io/badge/opencv-%23white.svg?style=for-the-badge&logo=opencv&logoColor=white)
26
+ </div>
27
+ """, unsafe_allow_html=True)
28
 
29
+ st.write("Pic-to-Headerは、マスク画像と入力画像を使用してヘッダー画像を生成するPythonアプリケーションです。")
30
  st.write("マスク画像と入力画像をアップロードして、ヘッダー画像を生成します。")
31
 
32
+ # 2段組レイアウトの作成(左側を狭く、右側を広く)
33
+ control_column, display_column = st.columns([1, 2])
34
+
35
+ with control_column:
36
+ # マスク管理インスタンスの作成
37
+ mask_manager = MaskManager()
38
+
39
+ # マスク画像の取得方法を選択
40
+ mask_source = st.radio(
41
+ "マスク画像の取得方法を選択",
42
+ ["プリセットから選択", "URLから取得", "ファイルをアップロード"]
43
+ )
44
+
45
+ mask_image = None
46
+ if mask_source == "プリセットから選択":
47
+ preset_name = st.selectbox(
48
+ "プリセットを選択",
49
+ mask_manager.get_preset_names()
50
+ )
51
+ mask_image = mask_manager.get_preset_mask(preset_name)
52
+
53
+ elif mask_source == "URLから取得":
54
+ mask_url = st.text_input("マスク画像のURLを入力")
55
+ if mask_url:
56
+ mask_image = mask_manager.load_mask_from_url(mask_url)
57
+
58
+ else: # ファイルをアップロード
59
+ mask_file = st.file_uploader("マスク画像をアップロード", type=["png", "jpg", "jpeg"])
60
+ if mask_file is not None:
61
+ mask_image = Image.open(mask_file)
62
+
63
+ # 透明度の調整
64
+ alpha = st.slider("マスクの透明度", 0.0, 1.0, 1.0)
65
+
66
+ # ファイルアップロード(複数ファイル対応)
67
+ uploaded_files = st.file_uploader(
68
+ "画像をアップロードしてください",
69
+ type=["png", "jpg", "jpeg", "webp"],
70
+ accept_multiple_files=True
71
+ )
72
+ if mask_image:
73
+ # マスク画像のプレビュー表示
74
+ st.write("選択中のマスク画像:")
75
+ st.image(mask_image, caption="マスク画像", use_column_width=True)
76
 
77
+ with display_column:
78
+
79
+ if uploaded_files:
80
+ st.write("処理結果:")
81
+ # 各画像の処理と表示
82
+ for idx, uploaded_file in enumerate(uploaded_files):
83
+ # 水平に2列で表示するための設定
84
+ img_col1, img_col2 = st.columns(2)
85
+
86
+ # 元画像の表示
87
+ with img_col1:
88
+ st.write(f"元画像 {idx + 1}:")
89
+ st.image(uploaded_file, use_column_width=True)
90
+
91
+ # 処理後の画像の表示
92
+ with img_col2:
93
+ st.write(f"処理後 {idx + 1}:")
94
+ input_image = prepare_image(uploaded_file)
95
+ if input_image is not None:
96
+ result = process_image(input_image, np.array(mask_image), alpha)
97
+ result_pil = convert_to_pil(result)
98
+ st.image(result_pil, use_column_width=True)
99
+
100
+ # ダウンロードボタンの作成
101
+ create_download_button(
102
+ result_pil,
103
+ f"header_{uploaded_file.name}"
104
+ )
105
+
106
+ # 区切り線を追加(最後の画像以外)
107
+ if idx < len(uploaded_files) - 1:
108
+ st.markdown("---")
109
+
110
  if __name__ == "__main__":
111
  main()
pic_to_header/modules/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+
pic_to_header/modules/image_processor.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from PIL import Image
4
+ import io
5
+
6
+ def process_image(image, mask_image, alpha=0.5):
7
+ """画像処理を行う関数"""
8
+ # PILイメージをnumpy配列に変換
9
+ if isinstance(image, Image.Image):
10
+ image = np.array(image)
11
+ if isinstance(mask_image, Image.Image):
12
+ mask_image = np.array(mask_image)
13
+
14
+ # 画像のチャンネル数を確認し、必要に応じて変換
15
+ if len(image.shape) == 2: # グレースケール
16
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGRA)
17
+ elif image.shape[2] == 3: # BGR
18
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
19
+ elif image.shape[2] == 4 and image.dtype == np.uint8:
20
+ pass # すでにBGRA形式
21
+
22
+ if len(mask_image.shape) == 2: # グレースケール
23
+ mask_image = cv2.cvtColor(mask_image, cv2.COLOR_GRAY2BGRA)
24
+ elif mask_image.shape[2] == 3: # BGR
25
+ mask_image = cv2.cvtColor(mask_image, cv2.COLOR_BGR2BGRA)
26
+ elif mask_image.shape[2] == 4 and mask_image.dtype == np.uint8:
27
+ pass # すでにBGRA形式
28
+
29
+ # マスク画像のリサイズ
30
+ mask_image = cv2.resize(mask_image, (image.shape[1], image.shape[0]))
31
+
32
+ # マスク画像のアルファチャンネルを取得
33
+ mask_alpha = mask_image[:, :, 3]
34
+
35
+ # 入力画像のアルファチャンネルを更新
36
+ # マスクのアルファ値を使って元の画像のアルファ値を減算
37
+ image[:, :, 3] = np.maximum(image[:, :, 3] - (mask_alpha * alpha).astype(np.uint8), 0)
38
+
39
+ return image
40
+
41
+ def convert_to_pil(image):
42
+ """numpy配列をPIL Imageに変換"""
43
+ if isinstance(image, np.ndarray):
44
+ return Image.fromarray(image)
45
+ return image
46
+
47
+ def prepare_image(uploaded_file):
48
+ """アップロードされたファイルを画像として準備"""
49
+ if uploaded_file is None:
50
+ return None
51
+
52
+ image_bytes = uploaded_file.read()
53
+ image = Image.open(io.BytesIO(image_bytes))
54
+
55
+ # RGBAモードに変換
56
+ if image.mode != 'RGBA':
57
+ image = image.convert('RGBA')
58
+
59
+ return image
pic_to_header/modules/mask_manager.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from PIL import Image
3
+ import io
4
+ import streamlit as st
5
+
6
+ class MaskManager:
7
+ def __init__(self):
8
+ self.default_mask_url = "https://raw.githubusercontent.com/Sunwood-ai-labs/pic-to-header/refs/heads/main/assets/mask.png"
9
+ self.presets = {
10
+ "デフォルトマスク": self.default_mask_url,
11
+ }
12
+
13
+ @staticmethod
14
+ @st.cache_data
15
+ def load_mask_from_url(url):
16
+ """URLから画像を読み込む"""
17
+ try:
18
+ response = requests.get(url)
19
+ response.raise_for_status()
20
+ image = Image.open(io.BytesIO(response.content))
21
+ return image
22
+ except Exception as e:
23
+ st.error(f"マスク画像の読み込みに失敗しました: {str(e)}")
24
+ return None
25
+
26
+ def get_preset_names(self):
27
+ """プリセット名の一覧を取得"""
28
+ return list(self.presets.keys())
29
+
30
+ def get_preset_mask(self, preset_name):
31
+ """プリセット名に対応するマスク画像を取得"""
32
+ if preset_name in self.presets:
33
+ return self.load_mask_from_url(self.presets[preset_name])
34
+ return None
35
+
36
+ def add_preset(self, name, url):
37
+ """新しいプリセットを追加"""
38
+ self.presets[name] = url
pic_to_header/modules/utils.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from PIL import Image
3
+ import io
4
+
5
+ def convert_image_to_bytes(image):
6
+ """画像をバイト列に変換"""
7
+ if image is None:
8
+ return None
9
+
10
+ img_byte_arr = io.BytesIO()
11
+ image.save(img_byte_arr, format='PNG')
12
+ return img_byte_arr.getvalue()
13
+
14
+ def create_download_button(image, filename):
15
+ """ダウンロードボタンを作成"""
16
+ if image is not None:
17
+ image_bytes = convert_image_to_bytes(image)
18
+ st.download_button(
19
+ label="画像をダウンロード",
20
+ data=image_bytes,
21
+ file_name=filename,
22
+ mime="image/png"
23
+ )