Manu commited on
Commit
10e69ed
1 Parent(s): 6f3b453

app without requirements added

Browse files
Files changed (4) hide show
  1. app.py +98 -3
  2. image_utils.py +37 -0
  3. requirements.txt +0 -0
  4. segmentation_utils.py +371 -0
app.py CHANGED
@@ -1,7 +1,102 @@
1
  import gradio as gr
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
  demo.launch()
 
1
  import gradio as gr
2
+ import requests
3
+ from image_utils import print_text_on_image_centered, create_background_image
4
+ from hf_utils import hf_validate_api_token
5
+ from segmentation_utils import segment_and_overlay_results
6
 
7
+ def segment_gradio_image(api_token, model, image):
8
+
9
+ # Validacion del token y la imagen
10
+
11
+ is_token_valid, api_token_message = hf_validate_api_token(api_token)
12
+ if not is_token_valid:
13
+ text_image = print_text_on_image_centered(
14
+ create_background_image(500, 500, "white"),
15
+ 'HuggingFace API Token invalid. Please enter a valid token.',
16
+ 'red'
17
+ )
18
+ segments_list = "No segments available."
19
+ else:
20
+ if image is None:
21
+ text_image = print_text_on_image_centered(
22
+ create_background_image(500, 500, "white"),
23
+ 'No image detected',
24
+ 'orange'
25
+ )
26
+ segments_list = "No segments available."
27
+ else:
28
+ text_image = print_text_on_image_centered(
29
+ create_background_image(500, 500, "white"),
30
+ 'PROCESANDO',
31
+ 'blue'
32
+ )
33
+ segments_list = "No segments available."
34
+ # Assuming segment_image is a placeholder for actual segmentation function
35
+ # Uncomment and modify this part according to your segmentation implementation
36
+ # response = segment_image(api_token, model, image)
37
+ # text_image = response["segmented_image"]
38
+
39
+ text_image, segments = segment_and_overlay_results(image,model,api_token)
40
+ print("app.py segment_gradio_image")
41
+ segments_list = "Segments:\n"
42
+ for segment in segments:
43
+ print(segment['label'] + " " + str(segment['score']))
44
+ segments_list += f"\n{segment['label']}: {segment['score']}"
45
+
46
+
47
+ return api_token_message, text_image, segments_list
48
+
49
+
50
+
51
+ with gr.Blocks() as demo:
52
+ gr.Markdown("# Segment Image")
53
+ gr.Markdown("Upload an image and let the model segment it.")
54
+
55
+ with gr.Row():
56
+ api_token = gr.Textbox(
57
+ label="API Token",
58
+ placeholder="Enter your Hugging Face API token here"
59
+ )
60
+ model_name = gr.Textbox(
61
+ label="AI Segmentation Model",
62
+ placeholder="Enter your Segmentation model here",
63
+ value="facebook/mask2former-swin-tiny-coco-panoptic"
64
+ )
65
+
66
+ image_input = gr.Image(label="Upload Image")
67
+
68
+ with gr.Row():
69
+ api_token_validation = gr.Textbox(label="API Token Validation")
70
+ segmented_image = gr.Image(label="Segmented Image")
71
+
72
+ # New block for segments output
73
+
74
+ with gr.Row():
75
+ segments_output = gr.Textbox(label="Segments")
76
+
77
+ examples = gr.Examples(
78
+ examples=[
79
+ ["Your HF API Token", "facebook/mask2former-swin-tiny-coco-panoptic", "https://upload.wikimedia.org/wikipedia/commons/7/74/A-Cat.jpg"]
80
+ ],
81
+ inputs=[api_token, model_name, image_input]
82
+ )
83
+
84
+ api_token.change(
85
+ fn=segment_gradio_image,
86
+ inputs=[api_token, model_name, image_input],
87
+ outputs=[api_token_validation, segmented_image, segments_output]
88
+ )
89
+
90
+ model_name.change(
91
+ fn=segment_gradio_image,
92
+ inputs=[api_token, model_name, image_input],
93
+ outputs=[api_token_validation, segmented_image, segments_output]
94
+ )
95
+
96
+ image_input.change(
97
+ fn=segment_gradio_image,
98
+ inputs=[api_token, model_name, image_input],
99
+ outputs=[api_token_validation, segmented_image, segments_output]
100
+ )
101
 
 
102
  demo.launch()
