KingNish commited on
Commit
9e41d90
1 Parent(s): 3285d76

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +84 -106
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import gradio as gr
2
  from loadimg import load_img
3
  import spaces
4
  from transformers import AutoModelForImageSegmentation
@@ -12,8 +12,8 @@ import os
12
  import tempfile
13
  import uuid
14
  import time
15
- import threading
16
  from concurrent.futures import ThreadPoolExecutor
 
17
 
18
  torch.set_float32_matmul_precision("medium")
19
  device = "cuda" if torch.cuda.is_available() else "cpu"
@@ -26,115 +26,92 @@ birefnet_lite = AutoModelForImageSegmentation.from_pretrained(
26
  "ZhengPeng7/BiRefNet_lite", trust_remote_code=True)
27
  birefnet_lite.to(device)
28
 
29
- transform_image = transforms.Compose(
30
- [
31
- transforms.Resize((1024, 1024)),
32
- transforms.ToTensor(),
33
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
34
- ]
35
- )
36
-
37
- # Function to process a single frame
38
- def process_frame(frame, bg_type, bg, fast_mode, bg_frame_index, background_frames, color):
39
- try:
40
- pil_image = Image.fromarray(frame)
41
- if bg_type == "Color":
42
- processed_image = process(pil_image, color, fast_mode)
43
- elif bg_type == "Image":
44
- processed_image = process(pil_image, bg, fast_mode)
45
- elif bg_type == "Video":
46
- background_frame = background_frames[bg_frame_index % len(background_frames)]
47
- bg_frame_index += 1
48
- background_image = Image.fromarray(background_frame)
49
- processed_image = process(pil_image, background_image, fast_mode)
50
- else:
51
- processed_image = pil_image # Default to original image if no background is selected
52
- return np.array(processed_image), bg_frame_index
53
- except Exception as e:
54
- print(f"Error processing frame: {e}")
55
- return frame, bg_frame_index
56
 
57
  @spaces.GPU
58
- def fn(vid, bg_type="Color", bg_image=None, bg_video=None, color="#00FF00", fps=0, video_handling="slow_down", fast_mode=True, max_workers=6):
59
- try:
60
- start_time = time.time() # Start the timer
61
-
62
- # Load the video using moviepy
63
- video = mp.VideoFileClip(vid)
64
-
65
- # Load original fps if fps value is equal to 0
66
- if fps == 0:
67
- fps = video.fps
68
-
69
- # Extract audio from the video
70
- audio = video.audio
71
-
72
- # Extract frames at the specified FPS
73
- frames = list(video.iter_frames(fps=fps))
74
-
75
- # Process each frame for background removal
76
- processed_frames = []
77
- yield gr.update(visible=True), gr.update(visible=False), f"Processing started... Elapsed time: 0 seconds"
78
-
79
- if bg_type == "Video":
80
- background_video = mp.VideoFileClip(bg_video)
81
- if background_video.duration < video.duration:
82
- if video_handling == "slow_down":
83
- background_video = background_video.fx(mp.vfx.speedx, factor=video.duration / background_video.duration)
84
- else: # video_handling == "loop"
85
- background_video = mp.concatenate_videoclips([background_video] * int(video.duration / background_video.duration + 1))
86
- background_frames = list(background_video.iter_frames(fps=fps)) # Convert to list
87
- else:
88
- background_frames = None
89
-
90
- bg_frame_index = 0 # Initialize background frame index
91
-
92
- # Use ThreadPoolExecutor for parallel processing with specified max_workers
93
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
94
- futures = [executor.submit(process_frame, frames[i], bg_type, bg_image, fast_mode, bg_frame_index, background_frames, color) for i in range(len(frames))]
95
- for future in futures:
96
- result, bg_frame_index = future.result()
97
- processed_frames.append(result)
98
- elapsed_time = time.time() - start_time
99
- yield result, None, f"Processing frame {len(processed_frames)}... Elapsed time: {elapsed_time:.2f} seconds"
100
-
101
- # Create a new video from the processed frames
102
- processed_video = mp.ImageSequenceClip(processed_frames, fps=fps)
103
-
104
- # Add the original audio back to the processed video
105
- processed_video = processed_video.set_audio(audio)
106
-
107
- # Save the processed video to a temporary file using tempfile
108
- with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
109
- temp_filepath = temp_file.name
110
- processed_video.write_videofile(temp_filepath, codec="libx264")
111
-
112
- elapsed_time = time.time() - start_time
113
- yield gr.update(visible=False), gr.update(visible=True), f"Processing complete! Elapsed time: {elapsed_time:.2f} seconds"
114
 
