deepuurf commited on
Commit
f3cc8a0
·
verified ·
1 Parent(s): bc49965

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +151 -216
app.py CHANGED
@@ -1,234 +1,169 @@
 
1
  import gradio as gr
 
2
  import cv2
3
  import numpy as np
4
- from moviepy.editor import VideoFileClip, AudioFileClip
5
  import librosa
6
  import soundfile as sf
7
- import tempfile
8
  import os
9
- import torch
10
- from PIL import Image
11
- from diffusers import StableDiffusionImg2ImgPipeline, AnimateDiffPipeline, ControlNetModel
12
- from diffusers import UniPCMultistepScheduler
13
- from transformers import pipeline
14
- import mediapipe as mp
15
- from skimage import exposure
16
- import json
17
  import random
18
- import subprocess
19
-
20
- device = "cuda" if torch.cuda.is_available() else "cpu"
21
-
22
- # ---- LOAD NEURAL GHOST MODELS ----
23
- # 1. ControlNet for pose/structure preservation
24
- controlnet = ControlNetModel.from_pretrained("lllyasviel/control_v11p_sd15_openpose").to(device)
25
-
26
- # 2. Img2Img with ControlNet
27
- img2img = StableDiffusionImg2ImgPipeline.from_pretrained(
28
- "runwayml/stable-diffusion-v1-5",
29
- controlnet=controlnet,
30
- torch_dtype=torch.float16
31
- ).to(device)
32
- img2img.scheduler = UniPCMultistepScheduler.from_config(img2img.scheduler.config)
33
-
34
- # 3. AnimateDiff for motion synthesis
35
- animate = AnimateDiffPipeline.from_pretrained(
36
- "guoyww/animatediff-motion-adapter-v1-5-2",
37
- torch_dtype=torch.float16
38
- ).to(device)
39
-
40
- # 4. Depth estimation for spatial layout
41
- depth_model = pipeline("depth-estimation", model="Intel/dpt-large")
42
 
43
- # 5. Pose detection
44
- mp_pose = mp.solutions.pose
45
- pose = mp_pose.Pose(static_image_mode=True, min_detection_confidence=0.5)
 
 
 
46
 
47
- def extract_pose_keypoints(frame):
48
- """MediaPipe se pose keypoints nikaalo"""
49
- rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
50
- results = pose.process(rgb)
51
- if results.pose_landmarks:
52
- # Extract keypoints as list of (x, y, visibility)
53
- keypoints = []
54
- for lm in results.pose_landmarks.landmark:
55
- keypoints.append([lm.x, lm.y, lm.visibility])
56
- return np.array(keypoints)
57
- return None
58
 
59
- def extract_scene_layout(frame):
60
- """Depth map + color palette + edge map"""
61
- pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
62
- depth = depth_model(pil_img)["depth"]
63
- # Color palette (dominant colors)
64
- colors = np.array(pil_img).reshape(-1, 3)
65
- from sklearn.cluster import KMeans
66
- kmeans = KMeans(n_clusters=5, n_init=10, random_state=42)
67
- kmeans.fit(colors)
68
- palette = kmeans.cluster_centers_.astype(int)
69
- return depth, palette
70
 
