Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import cv2
|
| 2 |
import numpy as np
|
|
|
|
| 3 |
import os
|
| 4 |
import subprocess
|
| 5 |
from pathlib import Path
|
|
@@ -10,7 +11,7 @@ def apply_artistic_filter(img, style):
|
|
| 10 |
return cv2.xphoto.oilPainting(img, 7, 1) if hasattr(cv2, 'xphoto') else cv2.detailEnhance(img, sigma_s=10, sigma_r=0.15)
|
| 11 |
elif style == "Cyberpunk":
|
| 12 |
img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)
|
| 13 |
-
img_hsv[:,:,0] = (img_hsv[:,:,0] + 20) % 180
|
| 14 |
img_hsv[:,:,1] = np.clip(img_hsv[:,:,1] * 1.5, 0, 255)
|
| 15 |
return cv2.cvtColor(img_hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)
|
| 16 |
elif style == "Vivid Anime":
|
|
@@ -19,64 +20,68 @@ def apply_artistic_filter(img, style):
|
|
| 19 |
elif style == "Blueprint":
|
| 20 |
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
| 21 |
edges = cv2.Canny(gray, 50, 150)
|
| 22 |
-
blue_bg = np.
|
| 23 |
-
blue_bg[:] = [20, 50, 150]
|
| 24 |
white_edges = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
| 25 |
return cv2.addWeighted(blue_bg, 0.7, white_edges, 0.3, 0)
|
| 26 |
elif style == "Vintage Film":
|
| 27 |
sepia = np.array([[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]])
|
| 28 |
return cv2.transform(img, sepia)
|
| 29 |
-
# Original filters here...
|
| 30 |
return img
|
| 31 |
except: return img
|
| 32 |
|
| 33 |
-
def
|
| 34 |
-
Path(output_folder).mkdir(parents=True, exist_ok=True)
|
| 35 |
-
valid_exts = (".jpg", ".jpeg", ".png", ".webp")
|
| 36 |
-
|
| 37 |
-
for filename in os.listdir(input_folder):
|
| 38 |
-
if filename.lower().endswith(valid_exts):
|
| 39 |
-
img_path = os.path.join(input_folder, filename)
|
| 40 |
-
video_name = Path(filename).stem + ".mp4"
|
| 41 |
-
output_path = os.path.join(output_folder, video_name)
|
| 42 |
-
|
| 43 |
-
print(f"Processing: {filename} -> {video_name}")
|
| 44 |
-
generate_speedpaint(img_path, output_path, duration, style)
|
| 45 |
-
|
| 46 |
-
def generate_speedpaint(img_path, final_path, duration, style):
|
| 47 |
img = cv2.imread(img_path)
|
| 48 |
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 49 |
-
h, w = 1080, 1920
|
| 50 |
img = cv2.resize(img, (w, h), interpolation=cv2.INTER_LANCZOS4)
|
| 51 |
target_img = apply_artistic_filter(img, style)
|
| 52 |
|
| 53 |
fps = 30
|
| 54 |
-
total_frames = duration * fps
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
video = cv2.VideoWriter(temp_raw, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
|
| 57 |
|
| 58 |
for i in range(total_frames):
|
| 59 |
progress = i / total_frames
|
| 60 |
-
# 3D Animation: Subtle Zoom-in effect
|
| 61 |
zoom = 1 + (0.05 * progress)
|
| 62 |
M = cv2.getRotationMatrix2D((w/2, h/2), 0, zoom)
|
| 63 |
animated_target = cv2.warpAffine(target_img, M, (w, h))
|
| 64 |
|
| 65 |
frame = np.ones((h, w, 3), dtype=np.uint8) * 255
|
| 66 |
reveal_w = int(progress * w)
|
| 67 |
-
|
| 68 |
-
# Horizontal Reveal
|
| 69 |
frame[:, 0:reveal_w] = animated_target[:, 0:reveal_w]
|
| 70 |
video.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
|
| 71 |
|
| 72 |
video.release()
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
|
| 78 |
-
if __name__ == "__main__":
|
| 79 |
-
# 1. Create a folder named 'input_images'
|
| 80 |
-
# 2. Put all your photos inside
|
| 81 |
-
# 3. Run this script
|
| 82 |
-
process_batch(style="Cyberpunk")
|
|
|
|
| 1 |
import cv2
|
| 2 |
import numpy as np
|
| 3 |
+
import gradio as gr
|
| 4 |
import os
|
| 5 |
import subprocess
|
| 6 |
from pathlib import Path
|
|
|
|
| 11 |
return cv2.xphoto.oilPainting(img, 7, 1) if hasattr(cv2, 'xphoto') else cv2.detailEnhance(img, sigma_s=10, sigma_r=0.15)
|
| 12 |
elif style == "Cyberpunk":
|
| 13 |
img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)
|
| 14 |
+
img_hsv[:,:,0] = (img_hsv[:,:,0] + 20) % 180
|
| 15 |
img_hsv[:,:,1] = np.clip(img_hsv[:,:,1] * 1.5, 0, 255)
|
| 16 |
return cv2.cvtColor(img_hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)
|
| 17 |
elif style == "Vivid Anime":
|
|
|
|
| 20 |
elif style == "Blueprint":
|
| 21 |
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
| 22 |
edges = cv2.Canny(gray, 50, 150)
|
| 23 |
+
blue_bg = np.full(img.shape, (255, 0, 0), dtype=np.uint8)
|
|
|
|
| 24 |
white_edges = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
| 25 |
return cv2.addWeighted(blue_bg, 0.7, white_edges, 0.3, 0)
|
| 26 |
elif style == "Vintage Film":
|
| 27 |
sepia = np.array([[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]])
|
| 28 |
return cv2.transform(img, sepia)
|
|
|
|
| 29 |
return img
|
| 30 |
except: return img
|
| 31 |
|
| 32 |
+
def generate_single_video(img_path, style, duration):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
img = cv2.imread(img_path)
|
| 34 |
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 35 |
+
h, w = 1080, 1920
|
| 36 |
img = cv2.resize(img, (w, h), interpolation=cv2.INTER_LANCZOS4)
|
| 37 |
target_img = apply_artistic_filter(img, style)
|
| 38 |
|
| 39 |
fps = 30
|
| 40 |
+
total_frames = int(duration * fps)
|
| 41 |
+
|
| 42 |
+
# Matching name: image_name.jpg -> image_name.mp4
|
| 43 |
+
video_name = Path(img_path).stem + ".mp4"
|
| 44 |
+
temp_raw = f"temp_{video_name}"
|
| 45 |
+
|
| 46 |
video = cv2.VideoWriter(temp_raw, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
|
| 47 |
|
| 48 |
for i in range(total_frames):
|
| 49 |
progress = i / total_frames
|
|
|
|
| 50 |
zoom = 1 + (0.05 * progress)
|
| 51 |
M = cv2.getRotationMatrix2D((w/2, h/2), 0, zoom)
|
| 52 |
animated_target = cv2.warpAffine(target_img, M, (w, h))
|
| 53 |
|
| 54 |
frame = np.ones((h, w, 3), dtype=np.uint8) * 255
|
| 55 |
reveal_w = int(progress * w)
|
|
|
|
|
|
|
| 56 |
frame[:, 0:reveal_w] = animated_target[:, 0:reveal_w]
|
| 57 |
video.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
|
| 58 |
|
| 59 |
video.release()
|
| 60 |
+
|
| 61 |
+
# Final Encode
|
| 62 |
+
final_output = f"finished_{video_name}"
|
| 63 |
+
subprocess.call(['ffmpeg', '-y', '-i', temp_raw, '-vcodec', 'libx264', '-crf', '18', '-pix_fmt', 'yuv420p', final_output])
|
| 64 |
+
if os.path.exists(temp_raw): os.remove(temp_raw)
|
| 65 |
+
return final_output
|
| 66 |
+
|
| 67 |
+
def batch_ui_handler(files, style, duration):
|
| 68 |
+
output_paths = []
|
| 69 |
+
for file in files:
|
| 70 |
+
output_paths.append(generate_single_video(file.name, style, duration))
|
| 71 |
+
return output_paths
|
| 72 |
+
|
| 73 |
+
# Gradio Interface
|
| 74 |
+
with gr.Blocks() as demo:
|
| 75 |
+
gr.Markdown("# 🎨 Batch 4K 3D SpeedPaint Generator")
|
| 76 |
+
with gr.Row():
|
| 77 |
+
with gr.Column():
|
| 78 |
+
file_input = gr.File(file_count="multiple", label="Upload Multiple Images")
|
| 79 |
+
style_choice = gr.Dropdown(["Oil Painting", "Cyberpunk", "Vivid Anime", "Blueprint", "Vintage Film"], value="Oil Painting", label="Choose Style")
|
| 80 |
+
dur_slider = gr.Slider(1, 15, value=5, step=1, label="Duration (Seconds)")
|
| 81 |
+
run_btn = gr.Button("Generate All Videos")
|
| 82 |
+
with gr.Column():
|
| 83 |
+
file_output = gr.File(label="Download Processed Videos")
|
| 84 |
+
|
| 85 |
+
run_btn.click(fn=batch_ui_handler, inputs=[file_input, style_choice, dur_slider], outputs=file_output)
|
| 86 |
|
| 87 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|