You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于8位向量近似实现数组中最匹配颜色的查找?

问题解答

核心结论

直接用((a - b) ** 2 & 0xff00) >> 8作为平方距离的近似完全不可行,会丢失关键精度,导致颜色匹配结果严重失真。

为什么这个近似无效

8位颜色的差值范围是0255,对应的平方差范围是065025(即16位整数)。你取平方差的高8位,相当于只保留了数值除以256的整数部分:

  • 当差值为15时,平方是225,高8位为0,会被误判为无差异;
  • 当差值为16时,平方是256,高8位为1,会被当成最小差异。
    这种粗暴的截断会把大量不同的颜色差异归为同一等级,完全无法准确区分调色板中颜色的匹配度。

替代的8位向量优化方案

既然AVX2/AVX512没有_mm256_mulhi_epu8指令,推荐两种高效实现平方距离计算的思路:

1. 预计算平方表+向量查表

利用8位绝对差的范围仅0~255的特性,预先生成一个256项的平方值表,再通过向量指令批量查表得到平方差,最后累加RGB通道的结果:

// 预先生成平方值表
uint16_t sq_table[256];
void init_sq_table() {
    for (int i = 0; i < 256; i++) {
        sq_table[i] = (uint16_t)i * i;
    }
}

// AVX2批量计算平方距离
#include <immintrin.h>
__m256i compute_sq_distances_avx2(__m256i pixel_r, __m256i pixel_g, __m256i pixel_b, 
                                 __m256i palette_r, __m256i palette_g, __m256i palette_b) {
    // 计算RGB各通道绝对差
    __m256i dr = _mm256_abs_epi8(_mm256_sub_epi8(pixel_r, palette_r));
    __m256i dg = _mm256_abs_epi8(_mm256_sub_epi8(pixel_g, palette_g));
    __m256i db = _mm256_abs_epi8(_mm256_sub_epi8(pixel_b, palette_b));
    
    // 查表获取平方值
    __m256i sq_dr = _mm256_shuffle_epi8(_mm256_loadu_si256((const __m256i*)sq_table), dr);
    __m256i sq_dg = _mm256_shuffle_epi8(_mm256_loadu_si256((const __m256i*)sq_table), dg);
    __m256i sq_db = _mm256_shuffle_epi8(_mm256_loadu_si256((const __m256i*)sq_table), db);
    
    // 将8位平方值扩展为16位
    sq_dr = _mm256_unpacklo_epi8(sq_dr, _mm256_setzero_si256());
    sq_dg = _mm256_unpacklo_epi8(sq_dg, _mm256_setzero_si256());
    sq_db = _mm256_unpacklo_epi8(sq_db, _mm256_setzero_si256());
    
    // 累加三通道平方差
    __m256i total = _mm256_add_epi16(sq_dr, sq_dg);
    total = _mm256_add_epi16(total, sq_db);
    
    return total;
}

2. 扩展为16位后计算平方

直接将8位差值扩展为16位整数,再用AVX2的16位乘法指令计算平方,最后累加结果。这种方法虽然用到16位向量,但AVX2对16位数据的处理效率很高,完全可以接受:

#include <immintrin.h>
__m256i compute_sq_distances_avx2_v2(__m256i pixel_r, __m256i pixel_g, __m256i pixel_b, 
                                     __m256i palette_r, __m256i palette_g, __m256i palette_b) {
    // 将8位颜色值扩展为16位有符号整数
    __m256i p_r = _mm256_cvtepi8_epi16(pixel_r);
    __m256i p_g = _mm256_cvtepi8_epi16(pixel_g);
    __m256i p_b = _mm256_cvtepi8_epi16(pixel_b);
    
    __m256i pal_r = _mm256_cvtepi8_epi16(palette_r);
    __m256i pal_g = _mm256_cvtepi8_epi16(palette_g);
    __m256i pal_b = _mm256_cvtepi8_epi16(palette_b);
    
    // 计算差值
    __m256i dr = _mm256_sub_epi16(p_r, pal_r);
    __m256i dg = _mm256_sub_epi16(p_g, pal_g);
    __m256i db = _mm256_sub_epi16(p_b, pal_b);
    
    // 计算平方
    __m256i sq_dr = _mm256_mullo_epi16(dr, dr);
    __m256i sq_dg = _mm256_mullo_epi16(dg, dg);
    __m256i sq_db = _mm256_mullo_epi16(db, db);
    
    // 累加三通道平方差
    __m256i total = _mm256_add_epi16(sq_dr, sq_dg);
    total = _mm256_add_epi16(total, sq_db);
    
    return total;
}

纯C版本代码优化建议

你的纯C实现可以做两点优化:用无符号类型存储距离避免溢出风险,显式计算绝对差让逻辑更清晰:

#include <stdint.h>
#include <stdlib.h>

struct col {
    uint8_t r,g,b;
};

struct col cols[256];

int find_i(struct col x) {
    int mini = 0;
    uint32_t mind = UINT32_MAX;
    for (int i = 0; i < 256; i++) {
        uint8_t dr = abs((int)x.r - (int)cols[i].r);
        uint8_t dg = abs((int)x.g - (int)cols[i].g);
        uint8_t db = abs((int)x.b - (int)cols[i].b);
        uint32_t d = (uint32_t)dr*dr + (uint32_t)dg*dg + (uint32_t)db*db;
        if (d < mind) {
            mind = d;
            mini = i;
        }
    }
    return mini;
}

内容的提问来源于stack exchange,提问作者Cloud11665

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 02:57:55