Files
photobooth-app/20_FEAT_MIDAS.md
T

134 lines
6.9 KiB
Markdown

Dưới đây là kế hoạch Markdown chi tiết để hiện thực hóa thuật toán **Xóa phông theo chiều sâu (Depth-Aware Blur)** mô phỏng hiệu ứng tiêu cự vật lý (DSLR Bokeh) cho dự án `photobooth-app`.
Kế hoạch này tích hợp trực tiếp luồng xử lý mô hình AI ước tính khoảng cách (Depth Estimation) và tối ưu hóa hiệu năng bằng cách tính toán ma trận mờ phân cấp theo từng phân vùng khoảng cách (`distance`).
---
# 📊 KẾ HOẠCH TRIỂN KHAI: LOGIC XÓA PHÔNG THEO CHIỀU SÂU TIÊU CỰ (DEPTH-AWARE BOKEH)
Mục tiêu cốt lõi:
1. Nâng cấp mô hình AI từ phân đoạn 2D (Nhị phân) lên mô hình ước tính chiều sâu **MiDaS (Monocular Depth Estimation)**.
2. Trích xuất **Bản đồ chiều sâu (Depth Map)** của bức ảnh để xác định khoảng cách tương đối của từng điểm ảnh.
3. Chia hậu cảnh thành các phân lớp khoảng cách để áp dụng các mức độ mờ tăng tiến (Xa hơn = Mờ hơn).
4. Giữ nguyên 100% độ sắc nét cho đối tượng chính ở vùng tiêu cự (Vùng lấy nét).
---
## 📦 1. Tích Hợp Mô Hình AI Chiều Sâu (`pubspec.yaml`)
Để chạy mô hình AI ước tính chiều sâu `.tflite` trực tiếp trên thiết bị (On-device), chúng ta sử dụng thư viện `tflite_flutter` hoặc tích hợp qua MediaPipe:
```yaml
dependencies:
flutter:
sdk: flutter
# Thư viện để chạy mô hình AI ước tính chiều sâu (.tflite) dưới nền
tflite_flutter: ^0.10.4
image: ^4.3.0
path_provider: ^2.1.3
```
> ⚠️ *Tải file mô hình `midas_v21_small.tflite` (khoảng 15MB, tối ưu cho mobile) đặt vào thư mục `assets/models/` và khai báo trong `pubspec.yaml`.*
---
## 📐 2. Nguyên Lý Phân Lớp Khoảng Cách Đồ Họa (Multi-Stage Blur Layer)
Thay vì tính toán bộ lọc Gaussian Blur cho từng pixel (khiến CPU quá tải và gây lag), thuật toán sẽ tạo sẵn **4 lớp ảnh nền** với các bán kính mờ (`radius`) khác nhau, sau đó dùng ma trận khoảng cách để nhặt điểm ảnh (Pixel Picking).
```
[Ảnh chụp thô từ phần cứng]
├── Lớp Gốc: Độ nét 100% (Tiêu cự đối tượng chính)
├── Lớp Mờ Nhẹ (Radius: 5) --> Áp dụng cho khoảng cách gần (~1m)
├── Lớp Mờ Vừa (Radius: 12) --> Áp dụng cho khoảng cách trung bình (~2.5m)
└── Lớp Mờ Đại (Radius: 25) --> Áp dụng cho khoảng cách rất xa (>5m)
```
---
## 💻 3. Thuật Toán Xử Lý Pixel Chiều Sâu (Depth-Aware Core Engine)
Hàm xử lý dưới đây nhận vào ảnh chụp và một mảng `depthMap` (chứa giá trị khoảng cách từ `0.0` đến `1.0` cho mỗi điểm ảnh do AI trả về). Hàm này bắt buộc phải chạy trong **Flutter Isolate** ngầm.
```dart
import 'package:image/image.dart' as img;
Future<img.Image> _processAdvancedDepthBlur({
required img.Image capturedImage,
required List<double> depthMap, // Mảng lưu giá trị chiều sâu tương ứng từng pixel
}) async {
final w = capturedImage.width;
final h = capturedImage.height;
img.Image output = img.Image(width: w, height: h);
// BƯỚC 1: Tạo sẵn 3 tầng phông nền mờ phân cấp (Multi-Stage Backgrounds)
img.Image blurStage1 = img.gaussianBlur(capturedImage, radius: 5); // Mờ nhẹ (Hậu cảnh gần)
img.Image blurStage2 = img.gaussianBlur(capturedImage, radius: 13); // Mờ vừa (Hậu cảnh trung)
img.Image blurStage3 = img.gaussianBlur(capturedImage, radius: 25); // Mờ đại (Hậu cảnh xa vô cực)
// BƯỚC 2: Duyệt ma trận điểm ảnh và hòa trộn theo khoảng cách vật lý
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
final int idx = y * w + x;
// Lấy giá trị khoảng cách (AI normalized từ 0.0 = Sát ống kính, 1.0 = Xa vô cực)
double distance = depthMap[idx];
img.Color finalPixel;
// --- LOGIC PHÂN LỚP TIÊU CỰ QUANG HỌC ---
if (distance < 0.3) {
// Vùng tiêu cự chính: Giữ nguyên độ sắc nét 100% (Tai, cổ, vai, tóc được bảo vệ)
finalPixel = capturedImage.getPixel(x, y);
} else if (distance >= 0.3 && distance < 0.5) {
// Khoảng cách gần (Ví dụ vật thể ngay sau lưng đối tượng): Lấy lớp mờ nhẹ
finalPixel = blurStage1.getPixel(x, y);
} else if (distance >= 0.5 && distance < 0.75) {
// Khoảng cách trung bình (Bức tường, đồ đạc xa hơn): Lấy lớp mờ vừa
finalPixel = blurStage2.getPixel(x, y);
} else {
// Khoảng cách rất xa (Vô cực, bầu trời, hàng cây xa): Lấy lớp mờ đại tạo hiệu ứng Bokeh chuyên nghiệp
finalPixel = blurStage3.getPixel(x, y);
}
// Ghi hạt màu đã chọn vào tọa độ ảnh xuất xưởng
output.setPixel(x, y, finalPixel);
}
}
// Giải phóng bộ nhớ đệm đồ họa ngay lập tức
blurStage1.clear();
blurStage2.clear();
blurStage3.clear();
return output;
}
```
---
## 🔄 4. Tích Hợp Xem Trước Thời Gian Thực & Thanh Kéo Slider
Để người dùng có thể **Swipe trên màn hình hoặc kéo thanh trượt Ruler** để thay đổi độ sâu trường ảnh, giá trị trượt từ tay người dùng sẽ hoạt động như một hệ số dịch chuyển tiêu cự (`focalMultiplier`):
```dart
double _userFocalMultiplier = 1.0; // Thay đổi khi vuốt màn hình (Kéo sang phải = Tăng hệ số mờ)
// Cập nhật bán kính mờ động dựa theo thao tác của người dùng
int radius1 = (5 * _userFocalMultiplier).toInt().clamp(1, 25);
int radius2 = (13 * _userFocalMultiplier).toInt().clamp(1, 25);
int radius3 = (25 * _userFocalMultiplier).toInt().clamp(1, 25);
```
---
## 📅 5. Kế Hoạch Triển Khai Kiểm Thử (Checklist)
* [ ] **Kiểm thử vùng biên (No-Edge Clipping):** Chụp một bức ảnh có phần tai và vai rõ ràng. Vì khoảng cách (`distance`) dịch chuyển từ tai ra bức tường phía sau là một dải thay đổi tuyến tính (ví dụ từ `0.2` lên `0.4`), viền tai sẽ chuyển sang lớp mờ một cách tự nhiên chứ không bị AI "cắt lẹm" sắc cạnh như mô hình cũ.
* [ ] **Đo lường thời gian xử lý dưới nền:** Vì luồng này chạy đồng thời 3 bộ lọc nhòe, hãy đảm bảo luồng tính toán nằm trọn vẹn trong `Isolate.run()` để kính ngắm camera của ứng dụng photobooth không bị giật lag khi người dùng thao tác liên tục.
* [ ] **Đồng bộ hóa cử chỉ Swipe:** Đảm bảo khi người dùng vuốt sang phải, giá trị `_userFocalMultiplier` tăng tiến đồng đều, giúp toàn bộ các lớp hậu cảnh xa mờ mịn hơn rõ rệt.