Реализация AI-генерации 3D-модели по фотографии в мобильном приложении
Сгенерировать 3D-объект из одного снимка на мобильном устройстве — одна из самых ресурсоёмких задач в мобильном AI. Классические подходы требуют десятков фотографий (фотограмметрия) или специального оборудования (LiDAR). Нейросетевая генерация из одного фото — реальная задача в 2024, но с существенными ограничениями по качеству при работе полностью on-device.
Архитектурные варианты
Полностью на устройстве — лёгкие модели типа DepthPro (Apple, 2024) для depth estimation + point cloud, или One-2-3-45 в мобильной редакции. Получаем грубую 3D-структуру, подходящую для AR-превью, но не для экспорта в профессиональные приложения.
Гибрид — на устройстве делаем depth map и начальную сегментацию объекта, на сервере — полноценная 3D-реконструкция через Zero123++, One-2-3-45 или TripoSR. Сервер возвращает .obj или .glb файл.
LiDAR-дополненный — на iPhone 12 Pro+ и iPad Pro есть LiDAR сканер. ARKit + ARMeshAnchor позволяет получить real mesh сцены. Комбинация LiDAR mesh + текстура из камеры + AI texture inpainting даёт качественный результат без сервера.
On-device: DepthPro для начального depth
Apple DepthPro (2024) — Foundation Model для metric depth estimation. Конвертируется в Core ML:
let model = try DepthPro(configuration: MLModelConfiguration())
// Входное изображение → depth map
let inputImage = try MLFeatureValue(cgImage: sourceImage.cgImage!, constraint: nil)
let prediction = try model.prediction(image: inputImage)
// prediction.depth — MLMultiArray с metric depth значениями (в метрах)
let depthArray = prediction.depth // shape [1, H, W]
Depth map → point cloud: для каждого пикселя (x, y) с известным depth Z вычисляем 3D-координату через pinhole camera model с focal length из EXIF. Получаем облако точек.
Визуализация point cloud в AR — через RealityKit и ModelEntity с кастомным MeshDescriptor:
var descriptor = MeshDescriptor(name: "pointCloud")
descriptor.positions = MeshBuffers.Positions(points) // [SIMD3<Float>]
descriptor.primitives = .points(Array(0..<points.count))
let mesh = try MeshResource.generate(from: [descriptor])
let entity = ModelEntity(mesh: mesh, materials: [UnlitMaterial(color: .white)])
Это не полноценная 3D-модель с мешем, а point cloud — визуально работает для демо, для экспорта нужен meshing.
Meshing: Poisson или Marching Cubes
Из point cloud → полигональный меш через алгоритм Poisson Surface Reconstruction. На мобиле — через Open3D (C++ library через Objective-C bridge) или собственная реализация через Metal compute shaders. Poisson reconstruction требует нормалей в каждой точке; нормали оцениваем из локального neighborhood через PCA.
Это нетривиально на мобиле: Open3D скомпилированная для iOS/Android — около 15 МБ бинарник, требует C++17 и работает в фоновом потоке. Результат — .obj файл с мешем.
LiDAR путь: ARKit ARMeshAnchor
На iPhone с LiDAR наиболее надёжный вариант — ARKit:
let configuration = ARWorldTrackingConfiguration()
configuration.sceneReconstruction = .meshWithClassification
// В ARSession delegate
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
for anchor in anchors.compactMap({ $0 as? ARMeshAnchor }) {
let geometry = anchor.geometry
// geometry.vertices, geometry.faces, geometry.normals — готовый меш
exportMesh(geometry: geometry, transform: anchor.transform)
}
}
ARMeshAnchor.geometry.vertices — ARGeometrySource с буфером Metal. Экспорт в .obj:
func exportToOBJ(geometry: ARMeshGeometry, transform: simd_float4x4) -> String {
var obj = ""
let vertices = geometry.vertices
// Итерируем MTLBuffer напрямую через withUnsafeBytes
vertices.buffer.contents().withMemoryRebound(to: SIMD3<Float>.self, capacity: vertices.count) { ptr in
for i in 0..<vertices.count {
let v = ptr[i]
let world = transform * SIMD4<Float>(v.x, v.y, v.z, 1)
obj += "v \(world.x) \(world.y) \(world.z)\n"
}
}
// Аналогично для faces (indices)
return obj
}
Текстурирование меша — проецируем видеокадр на меш через UV-mapping. Это отдельная задача; без неё меш будет серым.
Серверная генерация: TripoSR и Zero123++
Для высокого качества без LiDAR — серверный пайплайн. TripoSR (Stability AI, 2024): принимает одно фото, генерирует .obj за 0.5–1 секунду на A10. API:
func generateModel(from image: UIImage) async throws -> URL {
let imageData = image.jpegData(compressionQuality: 0.9)!
var request = URLRequest(url: URL(string: "https://api.example.com/triposr")!)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// ... upload + poll
}
Результат — .glb файл, загружается в RealityKit через Entity.loadModel(named:) или через ModelEntity(mesh: try .loadModel(contentsOf: url)).
AR-превью результата
Любой из вариантов заканчивается одинаково: показываем 3D-объект в AR через RealityKit/ARSCNView. Пользователь может «поставить» объект на реальную поверхность, повертеть, изменить масштаб. Это закрывает сценарий «посмотреть как мебель выглядит в комнате» или «показать продукт в AR».
Экспорт: .usdz для iOS (родной формат Apple, поддерживает AR Quick Look), .glb для Android и веба.
Процесс
Выбор архитектуры под задачу (LiDAR/on-device depth/сервер), реализация пайплайна захвата и обработки, AR-превью, экспорт в нужные форматы. Отдельно — тестирование на сложных объектах: стеклянные поверхности, тонкие детали, монотонные цвета.
Ориентиры по срокам
LiDAR-based сканирование с экспортом на iOS — 3–5 недель. Полный пайплайн с on-device depth + серверной реконструкцией + AR-превью на обеих платформах — 8–14 недель.







