| from __future__ import annotations |
|
|
| from typing import Literal |
|
|
| import gradio as gr |
| from PIL import Image |
|
|
| RotationMode = Literal["Clockwise 90", "Counterclockwise 90", "Custom angle"] |
|
|
|
|
| def rotate_image( |
| image: Image.Image | None, |
| rotation_mode: RotationMode, |
| custom_angle: float | None, |
| ) -> Image.Image | None: |
| if image is None: |
| return None |
|
|
| if rotation_mode == "Clockwise 90": |
| angle = -90 |
| elif rotation_mode == "Counterclockwise 90": |
| angle = 90 |
| else: |
| angle = -(custom_angle or 0) |
|
|
| return image.rotate( |
| angle, |
| expand=True, |
| resample=Image.Resampling.BICUBIC, |
| fillcolor=(255, 255, 255, 0) if image.mode == "RGBA" else None, |
| ) |
|
|
|
|
| def show_custom_angle(rotation_mode: RotationMode): |
| return gr.update(visible=rotation_mode == "Custom angle") |
|
|
|
|
| def build_app() -> gr.Blocks: |
| with gr.Blocks(title="PicRotate", theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# PicRotate") |
|
|
| with gr.Row(): |
| input_image = gr.Image( |
| label="Image", |
| sources=["upload", "clipboard"], |
| type="pil", |
| image_mode="RGBA", |
| height=460, |
| ) |
| output_image = gr.Image( |
| label="Rotated image", |
| type="pil", |
| image_mode="RGBA", |
| height=460, |
| ) |
|
|
| with gr.Row(): |
| rotation_mode = gr.Radio( |
| choices=["Clockwise 90", "Counterclockwise 90", "Custom angle"], |
| value="Clockwise 90", |
| label="Rotation", |
| ) |
| custom_angle = gr.Number( |
| value=45, |
| label="Custom angle in degrees clockwise", |
| precision=2, |
| visible=False, |
| ) |
|
|
| rotate_button = gr.Button("Rotate", variant="primary") |
| rotation_mode.change( |
| fn=show_custom_angle, |
| inputs=rotation_mode, |
| outputs=custom_angle, |
| ) |
| rotate_button.click( |
| fn=rotate_image, |
| inputs=[input_image, rotation_mode, custom_angle], |
| outputs=output_image, |
| ) |
|
|
| return demo |
|
|
|
|
| if __name__ == "__main__": |
| build_app().launch(server_name="0.0.0.0", server_port=7860) |
|
|