【问题标题】:color founding multiple colours颜色创建多种颜色
【发布时间】:2022-10-16 19:28:52
【问题描述】:

我首先创建了一个包含 21 种不同颜色代码及其名称的字典

rgb_colors = {"Red":[1.0,0.0,0.0],"Green":[0.0,1.0,0.0],"Blue":[0.0,0.0,1.0],
             "Black":[0.0,0.0,0.0],"Almond":[0.94,0.87,0.8],"White":[1.0,1.0,1.0],
            "Brown":[0.8,0.5,0.2],"Cadet":[0.33,0.41,0.47],"Camel":[0.76,0.6,0.42],
            "Capri":[0.0,0.75,1.0],"Cardinal":[0.77,0.12,0.23],"Ceil":[0.57,0.63,0.81],
            "Celadon":[0.67,0.88,0.69],"Champagne":[0.97,0.91,0.81],"Charcoal":[0.21,0.27,0.31],
            "Cream":[1.0,0.99,0.82],"Cyan":[0.0,1.0,1.0],"DarkBlue":[0.0,0.0,0.55],
            "AmericanRose":[1.0,0.01,0.24],"Gray":[0.5,0.5,0.5],"Wenge":[0.39,0.33,0.32]}

然后我将其转换为 Df

RGB = pd.DataFrame(rgb_colors.items(), columns = ["Color","Color Code"])

然后我创建了所有颜色代码的列表 并要求输入代码。然后我使用输入颜色并找到每个颜色代码与输入和资产之间的欧几里得距离,以选择匹配至少 60% 的代码并将前三个代码用作最接近的颜色。

#list of colors
list_of_rgb = [[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0],[0.0,0.0,0.0],[0.94,0.87,0.8],
                 [1.0,1.0,1.0],[0.8,0.5,0.2],[0.33,0.41,0.47],[0.76,0.6,0.42],
                  [0.0,0.75,1.0],[0.77,0.12,0.23],[0.57,0.63,0.81],
                  [0.67,0.88,0.69],[0.97,0.91,0.81],[0.21,0.27,0.31],
                  [1.0,0.99,0.82],[0.0,1.0,1.0],[0.0,0.0,0.55],[1.0,0.01,0.24]
                  ,[0.5,0.5,0.5],[0.39,0.33,0.32]]
#input color
print("Enter R,G,B color codes")
color1 = []
for i in range(0,3):
    ele = float(input())
    color1.append(ele)
      
print(color1)

def closest(colors,color, threshold=60, max_return=3):
    colors = np.array(colors)
    color = np.array(color)
    distances = np.sqrt(np.sum((colors-color)**2,axis=1))
    boolean_masks = distances < (1.0 - (threshold / 100))
    outputs = colors[boolean_masks]
    output_distances = distances[boolean_masks]
    return outputs[np.argsort(output_distances)][:max_return]

closest_color = closest(list_of_rgb, color1)

closest_color

假设输入是[0.52,0.5,0.5] 那么最接近的颜色是

array([[0.5 , 0.5 , 0.5 ],
       [0.76, 0.6 , 0.42],
       [0.8 , 0.5 , 0.2 ]])

我的问题是,我怎样才能找到这些最接近的颜色中的每一种应该用于获取输入颜色的百分比?

它可以通过找到 3 个比例 p1、p2 和 p3 来解决,使得 p1+p2+p3=1 和

p1*(r1,g1,b1) + p2*(r2,g2,b2) + p3*(r3,g3,b3) = (r0,g0,b0)

我找不到 p1、p2 和 p3。任何人都可以帮助我了解如何找到 p 值吗?

【问题讨论】:

  • 您不能使用与输入颜色的距离吗?假设最接近的颜色是 95% 匹配、80% 匹配和 66% 匹配。第一种颜色可以使用 95/241,第二种颜色可以使用 80/241,第三种颜色可以使用 66/241。那会是什么样子?
  • @tcotts 不完全是,因为距离是在 3 个正交维度上计算的,并且颜色通常会对 3 个暗淡做出不同的贡献。
  • 你的模型不正确。
  • @Vitalizzare 你能解释一下我做错了什么吗?
  • @Jeeth 忘记颜色,将其视为一组向量。你问的是基地之间的切换。你不能自愿只取最近的三个。此外,您不能确定在新的 basys 中,坐标将满足在 [0, 1] 中且总和等于 1 的要求,就好像它们是某种混合的比例一样。此外,您的网格(一组预定义的颜色)太稀疏,有点“线性”。几乎所有的颜色都可以用一个平面来近似。你永远不会像#ff00ff 或#ffff00 这样的颜色。

