Foydalanuvchi commited on
Commit
9b345f9
Β·
1 Parent(s): bb24ba7

Phase 3 Optimization & Final Deployment

Browse files
Files changed (6) hide show
  1. .env.example +8 -0
  2. deploy.sh +27 -0
  3. docker-compose.yml +19 -0
  4. filters.py +190 -79
  5. main.py +114 -80
  6. requirements.txt +2 -0
.env.example ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Bot To'keni (Botfather dan olinadi)
2
+ BOT_TOKEN=8669871278:AAFvGzurjasmBmV9SbHqtqLSjDkhwW5iDbA
3
+
4
+ # Admin ID (Telegram ID)
5
+ ADMIN_ID=373512294
6
+
7
+ # Hugging Face token (Agar NudeNet yoki boshqa HF modellari shaxsiy bo'lsa)
8
+ HF_TOKEN=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
deploy.sh ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # EditFiltrBot Deployment Script (Linux/Ubuntu)
3
+
4
+ echo "πŸš€ EditFiltrBot serverga yuklanmoqda..."
5
+
6
+ # 1. Tizim paketlarini yangilash
7
+ sudo apt-get update
8
+ sudo apt-get install -y docker.io docker-compose git
9
+
10
+ # 2. Docker servisini yoqish
11
+ sudo systemctl start docker
12
+ sudo systemctl enable docker
13
+
14
+ # 3. .env ni yaratish (agar yo'q bo'lsa)
15
+ if [ ! -f .env ]; then
16
+ echo "βš™οΈ .env fayli topilmadi. .env.example nusxalanmoqda..."
17
+ cp .env.example .env
18
+ echo "⚠️ DIQQAT: .env fayl yaratildi. Iltimos, Token va Admin ID ni to'g'rilab chiqing (nano .env)"
19
+ fi
20
+
21
+ # 4. Dockerni tizimga tushirish qismi
22
+ echo "🐳 Docker konteynerlar ishga tushirilmoqda..."
23
+ sudo docker-compose down
24
+ sudo docker-compose up -d --build
25
+
26
+ echo "βœ… Muvaffaqiyatli! Bot orqa fonda (24/7) ishlamoqda."
27
+ echo "Loglarni ko'rish uchun: sudo docker-compose logs -f"
docker-compose.yml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ editfiltrbot:
5
+ build: .
6
+ container_name: editfiltrbot_container
7
+ restart: unless-stopped
8
+ env_file:
9
+ - .env
10
+ volumes:
11
+ - .:/app
12
+ environment:
13
+ - TZ=Asia/Tashkent
14
+ deploy:
15
+ resources:
16
+ limits:
17
+ memory: 4G
18
+ reservations:
19
+ memory: 2G
filters.py CHANGED
@@ -103,8 +103,8 @@ def upscale_image(image_path, output_path, scale=2):
103
  new_size = (width * scale, height * scale)
104
  upscaled = cv2.resize(img, new_size, interpolation=cv2.INTER_LANCZOS4)
105
 
106
- # Shovqinni tozalash (yuqori sifat)
107
- upscaled = cv2.fastNlMeansDenoisingColored(upscaled, None, 6, 6, 7, 21)
108
 
109
  pill_img = Image.fromarray(cv2.cvtColor(upscaled, cv2.COLOR_BGR2RGB))
110
 
@@ -224,8 +224,8 @@ def apply_face_restore(image_path, output_path):
224
  x2, y2 = min(img.shape[1], x + w + p), min(img.shape[0], y + h + p)
225
  face_roi = img[y1:y2, x1:x2]
226
 
227
- # 1. Yuzni tekislash (skin smoothing - yengilroq Bilateral)
228
- smoothed = cv2.bilateralFilter(face_roi, 7, 60, 60)
229
  smoothed = cv2.medianBlur(smoothed, 3)
230
 
231
  # 2. Detallarni tabiiy qaytarish (Unsharp Mask)
@@ -278,7 +278,7 @@ def apply_auto_enhance(image_path, output_path):
278
  enhanced_final = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
279
 
280
  # Shovqinni yengil tozalash (xiralik oldini olish)
281
- enhanced_final = cv2.fastNlMeansDenoisingColored(enhanced_final, None, 5, 5, 7, 15)
282
 
283
  cv2.imwrite(output_path, enhanced_final)
284
  return output_path
@@ -290,25 +290,117 @@ def apply_auto_enhance(image_path, output_path):
290
  # YANGI VIDEO FILTRLAR (Phase 6)
291
  # ============================================================
292
 
293
- def process_video_slowmo(video_path, output_path, progress_callback=None):
294
- """Videoni 2x sekinlashtiradi (Slow Motion)."""
 
 
295
  try:
296
  video_path = os.path.abspath(video_path)
297
  output_path = os.path.abspath(output_path)
298
- video = VideoFileClip(video_path)
299
- slow_video = video.with_speed_scaled(0.5)
300
- audio_params = {"audio_codec": "aac"} if video.audio else {"audio": False}
301
 
302
- slow_video.write_videofile(
303
- output_path, codec="libx264", fps=video.fps or 24, preset="ultrafast",
304
- threads=4, logger=None, **audio_params
305
- )
306
- video.close()
307
- slow_video.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  return output_path if os.path.exists(output_path) else None
 
309
  except Exception as e:
310
- logger.error(f"Slow Motion xatosi: {e}")
311
  return None
 
 
 
 
 
 
 
 
 
312
 
313
  def process_video_bw(video_path, output_path, progress_callback=None):
