Всем привет, с вами я, Наиль Габутдинов, iOS разработчик.
На WWDC 2022 Apple представила новый инструмент RoomPlan, реализованный на основе ARKit 6, который использует LiDAR датчик на новейших iPhone и iPad для быстрого создания 3D планов комнат. По словам Apple, API может быть полезен для приложений в сфере недвижимости, архитектуры и дизайна интерьеров, поскольку он, с одной стороны, точен и гибок в настройке и экспортировании результатов а, с другой стороны, достаточно прост в использовании. Так ли это, давайте разбираться.
![](https://habrastorage.org/getpro/habr/upload_files/1f8/158/774/1f8158774928851b4ab05f028024c6ca.png)
У RoomPlan есть два основных режима использования:
Первый — это полностью готовое решение для сканирования помещения (жилой комнаты) со стандартным интерфейсом, которое легко интегрируется в приложение.
Второй — это более глубокий и гибкий подход, который позволяет вашему приложению самостоятельно настраивать параметры сеанса сканирования, использовать получаемые параметрические данные в режиме реального времени, реализовывать постобработку и получать доступ к детализированному результату.
Давайте рассмотрим оба этих режима подробнее.
Простой API для сканирования
RoomCaptureView — это подкласс UIView, который мы можем просто добавить в свое приложение. Он содержит в себе весь функционал сканирования, а также показа результата и подсказок для пользователя в реальном времени.
Давайте рассмотрим, как начать использовать RoomCaptureView в приложении, написав всего несколько строк кода:
import UIKit
import RoomPlan
class ViewController: UIViewController {
//1
private var roomCaptureView: RoomCaptureView!
//2
private var captureSessionConfig = RoomCaptureSession.Configuration()
override func viewDidLoad() {
super.viewDidLoad()
roomCaptureView = RoomCaptureView()
self.view.addSubview(roomCaptureView)
}
//3
private func startSession() {
roomCaptureView.captureSession.run(configuration: captureSessionConfig)
}
//4
private func stopSession() {
roomCaptureView.captureSession.stop()
}
}
Создаем ссылку на RoomCaptureView в наш ViewController. Инициализируем и добавляем его как обычный UIView.
Добавляем ссылку на объект конфигурации RoomCaptureSession, инициализируем данный объект.
Реализуем запуск сеанса сканирования, передавая нашу конфигурацию функции run.
Также нужно реализовать остановку сеанса сканирования через функцию stop.
При необходимости можно добавить методы протокола RoomCaptureViewDelegate и, например, отказаться от показа результатов постобработки.
extension ViewController: RoomCaptureViewDelegate {
func captureView(shouldPresent roomDataForProcessing: CapturedRoomData, error: Error?) -> Bool {
return false
}
}
Также, если нужно, мы можем экспортировать результаты сканирования в формат USDZ, вызвав функцию export, доступную в структуре CapturedRoom, которую получаем в методе captureView(didPresent: error: ).
func captureView(didPresent processedResult: CapturedRoom, error: Error?) {
try processedResult.export(to: URL)
}
Data API для гибкого использования
В данном разделе разберем базовые структуры данных, к которым дает доступ RoomPlan в процессе сканирования, во время обработки и после получения результата. Эти данные помогают нам самим настраивать визуализацию процесса сканирования и использовать полученный результат для дальнейшей работы.
![](https://habrastorage.org/getpro/habr/upload_files/305/245/6bf/3052456bfce5b3b740118acadc0f27ec.png)
Общий рабочий процесс RoomPlan состоит из трех частей: сканирование, обработка и экспорт. Рассмотрим их подробнее.
Сканирование
Процесс сканирования комнаты в свою очередь можно разделить на:
настройку и запуск сеанса,
отображение прогресса по мере прохождения по комнате.
![](https://habrastorage.org/getpro/habr/upload_files/97e/2e1/ae8/97e2e1ae88c16e07cc5df9089ad77327.png)
В RoomPlan API есть сущность RoomCaptureSession, она нужна для настройки сеанса и для получения текущей информации о процессе сканирования. Давайте рассмотрим её в коде:
class ViewController: UIViewController {
@IBOutlet weak var arView: ARView!
lazy var captureSession: RoomCaptureSession = {
let captureSession = RoomCaptureSession()
arView.session = captureSession.arSession
return captureSession
}
private var captureSessionConfig = RoomCaptureSession.Configuration()
private func startSession() {
captureSession.run(configuration: captureSessionConfig)
}
private func stopSession() {
captureSession.stop()
}
}
Мы просто добавляем новое свойство RoomCaptureSession во ViewController, предварительно сделав импорт ARKit и RoоmPlan. В нужный момент можем запустить сеанс, вызвав функцию run(configuration: ) и передав конфигурацию, и остановить функцией stop().
RoomCaptureSession предоставляет доступ к основному сеансу ARSession, чтобы приложение могло отображать отсканированные плоскости и ограничивающие рамки объектов в AR представлении.
![](https://habrastorage.org/getpro/habr/upload_files/2d1/ddf/38a/2d1ddf38a34078e92ad4a4fce539373a.png)
Кроме того, RoomCaptureSession имеет свойство delegate. Назначение ViewController делегатом позволит получать обновления от RoomCaptureSession в режиме реального времени. Эти обновления включают в себя 3D-модели и рекомендации по текущему сканированию.
Чтобы получать эти обновления, ViewController должен соответствовать протоколу RoomCaptureSessionDelegate, который включает в себя два метода:
extension ViewController: RoomCaptureSessionDelegate {
func captureSession(_ session: RoomCaptureSession, didUpdate room: CapturedRoom) {
//Обработка данных и поверхностях и объектов комнаты в real-time.
}
func captureSession(_ session: RoomCaptureSession, didProvide instruction: RoomCaptureSession.Instruction) {
//Обработка и показ поступающих инструкций (рекомендаций).
}
}
Первый — это метод captureSession(_ session: didUpdate room:) для получения структуры данных о сканируемой комнате CapturedRoom в реальном времени. Приложение может использовать объект CapturedRoom для визуализации в AR-режиме прогресса текущего сканирования, отображая уже отсканированные поверхности и объекты. Подробнее CapturedRoom рассмотрим ниже. Данный метод вызывается при обнаружении обновлений (изменение геометрии зафиксированных ранее поверхностей, появление новых поверхностей и объектов) в комнате.
Второй метод — это captureSession(_ session: didProvide instruction:). Он позволяет получать полезные инструкции (рекомендации) от RoomPlan API в контексте текущего процесса сканирования комнаты.
![](https://habrastorage.org/getpro/habr/upload_files/417/3b9/3d0/4173b93d05dcb2eab64762a5fdebab5d.png)
Эти инструкции можно использовать для отображения в интерфейсе в режиме реального времени, чтобы пользователь получал обратную связь, как отсканировать комнату оптимально. Инструкции могут касаться следующих параметров:
расстояние до объектов,
скорость сканирования,
уровень освещения в помещении,
фокусировка на определенных областях комнаты, которые имеют больше текстур.
Обработка
В этой части рассмотрим, как отсканированные данные будут обработаны и итоговая 3D модель будет получена для показа. Для этого используется класс RoomBuilder.
![](https://habrastorage.org/getpro/habr/upload_files/948/52d/f32/94852df329b4d11742f67e49a9b18e0b.png)
class ViewController: UIViewController {
@IBOutlet weak var arView: ARView!
var roomBuilder = RoomBuilder(options: [.beautifyObjects])
}
Первым делом нужно создать экземпляр RoomBuilder в нашем ViewController. Далее, чтобы получить данные с сенсоров после процесса сканирования, приложение должно реализовать метод captureSession(_ session: didEndWith data: error:), это метод протокола RoomCaptureSessionDelegate, который мы рассматривали выше.
extension ViewController: RoomCaptureSessionDelegate {
func captureSession(_ session: RoomCaptureSession, didEndWith data: CapturedRoomData, error: Error?) {
if let error = error {
// Обработка ошибки
print(error)
}
Task {
if let finalRoom = try? await roomBuilder.capturedRoom(from: data) {
// Показ итоговой модели
preview.update(model: finalRoom)
}
}
}
}
Когда RoomCaptureSession останавливается, этот метод будет вызываться для возврата объекта CaptureRoomData и возможной ошибки, из-за которой произошла остановка. Наконец, для обработки полученных данных мы вызываем асинхронный метод captureRoom(from:) из roomBuilder. Метод реализует обработку отсканированных данных и построение итоговой 3D-модели, это занимает какое-то время (несколько секунд для комнаты), поэтому он выполняется асинхронно.
Экспорт
![](https://habrastorage.org/getpro/habr/upload_files/a41/617/b9d/a41617b9d02e814900cbac3fce128d60.png)
Выше я уже упоминал структуру CapturedRoom, которая представляет собой результат сканирования и обработки. Давайте углубимся в детали: какие данные она содержит, каким образом ее можно экспортировать для последующей работы в других программах.
![](https://habrastorage.org/getpro/habr/upload_files/07b/dbb/c6c/07bdbbc6ced51aa654105eb4ab69d43e.png)
На верхнем уровне CapturedRoom состоит из обнаруженных поверхностей и объектов.
Поверхность содержит следующие атрибуты:
свойства кривых, такие как радиус, начальный и конечный углы;
четыре разных края поверхности;
архитектурная категория поверхности (стена, проём, окно, дверь).
Объект имеет атрибут category, это может быть, например, стол, кровать, диван и т. д.
Поверхность и объект имеют некоторые общие атрибуты, такие как размеры, уровень достоверности (уверенности) при определении, матрица трехмерного преобразования, а также уникальный идентификатор.
Давайте посмотрим, как они представлены в коде.
public struct CapturedRoom : Codable, Sendable {
public var walls: [CapturedRoom.Surface] { get }
public var doors: [CapturedRoom.Surface] { get }
public var windows: [CapturedRoom.Surface] { get }
public var openings: [CapturedRoom.Surface] { get }
public var objects: [CapturedRoom.Object] { get }
public func encode(to encoder: Encoder) throws
...
}
CapturedRoom содержит пять свойств: стены, проемы, двери, окна и объекты в комнате. Первые четыре атрибута представляют собой массивы поверхностей Surface, которые разделены по категориям.
Последнее свойство objects — это массив 3D-объектов в комнате, каждый из которых представляет собой прямоугольный параллелепипед с определенной категорией (диван, стол, стул, кровать и т. д).
Наконец, функция экспорта export(to: ) позволяет экспортировать полученный результат в файлы формата .USD или .USDZ для дальнейшей работы с ними в различных 3D инструментах.
Рекомендации по сканированию
Apple приводит рекомендации по сканированию, которые помогут добиться лучших результатов с помощью RoomPlan:
Объектом для сканирования могут быть отдельные жилые комнаты;
Максимальный размер комнаты – 30x30 футов или около 9x9 метров;
Уровень освещенности – 50 lux или выше.
RoomPlan API поддерживается на всех моделях iPhone и iPad Pro с поддержкой LiDAR.
Вывод
Существующие приложения, способные создавать 3D планы комнат с помощью камеры телефона, не совсем новы, но и не совсем точны. С появлением сканеров LiDAR с iPad Pro 2020 года и iPhone 12 Pro, стало возможным более точно определять размеры объектов и расстояния до них. Как видим, в этом году Apple сама сделала для нас готовый и мощный инструмент для сканирования комнат, позволяющий определять все значимые поверхности и объекты в комнате, точно определять их размеры и расположение, отрисовывать в режиме реального времени прогресс по процессу, давать пользователям полезные подсказки, как лучше сканировать, получать доступ к детальной структуре результата и экспортировать его в удобном формате.
Я считаю, это действительно прорывная технология, применимая в профессиональных приложениях, и в скором времени она может существенно изменить процесс работы архитекторов, дизайнеров интерьеров и специалистов по жилой недвижимости.