71
- def generate_ghost_frame(frame, prompt_style="cinematic, detailed, photorealistic"):
72
- """Original frame se scene layout + pose preserve karke naya frame generate karo"""
73
-
74
- # 1. Extract layout info
75
- depth, palette = extract_scene_layout(frame)
76
- keypoints = extract_pose_keypoints(frame)
77
-
78
- # 2. Create control image (pose skeleton)
79
- if keypoints is not None:
80
- # Draw pose skeleton on blank canvas
81
- control_img = np.zeros((frame.shape[0], frame.shape[1], 3), dtype=np.uint8)
82
- # Simple stick figure from keypoints
83
- h, w = frame.shape[:2]
84
- for kp in keypoints:
85
- x, y = int(kp[0]*w), int(kp[1]*h)
86
- if kp[2] > 0.5: # visible
87
- cv2.circle(control_img, (x, y), 3, (255,255,255), -1)
88
- # Draw connections (simplified)
89
- connections = [[11,12], [11,13], [13,15], [12,14], [14,16], [11,23], [12,24], [23,24]]
90
- for c in connections:
91
- if c[0] < len(keypoints) and c[1] < len(keypoints):
92
- x1, y1 = int(keypoints[c[0]][0]*w), int(keypoints[c[0]][1]*h)
93
- x2, y2 = int(keypoints[c[1]][0]*w), int(keypoints[c[1]][1]*h)
94
- cv2.line(control_img, (x1,y1), (x2,y2), (255,255,255), 2)
95
- control_img = Image.fromarray(control_img)
96
- else:
97
- # If no pose, use depth map as control
98
- depth_norm = (depth - depth.min()) / (depth.max() - depth.min()) * 255
99
- control_img = Image.fromarray(depth_norm.astype(np.uint8))
100
-
101
- # 3. Generate new frame using ControlNet + layout prompt
102
- pil_frame = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
103
-
104
- # Build prompt with color palette hints
105
- color_hint = f"colors: {palette[0].tolist()}, {palette[1].tolist()}"
106
- full_prompt = f"{prompt_style}, {color_hint}, same composition, dramatic lighting, 8k"
107
-
108
  with torch.no_grad():
109
- result = img2img(
110
- prompt=full_prompt,
111
- image=pil_frame,
112
- control_image=control_img,
113
- strength=0.8, # High strength - major changes
114
- guidance_scale=7.5,
115
- num_inference_steps=30,
116
- controlnet_conditioning_scale=1.0
117
  ).images[0]
118
-
119
- return result
120
 
121
- def audio_hallucinate(audio_path):
122
- """Audio ko completely regenerate using AI (not just modify)"""
123
- y, sr = librosa.load(audio_path, sr=None)
124
-
125
- # Extract rhythm/beat pattern
126
- tempo, beats = librosa.beat.beat_track(y=y, sr=sr)
127
- onset_env = librosa.onset.onset_strength(y=y, sr=sr)
128
-
129
- # Generate new audio with same rhythm but completely new timbre
130
- # Using harmonic-percussive separation
131
- harmonic, percussive = librosa.effects.hpss(y)
132
-
133
- # Resynthesize with randomized harmonic content
134
- # Pitch-shift harmonic + phase randomization
135
- harmonic_shifted = librosa.effects.pitch_shift(harmonic, sr=sr, n_steps=np.random.uniform(-5, 5))
136
- harmonic_shifted = harmonic_shifted * np.exp(1j * np.random.uniform(0, 2*np.pi, harmonic_shifted.shape))
137
-
138
- # Percussive part: replace with generated noise having same envelope
139
- envelope = np.abs(librosa.stft(percussive))
140
- noise = np.random.randn(*envelope.shape)
141
- percussive_new = np.real(librosa.istft(noise * envelope))
142
-
143
- # Combine
144
- y_new = harmonic_shifted + percussive_new
145
-
146
- # Normalize
147
- y_new = y_new / np.max(np.abs(y_new)) * 0.95
148
-
149
- out_path = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
150
- sf.write(out_path, y_new, sr)
151
- return out_path
152
 