314
  """Videoni oq-qora (B&W) holatga o'tkazadi."""
@@ -567,7 +659,19 @@ def process_video_fps_boost(video_path, output_path, target_fps=60, progress_cal
567
  gaussian = cv2.GaussianBlur(interpolated, (0, 0), 1.0)
568
  interpolated = cv2.addWeighted(interpolated, 1.3, gaussian, -0.3, 0)
569
 
 
570
  writer.write(interpolated)
 
 
 
 
 
 
 
 
 
 
 
571
 
572
  prev_frame = next_frame
573
 
@@ -774,14 +878,13 @@ def _transcribe_audio(audio_path):
774
  return [], "unknown"
775
 
776
  def _get_subtitle_font(font_size):
777
- """Subtitr uchun shrift olish (bir marta cache qilinadi)."""
778
  from PIL import ImageFont
779
  font_paths = [
780
- "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
781
- "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
782
- "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf",
783
  "C:/Windows/Fonts/arialbd.ttf",
784
- "C:/Windows/Fonts/arial.ttf",
 
785
  ]
786
  for fp in font_paths:
787
  if os.path.exists(fp):
@@ -792,15 +895,15 @@ def _get_subtitle_font(font_size):
792
  return ImageFont.load_default()
793
 
794
  def _render_subtitle_on_frame(frame, current_text, font, font_size):
795
- """Bitta kadrga subtitr yozish (umumiy yordamchi funksiya)."""
796
- from PIL import ImageDraw
797
 
798
  h, w = frame.shape[:2]
799
  pil_img = Image.fromarray(frame)
800
  draw = ImageDraw.Draw(pil_img)
801
 
802
- # Matnni satrlarga bo'lish
803
- max_chars = max(25, int(w / (font_size * 0.5)))
804
  lines = []
805
  words = current_text.split()
806
  current_line = ""
@@ -818,49 +921,26 @@ def _render_subtitle_on_frame(frame, current_text, font, font_size):
818
  if not lines:
819
  return frame
820
 
821
- # O'lchamlarni hisoblash
822
- line_height = font_size + 4
823
  total_text_h = line_height * len(lines)
824
- padding_x = 12
825
- padding_y = 6
826
-
827
- # Har bir satr uchun kenglikni o'lchash va eng kengini olish
828
- max_text_w = 0
829
- line_widths = []
830
- for line in lines:
831
- bbox = draw.textbbox((0, 0), line, font=font)
832
- tw = bbox[2] - bbox[0]
833
- line_widths.append(tw)
834
- max_text_w = max(max_text_w, tw)
835
 
836
- # Fon o'lchami va joylashuvi (markazda, pastda)
837
- bg_w = max_text_w + padding_x * 2
838
- bg_h = total_text_h + padding_y * 2
839
- bg_x = (w - bg_w) // 2
840
- bg_y = h - bg_h - 16 # pastdan 16px bo'shliq
841
 
842
- # Yarim-shaffof kompakt fon (pill shakli)
843
- overlay = np.array(pil_img).copy()
844
- y1 = max(0, bg_y)
845
- y2 = min(h, bg_y + bg_h)
846
- x1 = max(0, bg_x)
847
- x2 = min(w, bg_x + bg_w)
848
-
849
- # Fon: 60% qora (shaffof effekt)
850
- overlay[y1:y2, x1:x2] = (overlay[y1:y2, x1:x2].astype(np.float32) * 0.4).astype(np.uint8)
851
-
852
- pil_img = Image.fromarray(overlay)
853
- draw = ImageDraw.Draw(pil_img)
854
 
855
- # Matnni markazda chizish
856
  for i, line in enumerate(lines):
857
- tx = (w - line_widths[i]) // 2
858
- ty = bg_y + padding_y + (i * line_height)
 
859
 
860
- # Yupqa soya (1px)
861
- draw.text((tx + 1, ty + 1), line, font=font, fill=(0, 0, 0))
862
- # Asosiy oq matn
863
- draw.text((tx, ty), line, font=font, fill=(255, 255, 255))
 
 
864
 
865
  return np.array(pil_img)
866
 
@@ -1756,13 +1836,30 @@ def apply_style_transfer(image_path, output_path, style="anime"):
1756
 
1757
  if style == "anime":
1758
  # Anime uslubi: Edge-preserving filter + rang kvantizatsiyasi
1759
- # 1. Ranglarni kamaytirish (kvantizatsiya)
1760
- Z = img.reshape((-1, 3)).astype(np.float32)
1761
- K = 16 # ranglar soni (yaxshiroq gradientlar)
1762
- criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
1763
- _, labels, centers = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
 
 
 
 
 
 
 
 
 
1764
  centers = np.uint8(centers)
1765
- quantized = centers[labels.flatten()].reshape(img.shape)
 
 
 
 
 
 
 
 
1766
 
1767
  # 2. Qirralarni aniqlash
1768
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
@@ -1797,24 +1894,38 @@ def apply_style_transfer(image_path, output_path, style="anime"):
1797
 
1798
  elif style == "cartoon":
1799
  # Multfilm uslubi
1800
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1801
- gray = cv2.medianBlur(gray, 5)
1802
- edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
 
 
 
 
 
1803
 
1804
- # Ranglarni tekislash
1805
- color = img.copy()
1806
  for _ in range(5):
1807
- color = cv2.bilateralFilter(color, 9, 300, 300)
1808
 
1809
- # Rang kvantizatsiyasi
1810
  Z = color.reshape((-1, 3)).astype(np.float32)
1811
  K = 8
1812
- criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
1813
- _, labels, centers = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
1814
  centers = np.uint8(centers)
1815
- color = centers[labels.flatten()].reshape(img.shape)
1816
 
 
 
 
 
 
 
 
 
 
1817
  edges_colored = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
 
1818
  result = cv2.bitwise_and(color, edges_colored)
1819
  result = cv2.convertScaleAbs(result, alpha=1.3, beta=20)
1820
  else:
 
103
  new_size = (width * scale, height * scale)
104
  upscaled = cv2.resize(img, new_size, interpolation=cv2.INTER_LANCZOS4)
105
 
106
+ # Shovqinni tozalash (tabiiy to'qimalarni saqlab qolish uchun yumshoqroq)
107
+ upscaled = cv2.fastNlMeansDenoisingColored(upscaled, None, 3, 3, 7, 21)
108
 
109
  pill_img = Image.fromarray(cv2.cvtColor(upscaled, cv2.COLOR_BGR2RGB))
110
 
 
224
  x2, y2 = min(img.shape[1], x + w + p), min(img.shape[0], y + h + p)
225
  face_roi = img[y1:y2, x1:x2]
226
 
227
+ # 1. Yuzni tekislash (skin smoothing - juda yengil Bilateral filter plastmassa bo'lmasligi uchun)
228
+ smoothed = cv2.bilateralFilter(face_roi, 5, 25, 25)
229
  smoothed = cv2.medianBlur(smoothed, 3)
230
 
231
  # 2. Detallarni tabiiy qaytarish (Unsharp Mask)
 
278
  enhanced_final = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
279
 
280
  # Shovqinni yengil tozalash (xiralik oldini olish)
281
+ enhanced_final = cv2.fastNlMeansDenoisingColored(enhanced_final, None, 3, 3, 7, 15)
282
 
283
  cv2.imwrite(output_path, enhanced_final)
284
  return output_path
 
290
  # YANGI VIDEO FILTRLAR (Phase 6)
291
  # ============================================================
292
 
293
+ def process_video_slowmo(video_path, output_path, progress_callback=None, factor=2):
294
+ """Videoni Optical Flow (Farneback) orqali haqiqiy silliq sekinlashtiradi (Smooth Slow-Mo)."""
295
+ cap = None
296
+ writer = None
297
  try:
298
  video_path = os.path.abspath(video_path)
299
  output_path = os.path.abspath(output_path)
 
 
 
300
 
301
+ cap = cv2.VideoCapture(video_path)
302
+ if not cap.isOpened():
303
+ return None
304
+
305
+ orig_fps = cap.get(cv2.CAP_PROP_FPS) or 30
306
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
307
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
308
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
309
+
310
+ # Sekinlashtirish (kadrlar sonini oraliq kadrlar bilan oshiramiz)
311
+ target_fps = orig_fps # FPS bir xil qoladi, lekin kadr ko'payib video cho'ziladi
312
+
313
+ temp_video_path = output_path + "_slw_temp.mp4"
314
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
315
+ writer = cv2.VideoWriter(temp_video_path, fourcc, target_fps, (width, height))
316
+
317
+ ret, prev_frame = cap.read()
318
+ if not ret:
319
+ cap.release()
320
+ return None
321
+
322
+ frame_idx = 0
323
+ h, w = prev_frame.shape[:2]
324
+ y_coords, x_coords = np.mgrid[0:h, 0:w].astype(np.float32)
325
+
326
+ while True:
327
+ ret, next_frame = cap.read()
328
+ if not ret:
329
+ writer.write(prev_frame)
330
+ break
331
+
332
+ frame_idx += 1
333
+ if progress_callback and frame_idx % 5 == 0:
334
+ progress_callback(min(90, int((frame_idx / total_frames) * 90)))
335
+
336
+ # Asl kadrni yozish
337
+ writer.write(prev_frame)
338
+
339
+ # Optical Flow hisoblash (Faqat bitta yo'nalish yetarli slow-mo uchun)
340
+ prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
341
+ next_gray = cv2.cvtColor(next_frame, cv2.COLOR_BGR2GRAY)
342
+
343
+ flow = cv2.calcOpticalFlowFarneback(
344
+ prev_gray, next_gray, None,
345
+ 0.5, 3, 15, 3, 5, 1.2, 0
346
+ )
347
+
348
+ # factor-1 ta oraliq kadrlarni interpolyatsiya qilish
349
+ for i in range(1, factor):
350
+ alpha = i / factor
351
+ flow_step = flow * alpha
352
+ map_x = (x_coords + flow_step[..., 0]).astype(np.float32)
353
+ map_y = (y_coords + flow_step[..., 1]).astype(np.float32)
354
+
355
+ interpolated = cv2.remap(next_frame, map_x, map_y, cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT)
356
+ writer.write(interpolated)
357
+
358
+ prev_frame = next_frame
359
+
360
+ cap.release()
361
+ writer.release()
362
+
363
+ # Audio uzaytirish yoki o'chirish (Slow mode da audio ko'pincha buzuladi)
364
+ try:
365
+ original = VideoFileClip(video_path)
366
+ processed = VideoFileClip(temp_video_path)
367
+
368
+ if original.audio is not None:
369
+ slow_audio = original.audio.fx(importlib.import_module("moviepy.audio.fx.all").speedx, 1.0 / factor)
370
+ final = processed.set_audio(slow_audio)
371
+ final.write_videofile(output_path, codec="libx264", audio_codec="aac", preset="ultrafast", logger=None, threads=4)
372
+ final.close()
373
+ else:
374
+ processed.write_videofile(output_path, codec="libx264", preset="ultrafast", logger=None, threads=4)
375
+
376
+ original.close()
377
+ processed.close()
378
+ except Exception as audio_err:
379
+ logger.warning(f"Sekinlashtirilgan audioni biriktirish xatosi (Audiosiz o'tildi): {audio_err}")
380
+ import shutil
381
+ shutil.move(temp_video_path, output_path)
382
+
383
+ if os.path.exists(temp_video_path):
384
+ try: os.remove(temp_video_path)
385
+ except: pass
386
+
387
+ if progress_callback:
388
+ progress_callback(99)
389
+
390
  return output_path if os.path.exists(output_path) else None
391
+
392
  except Exception as e:
393
+ logger.error(f"Smooth Slow Motion xatosi: {e}")
394
  return None
395
+ finally:
396
+ if cap is not None:
397
+ try: cap.release()
398
+ except: pass
399
+ if writer is not None:
400
+ try: writer.release()
401
+ except: pass
402
+ import gc
403
+ gc.collect()
404
 
405
  def process_video_bw(video_path, output_path, progress_callback=None):
406
  """Videoni oq-qora (B&W) holatga o'tkazadi."""
 
659
  gaussian = cv2.GaussianBlur(interpolated, (0, 0), 1.0)
660
  interpolated = cv2.addWeighted(interpolated, 1.3, gaussian, -0.3, 0)
661
 
662
+
663
  writer.write(interpolated)
664
+
665
+ # RAM tozalash
666
+ del flow_f, map_x_f, map_y_f, warped_prev
667
+ del flow_b, map_x_b, map_y_b, warped_next
668
+ del interpolated, gaussian
669
+
670
+ # Xotirani himoya qilish (Memory Leak oldini olish)
671
+ del prev_gray, next_gray, flow_forward, flow_backward
672
+ if frame_count % 30 == 0:
673
+ import gc
674
+ gc.collect()
675
 
676
  prev_frame = next_frame
677
 
 
878
  return [], "unknown"
879
 
880
  def _get_subtitle_font(font_size):
881
+ """Subtitr uchun aqlli shrift izlash (Impact eng zo'ri - Reel/TikTok uslubi)."""
882
  from PIL import ImageFont
883
  font_paths = [
884
+ "C:/Windows/Fonts/impact.ttf",
 
 
885
  "C:/Windows/Fonts/arialbd.ttf",
886
+ "/usr/share/fonts/truetype/msttcorefonts/Impact.ttf",
887
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
888
  ]
889
  for fp in font_paths:
890
  if os.path.exists(fp):
 
895
  return ImageFont.load_default()
896
 
897
  def _render_subtitle_on_frame(frame, current_text, font, font_size):
898
+ """Kadrga subtitrni zamonaviy TikTok/Reel uslubida (qalin stroke) chizish."""
899
+ from PIL import ImageDraw, Image
900
 
901
  h, w = frame.shape[:2]
902
  pil_img = Image.fromarray(frame)
903
  draw = ImageDraw.Draw(pil_img)
904
 
905
+ # Matnni satrlarga bo'lish - qisqa bo'lishi (max 4-5 so'z) dinamik ko'rinadi
906
+ max_chars = min(20, int(w / (font_size * 0.6)))
907
  lines = []
908
  words = current_text.split()
909
  current_line = ""
 
921
  if not lines:
922
  return frame
923
 
924
+ line_height = font_size + int(font_size * 0.2)
 
925
  total_text_h = line_height * len(lines)
 
 
 
 
 
 
 
 
 
 
 
926
 
927
+ # Reels uslubi: Markaz-pastki
928
+ bg_y = int(h * 0.8) - total_text_h
 
 
 
929
 
930
+ # TikTok qalin stroke efekti (stroke_width)
931
+ stroke_w = max(2, int(font_size * 0.08))
 
 
 
 
 
 
 
 
 
 
932
 
 
933
  for i, line in enumerate(lines):
934
+ # Anchor o'rnatilmagan bo'lsa, qadimgi usulda kenglikni o'lchash
935
+ bbox = draw.textbbox((0, 0), line, font=font)
936
+ tw = bbox[2] - bbox[0]
937
 
938
+ tx = (w - tw) // 2
939
+ ty = bg_y + (i * line_height)
940
+
941
+ # Oq matn, qora va qalin stroke
942
+ draw.text((tx, ty), line, font=font, fill=(255, 255, 255),
943
+ stroke_width=stroke_w, stroke_fill=(0, 0, 0))
944
 
945
  return np.array(pil_img)
946
 
 
1836
 
1837
  if style == "anime":
1838
  # Anime uslubi: Edge-preserving filter + rang kvantizatsiyasi
1839
+
1840
+ # 1. K-Means ni tezlashtirish uchun rasmni kichraytirish (masalan, kenglikni max 500px qilish)
1841
+ h, w = img.shape[:2]
1842
+ max_dim = 500
1843
+ scale = min(1.0, max_dim / max(h, w))
1844
+ if scale < 1.0:
1845
+ small_img = cv2.resize(img, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA)
1846
+ else:
1847
+ small_img = img.copy()
1848
+
1849
+ Z = small_img.reshape((-1, 3)).astype(np.float32)
1850
+ K = 16 # ranglar soni
1851
+ criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) # iteration qisqartirildi
1852
+ _, labels, centers = cv2.kmeans(Z, K, None, criteria, 3, cv2.KMEANS_RANDOM_CENTERS)
1853
  centers = np.uint8(centers)
