Как лучше всего «округлить» объект Color до ближайшей константы цвета? - PullRequest
12 голосов
/ 13 июня 2011

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

Ответы [ 3 ]

18 голосов
/ 13 июня 2011

Основной подход заключается в том, чтобы найти ближайший стандартный цвет для вашего образца, просто сравнив образец с каждым из них.Проблема, конечно, заключается в определении «ближайшего».Наиболее очевидным было бы использование евклидова расстояния в пространстве 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(), что сэкономит некоторые вычислительные затраты.

3 голосов
/ 13 июня 2011

Вероятно, лучшим способом было бы перебрать каждую константу и сравнить их соответствующие каналы 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(...);
}

Редактировать: добавлен алгоритм различий, благодаря ссылке @ Теда.

1 голос
/ 30 апреля 2017

Вы можете использовать встроенное преобразование цветов 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];
    }
}
...