SafeAd_AI / app.py
Gulzhanatali's picture
Update app.py
5357792 verified
Raw History Blame Contribute Delete
18.8 kB
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()