1854
+
1855
+ # Kichik o'lchamdagi kvantizatsiyalangan rasm
1856
+ quantized_small = centers[labels.flatten()].reshape(small_img.shape)
1857
+
1858
+ # Kvantizatsiyalangan rasmni asl o'lchamga qaytarish (shovqinsiz kattalashtirish)
1859
+ if scale < 1.0:
1860
+ quantized = cv2.resize(quantized_small, (w, h), interpolation=cv2.INTER_NEAREST)
1861
+ else:
1862
+ quantized = quantized_small
1863
 
1864
  # 2. Qirralarni aniqlash
1865
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
1894
 
1895
  elif style == "cartoon":
1896
  # Multfilm uslubi
1897
+ h, w = img.shape[:2]
1898
+
1899
+ # Ranglarni tekislash (Downscale qilish tezroq)
1900
+ scale = min(1.0, 600 / max(h, w))
1901
+ if scale < 1.0:
1902
+ small_color = cv2.resize(img, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA)
1903
+ else:
1904
+ small_color = img.copy()
1905
 
1906
+ color = small_color
 
1907
  for _ in range(5):
1908
+ color = cv2.bilateralFilter(color, 9, 200, 200)
1909
 
1910
+ # K-Means
1911
  Z = color.reshape((-1, 3)).astype(np.float32)
