Начало развития идеи

С мая по август 2026 года я собирал учебный прототип виртуальной примерки одежды.

Сценарий простой: пользователь загружает тестовую фотографию, выбирает вещь из каталога и получает изображение с результатом примерки.

Идея проекта появилась из проблемы, с которой сталкиваются маркетплейсы. Покупатель не может оценить внешний вид вещи до заказа, поэтому иногда выбирает несколько размеров или похожих моделей. Это увеличивает расходы на доставку, обработку и возвраты.

Виртуальная примерка не определяет размер и не заменяет обычную примерочную. Но она может помочь ответить на один вопрос: подходит ли человеку визуальный образ с выбранной вещью.

Сначала я хотел развернуть модель локально. В итоге проверил IDM-VTON и CatVTON, а затем подключил сторонний API известного поставщика решений для виртуальной примерки.

Первый прототип сделал в Telegram

Полноценный сайт не хотелось разрабатывать до проверки самой идеи. Поэтому сначала я собрал небольшой Telegram-бот на aiogram 3.

Бот последовательно просил два изображения:

  • тестовую фотографию для примерки;

  • изображение вещи.

После этого файлы передавались в функцию инференса, а результат возвращался пользователю.

На первом варианте я сохранял изображения под фиксированными именами person.jpg и garment.jpg. Для одного пользователя этого хватало, но при параллельных запросах возникала проблема: один пользователь мог перезаписать файлы другого.

Я добавил отдельную директорию для каждой сессии:

pythonfrom pathlib import Path
from uuid import uuid4


session_id = uuid4().hex
session_dir = Path("uploads") / session_id
session_dir.mkdir(parents=True, exist_ok=True)

person_path = session_dir / "person.jpg"
garment_path = session_dir / "garment.jpg"

from pathlib import Path from uuid import uuid4 session_id = uuid4().hex session_dir = Path("uploads") / session_id session_dir.mkdir(parents=True, exist_ok=True) person_path = session_dir / "person.jpg" garment_path = session_dir / "garment.jpg"

В результате файлы разных запросов больше не пересекались:

textuploads/
    4f13.../
        person.jpg
        garment.jpg
    8a91.../
        person.jpg
        garment.jpg

uploads/ 4f13.../ person.jpg garment.jpg 8a91.../ person.jpg garment.jpg

Тестовый запуск проходил на RTX 3060 Ti с 8 ГБ видеопамяти и 16 ГБ оперативной памяти.

Генерация одного изображения занимала около 30 секунд. Если одновременно запускать несколько инференсов, видеопамять быстро заканчивалась и процесс падал с CUDA out of memory.

Для прототипа я ограничил число одновременных запусков:

pythonvton_lock = asyncio.Semaphore(1)

vton_lock = asyncio.Semaphore(1)

Запуск модели происходил внутри семафора:

pythonasync with vton_lock:
    loop = asyncio.get_running_loop()

    result_path = await loop.run_in_executor(
        None,
        run_vton,
        person_path,
        garment_path,
    )

async with vton_lock: loop = asyncio.get_running_loop() result_path = await loop.run_in_executor( None, run_vton, person_path, garment_path, )

Semaphore(1) не ускоряет модель. Он просто гарантирует, что на видеокарте одновременно выполняется только одна генерация. Запросы выстраиваются в очередь, зато процесс не падает от нехватки видеопамяти.

IDM-VTON: модель запускается, но результат не всегда подходит

Первой моделью была IDM-VTON. Она доступна в открытом виде, а инструкция по установке есть в репозитории и на Hugging Face.

Веса я скачивал отдельным скриптом:

pythonimport os

from huggingface_hub import snapshot_download


def download_idm_vton():
    target_dir = os.path.join(
        os.getcwd(),
        "models_cache",
        "idm_vton",
    )

    path = snapshot_download(
        repo_id="yisol/IDM-VTON",
        local_dir=target_dir,
    )

    print(f"Модель загружена в {path}")


if __name__ == "__main__":
    download_idm_vton()