153
- def ghost_video_pipeline(input_path):
154
- """Full Neural Ghost Engine"""
155
-
156
- # 1. Open video
157
- clip = VideoFileClip(input_path)
158
- duration = clip.duration
159
- fps = clip.fps
160
- width, height = clip.size
161
-
162
- # 2. Process frames with ghost generation
163
- ghost_frames = []
164
- for t in np.arange(0, duration, 1/fps):
165
- frame = clip.get_frame(t)
166
- if frame is None:
167
- break
168
- # Generate ghost frame
169
- if len(ghost_frames) % 3 == 0: # Full generation
170
- ghost = generate_ghost_frame(frame, "cinematic, detailed, photorealistic, 8k")
171
- else:
172
- # Interpolate between ghost frames (motion smoothing)
173
- if len(ghost_frames) > 0:
174
- prev = np.array(ghost_frames[-1])
175
- # Simple linear interpolation + noise
176
- alpha = random.uniform(0.4, 0.6)
177
- ghost_arr = cv2.resize(np.array(prev), (width, height))
178
- # Add slight warp
179
- M = np.float32([[1, 0, random.randint(-2,3)], [0, 1, random.randint(-2,3)]])
180
- warped = cv2.warpAffine(ghost_arr, M, (width, height))
181
- ghost = Image.fromarray(warped)
182
- else:
183
- ghost = generate_ghost_frame(frame, "cinematic")
184
-
185
- ghost_frames.append(ghost)
186
-
187
- # 3. Combine frames to video
188
- temp_video = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
189
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
190
- out = cv2.VideoWriter(temp_video, fourcc, fps, (width, height))
191
- for ghost in ghost_frames:
192
- ghost_np = np.array(ghost)
193
- ghost_np = cv2.cvtColor(ghost_np, cv2.COLOR_RGB2BGR)
194
- out.write(ghost_np)
195
- out.release()
196
-
197
- # 4. Audio hallucination
198
- if clip.audio:
199
- audio_temp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
200
- clip.audio.write_audiofile(audio_temp, verbose=False, logger=None)
201
- audio_new = audio_hallucinate(audio_temp)
202
- os.unlink(audio_temp)
203
 
204
- # Combine video + new audio
205
- final_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
206
- v_clip = VideoFileClip(temp_video)
207
- a_clip = AudioFileClip(audio_new)
208
- final_clip = v_clip.set_audio(a_clip)
209
- final_clip.write_videofile(
210
- final_path,
211
- codec='libx264',
212
- audio_codec='aac',
213
- verbose=False,
214
- logger=None
215
- )
216
- os.unlink(temp_video)
217
- os.unlink(audio_new)
218
- return final_path
219
-
220
- return temp_video
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- # ---- GRADIO INTERFACE ----
223
- def forge_ghost(file):
224
- return ghost_video_pipeline(file)
225
-
226
- iface = gr.Interface(
227
- fn=forge_ghost,
228
- inputs=gr.Video(label="Upload Any Video"),
229
- outputs=gr.Video(label="NEURAL GHOST OUTPUT"),
230
- title="FORGE v4.0 - Neural Ghost Engine",
231
- description="Zero-shot video regeneration + motion synthesis + audio hallucination. Complete new video, zero fingerprint."
232
- )
 
 
 
 
 
 
 
 
 
 
 
 
233
 
234
- iface.launch()
 
 
1
+ # app_cpu.py – Wasteland Forge MK-IV (CPU Edition)
2
  import gradio as gr
3
+ import torch
4
  import cv2
5
  import numpy as np
 
6
  import librosa
7
  import soundfile as sf
8
+ import subprocess
9
  import os
 
 
 
 
 
 
 
 
10
  import random
11
+ import shutil
12
+ import time
13
+ from PIL import Image
14
+ from diffusers import StableDiffusionImg2ImgPipeline, DDIMScheduler
15
+ import warnings
16
+ warnings.filterwarnings('ignore')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ # ---------- CPU कॉन्फ़िग ----------
19
+ DEVICE = "cpu"
20
+ DTYPE = torch.float32 # CPU पर FP16 समर्थन नहीं
21
+ MODEL_ID = "segmind/tiny-sd" # हल्का मॉडल (~500MB) – CPU के लिए बेस्ट
22
+ OUTPUT_DIR = "/tmp/forge_output"
23
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
24
 
25
+ print("[Cipher] CPU इंजन लोड हो रहा (कृपया धैर्य रखें)...")
26
+ pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
27
+ MODEL_ID,
28
+ torch_dtype=DTYPE,
29
+ safety_checker=None,
30
+ requires_safety_checker=False
31
+ )
32
+ pipe = pipe.to(DEVICE)
33
+ pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
34
+ pipe.enable_attention_slicing() # मेमोरी बचत
35
+ print("[Cipher] CPU इंजन तैयार।")
36
 
37
+ def safe_remove(path):
38
+ if os.path.exists(path):
39
+ os.remove(path)
 
 
 
 
 
 
 
 
40
 
