Реализация AI-удаления фона изображения в мобильном приложении
Удаление фона — одна из немногих AI-задач, которые реально работают on-device без потери качества. На iOS с Vision + Core ML, на Android с ML Kit или ONNX Runtime — результат за 100–300 мс без сетевых запросов. Облачные API нужны только для граничных случаев: волосы с тонкими деталями, прозрачные объекты, сложные текстуры.
On-device: iOS с Vision и Core ML
Apple добавила удаление фона в VisionKit начиная с iOS 16 через VNGenerateForegroundInstanceMaskRequest:
import Vision
import CoreImage.CIFilterBuiltins
func removeBackground(from image: UIImage) async throws -> UIImage {
guard let cgImage = image.cgImage else { throw BGRemovalError.invalidImage }
let request = VNGenerateForegroundInstanceMaskRequest()
let handler = VNImageRequestHandler(cgImage: cgImage)
try handler.perform([request])
guard let result = request.results?.first else { throw BGRemovalError.noResult }
// Получаем маску переднего плана
let maskBuffer = try result.generateScaledMaskForImage(forInstances: result.allInstances, from: handler)
// Применяем маску к исходному изображению через Core Image
let ciImage = CIImage(cgImage: cgImage)
let mask = CIImage(cvPixelBuffer: maskBuffer)
let blendFilter = CIFilter.blendWithMask()
blendFilter.inputImage = ciImage
blendFilter.maskImage = mask
blendFilter.backgroundImage = CIImage.empty() // прозрачный фон
guard let outputCI = blendFilter.outputImage,
let outputCG = CIContext().createCGImage(outputCI, from: outputCI.extent) else {
throw BGRemovalError.filterFailed
}
return UIImage(cgImage: outputCG)
}
VNGenerateForegroundInstanceMaskRequest работает через нейросеть Apple, оптимизированную для Neural Engine. На iPhone 13+ — 80–150 мс на фото с камеры. Без интернета, без оплаты за API.
Для iOS 15 и ниже — VNGeneratePersonSegmentationRequest (только люди, не произвольные объекты):
let request = VNGeneratePersonSegmentationRequest()
request.qualityLevel = .accurate // или .balanced, .fast
request.outputPixelFormat = kCVPixelFormatType_OneComponent8
On-device: Android с ML Kit
class BackgroundRemover(private val context: Context) {
private val segmenter = Segmentation.getClient(
SelfieSegmenterOptions.Builder()
.setDetectorMode(SelfieSegmenterOptions.SINGLE_IMAGE_MODE)
.enableRawSizeMask()
.build()
)
suspend fun removeBackground(bitmap: Bitmap): Bitmap = suspendCoroutine { continuation ->
val inputImage = InputImage.fromBitmap(bitmap, 0)
segmenter.process(inputImage)
.addOnSuccessListener { result ->
val maskBitmap = result.buffer.toMaskBitmap(bitmap.width, bitmap.height)
val outputBitmap = applyMask(bitmap, maskBitmap)
continuation.resume(outputBitmap)
}
.addOnFailureListener { e -> continuation.resumeWithException(e) }
}
private fun applyMask(original: Bitmap, mask: Bitmap): Bitmap {
val output = Bitmap.createBitmap(original.width, original.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
canvas.drawBitmap(original, 0f, 0f, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(mask, 0f, 0f, paint)
return output
}
private fun ByteBuffer.toMaskBitmap(width: Int, height: Int): Bitmap {
rewind()
val maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
maskBitmap.copyPixelsFromBuffer(this)
return maskBitmap
}
}
ML Kit Selfie Segmenter оптимизирован под людей и селфи. Для произвольных объектов — SubjectSegmenterOptions (доступно с ML Kit 17+):
val options = SubjectSegmenterOptions.Builder()
.enableForegroundBitmap()
.build()
val segmenter = SubjectSegmentation.getClient(options)
Облачные API для сложных случаев
Когда on-device даёт плохой результат (тонкие волосы, прозрачность, сложный фон):
remove.bg API — специализированный сервис, лучшее качество для волос и мелких деталей:
func removeBackgroundCloud(imageData: Data) async throws -> Data {
var request = URLRequest(url: URL(string: "https://api.remove.bg/v1.0/removebg")!)
request.httpMethod = "POST"
request.setValue(apiKey, forHTTPHeaderField: "X-Api-Key")
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
body.appendMultipart(boundary: boundary, name: "image_file", filename: "photo.jpg",
contentType: "image/jpeg", data: imageData)
body.appendMultipart(boundary: boundary, name: "size", data: "auto".data(using: .utf8)!)
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (data, response) = try await URLSession.shared.data(for: request)
// Ответ — PNG с прозрачным фоном
return data
}
Clipdrop API (Stability AI) — поддерживает не только людей, но и продукты, животных.
PhotoRoom API — специализация на продуктовых фотографиях для e-commerce.
Архитектура: on-device + cloud fallback
func removeBackground(_ image: UIImage) async -> UIImage {
// Сначала пробуем on-device
if let result = try? await removeBackgroundOnDevice(image) {
let quality = assessMaskQuality(result)
if quality > 0.85 { return result } // Маска хорошего качества
}
// Fallback на облако
guard let imageData = image.jpegData(compressionQuality: 0.9) else { return image }
if let cloudResult = try? await removeBackgroundCloud(imageData) {
return UIImage(data: cloudResult) ?? image
}
return image
}
private func assessMaskQuality(_ image: UIImage) -> Double {
// Оцениваем качество маски по равномерности краёв
// Простая эвристика: соотношение полупрозрачных пикселей к полностью прозрачным
// Высокое соотношение = тонкие детали = хорошее качество
guard let cgImage = image.cgImage else { return 0 }
// ... анализ пикселей маски
return 0.9 // заглушка
}
Постобработка маски
После удаления фона часто нужна доводка:
-
Feathering (размытие краёв) —
CIGaussianBlurна маске, радиус 1–2 пикселя - Erosion (сужение маски) — убирает артефакты пикселей у края
- Hair refinement — облачные API справляются лучше on-device с тонкими волосами
Сроки
On-device удаление фона (iOS VisionKit + Android ML Kit) с базовым UI — 3–4 дня. Cloud fallback + оценка качества маски + постобработка краёв + экспорт PNG — 8–10 дней.







