gpt4 book ai didi

java - 在JAVA中将图像减少到固定数量的给定颜色

转载 作者:行者123 更新时间:2023-12-03 04:28:42 24 4
gpt4 key购买 nike

我想以特殊方式编辑图像:

我给出了 40 种颜色的列表。现在我有了一张包含所有可能颜色的图像。我想将图像减少到只有 12 种颜色(40 种可能的颜色中)并获得最佳结果。有没有一个好的库可以存档(JAVA)。

最佳答案

在这个答案中,我做出以下假设:

  • 您的列表包含 40 独特颜色。
  • 每种颜色包含24信息位(8 红色位,8 绿色位,8 蓝色位)。
  • 12 -颜色解决方案应该是人类感知颜色的最佳组合。

因为您的目标是确定哪个 12颜色(提供的 40 中的)最接近图像中的所有颜色,您可以使用以下算法:

  1. 迭代所有40您输入的颜色。
  2. 对于每种颜色,迭代输入图像中的所有像素。
  3. 对于当前像素,计算其颜色与当前颜色之间的差异。
    • 这可能是算法中最困难的部分,因为我们需要一个函数来返回两种颜色之间的差异
    • 我认为实现此目的最有效的方法是首先将 RGB 颜色转换为 XYZ颜色空间,然后将 XYZ 颜色转换为 CIELAB色彩空间。
    • 然后我们可以使用this formula计算两种 CIELAB 颜色之间的差异。

enter image description here

  • 将颜色映射到相应颜色的差异总和。
  • 排序 Map<Integer, Double>按值(升序);解决办法是第一个12键。
  • 下面是实现此目的的 Java 代码:

    import javax.imageio.ImageIO;
    import java.awt.Color;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.UncheckedIOException;
    import java.nio.file.Paths;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;

    public class ColorDifference {

    public static void main(String[] args) {
    ConcurrentMap<Color, Double> colorDifferenceMap = new ConcurrentHashMap<>();
    ExecutorService executorService = Executors.newFixedThreadPool(
    Runtime.getRuntime().availableProcessors());

    BufferedImage inputImage;

    try {
    // Read in the input image
    inputImage = ImageIO.read(Paths.get("input.png").toFile());
    } catch (IOException e) {
    throw new UncheckedIOException("Unable to read input image!", e);
    }

    generateInfiniteColors().distinct().limit(40).forEach(color -> {
    executorService.execute(() -> {
    CIELab cieLabColor = CIELab.from(color);

    double sum = 0d;

    for (int y = 0; y < inputImage.getHeight(); y++) {
    for (int x = 0; x < inputImage.getWidth(); x++) {
    Color pixelColor = new Color(inputImage.getRGB(x, y));
    CIELab pixelCIELabColor = CIELab.from(pixelColor);
    sum += cieLabColor.difference(pixelCIELabColor);
    }
    }

    colorDifferenceMap.put(color, sum);
    });
    });

    executorService.shutdown();

    try {
    executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    // The 12 solution colors are held in this list
    List<Color> colorSolutions = colorDifferenceMap.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByValue())
    .limit(12)
    .map(Map.Entry::getKey)
    .collect(Collectors.toList());

    colorSolutions.forEach(System.out::println);

    for (int y = 0; y < inputImage.getHeight(); y++) {
    for (int x = 0; x < inputImage.getWidth(); x++) {
    CIELab cieLabColor = CIELab.from(new Color(inputImage.getRGB(x, y)));

    int finalX = x;
    int finalY = y;

    colorSolutions.stream()
    .min(Comparator.comparingDouble(color ->
    cieLabColor.difference(CIELab.from(color))))
    .ifPresent(closestColor ->
    inputImage.setRGB(finalX, finalY, closestColor.getRGB()));
    }
    }

    try {
    ImageIO.write(inputImage, "png", new File("output.png"));
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    // Inspiration taken from https://stackoverflow.com/a/20032024/7294647
    private static Stream<Color> generateInfiniteColors() {
    return Stream.generate(() ->
    new Color(ThreadLocalRandom.current().nextInt(0x1000000)));
    }

    static class CIELab {

    private final double L, a, b;

    public CIELab(double L, double a, double b) {
    this.L = L;
    this.a = a;
    this.b = b;
    }

    public double difference(CIELab cieLab) {
    return Math.sqrt(Math.pow(cieLab.L - L, 2) + Math.pow(cieLab.a - a, 2) +
    Math.pow(cieLab.b - b, 2));
    }

    public static CIELab from(Color color) {
    int sR = color.getRed();
    int sG = color.getGreen();
    int sB = color.getBlue();

    // Convert Standard-RGB to XYZ (http://www.easyrgb.com/en/math.php)
    double var_R = ( sR / 255d );
    double var_G = ( sG / 255d );
    double var_B = ( sB / 255d );

    if ( var_R > 0.04045 ) var_R = Math.pow( ( var_R + 0.055 ) / 1.055, 2.4 );
    else var_R = var_R / 12.92;
    if ( var_G > 0.04045 ) var_G = Math.pow( ( var_G + 0.055 ) / 1.055, 2.4 );
    else var_G = var_G / 12.92;
    if ( var_B > 0.04045 ) var_B = Math.pow( ( var_B + 0.055 ) / 1.055, 2.4 );
    else var_B = var_B / 12.92;

    var_R = var_R * 100;
    var_G = var_G * 100;
    var_B = var_B * 100;

    double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
    double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
    double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;

    // Convert XYZ to CIELAB (http://www.easyrgb.com/en/math.php
    double var_X = X / 96.422;
    double var_Y = Y / 100.000;
    double var_Z = Z / 82.521;

    if ( var_X > 0.008856 ) var_X = Math.pow( var_X, 1D / 3D );
    else var_X = ( 7.787 * var_X ) + ( 16D / 116 );
    if ( var_Y > 0.008856 ) var_Y = Math.pow( var_Y, 1D / 3D );
    else var_Y = ( 7.787 * var_Y ) + ( 16D / 116 );
    if ( var_Z > 0.008856 ) var_Z = Math.pow( var_Z, 1D / 3D );
    else var_Z = ( 7.787 * var_Z ) + ( 16D / 116 );

    double L = ( 116 * var_Y ) - 16;
    double a = 500 * ( var_X - var_Y );
    double b = 200 * ( var_Y - var_Z );

    return new CIELab(L, a, b);
    }
    }
    }
    <小时/>

    为了测试代码,我获取了以下 473x356图片来自您在评论中发送的链接:

    enter image description here

    在代码中可以看到所有40颜色是随机生成的,这是程序的输出:

    java.awt.Color[r=182,g=176,b=32]
    java.awt.Color[r=201,g=201,b=55]
    java.awt.Color[r=142,g=149,b=6]
    java.awt.Color[r=223,g=158,b=36]
    java.awt.Color[r=226,g=143,b=63]
    java.awt.Color[r=144,g=83,b=39]
    java.awt.Color[r=119,g=153,b=117]
    java.awt.Color[r=50,g=136,b=72]
    java.awt.Color[r=244,g=118,b=59]
    java.awt.Color[r=69,g=79,b=52]
    java.awt.Color[r=149,g=78,b=76]
    java.awt.Color[r=62,g=190,b=28]
    <小时/>

    一种可能的输出图像如下:

    enter image description here

    <小时/>

    上面的代码可以在 Java 8+ 上运行,对于上面发布的图像,它在一秒钟内完成。

    关于java - 在JAVA中将图像减少到固定数量的给定颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60721123/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com