标签: python-3.x pandas numpy colors


【解决方案1】:

您正在设置的system of linear equations 是过度确定的,这意味着通常没有解决方案。 对比例(或更准确地说是权重)的额外约束——总和为 1,在 [0, 1] 范围内——使事情变得更糟,因为即使存在解决方案,它也可能因为这些额外的约束而被丢弃.

当前形式的问题在数学上是不可解的。


无论您是否要包含固定总和约束,为线性组合寻找最佳权重的数学非常相似,尽管并不总是可以获得精确的解决方案,但有可能获得近似解决方案。

计算 的一种方法是通过线性编程,它基本上可以让您到达@greenerpastures's answer,但需要您使用线性编程。


使用蛮力和简单的最小二乘

在这里,我提出了一种更基本的方法,其中只涉及简单的线性代数,但忽略了权重在[0, 1] 范围内的要求(以后可能会介绍)。

写出的方程目标颜色b 作为颜色的线性组合可以写成矩阵形式:

A x = b

A 由您要使用的颜色组成,b目标颜色和x 是权重。

/ r0 r1 r2                 / r_ 
| g0 g1 g2 | (x0, x1, x2) = | g_ |
 b0 b1 b2 /                 b_ /

现在,如果det(A) != 0,这个方程组承认一个单一的解决方案。 由于在选定的颜色中有一个正交基,您实际上可以使用它们来构造一个Adet(A) != 0,因此总是可以找到一个x。 如果b 的元素在[0, 1] 范围内,那么x 的元素也是如此,因为本质上是b = x

一般来说,Ax = b线性方程组与np.linalg.solve()的解,只要det(A) != 0,当A由其他颜色组成时,可以用来寻找x

如果您想包含的颜色数量少于或少于通道数,则近似可以使用实现了least squares approximationnp.linalg.lstsq() 获得最小化平方和的解决方案:找到分配给n 向量(components)的最佳权重,使得它们的线性组合(加权和)尽可能接近(最小化平方和)到target 向量。

一旦你准备好寻找近似解,要求权重之和成为线性方程组中的附加参数。

这可以通过简单地增加Ab 来包含,并为A 设置一个额外的维度为1,为b 设置一个q,这样A x = b 变为:

/ r0 r1 r2                 / r3 
| g0 g1 g2 | (p0, p1, p2) = | g3 |
| b0 b1 b2 |                | b3 |
  1  1  1 /                  q /

现在包含了新方程p0 + p1 + p2 = q

虽然所有这些都可以使用任意颜色,但通过接近度选择的颜色不一定是很好地近似任意颜色的候选者。

例如,如果目标颜色是(1, 0, 1),而最接近的 3 个颜色恰好彼此成比例,比如(0.9, 0, 0)(0.8, 0, 0)(0.7, 0, 0),则最好使用更远的(0, 0, 0.5)但可以比(0.7, 0, 0) 做出更好的近似。

鉴于可能的组合数量相当少,可以尝试所有颜色,按固定递增大小的组。 这种方法称为蛮力,因为我们尝试了所有方法。 最小二乘法用于求权重。 然后我们可以添加额外的逻辑来强制执行我们想要的约束。

为了强制权重总和为 1,可以显式地对它们进行归一化。 为了将它们限制在特定范围内,我们可以丢弃不符合要求的权重(可能有一些容差atol 以减轻浮点比较的数值问题)。

代码如下:

import itertools
import dataclasses
from typing import Optional, Tuple, Callable, Sequence

import numpy as np


def best_linear_approx(target: np.ndarray, components: np.ndarray) -> np.ndarray:
    coeffs, _, _, _ = np.linalg.lstsq(components, target, rcond=-1)
    return coeffs


@dataclasses.dataclass
class ColorDecomposition:
    color: np.ndarray
    weights: np.ndarray
    components: np.ndarray
    indices: np.ndarray
    cost: float
    sum_weights: float


