javascript - 给定一个随机十六进制颜色列表,根据 "likeness"对它们进行排序

标签 javascript sorting colors grouping approximation

例如,此十六进制值列表:

{
"colors" : [{"hex"   : "#fe4670"},
            {"hex"   : "#5641bc"},
            {"hex"   : "#d53fc3"},
            {"hex"   : "#6b5e09"},
            {"hex"   : "#4dd685"},
            {"hex"   : "#88d63f"},
            {"hex"   : "#eb93f3"},
            {"hex"   : "#f44847"},
            {"hex"   : "#32d159"},
            {"hex"   : "#6e9bde"},
            {"hex"   : "#c3ec64"},
            {"hex"   : "#81cce5"},
            {"hex"   : "#7233b6"},
            {"hex"   : "#bb90c3"},
            {"hex"   : "#728fde"},
            {"hex"   : "#7ef46a"},
            {"hex"   : "#f7cfff"},
            {"hex"   : "#c8b708"},
            {"hex"   : "#b45a35"},
            {"hex"   : "#589279"},
            {"hex"   : "#51f1e1"},
            {"hex"   : "#b1d770"},
            {"hex"   : "#db463d"},
            {"hex"   : "#5b02a2"},
            {"hex"   : "#909440"},
            {"hex"   : "#6f53fe"},
            {"hex"   : "#4c29bd"},
            {"hex"   : "#3b24f8"},
            {"hex"   : "#465271"},
            {"hex"   : "#6243"},
            {"hex"   : "#dbcc4"},
            {"hex"   : "#187c6"},            
            {"hex"   : "#1085e2"},
            {"hex"   : "#b521e9"},
            {"hex"   : "#4bd36d"},             
            {"hex"   : "#11bc34"},
            {"hex"   : "#455c47"},
            {"hex"   : "#a71bbf"},
            {"hex"   : "#988fc2"},
            {"hex"   : "#226cfe"}]
}

理想情况下,它应该将“绿色”、“蓝色”、“紫色”等分组。

我还没有想出一个好的方法来对它们进行分组。将颜色转换为 HSV,然后按 Hue、Sat、Val 排序几乎可以正常工作,但有一些突出的异常(exception):

image

我读过的另一种方法是将它们转换为 LAB 色彩空间,然后计算 DeltaE。我在这方面取得了不同程度的成功:

image

(这是根据一种颜色对整个列表进行排序)。我想对每种颜色相对于每种颜色的距离进行排序。

最佳答案

以下示例选择第一种颜色用作比较颜色。它首先将该颜色添加到新数组中,然后迭代其余颜色,比较颜色以查找最相似的颜色。

对于它迭代的每种颜色,它会从比较中的第二种颜色中减去第一种颜色中的红色,然后减去绿色,然后减去蓝色。然后它找到绝对值(没有负数)。之后,它将这些值相加并除以三。该数字是两种颜色之间的平均差异。

一旦找到最接近的颜色,它就会选择该颜色作为新的比较颜色,将其从原始颜色数组中删除,并将其插入已排序的数组中。这样做直到没有颜色为止。

当提供更大的数据集时,它肯定需要一些工作,但这就是我昨晚的全部时间。我将继续努力,直到我有更好的东西。

const sort = data => {
    data = Object.assign([], data);
    const sorted = [data.shift()];

    while(data.length) {
        const [a] = sorted, c = { d: Infinity };

        for(let [i, b] of Object.entries(data)) {
            const average = Math.floor((
                Math.abs(a.r - b.r) +
                Math.abs(a.g - b.g) +
                Math.abs(a.b - b.b)
            ) / 3);

            if(average < c.d) {
                Object.assign(c, { d: average, i: i });
            }
        }
        
        sorted.unshift(data.splice(c.i, 1)[0]);
    }
    
    return sorted.reverse();
};

const test = (title, data) => {
    document.body.insertAdjacentHTML('beforeend', `<h2>${title}</h2>`);

    for(let c of data) {
        document.body.insertAdjacentHTML('beforeend', `<swatch style="background: rgb(${c.r},${c.g},${c.b})"></swatch>`);
    }
    
    return test;
}

const data = [
    {"hex": "#fe4670"},{"hex": "#5641bc"},{"hex": "#d53fc3"},{"hex": "#6b5e09"},
    {"hex": "#4dd685"},{"hex": "#88d63f"},{"hex": "#eb93f3"},{"hex": "#f44847"},
    {"hex": "#32d159"},{"hex": "#6e9bde"},{"hex": "#c3ec64"},{"hex": "#81cce5"},
    {"hex": "#7233b6"},{"hex": "#bb90c3"},{"hex": "#728fde"},{"hex": "#7ef46a"},
    {"hex": "#f7cfff"},{"hex": "#c8b708"},{"hex": "#b45a35"},{"hex": "#589279"},
    {"hex": "#51f1e1"},{"hex": "#b1d770"},{"hex": "#db463d"},{"hex": "#5b02a2"},
    {"hex": "#909440"},{"hex": "#6f53fe"},{"hex": "#4c29bd"},{"hex": "#3b24f8"},
    {"hex": "#465271"},{"hex": "#6243"},  {"hex": "#dbcc4"}, {"hex": "#187c6"},
    {"hex": "#1085e2"},{"hex": "#b521e9"},{"hex": "#4bd36d"},{"hex": "#11bc34"},
    {"hex": "#455c47"},{"hex": "#a71bbf"},{"hex": "#988fc2"},{"hex": "#226cfe"}
].reduce((m, e) => (m.push(Object.assign(e, {
    r: parseInt(e.hex.substring(1, 3), 16) || 0,
    g: parseInt(e.hex.substring(3, 5), 16) || 0,
    b: parseInt(e.hex.substring(5, 7), 16) || 0
})), m), []);