import os from huggingface_hub import snapshot_download def download_idm_vton(): target_dir = os.path.join( os.getcwd(), "models_cache", "idm_vton", ) path = snapshot_download( repo_id="yisol/IDM-VTON", local_dir=target_dir, ) print(f"Модель загружена в {path}") if name == "__main__": download_idm_vton()

Загрузка прошла без особых проблем. Первые сложности начались во время проверки результатов.

IDM-VTON подходит для верхней одежды. На простых тестовых изображениях с футболками и рубашками результат иногда выглядел нормально. Но модель плохо справлялась с отклонениями от стандартной позы.

Самая заметная проблема заключалась в изменении силуэта. После примерки часть изображения могла визуально расширяться, а одежда выглядела как слой, наложенный поверх исходной фотографии.

Оригинал
Оригинал
Результат примерки
Результат примерки

Для демонстрации это выглядело терпимо. Для карточки товара уже нет. Пользователь должен увидеть вещь в правдоподобном виде, а не искажённый результат генерации.

У IDM-VTON была и более очевидная проблема: модель не закрывала весь каталог. Брюки, юбки и полноразмерные вещи в нашем сценарии были не менее важны, чем футболки и куртки.

CatVTON закрывает больше категорий, но не решает проблему полностью

Следующим вариантом стала CatVTON. Она выглядела перспективнее, потому что поддерживает верхнюю одежду, нижнюю часть и полноразмерные вещи. Внутри пайплайна используются маски, связанные с категорией одежды и частями тела.

Я проверял промежуточные маски через интерфейс Gradio.

На простых тестовых изображениях результат был лучше, чем у IDM-VTON. Но при переходе к более сложным вещам начались проблемы.

Полноразмерные вещи обрабатывались частично

В нескольких тестах модель переносила только верхнюю часть полноразмерной вещи. Верх изделия появлялся на изображении, а нижняя часть оставалась без изменений.

Похожий сбой повторился на другом примере.

Это не означает, что CatVTON в принципе не поддерживает такие категории. Проблема была в стабильности на тестовых изображениях из моего сценария. Модель могла неправильно определить область замены или выбрать категорию, которая не соответствовала переданной одежде.

Нижняя одежда затрагивала соседние области

С нижней частью одежды ситуация была похожей. Иногда модель не ограничивалась нужной областью и начинала менять соседние элементы изображения.

В одном из тестов исходный верх был изменён, но нижняя часть одежды нормально не появилась. В другом результат выглядел как неверно составленный комплект, хотя на вход подавалась фотография нижней одежды.

Рисунок 5. Ошибка сегментации нижней части одежды в CatVTON.

После нескольких таких результатов стало ясно, что одной установки модели недостаточно. Чтобы довести локальный вариант до приемлемого состояния, пришлось бы отдельно заниматься масками, позой и обработкой каждой категории одежды.

Где заканчивается установка модели и начинается продукт

На этом этапе стало понятно, что «модель запускается» и «модель можно отдавать пользователю» — разные вещи.

Для улучшения локального пайплайна можно было бы добавить:

  • отдельный human parser;

  • оценку позы;

  • ручную корректировку масок;

  • разные параметры для верхней и нижней одежды;

  • отдельную обработку полноразмерных вещей;

  • постобработку результата;

  • проверки качества входной фотографии.

Для исследовательской работы это интересная задача. Но проект был учебным, и целью было быстро проверить пользовательский сценарий, а не разрабатывать собственную систему компьютерного зрения.

На попытки улучшить локальный вариант можно было потратить ещё несколько недель. При этом не было гарантии, что после доработок результат станет стабильным на произвольных тестовых изображениях.

Почему перешёл на сторонний API

После IDM-VTON и CatVTON я решил проверить сторонний API известного поставщика решений для виртуальной примерки. Основным критерием было не количество функций, а качество результата на тех же входных данных.

Через API передавались тестовая фотография и изображение выбранной вещи из каталога. Среднее время генерации составляло около 10–15 секунд. Это оказалось быстрее локального запуска на RTX 3060 Ti.