115
- # Return the path to the temporary file
116
- yield processed_frames[-1], temp_filepath, f"Processing complete! Elapsed time: {elapsed_time:.2f} seconds"
117
 
118
- except Exception as e:
119
- print(f"Error: {e}")
 
 
 
 
 
 
 
 
 
 
120
  elapsed_time = time.time() - start_time
121
- yield gr.update(visible=False), gr.update(visible=True), f"Error processing video: {e}. Elapsed time: {elapsed_time:.2f} seconds"
122
- yield None, f"Error processing video: {e}", f"Error processing video: {e}. Elapsed time: {elapsed_time:.2f} seconds"
 
 
 
 
 
 
 
 
 
 
123
 
124
  def process(image, bg, fast_mode=False):
125
  image_size = image.size
126
- input_images = transform_image(image).unsqueeze(0).to("cuda")
127
-
128
- # Select the model based on fast_mode
129
  model = birefnet_lite if fast_mode else birefnet
130
-
131
- # Prediction
132
  with torch.no_grad():
133
  preds = model(input_images)[-1].sigmoid().cpu()
134
  pred = preds[0].squeeze()
135
  pred_pil = transforms.ToPILImage()(pred)
136
  mask = pred_pil.resize(image_size)
137
-
138
  if isinstance(bg, str) and bg.startswith("#"):
139
  color_rgb = tuple(int(bg[i:i+2], 16) for i in (1, 3, 5))
140
  background = Image.new("RGBA", image_size, color_rgb + (255,))
@@ -142,18 +119,20 @@ def process(image, bg, fast_mode=False):
142
  background = bg.convert("RGBA").resize(image_size)
143
  else:
144
  background = Image.open(bg).convert("RGBA").resize(image_size)
145
-
146
- # Composite the image onto the background using the mask
147
  image = Image.composite(image, background, mask)
148
  return image
149
 
150
  with gr.Blocks(theme=gr.themes.Ocean()) as demo:
151
  gr.Markdown("# Video Background Remover & Changer\n### You can replace image background with any color, image or video.\nNOTE: As this Space is running on ZERO GPU it has limit. It can handle approx 200 frames at once. So, if you have a big video than use small chunks or Duplicate this space.")
 
152
  with gr.Row():
153
  in_video = gr.Video(label="Input Video", interactive=True)
154
  stream_image = gr.Image(label="Streaming Output", visible=False)
155
  out_video = gr.Video(label="Final Output Video")
 
156
  submit_button = gr.Button("Change Background", interactive=True)
 
157
  with gr.Row():
