聚合 HSL 值

Aggregating HSL Values

我编写了一段代码,它采用输入 HSL 颜色值,并将其归类为八种预定义颜色之一。因为我正在测量的物体的颜色并不完美 "smooth"(确切的 H、S 和 L 值因像素而异,但每个像素的范围都有限,具体取决于颜色),我想要在将生成的 HSL 值识别为特定颜色之前聚合对象上几个像素的 H、S 和 L。

举个例子,如果物体实际上是黑色的,那么它的任何像素的H应该在24-33的范围内,而黄色的H范围是33-37。聚合几个测量的 H,而不是依赖于单个测量,将倾向于产生更接近与正确颜色对应的范围的中间的结果,这理想地减少了需要解释模棱两可的 33 案例的可能性。

我一直在使用类似于中位数的东西作为我的聚合算法(具体算法如下所示),但我遇到过这种方法效果不佳的情况。特别是,紫色对象的 H 范围包括 231 - 240 和 0 - 10(240 是最大 H 值,因此它环绕)。所有其他颜色可能的 H、S 和 L 都是单一的、连续的范围,中值方法(或我的修改版本)对此效果很好,但在紫色 H 的情况下并不理想,因为它产生的结果实际上是更接近其范围的边缘,因此输入的 HSL 值更容易被误认为是另一种颜色(红色的 H 范围是 9 - 14)。

对于这项任务,是否有比中值算法更好的聚合算法,即使在环绕紫色 H 的情况下,该算法也往往会产生接近颜色 H、S 和 L 范围中间的结果?

算法:

private hslColor aggregateEachAttribute(hslColor[] hslData)
{
    List<double> hAttributes = new List<double>();
    List<double> sAttributes = new List<double>();
    List<double> lAttributes = new List<double>();

    for (int i = 0; i < hslData.Length; i++)
    {
        hAttributes.Add(hslData[i].H);
    }

    for (int i = 0; i < hslData.Length; i++)
    {
        sAttributes.Add(hslData[i].S);
    }

    for (int i = 0; i < hslData.Length; i++)
    {
        lAttributes.Add(hslData[i].L);
    }

    hAttributes.Sort();
    sAttributes.Sort();
    lAttributes.Sort();

    while (hAttributes.Distinct().Count() >= 3)
    {
        hAttributes.RemoveAll(h => h == hAttributes[0]);
        hAttributes.RemoveAll(h => h == hAttributes[hAttributes.Count() - 1]);
    }

    while (sAttributes.Distinct().Count() >= 3)
    {
        sAttributes.RemoveAll(s => s == sAttributes[0]);
        sAttributes.RemoveAll(s => s == sAttributes[sAttributes.Count() - 1]);
    }

    while (lAttributes.Distinct().Count() >= 3)
    {
        lAttributes.RemoveAll(l => l == lAttributes[0]);
        lAttributes.RemoveAll(l => l == lAttributes[lAttributes.Count() - 1]);
    }

    return new hslColor(hAttributes[0], sAttributes[0], lAttributes[0]);
}

如果您只是担心环绕,并且对于一些相当小的 X,您的分数是 < X 或 > 240-X,您可以通过将 X 添加到数据中来计算中位数 mod 240,计算一个中位数,然后从结果中减去X mod 240.

更一般地说,您可以通过找到最小化距离总和的值来寻找中值或中心点,其中 d(x, y) = min(|x-y|, |240 + x - y|, | 240 + y - x|)。我认为你可以在 O(n log n) 排序之后及时计算这个 O(n log n) 用一些相当繁琐的代码计算与每个候选中位数或中间值相关的距离总和,将点分成两组根据它们是从当前点顺时针还是逆时针旋转,并随着您从一个候选中位数或中心点移动到下一个而逐渐更新距离总和。