135 lines
9.9 KiB
Markdown
135 lines
9.9 KiB
Markdown
# Phân Tích Kỹ Thuật & Giải Pháp Khắc Phục Lỗi "Halo" Khi Chỉnh Highlight, Shadow, White, Black (Tích Hợp Cân Bằng Saturation & Vibrance)
|
|
|
|
---
|
|
|
|
## 1. Hiện Tượng Halo Artifact Khi Chỉnh 4 Thông Số Ánh Sáng (Highlight, Shadow, White, Black)
|
|
|
|
Khi người dùng điều chỉnh 4 thanh trượt ánh sáng (**Highlights**, **Shadows**, **Whites**, **Blacks**) — đặc biệt là kéo mạnh $+100$ hoặc $-100$ trên các tệp **ảnh RAW** (`.NEF`, `.RAF`, `.DNG`, `.RW2`):
|
|
* Tại các đường biên tương phản cao (Step Edges) như: ngọn núi/tán cây trên nền trời sáng, đường viền chủ thể ngược sáng, khung cửa sổ sáng trên mảng tường tối.
|
|
* Xuất hiện **đường viền phát sáng rực (Bright Halo)** hoặc **vệt viền đen sẫm (Dark Halo/Undershoot)** ôm theo mép cạnh.
|
|
* **Màu sắc tại vùng viền bị bếch hoặc rực lên bất thường (Saturation & Fringing Artifacts)** khi kết hợp với thanh trượt **Color (Saturation)** hoặc **Vibrance**.
|
|
|
|
```
|
|
MẶT CẮT ĐỘ SÁNG TẠI ĐƯỜNG BIÊN TƯƠNG PHẢN CAO (Tán cây vs Bầu trời):
|
|
|
|
Độ Sáng (Luminance L)
|
|
▲
|
|
1.0 ┤ [Overshoot: Viền sáng rực (Bright Halo)]
|
|
│ ┌───┐
|
|
0.9 ├────────────────────────────┘ └──────────────────────── (Bầu trời)
|
|
│
|
|
│
|
|
0.1 ├────────────────────────────┐ ┌──────────────────────── (Tán cây)
|
|
│ └───┘
|
|
0.0 ┴────────────────────────────────────────────────────────► Khoảng cách (X)
|
|
[Undershoot: Viền đen sẫm (Dark Halo)]
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Nguyên Nhân Kỹ Thuật Cốt Lõi
|
|
|
|
### A. Sự Rò Rỉ Không Gian Của Bộ Lọc Nền (Gaussian Neighborhood Leakage)
|
|
1. Để bảo tồn tương phản cục bộ (Local Contrast), thuật toán tách độ sáng ảnh thành 2 lớp:
|
|
$$\text{Image} = \text{Base Layer (Khối nền tổng thể)} + \text{Detail Layer (Chi tiết vi mô)}$$
|
|
2. Lớp `Base Layer` được trích xuất bằng bộ lọc làm mờ. Nếu bộ lọc làm mờ là **Gaussian Blur** thông thường:
|
|
* Về phía bầu trời sáng ($t \approx 0.95$) gần ngọn núi tối: Gaussian Blur gom cả pixel tối vào $\implies$ `base` bị giảm xuống (ví dụ `base = 0.60`).
|
|
* Phép tính target độ sáng: $\text{target} = \text{toneCurve}(\text{base}) + (t - \text{base})$.
|
|
* Vì `base = 0.60` thấp hơn thực tế, khi nén Highlight ($-Highlight$), hàm `toneCurve(0.60)` không nén đủ sâu $\implies$ Độ sáng tại mép trời bị đẩy vọt lên cao hơn vùng trời xa biên $\implies$ **Tạo viền trắng sáng rực (Bright Halo)**.
|
|
* Ngược lại, phía ngọn núi tối ($t \approx 0.05$) gần bầu trời sáng: `base` bị kéo lên cao $\implies$ Khi nâng Shadow ($+Shadow$), hàm `toneCurve` nâng không đủ $\implies$ **Tạo viền đen đậm (Dark Halo)**.
|
|
|
|
### B. Tại Sao Ảnh RAW Bị Ảnh Hưởng Nghiêm Trọng Hơn Ảnh JPEG?
|
|
* Ảnh JPEG 8-bit đã qua nén tương phản và kẹp dải sáng ($0.0 - 1.0$), chênh lệch bước nhảy $t - \text{base}$ ở mức vừa phải ($0.05 - 0.15$).
|
|
* Ảnh **RAW** lưu trữ năng lượng ánh sáng tuyến tính (Linear Scene-Referred Light) với Dải động cực rộng (Dynamic Range 12 - 14 stops). Khi giải mã qua Raw Develop:
|
|
* Khoảng chênh lệch giữa bầu trời chưa cháy và bóng râm tối sâu là cực lớn.
|
|
* Bước nhảy $|t - \text{base}|$ tại đường biên tương phản đạt tới $0.30 - 0.75$.
|
|
* Phép tính Base + Detail với Gaussian Blur thiếu Edge-Gating sẽ tạo ra vùng Overshoot/Undershoot khổng lồ, khiến ảnh RAW bị viền Halo dày cộp và sắc nét.
|
|
|
|
### C. Tác Động Dây Chuyền Đến Color (Saturation) Và Vibrance
|
|
1. **Lệch Sắc Tố (Hunt Effect Fringing):**
|
|
Thuật toán bù bão hòa màu tự động (Hunt Effect) áp dụng cho dải tối dựa trên `base`. Khi `base` bị lem qua đường biên sáng/tối, lượng Chroma Boost `boost` bị tính toán sai vị trí, tạo ra **viền loang màu (Color Fringing)** xung quanh Halo.
|
|
2. **Khuếch Đại Bởi Vibrance & Saturation:**
|
|
Thanh trượt **Vibrance** tác động khuếch đại màu sắc trên vùng ít bão hòa dựa trên `(1 - chroma)` và Luminance `l2`. Khi Luminance bị biến dạng bởi Halo, phép biến đổi màu sắc nhân tỷ lệ `lightMove` sẽ khuếch đại cả sắc độ tại viền Halo, khiến đường viền vừa bị lóa sáng vừa rực màu bất nhiên.
|
|
|
|
---
|
|
|
|
## 3. Giải Pháp Thuật Toán: Edge-Aware Range-Gated Tone Mapping
|
|
|
|
Để triệt tiêu hoàn toàn viền Halo trên cả ảnh JPEG và ảnh RAW mà vẫn giữ nguyên $100\%$ độ nổi khối chi tiết dải trung tính, thuật toán áp dụng cơ chế **Phân hoạch biên độ theo độ chênh lệch ánh sáng (Edge-Aware Range Gating)**:
|
|
|
|
```
|
|
[Luminance Pixel (t) & Gaussian Base (base)]
|
|
│
|
|
▼
|
|
[Tính độ lệch: diff = |t - base|]
|
|
│
|
|
▼
|
|
[Mặt nạ kiểm soát cạnh: edgeW = 1.0 - smoothstep(0.08, 0.35, diff)]
|
|
│
|
|
┌─────────────────┴─────────────────┐
|
|
▼ (diff nhỏ < 0.08) ▼ (diff lớn > 0.35 - Bước nhảy cạnh cứng)
|
|
[edgeW ≈ 1.0: Môi trường bề mặt] [edgeW ≈ 0.0: Đường biên tương phản cao]
|
|
• effBase = base • effBase = t
|
|
• Target = toneCurve(base)+(t-base) • Target = toneCurve(t)
|
|
• Giữ 100% chi tiết vân khối 3D. • Chuyển mượt về đường cong toàn cục.
|
|
• TRIỆT TIÊU HOÀN TOÀN HALO!
|
|
```
|
|
|
|
### Công Thức Toán Học Cho WebGL / SkSL Shader:
|
|
|
|
```glsl
|
|
// 1. Độ chênh lệch giữa luma điểm ảnh và luma môi trường lân cận
|
|
float diff = abs(t - base);
|
|
|
|
// 2. Mặt nạ suy giảm mượt (Range Falloff Window)
|
|
// Với diff < 0.08: edgeW = 1.0 (vùng vân mịn / dải chuyển mượt)
|
|
// Với diff > 0.35: edgeW = 0.0 (đường biên tương phản cứng, đặc biệt trên ảnh RAW)
|
|
float edgeW = 1.0 - smoothstep(0.08, 0.35, diff);
|
|
|
|
// 3. Luma môi trường hiệu dụng (Effective Base Luminance)
|
|
float effBase = mix(t, base, edgeW);
|
|
|
|
// 4. Áp dụng toneCurve trên effBase
|
|
float o = toneCurve(effBase, clamp(bl + dr * 0.12, -1.0, 1.0),
|
|
clamp(sh + dr * 0.06, -1.0, 1.0),
|
|
clamp(hl - dr * 0.09, -1.0, 1.0),
|
|
clamp(wh - dr * 0.18, -1.0, 1.0));
|
|
|
|
// 5. Tái cấu trúc luma đích không bị vệt halo
|
|
float target = clamp(o + (t - effBase), 0.0, 1.0);
|
|
vec3 res = lightMove(c, t, target);
|
|
|
|
// 6. Chroma Boost & Vibrance dựa trên effBase an toàn không lem viền
|
|
float shMask = smoothstep(0.02, 0.12, effBase) * (1.0 - smoothstep(0.25, 0.55, effBase));
|
|
float uBl = clamp(1.0 - effBase / TONE_BLACK_EDGE, 0.0, 1.0);
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Cân Chỉnh Sự Tương Tác Với Color (Saturation) & Vibrance
|
|
|
|
1. **Khóa viền màu theo effBase:** `shMask` và `blMask` sử dụng `effBase` thay cho `base` thô. Khi quét qua đường biên của ảnh RAW, `effBase` tự động tiệm cận `t`, ngăn chặn hoàn toàn việc bơm Saturation nhầm vào đường biên.
|
|
2. **Bảo toàn Chroma qua lightMove:** Hàm `lightMove` duy trì tỉ lệ chênh lệch giữa 3 kênh RGB theo Luminance đích `target`. Nhờ `target` không bị nhô (Overshoot) hay lõm (Undershoot), sắc độ của màu sắc (Hue & Saturation) được giữ nguyên vẹn.
|
|
3. **Vibrance thích ứng:** Nhờ độ chói `l2` được tính toán trên kết quả `toneRamp` đã sạch viền, phép biến đổi Vibrance `kv = 1.0 + vib * 0.75 * (1.0 - chroma)` hoạt động chính xác trên dải màu trung tính mà không gây rực màu vùng viền.
|
|
|
|
---
|
|
|
|
## 5. Mã Nguồn Đã Cập Nhật Trong Codebase
|
|
|
|
1. **Shader Core (`frontend/shared/utils/toneShader.ts`):**
|
|
* Đã tích hợp `edgeW` và `effBase` trực tiếp vào hàm `toneRamp` trong `TONE_SKSL`.
|
|
2. **Bộ Test Tự Động (`frontend/scripts/`):**
|
|
* `highlight-knee-check.mjs`: Đã cập nhật chuỗi khớp mẫu shader và kiểm tra toàn vẹn không bị nén đè.
|
|
* `tone-base-check.mjs`: Đã cập nhật mô hình tính toán dự đoán `effBase`, kiểm tra điểm ảnh $128$ trên nền $76$ cho kết quả chuẩn xác $151/255$.
|
|
* Đã kiểm tra `npm run typecheck` và 100% các script check trong `scripts/` đều **PASSED**.
|
|
|
|
---
|
|
|
|
## 6. Bảng Kiểm Tra Chất Lượng (Quality Assurance Checklist)
|
|
|
|
| Tiêu chí | Hành vi chuẩn (Sau khi fix) | Dấu hiệu lỗi (Cần kiểm tra lại) |
|
|
| :--- | :--- | :--- |
|
|
| **Chỉnh Highlight -100 trên RAW** | Bầu trời sáng hiện rõ mây, ranh giới ngọn núi đanh gọn, không có viền sáng lóa xung quanh. | Xuất hiện dải viền trắng phát sáng chạy dọc mép núi. |
|
|
| **Chỉnh Shadow +100 trên RAW** | Chi tiết bóng râm vách đá nâng lên rõ nét, không có đường viền đen đậm viền ngoài. | Có viền đen/tối sẫm bao quanh các tán cây ngược sáng. |
|
|
| **Tăng Vibrance +10 & Color +10** | Màu sắc bầu trời, da người và rêu đá rực rỡ tự nhiên, dải biên không bị lem màu neon. | Đường viền núi bị nổi màu xanh/đỏ rực rỡ như nét tô viền (Color Fringing). |
|