Стоимость была такой:

  • 8 долларов за 100 запросов;

  • 0,08 доллара за одну генерацию;

  • неудачные запросы также списывались.

Для большого магазина такую модель оплаты нужно считать отдельно. Для учебного MVP цена оказалась приемлемой, потому что не требовалось арендовать GPU и обслуживать собственный ML-сервер.

После перехода на API сайт получил каталог товаров и модальное окно для примерки

.

На backend использовался FastAPI. Frontend передавал тестовую фотографию и идентификатор товара, а сервер находил изображение одежды и формировал запрос к внешнему сервису.

Схема получилась такой:

textБраузер
   ↓
FastAPI
   ↓
Сторонний API виртуальной примерки
   ↓
FastAPI
   ↓
Браузер

Браузер ↓ FastAPI ↓ Сторонний API виртуальной примерки ↓ FastAPI ↓ Браузер

Запрос к backend отправлялся через multipart/form-data. FastAPI поддерживает передачу файлов и полей формы в одном запросе.

Упрощённо endpoint выглядел так:

pythonfrom fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()


@app.post("/api/try-on")
async def try_on(
    person: UploadFile = File(...),
    product_id: str = Form(...),
):
    person_data = await person.read()
    garment_path = get_garment_path(product_id)

    result = await try_on_api(
        person_data=person_data,
        garment_path=garment_path,
    )

    return {"result": result}

from fastapi import FastAPI, File, Form, UploadFile app = FastAPI() @app.post("/api/try-on") async def try_on( person: UploadFile = File(...), product_id: str = Form(...), ): person_data = await person.read() garment_path = get_garment_path(product_id) result = await try_on_api( person_data=person_data, garment_path=garment_path, ) return {"result": result}

API-ключ хранился в переменной окружения:

pythonTRYON_API_KEY = os.getenv("TRYON_API_KEY")

TRYON_API_KEY = os.getenv("TRYON_API_KEY")

Во frontend ключ не попадал. Если отправлять запрос к внешнему сервису напрямую из браузера, пользователь сможет найти ключ в DevTools и использовать баланс владельца аккаунта.

Что изменилось после перехода на API

На тех же тестовых изображениях API давал более предсказуемый результат.

Полноразмерные вещи чаще сохраняли исходную длину, нижняя часть одежды корректнее совмещалась с изображением, а общий силуэт менялся не так заметно.

примерка
примерка
Оригинал
Оригинал

Полностью избавиться от ошибок не получилось. При сложной позе качество всё равно снижалось. Особенно проблемными были изображения, где:

  • человек стоит боком;

  • руки закрывают часть одежды;

  • изображение обрезано;

  • в кадре несколько людей;

  • одежда сливается с фоном;

  • исходная фотография имеет низкое разрешение.

Поэтому я бы не называл результат точной симуляцией посадки. Сервис показывает примерный внешний вид одежды на тестовом изображении, но не определяет размер и не моделирует поведение ткани в движении.

Сравнение вариантов

Параметр

IDM-VTON

CatVTON

Сторонний API

Размещение

Локально

Локально

Внешний сервис

Проверенные категории

Верх

Верх, низ, полноразмерные вещи

Верх, низ, полноразмерные вещи

Результат на тестовых изображениях

Нестабильный

Нестабильный на части категорий

Более предсказуемый

Время генерации

Около 30 секунд

Около 30 секунд

10–15 секунд

Параллельные запросы

Ограничены видеокартой

Ограничены видеокартой

Обрабатываются провайдером

Оплата за запрос

Нет

Нет

8 долларов за 100 запросов

Основной недостаток

Ограниченные категории и изменение силуэта

Ошибки категорий и масок

Стоимость и зависимость от API

Фраза «любые типы одежды» здесь была бы слишком сильной. Даже если сервис заявляет поддержку нескольких категорий, результат зависит от фотографии, позы и качества изображения вещи.

Что нужно добавить для настоящего production-сервиса

Текущий проект был учебным прототипом. Перед использованием в магазине я бы добавил несколько обязательных вещей.

Лимиты

