Foydalanuvchi commited on
Commit
bb24ba7
·
1 Parent(s): e1ff18c

Feature: Advanced BG Remove - 8 improvements (alpha matting, 5 modes, edge refine, shadow, quality check, GrabCut fallback)

Browse files
Files changed (2) hide show
  1. filters.py +219 -14
  2. main.py +25 -3
filters.py CHANGED
@@ -1411,29 +1411,234 @@ def process_video_watermark(video_path, output_path, progress_callback=None):
1411
 
1412
  return _apply_video_transform(video_path, output_path, wm_frame, progress_callback)
1413
 
1414
- def apply_bg_remove(image_path, output_path):
1415
- """Rasmdan fonni olib tashlaydi (shaffof PNG)."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1416
  try:
1417
- from rembg import remove
 
1418
 
1419
- with open(os.path.abspath(image_path), 'rb') as inp:
1420
- input_data = inp.read()
 
 
 
1421
 
1422
- output_data = remove(input_data)
 
 
1423
 
1424
- # PNG sifatida saqlash (shaffof fon)
1425
- png_path = os.path.abspath(output_path).rsplit('.', 1)[0] + '.png'
1426
- with open(png_path, 'wb') as out:
1427
- out.write(output_data)
 
 
 
1428
 
1429
- return png_path
1430
- except ImportError:
1431
- logger.error("rembg kutubxonasi o'rnatilmagan.")
1432
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1433
  except Exception as e:
1434
  logger.error(f"Background Remove xatosi: {e}")
 
 
1435
  return None
1436
 
 
1437
  def process_video_subtitle_translate(video_path, output_path, target_lang="uz", progress_callback=None):
1438
  """Videoga avtomatik subtitle + tarjima qo'shadi."""
1439
  from PIL import ImageFont, ImageDraw
 
1411
 
1412
  return _apply_video_transform(video_path, output_path, wm_frame, progress_callback)
1413
 
1414
+ def apply_bg_remove(image_path, output_path, mode="transparent"):
1415
+ """Rasmdan fonni professional darajada olib tashlaydi.
1416
+
1417
+ Rejimlar:
1418
+ transparent — shaffof PNG (soya bilan)
1419
+ white — oq fon
1420
+ black — qora fon
1421
+ blur — asl fonni bokeh bilan xiralashtirib qaytarish
1422
+ gradient — ko'k-binafsha gradient fon
1423
+
1424
+ Xususiyatlar:
1425
+ - Alpha Matting (soch/detall uchun)
1426
+ - Edge Refinement (Morphology + Feather)
1427
+ - Kichik rasm avtomatik upscale
1428
+ - Sifat nazorati
1429
+ - GrabCut fallback
1430
+ """
1431
  try:
1432
+ image_path = os.path.abspath(image_path)
1433
+ output_path = os.path.abspath(output_path)
1434
 
1435
+ # ===== 1. Rasmni yuklash va hajmini tekshirish =====
1436
+ original_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
1437
+ if original_img is None:
1438
+ logger.error(f"BG Remove: Rasm o'qilmadi: {image_path}")
1439
+ return None
1440
 
1441
+ h, w = original_img.shape[:2]
1442
+ needs_downscale = False
1443
+ orig_size = (w, h)
1444
 
1445
+ # Kichik rasmlarni upscale qilish (sifatni saqlash)
1446
+ if min(w, h) < 500:
1447
+ scale = 2
1448
+ original_img = cv2.resize(original_img, (w * scale, h * scale), interpolation=cv2.INTER_LANCZOS4)
1449
+ h, w = original_img.shape[:2]
1450
+ needs_downscale = True
1451
+ logger.info(f"BG Remove: Kichik rasm {orig_size} → ({w},{h}) upscale qilindi")
1452
 
