← Quay lại blog

Toán học về trích xuất bảng màu: So sánh lượng tử hóa màu Median Cut, Octree và K-Means

PetexSpace

Hình ảnh kỹ thuật số được chụp bởi các máy ảnh hiện đại và các công cụ kết xuất thường chứa hàng triệu màu sắc khác nhau phân bố trên không gian màu ba chiều. Trong các hệ thống thiết kế (design systems), công cụ sáng tạo, trực quan hóa dữ liệu và tối ưu hóa hiệu năng web, việc tự động trích xuất một bảng màu đại diện là một tác vụ phổ biến.

Thách thức thuật toán của lượng tử hóa màu (color quantization) là giảm một hình ảnh chứa hàng trăm nghìn hoặc hàng triệu điểm RGB duy nhất xuống một bảng màu ngắn gọn gồm KK màu (thường từ 5 đến 16 mẫu màu). Bảng màu kết quả phải cân bằng giữa hai tiêu chí đối nghịch: bảo toàn các tông màu nền chủ đạo trong khi vẫn giữ lại các màu nhấn (accent colors) nổi bật về mặt thị giác.

Để thực hiện điều này một cách hiệu quả trên trình duyệt web, đòi hỏi phải hiểu rõ về các không gian màu, sự đánh đổi độ phức tạp tính toán và cơ chế phân cụm. Phân tích kỹ thuật này so sánh ba thuật toán lượng tử hóa chính: Median Cut, Rút gọn Octree và Phân cụm K-Means, khảo sát nền tảng toán học, đặc tính bộ nhớ, các mô hình màu cảm nhận và các triển khai thực thi ngay trên trình duyệt.

1. Thách thức trong lượng tử hóa không gian màu

Một hình ảnh TrueColor 24-bit tiêu chuẩn biểu diễn mỗi pixel bằng ba kênh 8-bit cho Đỏ (Red), Lục (Green) và Lam (Blue):

Pixel p=(R,G,B)∈[0,255]3⊂Z3\text{Pixel } p = (R, G, B) \in [0, 255]^3 \subset \mathbb{Z}^3

Điều này tạo ra một dải màu (gamut) lý thuyết liên tục gồm 224=16.777.2162^{24} = 16.777.216 màu khả dĩ. Một bức ảnh 12 megapixel thông thường chứa tới 500.000 bộ ba màu riêng biệt.

+------------------------------------------------------------------------+
|                          3D RGB COLOR CUBE                             |
|                                                                        |
|                 (0, 255, 0) Green                                      |
|                       +--------------------+ (255, 255, 0) Yellow      |
|                      /|                   /|                           |
|                     / |                  / |                           |
|                    /  |                 /  |                           |
|      (0, 255, 255)+--------------------+   |                           |
|              Cyan |   |                |   |                           |
|                   |   +----------------+---+ (255, 0, 0) Red           |
|                   |  / (0, 0, 0) Black |  /                            |
|                   | /                  | /                             |
|                   |/                   |/                              |
|                   +--------------------+ (255, 0, 255) Magenta         |
|              (0, 0, 255) Blue                                          |
+------------------------------------------------------------------------+

Tính phi đồng nhất về mặt cảm nhận của Euclidean RGB

Trong các triển khai sơ khai, các nhà phát triển thường coi không gian RGB như một hệ tọa độ Descartes trực giao, tính toán sự khác biệt màu sắc giữa hai pixel p1p_1 và p2p_2 bằng khoảng cách Euclidean tiêu chuẩn:

dRGB(p1,p2)=(R1−R2)2+(G1−G2)2+(B1−B2)2d_{\text{RGB}}(p_1, p_2) = \sqrt{(R_1 - R_2)^2 + (G_1 - G_2)^2 + (B_1 - B_2)^2}

Mặc dù có chi phí tính toán thấp, khoảng cách Euclidean trong RGB lại không tương ứng với cảm nhận thị giác của con người. Mắt người có độ nhạy quang phổ không đồng đều, phản ứng mạnh hơn nhiều với các biến thiên độ sáng nhỏ ở màu xanh lục so với các dịch chuyển giá trị số tương đương ở màu xanh lam.