image_utils.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image, ImageDraw, ImageFont
2
+
3
+
4
+ def print_text_on_image_centered(image, text, color="black"):
5
+ # Crea un objeto Draw para la imagen
6
+ draw = ImageDraw.Draw(image)
7
+
8
+
9
+ # Define el tamaño inicial de la fuente
10
+ font_size = 30
11
+ font = ImageFont.load_default().font_variant(size=font_size)
12
+
13
+ # Calcula las dimensiones del texto
14
+ text_bbox = draw.textbbox((0, 0), text, font=font)
15
+ text_width = text_bbox[2] - text_bbox[0]
16
+ text_height = text_bbox[3] - text_bbox[1]
17
+
18
+ # Reduce el tamaño de la fuente hasta que el texto se ajuste dentro de la imagen
19
+ while text_width > image.width:
20
+ font_size -= 1
21
+ font = ImageFont.load_default().font_variant(size=font_size)
22
+ text_bbox = draw.textbbox((0, 0), text, font=font)
23
+ text_width = text_bbox[2] - text_bbox[0]
24
+
25
+ # Calcula la posición del texto
26
+ text_x = (image.width - text_width) / 2
27
+ text_y = (image.height - text_height) / 2
28
+
29
+ # Dibuja el texto en la imagen
30
+ draw.text((text_x, text_y), text, font=font, fill=color)
31
+ return image
32
+
33
+ # Crea una imagen en blanco por defecto
34
+
35
+ def create_background_image(width, height, color="white"):
36
+ return Image.new("RGB", (width, height), color)
37
+
requirements.txt ADDED
File without changes
segmentation_utils.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from pycocotools import mask
3
+ import matplotlib.pyplot as plt
4
+ from PIL import Image, ImageDraw, ImageOps, ImageFont
5
+ from dotenv import find_dotenv, load_dotenv
6
+ import os
7
+ import base64
8
+ import io
9
+ import random
10
+ import numpy as np
11
+ import cv2
12
+ from image_utils import print_text_on_image_centered, create_background_image
13
+ from icecream import ic
14
+ import traceback
15
+ from pprint import pprint
16
+
17
+
18
+ load_dotenv(find_dotenv())
19
+ HUGGINGFACEHUB_API_TOKEN = os.getenv("HUGGINGFACEHUB_API_TOKEN")
20
+
21
+ API_URL = "https://api-inference.huggingface.co/models/facebook/mask2former-swin-tiny-coco-panoptic"
22
+ headers = {"Authorization": f"Bearer {HUGGINGFACEHUB_API_TOKEN}"}
23
+
24
+ # Función para transformar la entrada en un array de numpy
25
+ # Si la entrada es una URL, descarga la imagen y la convierte en un array de numpy
26
+ # Si la entrada es una ruta de archivo, carga la imagen y la convierte en un array de numpy
27
+ # Si la entrada ya es un array de numpy, devuélvela tal cual
28
+ # Si la entrada no es ninguna de las anteriores, lanza un ValueError
29
+
30
+ def transform_image_to_numpy_array(input):
31
+ if isinstance(input, np.ndarray):
32
+ # Si la entrada es un array de numpy, devuélvela tal cual
33
+ h, w = input.shape[:2]
34
+ new_height = int(h * (500 / w))
35
+ return cv2.resize(input, (500, new_height))
36
+ elif isinstance(input, str):
37
+ # Si la entrada es una cadena, podría ser una URL o una ruta de archivo
38
+ if input.startswith('http://') or input.startswith('https://'):
39
+ # Si la entrada es una URL, descarga la imagen y conviértela en un array de numpy
40
+ # se necesita un header para evitar el error 403
41
+ headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"}
42
+ response = requests.get(input, headers=headers)
43
+ ic(response.status_code)
44
+ image_array = np.frombuffer(response.content, dtype=np.uint8)
45
+ image = cv2.imdecode(image_array, -1)
46
+
47
+ # Si la imagen tiene 3 canales (es decir, es una imagen en color),
48
+ # convertirla de BGR a RGB
49
+ if image.ndim == 3:
50
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
51
+ image = Image.fromarray(image).convert("RGBA")
52
+ image = np.array(image)
53
+ else:
54
+ # Si la entrada es una ruta de archivo, carga la imagen y conviértela en un array de numpy
55
+ image = cv2.imread(input)
56
+
57
+ h, w = image.shape[:2]
58
+ new_height = int(h * (500 / w))
59
+ return cv2.resize(image, (500, new_height))
60
+ else:
61
+ raise ValueError("La entrada no es un array de numpy, una URL ni una ruta de archivo.")
62
+
63
+ def transform_image_to_numpy_array2(input):
64
+ if isinstance(input, np.ndarray):
65
+ # Si la entrada es un array de numpy, devuélvela tal cual
66
+ return cv2.resize(input, (500, 500))
67
+ elif isinstance(input, str):
68
+ # Si la entrada es una cadena, podría ser una URL o una ruta de archivo
69
+ if input.startswith('http://') or input.startswith('https://'):
70
+ # Si la entrada es una URL, descarga la imagen y conviértela en un array de numpy
71
+ # se necesita un header para evitar el error 403
72
+ headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"}
73
+ response = requests.get(input, headers=headers)
74
+ ic(response.status_code)
75
+ image_array = np.frombuffer(response.content, dtype=np.uint8)
76
+ image = cv2.imdecode(image_array, -1)
77
+
78
+ # Si la imagen tiene 3 canales (es decir, es una imagen en color),
79
+ # convertirla de BGR a RGB
80
+ if image.ndim == 3:
81
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
82
+ image = Image.fromarray(image).convert("RGBA")
83
+ image = np.array(image)
84
+ else:
85
+ # Si la entrada es una ruta de archivo, carga la imagen y conviértela en un array de numpy
86
+ image = cv2.imread(input)
87
+
88
+ return cv2.resize(image, (500, 500))
89
+ else:
90
+ raise ValueError("La entrada no es un array de numpy, una URL ni una ruta de archivo.")
91
+
92
+ def segment_image_from_numpy(image_array):
93
+ # Convert the image to bytes
94
+ is_success, im_buf_arr = cv2.imencode(".jpg", image_array)
95
+ data = im_buf_arr.tobytes()
96
+ response = requests.post(API_URL, headers=headers, data=data)
97
+ pprint(response.json())
98
+ return response.json()
99
+
100
+
101
+ def segment_image_from_path(image_path):
102
+ with open(image_path, "rb") as f:
103
+ data = f.read()
104
+ response = requests.post(API_URL, headers=headers, data=data)
105
+ return response.json()
106
+
107
+ def segment_image_from_image(image):
108
+ # Convert the image to bytes
109
+ is_success, im_buf_arr = cv2.imencode(".jpg", image)
110
+ data = im_buf_arr.tobytes()
111
+
112
+ response = requests.post(API_URL, headers=headers, data=data)
113
+ return response.json()
114
+
115
+ def decode_mask(mask_str, size):
116
+ mask_data = base64.b64decode(mask_str)
117
+ mask_image = Image.open(io.BytesIO(mask_data))
118
+ mask_image = mask_image.resize(size).convert("L")
119
+ return mask_image
120
+
121
+
122
+ def overlay_masks_on_image(image, segments, transparency=0.4):
123
+ if isinstance(image, np.ndarray):
124
+ image = Image.fromarray(image)
125
+
126
+ original_image = image
127
+ if original_image.mode != 'RGBA':
128
+ original_image = original_image.convert('RGBA')
129
+
130
+ overlay = Image.new("RGBA", original_image.size, (255, 255, 255, 0))
131
+ text_layer = Image.new("RGBA", original_image.size, (255, 255, 255, 0))
132
+
133
+ for segment in segments:
134
+ mask_str = segment['mask']
135
+ mask_image = decode_mask(mask_str, original_image.size)
136
+ color = generate_random_color()
137
+
138
+ color_mask = ImageOps.colorize(mask_image, black="black", white=color)
139
+ color_mask.putalpha(mask_image)
140
+
141
+ overlay = Image.alpha_composite(overlay, color_mask)
142
+
143
+ # Calcula el centroide de la mascara
144
+ x, y = np.where(np.array(mask_image) > 0)
145
+ centroid_x = x.mean()
146
+ centroid_y = y.mean()
147
+
148
+ # Imprime la etiqueta y la puntuación en la capa de texto
149
+ font_size = 30
150
+ draw = ImageDraw.Draw(text_layer)
151
+ font = ImageFont.load_default().font_variant(size=font_size)
152
+ label = segment['label']
153
+ score = segment['score']
154
+ text =f"{label}: {score}"
155
+
156
+ # Calcula el tamaño del texto
157
+ text_bbox = draw.textbbox((0, 0), text, font=font)
158
+ text_width = text_bbox[2] - text_bbox[0]
159
+ text_height = text_bbox[3] - text_bbox[1]
160
+
161
+ # Asegúrate de que las coordenadas del texto están dentro de los límites de la imagen
162
+ text_x = max(0, min(centroid_x - text_width / 2, original_image.size[0] - text_width))
163
+ text_y = max(0, min(centroid_y - text_height / 2, original_image.size[1] - text_height))
164
+
165
+ draw.text((text_x, text_y), text, fill=(255, 255, 255, 255), font=font)
166
+
167
+ # Ajusta la transparencia de la capa de superposición
168
+ overlay = Image.blend(original_image, overlay, transparency)
169
+
170
+ # Combina la capa de superposición con la capa de texto
171
+ final_image = Image.alpha_composite(overlay, text_layer)
172
+
173
+ return final_image
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+ def overlay_masks_on_image2(image, segments, transparency=0.4):
183
+ # Convert numpy array to PIL Image
184
+ #original_image = Image.fromarray(image).convert("RGBA")
185
+ #original_image = image
186
+ #original_image = Image.open(image).convert("RGBA")
187
+ # para file es str
188
+ # para url es numpy.ndarray
189
+ # para cv.imread es numpy.ndarray
190
+
191
+ # Convertir el array de numpy a una imagen PIL si es necesario
192
+ if isinstance(image, np.ndarray):
193
+ image = Image.fromarray(image)
194
+
195
+ print(type(image))
196
+ print(image)
197
+ original_image = image
198
+
199
+ if original_image.mode != 'RGBA':
200
+ original_image = original_image.convert('RGBA')
201
+
202
+ print(original_image.size)
203
+ overlay = Image.new("RGBA", original_image.size, (255, 255, 255, 0))
204
+ print(overlay.size)
205
+ # Nueva capa para el texto
206
+
207
+ text_layer = Image.new("RGBA", original_image.size, (255, 255, 255, 0))
208
+
209
+ for segment in segments:
210
+
211
+
212
+ print(segment['label'] + " " + str(segment['score']))
213
+ mask_str = segment['mask']
214
+ mask_image = decode_mask(mask_str, original_image.size)
215
+
216
+
217
+
218
+ # Convierte la imagen de la máscara a un array de numpy
219
+ mask_array = np.array(mask_image)
220
+
221
+ # Encuentra los píxeles blancos
222
+ y, x = np.where(mask_array > 0)
223
+
224
+ # Calcula el cuadro delimitador de los píxeles blancos
225
+ x_min, y_min, width, height = cv2.boundingRect(np.array(list(zip(x, y))))
226
+
227
+
228
+ # Crea un objeto ImageDraw para dibujar en la imagen original
229
+ draw = ImageDraw.Draw(original_image)
230
+
231
+
232
+ # Dibuja el cuadro delimitador en la imagen original
233
+ draw.rectangle([(x_min, y_min), (x_min + width, y_min + height)], outline=(0, 255, 0), width=2)
234
+
235
+
236
+ color = generate_random_color()
237
+
238
+ color_mask = ImageOps.colorize(mask_image, black="black", white=color)
239
+ color_mask.putalpha(mask_image)
240
+
241
+ overlay = Image.alpha_composite(overlay, color_mask)
242
+
243
+
244
+ # Calcula el centroide de la mascara
245
+
246
+ x, y = np.where(np.array(mask_image) > 0)
247
+ centroid_x = x.mean()
248
+ centroid_y = y.mean()
249
+
250
+ # Imprime la etiqueta y la puntuación en la capa de texto
251
+
252
+ font_size = 30
253
+ draw = ImageDraw.Draw(text_layer)
254
+ font_path = "/System/Library/Fonts/Arial.ttf" # Path to Arial font on macOS
255
+ font = ImageFont.truetype(font_path, font_size)
256
+ label = segment['label']
257
+ score = segment['score']
258
+ text =f"{label}: {score}"
259
+
260
+ # Estima el tamaño del texto hard rockandroll way
261
+
262
+ text_width = 500
263
+ text_height = 100
264
+
265
+
266
+ # Asegúrate de que las coordenadas del texto están dentro de los límites de la imagen
267
+ text_x = max(0, min(centroid_x - text_width / 2, original_image.size[0] - text_width))
268
+ text_y = max(0, min(centroid_y - text_height / 2, original_image.size[1] - text_height))
269
+ # Asegúrate de que las coordenadas del texto están dentro de los límites de la imagen
270
+ text_x = max(0, min(centroid_x, original_image.size[0] - text_width))
271
+ text_y = max(0, min(centroid_y, original_image.size[1] - text_height))
272
+
273
+
274
+ # Calcula las coordenadas del texto
275
+ text_x = centroid_x - text_width / 2
276
+ text_y = centroid_y - text_height / 2
277
+
278
+
279
+ # Asegúrate de que las coordenadas del texto están dentro de los límites de la imagen
280
+ text_x = max(0, min(text_x, original_image.size[0] - text_width))
281
+ text_y = max(0, min(text_y, original_image.size[1] - text_height))
282
+
283
+
284
+ draw.text((centroid_x - text_width / 2, centroid_y - text_height / 2), text, fill=(255, 255, 255, 255), font=font)
285
+
286
+ #draw.text((text_x, text_y), text, fill=(255, 255, 255, 255), font=font)
287
+
288
+ # Ajusta la transparencia de la capa de superposición
289
+ print(original_image.size)
290
+ print(overlay.size)
291
+ overlay = Image.blend(original_image, overlay, transparency)
292
+
293
+ # Combina la capa de superposición con la capa de texto
294
+
295
+ final_image = Image.alpha_composite(overlay, text_layer)
296
+
297
+ #final_image = print_text_on_image_centered(final_image, 'SEGMENTING OK', 'green')
298
+
299
+ return final_image
300
+
301
+ def generate_random_color():
302
+ return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
303
+
304
+
305
+ def segment_and_overlay_results(image_path, api_token, model):
306
+ #segments = segment_image_from_image(image)
307
+ #final_image = overlay_masks_on_image(image, segments)
308
+ #return final_image
309
+ processed_image = None # Initialize processed_image
310
+ segments = []
311
+ #image_type = None
312
+ #if isinstance(image_path, str):
313
+ # image_type = 'FILE'
314
+ # image = cv2.imread('cats.jpg')
315
+ #elif isinstance(image_path, np.ndarray):
316
+ # image_type = 'NUMPY ARRAY'
317
+ #else:
318
+ # raise ValueError("The image is neither a Image nor a local file.")
319
+
320
+ #ic(image_type)
321
+ image = transform_image_to_numpy_array(image_path)
322
+ # imprime tres primeros pixeles
323
+ print(type(image))
324
+ ic(image[0, 0:3])
325
+
326
+
327
+
328
+
329
+ try:
330
+ #segments = segment_image_from_image(image)
331
+ #processed_image = overlay_masks_on_image(image, segments)
332
+
333
+ # debug image contents
334
+
335
+ #if os.path.isfile(image):
336
+ # ic ("--- image is a file ---")
337
+ # image = Image.open(image)
338
+ # if image is None:
339
+ # ic("image is None")
340
+ # return None, []
341
+
342
+ ic("--- calling segment_image_from_path ---")
343
+ segments = segment_image_from_numpy(image)
344
+ #if image_type == 'FILE':
345
+ # segments = segment_image_from_path(image_path)
346
+ #if image_type == 'NUMPY ARRAY':
347
+ # segments = segment_image_from_image(image_path)
348
+
349
+ ic("--- printing segments ---")
350
+ for segment in segments:
351
+ ic(segment['label'] ,segment['score'])
352
+ processed_image = print_text_on_image_centered(
353
+ create_background_image(500, 500, "white"),
354
+ 'SEGMENTING OK',
355
+ 'green'
356
+ )
357
+ ic("--- calling overlay_masks_on_image ---")
358
+ processed_image = overlay_masks_on_image(image, segments)
359
+ except Exception as e:
360
+ print("EXCEPTION")
361
+ ic(e)
362
+ print(traceback.format_exc())
363
+ processed_image = print_text_on_image_centered(
364
+ create_background_image(500, 500, "white"),
365
+ e,
366
+ 'green'
367
+ )
368
+ segments = []
369
+ return processed_image, segments
370
+ finally:
371
+ return processed_image, segments