При цене 0,08 доллара за генерацию нельзя оставлять кнопку без ограничений. Пользователь может случайно нажать её несколько раз, а автоматические запросы могут быстро увеличить расходы.

Нужны:

  • лимит примерок на пользователя;

  • защита от повторной отправки;

  • ограничение частоты запросов;

  • дневной бюджет;

  • журнал списаний.

Проверка изображений

До отправки запроса стоит проверить:

  • формат;

  • размер;

  • разрешение;

  • наличие одного человека в кадре;

  • соответствие фотографии требованиям сервиса;

  • наличие товара в каталоге.

Если входная фотография заведомо не подходит, лучше вернуть ошибку до платной генерации.

Асинхронная обработка

Сейчас пользователь ждёт ответ на той же странице. Для MVP это нормально, но при росте нагрузки лучше разделить создание задачи и получение результата:

textPOST /api/try-on
        ↓
job_id
        ↓
GET /api/try-on/{job_id}
        ↓
processing / completed / failed

POST /api/try-on ↓ job_id ↓ GET /api/try-on/{job_id} ↓ processing / completed / failed

На фронтенде можно показывать отдельные состояния:

  • загрузка фотографии;

  • подготовка изображения;

  • примерка одежды;

  • готово;

  • ошибка.

Очистка изображений

Тестовые и пользовательские фотографии, а также результаты примерки, не должны храниться бесконечно. Нужно заранее определить срок хранения, ограничить доступ к файлам и удалять временные данные.

Для демонстрации проекта стоит использовать только собственные тестовые материалы или изображения, на публикацию которых получено разрешение.

Что бы я сделал иначе

Если бы я начинал проект заново, то сначала проверил бы готовый API, а уже потом экспериментировал с локальными моделями.

IDM-VTON и CatVTON всё равно были полезны. Благодаря им стало понятнее, из каких этапов состоит виртуальная примерка и почему одна и та же модель по-разному работает с верхней, нижней и полноразмерной одеждой.

Но локальный запуск оказался дороже по времени, чем я ожидал. Нужно учитывать не только скачивание весов:

  • подготовку окружения;

  • доступную видеопамять;

  • параллельные запросы;

  • построение масок;

  • качество тестовых фотографий;

  • отдельные параметры для разных категорий;

  • обработку неудачных результатов.

Сторонний API добавил стоимость каждого запроса и зависимость от внешнего провайдера. Зато для MVP он позволил быстрее получить результат, который можно показать пользователю.

Итоги

За неделю удалось собрать рабочий прототип с каталогом одежды и виртуальной примеркой.

Путь проекта выглядел так:

textIDM-VTON
   ↓
Telegram-бот
   ↓
Проблемы с качеством и видеопамятью
   ↓
CatVTON
   ↓
Ошибки на полноразмерных вещах и нижней одежде
   ↓
Сторонний API
   ↓
Веб-сервис на FastAPI

IDM-VTON ↓ Telegram-бот ↓ Проблемы с качеством и видеопамятью ↓ CatVTON ↓ Ошибки на полноразмерных вещах и нижней одежде ↓ Сторонний API ↓ Веб-сервис на FastAPI

Главный вывод такой: локальная open-source модель может хорошо выглядеть в демонстрации, но этого недостаточно для пользовательского продукта.

IDM-VTON закрывала только верхнюю одежду и иногда меняла силуэт. CatVTON поддерживала больше категорий, но на тестовых изображениях могла путать верхнюю, нижнюю и полноразмерную одежду. Сторонний API оказался стабильнее и быстрее в рамках прототипа, хотя каждый запрос стоил денег.

Для учебного проекта и проверки продуктовой гипотезы готовый API оказался разумным выбором. Для крупного маркетплейса потребовались бы дополнительные тесты: нагрузка, стоимость хранения, безопасность фотографий, лицензии и измерение влияния на возвраты.

Виртуальная примерка не заменяет размерную сетку и не гарантирует, что вещь сядет по фигуре. Но она может снизить неопределённость перед покупкой. Если человек ещё до заказа понимает, что одежда ему визуально не подходит, часть лишних отправлений и возвратов можно предотвратить.