158
  fps_slider = gr.Slider(
159
  minimum=0,
@@ -167,12 +146,14 @@ with gr.Blocks(theme=gr.themes.Ocean()) as demo:
167
  color_picker = gr.ColorPicker(label="Background Color", value="#00FF00", visible=True, interactive=True)
168
  bg_image = gr.Image(label="Background Image", type="filepath", visible=False, interactive=True)
169
  bg_video = gr.Video(label="Background Video", visible=False, interactive=True)
 
170
  with gr.Column(visible=False) as video_handling_options:
171
  video_handling_radio = gr.Radio(["slow_down", "loop"], label="Video Handling", value="slow_down", interactive=True)
 
172
  fast_mode_checkbox = gr.Checkbox(label="Fast Mode (Use BiRefNet_lite)", value=True, interactive=True)
173
- max_workers_slider = gr.Slider( minimum=1, maximum=32, step=1, value=6, label="Max Workers", info="Determines how many Franes to process parallel", interactive=True
174
- )
175
- time_textbox = gr.Textbox(label="Time Elapsed", interactive=False) # Add time textbox
176
 
177
  def update_visibility(bg_type):
178
  if bg_type == "Color":
@@ -184,10 +165,8 @@ with gr.Blocks(theme=gr.themes.Ocean()) as demo:
184
  else:
185
  return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
186
 
187
-
188
  bg_type.change(update_visibility, inputs=bg_type, outputs=[color_picker, bg_image, bg_video, video_handling_options])
189
 
190
-
191
  examples = gr.Examples(
192
  [
193
  ["rickroll-2sec.mp4", "Video", None, "background.mp4"],
@@ -201,7 +180,6 @@ with gr.Blocks(theme=gr.themes.Ocean()) as demo:
201
  cache_mode="eager",
202
  )
203
 
204
-
205
  submit_button.click(
206
  fn,
207
  inputs=[in_video, bg_type, bg_image, bg_video, color_picker, fps_slider, video_handling_radio, fast_mode_checkbox, max_workers_slider],
@@ -209,4 +187,4 @@ with gr.Blocks(theme=gr.themes.Ocean()) as demo:
209
  )
210
 
211
  if __name__ == "__main__":
212
- demo.launch(show_error=True)
 
1
+ import gradio as gr
2
  from loadimg import load_img
3
  import spaces
4
  from transformers import AutoModelForImageSegmentation
 
12
  import tempfile
13
  import uuid
14
  import time
 
15
  from concurrent.futures import ThreadPoolExecutor
16
+ import asyncio
17
 
18
  torch.set_float32_matmul_precision("medium")
19
  device = "cuda" if torch.cuda.is_available() else "cpu"
 
26
  "ZhengPeng7/BiRefNet_lite", trust_remote_code=True)
27
  birefnet_lite.to(device)
28
 
29
+ transform_image = transforms.Compose([
30
+ transforms.Resize((1024, 1024)),
31
+ transforms.ToTensor(),
32
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
33
+ ])
34
+
35
+ # Function to process a single frame asynchronously
36
+ async def process_frame_async(frame, bg_type, bg, fast_mode, bg_frame_index, background_frames, color):
37
+ pil_image = Image.fromarray(frame)
38
+ if bg_type == "Color":
39
+ processed_image = process(pil_image, color, fast_mode)
40
+ elif bg_type == "Image":
41
+ processed_image = process(pil_image, bg, fast_mode)
42
+ elif bg_type == "Video":
43
+ background_frame = background_frames[bg_frame_index % len(background_frames)]
44
+ bg_frame_index += 1
45
+ background_image = Image.fromarray(background_frame)
46
+ processed_image = process(pil_image, background_image, fast_mode)
47
+ else:
48
+ processed_image = pil_image # Default to original image if no background is selected
49
+ return np.array(processed_image), bg_frame_index
 
 
 
 
 
 
50
 
51
  @spaces.GPU
52
+ async def fn(vid, bg_type="Color", bg_image=None, bg_video=None, color="#00FF00", fps=0, video_handling="slow_down", fast_mode=True, max_workers=6):
53
+ start_time = time.time() # Start the timer
54
+
55
+ video = mp.VideoFileClip(vid)
56
+ if fps == 0:
57
+ fps = video.fps
58
+
59
+ audio = video.audio
60
+ frames = list(video.iter_frames(fps=fps))
61
+
62
+ processed_frames = []
63
+ yield gr.update(visible=True), gr.update(visible=False), f"Processing started... Elapsed time: 0 seconds"
64
+
65
+ if bg_type == "Video":
66
+ background_video = mp.VideoFileClip(bg_video)
67
+ if background_video.duration < video.duration:
68
+ if video_handling == "slow_down":
69
+ background_video = background_video.fx(mp.vfx.speedx, factor=video.duration / background_video.duration)
70
+ else: # video_handling == "loop"
71
+ background_video = mp.concatenate_videoclips([background_video] * int(video.duration / background_video.duration + 1))
72
+ background_frames = list(background_video.iter_frames(fps=fps))
73
+ else:
74
+ background_frames = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ bg_frame_index = 0
 
77
 
78
+ # Use ThreadPoolExecutor for parallel processing with specified max_workers
79
+ loop = asyncio.get_event_loop()
80
+ tasks = [
81
+ loop.run_in_executor(
82
+ None, process_frame_async, frames[i], bg_type, bg_image, fast_mode, bg_frame_index, background_frames, color
83
+ )
84
+ for i in range(len(frames))
85
+ ]
86
+
87
+ for future in asyncio.as_completed(tasks):
88
+ result, bg_frame_index = await future
89
+ processed_frames.append(result)
90
  elapsed_time = time.time() - start_time
91
+ yield result, None, f"Processing frame {len(processed_frames)}... Elapsed time: {elapsed_time:.2f} seconds"
92
+
93
+ processed_video = mp.ImageSequenceClip(processed_frames, fps=fps)
94
+ processed_video = processed_video.set_audio(audio)
95
+
96
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
97
+ temp_filepath = temp_file.name
98
+ processed_video.write_videofile(temp_filepath, codec="libx264")
99
+
100
+ elapsed_time = time.time() - start_time
101
+ yield gr.update(visible=False), gr.update(visible=True), f"Processing complete! Elapsed time: {elapsed_time:.2f} seconds"
102
+ yield processed_frames[-1], temp_filepath, f"Processing complete! Elapsed time: {elapsed_time:.2f} seconds"
103
 
104
  def process(image, bg, fast_mode=False):
105
  image_size = image.size
106
+ input_images = transform_image(image).unsqueeze(0).to(device)
 
 
107
  model = birefnet_lite if fast_mode else birefnet
108
+
 
109
  with torch.no_grad():
110
  preds = model(input_images)[-1].sigmoid().cpu()
111
  pred = preds[0].squeeze()
112
  pred_pil = transforms.ToPILImage()(pred)
113
  mask = pred_pil.resize(image_size)
114
+
115
  if isinstance(bg, str) and bg.startswith("#"):
116
  color_rgb = tuple(int(bg[i:i+2], 16) for i in (1, 3, 5))
117
  background = Image.new("RGBA", image_size, color_rgb + (255,))
 
119
  background = bg.convert("RGBA").resize(image_size)
120
  else:
121
  background = Image.open(bg).convert("RGBA").resize(image_size)
122
+
 
123
  image = Image.composite(image, background, mask)
124
  return image
125
 
126
  with gr.Blocks(theme=gr.themes.Ocean()) as demo:
127
  gr.Markdown("# Video Background Remover & Changer\n### You can replace image background with any color, image or video.\nNOTE: As this Space is running on ZERO GPU it has limit. It can handle approx 200 frames at once. So, if you have a big video than use small chunks or Duplicate this space.")
128
+
129
  with gr.Row():
130
  in_video = gr.Video(label="Input Video", interactive=True)
131
  stream_image = gr.Image(label="Streaming Output", visible=False)
132
  out_video = gr.Video(label="Final Output Video")
133
+
134
  submit_button = gr.Button("Change Background", interactive=True)
135
+
136
  with gr.Row():
137
  fps_slider = gr.Slider(
138
  minimum=0,
 
146
  color_picker = gr.ColorPicker(label="Background Color", value="#00FF00", visible=True, interactive=True)
147
  bg_image = gr.Image(label="Background Image", type="filepath", visible=False, interactive=True)
148
  bg_video = gr.Video(label="Background Video", visible=False, interactive=True)
149
+
150
  with gr.Column(visible=False) as video_handling_options:
151
  video_handling_radio = gr.Radio(["slow_down", "loop"], label="Video Handling", value="slow_down", interactive=True)
152
+
153
  fast_mode_checkbox = gr.Checkbox(label="Fast Mode (Use BiRefNet_lite)", value=True, interactive=True)
154
+ max_workers_slider = gr.Slider( minimum=1, maximum=32, step=1, value=6, label="Max Workers", info="Determines how many frames to process in parallel", interactive=True )
155
+
156
+ time_textbox = gr.Textbox(label="Time Elapsed", interactive=False)
157
 
158
  def update_visibility(bg_type):
159
  if bg_type == "Color":
 
165
  else:
166
  return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
167
 
 
168
  bg_type.change(update_visibility, inputs=bg_type, outputs=[color_picker, bg_image, bg_video, video_handling_options])
169
 
 
170
  examples = gr.Examples(
171
  [
172
  ["rickroll-2sec.mp4", "Video", None, "background.mp4"],
 
180
  cache_mode="eager",
181
  )
182
 
 
183
  submit_button.click(
184
  fn,
185
  inputs=[in_video, bg_type, bg_image, bg_video, color_picker, fps_slider, video_handling_radio, fast_mode_checkbox, max_workers_slider],
 
187
  )
188
 
189
  if __name__ == "__main__":
190
+ demo.launch(show_error=True)