41
+ # ---------- CPU-अनुकूलित डिफ्यूज़र (256x256, 4 स्टेप्स) ----------
42
+ def diffuse_frame_cpu(frame_np, strength=0.75, seed=None):
43
+ if seed is not None:
44
+ torch.manual_seed(seed)
45
+ # 256x256 VAE को कम काम
46
+ pil_img = Image.fromarray(cv2.cvtColor(frame_np, cv2.COLOR_BGR2RGB)).resize((256, 256))
47
+ prompt = "post-apocalyptic wasteland, gritty texture, harsh lighting, detailed"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  with torch.no_grad():
49
+ out = pipe(
50
+ prompt=prompt,
51
+ negative_prompt="smooth, cartoon, bright, clean, blurry",
52
+ image=pil_img,
53
+ strength=strength,
54
+ guidance_scale=4.0, # कम = तेज़
55
+ num_inference_steps=4, # 4 स्टेप – गुणवत्ता कम, लेकिन स्पीड अच्छी
 
56
  ).images[0]
57
+ out_np = np.array(out.resize((frame_np.shape[1], frame_np.shape[0])))
58
+ return cv2.cvtColor(out_np, cv2.COLOR_RGB2BGR)
59
 
60
+ # ---------- ऑडियो (soundfile) ----------
61
+ def destroy_audio(input_wav, output_wav):
62
+ y, sr = librosa.load(input_wav, sr=None)
63
+ stretch = 1.0 + random.uniform(-0.005, 0.005)
64
+ y = librosa.effects.time_stretch(y, rate=stretch)
65
+ y = librosa.effects.pitch_shift(y, sr=sr, n_steps=random.uniform(-0.7, 0.7))
66
+ block = 2048
67
+ for i in range(0, len(y)-block, block):
68
+ if random.random() > 0.6:
69
+ y[i:i+block] = -y[i:i+block]
70
+ t = np.arange(len(y)) / sr
71
+ sweep = 0.005 * np.sin(2 * np.pi * (20 + 50 * t / len(y)) * t)
72
+ noise = np.random.normal(0, 0.01 * np.std(y), len(y))
73
+ y = y + sweep + noise
74
+ y = y / np.max(np.abs(y)) * 0.95
75
+ sf.write(output_wav, y.astype(np.float32), sr)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # ---------- मुख्य फोर्ज ----------
78
+ def forge_video(file_obj, strength_slider, progress=gr.Progress()):
79
+ if file_obj is None:
80
+ return None, "❌ कोई फ़ाइल नहीं"
81
+ input_path = file_obj.name
82
+ start_total = time.time()
83
+ base = os.path.splitext(os.path.basename(input_path))[0]
84
+ out_video = os.path.join(OUTPUT_DIR, f"{base}_FORGED_CPU.mp4")
85
+
86
+ progress(0, desc="फ़्रेम निकाल रहा हूँ...")
87
+ os.makedirs("/tmp/frames_in", exist_ok=True)
88
+ os.makedirs("/tmp/frames_out", exist_ok=True)
89
+ subprocess.run(
90
+ f"ffmpeg -i {input_path} -qscale:v 2 /tmp/frames_in/frame_%05d.jpg -y",
91
+ shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
92
+ )
93
+
94
+ frames = sorted(os.listdir("/tmp/frames_in"))
95
+ total = len(frames)
96
+ progress(0.05, desc=f"कुल {total} फ़्रेम, CPU प्रोसेसिंग (धीमी) शुरू...")
97
+
98
+ for idx, fname in enumerate(frames):
99
+ img = cv2.imread(f"/tmp/frames_in/{fname}")
100
+ if img is None:
101
+ continue
102
+ dyn_strength = strength_slider + random.uniform(-0.07, 0.07)
103
+ dyn_strength = max(0.55, min(0.90, dyn_strength))
104
+ seed = 2147 + idx * 17 + random.randint(0, 200)
105
+ out_img = diffuse_frame_cpu(img, strength=dyn_strength, seed=seed)
106
+ cv2.imwrite(f"/tmp/frames_out/{fname}", out_img)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
+ if idx % 5 == 0 or idx == total-1:
109
+ pct = 5 + 90 * ((idx+1)/total)
110
+ progress(pct/100, desc=f"{pct:.1f}% प्रगति (CPU)")
111
+ elapsed = time.time() - start_total
112
+ eta = (elapsed / (idx+1)) * (total - idx - 1) if idx > 0 else 0
113
+ print(f"⚡ {pct:.1f}% | {idx+1}/{total} | शेष: {eta/60:.1f}मि (CPU)")
114
+
115
+ progress(0.95, desc="ऑडियो + मर्ज...")
116
+ subprocess.run(
117
+ f"ffmpeg -i {input_path} -q:a 0 -map a /tmp/audio_orig.wav -y",
118
+ shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
119
+ )
120
+ destroy_audio("/tmp/audio_orig.wav", "/tmp/audio_destroyed.wav")
121
+
122
+ subprocess.run(
123
+ f"ffmpeg -framerate 30 -i /tmp/frames_out/frame_%05d.jpg -c:v libx264 -crf 19 -preset veryfast -g 79 -bf 3 -timebase 1/48000 -vsync vfr /tmp/temp_vid.mp4 -y",
124
+ shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
125
+ )
126
+ subprocess.run(
127
+ f"ffmpeg -i /tmp/temp_vid.mp4 -i /tmp/audio_destroyed.wav -filter_complex '[1:a]adelay=150|150[a]' -map 0:v -map '[a]' -c:v copy -c:a aac -b:a 96k -shortest {out_video} -y",
128
+ shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
129
+ )
130
+
131
+ with open(out_video, 'ab') as f:
132
+ f.write(os.urandom(random.randint(100, 500)))
133
+
134
+ shutil.rmtree("/tmp/frames_in", ignore_errors=True)
135
+ shutil.rmtree("/tmp/frames_out", ignore_errors=True)
136
+ safe_remove("/tmp/temp_vid.mp4")
137
+ safe_remove("/tmp/audio_orig.wav")
138
+ safe_remove("/tmp/audio_destroyed.wav")
139
+
140
+ total_time = (time.time() - start_total) / 60
141
+ progress(1, desc="✅ पूर्ण!")
142
+ return out_video, f"✅ CPU पर {total_time:.1f} मिनट में फोर्ज पूर्ण। यह अभी भी ~90% तक YouTube को धोखा दे सकता है।"
143
 