1453
+ # ===== 2. rembg bilan fon o'chirish (Alpha Matting) =====
1454
+ result_pil = None
1455
+ alpha_mask = None
1456
+
1457
+ try:
1458
+ from rembg import remove
1459
+ from PIL import Image as PILImage
1460
+ import io
1461
+
1462
+ # Rasmni bytes ga o'tkazish
1463
+ _, img_encoded = cv2.imencode('.png', original_img)
1464
+ input_bytes = img_encoded.tobytes()
1465
+
1466
+ # Alpha Matting + Post-Process bilan o'chirish
1467
+ output_bytes = remove(
1468
+ input_bytes,
1469
+ alpha_matting=True,
1470
+ alpha_matting_foreground_threshold=230,
1471
+ alpha_matting_background_threshold=20,
1472
+ alpha_matting_erode_size=12,
1473
+ post_process_mask=True
1474
+ )
1475
+
1476
+ result_pil = PILImage.open(io.BytesIO(output_bytes)).convert("RGBA")
1477
+ alpha_mask = np.array(result_pil.split()[-1]) # Alpha kanal
1478
+
1479
+ logger.info("BG Remove: rembg + Alpha Matting muvaffaqiyatli")
1480
+
1481
+ except ImportError:
1482
+ logger.warning("rembg kutubxonasi yo'q — GrabCut fallback ishlatiladi")
1483
+ result_pil = None
1484
+ except Exception as e:
1485
+ logger.warning(f"rembg xatosi: {e} — GrabCut fallback ishlatiladi")
1486
+ result_pil = None
1487
+
1488
+ # ===== 3. GrabCut Fallback =====
1489
+ if result_pil is None:
1490
+ logger.info("BG Remove: GrabCut fallback boshlandi")
1491
+ img_bgr = original_img[:, :, :3] if original_img.shape[2] == 4 else original_img
1492
+
1493
+ mask = np.zeros(img_bgr.shape[:2], np.uint8)
1494
+ bgd_model = np.zeros((1, 65), np.float64)
1495
+ fgd_model = np.zeros((1, 65), np.float64)
1496
+
1497
+ # Markaziy to'rtburchak (fon chetlarda deb taxmin)
1498
+ margin_x = int(w * 0.05)
1499
+ margin_y = int(h * 0.05)
1500
+ rect = (margin_x, margin_y, w - 2 * margin_x, h - 2 * margin_y)
1501
+
1502
+ cv2.grabCut(img_bgr, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)
1503
+
1504
+ # Foreground va probable foreground
1505
+ alpha_mask = np.where((mask == 2) | (mask == 0), 0, 255).astype('uint8')
1506
+
1507
+ # PIL RGBA rasm yaratish
1508
+ img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
1509
+ result_pil = Image.fromarray(img_rgb).convert("RGBA")
1510
+ r, g, b, _ = result_pil.split()
1511
+ result_pil = Image.merge("RGBA", (r, g, b, Image.fromarray(alpha_mask)))
1512
+
1513
+ logger.info("BG Remove: GrabCut fallback muvaffaqiyatli")
1514
+
1515
+ # ===== 4. Sifat Nazorati =====
1516
+ total_pixels = alpha_mask.shape[0] * alpha_mask.shape[1]
1517
+ transparent_pixels = np.sum(alpha_mask < 30)
1518
+ opaque_pixels = np.sum(alpha_mask > 225)
1519
+ removed_ratio = transparent_pixels / total_pixels
1520
+ kept_ratio = opaque_pixels / total_pixels
1521
+
1522
+ if removed_ratio < 0.03:
1523
+ logger.warning(f"BG Remove: Fon topilmadi (faqat {removed_ratio:.1%} o'chirildi)")
1524
+ # Davom etamiz, lekin natija yaxshi bo'lmasligi mumkin
1525
+
1526
+ if kept_ratio < 0.03:
1527
+ logger.warning(f"BG Remove: Ob'ekt topilmadi (faqat {kept_ratio:.1%} qoldi)")
1528
+
1529
+ logger.info(f"BG Remove sifat: {removed_ratio:.0%} fon o'chirildi, {kept_ratio:.0%} ob'ekt qoldi")
1530
+
1531
+ # ===== 5. Edge Refinement (Chetlarni tozalash) =====
1532
+ refined_alpha = alpha_mask.copy()
1533
+
1534
+ # Morphology Close — kichik teshiklarni to'ldirish
1535
+ kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
1536
+ refined_alpha = cv2.morphologyEx(refined_alpha, cv2.MORPH_CLOSE, kernel, iterations=2)
1537
+
1538
+ # Morphology Open — kichik shovqinlarni olib tashlash
1539
+ kernel_small = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
1540
+ refined_alpha = cv2.morphologyEx(refined_alpha, cv2.MORPH_OPEN, kernel_small, iterations=1)
1541
+
1542
+ # Gaussian Feathering — yumshoq chet
1543
+ refined_alpha = cv2.GaussianBlur(refined_alpha, (3, 3), 0)
1544
+
1545
+ # Threshold clean — partial transparency → solid
1546
+ refined_alpha = np.where(refined_alpha > 200, 255,
1547
+ np.where(refined_alpha < 30, 0, refined_alpha)).astype(np.uint8)
1548
+
1549
+ # Alpha kanalni yangilash
1550
+ r, g, b, _ = result_pil.split()
1551
+ result_pil = Image.merge("RGBA", (r, g, b, Image.fromarray(refined_alpha)))
1552
+
1553
+ # ===== 6. Agar upscale qilingan bo'lsa — qaytarish =====
1554
+ if needs_downscale:
1555
+ result_pil = result_pil.resize(orig_size, Image.LANCZOS)
1556
+ logger.info(f"BG Remove: Asl o'lchamga qaytarildi: {orig_size}")
1557
+
1558
+ # ===== 7. Rejimga qarab natija yaratish =====
1559
+ final_w, final_h = result_pil.size
1560
+
1561
+ if mode == "transparent":
1562
+ # Shaffof PNG + tabiiy soya
1563
+ shadow_layer = Image.new("RGBA", (final_w, final_h), (0, 0, 0, 0))
1564
+
1565
+ # Soya — ob'ektning alpha maskini siljitib yumshatish
1566
+ shadow_alpha = np.array(result_pil.split()[-1])
1567
+ # Soyani pastga va o'ngga siljitish
1568
+ shift_x, shift_y = max(2, final_w // 200), max(3, final_h // 150)
1569
+ shadow_shifted = np.zeros_like(shadow_alpha)
1570
+ shadow_shifted[shift_y:, shift_x:] = shadow_alpha[:-shift_y, :-shift_x]
1571
+ # Yumshatish
1572
+ shadow_blurred = cv2.GaussianBlur(shadow_shifted, (21, 21), 0)
1573
+ shadow_blurred = (shadow_blurred * 0.25).astype(np.uint8) # 25% shaffoflik
1574
+
1575
+ shadow_img = Image.new("RGBA", (final_w, final_h), (0, 0, 0, 0))
1576
+ shadow_r = Image.new("L", (final_w, final_h), 0)
1577
+ shadow_img = Image.merge("RGBA", (shadow_r, shadow_r, shadow_r, Image.fromarray(shadow_blurred)))
1578
+
1579
+ # Soya + Ob'ekt birlashtirish
1580
+ final = Image.alpha_composite(shadow_img, result_pil)
1581
+
1582
+ png_path = output_path.rsplit('.', 1)[0] + '.png'
1583
+ final.save(png_path, "PNG", optimize=True)
1584
+ return png_path
1585
+
1586
+ elif mode == "white":
1587
+ bg = Image.new("RGBA", (final_w, final_h), (255, 255, 255, 255))
1588
+ bg = Image.alpha_composite(bg, result_pil).convert("RGB")
1589
+ jpg_path = output_path.rsplit('.', 1)[0] + '.jpg'
1590
+ bg.save(jpg_path, "JPEG", quality=92, optimize=True)
1591
+ return jpg_path
1592
+
1593
+ elif mode == "black":
1594
+ bg = Image.new("RGBA", (final_w, final_h), (0, 0, 0, 255))
1595
+ bg = Image.alpha_composite(bg, result_pil).convert("RGB")
1596
+ jpg_path = output_path.rsplit('.', 1)[0] + '.jpg'
1597
+ bg.save(jpg_path, "JPEG", quality=92, optimize=True)
1598
+ return jpg_path
1599
+
1600
+ elif mode == "blur":
1601
+ # Asl rasmni bokeh bilan xiralshtirish
1602
+ orig_bgr = cv2.imread(image_path)
1603
+ if needs_downscale:
1604
+ orig_bgr = cv2.resize(orig_bgr, orig_size, interpolation=cv2.INTER_LANCZOS4)
1605
+ blurred_bg = cv2.GaussianBlur(orig_bgr, (51, 51), 25)
1606
+ blurred_pil = Image.fromarray(cv2.cvtColor(blurred_bg, cv2.COLOR_BGR2RGB)).convert("RGBA")
1607
+
1608
+ # Xiralashtirilgan fon + ob'ekt
1609
+ final = Image.alpha_composite(blurred_pil, result_pil).convert("RGB")
1610
+ jpg_path = output_path.rsplit('.', 1)[0] + '.jpg'
1611
+ final.save(jpg_path, "JPEG", quality=92, optimize=True)
1612
+ return jpg_path
1613
+
1614
+ elif mode == "gradient":
1615
+ # Ko'k-binafsha gradient fon
1616
+ gradient = np.zeros((final_h, final_w, 3), dtype=np.uint8)
1617
+ for y in range(final_h):
1618
+ ratio = y / max(1, final_h - 1)
1619
+ # Ko'kdan (70, 130, 230) binafshaga (150, 80, 200)
1620
+ r_val = int(70 + (150 - 70) * ratio)
1621
+ g_val = int(130 + (80 - 130) * ratio)
1622
+ b_val = int(230 + (200 - 230) * ratio)
1623
+ gradient[y, :] = [r_val, g_val, b_val]
1624
+
1625
+ gradient_pil = Image.fromarray(gradient).convert("RGBA")
1626
+ final = Image.alpha_composite(gradient_pil, result_pil).convert("RGB")
1627
+ jpg_path = output_path.rsplit('.', 1)[0] + '.jpg'
1628
+ final.save(jpg_path, "JPEG", quality=92, optimize=True)
1629
+ return jpg_path
1630
+
1631
+ else:
1632
+ logger.error(f"BG Remove: Noma'lum rejim: {mode}")
1633
+ return None
1634
+
1635
  except Exception as e:
1636
  logger.error(f"Background Remove xatosi: {e}")
1637
+ import traceback
1638
+ logger.error(traceback.format_exc())
1639
  return None
1640
 
1641
+
1642
  def process_video_subtitle_translate(video_path, output_path, target_lang="uz", progress_callback=None):
1643
  """Videoga avtomatik subtitle + tarjima qo'shadi."""
1644
  from PIL import ImageFont, ImageDraw
main.py CHANGED
@@ -258,7 +258,15 @@ async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
258
  InlineKeyboardButton("🪞 Oyna Effekt", callback_data=f"mr|p|{short_id}")
259
  ],