const bigdata = (() => {
    const data = [];

    const rand = () => Math.floor(Math.random() * 256);

    for(let i = 0; i < 1000; ++i) {
        data.push({r: rand(), g: rand(), b: rand()});
    }
    
    return data;
})();

test('Unsorted', data)('Sorted', sort(data))('A Larger Dataset', sort(bigdata));
swatch { display: inline-block;  border: 1px solid;  margin-left: 1px; margin-top: 1px; width: 20px; height: 20px; }
h2 { margin: 0; font-family: Verdana, Tahoma, "Sans Serif"}

以下代码片段的作用基本相同,只不过它搜索已排序的数组以查找该数组中最接近的匹配项,然后将未排序数组中的颜色插入到其最接近的匹配项旁边。

它似乎对样本进行渐变处理效果不佳,但它似乎确实可以更好地将颜色组合在一起。

const sort = data => {
    data = Object.assign([], data);
    const sorted = [data.shift()];

    while(data.length) {
        const a = data.shift(), c = { d: Infinity };

        for(let [i, b] of Object.entries(sorted)) {
            const average = Math.floor((
                Math.abs(a.r - b.r) +
                Math.abs(a.g - b.g) +
                Math.abs(a.b - b.b)
            ) / 3);

            if(average < c.d) {
                Object.assign(c, { d: average, i: i });
            }
        }
        
        sorted.splice(c.i, 0, a);
    }
    
    return sorted.reverse();
};

const test = (title, data) => {
    document.body.insertAdjacentHTML('beforeend', `<h2>${title}</h2>`);

    for(let c of data) {
        document.body.insertAdjacentHTML('beforeend', `<swatch style="background: rgb(${c.r},${c.g},${c.b})"></swatch>`);
    }
    
    return test;
}

const data = [
    {"hex": "#fe4670"},{"hex": "#5641bc"},{"hex": "#d53fc3"},{"hex": "#6b5e09"},
    {"hex": "#4dd685"},{"hex": "#88d63f"},{"hex": "#eb93f3"},{"hex": "#f44847"},
    {"hex": "#32d159"},{"hex": "#6e9bde"},{"hex": "#c3ec64"},{"hex": "#81cce5"},
    {"hex": "#7233b6"},{"hex": "#bb90c3"},{"hex": "#728fde"},{"hex": "#7ef46a"},
    {"hex": "#f7cfff"},{"hex": "#c8b708"},{"hex": "#b45a35"},{"hex": "#589279"},
    {"hex": "#51f1e1"},{"hex": "#b1d770"},{"hex": "#db463d"},{"hex": "#5b02a2"},
    {"hex": "#909440"},{"hex": "#6f53fe"},{"hex": "#4c29bd"},{"hex": "#3b24f8"},
    {"hex": "#465271"},{"hex": "#6243"},  {"hex": "#dbcc4"}, {"hex": "#187c6"},
    {"hex": "#1085e2"},{"hex": "#b521e9"},{"hex": "#4bd36d"},{"hex": "#11bc34"},
    {"hex": "#455c47"},{"hex": "#a71bbf"},{"hex": "#988fc2"},{"hex": "#226cfe"}
].reduce((m, e) => (m.push(Object.assign(e, {
    r: parseInt(e.hex.substring(1, 3), 16) || 0,
    g: parseInt(e.hex.substring(3, 5), 16) || 0,
    b: parseInt(e.hex.substring(5, 7), 16) || 0
})), m), []);

const bigdata = (() => {
    const data = [];

    const rand = () => Math.floor(Math.random() * 256);

    for(let i = 0; i < 1000; ++i) {
        data.push({r: rand(), g: rand(), b: rand()});
    }
    
    return data;
})();

test('Unsorted', data)('Sorted', sort(data))('A Larger Dataset', sort(bigdata));
swatch { display: inline-block;  border: 1px solid;  margin-left: 1px; margin-top: 1px; width: 20px; height: 20px; }
h2 { margin: 0; font-family: Verdana, Tahoma, "Sans Serif"}

关于javascript - 给定一个随机十六进制颜色列表,根据 "likeness"对它们进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22973926/

相关文章:

java - 当我需要时更改 JList 项目颜色

javascript - 使用angular js通过代理服务器的Http请求

c++ - 对 vector 中的较高值和较低值进行排序

java - 设置文本字段的禁用背景颜色

sorting - 如何反转 "glob"标志选项提供的 toctree 的顺序?

php - 从数组中选择2个最新日期并按顺序排列

python - 如何在 tkinter ttk.button 中使用不同颜色为按钮着色?

javascript - snyk依赖下载问题

javascript - 是否可以调用变量之前的增量?

javascript - 为对象创建跳跃效果