Hai màu xanh lục khác biệt xuất hiện rõ ràng đối với người quan sát có thể tạo ra khoảng cách Euclidean RGB nhỏ hơn so với hai màu xanh navy đậm gần như giống hệt nhau.

Không gian màu CIE L*a*b* và khoảng cách Delta E (ΔE\Delta E)

Để khắc phục tính phi đồng nhất của RGB, các pipeline xử lý màu nâng cao chiếu các pixel vào không gian màu CIE L∗a∗b∗L^*a^*b^* (CIELAB):

  • L∗L^*: Độ sáng cảm nhận (0=đen,100=tra˘ˊng khueˆˊch taˊn0 = \text{đen}, 100 = \text{trắng khuếch tán}).
  • a∗a^*: Vị trí dọc theo trục sắc thái lục - đỏ (âm = lục, dương = đỏ).
  • b∗b^*: Vị trí dọc theo trục sắc thái lam - vàng (âm = lam, dương = vàng).

Trong CIELAB, khoảng cách cảm nhận giữa hai màu được công thức hóa thông qua ΔE\Delta E. Trong khi tiêu chuẩn ban đầu ΔE76∗\Delta E^*_{76} sử dụng khoảng cách Euclidean trong tọa độ L∗a∗b∗L^*a^*b^*, tiêu chuẩn hiện đại CIEDE2000 (ΔE00\Delta E_{00}) giới thiệu các thành phần xoay và hàm trọng số (SL,SC,SHS_L, S_C, S_H) để giải thích cho các phi tuyến tính trong cảm nhận của con người:

ΔE00=(ΔL′kLSL)2+(ΔC′kCSC)2+(ΔH′kHSH)2+RT(ΔC′kCSC)(ΔH′kHSH)\Delta E_{00} = \sqrt{\left(\frac{\Delta L'}{k_L S_L}\right)^2 + \left(\frac{\Delta C'}{k_C S_C}\right)^2 + \left(\frac{\Delta H'}{k_H S_H}\right)^2 + R_T \left(\frac{\Delta C'}{k_C S_C}\right)\left(\frac{\Delta H'}{k_H S_H}\right)}

Lượng tử hóa một hình ảnh bằng khoảng cách ΔE00\Delta E_{00} tạo ra các bảng màu bám sát chặt chẽ với cảm nhận của con người, đảm bảo các mẫu màu được trích xuất đại diện cho các nhóm thị giác riêng biệt.

2. Thuật toán Median Cut: Phân chia không gian đệ quy

Được giới thiệu bởi Paul Heckbert vào năm 1982, thuật toán Median Cut vẫn là một phương pháp được sử dụng rộng rãi để lượng tử hóa màu. Thuật toán hoạt động bằng cách phân vùng đệ quy khối màu RGB 3D thành các hộp con nhỏ hơn chứa số lượng pixel gần như bằng nhau.

+------------------------------------------------------------------------+
|                      MEDIAN CUT PARTITIONING                           |
|                                                                        |
|  [Initial RGB Bounding Box (All Pixels)]                               |
|                     |                                                  |
|                     | Find dimension with largest range: max(dR, dG, dB)|
|                     v                                                  |
|  [Sort pixels along widest axis & split at median]                     |
|                     |                                                  |
|         +-----------+-----------+                                      |
|         |                       |                                      |
|         v                       v                                      |
|   [Sub-Box 1]             [Sub-Box 2]                                  |
|   (50% of pixels)         (50% of pixels)                              |
|         |                       |                                      |
|    Recurse                 Recurse                                     |
|         |                       |                                      |
|         +-----------+-----------+                                      |
|                     |                                                  |
|                     v                                                  |
|  [K Terminal Leaf Boxes (e.g., K = 8)]                                 |
|                     |                                                  |
|                     v                                                  |
|  [Compute Arithmetic Mean Centroid for Each Box]                       |
+------------------------------------------------------------------------+

Các giai đoạn của thuật toán Median Cut

  1. Bao đóng hộp giới hạn (Bounding Box): Bao bọc tất cả các pixel trong một hộp giới hạn 3D ban đầu được xác định bởi các tọa độ kênh nhỏ nhất và lớn nhất:
Rmin,Rmax,Gmin,Gmax,Bmin,BmaxR_{\text{min}}, R_{\text{max}}, \quad G_{\text{min}}, G_{\text{max}}, \quad B_{\text{min}}, B_{\text{max}}
  1. Trục có phương sai lớn nhất: Tính toán phạm vi của các kênh:
ΔR=Rmax−Rmin,ΔG=Gmax−Gmin,ΔB=Bmax−Bmin\Delta R = R_{\text{max}} - R_{\text{min}}, \quad \Delta G = G_{\text{max}} - G_{\text{min}}, \quad \Delta B = B_{\text{max}} - B_{\text{min}}

Xác định kênh có phạm vi lớn nhất:

Axissplit=arg⁡max⁡(ΔR,ΔG,ΔB)\text{Axis}_{\text{split}} = \arg\max (\Delta R, \Delta G, \Delta B)
  1. Sắp xếp theo trung vị và phân vùng: Sắp xếp các pixel trong hộp dọc theo trục đã chọn. Chia hộp tại đúng vị trí chỉ số pixel trung vị (N/2N/2), tạo ra hai hộp con, mỗi hộp chứa 50% số lượng pixel của hộp cha.
  2. Phân chia đệ quy: Lặp lại các bước 2 và 3 trên các hộp con cho đến khi tạo ra KK hộp lá. Để tạo ra số lượng mục tiêu KK tùy ý, một hàng đợi ưu tiên (priority queue) sẽ sắp xếp các hộp theo thể tích hoặc số lượng pixel, phân tách hộp ứng viên lớn nhất ở mỗi lần lặp.
  3. Tính trung bình trọng tâm: Đối với mỗi hộp trong số KK hộp lá, tính giá trị trung bình số học của các pixel chứa bên trong:
Ravg=1∣Bk∣∑p∈BkRp,Gavg=1∣Bk∣∑p∈BkGp,Bavg=1∣Bk∣∑p∈BkBpR_{\text{avg}} = \frac{1}{|B_k|} \sum_{p \in B_k} R_p, \quad G_{\text{avg}} = \frac{1}{|B_k|} \sum_{p \in B_k} G_p, \quad B_{\text{avg}} = \frac{1}{|B_k|} \sum_{p \in B_k} B_p

KK giá trị trung bình này tạo thành bảng màu đại diện được trích xuất.

Điểm mạnh và hạn chế

Median Cut đảm bảo rằng các cụm màu có mật độ cao được chia thành nhiều sắc thái riêng biệt. Tuy nhiên, điểm yếu lớn nhất của nó là chi phí sắp xếp (sorting overhead). Sắp xếp các mảng pixel lớn dọc theo các trục ứng viên đòi hỏi thời gian tính toán đáng kể.

Ngoài ra, các cụm nhỏ chứa màu nhấn có độ bão hòa cao (chẳng hạn như một bông hoa màu đỏ tươi trên một cánh đồng cỏ xanh) có thể bị gộp vào các phân vùng nền lớn hơn nếu thể tích của chúng nhỏ, dẫn đến việc bỏ sót các điểm nổi bật thị giác.

3. Lượng tử hóa màu Octree: Tỉa cây phân cấp

Được phát triển bởi Gervautz và Purgathofer vào năm 1988, thuật toán Lượng tử hóa màu Octree giải quyết các điểm nghẽn về sắp xếp của Median Cut bằng cách xây dựng một cấu trúc dữ liệu cây phân cấp 8 nhánh.

+------------------------------------------------------------------------+
|                          OCTREE DATA STRUCTURE                         |
|                                                                        |
|                                [Root] (Level 0)                        |
|                               /  |  \                                  |
|                            (8 Children: Branch per 3-bit octant)       |
|                            /     |     \                               |
|                         [N_0]  [N_1] ... [N_7] (Level 1)               |
|                                  |                                     |
|                                 ...                                    |
|                                  |                                     |
|                                [Leaf] (Level 8, Max Depth)             |
|                                (Accumulates Red, Green, Blue Sums)     |
+------------------------------------------------------------------------+

Cấu trúc Octree và duyệt mức bit

Một Octree là một cây trong đó mỗi nút nội bộ có tối đa tám nút con, tương ứng với tám góc phần tám (octants) của không gian ba chiều. Cây có độ sâu tối đa là 8, khớp với độ chính xác 8-bit của các kênh màu tiêu chuẩn.

Để chèn một pixel (R,G,B)(R, G, B) vào Octree, thuật toán kiểm tra các vị trí bit tương ứng của từng kênh ở cấp độ sâu dd (trong đó d=0d = 0 tương ứng với bit có trọng số cao nhất, MSB):

Child Index=(bitd(R)≪2)∣(bitd(G)≪1)∣bitd(B)\text{Child Index} = (\text{bit}_d(R) \ll 2) \mid (\text{bit}_d(G) \ll 1) \mid \text{bit}_d(B)

Thao tác này tạo ra một số nguyên từ 0 đến 7 định tuyến việc chèn vào nhánh con phù hợp ở mỗi cấp độ.

Bit Extraction at Depth d:
Red:    [b7 b6 b5 b4 b3 b2 b1 b0] ---> Take bit (7 - d) ---> Shift left 2
Green:  [b7 b6 b5 b4 b3 b2 b1 b0] ---> Take bit (7 - d) ---> Shift left 1
Blue:   [b7 b6 b5 b4 b3 b2 b1 b0] ---> Take bit (7 - d) ---> Shift left 0
                                                               |
Child Node Index (0 - 7):                                      v
                                                [Bit_R | Bit_G | Bit_B]

Mỗi nút lá duy trì ba bộ tích lũy:

  1. pixelCount: Tổng số lượng pixel được ánh xạ vào lá này.
  2. redSum, greenSum, blueSum: Tổng lũy kế của các kênh màu.

Tỉa cây tuần tự

Trong quá trình chèn, nếu tổng số nút lá đang hoạt động vượt quá kích thước bảng màu mục tiêu KK, Octree sẽ được tỉa bớt:

  1. Xác định các nút nội bộ ở cấp độ sâu nhất có các lá con.
  2. Chọn nút có các lá con biểu thị tổng số lượng pixel nhỏ nhất.
  3. Thu gọn các lá con vào nút cha bằng cách cộng tổng số lượng pixel và các bộ tích lũy màu của chúng. Nút cha chuyển đổi thành một lá kết thúc, làm giảm tổng số lượng lá.

Sau khi xử lý tất cả các pixel hình ảnh trong một lượt quét tuyến tính duy nhất, KK lá còn lại sẽ cung cấp các màu trong bảng màu thông qua phép chia kênh:

R=redSumpixelCount,G=greenSumpixelCount,B=blueSumpixelCountR = \frac{\text{redSum}}{\text{pixelCount}}, \quad G = \frac{\text{greenSum}}{\text{pixelCount}}, \quad B = \frac{\text{blueSum}}{\text{pixelCount}}

Lượng tử hóa Octree hoạt động trong thời gian tuyến tính O(N)O(N) với giới hạn bộ nhớ nghiêm ngặt, khiến nó rất phù hợp cho các môi trường có RAM hạn chế.

4. Phân cụm K-Means và Cực đại hóa kỳ vọng (Expectation-Maximization)

Phân cụm K-Means mô hình hóa việc trích xuất bảng màu như một bài toán tối ưu hóa lượng tử hóa vector không giám sát. Nó phân vùng NN quan sát pixel thành KK cụm Voronoi rời rạc, với mỗi pixel thuộc về cụm có vector trung bình gần nhất.

+------------------------------------------------------------------------+
|                          K-MEANS CLUSTERING LOOP                       |
|                                                                        |
|  [Input Pixels] ---> [K-Means++ Centroid Initialization]               |
|                                 |                                      |
|             +------------------->-------------------+                  |
|             |                                       |                  |
|             v                                       v                  |
|  [Assignment Step (Expectation)]     [Update Step (Maximization)]       |
|  Assign every pixel to nearest       Recompute cluster centroid as the |
|  centroid: argmin ||p - mu_k||^2     arithmetic mean of member pixels  |
|             |                                       |                  |
|             +-------------------<-------------------+                  |
|                                 |                                      |
|                                 v Drift < Epsilon                      |
|                  [Converged Representative Palette]                    |
+------------------------------------------------------------------------+

Hàm mục tiêu và các bước EM

K-Means giảm thiểu Tổng bình phương nội cụm (Within-Cluster Sum of Squares - WCSS), thường được gọi là quán tính cụm:

J=∑k=1K∑p∈Sk∥p−μk∥2J = \sum_{k=1}^K \sum_{p \in S_k} \| p - \mu_k \|^2

Ở đây, SkS_k biểu thị tập hợp các pixel được gán cho cụm kk, và μk\mu_k là vector trọng tâm của cụm.

Thuật toán lặp qua hai bước:

  1. Gán nhãn (Bước kỳ vọng - Expectation): Mỗi vector pixel pip_i được ánh xạ tới trọng tâm gần nhất dựa trên khoảng cách Euclidean hoặc ΔE\Delta E:
Sk(t)={pi:∥pi−μk(t)∥2≤∥pi−μj(t)∥2∀j,1≤j≤K}S_k^{(t)} = \left\{ p_i : \| p_i - \mu_k^{(t)} \|^2 \le \| p_i - \mu_j^{(t)} \|^2 \quad \forall j, 1 \le j \le K \right\}
  1. Cập nhật (Bước cực đại hóa - Maximization): Các trọng tâm được tính toán lại dưới dạng khối tâm của tất cả các pixel được gán:
μk(t+1)=1∣Sk(t)∣∑p∈Sk(t)p\mu_k^{(t+1)} = \frac{1}{|S_k^{(t)}|} \sum_{p \in S_k^{(t)}} p

Vòng lặp kết thúc khi độ dịch chuyển trọng tâm giữa các lần lặp liên tiếp giảm xuống dưới ngưỡng sai số ϵ\epsilon, hoặc khi đạt đến số lần lặp tối đa.

Khởi tạo K-Means++

K-Means tiêu chuẩn thường rơi vào các điểm cực tiểu cục bộ kém tối ưu nếu các trọng tâm ban đầu được chọn ngẫu nhiên đồng đều. K-Means++ giảm thiểu điều này bằng cách phân tán các điểm khởi đầu (seeds) ban đầu trên miền không gian:

  1. Chọn trọng tâm đầu tiên μ1\mu_1 ngẫu nhiên từ tập hợp pixel.
  2. Với mỗi pixel còn lại pp, tính D(p)D(p), khoảng cách ngắn nhất đến bất kỳ trọng tâm nào đã được chọn trước đó.
  3. Chọn trọng tâm tiếp theo μj\mu_{j} với xác suất tỉ lệ thuận với bình phương khoảng cách:
P(p)=D(p)2∑xD(x)2P(p) = \frac{D(p)^2}{\sum_{x} D(x)^2}
  1. Lặp lại cho đến khi tất cả KK điểm khởi tạo được xác định.
// Pure TypeScript K-Means implementation with spatial downsampling
export class ColorQuantizerKMeans {
  public static extractPalette(
    pixels: Uint8ClampedArray,
    k: number = 6,
    maxIterations: number = 20
  ): Array<{ r: number; g: number; b: number; hex: string; population: number }> {
    const sampleCount = pixels.length / 4;
    const stride = Math.max(1, Math.floor(sampleCount / 10000)); // Downsample for fast convergence
    const points: Array<[number, number, number]> = [];

    for (let i = 0; i < pixels.length; i += 4 * stride) {
      points.push([pixels[i], pixels[i + 1], pixels[i + 2]]);
    }

    if (points.length === 0) return [];

    // K-Means++ Initialization
    const centroids: Array<[number, number, number]> = [];
    centroids.push(points[Math.floor(Math.random() * points.length)]);

    while (centroids.length < k) {
      const distances: number[] = [];
      let totalDistSq = 0;

      for (let i = 0; i < points.length; i++) {
        let minDist = Infinity;
        for (let c = 0; c < centroids.length; c++) {
          const dr = points[i][0] - centroids[c][0];
          const dg = points[i][1] - centroids[c][1];
          const db = points[i][2] - centroids[c][2];
          const distSq = dr * dr + dg * dg + db * db;
          if (distSq < minDist) minDist = distSq;
        }
        distances.push(minDist);
        totalDistSq += minDist;
      }

      let threshold = Math.random() * totalDistSq;
      let selectedIdx = 0;
      for (let i = 0; i < distances.length; i++) {
        threshold -= distances[i];
        if (threshold <= 0) {
          selectedIdx = i;
          break;
        }
      }
      centroids.push(points[selectedIdx]);
    }

    // Expectation-Maximization Iteration Loop
    let assignments = new Int32Array(points.length);
    for (let iter = 0; iter < maxIterations; iter++) {
      let changed = false;

      // Assignment step
      for (let i = 0; i < points.length; i++) {
        let minDist = Infinity;
        let bestCluster = 0;
        for (let c = 0; c < k; c++) {
          const dr = points[i][0] - centroids[c][0];
          const dg = points[i][1] - centroids[c][1];
          const db = points[i][2] - centroids[c][2];
          const distSq = dr * dr + dg * dg + db * db;
          if (distSq < minDist) {
            minDist = distSq;
            bestCluster = c;
          }
        }
        if (assignments[i] !== bestCluster) {
          assignments[i] = bestCluster;
          changed = true;
        }
      }

      if (!changed && iter > 0) break;

      // Update step
      const sums = Array.from({ length: k }, () => [0, 0, 0]);
      const counts = new Int32Array(k);

      for (let i = 0; i < points.length; i++) {
        const cluster = assignments[i];
        sums[cluster][0] += points[i][0];
        sums[cluster][1] += points[i][1];
        sums[cluster][2] += points[i][2];
        counts[cluster]++;
      }

      for (let c = 0; c < k; c++) {
        if (counts[c] > 0) {
          centroids[c][0] = Math.round(sums[c][0] / counts[c]);
          centroids[c][1] = Math.round(sums[c][1] / counts[c]);
          centroids[c][2] = Math.round(sums[c][2] / counts[c]);
        }
      }
    }

    // Calculate cluster populations
    const clusterPopulations = new Int32Array(k);
    for (let i = 0; i < assignments.length; i++) {
      clusterPopulations[assignments[i]]++;
    }

    const toHex = (c: number): string => Math.max(0, Math.min(255, c)).toString(16).padStart(2, '0');

    return centroids.map((c, idx) => ({
      r: c[0],
      g: c[1],
      b: c[2],
      hex: `#${toHex(c[0])}${toHex(c[1])}${toHex(c[2])}`.toUpperCase(),
      population: clusterPopulations[idx]
    })).sort((a, b) => b.population - a.population);
  }
}

5. Tổng hợp bảng màu trên trình duyệt: Engine của PetexSpace

Việc xây dựng một công cụ trích xuất bảng màu tương tác ngay trên trình duyệt web đòi hỏi sự cân bằng giữa độ chính xác thị giác và thông lượng tính toán.

+------------------------------------------------------------------------+
|             PetexSpace In-Browser Palette Pipeline                     |
|                                                                        |
|  [User Image Input]                                                    |
|          |                                                             |
|          v                                                             |
|  [Canvas Downsampler (Max 256x256 px)]                                 |
|  (Reduces sample space from millions to <= 65,536 pixels)              |
|          |                                                             |
|          v                                                             |
|  [Web Worker Thread Isolation]                                         |
|    +-- Pre-pass: Octree / Median Cut bounding box partitioning         |
|    +-- Refinement: K-Means++ clustering with Delta E distance          |
|    +-- Dynamic Sorting: Luminance ranking & saturation weighting       |
|          |                                                             |
|          v                                                             |
|  [Accessible Design Tokens]                                            |
|  (Hex Swatches, Contrast Validation, Foreground Recommendations)       |
|          |                                                             |
|          v                                                             |
|  [Client-Side Execution: Zero Cloud Telemetry]                      |
+------------------------------------------------------------------------+

Pipeline lai: Giảm mẫu không gian và lượng tử hóa hai giai đoạn

Việc xử lý trực tiếp một hình ảnh 48 megapixel độ phân giải cao trong JavaScript sẽ làm nghẽn event loop và tiêu tốn quá nhiều bộ nhớ.

Engine trích xuất bảng màu chủ đạo của PetexSpace triển khai một pipeline trích xuất lai:

  1. Giảm mẫu song tuyến tính trên Canvas: Hình ảnh được vẽ lên một canvas ngoài màn hình (offscreen canvas) với kích thước giới hạn trong một hộp bao tối đa 256×256256 \times 256 pixel. Thao tác này giới hạn không gian đầu vào ở mức tối đa 65.536 pixel trong khi vẫn bảo toàn được sự cân bằng màu sắc không gian và phân bố tần số của hình ảnh gốc.
  2. Lượng tử hóa hai giai đoạn:* **Giai đoạn 1 (Phân vùng):** Một lượt quét Median Cut nhanh ban đầu sẽ nhóm các pixel đã giảm mẫu vào các hộp bao ứng viên, xác định các ứng viên cụm ban đầu. * **Giai đoạn 2 (Tinh chỉnh bằng K-Means):** Các ứng viên này làm hạt giống khởi tạo cho thuật toán K-Means dưới dạng các trọng tâm ban đầu, đảm bảo sự hội tụ nhanh chóng chỉ trong 5 đến 8 lần lặp.
  3. Điều hòa độ tương phản và Design Token: Bảng màu được trích xuất được sắp xếp theo độ sáng cảm nhận và kết hợp với bộ công cụ PetexSpace để xử lý hình ảnh phía client, tạo ra các design token CSS sẵn sàng sử dụng với tỉ lệ tương phản đã được kiểm chứng.

Quyền riêng tư và thực thi ngay trên trình duyệt

Ảnh chụp cá nhân, đồ họa nhận diện thương hiệu và ảnh chụp màn hình giao diện thường chứa dữ liệu thị giác nhạy cảm hoặc bảo mật. Theo các nguyên tắc kiến trúc của PetexSpace, toàn bộ quá trình xử lý hình ảnh đều diễn ra thông qua việc thực thi ngay trên trình duyệt.

Dữ liệu pixel được giải mã từ tệp người dùng chỉ được xử lý nghiêm ngặt bên trong ngữ cảnh canvas cục bộ và bộ nhớ Web Worker cô lập. Không có hình ảnh, ảnh thu nhỏ nén hay vector màu trích xuất nào được truyền qua các mạng bên ngoài, đảm bảo sự riêng tư và an toàn dữ liệu tuyệt đối.

CryptographyXác thựcSecurity

Kiến trúc của Mật khẩu Một lần Dựa trên Thời gian: Khám phá RFC 6238 và Cơ chế Tạo HMAC

Phân tích toán học và giao thức toàn diện về Mật khẩu Một lần Dựa trên Thời gian (TOTP) theo chuẩn RFC 6238, khám phá kỹ thuật dynamic truncation, bộ đếm bước HMAC-SHA1, xử lý lệch đồng hồ và thực thi an toàn ngay trên trình duyệt.

PetexSpace
CryptographyData EncodingMã QR

Giải phẫu mã QR: Cơ chế sửa lỗi Reed-Solomon và mã hóa ma trận

Khám phá chuyên sâu về mặt toán học của mã QR chuẩn ISO/IEC 18004, phân tích chi tiết số học trường Galois GF(2^8), đa thức sinh, cơ chế sửa lỗi Reed-Solomon và kỹ thuật kết xuất ma trận ngay trên trình duyệt.

PetexSpace
CryptographyWeb CryptoSecurity

Tính ngẫu nhiên mật mã học trong trình duyệt: Math.random so với crypto.getRandomValues

Nghiên cứu chuyên sâu về kiến trúc giữa bộ tạo số giả ngẫu nhiên (PRNG) và bộ tạo số giả ngẫu nhiên an toàn về mặt mật mã (CSPRNG) trong môi trường runtime trình duyệt, phân tích chi tiết xorshift128+, các cuộc tấn công tái tạo trạng thái và Web Cryptography API.

PetexSpace
CryptographyData EncodingBase32

So sánh Base32 và Base64: Khác biệt cấu trúc, quy tắc đệm và ứng dụng mật mã học

Đặc tả kỹ thuật chuyên sâu về các chuẩn mã hóa nhị phân sang văn bản theo RFC 4648, đối chiếu cơ chế đóng gói bitwise, ma trận đệm (padding), tính công thái học của bảng chữ cái và quá trình thực thi trực tiếp trên trình duyệt giữa Base32 và Base64.

PetexSpace