Каков наилучший способ "округлить" объект "Цвет" до ближайшей цветовой константы?

Я получаю точный цвет пикселя и хотел бы связать этот точный цвет с константой, подобной Color.blue. Есть ли простой способ "округлить" до ближайшей цветовой константы? Кроме того, есть ли способ определить ваши собственные цветовые константы?

Ответ 1

Основной подход заключается в том, чтобы найти ближайший стандартный цвет для вашего образца, просто сравнивая образец с каждым из них. Проблема, конечно, заключается в определении "ближайшего". Наиболее очевидным было бы использование евклидова расстояния в пространстве RGB. Проблема в том, что это расстояние не очень хорошо согласуется с нашим восприятием "самого близкого цвета". Обсуждение этой проблемы, наряду с хорошей (легко вычисляемой) метрикой (включая псевдокод!), Можно найти в этой статье.

РЕДАКТИРОВАТЬ: просто в случае, если ссылка на этот документ не работает (или если вы ленивы и хотите использовать код, не понимая, что он делает), здесь моя Java-версия "функции цветового расстояния", которую статья предлагает как "низкий "приближение стоимости" к их рекомендуемой функции расстояния (взвешенное евклидово расстояние в пространстве RGB):

double colorDistance(Color c1, Color c2)
{
    int red1 = c1.getRed();
    int red2 = c2.getRed();
    int rmean = (red1 + red2) >> 1;
    int r = red1 - red2;
    int g = c1.getGreen() - c2.getGreen();
    int b = c1.getBlue() - c2.getBlue();
    return Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}

Обратите внимание, что если вы просто собираетесь ранжировать цветовые расстояния, вы можете обойтись без вызова Math.sqrt(), что сэкономит некоторые вычислительные затраты.

Ответ 2

Вероятно, лучшим способом было бы getRed каждую константу и сравнить их соответствующие каналы RGB (getRed, getGreen, getBlue). Следите за тем, который ближе всего.

Color color = new Color(...);
Color[] constantColors = new Color[] { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow };
Color nearestColor = null;
Integer nearestDistance = new Integer(Integer.MAX_VALUE);

for (Color constantColor : constantColors) {
    if (nearestDistance > Math.sqrt(
            Math.pow(color.getRed() - constantColor.getRed(), 2)
            - Math.pow(color.getGreen() - constantColor.getGreen(), 2)
            - Math.pow(color.getBlue() - constantColor.getBlue(), 2)
        )
    ) {
        nearestColor = color;
    }
}

Нет, вы не можете добавить цветовые константы в класс, но вы можете создать собственный класс для хранения констант.

class MyColors {
    public static final Color heliotrope = new Color(...);
}

Изменить: добавлен алгоритм разницы, благодаря ссылке @Ted.

Ответ 3

Вы можете использовать встроенное преобразование цвета Java с помощью IndexColorModel, содержащего палитру возможных цветов. Внутренне класс использует эвклидовое расстояние над цветовыми компонентами для определения ближайшего цвета.

import java.awt.Color;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;

public class ColorConverter {
    private final Color[] colors;
    private final IndexColorModel colorModel;

    public ColorConverter(Color[] colors) {
        this.colors = colors;
        this.colorModel = createColorModel(colors);
    }

    private static IndexColorModel createColorModel(Color[] colors) {
        final int[] cmap = new int[colors.length];
        for (int i = 0; i<colors.length; i++) {
            cmap[i] = colors[i].getRGB();
        }
        final int bits = (int) Math.ceil(Math.log(cmap.length)/Math.log(2));
        return new IndexColorModel(bits, cmap.length, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
    }

    public Color nearestColor(Color color) {
        final byte index = ((byte[])colorModel.getDataElements(color.getRGB(), null))[0];
        return colors[index];
    }
}