import gradio as gr from transformers import pipeline from PIL import Image, ImageOps, ImageEnhance import numpy as np # опционалды кітапханалар try: import cv2 cv2_available = True except Exception: cv2_available = False try: import pytesseract pytesseract_available = True except Exception: pytesseract_available = False # Жеңіл мәтін классификаторы (спам/жарнама анықтау) classifier = None classifier_err = None try: classifier = pipeline("text-classification", model="mrm8488/bert-tiny-finetuned-sms-spam-detection") except Exception as e: classifier_err = str(e) # OCR pipeline (балама: TroCR) ocr_pipeline = None ocr_err = None try: ocr_pipeline = pipeline("image-to-text", model="microsoft/trocr-small-printed") except Exception as e: ocr_err = str(e) # ➊ Сурет мазмұнын талдау модельдері nsfw_cls, nsfw_err = None, None try: nsfw_cls = pipeline("image-classification", model="Falconsai/nsfw_image_detection") except Exception as e: nsfw_err = str(e) zs_cls, zs_err = None, None try: zs_cls = pipeline("zero-shot-image-classification", model="openai/clip-vit-base-patch32") except Exception as e: zs_err = str(e) captioner, caption_err = None, None try: # жеңілі — base нұсқа captioner = pipeline("image-to-text", model="Salesforce/blip-image-captioning-base") except Exception as e: caption_err = str(e) dangerous_keywords = [ "ұтып ал", "ставка", "казино", "ақша", "бонус", "18+","vape","electronic cigarette","vaping device","cigarette","smoking","hookah","shisha", "drug", "cocaine", "heroin", "marijuana", "cannabis", "weed", "жақын таныстық", "тегін", "ойын", "арықта", "салмақ таста","alcohol","beer","wine","vodka","whiskey","champagne", "резать", "бить", "убить", "колоть", "ссылка", "карта", "жалаңаш", "танысу", "миллионер", "сілтеме", "сыйлық","suicide", "selfharm", "depression", "hopeless", "cut", "die", "kill myself", "pain", "sad", "loneliness", "anxiety", "шылым", "алкоголь", "карта", "transaction", "card", "discount", "nude","nudity","naked person","bare chest","underwear","bikini", "sex", "porn", "xxx", "nude", "naked", "body", "erotic", "adult", "sexy", "lingerie", "hot girl", "hot boy", "kiss", "strip", "webcam", "casino", "drug", "violence", "rulet", "bet", "betting", "poker", "jackpot", "lottery", "slot", "roulette", "spin", "gamble", "win big", "bonus", "prize", "money game", "777", "fortune" ] # ➋ Zero-shot үшін англ. белгілер (модель ағылшынша жақсы түсінеді) ZS_DANGER = [ # темекі/вэйп "vape","electronic cigarette","vaping device","cigarette","smoking","hookah","shisha", "drug", "cocaine", "heroin", "marijuana", "cannabis", "weed", # алкоголь "alcohol","beer","wine","vodka","whiskey","champagne", # жалаңаштық Жыныстық мазмұн "nude","nudity","naked person","bare chest","underwear","bikini", "sex", "porn", "xxx", "nude", "naked", "body", "erotic", "adult", "sexy", "lingerie", "hot girl", "hot boy", "kiss", "strip", "webcam", # қару-жарақ (қаласаңыз) "gun","knife","weapon", "violence", "fight", "kill", "blood", "war", "bomb", "attack", "shoot", "murder", "death", "weapon", "assault", # Өзін-өзі зақымдау, психологиялық қауіп (Self-harm / Depression) "suicide", "selfharm", "depression", "hopeless", "cut", "die", "kill myself", "pain", "sad", "loneliness", "anxiety", # құмар ойындар "casino", "drug", "violence", "rulet", "bet", "betting", "poker", "jackpot", "lottery", "slot", "roulette", "spin", "gamble", "win big", "bonus", "prize", "money game", "777", "fortune" ] ZS_SAFE = [ "flower", "bouquet of flowers", "rose", "tulip", "daisy", "plant", "nature", "tree" ] def preprocess_image(image: Image.Image) -> Image.Image: if image.mode != "RGB": image = image.convert("RGB") if cv2_available: arr = np.array(image) gray = cv2.cvtColor(arr, cv2.COLOR_RGB2GRAY) blur = cv2.medianBlur(gray, 3) # Otsu бинаризациясы _, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # фон қою болса инверсия if np.mean(thresh) < 127: thresh = cv2.bitwise_not(thresh) # жұқа әріптерді жақсарту үшін аздап dilation kernel = np.ones((1,1), np.uint8) thresh = cv2.dilate(thresh, kernel, iterations=1) return Image.fromarray(thresh) else: gray = ImageOps.grayscale(image) enh = ImageEnhance.Contrast(gray) enhanced = enh.enhance(1.8) bw = enhanced.point(lambda x: 0 if x < 128 else 255, '1') return bw.convert("RGB") def extract_text_from_image(image: Image.Image, lang: str = "kaz+rus+eng", engine_choice: str = "auto") -> str: """ engine_choice: "auto" | "tesseract" | "trocr" """ # 1) pytesseract (негізгі) if engine_choice in ("auto", "tesseract") and pytesseract_available: try: proc = preprocess_image(image) text = pytesseract.image_to_string(proc, lang=lang, config="--oem 1 --psm 6") text = text.strip() if text or engine_choice == "tesseract": return text except Exception as e: if engine_choice == "tesseract": return f"[pytesseract error: {e}]" # 2) TroCR fallback if engine_choice in ("auto", "trocr") and ocr_pipeline is not None: try: out = ocr_pipeline(image) if isinstance(out, list) and len(out) > 0: text = out[0].get("generated_text", "") or out[0].get("text", "") or str(out[0]) else: text = str(out) return text.strip() except Exception as e: return f"[ocr pipeline error: {e}]" return "[OCR қолжетімсіз: pytesseract және/немесе OCR pipeline жоқ]" # ➌ Сурет мазмұнын қауіптілікке тексеру def analyze_image_content(image: Image.Image) -> str: lines = [] reasons = [] # A) NSFW детекция if nsfw_cls is not None: try: nsfw_res = nsfw_cls(image) # мысалы: [{'label': 'nsfw', 'score': 0.97}, {'label':'sfw','score':0.03}] scores = {d["label"].lower(): d["score"] for d in nsfw_res} nsfw_score = max(scores.get("nsfw", 0.0), scores.get("unsafe", 0.0)) if nsfw_score >= 0.60: reasons.append(f"Жалаңаш/NSFW ықтималдығы жоғары ({nsfw_score:.2f}).") except Exception as e: lines.append(f"ℹ️ NSFW моделі қатесі: {e}") else: if nsfw_err: lines.append(f"ℹ️ NSFW моделі жүктелмеді: {nsfw_err}") # B) Zero-shot (CLIP) — мақсатты белгілер zs_scores = {} if zs_cls is not None: try: labels = ZS_DANGER + ZS_SAFE res = zs_cls(image, candidate_labels=labels, hypothesis_template="a photo of {}") # res сортталған, бәрін dict-ке түсіреміз for r in res: zs_scores[r["label"].lower()] = float(r["score"]) # шектер th_vape = 0.35 th_nude = 0.35 th_alc = 0.30 th_weapon = 0.35 th_casino = 0.35 th_selfharm = 0.35 # темекі/есірткі if max([zs_scores.get(k, 0) for k in ["vape","electronic cigarette","vaping device","cigarette","smoking","hookah","shisha", "drug", "cocaine", "heroin", "marijuana", "cannabis", "weed"]], default=0) >= th_vape: s = max([zs_scores.get(k, 0) for k in ["vape","electronic cigarette","vaping device","cigarette","smoking","hookah","shisha", "drug", "cocaine", "heroin", "marijuana", "cannabis", "weed"]]) reasons.append(f"Темекі/есірткі белгісі ({s:.2f}).") # жалаңаштық Жыныстық мазмұн if max([zs_scores.get(k, 0) for k in ["nude","nudity","naked person","bare chest","underwear","bikini", "sex", "porn", "xxx", "nude", "naked", "body", "erotic", "adult", "sexy", "lingerie", "hot girl", "hot boy", "kiss", "strip", "webcam"]], default=0) >= th_nude: s = max([zs_scores.get(k, 0) for k in ["nude","nudity","naked person","bare chest","underwear","bikini", "sex", "porn", "xxx", "nude", "naked", "body", "erotic", "adult", "sexy", "lingerie", "hot girl", "hot boy", "kiss", "strip", "webcam"]]) reasons.append(f"Жалаңаштық/жеке аймақ ({s:.2f}).") # алкоголь if max([zs_scores.get(k, 0) for k in ["alcohol","beer","wine","vodka","whiskey","champagne"]], default=0) >= th_alc: s = max([zs_scores.get(k, 0) for k in ["alcohol","beer","wine","vodka","whiskey","champagne"]]) reasons.append(f"Алкоголь белгісі ({s:.2f}).") # қару-жарақ зорлық-зомбылық if max([zs_scores.get(k, 0) for k in ["gun","knife","weapon", "violence", "fight", "kill", "blood", "war", "bomb", "attack", "shoot", "murder", "death", "weapon", "assault"]], default=0) >= th_weapon: s = max([zs_scores.get(k, 0) for k in ["gun","knife","weapon", "violence", "fight", "kill", "blood", "war", "bomb", "attack", "shoot", "murder", "death", "weapon", "assault"]]) reasons.append(f"Қару-жарақ, зорлық-зомбылық белгісі ({s:.2f}).") # құмар ойындар if max([zs_scores.get(k, 0) for k in ["casino", "drug", "violence", "rulet", "bet", "betting", "poker", "jackpot", "lottery", "slot", "roulette", "spin", "gamble", "win big", "bonus", "prize", "money game", "777", "fortune"]], default=0) >= th_casino: s = max([zs_scores.get(k, 0) for k in ["casino", "drug", "violence", "rulet", "bet", "betting", "poker", "jackpot", "lottery", "slot", "roulette", "spin", "gamble", "win big", "bonus", "prize", "money game", "777", "fortune"]]) reasons.append(f"Құмар ойындар белгісі ({s:.2f}).") # Өзін-өзі зақымдау, психологиялық қауіп (Self-harm / Depression) if max([zs_scores.get(k, 0) for k in ["suicide", "selfharm", "depression", "hopeless", "cut", "die", "kill myself", "pain", "sad", "loneliness", "anxiety"]], default=0) >= th_selfharm: s = max([zs_scores.get(k, 0) for k in ["suicide", "selfharm", "depression", "hopeless", "cut", "die", "kill myself", "pain", "sad", "loneliness", "anxiety"]]) reasons.append(f"Психологиялық қауіп белгісі ({s:.2f}).") except Exception as e: lines.append(f"ℹ️ Zero-shot қатесі: {e}") else: if zs_err: lines.append(f"ℹ️ Zero-shot моделі жүктелмеді: {zs_err}") # C) Caption (түсіндірме үшін) caption = "" if captioner is not None: try: out = captioner(image) if isinstance(out, list) and len(out) > 0: caption = out[0].get("generated_text", "") or out[0].get("text", "") except Exception as e: lines.append(f"ℹ️ Caption қатесі: {e}") else: if caption_err: lines.append(f"ℹ️ Caption моделі жүктелмеді: {caption_err}") # Нәтижені қалыптастыру if reasons: lines.append("⚠️ Сурет қауіпті (сіздің денсаулығыңыз бен жүйкеңізге зиянды болуы мүмкін):") for r in reasons: lines.append(f"• {r}") else: # қауіпсіздікке ишара — гүл/табиғат жоғары болса safe_score = max([zs_scores.get(k, 0) for k in ["flower","bouquet of flowers","rose","tulip","daisy","plant","nature","tree"]], default=0) if safe_score >= 0.30: lines.append(f"✅ Сурет қауіпсіз (гүл/табиғат ықтималдығы {safe_score:.2f}).") else: lines.append("ℹ️ Нақты қауіп белгісі табылмады. (Белгісіз/бейтарап)") if caption: lines.append(f"📝 Автожазба (caption): {caption}") return "\n".join(lines) def analyze_ad(text, image, lang, engine_choice): parts = [] if classifier_err: parts.append(f"⚠️ Классификатор жүктелмеді: {classifier_err}") if ocr_err and not pytesseract_available: parts.append(f"⚠️ OCR pipeline жүктелмеді: {ocr_err}") if nsfw_err: parts.append(f"ℹ️ Ескерту (NSFW): {nsfw_err}") if zs_err: parts.append(f"ℹ️ Ескерту (Zero-shot): {zs_err}") if caption_err: parts.append(f"ℹ️ Ескерту (Caption): {caption_err}") # 1) қолмен енгізілген мәтін if text and text.strip(): parts.append("📄 Енгізілген мәтін:") parts.append(text) if any(k in text.lower() for k in dangerous_keywords): parts.append("⚠️ Қауіпті жарнама (кілт сөз бойынша анықталды).") else: if classifier is None: parts.append("ℹ️ Классификатор жоқ — модельдік тексеру жүргізілмеді.") else: try: pred = classifier(text)[0] label = pred.get("label", "").lower() score = pred.get("score", 0.0) if ("spam" in label or "toxic" in label) and score > 0.5: parts.append(f"⚠️ Қауіпті жарнама (модель шешімі, сенімділік {score:.2f}).") else: parts.append(f"✅ Қауіпсіз жарнама (модель шешімі, сенімділік {score:.2f}).") except Exception as e: parts.append(f"⚠️ Мәтінді классификациялау қатесі: {e}") else: parts.append("📄 Мәтін енгізілмеді.") # 2) суреттен OCR if image is not None: parts.append("\n🖼️ Суреттен мәтін алу (OCR):") text_from_image = extract_text_from_image(image, lang=lang, engine_choice=engine_choice) parts.append(text_from_image or "(модель мәтін таппады)") if text_from_image.startswith("[") and "error" in text_from_image.lower(): parts.append("❌ OCR қатесі пайда болды. Логтарды тексеріңіз.") else: if text_from_image.strip(): if any(k in text_from_image.lower() for k in dangerous_keywords): parts.append("⚠️ Қауіпті жарнама (кілт сөз бойынша анықталды).") else: if classifier is None: parts.append("ℹ️ Классификатор жоқ — модель арқылы тексерілмеді.") else: try: pred = classifier(text_from_image)[0] label = pred.get("label", "").lower() score = pred.get("score", 0.0) if ("spam" in label or "toxic" in label) and score > 0.5: parts.append(f"⚠️ Қауіпті жарнама (модель шешімі, сенімділік {score:.2f}).") else: parts.append(f"✅ Қауіпсіз жарнама (модель шешімі, сенімділік {score:.2f}).") except Exception as e: parts.append(f"⚠️ Суреттен алынған мәтінді классификациялау қатесі: {e}") else: parts.append("(Суреттен мәтін табылмады.)") # 3) сурет мазмұны (визуал) — жаңа бөлім parts.append("\n🧩 Сурет мазмұнын қауіптілікке талдау:") parts.append(analyze_image_content(image)) else: parts.append("\n🖼️ Сурет жүктелмеді.") return "\n".join(parts) # Интерфейс параметрлері # Интерфейс параметрлері (дизайнмен) custom_css = """ body { background-color: #f0f6ff; } #component-0 { border-radius: 15px; background-color: #e6f0ff; padding: 20px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } textarea { border-radius: 10px; border: 1px solid #5a8dee; padding: 10px; font-size: 16px; } .gradio-container .gr-button { padding: 10px 18px; font-weight: 700; } .gradio-container .gr-button-primary { box-shadow: 0 2px 6px rgba(22,163,74,.25); } .gradio-container .gr-button-secondary { border: 1px solid #cbd5e1 !important; } """ demo = gr.Interface( fn=analyze_ad, inputs=[ gr.Textbox(label="✏️ Жарнама мәтінін енгізіңіз", placeholder="Мысалы: «Тегін iPhone ұтып ал!»"), gr.Image(type="pil", label="🖼️ Жарнама суретін жүктеңіз"), gr.Dropdown( choices=["kaz", "kaz+rus", "kaz+rus+eng", "rus", "eng"], value="kaz+rus+eng", label="🗣️ OCR тілі" ), gr.Radio(choices=["auto", "tesseract", "trocr"], value="auto", label="⚙️ OCR қозғалтқышы"), ], outputs=gr.Textbox(label="🔍 Талдау нәтижесі", lines=18), title="🧠 Қауіпті жарнамаларды анықтау (OCR + Image Safety)", description="Жүйе мәтін мен суреттегі мазмұнды талдап, қауіпті элементтерді анықтайды.", css=custom_css, submit_btn="Талдау", clear_btn="Тазалау", ) demo.launch()