Spaces:
Sleeping
Sleeping
File size: 7,579 Bytes
1f1d2ca fc64c8f 1f1d2ca 0018a37 ca315bd 1f1d2ca 0018a37 ca315bd 0018a37 ca315bd 0018a37 ca315bd 0018a37 ca315bd 0018a37 ca315bd 0018a37 ca315bd 63b0612 0018a37 34717dc 0018a37 34717dc 0018a37 34717dc 0018a37 34717dc 63b0612 0018a37 34717dc 0018a37 ca315bd 0018a37 34717dc 0018a37 ca315bd 0018a37 34717dc 0018a37 34717dc 0018a37 1f1d2ca 0018a37 ca315bd 0018a37 34717dc 0018a37 ca315bd 0018a37 ca315bd 34717dc 36217b2 0018a37 36217b2 34717dc 36217b2 0018a37 34717dc ca315bd 34717dc ca315bd 34717dc ca315bd 34717dc 0018a37 34717dc 0018a37 34717dc 0018a37 ca315bd 34717dc ca315bd 0018a37 ca315bd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | import cv2
import numpy as np
import gradio as gr
import subprocess
import os
import time
from pathlib import Path
# --- CORE ARTISTIC FILTERS ---
def apply_artistic_filter(img, style):
try:
if style == "Original (Direct)": return img
if "Pencil" in style:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
inv = 255 - gray
blur = cv2.GaussianBlur(inv, (31, 31), 0)
return cv2.cvtColor(cv2.divide(gray, 255 - blur, scale=256), cv2.COLOR_GRAY2RGB)
elif "Charcoal" in style:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
blur = cv2.medianBlur(gray, 7)
return cv2.cvtColor(cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 5), cv2.COLOR_GRAY2RGB)
elif "Watercolor" in style:
if hasattr(cv2, 'stylization'): return cv2.stylization(img, sigma_s=60, sigma_r=0.6)
return cv2.bilateralFilter(img, 15, 75, 75)
elif style == "Neon Glow":
edges = cv2.Canny(img, 100, 200)
neon = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
neon[:, :, 1], neon[:, :, 2] = edges, 255
return cv2.bitwise_and(neon, neon, mask=edges)
return img
except: return img
# --- BRUSH TEXTURES ---
def get_brush_mask(length, brush_type):
if brush_type == "Sharp Row Reveal": return np.zeros(length, dtype=np.int32)
elif brush_type == "Flat Brush": return np.random.randint(-2, 3, length)
elif brush_type == "Rough Bristle": return np.random.randint(-15, 16, length)
elif brush_type == "Fan Brush":
x = np.linspace(0, 4 * np.pi, length)
return (np.sin(x) * 15).astype(np.int32)
return np.zeros(length, dtype=np.int32)
# --- VIDEO GENERATION ENGINE ---
def generate_video_engine(img_array, art_style, brush_texture, duration, direction, resolution, motion_effect, original_name):
res_map = {"720p (HD)": (1280, 720), "1080p (Full HD)": (1920, 1080), "4K (Ultra HD)": (3840, 2160)}
target_w, target_h = res_map[resolution]
fps = 30
total_frames = int(duration * fps)
paint_frames = int(total_frames * 0.8)
resized_img = cv2.resize(img_array, (target_w, target_h), interpolation=cv2.INTER_LANCZOS4)
target_img = apply_artistic_filter(resized_img, art_style)
# Filename Logic
temp_path = f'temp_{int(time.time() * 1000)}.mp4'
final_filename = f"{original_name}.mp4"
video = cv2.VideoWriter(temp_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (target_w, target_h))
mask_len = target_h if "Horizontal" in direction else target_w
brush_offsets = get_brush_mask(mask_len, brush_texture)
for i in range(total_frames):
t = i / total_frames
p_t = min(1.0, i / paint_frames)
canvas_img = target_img.copy()
# MOTION EFFECTS
if motion_effect == "CapCut 3D Zoom":
scale = 1.0 + (pow(t, 2) * 0.3)
offset = int(target_w * 0.03 * t)
src_pts = np.float32([[0,0], [target_w,0], [0,target_h], [target_w,target_h]])
dst_pts = np.float32([[-offset, -offset], [target_w+offset, -offset], [0,target_h], [target_w,target_h]])
M_3d = cv2.getPerspectiveTransform(src_pts, dst_pts)
canvas_img = cv2.warpPerspective(target_img, M_3d, (target_w, target_h), borderMode=cv2.BORDER_REPLICATE)
M_scale = cv2.getRotationMatrix2D((target_w/2, target_h/2), 0, scale)
canvas_img = cv2.warpAffine(canvas_img, M_scale, (target_w, target_h), borderMode=cv2.BORDER_REPLICATE)
elif motion_effect == "Spiral Cinematic":
M = cv2.getRotationMatrix2D((target_w/2, target_h/2), t * 15, 1.0 + (t * 0.15))
canvas_img = cv2.warpAffine(target_img, M, (target_w, target_h), borderMode=cv2.BORDER_REPLICATE)
elif motion_effect == "3D Parallax Tilt":
shift = int(target_w * 0.05 * t)
pts1 = np.float32([[0,0], [target_w,0], [0,target_h], [target_w,target_h]])
pts2 = np.float32([[shift,shift], [target_w-shift,0], [0,target_h], [target_w-shift,target_h-shift]])
M_p = cv2.getPerspectiveTransform(pts1, pts2)
canvas_img = cv2.warpPerspective(target_img, M_p, (target_w, target_h), borderMode=cv2.BORDER_REPLICATE)
# REVEAL LOGIC
frame = np.ones((target_h, target_w, 3), dtype=np.uint8) * 255
if "Horizontal" in direction:
base_w = int(p_t * target_w)
for y in range(target_h):
reveal_w = max(0, min(target_w, base_w + brush_offsets[y]))
if reveal_w > 0: frame[y, 0:reveal_w] = canvas_img[y, 0:reveal_w]
else:
base_h = int(p_t * target_h)
for x in range(target_w):
reveal_h = max(0, min(target_h, base_h + brush_offsets[x]))
if reveal_h > 0: frame[0:reveal_h, x] = canvas_img[0:reveal_h, x]
video.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
video.release()
# Final Encode
if os.path.exists(final_filename): os.remove(final_filename)
subprocess.call(['ffmpeg', '-y', '-i', temp_path, '-vcodec', 'libx264', '-crf', '18', '-pix_fmt', 'yuv420p', final_filename])
if os.path.exists(temp_path): os.remove(temp_path)
return final_filename
# --- BATCH HANDLER ---
def batch_handler(files, art_style, brush, duration, direction, resolution, motion):
if not files: return None, None
processed_videos = []
for f in files:
# Extract original filename
original_name = Path(f.name).stem
img = cv2.imread(f.name)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Pass name to engine
video_path = generate_video_engine(img, art_style, brush, duration, direction, resolution, motion, original_name)
processed_videos.append(video_path)
return processed_videos, processed_videos[0] if processed_videos else None
# --- UI ---
with gr.Blocks() as demo:
gr.Markdown("# 🎌 Ultimate Batch SpeedPaint Master")
with gr.Row():
with gr.Column():
file_input = gr.File(file_count="multiple", label="Upload Images")
motion_opt = gr.Dropdown(["None", "CapCut 3D Zoom", "Spiral Cinematic", "3D Parallax Tilt"], value="CapCut 3D Zoom", label="Motion Style")
art_opt = gr.Dropdown(["Original (Direct)", "Soft Pencil", "Pencil Sketch", "Charcoal Dust", "Watercolor Wash", "Neon Glow"], value="Original (Direct)", label="Filter")
brush_opt = gr.Dropdown(["Sharp Row Reveal", "Flat Brush", "Rough Bristle", "Fan Brush"], value="Rough Bristle", label="Brush")
with gr.Row():
dur_slider = gr.Slider(2, 20, value=6, step=1, label="Seconds")
res_opt = gr.Dropdown(["720p (HD)", "1080p (Full HD)", "4K (Ultra HD)"], value="1080p (Full HD)", label="Res")
dir_opt = gr.Radio(["Vertical (Top-Bottom)", "Horizontal (Left-Right)"], value="Vertical (Top-Bottom)", label="Direction")
run_btn = gr.Button("🚀 Start Batch Render", variant="primary")
with gr.Column():
video_preview = gr.Video(label="Preview")
file_download = gr.File(label="Download Videos (Named as Original Images)")
run_btn.click(
fn=batch_handler,
inputs=[file_input, art_opt, brush_opt, dur_slider, dir_opt, res_opt, motion_opt],
outputs=[file_download, video_preview]
)
demo.launch()
|