Spaces:
Running on T4
Running on T4
Commit ·
16ed97d
1
Parent(s): 9c900d6
fix(dino): change core to additive logic
Browse files
logic.py
CHANGED
|
@@ -12,14 +12,12 @@ import time
|
|
| 12 |
class WatermarkRemover:
|
| 13 |
def __init__(self, device="cpu"):
|
| 14 |
self.device = device
|
| 15 |
-
# --- Не загружаем модели здесь! Просто инициализируем как None ---
|
| 16 |
self.dino_processor = None
|
| 17 |
self.dino_model = None
|
| 18 |
self.inpainting_model = None
|
| 19 |
logger.info(f"WatermarkRemover инициализирован для устройства {self.device}. Модели будут загружены по первому требованию.")
|
| 20 |
|
| 21 |
def _get_dino_components(self):
|
| 22 |
-
"""Лениво загружает и возвращает компоненты DINO."""
|
| 23 |
if self.dino_model is None:
|
| 24 |
logger.info("Первый вызов: Загрузка модели детектора Grounding DINO...")
|
| 25 |
dino_model_id = "IDEA-Research/grounding-dino-base"
|
|
@@ -29,7 +27,6 @@ class WatermarkRemover:
|
|
| 29 |
return self.dino_processor, self.dino_model
|
| 30 |
|
| 31 |
def _get_inpainting_model(self):
|
| 32 |
-
"""Лениво загружает и возвращает модель LaMa."""
|
| 33 |
if self.inpainting_model is None:
|
| 34 |
logger.info("Первый вызов: Загрузка модели LaMA для закрашивания...")
|
| 35 |
self.inpainting_model = ModelManager(name="lama", device=self.device)
|
|
@@ -43,7 +40,6 @@ class WatermarkRemover:
|
|
| 43 |
with torch.no_grad():
|
| 44 |
outputs = model(**inputs)
|
| 45 |
|
| 46 |
-
# Фильтрация будет произведена позже, на основе 'confidence'
|
| 47 |
results = processor.post_process_grounded_object_detection(
|
| 48 |
outputs,
|
| 49 |
input_ids=inputs["input_ids"],
|
|
@@ -52,19 +48,12 @@ class WatermarkRemover:
|
|
| 52 |
target_sizes=[image.size[::-1]]
|
| 53 |
)[0]
|
| 54 |
|
| 55 |
-
|
| 56 |
xyxy=results["boxes"].cpu().numpy(),
|
| 57 |
confidence=results["scores"].cpu().numpy()
|
| 58 |
)
|
| 59 |
-
|
| 60 |
-
if len(detections) > 1:
|
| 61 |
-
logger.info(f"Найдено {len(detections)} боксов до NMS. Запускаю очистку...")
|
| 62 |
-
return detections.with_nms(class_agnostic=True, threshold=0.7)
|
| 63 |
-
|
| 64 |
-
return detections
|
| 65 |
|
| 66 |
def _inpaint_image(self, image_np: np.ndarray, mask_np: np.ndarray) -> np.ndarray:
|
| 67 |
-
# Получаем модель через "ленивый" getter
|
| 68 |
inpainting_model = self._get_inpainting_model()
|
| 69 |
config = InpaintRequest(ldm_steps=50, ldm_sampler=LDMSampler.ddim, hd_strategy=HDStrategy.CROP)
|
| 70 |
result = inpainting_model(image_np, mask_np, config)
|
|
@@ -74,25 +63,42 @@ class WatermarkRemover:
|
|
| 74 |
|
| 75 |
def run(self, image: Image.Image) -> Image.Image:
|
| 76 |
start_time = time.time()
|
| 77 |
-
logger.info("Начало процесса удаления вотермарок
|
| 78 |
-
|
| 79 |
-
TEXT_PROMPT = "watermark. logo. text."
|
| 80 |
-
BOX_THRESHOLD = 0.35 # Основной порог уверенности
|
| 81 |
-
TEXT_THRESHOLD = 0.25 # Дополнительный порог для текста
|
| 82 |
|
| 83 |
-
#
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
if len(detections) == 0:
|
| 87 |
-
logger.info("
|
| 88 |
return image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
|
|
|
| 90 |
mask = np.zeros(image.size[::-1], dtype=np.uint8)
|
| 91 |
-
|
| 92 |
-
for box in detections.xyxy:
|
| 93 |
cv2.rectangle(mask, tuple(map(int, box[:2])), tuple(map(int, box[2:])), 255, -1)
|
| 94 |
|
| 95 |
-
kernel = np.ones((
|
| 96 |
processed_mask = cv2.dilate(mask, kernel, iterations=1)
|
| 97 |
|
| 98 |
image_np = np.array(image.convert("RGB"))
|
|
|
|
| 12 |
class WatermarkRemover:
|
| 13 |
def __init__(self, device="cpu"):
|
| 14 |
self.device = device
|
|
|
|
| 15 |
self.dino_processor = None
|
| 16 |
self.dino_model = None
|
| 17 |
self.inpainting_model = None
|
| 18 |
logger.info(f"WatermarkRemover инициализирован для устройства {self.device}. Модели будут загружены по первому требованию.")
|
| 19 |
|
| 20 |
def _get_dino_components(self):
|
|
|
|
| 21 |
if self.dino_model is None:
|
| 22 |
logger.info("Первый вызов: Загрузка модели детектора Grounding DINO...")
|
| 23 |
dino_model_id = "IDEA-Research/grounding-dino-base"
|
|
|
|
| 27 |
return self.dino_processor, self.dino_model
|
| 28 |
|
| 29 |
def _get_inpainting_model(self):
|
|
|
|
| 30 |
if self.inpainting_model is None:
|
| 31 |
logger.info("Первый вызов: Загрузка модели LaMA для закрашивания...")
|
| 32 |
self.inpainting_model = ModelManager(name="lama", device=self.device)
|
|
|
|
| 40 |
with torch.no_grad():
|
| 41 |
outputs = model(**inputs)
|
| 42 |
|
|
|
|
| 43 |
results = processor.post_process_grounded_object_detection(
|
| 44 |
outputs,
|
| 45 |
input_ids=inputs["input_ids"],
|
|
|
|
| 48 |
target_sizes=[image.size[::-1]]
|
| 49 |
)[0]
|
| 50 |
|
| 51 |
+
return sv.Detections(
|
| 52 |
xyxy=results["boxes"].cpu().numpy(),
|
| 53 |
confidence=results["scores"].cpu().numpy()
|
| 54 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
def _inpaint_image(self, image_np: np.ndarray, mask_np: np.ndarray) -> np.ndarray:
|
|
|
|
| 57 |
inpainting_model = self._get_inpainting_model()
|
| 58 |
config = InpaintRequest(ldm_steps=50, ldm_sampler=LDMSampler.ddim, hd_strategy=HDStrategy.CROP)
|
| 59 |
result = inpainting_model(image_np, mask_np, config)
|
|
|
|
| 63 |
|
| 64 |
def run(self, image: Image.Image) -> Image.Image:
|
| 65 |
start_time = time.time()
|
| 66 |
+
logger.info("Начало процесса удаления вотермарок...")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
+
# --- Этап 1: Поиск высококонтрастных частей (текст) ---
|
| 69 |
+
logger.info("--- ЭТАП 1: Поиск текста с высокой уверенностью ---")
|
| 70 |
+
detections_text = self._detect_watermarks(
|
| 71 |
+
image, text_prompt="text . watermark", box_threshold=0.4, text_threshold=0.4
|
| 72 |
+
)
|
| 73 |
+
logger.info(f"Найдено {len(detections_text)} текстовых фрагментов.")
|
| 74 |
|
| 75 |
+
# --- Этап 2: Поиск низкоконтрастных частей (фон) ---
|
| 76 |
+
logger.info("--- ЭТАП 2: Поиск фона с низкой уверенностью ---")
|
| 77 |
+
detections_bg = self._detect_watermarks(
|
| 78 |
+
image, text_prompt="semi-transparent background . transparent overlay . watermark background", box_threshold=0.25, text_threshold=0.25
|
| 79 |
+
)
|
| 80 |
+
logger.info(f"Найдено {len(detections_bg)} фоновых фрагментов.")
|
| 81 |
+
|
| 82 |
+
# --- Этап 3: Объединение и очистка ---
|
| 83 |
+
# Складываем все найденные детекции вместе
|
| 84 |
+
all_detections_list = [detections_text, detections_bg]
|
| 85 |
+
detections = sv.Detections.merge(all_detections_list)
|
| 86 |
+
|
| 87 |
if len(detections) == 0:
|
| 88 |
+
logger.info("Ни один из этапов не нашел вотермарок. Возвращаем оригинальное изображение.")
|
| 89 |
return image
|
| 90 |
+
|
| 91 |
+
logger.info(f"Всего найдено {len(detections)} фрагментов. Запускаю NMS для слияния...")
|
| 92 |
+
# Применяем NMS для слияния всех пересекающихся рамок в одну
|
| 93 |
+
merged_detections = detections.with_nms(class_agnostic=True, threshold=0.6)
|
| 94 |
+
logger.success(f"После слияния осталось {len(merged_detections)} уникальных областей.")
|
| 95 |
|
| 96 |
+
# --- Этап 4: Создание маски и закрашивание ---
|
| 97 |
mask = np.zeros(image.size[::-1], dtype=np.uint8)
|
| 98 |
+
for box in merged_detections.xyxy:
|
|
|
|
| 99 |
cv2.rectangle(mask, tuple(map(int, box[:2])), tuple(map(int, box[2:])), 255, -1)
|
| 100 |
|
| 101 |
+
kernel = np.ones((9, 9), np.uint8)
|
| 102 |
processed_mask = cv2.dilate(mask, kernel, iterations=1)
|
| 103 |
|
| 104 |
image_np = np.array(image.convert("RGB"))
|