sukiboo's picture
Sync from GitHub 2940a179ade5f5332404a182f9d3825fa3774154
bef4f83 verified
Raw
History Blame Contribute Delete
3.65 kB
import streamlit as st
from PIL import Image
from src import cropper_overlay, download_button, state
from src.constants import (
DEFAULT_IMAGE,
MIN_SELECTION,
PREVIEW_HEIGHT,
ROTATION_MAX,
ROTATION_MIN,
ROTATION_STEP,
)
from src.mirror import mirror_pair
from src.styles import STYLES
from src.widgets import inline_label, section
def main():
st.set_page_config(page_title="Image Rotate & Mirror", layout="wide")
st.markdown(STYLES, unsafe_allow_html=True)
st.title("Image Rotate & Mirror")
top_left, top_right = st.columns(2)
mid_left, mid_right = st.columns(2)
sel_container = mid_left.container()
section(mid_right, "Upload / Download image", "ud")
uploaded = mid_right.file_uploader(
"Upload image",
type=["png", "jpg", "jpeg", "webp"],
label_visibility="collapsed",
)
if uploaded is not None:
upload_key = getattr(uploaded, "file_id", uploaded.name)
upload_name = uploaded.name
else:
upload_key = DEFAULT_IMAGE
upload_name = DEFAULT_IMAGE.rsplit("/", 1)[-1]
img = state.load_image(uploaded, upload_key)
rotation = state.get_rotation()
full_rotated = state.cache_rotation(img, upload_key, rotation)
max_size = min(full_rotated.width, full_rotated.height)
max_size -= max_size % 2
min_size = min(MIN_SELECTION, max(2, max_size))
geom = cropper_overlay.geometry(full_rotated)
scale_x = st.session_state.setdefault("_fabric_scale_x", 1.0)
cropper_key = f"_cropper_{upload_key}"
scale_x = state.apply_cropper_changes(
cropper_key, scale_x, geom.display_scale, geom.offset_x, geom.offset_y
)
size, left, top = state.normalize_box(full_rotated, min_size, max_size)
st.session_state["_box_size_in"] = size
st.session_state["_box_x_in"] = left
st.session_state["_box_y_in"] = top
section(sel_container, "Selection Area", "sel")
inline_label(sel_container, "Size").number_input(
"Size",
min_value=min_size,
max_value=max_size,
step=2,
key="_box_size_in",
on_change=state.sync_size,
label_visibility="collapsed",
)
inline_label(sel_container, "X").number_input(
"X",
min_value=0,
max_value=full_rotated.width - size,
step=1,
key="_box_x_in",
on_change=state.sync_x,
label_visibility="collapsed",
)
inline_label(sel_container, "Y").number_input(
"Y",
min_value=0,
max_value=full_rotated.height - size,
step=1,
key="_box_y_in",
on_change=state.sync_y,
label_visibility="collapsed",
)
inline_label(sel_container, "Rotation").number_input(
"Rotation",
min_value=ROTATION_MIN,
max_value=ROTATION_MAX,
step=ROTATION_STEP,
key="_rotation_in",
label_visibility="collapsed",
)
cropper_overlay.render(top_left, full_rotated, geom, size, left, top, scale_x, cropper_key)
region = full_rotated.crop((left, top, left + size, top + size))
left_out, right_out = mirror_pair(region)
preview_left = left_out.resize(
(max(1, round(left_out.width * PREVIEW_HEIGHT / left_out.height)), PREVIEW_HEIGHT),
Image.Resampling.LANCZOS,
)
preview_right = right_out.resize(
(max(1, round(right_out.width * PREVIEW_HEIGHT / right_out.height)), PREVIEW_HEIGHT),
Image.Resampling.LANCZOS,
)
top_right.image(preview_left, width="stretch")
top_right.image(preview_right, width="stretch")
download_button.render(mid_right, left_out, right_out, upload_name)