1912
  K = 8
1913
+ criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
1914
+ _, labels, centers = cv2.kmeans(Z, K, None, criteria, 3, cv2.KMEANS_RANDOM_CENTERS)
1915
  centers = np.uint8(centers)
1916
+ color_small_quant = centers[labels.flatten()].reshape(color.shape)
1917
 
1918
+ if scale < 1.0:
1919
+ color = cv2.resize(color_small_quant, (w, h), interpolation=cv2.INTER_NEAREST)
1920
+ else:
1921
+ color = color_small_quant
1922
+
1923
+ # Qirralar to'liq o'lchamda
1924
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1925
+ gray = cv2.medianBlur(gray, 5)
1926
+ edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
1927
  edges_colored = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
1928
+
1929
  result = cv2.bitwise_and(color, edges_colored)
1930
  result = cv2.convertScaleAbs(result, alpha=1.3, beta=20)
1931
  else:
main.py CHANGED
@@ -229,53 +229,43 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
229
  await show_main_menu(update, context)
230
 
231
  async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
232
- """Rasmlarni qabul qilish."""
233
  try:
234
  photo = update.message.photo[-1]
235
  file_id = photo.file_id
236
  short_id = str(update.message.message_id)
237
  media_storage[short_id] = {"file_id": file_id, "type": "photo", "timestamp": datetime.now()}
