fix: sử dụng AI để nhận diện khuôn mặt và focus
This commit is contained in:
+142
@@ -0,0 +1,142 @@
|
||||
Dựa trên các vấn đề bạn vừa nêu, nguyên nhân cốt lõi nằm ở việc **quản lý trạng thái (State Management)** giữa các tab chưa độc lập (khiến bộ lọc này đè/xóa bộ lọc kia) và **ngưỡng lọc AI (Mask Thresholding)** quá gắt ở vùng biên.
|
||||
|
||||
Dưới đây là kế hoạch Markdown chi tiết để khắc phục triệt để các lỗi trên trong dự án `photobooth-app`.
|
||||
|
||||
---
|
||||
|
||||
# 🛠️ KẾ HOẠCH KHẮC PHỤC LỖI: GIỮ TRẠNG THÁI NỀN, TỐI ƯU BIÊN AI & GỠ BỎ TOAST THÔNG BÁO
|
||||
|
||||
Mục tiêu chính:
|
||||
|
||||
1. **Sửa lỗi mất nền:** Giữ nguyên ảnh nền đã chèn (`BG Replace`) khi người dùng quay lại tab Xóa nền (`Portrait Blur`), cho phép vừa thay nền vừa làm mờ (Blur) chính nền mới đó theo giá trị Ruler.
|
||||
2. **Sửa lỗi cắt nhầm tai/cổ/tóc:** Nới lỏng vùng đệm Alpha, áp dụng bộ lọc mượt viền (Anti-aliasing) để tránh AI "gọt" mất các chi tiết cơ thể.
|
||||
3. **Gỡ bỏ UI thừa:** Xóa hoàn toàn hàm hiển thị thông báo "Đã lưu vào thư viện".
|
||||
|
||||
---
|
||||
|
||||
## 🔄 1. Sửa Lỗi Mất Ảnh Nền Khi Tăng Giảm Mức Độ Blur
|
||||
|
||||
### 🔴 Nguyên nhân:
|
||||
|
||||
Trong logic cũ, biến trạng thái `_selectedBgMode` chỉ nhận một giá trị duy nhất tại một thời điểm (`blur` **hoặc** `replace`). Khi bạn quay lại chỉnh blur, hệ thống chuyển `_selectedBgMode = BackgroundMode.blur` và vô tình làm mất đường dẫn ảnh nền `_selectedBgAssetPath`.
|
||||
|
||||
### 🟢 Giải pháp: tách biệt hai trạng thái cấu hình song song
|
||||
|
||||
Thay vì dùng `enum` loại trừ nhau, ta quản lý độc lập: **Có thay nền hay không?** và **Mức độ làm mờ nền là bao nhiêu?**
|
||||
|
||||
```dart
|
||||
// Cấu trúc trạng thái mới (Tách biệt độc lập)
|
||||
bool _isBgReplacementActive = false; // Bật/tắt chế độ thay nền
|
||||
String? _selectedBgAssetPath; // Đường dẫn ảnh nền nếu có
|
||||
double _blurIntensity = 0.0; // Mức độ blur nền (Áp dụng cho cả nền gốc hoặc nền mới)
|
||||
|
||||
```
|
||||
|
||||
### 💻 Cập nhật lại Logic xử lý Phông nền trong Engine:
|
||||
|
||||
```dart
|
||||
// Trong hàm xử lý đồ họa ngầm:
|
||||
img.Image bgImage;
|
||||
|
||||
if (_isBgReplacementActive && _selectedBgAssetPath != null) {
|
||||
// 1. Nếu người dùng ĐÃ CHỌN THAY NỀN -> Lấy ảnh nền mới làm nền
|
||||
final ByteData bgData = await rootBundle.load(_selectedBgAssetPath!);
|
||||
bgImage = img.decodeImage(bgData.buffer.asUint8List())!;
|
||||
bgImage = img.copyResize(bgImage, width: w, height: h);
|
||||
} else {
|
||||
// 2. Nếu KHÔNG thay nền -> Lấy chính ảnh chụp gốc làm nền (Xóa phông mặc định)
|
||||
bgImage = img.copyResize(foregroundImage, width: w, height: h);
|
||||
}
|
||||
|
||||
// KHẮC PHỤC LỖI: Dù là nền gốc hay nền mới, nếu thanh trượt Blur > 0 thì tiến hành làm mờ nền đó
|
||||
if (_blurIntensity > 0) {
|
||||
int blurRadius = (_blurIntensity * 25).toInt().clamp(1, 25);
|
||||
bgImage = img.gaussianBlur(bgImage, radius: blurRadius);
|
||||
}
|
||||
|
||||
// Tiếp tục đưa bgImage vào vòng lặp trộn Alpha Blending với Foreground người/vật...
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👤 2. Sửa Lỗi AI Cắt Nhầm Tóc, Tai, Cổ, Vai Của Đối Tượng
|
||||
|
||||
### 🔴 Nguyên nhân:
|
||||
|
||||
Mô hình `SelfieSegmenter` trả về mảng `confidences` chứa độ chính xác từ `0.0` đến `1.0`. Lỗi cắt lẹm vào tai, cổ hoặc vai xảy ra do thuật toán đang lấy giá trị cứng ở mức quá cao (ví dụ: `alpha > 0.5` mới giữ lại), khiến các vùng chuyển tiếp hoặc vùng bị sụt giảm chi tiết do ánh sáng bị AI nhận nhầm là phông nền.
|
||||
|
||||
### 🟢 Giải pháp: nới rộng vùng đệm (Soft Boundary) và tối ưu toán học viền
|
||||
|
||||
Ta sẽ hạ thấp ngưỡng nhận diện tối thiểu cho các vùng cơ thể, đồng thời áp dụng hàm **Smoothstep** dốc mềm để bảo vệ tai, cổ, vai và tóc không bị "gọt" mất.
|
||||
|
||||
```dart
|
||||
// VÒNG LẶP TRỘN ẢNH CẢI TIẾN TRÁNH CẮT LẸM
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
final int idx = y * w + x;
|
||||
double alpha = confidences[idx]; // Điểm số từ AI
|
||||
|
||||
// --- THUẬT TOÁN BẢO VỆ CHI TIẾT TAI, CỔ, VAI ---
|
||||
// Thay vì cắt cứng ở 0.5, ta nới rộng vùng nhận diện sang dải rộng hơn (từ 0.15 đến 0.8)
|
||||
if (alpha > 0.15 && alpha < 0.8) {
|
||||
// Hàm nội suy bậc 3 giúp làm mịn vùng biên tiếp giáp, giữ lại phần tai/tóc mảnh
|
||||
alpha = cubicSmoothStep(0.15, 0.8, alpha);
|
||||
} else if (alpha >= 0.8) {
|
||||
alpha = 1.0; // Giữ nguyên 100% chi tiết cơ thể người/vật
|
||||
} else {
|
||||
alpha = 0.0; // Chắc chắn là nền
|
||||
}
|
||||
|
||||
// Tiến hành pha màu Alpha Blending giữa Foreground (Người) và Background (Nền đã xử lý)
|
||||
final fgPixel = foreground.getPixel(x, y);
|
||||
final bgPixel = bgImage.getPixel(x, y);
|
||||
|
||||
final int outR = (fgPixel.r * alpha + bgPixel.r * (1.0 - alpha)).toInt();
|
||||
final int outG = (fgPixel.g * alpha + bgPixel.g * (1.0 - alpha)).toInt();
|
||||
final int outB = (fgPixel.b * alpha + bgPixel.b * (1.0 - alpha)).toInt();
|
||||
|
||||
output.setPixel(x, y, img.ColorRgb8(outR, outG, outB));
|
||||
}
|
||||
}
|
||||
|
||||
// Hàm toán học phụ trợ Smoothstep bậc cao (Cubic) để tạo biên mượt hơn nữa
|
||||
double cubicSmoothStep(double edge0, double edge1, double x) {
|
||||
final double t = ((x - edge0) / (edge1 - edge0)).clamp(0.0, 1.0);
|
||||
return t * t * (3.0 - 2.0 * t);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔇 3. Xóa Bỏ Thông Báo "Đã lưu vào thư viện" Sau Khi Chụp Ảnh
|
||||
|
||||
### 🔴 Nguyên nhân:
|
||||
|
||||
Sau khi luồng `Gal.putImage` hoàn thành, hệ thống đang gọi một hàm thông báo (có thể là `_showSuccessToast()`, `_showSuccessOverlay()`, hoặc `SnackBar`).
|
||||
|
||||
### 🟢 Giải pháp:
|
||||
|
||||
Rà soát lại phần cuối của hàm xử lý chuỗi ảnh `captureAndProcessImagePipeline()` và **xóa bỏ/comment** dòng lệnh kích hoạt thông báo này để giao diện chụp ảnh sạch sẽ hoàn toàn.
|
||||
|
||||
```dart
|
||||
// TRONG PIPELINE XỬ LÝ SAU KHI CHỤP
|
||||
await Gal.putImage(tempFile.path); // Lưu ảnh âm thầm dưới nền thành công
|
||||
await tempFile.delete(); // Giải phóng bộ nhớ tạm
|
||||
|
||||
// ❌ XÓA HOẶC COMMENT BỎ DÒNG LỆNH NÀY:
|
||||
// _showSuccessToast();
|
||||
// ScaffoldMessenger.of(context).showSnackBar(...);
|
||||
|
||||
print("Xử lý và lưu ảnh hoàn tất im lặng.");
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📅 4. Checklist Kiểm Thử Sau Khi Sửa Lỗi (Testing Checklist)
|
||||
|
||||
* [ ] **Test giữ nền:** Chọn một ảnh nền bất kỳ (`Nền 1`), sau đó chuyển sang tab **Xóa nền** trượt Slider/Swipe trên màn hình xem ảnh nền có bị mất đi không. (Yêu cầu: Nền 1 vẫn giữ nguyên và mờ dần đi theo tay vuốt).
|
||||
* [ ] **Test cắt viền:** Chụp nghiêng góc để lộ rõ tai, vai, và tóc xù. Phóng to ảnh kiểm tra xem phần tai có bị lẹm méo hay mất một phần vai/cổ không.
|
||||
* [ ] **Test thông báo:** Nhấn nút Shutter chụp 3 tấm liên tiếp, quan sát màn hình xem có hiện bất kỳ chữ hay hộp thoại thông báo nào che mắt người dùng hay không. (Yêu cầu: Chỉ chớp trắng 1 cái và ảnh thu nhỏ bay về nút Media ở góc dưới).
|
||||
Reference in New Issue
Block a user