Spaces:
Runtime error
Runtime error
| 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) | |