Реалізація AI-редагування зображень (Outpainting) у мобільному додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Реалізація AI-редагування зображень (Outpainting) у мобільному додатку
Складний
~5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Впровадження редагування зображень з штучним інтелектом (Outpainting) в мобільному додатку

Outpainting розширює зображення за його межами. Користувач завантажує фото 9:16, хоче отримати 16:9 — модель дорисовує бічні частини, зберігаючи стиль і контекст. Або хоче «відсунути камеру»: показати більше оточення навколо об'єкта. DALL-E 2 підтримує це нативно, Stable Diffusion — через те ж inpainting з розширеним полотном.

Логіка на клієнті: підготовка полотна

Суть outpainting: розміс оригіналу на більше полотно (з прозорими або нейтральними полями), відправ на API як image + mask (де mask = нові пусті області), отримай заповнений результат.

func prepareOutpaintingCanvas(
    original: UIImage,
    targetSize: CGSize,
    placement: CGPoint // де розмістити оригінал на полотні
) -> (image: UIImage, mask: UIImage) {
    // Створюємо полотно потрібного розміру
    UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
    let context = UIGraphicsGetCurrentContext()!

    // Заливаємо сірим (нейтральний колір для областей розширення)
    context.setFillColor(UIColor.gray.cgColor)
    context.fill(CGRect(origin: .zero, size: targetSize))

    // Вставляємо оригінал
    original.draw(at: placement)
    let compositeImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    // Створюємо маску: чорні пікселі = зберігти, білі = дорисувати
    // Для DALL-E: альфа-канал замість ч/б
    UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
    let maskContext = UIGraphicsGetCurrentContext()!

    // Білі (прозорі для DALL-E) — нові області
    maskContext.setFillColor(UIColor.white.cgColor)
    maskContext.fill(CGRect(origin: .zero, size: targetSize))

    // Чорні (непрозорі) — оригінальне зображення
    maskContext.setFillColor(UIColor.black.cgColor)
    maskContext.fill(CGRect(origin: placement, size: original.size))
    let maskImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return (compositeImage, maskImage)
}

Позиція оригіналу на полотні визначає напрямок розширення:

  • Центр полотна → розширення з усіх сторін
  • Лівий край → розширення тільки вправо
  • Довільно → користувач керує через drag

UI керування розширенням

Паттерн взаємодії:

  1. Користувач бачить вихідне зображення у «рамці» полотна
  2. Може перетягувати зображення всередину рамки (або рухати рамку)
  3. Вибирає остаточний aspect ratio: 16:9, 4:3, 1:1, або довільний
  4. Натискає «Розширити»
// Android: draggable image inside canvas with GestureDetector
class OutpaintingView(context: Context) : View(context) {
    var imageOffsetX = 0f
    var imageOffsetY = 0f
    private val gestureDetector = GestureDetector(context, object : SimpleOnGestureListener() {
        override fun onScroll(e1: MotionEvent?, e2: MotionEvent, dx: Float, dy: Float): Boolean {
            imageOffsetX -= dx
            imageOffsetY -= dy
            // Обмежуємо смищення межами полотна
            imageOffsetX = imageOffsetX.coerceIn(-maxOffsetX, 0f)
            imageOffsetY = imageOffsetY.coerceIn(-maxOffsetY, 0f)
            invalidate()
            return true
        }
    })

    override fun onDraw(canvas: Canvas) {
        canvas.drawColor(Color.DKGRAY) // фон розширюємої області
        canvas.drawBitmap(originalBitmap, imageOffsetX, imageOffsetY, null)
    }
}

Відправка на DALL-E 2

// DALL-E 2: image та mask передаються як PNG з альфа-каналом
// У маці: прозорість = редагувати, непрозорість = зберігти

func outpaint(composite: UIImage, mask: UIImage, prompt: String, targetSize: String = "1024x1024") async throws -> UIImage {
    // Конвертуємо маску: білі пікселі → прозорі (альфа = 0)
    let alphaMask = convertToAlphaMask(mask)

    guard let compositeData = composite.pngData(),
          let maskData = alphaMask.pngData() else { throw OutpaintError.conversionFailed }

    // Запрос ідентичний inpainting — той ж ендпоінт /v1/images/edits
    return try await sendInpaintRequest(imageData: compositeData, maskData: maskData, prompt: prompt, size: targetSize)
}

private func convertToAlphaMask(_ mask: UIImage) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(mask.size, false, 1.0)
    guard let context = UIGraphicsGetCurrentContext() else { return mask }
    // Інвертуємо: біле → прозоре
    context.setBlendMode(.normal)
    mask.draw(in: CGRect(origin: .zero, size: mask.size))
    // Інверсія альфа-каналу через CIImage
    UIGraphicsEndImageContext()
    // Використовуємо Core Image для інверсії маски
    let ciImage = CIImage(image: mask)!
    let inverted = ciImage.applyingFilter("CIColorInvert")
    return UIImage(ciImage: inverted)
}

Обмеження розмірів

DALL-E 2 приймає лише квадратні зображення: 256×256, 512×512, 1024×1024. Для outpainting у 16:9 потрібне проміжне рішення: генеруємо в 1024×1024, потім кропаємо до потрібного ratio. Або робимо кілька ітерацій outpainting.

Stable Diffusion через Replicate/FAL приймає довільні розміри (кратні 64), що зручніше для outpainting у нестандартних ratio.

Типові проблеми

Видимий шов на границі оригіналу та дорисованої частини — трапляється коли оригінал розміщений із гострим краєм на нейтральному фоні. Рішення: невеликий градієнтний перехід (feather) на краях маски — розмиваємо маску на 10–20 пікселів.

Невідповідність стилю — модель дорисувала щось зовсім іншого стилю. Промпт повинен описувати всю сцену, не лише нову частину: «сонячний міський пейзаж, фотореалізм, теплі тони» а не «продовж».

Терміни

Базовий outpainting з DALL-E 2 (фіксовані напрямки розширення) — 5–7 днів. Інтерактивне draggable полотно з довільним позиціонуванням, множественні ітерації, історія — 3–4 тижні.