def decompose_bf_lsq(
    color: Sequence,
    colors: Sequence,
    max_nums: int = 3,
    min_nums: int = 1,
    min_weights: float = 0.0, 
    max_weights: float = 1.0,
    atol: float = 1e-6,
    norm_in_cost: bool = False,
    force_norm: bool = False,
) -> Optional[ColorDecomposition]:
    """Decompose `color` into a linear combination of a number of `colors`.

    This perfoms a brute-force search.

    Some constraints can be introduced into the decomposition:
     - The weights within a certain range ([`min_weights`, `max_weights`])
     - The weights to accumulate (sum or average) to a certain value.

    The colors are chosen to have minimum sum of squared differences (least squares).

    Additional costs may be introduced in the brute-force search, to favor
    particular solutions when the least squares are the same.

    Args:
        color: The color to decompose.
        colors: The base colors to use for the decomposition.
        max_nums: The maximum number of base colors to use.
        min_weights: The minimum value for the weights.
        max_weights: The maximum value for the weights.
        atol: The tolerance on the weights.
        norm_in_cost: Include the norm in the cost for the least squares.
        force_norm: If True, the weights are normalized to `acc_to`, if set.
        weight_costs: The additional weight costs to prefer specific solutions.

    Returns:
        The resulting color decomposition.
    """
    color = np.array(color)
    colors = np.array(colors)
    num_colors, num_channels = colors.shape
    # augment color/colors
    if norm_in_cost:
        colors = np.concatenate(
            [colors, np.ones(num_colors, dtype=colors.dtype)[:, None]],
            axis=1,
        )
        color = np.concatenate([color, np.ones(1, dtype=colors.dtype)])
    # brute-force search
    best_indices = None
    best_weights = np.zeros(1)
    best_cost = np.inf
    for n in range(min_nums, max_nums + 1):
        for indices in itertools.combinations(range(num_colors), n):
            if np.allclose(color, np.zeros_like(color)):
                # handles the 0 case
                weights = np.ones(n)
            else:
                # find best linear approx
                weights = best_linear_approx(color, colors[indices, :].T)
            # weights normalization
            if force_norm and np.all(weights > 0.0):
                norm = np.sum(weights)
                weights /= norm
            # add some tolerance
            if atol > 0:
                mask = np.abs(weights) > atol
                weights = weights[mask]
                indices = np.array(indices)[mask].tolist()
            if atol > 0 and max_weights is not None:
                mask = (weights > max_weights - atol) & (weights < max_weights + atol)
                weights[mask] = max_weights
            if atol > 0 and min_weights is not None:
                mask = (weights < min_weights + atol) & (weights > min_weights - atol)
                weights[mask] = min_weights
            # compute the distance between the current approximation and the target
            err = color - (colors[indices, :].T @ weights)
            curr_cost = np.sum(err * err)
            if (
                curr_cost <= best_cost
                and (min_weights is None or np.all(weights >= min_weights))
                and (max_weights is None or np.all(weights <= max_weights))
            ):
                best_indices = indices
                best_weights = weights
                best_cost = curr_cost
    if best_indices is not None:
        return ColorDecomposition(
            color=(colors[best_indices, :].T @ best_weights)[:num_channels],
            weights=best_weights,
            components=[c for c in colors[best_indices, :num_channels]],
            indices=best_indices,
            cost=best_cost,
            sum_weights=np.sum(best_weights),
        )
    else:
        return None

这可以按如下方式使用:

colors = [
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0],
    [0.0, 0.0, 0.0],
    [0.94, 0.87, 0.8],
    [1.0, 1.0, 1.0],
    [0.8, 0.5, 0.2],
    [0.33, 0.41, 0.47],
    [0.76, 0.6, 0.42],
    [0.0, 0.75, 1.0],
    [0.77, 0.12, 0.23],
    [0.57, 0.63, 0.81],
    [0.67, 0.88, 0.69],
    [0.97, 0.91, 0.81],
    [0.21, 0.27, 0.31],
    [1.0, 0.99, 0.82],
    [0.0, 1.0, 1.0],
    [0.0, 0.0, 0.55],
    [1.0, 0.01, 0.24],
    [0.5, 0.5, 0.5],
    [0.39, 0.33, 0.32],
]


