from PIL import Image, ImageDraw, ImageFont, ExifTags, ImageEnhance import textwrap import io import os import streamlit as st import cv2 import numpy as np import rawpy import matplotlib.pyplot as plt # 画像のアップロード uploaded_file = st.file_uploader("画像をアップロードしてください", type=["jpg", "jpeg", "png", "nef", "cr2", "dng"]) if uploaded_file is not None: # RAWファイルの処理 if uploaded_file.name.lower().endswith(('.nef', '.cr2', '.dng')): try: raw = rawpy.imread(uploaded_file) rgb = raw.postprocess() image = Image.fromarray(rgb) except Exception as e: st.error(f"RAWファイルの読み込み中にエラーが発生しました: {e}") st.stop() else: image = Image.open(uploaded_file) # 背景除去の処理 def remove_background(image): img_np = np.array(image) img_rgb = cv2.cvtColor(img_np, cv2.COLOR_RGBA2RGB) if img_np.shape[-1] == 4 else img_np mask = np.zeros(img_rgb.shape[:2], dtype=np.uint8) # GrabCutアルゴリズムによる背景除去 rect = (10, 10, img_rgb.shape[1] - 10, img_rgb.shape[0] - 10) bgd_model = np.zeros((1, 65), np.float64) fgd_model = np.zeros((1, 65), np.float64) cv2.grabCut(img_rgb, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT) # 0と2は背景、1と3は前景 mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8') img_np = img_rgb * mask2[:, :, np.newaxis] return Image.fromarray(img_np) # 背景除去を適用 if st.sidebar.checkbox("背景除去を適用"): image = remove_background(image) # Exif情報を取得する関数 def get_exif_data(image): exif_data = {} try: exif_raw = image._getexif() if exif_raw: for tag, value in exif_raw.items(): decoded_tag = ExifTags.TAGS.get(tag, tag) exif_data[decoded_tag] = value except AttributeError: pass return exif_data # Exif情報をフォーマットする関数 def format_exif_data(exif_data): display_data = [] important_tags = ['DateTimeOriginal', 'Model', 'FNumber', 'ExposureTime', 'ISOSpeedRatings', 'FocalLength'] for tag in important_tags: value = exif_data.get(tag, None) if value: if tag == 'FNumber': value = f"F/{value}" elif tag == 'ExposureTime': value = f"{value} sec" elif tag == 'FocalLength': value = f"{value} mm" display_data.append(f"{tag}: {value}") return '\n'.join(display_data) # Exif情報の取得とフォーマット exif_data = get_exif_data(image) exif_text = format_exif_data(exif_data) # 外枠の追加 border_size = 100 # 外枠のサイズを指定 img_width, img_height = image.size new_width = img_width + border_size * 2 new_height = img_height + border_size * 2 + 100 # 下部にExif情報分のスペースを追加 new_image = Image.new("RGB", (new_width, new_height), "white") new_image.paste(image, (border_size, border_size)) # Exif情報を描画 draw = ImageDraw.Draw(new_image) try: font = ImageFont.truetype("arial.ttf", int(border_size / 5)) except IOError: font = ImageFont.load_default() text_color = (0, 0, 0) text_margin = 10 text_position = (border_size + text_margin, img_height + border_size + text_margin) wrapped_text = textwrap.fill(exif_text, width=80) # 枠内にExif情報をおしゃれに描画(中央寄せ) lines = wrapped_text.split('\n') y_text = text_position[1] for line in lines: line_width, line_height = draw.textbbox((0, 0), line, font=font)[2:4] x_text = (new_width - line_width) / 2 # 中央に揃える draw.text((x_text, y_text), line, font=font, fill=text_color) y_text += line_height # カラーカーブ調整 st.sidebar.header("カラーカーブ調整") r_curve = st.sidebar.slider("Red Curve", 0, 255, (0, 255), step=1) g_curve = st.sidebar.slider("Green Curve", 0, 255, (0, 255), step=1) b_curve = st.sidebar.slider("Blue Curve", 0, 255, (0, 255), step=1) img_np = np.array(new_image) for i, curve in enumerate([r_curve, g_curve, b_curve]): lut = np.interp(np.arange(256), [curve[0], curve[1]], [0, 255]) img_np[:, :, i] = cv2.LUT(img_np[:, :, i], lut.astype('uint8')) new_image = Image.fromarray(img_np) # ノイズリダクション if st.sidebar.checkbox("ノイズリダクションを適用"): img_np = cv2.fastNlMeansDenoisingColored(np.array(new_image), None, 10, 10, 7, 21) new_image = Image.fromarray(img_np) # シャープネス調整 sharpness_factor = st.sidebar.slider("シャープネス調整", 0.0, 3.0, 1.0, 0.1) enhancer = ImageEnhance.Sharpness(new_image) new_image = enhancer.enhance(sharpness_factor) # レンズ補正 if st.sidebar.checkbox("レンズ補正を適用"): # 仮のレンズ補正(簡単な周辺減光の軽減) img_np = np.array(new_image) rows, cols = img_np.shape[:2] mask = np.zeros((rows, cols), dtype=np.float32) cv2.circle(mask, (cols // 2, rows // 2), min(rows, cols) // 2, 1, thickness=-1) img_np = cv2.addWeighted(img_np, 0.9, img_np * mask[:, :, None], 0.1, 0) new_image = Image.fromarray(img_np.astype('uint8')) # ホワイトバランスの詳細調整 st.sidebar.header("ホワイトバランス調整") temperature = st.sidebar.slider("色温度", 2000, 10000, 5500) tint = st.sidebar.slider("色被り補正", -100, 100, 0) # 簡単なホワイトバランス調整処理 img_np = np.array(new_image, dtype=np.float32) img_np[:, :, 0] *= (temperature / 5500) # 青チャンネルに対する温度補正 img_np[:, :, 2] *= (1 + tint / 100) # 赤チャンネルに対する色被り補正 img_np = np.clip(img_np, 0, 255).astype('uint8') new_image = Image.fromarray(img_np) # HSL調整 st.sidebar.header("HSL調整") hue = st.sidebar.slider("色相調整", -180, 180, 0) saturation = st.sidebar.slider("彩度調整", 0.0, 3.0, 1.0, 0.1) luminance = st.sidebar.slider("輝度調整", 0.0, 3.0, 1.0, 0.1) hsv = cv2.cvtColor(np.array(new_image), cv2.COLOR_RGB2HSV) hsv[:, :, 0] = (hsv[:, :, 0].astype(int) + hue) % 180 hsv[:, :, 1] = np.clip(hsv[:, :, 1] * saturation, 0, 255).astype('uint8') hsv[:, :, 2] = np.clip(hsv[:, :, 2] * luminance, 0, 255).astype('uint8') new_image = Image.fromarray(cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)) # プリセットの適用 if st.sidebar.checkbox("プリセットを適用"): # 仮のプリセット適用 lut = np.linspace(0, 255, 256)**0.8 # ガンマ補正によるLUT適用の例 img_np = np.array(new_image) for i in range(3): img_np[:, :, i] = cv2.LUT(img_np[:, :, i], lut.astype('uint8')) new_image = Image.fromarray(img_np) # ヒストグラムの表示 st.sidebar.header("ヒストグラム表示") if st.sidebar.checkbox("ヒストグラムを表示"): fig, ax = plt.subplots() img_np = np.array(new_image) if img_np.ndim == 2: # グレースケール ax.hist(img_np.ravel(), bins=256, color='black') else: colors = ('r', 'g', 'b') for i, color in enumerate(colors): hist, bins = np.histogram(img_np[:, :, i], bins=256, range=(0, 256)) ax.plot(hist, color=color) st.pyplot(fig) # 編集後の画像を保存 output_buffer = io.BytesIO() new_image.save(output_buffer, format='JPEG') output_buffer.seek(0) # 結果の画像の表示 st.image(new_image, caption='編集後の画像', use_column_width=True) st.download_button(label="編集した画像をダウンロード", data=output_buffer, file_name="edited_image_with_border_and_exif.jpg", mime="image/jpeg") else: st.warning("画像をアップロードしてください。")