parokshsaxena commited on
Commit
cbe97f0
β€’
1 Parent(s): 1dddd5f

using remove bg to add background to the output image

Browse files
app.py CHANGED
@@ -261,7 +261,7 @@ def start_tryon(dict,garm_img,garment_des, background_img, is_checked,is_checked
261
  # apply background to final image
262
  if background_img:
263
  logging.info("Adding background")
264
- final_image = BackgroundProcessor.add_background_v3(final_image, background_img)
265
  return final_image, mask_gray
266
  # return images[0], mask_gray
267
 
 
261
  # apply background to final image
262
  if background_img:
263
  logging.info("Adding background")
264
+ final_image = BackgroundProcessor.replace_background_with_removebg(final_image, background_img)
265
  return final_image, mask_gray
266
  # return images[0], mask_gray
267
 
src/background_processor.py CHANGED
@@ -1,11 +1,18 @@
 
 
 
1
  from PIL import Image, ImageEnhance
2
  import cv2
3
  import numpy as np
4
  from preprocess.humanparsing.run_parsing import Parsing
 
 
 
5
 
6
  parsing_model = Parsing(0)
7
 
8
  class BackgroundProcessor:
 
9
  @classmethod
10
  def add_background(cls, human_img: Image, background_img: Image):
11
 
@@ -38,59 +45,7 @@ class BackgroundProcessor:
38
  # Return or save the result
39
  return result_img
40
 
41
- @classmethod
42
- def temp_v2(cls, human_img_path, background_img_path, mask_img_path):
43
- # Load the images
44
- foreground_img = cv2.imread(human_img_path).resize((768,1024)) # The segmented person image
45
- background_img = cv2.imread(background_img_path) # The new background image
46
- mask_img = cv2.imread(mask_img_path, cv2.IMREAD_GRAYSCALE) # The mask image from the human parser model
47
-
48
- # Ensure the foreground image and the mask are the same size
49
- if foreground_img.shape[:2] != mask_img.shape[:2]:
50
- raise ValueError("Foreground image and mask must be the same size")
51
-
52
- # Resize background image to match the size of the foreground image
53
- background_img = cv2.resize(background_img, (foreground_img.shape[1], foreground_img.shape[0]))
54
-
55
- # Create an inverted mask
56
- mask_inv = cv2.bitwise_not(mask_img)
57
-
58
- # Convert mask to 3 channels
59
- mask_3ch = cv2.cvtColor(mask_img, cv2.COLOR_GRAY2BGR)
60
- mask_inv_3ch = cv2.cvtColor(mask_inv, cv2.COLOR_GRAY2BGR)
61
-
62
- # Extract the person from the foreground image using the mask
63
- person = cv2.bitwise_and(foreground_img, mask_3ch)
64
-
65
- # Extract the background where the person is not present
66
- background = cv2.bitwise_and(background_img, mask_inv_3ch)
67
-
68
- # Combine the person and the new background
69
- combined_img = cv2.add(person, background)
70
-
71
- # Refine edges using Gaussian Blur (feathering technique)
72
- blurred_combined_img = cv2.GaussianBlur(combined_img, (5, 5), 0)
73
-
74
- # Post-processing: Adjust brightness, contrast, etc. (optional)
75
- alpha = 1.2 # Contrast control (1.0-3.0)
76
- beta = 20 # Brightness control (0-100)
77
-
78
- post_processed_img = cv2.convertScaleAbs(blurred_combined_img, alpha=alpha, beta=beta)
79
-
80
- # Save the final image
81
- # cv2.imwrite('path_to_save_final_image.png', post_processed_img)
82
-
83
- # Display the images (optional)
84
- cv2.imshow('Foreground', foreground_img)
85
- cv2.imshow('Background', background_img)
86
- cv2.imshow('Mask', mask_img)
87
- cv2.imshow('Combined', combined_img)
88
- cv2.imshow('Post Processed', post_processed_img)
89
- cv2.waitKey(0)
90
- cv2.destroyAllWindows()
91
- return post_processed_img
92
-
93
-
94
  @classmethod
95
  def add_background_v3(cls, foreground_pil: Image, background_pil: Image):
96
  foreground_pil= foreground_pil.convert("RGB")
@@ -120,8 +75,8 @@ class BackgroundProcessor:
120
  #mask_pil = mask_pil.resize(foreground_pil.size)
121
 
122
  # Convert PIL images to OpenCV format
123
- foreground_cv2 = cls.pil_to_cv2(foreground_pil)
124
- background_cv2 = cls.pil_to_cv2(background_pil)
125
  #mask_cv2 = pil_to_cv2(mask_pil)
126
  mask_cv2 = np.array(mask_pil) # Directly convert to NumPy array without color conversion
127
 
@@ -156,7 +111,7 @@ class BackgroundProcessor:
156
  blurred_combined_cv2 = cv2.GaussianBlur(combined_cv2, (5, 5), 0)
157
 
158
  # Convert the result back to PIL format
159
- combined_pil = cls.cv2_to_pil(blurred_combined_cv2)
160
 
161
 
162
  """
@@ -180,12 +135,20 @@ class BackgroundProcessor:
180
 
181
  return combined_pil
182
 
 
183
  @classmethod
184
  def replace_background(cls, foreground_img_path: str, background_img_path: str):
185
  # Load the input image (with alpha channel) and the background image
186
- #input_image = cv2.imread(foreground_img_path, cv2.IMREAD_UNCHANGED)
187
- input_image = cv2.imread(foreground_img_path)
188
- background_image = cv2.imread(background_img_path)
 
 
 
 
 
 
 
189
 
190
  # Ensure the input image has an alpha channel
191
  if input_image.shape[2] != 4:
@@ -203,34 +166,63 @@ class BackgroundProcessor:
203
 
204
  # Extract the BGR channels of the input image
205
  input_bgr = input_image[:, :, :3]
206
-
207
  # Blend the images using the alpha channel
208
  foreground = cv2.multiply(alpha_channel_3ch, input_bgr.astype(float))
209
- background = cv2.multiply(1.0 - alpha_channel_3ch, background_image.astype(float))
210
  combined_image = cv2.add(foreground, background).astype(np.uint8)
211
 
212
  # Save and display the result
213
  cv2.imwrite('path_to_save_combined_image.png', combined_image)
214
  cv2.imshow('Combined Image', combined_image)
215
  cv2.waitKey(0)
 
216
  cv2.destroyAllWindows()
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
 
220
- # Function to convert PIL Image to OpenCV format
221
- @classmethod
222
- def pil_to_cv2(cls, pil_image):
223
- open_cv_image = np.array(pil_image)
224
- # Convert RGB to BGR if it's a 3-channel image
225
- if len(open_cv_image.shape) == 3:
226
- open_cv_image = open_cv_image[:, :, ::-1].copy()
227
- return open_cv_image
228
-
229
- # Function to convert OpenCV format to PIL Image
230
  @classmethod
231
- def cv2_to_pil(cls, cv2_image):
232
- # Convert BGR to RGB if it's a 3-channel image
233
- if len(cv2_image.shape) == 3:
234
- cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB)
235
- pil_image = Image.fromarray(cv2_image)
236
- return pil_image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import logging
4
  from PIL import Image, ImageEnhance
5
  import cv2
6
  import numpy as np
7
  from preprocess.humanparsing.run_parsing import Parsing
8
+ from src.image_format_convertor import ImageFormatConvertor
9
+
10
+ REMOVE_BG_KEY = os.getenv('REMOVE_BG_KEY', "8XHtXvvhWFBpAA6jVt3yzVmh")
11
 
12
  parsing_model = Parsing(0)
13
 
14
  class BackgroundProcessor:
15
+ DeprecationWarning("Created only for testing. Not in use")
16
  @classmethod
17
  def add_background(cls, human_img: Image, background_img: Image):
18
 
 
45
  # Return or save the result
46
  return result_img
47
 
48
+ DeprecationWarning("Created only for testing. Not in use")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  @classmethod
50
  def add_background_v3(cls, foreground_pil: Image, background_pil: Image):
51
  foreground_pil= foreground_pil.convert("RGB")
 
75
  #mask_pil = mask_pil.resize(foreground_pil.size)
76
 
77
  # Convert PIL images to OpenCV format
78
+ foreground_cv2 = ImageFormatConvertor.pil_to_cv2(foreground_pil)
79
+ background_cv2 = ImageFormatConvertor.pil_to_cv2(background_pil)
80
  #mask_cv2 = pil_to_cv2(mask_pil)
81
  mask_cv2 = np.array(mask_pil) # Directly convert to NumPy array without color conversion
82
 
 
111
  blurred_combined_cv2 = cv2.GaussianBlur(combined_cv2, (5, 5), 0)
112
 
113
  # Convert the result back to PIL format
114
+ combined_pil = ImageFormatConvertor.cv2_to_pil(blurred_combined_cv2)
115
 
116
 
117
  """
 
135
 
136
  return combined_pil
137
 
138
+ DeprecationWarning("Created only for testing. Not in use")
139
  @classmethod
140
  def replace_background(cls, foreground_img_path: str, background_img_path: str):
141
  # Load the input image (with alpha channel) and the background image
142
+ #input_image = cv2.imread(foreground_img_path, cv2.IMREAD_UNCHANGED)
143
+ # background_image = cv2.imread(background_img_path)
144
+ foreground_img_pil = Image.open(foreground_img_path)
145
+ width = foreground_img_pil.width
146
+ height = foreground_img_pil.height
147
+ background_image_pil = Image.open(background_img_path)
148
+ background_image_pil = background_image_pil.resize((width, height))
149
+ input_image = ImageFormatConvertor.pil_to_cv2(foreground_img_pil)
150
+ background_image = ImageFormatConvertor.pil_to_cv2(background_image_pil)
151
+
152
 
153
  # Ensure the input image has an alpha channel
154
  if input_image.shape[2] != 4:
 
166
 
167
  # Extract the BGR channels of the input image
168
  input_bgr = input_image[:, :, :3]
169
+ background_bgr = background_image[:,:,:3]
170
  # Blend the images using the alpha channel
171
  foreground = cv2.multiply(alpha_channel_3ch, input_bgr.astype(float))
172
+ background = cv2.multiply(1.0 - alpha_channel_3ch, background_bgr.astype(float))
173
  combined_image = cv2.add(foreground, background).astype(np.uint8)
174
 
175
  # Save and display the result
176
  cv2.imwrite('path_to_save_combined_image.png', combined_image)
177
  cv2.imshow('Combined Image', combined_image)
178
  cv2.waitKey(0)
179
+
180
  cv2.destroyAllWindows()
181
 
182
+ @classmethod
183
+ def replace_background_with_removebg(cls, foreground_img_pil: Image, background_image_pil: Image):
184
+ foreground_img_pil= foreground_img_pil.convert("RGB")
185
+ width = foreground_img_pil.width
186
+ height = foreground_img_pil.height
187
+
188
+ # Resize background image
189
+ background_image_pil = background_image_pil.convert("RGB")
190
+ background_image_pil = background_image_pil.resize((width, height))
191
+
192
+ #foreground_img_pil = Image.open(foreground_img_path)
193
+ #width = foreground_img_pil.width
194
+ #height = foreground_img_pil.height
195
+ #background_image_pil = Image.open(background_img_path)
196
+ #background_image_pil = background_image_pil.resize((width, height))
197
+
198
+ foreground_binary = ImageFormatConvertor.pil_image_to_binary_data(foreground_img_pil)
199
+ background_binary = ImageFormatConvertor.pil_image_to_binary_data(background_image_pil)
200
+ combined_img_pil = cls.remove_bg(foreground_binary, background_binary)
201
+ combined_img_pil.show()
202
+ return combined_img_pil
203
 
204
 
 
 
 
 
 
 
 
 
 
 
205
  @classmethod
206
+ def remove_bg(cls, foreground_binary: str, background_binary: str):
207
+ # ref: https://www.remove.bg/api#api-reference
208
+ url = "https://api.remove.bg/v1.0/removebg"
209
+
210
+ # using form-data as passing binary data is not supported in application/json
211
+ files = {
212
+ "image_file": ('foreground.png', foreground_binary, 'image/png'),
213
+ "bg_image_file": ('background.png', background_binary, 'image/png')
214
+ }
215
+
216
+ headers = {
217
+ "accept": "image/*",
218
+ 'X-Api-Key': REMOVE_BG_KEY
219
+ }
220
+ remove_bg_request = requests.post(url, files=files,headers=headers, timeout=20)
221
+ if remove_bg_request.status_code == 200:
222
+ image_content = remove_bg_request.content
223
+ pil_image = ImageFormatConvertor.binary_data_to_pil_image(image_content)
224
+ return pil_image
225
+ logging.error(f"failed to use remove bg. Status: {remove_bg_request.status_code}. Resp: {remove_bg_request.content}")
226
+ return None
227
+
228
+
src/image_format_convertor.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+ from PIL import Image
3
+ import numpy as np
4
+ import cv2
5
+
6
+ class ImageFormatConvertor:
7
+ # Function to convert PIL Image to Binary Data
8
+ @classmethod
9
+ def pil_image_to_binary_data(cls, pil_image, format='PNG'):
10
+ # Create a buffer to hold the image data
11
+ buffer = BytesIO()
12
+ # Save the PIL image to the buffer in the specified format
13
+ pil_image.save(buffer, format=format)
14
+ # Get the byte data from the buffer
15
+ binary_data = buffer.getvalue()
16
+ return binary_data
17
+
18
+ # Function to convert Binary Format to PIL Image
19
+ @classmethod
20
+ def binary_data_to_pil_image(cls, binary_data):
21
+ # Create a BytesIO object from the binary data
22
+ buffer = BytesIO(binary_data)
23
+ # Open the image from the buffer
24
+ pil_image = Image.open(buffer)
25
+ return pil_image
26
+
27
+ # Function to convert PIL Image to OpenCV format
28
+ @classmethod
29
+ def pil_to_cv2(cls, pil_image):
30
+ open_cv_image = np.array(pil_image)
31
+ # Convert RGB to BGR if it's a 3-channel image
32
+ if len(open_cv_image.shape) == 3 and open_cv_image.shape[2] == 3:
33
+ open_cv_image = open_cv_image[:, :, ::-1].copy()
34
+ # Convert RGBA to BGRA if it's a 4-channel image
35
+ elif len(open_cv_image.shape) == 3 and open_cv_image.shape[2] == 4:
36
+ open_cv_image = open_cv_image[:, :, [2, 1, 0, 3]].copy()
37
+
38
+ return open_cv_image
39
+
40
+ # Function to convert OpenCV format to PIL Image
41
+ @classmethod
42
+ def cv2_to_pil(cls, cv2_image):
43
+ # Convert BGR to RGB if it's a 3-channel image
44
+ if len(cv2_image.shape) == 3 and cv2_image.shape[2] == 3:
45
+ cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB)
46
+ # Convert BGRA to RGBA if it's a 4-channel image
47
+ elif len(cv2_image.shape) == 3 and cv2_image.shape[2] == 4:
48
+ cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_BGRA2RGBA)
49
+ pil_image = Image.fromarray(cv2_image)
50
+ return pil_image