some_colors = [[0.9, 0.6, 0.5], [0.52, 0.5, 0.5], [0.5, 0.5, 0.5], [0, 0, 0], [1, 1, 1]]
# some_colors = [[0., 0., 0.]]
for color in some_colors:
    print(color)
    print(decompose_bf_lsq(color, colors, max_nums=1))
    print(decompose_bf_lsq(color, colors, max_nums=2))
    print(decompose_bf_lsq(color, colors))
    print(decompose_bf_lsq(color, colors, min_weights=0.0, max_weights=1.0))
    print(decompose_bf_lsq(color, colors, norm_in_cost=True))
    print(decompose_bf_lsq(color, colors, force_norm=True))
    print(decompose_bf_lsq(color, colors, norm_in_cost=True, force_norm=True))
# [0.9, 0.6, 0.5]
# ColorDecomposition(color=array([0.72956991, 0.68444188, 0.60922849]), weights=array([0.75213393]), components=[array([0.97, 0.91, 0.81])], indices=[13], cost=0.048107706898684606, sum_weights=0.7521339326213355)
# ColorDecomposition(color=array([0.9       , 0.60148865, 0.49820272]), weights=array([0.2924357, 0.6075643]), components=[array([1., 0., 0.]), array([1.  , 0.99, 0.82])], indices=[0, 15], cost=5.446293494705139e-06, sum_weights=0.8999999999999999)
# ColorDecomposition(color=array([0.9, 0.6, 0.5]), weights=array([0.17826087, 0.91304348, 0.43478261]), components=[array([0., 0., 1.]), array([0.8, 0.5, 0.2]), array([0.39, 0.33, 0.32])], indices=[2, 6, 20], cost=0.0, sum_weights=1.526086956521739)
# ColorDecomposition(color=array([0.9, 0.6, 0.5]), weights=array([0.17826087, 0.91304348, 0.43478261]), components=[array([0., 0., 1.]), array([0.8, 0.5, 0.2]), array([0.39, 0.33, 0.32])], indices=[2, 6, 20], cost=0.0, sum_weights=1.526086956521739)
# ColorDecomposition(color=array([0.9, 0.6, 0.5]), weights=array([0.4, 0.1, 0.5]), components=[array([1., 0., 0.]), array([0., 1., 0.]), array([1., 1., 1.])], indices=[0, 1, 5], cost=2.6377536518327582e-30, sum_weights=0.9999999999999989)
# ColorDecomposition(color=array([0.9, 0.6, 0.5]), weights=array([0.4, 0.1, 0.5]), components=[array([1., 0., 0.]), array([0., 1., 0.]), array([1., 1., 1.])], indices=[0, 1, 5], cost=3.697785493223493e-32, sum_weights=0.9999999999999999)
# ColorDecomposition(color=array([0.9, 0.6, 0.5]), weights=array([0.4, 0.1, 0.5]), components=[array([1., 0., 0.]), array([0., 1., 0.]), array([1., 1., 1.])], indices=[0, 1, 5], cost=1.355854680848614e-31, sum_weights=1.0)
# [0.52, 0.5, 0.5]
# ColorDecomposition(color=array([0.50666667, 0.50666667, 0.50666667]), weights=array([0.50666667]), components=[array([1., 1., 1.])], indices=[5], cost=0.0002666666666666671, sum_weights=0.5066666666666667)
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.52, 0.5 ]), components=[array([1., 0., 0.]), array([0., 1., 1.])], indices=[0, 16], cost=2.465190328815662e-32, sum_weights=1.02)
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.2  , 0.2  , 0.508]), components=[array([0.76, 0.6 , 0.42]), array([0.57, 0.63, 0.81]), array([0.5, 0.5, 0.5])], indices=[8, 11, 19], cost=0.0, sum_weights=0.9079999999999999)
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.2  , 0.2  , 0.508]), components=[array([0.76, 0.6 , 0.42]), array([0.57, 0.63, 0.81]), array([0.5, 0.5, 0.5])], indices=[8, 11, 19], cost=0.0, sum_weights=0.9079999999999999)
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.02, 0.48, 0.5 ]), components=[array([1., 0., 0.]), array([0., 0., 0.]), array([1., 1., 1.])], indices=[0, 3, 5], cost=2.0954117794933126e-31, sum_weights=0.9999999999999996)
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.02, 1.  ]), components=[array([1., 0., 0.]), array([0.5, 0.5, 0.5])], indices=[0, 19], cost=0.0, sum_weights=1.02)
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.02, 0.02, 0.96]), components=[array([1., 0., 0.]), array([1., 1., 1.]), array([0.5, 0.5, 0.5])], indices=[0, 5, 19], cost=9.860761315262648e-32, sum_weights=1.0)
# [0.5, 0.5, 0.5]
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([1.]), components=[array([0.5, 0.5, 0.5])], indices=[19], cost=0.0, sum_weights=1.0)
# [0, 0, 0]
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=[3], cost=0.0, sum_weights=1.0)
# [1, 1, 1]
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1.]), components=[array([1., 1., 1.])], indices=[5], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1.]), components=[array([1., 1., 1.])], indices=[5], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([0.1610306 , 0.96618357, 0.28692724]), components=[array([0.21, 0.27, 0.31]), array([1.  , 0.99, 0.82]), array([0.  , 0.  , 0.55])], indices=[14, 15, 17], cost=0.0, sum_weights=1.4141414141414144)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([0.1610306 , 0.96618357, 0.28692724]), components=[array([0.21, 0.27, 0.31]), array([1.  , 0.99, 0.82]), array([0.  , 0.  , 0.55])], indices=[14, 15, 17], cost=0.0, sum_weights=1.4141414141414144)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1.]), components=[array([1., 1., 1.])], indices=[5], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1.]), components=[array([1., 1., 1.])], indices=[5], cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1.]), components=[array([1., 1., 1.])], indices=[5], cost=0.0, sum_weights=1.0)

