PhotoEditer / app.py
naohiro701's picture
Update app.py
68e2e89 verified
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("画像をアップロードしてください。")