基于已提取像素集的图像颜色空间压缩实现方案问询
Alright, let's tackle color space compression for your already processed small image. You've got width, height, a unique colors array, and a pixels index matrix—perfect setup to dive right in. Here's how to pull this off effectively:
The most common approach for this scenario is color quantization—reducing the number of unique colors in the image while preserving visual fidelity. Two reliable methods are:
- K-Means clustering (adaptive to your image's specific colors)
- Fixed palette mapping (using pre-defined color sets like web-safe colors)
This method creates a custom palette tailored to your image, which usually gives better visual results than fixed palettes. Here's a step-by-step implementation (using JavaScript as an example):
Step 1: Prep Your Color Data
First, convert your colors objects into a format suitable for clustering—simple RGB arrays:
// Convert {r,g,b} objects to [r,g,b] arrays const colorPoints = colors.map(color => [color.r, color.g, color.b]);
Step 2: Run K-Means Clustering
Define a K-Means function to group similar colors into k clusters (where k is your target number of colors, e.g., 16, 32):
function kMeans(points, k, maxIterations = 100) { // Initialize centroids by picking random points from the color set let centroids = points.sort(() => Math.random() - 0.5).slice(0, k); for (let iter = 0; iter < maxIterations; iter++) { // Assign each color to its closest centroid const clusters = Array.from({ length: k }, () => []); points.forEach(point => { const distances = centroids.map(centroid => Math.sqrt( Math.pow(point[0] - centroid[0], 2) + Math.pow(point[1] - centroid[1], 2) + Math.pow(point[2] - centroid[2], 2) ) ); const closestIdx = distances.indexOf(Math.min(...distances)); clusters[closestIdx].push(point); }); // Update centroids to the average of their cluster const newCentroids = clusters.map(cluster => { if (cluster.length === 0) return centroids[clusters.indexOf(cluster)]; const sum = cluster.reduce((acc, p) => [ acc[0] + p[0], acc[1] + p[1], acc[2] + p[2] ], [0, 0, 0]); // Round to integer RGB values return sum.map(val => Math.round(val / cluster.length)); }); // Stop early if centroids stop changing if (JSON.stringify(centroids) === JSON.stringify(newCentroids)) break; centroids = newCentroids; } return centroids; }
Step 3: Update Your Image Variables
Run the clustering and rebuild your colors and pixels arrays with the compressed palette:
// Set your target number of colors (adjust based on your needs) const targetColorCount = 16; const compressedColorCentroids = kMeans(colorPoints, targetColorCount); // New colors array: convert centroids back to {r,g,b} objects const newColors = compressedColorCentroids.map(([r, g, b]) => ({ r, g, b })); // New pixels array: map original color indices to new palette indices const newPixels = pixels.map(row => row.map(pixelIdx => { const originalColor = colorPoints[pixelIdx]; const distances = compressedColorCentroids.map(centroid => Math.sqrt( Math.pow(originalColor[0] - centroid[0], 2) + Math.pow(originalColor[1] - centroid[1], 2) + Math.pow(originalColor[2] - centroid[2], 2) ) ); return distances.indexOf(Math.min(...distances)); }) );
If you want a simpler, faster approach, use a pre-defined palette (like the 216 web-safe colors). For each color in your original colors array, find the closest match in the fixed palette, then update your variables accordingly.
- Check quality: Calculate the mean squared error (MSE) between original and compressed colors to measure fidelity.
- Adjust
k: Balance between compression ratio (smallerk= more compression) and visual quality (largerk= better quality). - Performance: Since your image is under 100x100, even K-Means will run almost instantly—no need to worry about slowdowns.
内容的提问来源于stack exchange,提问作者Benjamin Brownlee

