Spaces:
Sleeping
Sleeping
removed emojis
Browse files
app.py
CHANGED
|
@@ -46,7 +46,7 @@ warnings.filterwarnings("ignore")
|
|
| 46 |
# Set page config
|
| 47 |
st.set_page_config(
|
| 48 |
page_title="Style Transfer Studio",
|
| 49 |
-
page_icon="π¨",
|
| 50 |
layout="wide",
|
| 51 |
initial_sidebar_state="expanded"
|
| 52 |
)
|
|
@@ -1754,17 +1754,17 @@ style_choices = sorted([info['name'] for info in system.cyclegan_models.values()
|
|
| 1754 |
# ===========================
|
| 1755 |
|
| 1756 |
# Main app
|
| 1757 |
-
st.title("
|
| 1758 |
st.markdown("Professional image and video style transfer with CycleGAN and custom training capabilities")
|
| 1759 |
|
| 1760 |
# Sidebar for global settings
|
| 1761 |
with st.sidebar:
|
| 1762 |
-
st.header("
|
| 1763 |
|
| 1764 |
# GPU status
|
| 1765 |
if torch.cuda.is_available():
|
| 1766 |
gpu_info = torch.cuda.get_device_properties(0)
|
| 1767 |
-
st.success(f"
|
| 1768 |
|
| 1769 |
# Show memory usage
|
| 1770 |
total_memory = gpu_info.total_memory / 1e9
|
|
@@ -1784,15 +1784,15 @@ with st.sidebar:
|
|
| 1784 |
|
| 1785 |
# Check if GPU is actually being used
|
| 1786 |
if used_memory < 0.1:
|
| 1787 |
-
st.warning("
|
| 1788 |
|
| 1789 |
# Force GPU button
|
| 1790 |
-
if st.button("
|
| 1791 |
torch.cuda.empty_cache()
|
| 1792 |
torch.cuda.synchronize()
|
| 1793 |
st.rerun()
|
| 1794 |
else:
|
| 1795 |
-
st.warning("
|
| 1796 |
st.caption("For faster processing, use a GPU-enabled environment")
|
| 1797 |
|
| 1798 |
st.markdown("---")
|
|
@@ -1808,9 +1808,9 @@ with st.sidebar:
|
|
| 1808 |
# Unsplash API status
|
| 1809 |
st.markdown("---")
|
| 1810 |
if unsplash.access_key:
|
| 1811 |
-
st.success("
|
| 1812 |
else:
|
| 1813 |
-
st.info("
|
| 1814 |
|
| 1815 |
# Debug mode
|
| 1816 |
st.markdown("---")
|
|
@@ -1825,18 +1825,18 @@ Models Loaded: {len(system.loaded_generators)}
|
|
| 1825 |
|
| 1826 |
# Main tabs
|
| 1827 |
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([
|
| 1828 |
-
"
|
| 1829 |
-
"
|
| 1830 |
-
"
|
| 1831 |
-
"
|
| 1832 |
-
"
|
| 1833 |
-
"
|
| 1834 |
])
|
| 1835 |
|
| 1836 |
# TAB 1: Style Transfer (with Unsplash integration)
|
| 1837 |
with tab1:
|
| 1838 |
# Unsplash Search Section
|
| 1839 |
-
with st.expander("
|
| 1840 |
if not unsplash.access_key:
|
| 1841 |
st.info("""
|
| 1842 |
To enable Unsplash search:
|
|
@@ -1850,10 +1850,10 @@ with tab1:
|
|
| 1850 |
with search_col2:
|
| 1851 |
orientation = st.selectbox("Orientation", ["all", "landscape", "portrait", "squarish"])
|
| 1852 |
with search_col3:
|
| 1853 |
-
search_button = st.button("
|
| 1854 |
|
| 1855 |
# Random photos button
|
| 1856 |
-
if st.button("
|
| 1857 |
with st.spinner("Loading random photos..."):
|
| 1858 |
results, error = unsplash.get_random_photos(count=12)
|
| 1859 |
|
|
@@ -2005,7 +2005,7 @@ with tab1:
|
|
| 2005 |
|
| 2006 |
# TAB 2: Regional Transform
|
| 2007 |
with tab2:
|
| 2008 |
-
st.header("
|
| 2009 |
st.markdown("Paint different regions to apply different styles locally")
|
| 2010 |
|
| 2011 |
# Initialize session state
|
|
@@ -2078,7 +2078,7 @@ with tab2:
|
|
| 2078 |
st.rerun()
|
| 2079 |
|
| 2080 |
with col_btn2:
|
| 2081 |
-
if st.button("
|
| 2082 |
st.session_state.regions = []
|
| 2083 |
st.session_state.canvas_results = {}
|
| 2084 |
if 'regional_result' in st.session_state:
|
|
@@ -2088,7 +2088,7 @@ with tab2:
|
|
| 2088 |
st.rerun()
|
| 2089 |
|
| 2090 |
with col_btn3:
|
| 2091 |
-
if st.button("
|
| 2092 |
if 'regional_result' in st.session_state:
|
| 2093 |
del st.session_state['regional_result']
|
| 2094 |
st.session_state.canvas_ready = True
|
|
@@ -2146,11 +2146,11 @@ with tab2:
|
|
| 2146 |
# Show workflow status
|
| 2147 |
if 'regional_result' in st.session_state:
|
| 2148 |
if st.session_state.canvas_ready:
|
| 2149 |
-
st.success("
|
| 2150 |
else:
|
| 2151 |
-
st.info("
|
| 2152 |
else:
|
| 2153 |
-
st.info("
|
| 2154 |
|
| 2155 |
# Check if we're in edit mode
|
| 2156 |
if not st.session_state.canvas_ready:
|
|
@@ -2165,7 +2165,7 @@ with tab2:
|
|
| 2165 |
display_width, display_height = display_image.size
|
| 2166 |
|
| 2167 |
# Info message
|
| 2168 |
-
st.info(f"
|
| 2169 |
|
| 2170 |
# Get current region
|
| 2171 |
current_region_idx = st.selectbox(
|
|
@@ -2297,11 +2297,11 @@ with tab2:
|
|
| 2297 |
# TAB 3: Video Processing
|
| 2298 |
# TAB 3: Video Processing
|
| 2299 |
with tab3:
|
| 2300 |
-
st.header("
|
| 2301 |
|
| 2302 |
if not VIDEO_PROCESSING_AVAILABLE:
|
| 2303 |
st.warning("""
|
| 2304 |
-
|
| 2305 |
|
| 2306 |
To enable video processing, add `opencv-python` to your requirements.txt
|
| 2307 |
""")
|
|
@@ -2370,7 +2370,7 @@ with tab3:
|
|
| 2370 |
st.session_state['video_result_available'] = True
|
| 2371 |
st.session_state['video_is_mp4'] = (file_ext == '.mp4')
|
| 2372 |
|
| 2373 |
-
st.success(f"
|
| 2374 |
|
| 2375 |
# Clean up files
|
| 2376 |
try:
|
|
@@ -2409,7 +2409,7 @@ with tab3:
|
|
| 2409 |
if st.session_state.get('video_is_mp4', False):
|
| 2410 |
# For MP4, should work in browser
|
| 2411 |
st.video(video_bytes)
|
| 2412 |
-
st.success(f"
|
| 2413 |
else:
|
| 2414 |
# For non-MP4, show info and download
|
| 2415 |
st.info(f"Video format ({file_ext}) may not play in browser. Please download to view.")
|
|
@@ -2444,7 +2444,7 @@ with tab3:
|
|
| 2444 |
|
| 2445 |
with col_dl1:
|
| 2446 |
st.download_button(
|
| 2447 |
-
label=f"
|
| 2448 |
data=video_bytes,
|
| 2449 |
file_name=f"styled_video_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}{file_ext}",
|
| 2450 |
mime=mime_type,
|
|
@@ -2452,7 +2452,7 @@ with tab3:
|
|
| 2452 |
)
|
| 2453 |
|
| 2454 |
with col_dl2:
|
| 2455 |
-
if st.button("
|
| 2456 |
del st.session_state['video_result_bytes']
|
| 2457 |
st.session_state['video_result_available'] = False
|
| 2458 |
if 'video_result_ext' in st.session_state:
|
|
@@ -2463,7 +2463,7 @@ with tab3:
|
|
| 2463 |
|
| 2464 |
# Info about playback
|
| 2465 |
if not st.session_state.get('video_is_mp4', False):
|
| 2466 |
-
st.caption("
|
| 2467 |
|
| 2468 |
except Exception as e:
|
| 2469 |
st.error(f"Error displaying video: {str(e)}")
|
|
@@ -2487,7 +2487,7 @@ with tab3:
|
|
| 2487 |
|
| 2488 |
# TAB 4: Training with AdaIN
|
| 2489 |
with tab4:
|
| 2490 |
-
st.header("
|
| 2491 |
st.markdown("Train your own style transfer model using Adaptive Instance Normalization")
|
| 2492 |
|
| 2493 |
# Initialize session state for content images
|
|
@@ -2497,7 +2497,7 @@ with tab4:
|
|
| 2497 |
col1, col2, col3 = st.columns([1, 1, 1])
|
| 2498 |
|
| 2499 |
with col1:
|
| 2500 |
-
st.subheader("
|
| 2501 |
style_imgs = st.file_uploader("Upload 1-5 style images", type=['png', 'jpg', 'jpeg'],
|
| 2502 |
accept_multiple_files=True, key="train_style_adain")
|
| 2503 |
|
|
@@ -2513,7 +2513,7 @@ with tab4:
|
|
| 2513 |
st.caption(f"... and {len(style_imgs) - 3} more")
|
| 2514 |
|
| 2515 |
with col2:
|
| 2516 |
-
st.subheader("
|
| 2517 |
content_imgs = st.file_uploader("Upload content images (5-50 recommended)",
|
| 2518 |
type=['png', 'jpg', 'jpeg'],
|
| 2519 |
accept_multiple_files=True,
|
|
@@ -2553,7 +2553,7 @@ with tab4:
|
|
| 2553 |
if st.button("π Start AdaIN Training", type="primary", use_container_width=True):
|
| 2554 |
if style_imgs and content_imgs:
|
| 2555 |
if len(content_imgs) < 5:
|
| 2556 |
-
st.warning("
|
| 2557 |
|
| 2558 |
with st.spinner("Training AdaIN model..."):
|
| 2559 |
progress_bar = st.progress(0)
|
|
@@ -2594,7 +2594,7 @@ with tab4:
|
|
| 2594 |
st.session_state['trained_adain_model'] = model
|
| 2595 |
st.session_state['trained_style_images'] = style_images
|
| 2596 |
st.session_state['model_path'] = f'/tmp/trained_models/{model_name}_final.pth'
|
| 2597 |
-
st.success("
|
| 2598 |
|
| 2599 |
progress_bar.empty()
|
| 2600 |
status_text.empty()
|
|
@@ -2604,7 +2604,7 @@ with tab4:
|
|
| 2604 |
# Testing section
|
| 2605 |
if 'trained_adain_model' in st.session_state:
|
| 2606 |
st.markdown("---")
|
| 2607 |
-
st.header("
|
| 2608 |
|
| 2609 |
test_col1, test_col2, test_col3 = st.columns([1, 1, 1])
|
| 2610 |
|
|
@@ -2684,7 +2684,7 @@ with tab4:
|
|
| 2684 |
with col_dl1:
|
| 2685 |
with open(st.session_state['model_path'], 'rb') as f:
|
| 2686 |
st.download_button(
|
| 2687 |
-
label="
|
| 2688 |
data=f.read(),
|
| 2689 |
file_name=f"{model_name}_adain_final.pth",
|
| 2690 |
mime="application/octet-stream",
|
|
@@ -2695,7 +2695,7 @@ with tab4:
|
|
| 2695 |
|
| 2696 |
# TAB 5: Batch Processing
|
| 2697 |
with tab5:
|
| 2698 |
-
st.header("
|
| 2699 |
|
| 2700 |
col1, col2 = st.columns(2)
|
| 2701 |
|
|
@@ -2825,13 +2825,13 @@ with tab6:
|
|
| 2825 |
|
| 2826 |
### Features
|
| 2827 |
|
| 2828 |
-
####
|
| 2829 |
- Apply multiple styles simultaneously
|
| 2830 |
- Adjustable intensity for each style
|
| 2831 |
- Multiple blending modes for creative effects
|
| 2832 |
- **NEW**: Search and use images from Unsplash
|
| 2833 |
|
| 2834 |
-
####
|
| 2835 |
- Paint specific regions to apply different styles
|
| 2836 |
- Support for multiple regions with different styles
|
| 2837 |
- Adjustable brush size and drawing tools
|
|
@@ -2839,7 +2839,7 @@ with tab6:
|
|
| 2839 |
- Persistent brush strokes across regions
|
| 2840 |
- Optimized display for large images
|
| 2841 |
|
| 2842 |
-
####
|
| 2843 |
- Frame-by-frame style transfer
|
| 2844 |
- Maintains temporal consistency
|
| 2845 |
- Supports all style combinations and blend modes
|
|
|
|
| 46 |
# Set page config
|
| 47 |
st.set_page_config(
|
| 48 |
page_title="Style Transfer Studio",
|
| 49 |
+
# page_icon="π¨",
|
| 50 |
layout="wide",
|
| 51 |
initial_sidebar_state="expanded"
|
| 52 |
)
|
|
|
|
| 1754 |
# ===========================
|
| 1755 |
|
| 1756 |
# Main app
|
| 1757 |
+
st.title("Style Transfer")
|
| 1758 |
st.markdown("Professional image and video style transfer with CycleGAN and custom training capabilities")
|
| 1759 |
|
| 1760 |
# Sidebar for global settings
|
| 1761 |
with st.sidebar:
|
| 1762 |
+
st.header("Settings")
|
| 1763 |
|
| 1764 |
# GPU status
|
| 1765 |
if torch.cuda.is_available():
|
| 1766 |
gpu_info = torch.cuda.get_device_properties(0)
|
| 1767 |
+
st.success(f"GPU: {gpu_info.name}")
|
| 1768 |
|
| 1769 |
# Show memory usage
|
| 1770 |
total_memory = gpu_info.total_memory / 1e9
|
|
|
|
| 1784 |
|
| 1785 |
# Check if GPU is actually being used
|
| 1786 |
if used_memory < 0.1:
|
| 1787 |
+
st.warning("GPU detected but not in use. Models may be running on CPU.")
|
| 1788 |
|
| 1789 |
# Force GPU button
|
| 1790 |
+
if st.button("Force GPU Reset"):
|
| 1791 |
torch.cuda.empty_cache()
|
| 1792 |
torch.cuda.synchronize()
|
| 1793 |
st.rerun()
|
| 1794 |
else:
|
| 1795 |
+
st.warning("Running on CPU (GPU not available)")
|
| 1796 |
st.caption("For faster processing, use a GPU-enabled environment")
|
| 1797 |
|
| 1798 |
st.markdown("---")
|
|
|
|
| 1808 |
# Unsplash API status
|
| 1809 |
st.markdown("---")
|
| 1810 |
if unsplash.access_key:
|
| 1811 |
+
st.success("Unsplash API Connected")
|
| 1812 |
else:
|
| 1813 |
+
st.info("Add Unsplash API key for image search")
|
| 1814 |
|
| 1815 |
# Debug mode
|
| 1816 |
st.markdown("---")
|
|
|
|
| 1825 |
|
| 1826 |
# Main tabs
|
| 1827 |
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([
|
| 1828 |
+
"Style Transfer",
|
| 1829 |
+
"Regional Transform",
|
| 1830 |
+
"Video Processing",
|
| 1831 |
+
"Train Custom Style",
|
| 1832 |
+
"Batch Processing",
|
| 1833 |
+
"Documentation"
|
| 1834 |
])
|
| 1835 |
|
| 1836 |
# TAB 1: Style Transfer (with Unsplash integration)
|
| 1837 |
with tab1:
|
| 1838 |
# Unsplash Search Section
|
| 1839 |
+
with st.expander("Search Unsplash for Images", expanded=False):
|
| 1840 |
if not unsplash.access_key:
|
| 1841 |
st.info("""
|
| 1842 |
To enable Unsplash search:
|
|
|
|
| 1850 |
with search_col2:
|
| 1851 |
orientation = st.selectbox("Orientation", ["all", "landscape", "portrait", "squarish"])
|
| 1852 |
with search_col3:
|
| 1853 |
+
search_button = st.button("Search", use_container_width=True)
|
| 1854 |
|
| 1855 |
# Random photos button
|
| 1856 |
+
if st.button("Get Random Photos"):
|
| 1857 |
with st.spinner("Loading random photos..."):
|
| 1858 |
results, error = unsplash.get_random_photos(count=12)
|
| 1859 |
|
|
|
|
| 2005 |
|
| 2006 |
# TAB 2: Regional Transform
|
| 2007 |
with tab2:
|
| 2008 |
+
st.header("Regional Style Transform")
|
| 2009 |
st.markdown("Paint different regions to apply different styles locally")
|
| 2010 |
|
| 2011 |
# Initialize session state
|
|
|
|
| 2078 |
st.rerun()
|
| 2079 |
|
| 2080 |
with col_btn2:
|
| 2081 |
+
if st.button("Clear All", use_container_width=True):
|
| 2082 |
st.session_state.regions = []
|
| 2083 |
st.session_state.canvas_results = {}
|
| 2084 |
if 'regional_result' in st.session_state:
|
|
|
|
| 2088 |
st.rerun()
|
| 2089 |
|
| 2090 |
with col_btn3:
|
| 2091 |
+
if st.button("Reset Result", use_container_width=True):
|
| 2092 |
if 'regional_result' in st.session_state:
|
| 2093 |
del st.session_state['regional_result']
|
| 2094 |
st.session_state.canvas_ready = True
|
|
|
|
| 2146 |
# Show workflow status
|
| 2147 |
if 'regional_result' in st.session_state:
|
| 2148 |
if st.session_state.canvas_ready:
|
| 2149 |
+
st.success("**Edit Mode** - Paint your regions and click 'Apply Regional Styles' when ready")
|
| 2150 |
else:
|
| 2151 |
+
st.info("**Preview Mode** - Click 'Continue Editing' to modify regions")
|
| 2152 |
else:
|
| 2153 |
+
st.info("Paint on the canvas below to define regions for each style")
|
| 2154 |
|
| 2155 |
# Check if we're in edit mode
|
| 2156 |
if not st.session_state.canvas_ready:
|
|
|
|
| 2165 |
display_width, display_height = display_image.size
|
| 2166 |
|
| 2167 |
# Info message
|
| 2168 |
+
st.info(f"Image resized to {display_width}x{display_height} for display. Original resolution will be used for processing.")
|
| 2169 |
|
| 2170 |
# Get current region
|
| 2171 |
current_region_idx = st.selectbox(
|
|
|
|
| 2297 |
# TAB 3: Video Processing
|
| 2298 |
# TAB 3: Video Processing
|
| 2299 |
with tab3:
|
| 2300 |
+
st.header("Video Processing")
|
| 2301 |
|
| 2302 |
if not VIDEO_PROCESSING_AVAILABLE:
|
| 2303 |
st.warning("""
|
| 2304 |
+
Video processing requires OpenCV to be installed.
|
| 2305 |
|
| 2306 |
To enable video processing, add `opencv-python` to your requirements.txt
|
| 2307 |
""")
|
|
|
|
| 2370 |
st.session_state['video_result_available'] = True
|
| 2371 |
st.session_state['video_is_mp4'] = (file_ext == '.mp4')
|
| 2372 |
|
| 2373 |
+
st.success(f"Video processing complete! Format: {file_ext.upper()}")
|
| 2374 |
|
| 2375 |
# Clean up files
|
| 2376 |
try:
|
|
|
|
| 2409 |
if st.session_state.get('video_is_mp4', False):
|
| 2410 |
# For MP4, should work in browser
|
| 2411 |
st.video(video_bytes)
|
| 2412 |
+
st.success(f"Video ready! Size: {file_size_mb:.2f} MB")
|
| 2413 |
else:
|
| 2414 |
# For non-MP4, show info and download
|
| 2415 |
st.info(f"Video format ({file_ext}) may not play in browser. Please download to view.")
|
|
|
|
| 2444 |
|
| 2445 |
with col_dl1:
|
| 2446 |
st.download_button(
|
| 2447 |
+
label=f"Download Video ({file_ext.upper()})",
|
| 2448 |
data=video_bytes,
|
| 2449 |
file_name=f"styled_video_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}{file_ext}",
|
| 2450 |
mime=mime_type,
|
|
|
|
| 2452 |
)
|
| 2453 |
|
| 2454 |
with col_dl2:
|
| 2455 |
+
if st.button("Clear Result", use_container_width=True):
|
| 2456 |
del st.session_state['video_result_bytes']
|
| 2457 |
st.session_state['video_result_available'] = False
|
| 2458 |
if 'video_result_ext' in st.session_state:
|
|
|
|
| 2463 |
|
| 2464 |
# Info about playback
|
| 2465 |
if not st.session_state.get('video_is_mp4', False):
|
| 2466 |
+
st.caption("For best compatibility, download and use VLC or another video player.")
|
| 2467 |
|
| 2468 |
except Exception as e:
|
| 2469 |
st.error(f"Error displaying video: {str(e)}")
|
|
|
|
| 2487 |
|
| 2488 |
# TAB 4: Training with AdaIN
|
| 2489 |
with tab4:
|
| 2490 |
+
st.header("Train Custom Style with AdaIN")
|
| 2491 |
st.markdown("Train your own style transfer model using Adaptive Instance Normalization")
|
| 2492 |
|
| 2493 |
# Initialize session state for content images
|
|
|
|
| 2497 |
col1, col2, col3 = st.columns([1, 1, 1])
|
| 2498 |
|
| 2499 |
with col1:
|
| 2500 |
+
st.subheader("Style Images")
|
| 2501 |
style_imgs = st.file_uploader("Upload 1-5 style images", type=['png', 'jpg', 'jpeg'],
|
| 2502 |
accept_multiple_files=True, key="train_style_adain")
|
| 2503 |
|
|
|
|
| 2513 |
st.caption(f"... and {len(style_imgs) - 3} more")
|
| 2514 |
|
| 2515 |
with col2:
|
| 2516 |
+
st.subheader("Content Images")
|
| 2517 |
content_imgs = st.file_uploader("Upload content images (5-50 recommended)",
|
| 2518 |
type=['png', 'jpg', 'jpeg'],
|
| 2519 |
accept_multiple_files=True,
|
|
|
|
| 2553 |
if st.button("π Start AdaIN Training", type="primary", use_container_width=True):
|
| 2554 |
if style_imgs and content_imgs:
|
| 2555 |
if len(content_imgs) < 5:
|
| 2556 |
+
st.warning("For best results, use at least 5 content images")
|
| 2557 |
|
| 2558 |
with st.spinner("Training AdaIN model..."):
|
| 2559 |
progress_bar = st.progress(0)
|
|
|
|
| 2594 |
st.session_state['trained_adain_model'] = model
|
| 2595 |
st.session_state['trained_style_images'] = style_images
|
| 2596 |
st.session_state['model_path'] = f'/tmp/trained_models/{model_name}_final.pth'
|
| 2597 |
+
st.success("AdaIN training complete!")
|
| 2598 |
|
| 2599 |
progress_bar.empty()
|
| 2600 |
status_text.empty()
|
|
|
|
| 2604 |
# Testing section
|
| 2605 |
if 'trained_adain_model' in st.session_state:
|
| 2606 |
st.markdown("---")
|
| 2607 |
+
st.header("Test Your AdaIN Model")
|
| 2608 |
|
| 2609 |
test_col1, test_col2, test_col3 = st.columns([1, 1, 1])
|
| 2610 |
|
|
|
|
| 2684 |
with col_dl1:
|
| 2685 |
with open(st.session_state['model_path'], 'rb') as f:
|
| 2686 |
st.download_button(
|
| 2687 |
+
label="Download Trained AdaIN Model",
|
| 2688 |
data=f.read(),
|
| 2689 |
file_name=f"{model_name}_adain_final.pth",
|
| 2690 |
mime="application/octet-stream",
|
|
|
|
| 2695 |
|
| 2696 |
# TAB 5: Batch Processing
|
| 2697 |
with tab5:
|
| 2698 |
+
st.header("Batch Processing")
|
| 2699 |
|
| 2700 |
col1, col2 = st.columns(2)
|
| 2701 |
|
|
|
|
| 2825 |
|
| 2826 |
### Features
|
| 2827 |
|
| 2828 |
+
#### Style Transfer
|
| 2829 |
- Apply multiple styles simultaneously
|
| 2830 |
- Adjustable intensity for each style
|
| 2831 |
- Multiple blending modes for creative effects
|
| 2832 |
- **NEW**: Search and use images from Unsplash
|
| 2833 |
|
| 2834 |
+
#### Regional Transform
|
| 2835 |
- Paint specific regions to apply different styles
|
| 2836 |
- Support for multiple regions with different styles
|
| 2837 |
- Adjustable brush size and drawing tools
|
|
|
|
| 2839 |
- Persistent brush strokes across regions
|
| 2840 |
- Optimized display for large images
|
| 2841 |
|
| 2842 |
+
#### Video Processing
|
| 2843 |
- Frame-by-frame style transfer
|
| 2844 |
- Maintains temporal consistency
|
| 2845 |
- Supports all style combinations and blend modes
|