使用蛮力和有界最小化

这与上面基本相同,除了现在我们使用比简单的无界最小二乘法更复杂的优化方法。 这为我们提供了已经有界的权重,因此不需要额外的代码来处理这种情况,最重要的是,仅根据成本就放弃了最佳解决方案。

该方法将显示为:

import scipy.optimize


def _err(x, A, b):
    return b - A @ x


def cost(x, A, b):
    err = _err(x, A, b)
    return np.sum(err * err)


def decompose_bf_min(
    color: Sequence,
    colors: Sequence,
    max_nums: int = 3,
    min_nums: int = 1,
    min_weights: float = 0.0, 
    max_weights: float = 1.0,
    normalize: bool = False,
) -> Optional[ColorDecomposition]:
    color = np.array(color)
    colors = np.array(colors)
    num_colors, num_channels = colors.shape
    # augment color/colors to include norm in cost
    if normalize:
        colors = np.concatenate(
            [colors, np.ones(num_colors, dtype=colors.dtype)[:, None]],
            axis=1,
        )
        color = np.concatenate([color, np.ones(1, dtype=colors.dtype)])
    # brute-force search
    best_indices = None
    best_weights = np.zeros(1)
    best_cost = np.inf
    for n in range(min_nums, max_nums + 1):
        for indices in itertools.combinations(range(num_colors), n):
            weights = np.full(n, 1 / n)
            if not np.allclose(color, 0):
                res = scipy.optimize.minimize(
                    cost,
                    weights,
                    (colors[indices, :].T, color),
                    bounds=[(min_weights, max_weights) for _ in range(n)]
                )
                weights = res.x
            curr_cost = cost(weights, colors[indices, :].T, color)
            if curr_cost <= best_cost:
                best_indices = indices
                best_weights = weights
                best_cost = curr_cost
    if best_indices is not None:
        return ColorDecomposition(
            color=(colors[best_indices, :].T @ best_weights)[:num_channels],
            weights=best_weights,
            components=[c for c in colors[best_indices, :num_channels]],
            indices=best_indices,
            cost=best_cost,
            sum_weights=np.sum(best_weights),
        )
    else:
        return None

其工作原理如下:

some_colors = [[0.9, 0.6, 0.5], [0.52, 0.5, 0.5], [0.5, 0.5, 0.5], [0, 0, 0], [1, 1, 1]]
# some_colors = [[0., 0., 0.]]
for color in some_colors:
    print(color)
    print(decompose_bf_min(color, colors))
    print(decompose_bf_min(color, colors, normalize=True))
