Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import cv2 | |
| import numpy as np | |
| from registry import registry | |
| from filters import * | |
| from components import create_filter_controls | |
| def create_app(): | |
| with gr.Blocks(theme=gr.themes.Soft(), css=""" | |
| .gradio-container { | |
| max-width: 1600px !important; | |
| margin: auto !important; | |
| } | |
| .filter-header { | |
| text-align: center; | |
| padding: 30px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border-radius: 15px; | |
| margin-bottom: 30px; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.2); | |
| } | |
| .filter-header h1 { | |
| color: white !important; | |
| margin: 0; | |
| font-size: 2.5em; | |
| font-weight: bold; | |
| } | |
| .filter-header p { | |
| color: rgba(255,255,255,0.95) !important; | |
| margin: 10px 0 0 0; | |
| font-size: 1.1em; | |
| } | |
| .filter-header a { | |
| color: rgba(255,255,255,0.9) !important; | |
| text-decoration: none; | |
| font-weight: 500; | |
| transition: all 0.3s ease; | |
| } | |
| .filter-header a:hover { | |
| color: white !important; | |
| text-decoration: underline; | |
| } | |
| .image-container { | |
| border-radius: 12px; | |
| padding: 15px; | |
| transition: all 0.3s ease; | |
| } | |
| .image-container:hover { | |
| transform: translateY(-2px); | |
| } | |
| .control-panel { | |
| border-radius: 12px; | |
| padding: 20px; | |
| margin-top: 15px; | |
| } | |
| .filter-description { | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin: 15px 0; | |
| border-left: 4px solid #667eea; | |
| } | |
| .gr-button-primary { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| border: none !important; | |
| font-weight: bold !important; | |
| color: white !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .gr-button-primary:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4) !important; | |
| } | |
| .gr-button-secondary { | |
| transition: all 0.3s ease !important; | |
| } | |
| .gr-button-secondary:hover { | |
| transform: translateY(-2px) !important; | |
| } | |
| .stats-panel { | |
| border-radius: 8px; | |
| padding: 15px; | |
| margin-top: 10px; | |
| text-align: center; | |
| } | |
| /* Dark mode compatibility */ | |
| .dark .filter-description { | |
| background: rgba(255,255,255,0.05); | |
| } | |
| .dark .image-container { | |
| background: rgba(255,255,255,0.02); | |
| } | |
| .dark .control-panel { | |
| background: rgba(255,255,255,0.03); | |
| } | |
| /* Light mode */ | |
| .filter-description { | |
| background: #f0f4f8; | |
| } | |
| .image-container { | |
| background: rgba(0,0,0,0.02); | |
| } | |
| .control-panel { | |
| background: linear-gradient(to bottom, #f8f9fa, #ffffff); | |
| } | |
| """) as app: | |
| # Header | |
| with gr.Column(elem_classes="filter-header"): | |
| gr.Markdown(""" | |
| # 📷 Photo Filter App | |
| Professional photo editing with powerful filters - Fast & Easy | |
| Built with [anycoder](https://huggingface.co/spaces/akhaliq/anycoder) | |
| """) | |
| # Initialize components | |
| filter_names = list(registry.filters.keys()) | |
| # Top Row - Images side by side | |
| with gr.Row(): | |
| # Left Column - Input Image | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes="image-container"): | |
| input_image = gr.Image( | |
| label="📤 Original Image", | |
| type="numpy", | |
| height=500 | |
| ) | |
| # Right Column - Output Image | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes="image-container"): | |
| output_image = gr.Image( | |
| label="✅ Edited Image", | |
| height=500 | |
| ) | |
| error_message = gr.Markdown(visible=False) | |
| with gr.Row(): | |
| download_button = gr.Button( | |
| "💾 Download", | |
| visible=False, | |
| size="lg" | |
| ) | |
| # Action Buttons | |
| with gr.Row(): | |
| apply_button = gr.Button( | |
| "✨ Apply Filter", | |
| variant="primary", | |
| size="lg", | |
| scale=2 | |
| ) | |
| reset_button = gr.Button( | |
| "🔄 Reset", | |
| variant="secondary", | |
| size="lg", | |
| scale=1 | |
| ) | |
| # Bottom Row - Filter Selection & Parameters | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes="control-panel"): | |
| gr.Markdown("### 🎨 Select Filter") | |
| filter_select = gr.Dropdown( | |
| label="Filter", | |
| choices=filter_names, | |
| value="Original", | |
| interactive=True | |
| ) | |
| filter_doc = gr.Markdown( | |
| value="Select a filter to see detailed description.", | |
| elem_classes="filter-description" | |
| ) | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes="control-panel"): | |
| gr.Markdown("### ⚙️ Customize") | |
| # Create dynamic filter controls | |
| filter_controls, control_components = create_filter_controls() | |
| # Stats panel | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown( | |
| f""" | |
| <div class="stats-panel"> | |
| 📊 <b>Total Filters:</b> {len(filter_names)} | | |
| 🎯 <b>Parameterized Filters:</b> {sum(1 for f in filter_names if registry.params_map.get(f))} | | |
| 🚀 <b>Quick Filters:</b> {sum(1 for f in filter_names if not registry.params_map.get(f))} | |
| </div> | |
| """ | |
| ) | |
| # Handle UI updates | |
| def update_controls(filter_name): | |
| updates = [] | |
| for group_name in filter_controls: | |
| updates.append(gr.update(visible=group_name == filter_name)) | |
| doc = registry.filters[filter_name].__doc__ or "No detailed description available." | |
| return updates + [doc] | |
| # Handle image processing | |
| def process(image, filter_name, *args): | |
| if image is None: | |
| return None, gr.update(visible=True, value="⚠️ **Note:** Please upload an image before applying filters!"), gr.update(visible=False) | |
| try: | |
| image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| # Get parameters for current filter | |
| params = {} | |
| if filter_name in control_components: | |
| param_names = list(registry.params_map.get(filter_name, {}).keys()) | |
| controls_list = control_components[filter_name] | |
| for i, param_name in enumerate(param_names): | |
| if i < len(controls_list): | |
| params[param_name] = args[i] | |
| processed = registry.filters[filter_name](image, **params) | |
| if len(processed.shape) == 2: | |
| processed = cv2.cvtColor(processed, cv2.COLOR_GRAY2RGB) | |
| else: | |
| processed = cv2.cvtColor(processed, cv2.COLOR_BGR2RGB) | |
| return processed, gr.update(visible=False), gr.update(visible=True) | |
| except Exception as e: | |
| return None, gr.update(visible=True, value=f"❌ **Error:** {str(e)}"), gr.update(visible=False) | |
| # Handle reset | |
| def reset_all(): | |
| return None, None, gr.update(visible=False), gr.update(visible=False), "Original" | |
| # Collect all control components | |
| all_control_components = [] | |
| for filter_name in control_components: | |
| all_control_components.extend(control_components[filter_name]) | |
| # Connect events | |
| filter_select.change( | |
| update_controls, | |
| inputs=filter_select, | |
| outputs=list(filter_controls.values()) + [filter_doc], | |
| api_name=False | |
| ) | |
| input_components = [input_image, filter_select] + all_control_components | |
| apply_button.click( | |
| process, | |
| inputs=input_components, | |
| outputs=[output_image, error_message, download_button], | |
| ) | |
| reset_button.click( | |
| reset_all, | |
| inputs=None, | |
| outputs=[input_image, output_image, error_message, download_button, filter_select] | |
| ) | |
| return app | |
| if __name__ == "__main__": | |
| app = create_app() | |
| app.launch() |