什么是使用BufferedImage和Java在7000张图像中查找纯色的最有效方法?

huangapple go评论73阅读模式
英文:

What is the most efficient approach for finding solid colors in 7000 images using BufferedImage and Java?

问题

我有一个方法,它计算并返回图像中具有最常见颜色的像素百分比 - 我有超过7000张图像需要迭代,以获取每个jpg的百分比。

下面的方法迭代6000个jpg以返回每个jpg中最常见颜色的百分比。这需要超过6-7分钟来迭代所有的jpg。

public static double calculatePercentage(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int totalPixels = width * height;
        Map<Integer, Integer> colorCounts = new HashMap<>();

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int color = image.getRGB(x, y);
                colorCounts.put(color, colorCounts.getOrDefault(color, 0) + 1);
            }
        }

        int mostCommonColorCount = colorCounts.values().stream().max(Integer::compareTo).orElse(0);
        double percentage = (double) mostCommonColorCount / totalPixels;

        return percentage;
    }

是否有一种高效的方法来检查纯色,并使这个程序在7000张图像上迭代得更快。基本上,我正在尝试识别那些具有纯色(如黑色、蓝色或白色)的jpg。

英文:

I have a method which calculates and returns the percentage of pixels in an image that have the most common color - I have over 7000 images which i need to iterate over to get the percentage for each jpg.

The method below iterates over 6000 jpgs to return the percentage of most common color in each jpg. This takes over 6-7 minutes to iterate over all jpgs.

public static double calculatePercentage(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int totalPixels = width * height;
        Map&lt;Integer, Integer&gt; colorCounts = new HashMap&lt;&gt;();

        for (int y = 0; y &lt; height; y++) {
            for (int x = 0; x &lt; width; x++) {
                int color = image.getRGB(x, y);
                colorCounts.put(color, colorCounts.getOrDefault(color, 0) + 1);
            }
        }

        int mostCommonColorCount = colorCounts.values().stream().max(Integer::compareTo).orElse(0);
        double percentage = (double) mostCommonColorCount / totalPixels;

        return percentage;
    }

Is there an efficient way to check for solid colors and iterate this program faster over 7000 images. Basically I am trying to identify those jpgs which have plain solid colors (like a black, blue or white color).

答案1

得分: 1

以下是您要翻译的代码部分:

最有可能导致性能问题的图像加载速度很慢但对于计算步骤您可以尝试使用`AtomicInteger`而不是自动装箱的`Integer`,并使用并行的`IntStream`同时处理多行类似这样

public static double calculatePercentage(BufferedImage image) {
    int width = image.getWidth();
    int height = image.getHeight();
    int totalPixels = width * height;
    ConcurrentHashMap<Integer, AtomicInteger> colorCounts = new ConcurrentHashMap<>();
    IntStream.range(0, height).parallel().forEach(y -> {
        for (int x = 0; x < width; x++) {
            colorCounts.computeIfAbsent(image.getRGB(x, y), c -> new AtomicInteger()).incrementAndGet();
        }
    });
    int mostCommonColorCount = colorCounts.values().stream().mapToInt(AtomicInteger::get).max().orElse(0);
    double percentage = (double) mostCommonColorCount / totalPixels;

    return percentage;
}

您也可以尝试加载图像 - 您可能会发现在不同类型的存储之间,通过File->BufferedImageFile->byte[]->BufferedImage读取的速度不同(我注意到对于我的NAS驱动器上的照片,后者更快)。

英文:

Most likely speed of image load is a big cause of a performance issue, but for the calculation step you could experiment with counter as AtomicInteger over autoboxed Integer, and parallel Instream to handle multiple rows at same time. Something like:

public static double calculatePercentage(BufferedImage image) {
    int width = image.getWidth();
    int height = image.getHeight();
    int totalPixels = width * height;
    ConcurrentHashMap&lt;Integer, AtomicInteger&gt; colorCounts = new ConcurrentHashMap&lt;&gt;();
    IntStream.range(0, height).parallel().forEach(y -&gt; {
        for (int x = 0; x &lt; width; x++) {
            colorCounts.computeIfAbsent(image.getRGB(x, y), c -&gt; new AtomicInteger()).incrementAndGet();
        }
    });
    int mostCommonColorCount = colorCounts.values().stream().mapToInt(AtomicInteger::get).max().orElse(0);
    double percentage = (double) mostCommonColorCount / totalPixels;

    return percentage;
}

You should experiment with loading images too - you might find speed of reading File-&gt;BufferedImage vs File-&gt;byte[]-&gt;BufferedImage differs between different types of storage (I have noticed that the latter is faster for photos on my NAS drive).

答案2

得分: 0

将以下代码段翻译为中文:

"One thing you can do to reduce the duration is promote your local variables to fields.
This will create re-usability."

"要减少持续时间的一种方法是将您的局部变量提升为字段。
这将创建可重用性。"

"Alternately, you can create two threads, one to load the next image into memory, while the current calculation is running.
This will typically increase speed significantly."

"或者,您可以创建两个线程,一个用于在当前计算正在运行时将下一张图像加载到内存中。
这通常会显著提高速度。"

英文:

One thing you can do to reduce the duration is promote your local variables to fields.

This will create re-usability.

Map&lt;Integer, Integer&gt; colorCounts;
int width, height;
int totalPixels;

int mostCommonColorCount;
double percentage;

public void calculatePercentage(BufferedImage image) {
    width = image.getWidth();
    height = image.getHeight();
    totalPixels = width * height;

    colorCounts = new HashMap&lt;&gt;();

    for (int y = 0; y &lt; height; y++) {
        for (int x = 0; x &lt; width; x++) {
            int color = image.getRGB(x, y);
            colorCounts.put(color, colorCounts.getOrDefault(color, 0) + 1);
        }
    }

    mostCommonColorCount = 0;
    for (int count : colorCounts.values())
        if (count &gt; mostCommonColorCount) mostCommonColorCount = count;

    percentage = (double) mostCommonColorCount / totalPixels;
}

Example with one image, 1,920 x 1,280 JPEG (24-bit color) 170.97 kB.

Output

726 ms
0.044464518229166666

Alternately, you can create two threads, one to load the next image into memory, while the current calculation is running.
This will typically increase speed significantly.

答案3

得分: 0

方法image.getRGB非常慢,不建议用它来访问像素值。

如果你想更快地遍历所有像素值,可以使用getRaster(),但最快的方法是访问DataBuffer(它只是一个数组)。参见这个讨论这个关于如何使用RasterDataBuffer(后者强烈推荐)来访问像素值。

英文:

The method image.getRGB is very slow and not recommended to access the pixel values.

If you want to go through all the pixel values faster, use the getRaster(), but the fastest is to access the DataBuffer (which is just an array). See this discussion or this one about how to access pixel values using the Raster or the DataBuffer (latter highly recommended).

huangapple
  • 本文由 发表于 2023年5月25日 00:24:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76325610.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定