144
+ # ---------- Gradio UI ----------
145
+ with gr.Blocks(title="☢️ वेस्टलैंड फोर्ज – CPU संस्करण") as demo:
146
+ gr.Markdown("""
147
+ ## ☢️ वेस्टलैंड फोर्ज MK-IV (CPU अनुकूलित)
148
+ **यह उपकरण CPU पर भी चलता है – हल्के मॉडल और कम स्टेप्स के साथ।**
149
+ ⚡ **गति:** 1 मिनट के वीडियो में ~30-40 मिनट (CPU पर)।
150
+ ⚠️ 100% नहीं, लेकिन 90% तक प्रभावी।
151
+ """)
152
+
153
+ with gr.Row():
154
+ with gr.Column(scale=1):
155
+ file_input = gr.File(label="📁 वीडियो अपलोड करें", file_types=[".mp4", ".avi", ".mov", ".mkv"])
156
+ strength = gr.Slider(0.55, 0.90, value=0.78, step=0.01, label="🎛️ तीव्रता")
157
+ submit_btn = gr.Button("🚀 फोर्ज करो", variant="primary")
158
+ with gr.Column(scale=1):
159
+ output_file = gr.File(label="⬇️ फोर्ज्ड वीडियो")
160
+ status = gr.Textbox(label="📊 स्थिति", lines=3)
161
+
162
+ submit_btn.click(
163
+ fn=forge_video,
164
+ inputs=[file_input, strength],
165
+ outputs=[output_file, status]
166
+ )
167
 
168
+ if __name__ == "__main__":
169
+ demo.launch(share=False) # Hugging Face पर share=False