260
  [
261
- InlineKeyboardButton("🎨 Fonni O'chirish", callback_data=f"bg|p|{short_id}"),
 
 
 
 
 
 
 
 
262
  InlineKeyboardButton("📱 Suv belgisi", callback_data=f"wm|p|{short_id}")
263
  ],
264
  [
@@ -533,13 +541,25 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
533
  try: await query.edit_message_text("💎 AI Ultra HD sifatga oshirilmoqda...\n(Bu jarayon bir oz vaqt olishi mumkin)")
534
  except: pass
535
  success_path = await loop.run_in_executor(executor, upscale_image, input_path, output_path)
 
 
 
 
 
 
 
 
 
 
 
 
536
  else:
537
  func = {
538
  "r": apply_retro_filter, "u": upscale_image,
539
  "f": apply_face_restore, "a": apply_auto_enhance,
540
  "n": apply_nudenet_filter,
541
  "gl": apply_glitch_filter, "mr": apply_mirror_filter,
542
- "wm": apply_watermark, "bg": apply_bg_remove,
543
  "qb": apply_quality_boost,
544
  }.get(action)
545
 
@@ -603,7 +623,9 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
603
  "st_anime": "AI anime", "st_sketch": "AI sketch",
604
  "st_oil": "AI oil paint", "st_cart": "AI cartoon",
605
  "stuz": "sub+o'zbek", "stru": "sub+rus", "sten": "sub+english",
606
- "qb": "sifat+ pro", "stb": "stabilizatsiya"
 
 
607
  }.get(action, f"filter_{action}")
