172 lines
9.2 KiB
Markdown
172 lines
9.2 KiB
Markdown
Dưới đây là kế hoạch Markdown chi tiết giúp bạn tách biệt rạch ròi giữa **Hiệu ứng xóa phông AI** và **Lấy nét vật lý phần cứng**, giải quyết tận gốc lỗi lag ngầm, đồng thời tái cấu trúc lại hệ thống quản lý thuộc tính **Presets** để bổ sung đầy đủ các thông số ánh sáng và sắc độ.
|
|
|
|
---
|
|
|
|
# 📊 KẾ HOẠCH TOÀN DIỆN: ĐỒNG BỘ FOCUS VẬT LÝ, TỐI ƯU LUỒNG BLUR AI VÀ CẤU TRÚC LẠI PRESETS
|
|
|
|
Mục tiêu cốt lõi:
|
|
|
|
1. **Triệt tiêu lag ngầm khi tắt Blur AI:** Sửa lỗi hệ thống vẫn chạy ngầm vòng lặp pixel đồ họa của `multiclass_segmenter.tflite` gây lag mặc dù giao diện không hiển thị hiệu ứng.
|
|
2. **Đồng bộ Focus vật lý:** Giữ nguyên cơ chế lấy nét quang học qua Camera2 API, loại bỏ hoàn toàn sự can thiệp của thuật toán mờ phần mềm lên vật thể.
|
|
3. **Mở rộng thuộc tính Presets:** Định nghĩa lại cấu trúc dữ liệu cấu hình màu sắc, bổ sung các thông số chuyên sâu: *Highlight, Shadow, Saturation, Contrast, Brightness*.
|
|
|
|
---
|
|
|
|
## 🛠️ 1. Khắc Phục Lỗi Lag Ngầm Của Bộ Xử Lý Blur AI (`MainActivity.kt`)
|
|
|
|
**Nguyên nhân gây lag:** Khi bạn tắt chế độ xóa nền AI nhưng tăng thước trượt Blur, ứng dụng hiển thị như thể không dùng AI, nhưng ở tầng mã nguồn Native, điều kiện kiểm tra (`if/else`) chưa chuẩn khiến hàm `processMediaPipeOnlyBokeh()` vẫn bị gọi liên tục dưới nền, ép CPU xử lý ma trận `ByteBuffer` vô ích.
|
|
|
|
### 🟢 Giải pháp: Thiết lập cờ trạng thái hiệu ứng (Feature Flags) để ngắt luồng AI tuyệt đối
|
|
|
|
```kotlin
|
|
// Định nghĩa các chế độ hoạt động của ứng dụng Photobooth
|
|
enum class BackgroundMode {
|
|
DEFAULT, // Giữ nguyên ảnh gốc thô của Camera
|
|
HARDWARE_DOF, // Chỉ dùng Focus vật lý của thấu kính
|
|
AI_BLUR // Dùng AI tách nền và làm mờ phần mềm
|
|
}
|
|
|
|
var currentBackgroundMode = BackgroundMode.DEFAULT
|
|
|
|
fun handleImageStreamPipeline(capturedBitmap: Bitmap, rawMaskBuffer: ByteBuffer, rulerValue: Int): Bitmap {
|
|
// SỬA LỖI LAG NGẦM: Nếu không bật chế độ AI_BLUR hoặc thước trượt bằng 0,
|
|
// trả về ngay lập tức ảnh thô, chặn đứng hoàn toàn vòng lặp CPU.
|
|
if (currentBackgroundMode != BackgroundMode.AI_BLUR || rulerValue == 0) {
|
|
return capturedBitmap
|
|
}
|
|
|
|
// Chỉ khi bật AI_BLUR và rulerValue > 0 thì mới gánh luồng xử lý nặng này
|
|
return processMediaPipeOnlyBokeh(capturedBitmap, rawMaskBuffer, rulerValue)
|
|
}
|
|
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 2. Quy Hoạch Luồng Focus Vật Lý Khi Chạm Khung Ngắm
|
|
|
|
Khi chuyển sang tab **"Focus Vật lý"**, chúng ta chuyển trạng thái hệ thống về `BackgroundMode.HARDWARE_DOF`. Lúc này, hệ thống bỏ qua toàn bộ mặt nạ phân lớp của Google MediaPipe, giao phó 100% việc tạo độ sâu trường ảnh (DoF) tự nhiên cho mô-tơ thấu kính của thiết bị di động.
|
|
|
|
* **Viewfinder:** Gọi lệnh `applyHardwareTouchToFocus()` qua Camera2 API gửi tọa độ vùng đo tiêu cự `MeteringRectangle` xuống phần cứng thấu kính.
|
|
* **Ảnh xuất xưởng:** Nhận trực tiếp Bitmap thô từ cảm biến camera để lưu, đảm bảo ảnh trung thực như máy ảnh cơ, không bị lỗi lẹm viền hay tạo bóng ma (Ghost Shadow) quanh cơ thể.
|
|
|
|
---
|
|
|
|
## 🎨 3. Cấu Trúc Lại Hệ Thống Thuộc Tính Presets Màu Sắc
|
|
|
|
Để các Presets (bộ lọc màu) chỉnh sửa đúng các thuộc tính ánh sáng và sắc độ, chúng ta cần chuyển đổi cấu trúc dữ liệu từ dạng chuỗi (String) đơn giản sang một đối tượng cấu hình có cấu trúc dữ liệu cụ thể (Structured Configuration Class).
|
|
|
|
### 📐 3.1. Thiết kế Model Dữ Liệu Bộ Màu (`PresetModel.kt`)
|
|
|
|
```kotlin
|
|
data class ColorPreset(
|
|
val id: String,
|
|
val name: String,
|
|
// --- Thuộc tính ánh sáng ---
|
|
val brightness: Float, // -1.0f đến 1.0f (Mặc định 0.0f)
|
|
val contrast: Float, // 0.5f đến 2.0f (Mặc định 1.0f)
|
|
val highlight: Float, // -1.0f đến 1.0f (Mặc định 0.0f) -> Mới bổ sung
|
|
val shadow: Float, // -1.0f đến 1.0f (Mặc định 0.0f) -> Mới bổ sung
|
|
// --- Thuộc tính sắc độ & màu sắc ---
|
|
val saturation: Float, // 0.0f đến 2.0f (Mặc định 1.0f)
|
|
val temperature: Float // -0.5f đến 0.5f (Mặc định 0.0f) (Ấm/Lạnh)
|
|
)
|
|
|
|
```
|
|
|
|
### 💻 3.2. Thuật Toán Áp Dụng Thông Số Preset Lên Điểm Ảnh (Color Toning Engine)
|
|
|
|
Thuật toán xử lý ma trận màu sắc dưới đây nhận cấu hình từ các thuộc tính trên để biến đổi hệ màu RGB của bức ảnh:
|
|
|
|
```kotlin
|
|
fun applyAdvancedPreset(inputBitmap: Bitmap, preset: ColorPreset): Bitmap {
|
|
val w = inputBitmap.width
|
|
val h = inputBitmap.height
|
|
val outputBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
|
|
|
for (y in 0 until h) {
|
|
for (x in 0 until w) {
|
|
val pixel = inputBitmap.getPixel(x, y)
|
|
|
|
// Trích xuất kênh màu gốc
|
|
var r = (pixel shr 16 and 0xFF) / 255.0f
|
|
var g = (pixel shr 8 and 0xFF) / 255.0f
|
|
var b = (pixel and 0xFF) / 255.0f
|
|
|
|
// 1. XỬ LÝ ÁNH SÁNG (Brightness & Contrast)
|
|
r = (r - 0.5f) * preset.contrast + 0.5f + preset.brightness
|
|
g = (g - 0.5f) * preset.contrast + 0.5f + preset.brightness
|
|
b = (b - 0.5f) * preset.contrast + 0.5f + preset.brightness
|
|
|
|
// Tính toán độ sáng tổng thể của pixel (Luminance) để định vị vùng Highlight/Shadow
|
|
val luminance = 0.2126f * r + 0.7152f * g + 0.0722f * b
|
|
|
|
// 2. BỔ SUNG: XỬ LÝ HIGHLIGHT & SHADOW
|
|
if (luminance > 0.6f) {
|
|
// Vùng sáng (Highlight): Tăng/Giảm độ sáng dựa trên thông số highlight của Preset
|
|
val weight = (luminance - 0.6f) / 0.4f
|
|
r += preset.highlight * weight
|
|
g += preset.highlight * weight
|
|
b += preset.highlight * weight
|
|
} else if (luminance < 0.4f) {
|
|
// Vùng tối (Shadow): Bù sáng hoặc dìm tối dựa trên thông số shadow của Preset
|
|
val weight = (0.4f - luminance) / 0.4f
|
|
r += preset.shadow * weight
|
|
g += preset.shadow * weight
|
|
b += preset.shadow * weight
|
|
}
|
|
|
|
// 3. XỬ LÝ SẮC ĐỘ (Saturation)
|
|
val gray = luminance
|
|
r = gray + (r - gray) * preset.saturation
|
|
g = gray + (g - gray) * preset.saturation
|
|
b = gray + (b - gray) * preset.saturation
|
|
|
|
// Ép dải màu về giới hạn an toàn [0.0, 1.0] trước khi đóng gói lại pixel
|
|
val finalR = (r.coerceIn(0.0f, 1.0f) * 255).toInt()
|
|
val finalG = (g.coerceIn(0.0f, 1.0f) * 255).toInt()
|
|
val finalB = (b.coerceIn(0.0f, 1.0f) * 255).toInt()
|
|
|
|
outputBitmap.setPixel(x, y, (0xFF shl 24) or (finalR shl 16) or (finalG shl 8) or finalB)
|
|
}
|
|
}
|
|
return outputBitmap
|
|
}
|
|
|
|
```
|
|
|
|
---
|
|
|
|
## 🌐 4. Quản Lý Đồng Bộ File Định Cấu Hình Mẫu (`PresetsStore`)
|
|
|
|
Đối với tính năng **Tải thêm từ Server trực tuyến (Online Store)** qua nút `...` đã lên kế hoạch từ trước, file cấu hình mẫu `manifest.json` trên Server của bạn sẽ được bổ sung các thuộc tính mới đồng bộ như sau:
|
|
|
|
```json
|
|
{
|
|
"presets": [
|
|
{
|
|
"id": "preset_kodak_gold",
|
|
"name": "Kodak Gold 200",
|
|
"url": "https://yourserver.com/assets/presets/kodak_gold.json",
|
|
"version": 1,
|
|
"properties": {
|
|
"brightness": 0.05,
|
|
"contrast": 1.15,
|
|
"highlight": -0.10,
|
|
"shadow": 0.15,
|
|
"saturation": 1.20,
|
|
"temperature": 0.08
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
```
|
|
|
|
---
|
|
|
|
## 📅 5. Kịch Bản Kiểm Thử Đảm Bảo Chất Lượng (QA Checklist)
|
|
|
|
* [ ] **Kiểm thử triệt tiêu lag ngầm (Lag Isolation Test):** Tắt tính năng Xóa nền AI (chuyển sang chế độ Mặc định/Focus vật lý). Kéo thanh trượt Ruler lên tối đa `+100` và di chuyển máy liên tục. (Yêu cầu: Kính ngắm phải mượt mà tuyệt đối 60 FPS, không được khựng giật vì hàm xử lý AI lúc này đã bị khóa chặn hoàn toàn bằng cờ trạng thái).
|
|
* [ ] **Kiểm thử Focus vật lý (Hardware Lens Shift):** Chạm vào các vùng khoảng cách khác nhau (Gần/Xa) trên Viewfinder xem ống kính camera có dịch chuyển mô-tơ lấy nét vật lý chính xác không, ảnh thu được không được phép có vết cắt giả tạo hay răng cưa phần mềm xung quanh tóc.
|
|
* [ ] **Kiểm thử thông số Highlight/Shadow trên Preset:** Áp dụng một bộ màu có thông số `highlight: -0.3` và `shadow: +0.2` lên một bức ảnh có vùng trời quá sáng và vùng tối khuất bóng. (Yêu cầu: Chi tiết vùng mây trên trời được dìm sáng rõ nét lại, vùng tối khuất sau lưng được cứu sáng mượt mà). |