Spaces:
Sleeping
Sleeping
| 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("画像をアップロードしてください。") |