如何找到一张图片的主色调?

15

你是指图像中最常用的色调还是平均颜色? - Alexis Dufrenoy
7个回答

12

在Java中迭代每个像素并确定颜色

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;


public class ImageTester {


    public static void main(String args[]) throws Exception {
        File file = new File("C:\\Users\\Andrew\\Desktop\\myImage.gif");
        ImageInputStream is = ImageIO.createImageInputStream(file);
        Iterator iter = ImageIO.getImageReaders(is);

        if (!iter.hasNext())
        {
            System.out.println("Cannot load the specified file "+ file);
            System.exit(1);
        }
        ImageReader imageReader = (ImageReader)iter.next();
        imageReader.setInput(is);

        BufferedImage image = imageReader.read(0);

        int height = image.getHeight();
        int width = image.getWidth();

        Map m = new HashMap();
        for(int i=0; i < width ; i++)
        {
            for(int j=0; j < height ; j++)
            {
                int rgb = image.getRGB(i, j);
                int[] rgbArr = getRGBArr(rgb);                
                // Filter out grays....                
                if (!isGray(rgbArr)) {                
                        Integer counter = (Integer) m.get(rgb);   
                        if (counter == null)
                            counter = 0;
                        counter++;                                
                        m.put(rgb, counter);                
                }                
            }
        }        
        String colourHex = getMostCommonColour(m);
        System.out.println(colourHex);
    }


    public static String getMostCommonColour(Map map) {
        List list = new LinkedList(map.entrySet());
        Collections.sort(list, new Comparator() {
              public int compare(Object o1, Object o2) {
                return ((Comparable) ((Map.Entry) (o1)).getValue())
                  .compareTo(((Map.Entry) (o2)).getValue());
              }
        });    
        Map.Entry me = (Map.Entry )list.get(list.size()-1);
        int[] rgb= getRGBArr((Integer)me.getKey());
        return Integer.toHexString(rgb[0])+" "+Integer.toHexString(rgb[1])+" "+Integer.toHexString(rgb[2]);        
    }    

    public static int[] getRGBArr(int pixel) {
        int alpha = (pixel >> 24) & 0xff;
        int red = (pixel >> 16) & 0xff;
        int green = (pixel >> 8) & 0xff;
        int blue = (pixel) & 0xff;
        return new int[]{red,green,blue};

  }
    public static boolean isGray(int[] rgbArr) {
        int rgDiff = rgbArr[0] - rgbArr[1];
        int rbDiff = rgbArr[0] - rgbArr[2];
        // Filter out black, white and grays...... (tolerance within 10 pixels)
        int tolerance = 10;
        if (rgDiff > tolerance || rgDiff < -tolerance) 
            if (rbDiff > tolerance || rbDiff < -tolerance) { 
                return false;
            }                 
        return true;
    }
}

8
我刚发布了一个非常简单的算法,可以很容易地转换成Java。它称为color-finder,使用JavaScript工作。
这个线程中提出的解决方案可能会被图像中的一些空格字符扰乱,而我的算法确实尝试找到最突出的颜色,即使所有像素的颜色并不完全相同。 这里有一个实时演示
如果您觉得有用,请告诉我。

嗨@pieroxy,这看起来很有前途 - 但是对我来说将其翻译成Java并不那么简单 - 至少乍一看是这样的 :-) - Jan

4

3

使用纯Java,您只需迭代每个像素并计算每种颜色的包含次数即可...

伪代码:

Map<Color, Integer> color2counter;
for (x : width) {
   for (y : height) {
      color = image.getPixel(x, y)
      occurrences = color2counter.get(color)
      color2counter.put(color, occurrences + 1)
   }
}

2
我们可以使用Material 颜色工具库,它目前可用于 Java/Kotlin、C++、Dart 和 TypeScript。
这些颜色可能不一定是最常见的颜色,但是是适合Material 3 设计系统和轻色或暗色主题应用的主要颜色。
该库主要用于 Android 12 及以上版本的应用程序,也用于Material Design 网站本身,但我测试了一下并且获得了很好的结果。
要使用它,将您所需语言的代码从Material 颜色工具库存储库复制粘贴到您的项目中,然后您就可以提取主导颜色和配色方案。
这是 Java 和 Kotlin 的示例:
Java:
var MAX_DESIRED_COLOR_COUNT = 128;
var file = new File("my-image.jpg");
var image = ImageIO.read(file);
var pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
var colorFrequency = QuantizerCelebi.quantize(pixels, MAX_DESIRED_COLOR_COUNT);
var decentColors = Score.score(colorFrequency);
var desiredColor = decentColors.get(0);
// You can take the desiredColor or any other decentColors and forget the rest of code below


// Could also use Scheme.dark(desiredColor); to get colors suitable for dark themes
var colorScheme = Scheme.light(desiredColor);
System.out.println("Decent colors: " + decentColors);
System.out.println("Primary color (light theme): " + colorScheme.getPrimary());

Kotlin:
val MAX_DESIRED_COLOR_COUNT = 128
val file = File("my-image.jpg")
val image = ImageIO.read(file)
val pixels = image.getRGB(0, 0, image.width, image.height, null, 0, image.width)
val colorFrequency = QuantizerCelebi.quantize(pixels, MAX_DESIRED_COLOR_COUNT)
val decentColors = Score.score(colorFrequency)
val desiredColor = decentColors.first()
// You can take the desiredColor or any other decentColors and forget the rest of code below


// Could also use Scheme.dark(desiredColor) to get colors suitable for dark themes
val colorScheme = Scheme.light(desiredColor)
println("Decent colors: ${decentColors.joinToString { it.toHexString() }}")
println("Primary color (light theme): ${colorScheme.primary.toHexString()}")

fun Int.toHexString() = "#%06X".format(this and 0xFFFFFF)

在此处了解更多关于Material Design颜色系统和颜色角色的信息(例如上面代码片段中使用的colorScheme.primary)。

1

假设您使用的是加色方案,其中(0,0,0)为黑色,(255,255,255)为白色(如果我错了,请纠正我)。此外,如果您只想从RGB中找到主要颜色:

我有一个想法,任何人都可以仔细检查,即有3个变量,每个变量存储RGB值之一,并向图像中的每个像素添加相应的值,然后除以(255 * numOfPixels)以获得颜色比率。然后比较3个比率:.60表示红色更占优势,绿色为.5。

这只是一个想法,可能需要调整...


我使用了Jamagick,其中颜色以RGB类型使用,但是用这种方式,我只能找到图像的平均颜色。但是我有一个想法,我可以裁剪中间的图像,这样我就可以找到那部分的平均颜色,它将再次给出几乎相同的颜色。我只能找到主导颜色 :/ - Erçin Akçay
@ErçinAkçay 如果你想裁剪图片,肯定希望边框上有主要颜色。例如,假设你有一个被白色包围的大正方形。你想检测白色的颜色来裁剪它,而不是正方形的颜色。 - Peter Lawrey

1
在另一种方式中,我们可以使用Color Thief库完成此工作。更多信息可以在这里这里找到。
感谢@svenwoltmann和@lokeshdhakar。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接