238
 
239
- text = "πŸ–Ό **Rasm qabul qilindi!**\nQanday mo''jiza yaratamiz?"
240
- if photo.file_size < 150000:
241
- text = "πŸ” **Tahlil:** Rasm biroz xira. 'Ultra HD' yoki 'Face Fix' tavsiya etiladi!\n\n" + text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
 
243
  keyboard = [
244
  [
245
- InlineKeyboardButton("πŸ“Έ Retro Kamera", callback_data=f"r|p|{short_id}"),
246
- InlineKeyboardButton("✨ Sifatni Oshirish", callback_data=f"u|p|{short_id}")
247
- ],
248
- [
249
- InlineKeyboardButton("πŸ‘€ Yuzni Tiniqlash", callback_data=f"f|p|{short_id}"),
250
- InlineKeyboardButton("πŸͺ„ Avto-Tahrir", callback_data=f"a|p|{short_id}")
251
- ],
252
- [
253
- InlineKeyboardButton("πŸ’Ž Sifat+ (Pro)", callback_data=f"qb|p|{short_id}"),
254
- InlineKeyboardButton("πŸ›‘οΈ Media Qalqon", callback_data=f"n|p|{short_id}")
255
- ],
256
- [
257
- InlineKeyboardButton("πŸͺ„ Glitch Effekt", callback_data=f"gl|p|{short_id}"),
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
- [
273
- InlineKeyboardButton("πŸ–ŒοΈ Anime Uslub", callback_data=f"st_anime|p|{short_id}"),
274
- InlineKeyboardButton("πŸ–ŒοΈ Qalam Uslub", callback_data=f"st_sketch|p|{short_id}")
275
  ],
276
  [
277
- InlineKeyboardButton("πŸ–ŒοΈ Moybo'yoq", callback_data=f"st_oil|p|{short_id}"),
278
- InlineKeyboardButton("πŸ–ŒοΈ Multfilm", callback_data=f"st_cart|p|{short_id}")
279
  ],
280
  [
281
  InlineKeyboardButton("πŸ”™ Bekor qilish", callback_data="nav|main")
@@ -315,7 +305,7 @@ async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
315
 
316
 
317
  async def handle_video(update: Update, context: ContextTypes.DEFAULT_TYPE):
318
- """Videolarni qabul qilish."""
319
  try:
320
  message = update.message
321
  video = message.video or message.animation or message.document
@@ -333,57 +323,21 @@ async def handle_video(update: Update, context: ContextTypes.DEFAULT_TYPE):
333
  short_id = str(message.message_id)
334
  media_storage[short_id] = {"file_id": file_id, "type": "video", "timestamp": datetime.now()}
335
 
 
336
  keyboard = [
337
  [
338
- InlineKeyboardButton("🎞️ Retro Video", callback_data=f"r|v|{short_id}"),
339
- InlineKeyboardButton("✨ Sifatni Oshirish", callback_data=f"u|v|{short_id}")
340
- ],
341
- [
342
- InlineKeyboardButton("🐒 Sekinlashtirish", callback_data=f"s|v|{short_id}"),
343
- InlineKeyboardButton("⚫ Oq-Qora", callback_data=f"bw|v|{short_id}")
344
- ],
345
- [
346
- InlineKeyboardButton("🎨 Ranglarni Tahrirlash", callback_data=f"cc|v|{short_id}"),
347
- InlineKeyboardButton("πŸ”‡ Ovozni O'chirish", callback_data=f"ra|v|{short_id}")
348
- ],
349
- [
350
- InlineKeyboardButton("βœ‚οΈ Kesish (O'rtasidan)", callback_data=f"t|v|{short_id}"),
351
- InlineKeyboardButton("πŸ‘€ Yuzni Tiniqlash", callback_data=f"vf|v|{short_id}")
352
- ],
353
- [
354
- InlineKeyboardButton("πŸͺ„ Avto-Tahrir", callback_data=f"va|v|{short_id}")
355
- ],
356
- [
357
- InlineKeyboardButton("πŸš€ 30 FPS", callback_data=f"fps30|v|{short_id}"),
358
- InlineKeyboardButton("πŸš€ 60 FPS", callback_data=f"fps60|v|{short_id}"),
359
- InlineKeyboardButton("πŸš€ 120 FPS", callback_data=f"fps120|v|{short_id}")
360
- ],
361
- [
362
- InlineKeyboardButton("πŸ›‘οΈ Media Qalqon", callback_data=f"n|v|{short_id}")
363
- ],
364
- [
365
- InlineKeyboardButton("πŸ“ Avto-Taglavha", callback_data=f"sub|v|{short_id}")
366
  ],
367
  [
368
- InlineKeyboardButton("πŸͺ„ Glitch Effekt", callback_data=f"gl|v|{short_id}"),
369
- InlineKeyboardButton("πŸͺž Oyna Effekt", callback_data=f"mr|v|{short_id}")
370
- ],
371
- [
372
- InlineKeyboardButton("πŸ“± Suv belgisi", callback_data=f"wm|v|{short_id}"),
373
- InlineKeyboardButton("πŸ“Ή Stabilizatsiya", callback_data=f"stb|v|{short_id}")
374
- ],
375
- [
376
- InlineKeyboardButton("🌐 Sub+O'zbek", callback_data=f"stuz|v|{short_id}"),
377
- InlineKeyboardButton("🌐 Sub+Rus", callback_data=f"stru|v|{short_id}")
378
- ],
379
- [
380
- InlineKeyboardButton("🌐 Sub+English", callback_data=f"sten|v|{short_id}")
381
  ],
382
  [
383
  InlineKeyboardButton("πŸ”™ Bekor qilish", callback_data="nav|main")
384
  ]
385
  ]
386
- await update.message.reply_text("πŸŽ₯ **Video qabul qilindi!**\nQanday effekt beramiz?", reply_markup=InlineKeyboardMarkup(keyboard), parse_mode=constants.ParseMode.MARKDOWN)
387
  except Exception as e:
388
  logger.error(f"Error in handle_video: {e}")
389
  await update.message.reply_text("❌ Xatolik yuz berdi.")
@@ -485,13 +439,93 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
485
  reply_markup=InlineKeyboardMarkup(keyboard), parse_mode=constants.ParseMode.MARKDOWN)
486
  return
487
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  # [Avvalgi button_handler mantiqi davom etadi...]
489
  try:
490
  data = query.data.split("|")
491
  action, m_type, short_id = data[0], data[1], data[2]
492
 
493
  if short_id not in media_storage:
494
- await query.edit_message_text("⚠️ Media ma'lumotlari topilmadi. Iltimos, qayta yuboring.")
495
  return
496
 
497
  # Spam himoyasi: foydalanuvchi juda tez-tez so'rov yuborsa bloklash
 
229
  await show_main_menu(update, context)
230
 
231
  async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
232
+ """Rasmlarni qabul qilish va Kategoriyalangan Menyuni ko'rsatish."""
233
  try:
234
  photo = update.message.photo[-1]
235
  file_id = photo.file_id
236
  short_id = str(update.message.message_id)
237
  media_storage[short_id] = {"file_id": file_id, "type": "photo", "timestamp": datetime.now()}
238
 
239
+ # 1. Haqiqiy Xiralikni tekshirish (Laplacian Variance)
240
+ is_blurry = False
241
+ try:
242
+ file = await context.bot.get_file(file_id)
243
+ input_path = os.path.join(base_dir, f"tmp_chk_{short_id}.jpg")
244
+ await file.download_to_drive(input_path)
245
+ import cv2
246
+ img_chk = cv2.imread(input_path, cv2.IMREAD_GRAYSCALE)
247
+ if img_chk is not None:
248
+ variance = cv2.Laplacian(img_chk, cv2.CV_64F).var()
249
+ if variance < 100: # Odatda 100 dan pasti xira hisoblanadi
250
+ is_blurry = True
251
+ if os.path.exists(input_path):
252
+ os.remove(input_path)
253
+ except Exception as filter_err:
254
+ logger.warning(f"Xiralikni tekshirish xatosi: {filter_err}")
255
+
256
+ text = "πŸ–Ό **Rasm qabul qilindi!**\nO'zingizga kerakli bo'limni tanlang πŸ‘‡"
257
+ if is_blurry:
258
+ text = "πŸ” **Tahlil:** Rasm xiraroq ko'rinmoqda. 'AI Asboblar' bo'limidan sifatni oshirish tavsiya etiladi!\n\n" + text
259
 
260
+ # KATEGORIYALANGAN MENYU (Asosiy)
261
  keyboard = [
262
  [
263
+ InlineKeyboardButton("πŸͺ„ Effektlar", callback_data=f"cat_fx|p|{short_id}"),
264
+ InlineKeyboardButton("πŸ€– AI Asboblar", callback_data=f"cat_ai|p|{short_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  ],
266
  [
267
+ InlineKeyboardButton("🎨 Fon (Tahrirlash)", callback_data=f"cat_bg|p|{short_id}"),
268
+ InlineKeyboardButton("πŸ–ŒοΈ Badiiy Uslublar", callback_data=f"cat_st|p|{short_id}")
269
  ],
270
  [
271
  InlineKeyboardButton("πŸ”™ Bekor qilish", callback_data="nav|main")
 
305
 
306
 
307
  async def handle_video(update: Update, context: ContextTypes.DEFAULT_TYPE):
308
+ """Videolarni qabul qilish va Kategoriyalangan Menyuni ko'rsatish."""
309
  try:
310
  message = update.message
311
  video = message.video or message.animation or message.document
 
323
  short_id = str(message.message_id)
324
  media_storage[short_id] = {"file_id": file_id, "type": "video", "timestamp": datetime.now()}
325
 
326
+ # KATEGORIYALANGAN MENYU (Video uchun)
327
  keyboard = [
328
  [
329
+ InlineKeyboardButton("πŸͺ„ Effektlar", callback_data=f"cv_fx|v|{short_id}"),
330
+ InlineKeyboardButton("πŸ€– AI Kadrlar", callback_data=f"cv_ai|v|{short_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  ],
332
  [
333
+ InlineKeyboardButton("πŸ›  Uzunlik va Ovoz", callback_data=f"cv_ed|v|{short_id}"),
334
+ InlineKeyboardButton("πŸ“ Subtitr va Tarjima", callback_data=f"cv_sb|v|{short_id}")
 
 
 
 
 
 
 
 
 
 
 
335
  ],
336
  [
337
  InlineKeyboardButton("πŸ”™ Bekor qilish", callback_data="nav|main")
338
  ]
339
  ]
340
+ await update.message.reply_text("πŸŽ₯ **Video qabul qilindi!**\nQanday mo'jiza yaratamiz? πŸ‘‡", reply_markup=InlineKeyboardMarkup(keyboard), parse_mode=constants.ParseMode.MARKDOWN)
341
  except Exception as e:
342
  logger.error(f"Error in handle_video: {e}")
343
  await update.message.reply_text("❌ Xatolik yuz berdi.")
 
439
  reply_markup=InlineKeyboardMarkup(keyboard), parse_mode=constants.ParseMode.MARKDOWN)
440
  return
441
 
442
+ # KATEGORIYA MENYULARINI QAYTA ISHLASH (Sub-menyular)
443
+ if query.data.startswith("cat_") or query.data.startswith("cv_"):
444
+ data_parts = query.data.split("|")
445
+ cat_type = data_parts[0]
446
+ m_type = data_parts[1]
447
+ short_id = data_parts[2]
448
+
449
+ keyboard = []
450
+ text = "Tanlang πŸ‘‡"
451
+
452
+ # --- RASM KATEGORIYALARI ---
453
+ if cat_type == "cat_fx":
454
+ text = "πŸͺ„ **Rasm Effektlari:**"
455
+ keyboard = [
456
+ [InlineKeyboardButton("πŸ“Έ Retro Kamera", callback_data=f"r|p|{short_id}"), InlineKeyboardButton("πŸͺ„ Glitch", callback_data=f"gl|p|{short_id}")],
457
+ [InlineKeyboardButton("πŸͺž Oyna", callback_data=f"mr|p|{short_id}"), InlineKeyboardButton("πŸ“± Suv belgisi", callback_data=f"wm|p|{short_id}")],
458
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
459
+ ]
460
+ elif cat_type == "cat_ai":
461
+ text = "πŸ€– **AI Asboblar:**"
462
+ keyboard = [
463
+ [InlineKeyboardButton("✨ Sifat (Ultra HD)", callback_data=f"u|p|{short_id}"), InlineKeyboardButton("πŸ‘€ Yuz (Face Fix)", callback_data=f"f|p|{short_id}")],
464
+ [InlineKeyboardButton("πŸͺ„ Avto-Tahrir", callback_data=f"a|p|{short_id}"), InlineKeyboardButton("πŸ’Ž Sifat+ (Pro)", callback_data=f"qb|p|{short_id}")],
465
+ [InlineKeyboardButton("πŸ›‘οΈ Media Qalqon", callback_data=f"n|p|{short_id}")],
466
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
467
+ ]
468
+ elif cat_type == "cat_bg":
469
+ text = "🎨 **Fonni Tahrirlash (AI):**"
470
+ keyboard = [
471
+ [InlineKeyboardButton("🎨 Shaffof", callback_data=f"bg_t|p|{short_id}"), InlineKeyboardButton("⬜ Oq Fon", callback_data=f"bg_w|p|{short_id}")],
472
+ [InlineKeyboardButton("⬛ Qora Fon", callback_data=f"bg_k|p|{short_id}"), InlineKeyboardButton("🌫️ Blur", callback_data=f"bg_b|p|{short_id}")],
473
+ [InlineKeyboardButton("🌈 Gradient", callback_data=f"bg_c|p|{short_id}")],
474
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
475
+ ]
476
+ elif cat_type == "cat_st":
477
+ text = "πŸ–ŒοΈ **Badiiy Uslublar (AI):**"
478
+ keyboard = [
479
+ [InlineKeyboardButton("πŸ–ŒοΈ Anime", callback_data=f"st_anime|p|{short_id}"), InlineKeyboardButton("πŸ–ŒοΈ Qalam", callback_data=f"st_sketch|p|{short_id}")],
480
+ [InlineKeyboardButton("πŸ–ŒοΈ Moybo'yoq", callback_data=f"st_oil|p|{short_id}"), InlineKeyboardButton("πŸ–ŒοΈ Multfilm", callback_data=f"st_cart|p|{short_id}")],
481
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
482
+ ]
483
+
484
+ # --- VIDEO KATEGORIYALARI ---
485
+ elif cat_type == "cv_fx":
486
+ text = "πŸͺ„ **Video Effektlari:**"
487
+ keyboard = [
488
+ [InlineKeyboardButton("🎞️ Retro", callback_data=f"r|v|{short_id}"), InlineKeyboardButton("⚫ Oq-Qora", callback_data=f"bw|v|{short_id}")],
489
+ [InlineKeyboardButton("πŸͺ„ Glitch", callback_data=f"gl|v|{short_id}"), InlineKeyboardButton("πŸͺž Oyna", callback_data=f"mr|v|{short_id}")],
490
+ [InlineKeyboardButton("🎨 Rang Tahrir", callback_data=f"cc|v|{short_id}"), InlineKeyboardButton("πŸ“± Suv belgisi", callback_data=f"wm|v|{short_id}")],
491
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
492
+ ]
493
+ elif cat_type == "cv_ai":
494
+ text = "πŸ€– **Video AI Kadrlar:**"
495
+ keyboard = [
496
+ [InlineKeyboardButton("✨ Sifatni Oshirish", callback_data=f"u|v|{short_id}"), InlineKeyboardButton("πŸ‘€ Yuzni Tiniqlash", callback_data=f"vf|v|{short_id}")],
497
+ [InlineKeyboardButton("πŸͺ„ Avto-Tahrir", callback_data=f"va|v|{short_id}"), InlineKeyboardButton("πŸ›‘οΈ Media Qalqon", callback_data=f"n|v|{short_id}")],
498
+ [InlineKeyboardButton("πŸ“Ή Stabilizatsiya", callback_data=f"stb|v|{short_id}")],
499
+ [InlineKeyboardButton("πŸš€ 30 FPS", callback_data=f"fps30|v|{short_id}"), InlineKeyboardButton("πŸš€ 60 FPS", callback_data=f"fps60|v|{short_id}")],
500
+ [InlineKeyboardButton("πŸš€ 120 FPS", callback_data=f"fps120|v|{short_id}")],
501
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
502
+ ]
503
+ elif cat_type == "cv_ed":
504
+ text = "πŸ›  **Uzunlik va Ovoz:**"
505
+ keyboard = [
506
+ [InlineKeyboardButton("🐒 Sekinlashtirish", callback_data=f"s|v|{short_id}"), InlineKeyboardButton("βœ‚οΈ Kesish", callback_data=f"t|v|{short_id}")],
507
+ [InlineKeyboardButton("πŸ”‡ Ovozni O'chirish", callback_data=f"ra|v|{short_id}")],
508
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
509
+ ]
510
+ elif cat_type == "cv_sb":
511
+ text = "πŸ“ **Subtitr va Tarjima (AI):**"
512
+ keyboard = [
513
+ [InlineKeyboardButton("πŸ“ Avto-Taglavha (Asl)", callback_data=f"sub|v|{short_id}")],
514
+ [InlineKeyboardButton("🌐 Sub+O'zbek", callback_data=f"stuz|v|{short_id}"), InlineKeyboardButton("🌐 Sub+Rus", callback_data=f"stru|v|{short_id}")],
515
+ [InlineKeyboardButton("🌐 Sub+English", callback_data=f"sten|v|{short_id}")],
516
+ [InlineKeyboardButton("πŸ”™ Orqaga", callback_data="nav|main")]
517
+ ]
518
+
519
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(keyboard), parse_mode=constants.ParseMode.MARKDOWN)
520
+ return
521
+
522
  # [Avvalgi button_handler mantiqi davom etadi...]
523
  try:
524
  data = query.data.split("|")
525
  action, m_type, short_id = data[0], data[1], data[2]
526
 
527
  if short_id not in media_storage:
528
+ await query.edit_message_text("⚠️ Media ma'lumotlari xotiradan tozalangan. Iltimos, aserni qayta yuboring.")
529
  return
530
 
531
  # Spam himoyasi: foydalanuvchi juda tez-tez so'rov yuborsa bloklash
requirements.txt CHANGED
@@ -13,5 +13,7 @@ nudenet
13
  onnxruntime
14
  faster-whisper
15
  rembg[cpu]
 
 
16
  deep-translator
17
  huggingface_hub
 
13
  onnxruntime
14
  faster-whisper
15
  rembg[cpu]
16
+ pymatting
17
+ scipy
18
  deep-translator
19
  huggingface_hub