# [0.9, 0.6, 0.5]
# ColorDecomposition(color=array([0.9, 0.6, 0.5]), weights=array([0.42982455, 0.2631579 , 0.70701754]), components=[array([0.8, 0.5, 0.2]), array([0.77, 0.12, 0.23]), array([0.5, 0.5, 0.5])], indices=(6, 10, 19), cost=2.3673037349051385e-17, sum_weights=1.399999995602849)
# ColorDecomposition(color=array([0.89999998, 0.60000001, 0.49999999]), weights=array([0.4       , 0.10000003, 0.49999999]), components=[array([1., 0., 0.]), array([0., 1., 0.]), array([1., 1., 1.])], indices=(0, 1, 5), cost=6.957464274781682e-16, sum_weights=1.0000000074212045)
# [0.52, 0.5, 0.5]
# ColorDecomposition(color=array([0.52, 0.5 , 0.5 ]), weights=array([0.02, 0.  , 1.  ]), components=[array([1., 0., 0.]), array([1.  , 0.99, 0.82]), array([0.5, 0.5, 0.5])], indices=(0, 15, 19), cost=2.1441410828292465e-17, sum_weights=1.019999995369513)
# ColorDecomposition(color=array([0.52000021, 0.50000018, 0.50000018]), weights=array([0.02000003, 0.02000077, 0.95999883]), components=[array([1., 0., 0.]), array([1., 1., 1.]), array([0.5, 0.5, 0.5])], indices=(0, 5, 19), cost=2.517455337509621e-13, sum_weights=0.9999996259509482)
# [0.5, 0.5, 0.5]
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([0., 0., 1.]), components=[array([0., 1., 1.]), array([1.  , 0.01, 0.24]), array([0.5, 0.5, 0.5])], indices=(16, 18, 19), cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0.5, 0.5, 0.5]), weights=array([0., 1., 0.]), components=[array([1.  , 0.01, 0.24]), array([0.5, 0.5, 0.5]), array([0.39, 0.33, 0.32])], indices=(18, 19, 20), cost=0.0, sum_weights=1.0)
# [0, 0, 0]
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1.]), components=[array([0., 0., 0.])], indices=(3,), cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([0., 0., 0.]), weights=array([1., 0., 0.]), components=[array([0., 0., 0.]), array([0.  , 0.  , 0.55]), array([1.  , 0.01, 0.24])], indices=(3, 17, 18), cost=0.0, sum_weights=1.0)
# [1, 1, 1]
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1., 0., 0.]), components=[array([1., 1., 1.]), array([0., 1., 1.]), array([1.  , 0.01, 0.24])], indices=(5, 16, 18), cost=0.0, sum_weights=1.0)
# ColorDecomposition(color=array([1., 1., 1.]), weights=array([1., 0., 0.]), components=[array([1., 1., 1.]), array([1.  , 0.01, 0.24]), array([0.5, 0.5, 0.5])], indices=(5, 18, 19), cost=0.0, sum_weights=1.0)

【讨论】:

  • 对比例的附加约束(总计为 1,在 [0, 1] 范围内)。如果范围是[0,3]并且每个约束范围是[0,1]怎么办?我可以使用这个获得解决方案吗?
  • 你能向我解释一下你是如何选择合适的颜色进行混合的吗?
  • best_linear_approx() 函数的用例是什么?
  • @Jeeth 如果您将每个权重设置在 [0, 1] 范围内,则 [0, num_weights] 中的总和是自动的。在我提供的函数中,您只需将acc_to 设置为None。选择混合颜色以选择提供最佳近似值的颜色,即最小平方和。所有这些都被尝试过(这就是蛮力的意思)。可以使用任何其他指标,但它与 np.linalg.lstsq() 使用的不匹配。
  • best_linear_approx() 找到分配给 n 向量 (components) 的最佳权重,以使它们的线性组合(加权和)尽可能接近(最小化平方和)target 向量。
【解决方案2】:

您可以制作一个方程组来得出一个加权向量,该向量将告诉您三种颜色的组合,它与输入完全相等。它的形式是 Ax=b,其中 A 是颜色矩阵,x 是要求解的未知变量,b 是颜色目标。在这种情况下,您已经计算了“A”,只需要转置即可。当然,你也有你的目标颜色。然而,这并没有映射到模糊集(即从 0 到 1 包括在内)。例如,如果您只能改变三种颜色的强度(从 0 到 1 或等效地从 0% 到 100%)来实现这种输入颜色,那么这种方法是不够的。如果您需要每个权重值介于 0 和 1 之间,那么您可以求解一个线性程序,在其中您指定权重上的 0<=w<=1 约束。这似乎有点复杂,但如果有兴趣,可以这样做。

