From 612564389b52a973f4aa33efef5a13afe5fd10a9 Mon Sep 17 00:00:00 2001 From: dddennnisss Date: Sun, 23 Aug 2026 15:06:04 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82-=D1=80=D0=B5=D0=B6?= =?UTF-8?q?=D0=B8=D0=BC:=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D1=82=D1=8C=20=D0=BD=D0=B0=20/check/=20=D0=B0=D0=B2?= =?UTF-8?q?=D1=82=D0=BE-=D0=BA=D1=80=D0=BE=D0=BF=20=D0=BB=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=20(=D0=BA=D0=B0=D0=BA=20=D0=B2=20=D0=BF=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E/=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D0=BA=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8),=20=D0=B0=20=D0=BD=D0=B5=20=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B3=D0=B8=D0=BD=D0=B0=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - make_face_crop_bytes(): тот же квадратный кроп по центру лица, что превью и публикуемый кадр (полное разрешение, JPEG q90) - handlers: кнопка 1 шлёт на проверку кроп, не оригинал - правило «голова >= 30%» теперь меряется по публикуемому результату --- photo_bot/handlers.py | 11 ++++++++-- photo_bot/photo_processor.py | 40 +++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/photo_bot/handlers.py b/photo_bot/handlers.py index 5cde631..f7409d8 100644 --- a/photo_bot/handlers.py +++ b/photo_bot/handlers.py @@ -14,7 +14,7 @@ from trueconf.types import FSInputFile import config.config as config from utils import states from utils.menu import MENU_TEXT -from photo_bot.photo_processor import prepare_ad_photo, get_crop_coordinates, check_photo_api +from photo_bot.photo_processor import prepare_ad_photo, get_crop_coordinates, check_photo_api, make_face_crop_bytes from utils.texts import ( UNKNOWN_MAIN_CMD_TEXT, EMOJI_DIGITS, @@ -339,9 +339,16 @@ async def photo_module_handler(msg: Message): try: with open(original_path, 'rb') as f: raw_photo = f.read() + # На проверку уходит АВТО-КРОП ЛИЦА (тот же, что в превью + # и что публикуется на сайте) — правила «голова ≥ 30%» и + # прочее имеют смысл для публикуемого результата, а не + # для произвольно снятого оригинала. + crop_bytes = await asyncio.to_thread( + make_face_crop_bytes, raw_photo, photo_data.get('coords') + ) # Dry-run: полный пайплайн проверки на САЙТЕ (CV + гримасы + ИИ), # фото в БД/AD/Orion не попадает - check_res = await asyncio.to_thread(check_photo_api, raw_photo) + check_res = await asyncio.to_thread(check_photo_api, crop_bytes) except Exception as e: logger.error(f"🔥 [TEST MODE] Ошибка проверки фото: {e}") check_res = {"status": "error", "reason": f"Ошибка бота: {e}"} diff --git a/photo_bot/photo_processor.py b/photo_bot/photo_processor.py index f7a4abe..84db258 100644 --- a/photo_bot/photo_processor.py +++ b/photo_bot/photo_processor.py @@ -189,13 +189,47 @@ def prepare_ad_photo(input_path: str, user_id: str, output_path: str = None, tar return {"success": False, "error": str(e)} +def make_face_crop_bytes(image_bytes: bytes, coords: tuple) -> bytes: + """ + Вырезает квадратный кадр по центру лица (тот же регион, что превью + бота и публикуемый на сайте кроп) и возвращает JPEG-байты. + + На проверку уходит именно этот кроп: правило «голова ≥ 30%» и все + дальнейшие проверки имеют смысл для публикуемого результата, а не + для произвольно снятого оригинала. + + Returns: + JPEG-байты квадрата (полное разрешение, без ресайза). + """ + from PIL import Image + import io + + img = Image.open(io.BytesIO(image_bytes)) + if img.mode != 'RGB': + img = img.convert('RGB') + + x1, y1, x2, y2 = coords + cropped = img.crop((x1, y1, x2, y2)) + + # Квадрат из центра (как превью) + side = min(cropped.size[0], cropped.size[1]) + left = (cropped.size[0] - side) // 2 + top = (cropped.size[1] - side) // 2 + cropped = cropped.crop((left, top, left + side, top + side)) + + buf = io.BytesIO() + cropped.save(buf, format="JPEG", quality=90) + return buf.getvalue() + + def check_photo_api(image_bytes: bytes, timeout: float = 300.0) -> dict: """ Dry-run проверка фото через API сайта (POST /api/photo-bot/check/). - Фото проходит полный пайплайн проверки на сайте (MediaPipe CV + - ARKit-формы + VLM ИИ), НО ни в БД, ни в AD, ни в Orion не попадает — - ответ возвращается боту для тест-под-режима. + image_bytes — АВТО-КРОП ЛИЦА от бота (make_face_crop_bytes), тот же + кадр, что и превью/публикация. Фото проходит полный пайплайн проверки + на сайте (MediaPipe CV + ARKit-формы + VLM ИИ), НО ни в БД, ни в AD, + ни в Orion не попадает — ответ возвращается боту для тест-под-режима. Returns: {"status": "ok", "verdict", "ai_reason", "cv_approved", "cv_reason",