608
  db.log_history(query.from_user.id, "photo" if m_type == "p" else "video", f_name, file_id)
609
 
 
258
  InlineKeyboardButton("🪞 Oyna Effekt", callback_data=f"mr|p|{short_id}")
259
  ],
260
  [
261
+ InlineKeyboardButton("🎨 Shaffof Fon", callback_data=f"bg_t|p|{short_id}"),
262
+ InlineKeyboardButton("⬜ Oq Fon", callback_data=f"bg_w|p|{short_id}")
263
+ ],
264
+ [
265
+ InlineKeyboardButton("⬛ Qora Fon", callback_data=f"bg_k|p|{short_id}"),
266
+ InlineKeyboardButton("🌫️ Blur Fon", callback_data=f"bg_b|p|{short_id}")
267
+ ],
268
+ [
269
+ InlineKeyboardButton("🌈 Gradient Fon", callback_data=f"bg_c|p|{short_id}"),
270
  InlineKeyboardButton("📱 Suv belgisi", callback_data=f"wm|p|{short_id}")
271
  ],
272
  [
 
541
  try: await query.edit_message_text("💎 AI Ultra HD sifatga oshirilmoqda...\n(Bu jarayon bir oz vaqt olishi mumkin)")
542
  except: pass
543
  success_path = await loop.run_in_executor(executor, upscale_image, input_path, output_path)
544
+ elif action.startswith("bg_"):
545
+ bg_mode_map = {
546
+ "bg_t": "transparent", "bg_w": "white",
547
+ "bg_k": "black", "bg_b": "blur", "bg_c": "gradient"
548
+ }
549
+ bg_mode = bg_mode_map.get(action, "transparent")
550
+ mode_names = {"bg_t": "🎨 Shaffof fon", "bg_w": "⬜ Oq fon", "bg_k": "⬛ Qora fon", "bg_b": "🌫️ Blur fon", "bg_c": "🌈 Gradient fon"}
551
+ try: await query.edit_message_text(f"✂️ {mode_names.get(action, 'Fon')} tayyorlanmoqda...\n(AI tahlil + Edge Refinement)")
552
+ except: pass
553
+ success_path = await loop.run_in_executor(
554
+ executor, apply_bg_remove, input_path, output_path, bg_mode
555
+ )
556
  else:
557
  func = {
558
  "r": apply_retro_filter, "u": upscale_image,
559
  "f": apply_face_restore, "a": apply_auto_enhance,
560
  "n": apply_nudenet_filter,
561
  "gl": apply_glitch_filter, "mr": apply_mirror_filter,
562
+ "wm": apply_watermark,
563
  "qb": apply_quality_boost,
564
  }.get(action)
565
 
 
623
  "st_anime": "AI anime", "st_sketch": "AI sketch",
624
  "st_oil": "AI oil paint", "st_cart": "AI cartoon",
625
  "stuz": "sub+o'zbek", "stru": "sub+rus", "sten": "sub+english",
626
+ "qb": "sifat+ pro", "stb": "stabilizatsiya",
627
+ "bg_t": "shaffof fon", "bg_w": "oq fon",
628
+ "bg_k": "qora fon", "bg_b": "blur fon", "bg_c": "gradient fon"
629
  }.get(action, f"filter_{action}")
630
  db.log_history(query.from_user.id, "photo" if m_type == "p" else "video", f_name, file_id)
631