编辑:我添加了线性程序来解决问题。线性程序用于解决对方程组中的变量施加约束的复杂优化问题。他们非常强大,可以完成很多工作。不幸的是,它大大提高了代码的复杂性。另外,只是为了让您知道,不能保证会有一个解决方案,其中所有变量都在集合 [0,1] 中。我相信在这个特定的例子中,这是不可能的,但它确实非常接近。

import numpy as np

# target vector
input_color = np.array([.52, .5, .5])
input_color = np.reshape(input_color, (1, len(input_color))).T

# create color matrix with 3 chosen colors
color_1 = np.array([.5, .5, .5])
color_2 = np.array([.76, .6, .42])
color_3 = np.array([.8, .5, .2])
C = np.vstack([color_1, color_2, color_3]).T

# use linear algebra to solve for variables
weights = np.matmul(np.linalg.pinv(C),input_color)

# show that the correct values for each color were calculated
print(weights[0]*color_1 + weights[1]*color_2 + weights[2]*color_3)




from scipy.optimize import linprog

color_1 = np.array([.5, .5, .5])
color_2 = np.array([.76, .6, .42])
color_3 = np.array([.8, .5, .2])

# make variables greater than zero
ineq_1 = np.array([-1, 0, 0])
ineq_2 = np.array([0, -1, 0])
ineq_3 = np.array([0, 0, -1])

# make variables less than or equal to one
ineq_4 = np.array([1, 0, 0])
ineq_5 = np.array([0, 1, 0])
ineq_6 = np.array([0, 0, 1])

C = np.vstack([color_1, color_2, color_3]).T
C = np.vstack([C, ineq_1, ineq_2, ineq_3, ineq_4, ineq_5, ineq_6])

A = C

input_color = np.array([.52, .5, .5])
b = np.concatenate((input_color, np.array([0, 0, 0, 1, 1, 1])),axis=0)
b = np.reshape(b, (1, len(b))).T

# scipy minimizes, so maximize by multiplying by -1
c = -1*np.array([1, 1, 1])


# Visually, what we have right now is

# maximize f = x1 + x2 + x3
#    color_1_red*x1 + color_2_red*x2 + color_3_red*x3 <= input_color_red
#    color_1_gre*x1 + color_2_gre*x2 + color_3_gre*x3 <= input_color_gre
#    color_1_blu*x1 + color_2_blu*x2 + color_3_blu*x3 <= input_color_blu
#    x1 >= 0
#    x2 >= 0
#    x3 >= 0
#    x1 <= 1
#    x2 <= 1
#    x3 <= 1

# As you'll notice, we have the original system of equations in our constraint
#  on the system. However, we have added the constraints that the variables
#  must be in the set [0,1]. We maximize the variables because linear programs
#  are made simpler when the system of equations are less than or equal to.


# calculate optimal variables with constraints
res = linprog(c, A_ub=A, b_ub=b)

print(res.x)

print(res.x[0]*color_1 + res.x[1]*color_2 + res.x[2]*color_3)

【讨论】:

  • 实际上我需要权重值在 0 到 1 之间你能帮我吗,你能告诉我如何实现它我可以使用什么方法来实现它。你所说的线性程序是什么意思,你能告诉我如何使用它来实现所需的权重。
  • 代码与输入代码不同,我实际上是在寻找一种可以使输出代码与输入代码相同的方法。感谢您尝试帮助我,但这不是我想要的。
  • 重要的是我想知道每个颜色代码需要多少百分比才能获得输入颜色代码。我实际上想要在这里混合颜色所以,我需要找出每种颜色需要多少百分比才能获得输入颜色。
  • 只要基向量在[0, 1] 范围内并且向量是正交的,就可以保证权重在[0, 1] 范围内。例如,规范基础(即[1, 0, 0][0, 1, 0][0, 0, 1])将实现这一点。如果向量不是正交的,你不能保证,但你可以搜索最接近的向量,它们是任何其他向量的跨度。 @Jeeth,您确实需要定义您的优先事项。
  • @Jeeth 你可能对一些geometric interpretations of this problem 感兴趣。我认为一般来说你不能保证最近的三个点是正确的。我怀疑这是真的与你的点是凸多面体的顶点有关。因此,您可能想看看this 并将其应用于您的观点。
猜你喜欢
  • 1970-01-01
  • 2020-03-08
  • 2018-07-24
  • 1970-01-01
  • 1970-01-01
  • 2017-05-04
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多