Implement anti-ghosting inpainting for original background blur during photo capture as described in 25_FIX_CAMERA.md

This commit is contained in:
2026-07-06 19:52:12 +07:00
parent c95f12cedc
commit a7a539b97f
3 changed files with 300 additions and 1 deletions
+138
View File
@@ -0,0 +1,138 @@
Ý tưởng của bạn rất hay và hoàn toàn khả thi! Thay vì đóng gói cứng tất cả file `.png`, `.jpg`, hay `.json` vào ứng dụng làm tăng dung lượng bộ cài, việc tạo một **Thư viện tài nguyên động (Dynamic Asset Library)** sẽ giúp ứng dụng của bạn linh hoạt hơn rất nhiều. Người dùng có thể tự chọn, nhấn nút `...` để tải thêm những bộ khung Instax mới, phông nền AI mới hoặc các bộ màu Hot Trend từ Server về máy.
Dưới đây là kế hoạch Markdown chi tiết từ kiến trúc lưu trữ Server, cấu trúc giao diện cho đến luồng tải tệp tin dưới nền.
---
# 🌐 KẾ HOẠCH TRIỂN KHAI: THƯ VIỆN ĐỘNG TRỰC TUYẾN (ONLINE ASSETS STORE)
Mục tiêu cốt lõi:
1. Xây dựng cấu trúc lưu trữ gọn nhẹ trên Server (hoặc các dịch vụ Cloud Storage như Firebase, Supabase, hay Hosting riêng).
2. Thêm nút `...` (More) đồng cấp vào cuối các dải cuộn của 3 mục: **Frame, Background, Presets**.
3. Thiết lập luồng tải tệp tin bất đồng bộ, tự động lưu vào bộ nhớ máy (`Internal Storage`) để ứng dụng sử dụng offline cho các lần sau mà không cần tải lại.
---
## 📂 1. Thiết Kế Kiến Trúc Lưu Trữ Trên Cloud Server
Server của bạn chỉ cần lưu trữ các tệp tin asset trực tiếp và một file `manifest.json` tổng để ứng dụng Photobooth đọc danh sách.
### Cấu trúc file `manifest.json` trên Server:
```json
{
"frames": [
{ "id": "frame_classic", "name": "Instax Classic", "url": "https://yourserver.com/assets/frames/instaxframe.png", "version": 1 },
{ "id": "frame_vintage", "name": "Vintage Film", "url": "https://yourserver.com/assets/frames/vintage_film.png", "version": 1 }
],
"backgrounds": [
{ "id": "bg_neon", "name": "Neon Street", "url": "https://yourserver.com/assets/bg/neon_street.jpg", "version": 1 }
],
"presets": [
{ "id": "preset_fuji", "name": "Fuji Retro", "url": "https://yourserver.com/assets/presets/fuji_retro.json", "version": 2 }
]
}
```
---
## 📐 2. Quy Hoạch Lại Giao Diện Điều Khiển (UI/UX Layer)
Tại mỗi dải cuộn của Menu Tầng 2 (ngay sau danh sách các item có sẵn như trong ảnh `image_ce9f68.jpg`), ta thêm một ô nút chức năng đặc biệt làm nút **"Tải thêm"** (biểu tượng dấu `...` hoặc `+` tùy bạn chọn).
```
[DANH SÁCH CUỘN NGANG TẦNG 2]
├── [Mặc định] ── [Nền 1] ── [Nền 2] ── [Nền 3] ── 🔳 [ Nút "..." Tải Thêm ]
```
### Logic hiển thị khi người dùng tương tác:
* Khi nhấn vào nút `...`, một hộp thoại (Bottom Sheet hoặc Dialog Popup) sẽ hiện lên, tự động lấy dữ liệu từ file `manifest.json` trên mạng để hiển thị kho lưu trữ trực tuyến.
* Mỗi item trên kho sẽ có một nút **Tải về (Download)**. Nếu tệp tin đã được tải về máy rồi, nút đó sẽ đổi thành chữ **"Áp dụng"**.
---
## 💻 3. Thuật Toán Lõi Tải Và Quản Lý Tệp Tin Offline (`MainActivity.kt`)
Để thực hiện tải và đọc file động từ bộ nhớ thay vì thư mục `assets` cứng, chúng ta sử dụng thư viện mạng và đường dẫn thư mục lưu trữ nội bộ của ứng dụng (`filesDir`).
### 📦 Thêm thư viện tải tệp trong gói cấu hình (Android Native/Flutter):
Nếu làm ở tầng Native Android, bạn sử dụng `HttpURLConnection` hoặc thư viện `OkHttp`. Nếu ở tầng Flutter, bạn dùng package `dio` kết hợp với `path_provider`.
### 💻 Logic xử lý tải và lưu tệp ngầm dưới thiết bị:
```kotlin
import java.io.File
import java.net.URL
class DynamicAssetManager(private val context: Context) {
// Hàm thực hiện tải file ẩn dưới nền khi người dùng nhấn nút tải ở mục "..."
fun downloadAsset(fileUrl: String, assetType: String, fileName: String, onComplete: (File?) -> Unit) {
// Chạy ngầm hoàn toàn không gây lag giao diện camera
Thread {
try {
// Tạo thư mục lưu trữ cục bộ riêng cho từng loại: /data/user/0/com.app/files/downloaded_frames/
val targetDir = File(context.filesDir, "downloaded_$assetType")
if (!targetDir.exists()) targetDir.mkdirs()
val outputFile = File(targetDir, fileName)
// Kích hoạt luồng đọc ghi dòng dữ liệu từ Internet
URL(fileUrl).openStream().use { input ->
output.outputStream().use { outputStream ->
input.copyTo(outputStream)
}
}
// Trả về file đã tải thành công để cập nhật UI
onComplete(outputFile)
} catch (e: Exception) {
e.printStackTrace()
onComplete(null)
}
}.start()
}
// Hàm kiểm tra xem file này đã được tải về máy trước đó chưa
fun getLocalAssetFile(assetType: String, fileName: String): File? {
val file = File(context.filesDir, "downloaded_$assetType/$fileName")
return if (file.exists()) file else null
}
}
```
---
## 🔄 4. Cải Tiến Cụm Engine Để Đọc Được Cả Hai Nguồn Asset
Để hệ thống xử lý ảnh (như thuật toán MiDaS, MediaPipe hay Ép khung PNG) nhận diện được cả file cứng trong app lẫn file động mới tải về, bạn chỉ cần thay đổi hàm nạp ảnh sang cơ chế **Đọc Đa Luồng (Hybrid Asset Loader)**:
```kotlin
// THAY THẾ CƠ CHẾ ĐỌC CỨNG:
// Trước đây: val frameData = context.assets.open("frames/instaxframe.png")
// BÂY GIỜ: Kiểm tra file local trước, nếu không có mới tìm trong assets mặc định
val frameBitmap = val localFile = assetManager.getLocalAssetFile("frames", "instaxframe.png")
val finalFrame = if (localFile != null) {
BitmapFactory.decodeFile(localFile.absolutePath) // Nạp file người dùng vừa tải từ nút "..."
} else {
BitmapFactory.decodeStream(context.assets.open("frames/instaxframe.png")) // Nạp file mặc định của app
}
```
---
## 📅 5. Kế Hoạch Triển Khai Kiểm Thử (Checklist)
* [ ] **Kiểm thử kết nối mạng:** Tắt mạng (Wifi/Mạng di động) rồi bấm vào nút `...` xem ứng dụng có xử lý lỗi mượt mà không (Yêu cầu: Hiện thông báo nhẹ *"Vui lòng kết nối mạng để tải thêm tài nguyên"* thay vì bị văng ứng dụng).
* [ ] **Kiểm thử lưu trữ:** Tải về một mẫu `Nền mới`. Tắt hẳn ứng dụng đi, bật lại, kiểm tra xem ô `Nền mới` đó có còn nằm trong danh sách chọn không và có áp dụng được bình thường khi offline không.
* [ ] **Kiểm thử trùng lặp:** Bấm tải đi tải lại 1 tệp tin xem hệ thống có tự ghi đè (`Overwrite`) để tối ưu dung lượng bộ nhớ máy hay không, tránh việc sinh ra nhiều file rác trùng tên.
Tính năng kho lưu trữ động thông qua nút `...` này sẽ giúp app Photobooth của bạn cực kỳ nhẹ khi tải trên Store (chỉ khoảng 20-30MB), nhưng người dùng vẫn có thể sở hữu một kho tài nguyên khổng lồ tùy chọn theo ý muốn!