如何用java将一张图片分成两部分

发布于 2024-08-19 00:50:58 字数 2647 浏览 5 评论 0原文

我想知道是否有一种“智能”方法可以根据某些特征分割图像。

图像为 300x57,黑白(实际上是灰度,但大多数颜色不是黑色就是白色),它由两个由黑色空间分隔的主要特征(我们称之为斑点)组成,每个斑点的宽度和高度略有不同,斑点的位置也不同,斑点永远不会重叠!

这是图像“看起来”的样子:

-------------------------
----WWW---------WWWWW----
---WWWWWWW----WWWWWW-----
-----WWWW-------WWW------
-------------------------

生成的分割结果将如下所示:

------------     -------------
----WWW-----     ----WWWWW----
---WWWWWWW--     --WWWWWW-----
-----WWWW---     ----WWW------
------------     -------------

我计划分割图像所采取的步骤:

  1. 将图像从一侧扫描到另一侧。
  2. 确定斑点的边缘。
  3. 获取两个内边缘之间的距离。
  4. 在内部距离的中间分割图像。
  5. 将两个图像另存为单独的文件。

如果我标准化图像宽度就好了,这样我的所有图像在保存时都有统一的宽度。

我没有图像处理方面的经验,所以我不知道什么是有效的方法来做到这一点。我目前正在使用 BufferedImage,获取宽度/高度,迭代每个像素等。对于我的问题没有错误的解决方案,但我正在寻找一种更有效的解决方案(更少的代码+更快)。我也一直在研究 java.awt.Graphics...

如果我能得到一些更有效的方法来完成此任务的想法,我将不胜感激。我想坚持使用 Java 的内置库,那么在这种情况下 BufferedImage 或 Graphics2D 是最有效的选择吗?

编辑: 这是阅读建议后的代码:

public void splitAndSaveImage( BufferedImage image ) throws IOException
{
    // Process image ------------------------------------------         
    int height = image.getHeight();
    int width = image.getWidth();
    boolean edgeDetected = false;
    double averageColor = 0;
    int threshold = -10;
    int rightEdge = 0;
    int leftEdge = 0;
    int middle = 0;

    // Scan the image and determine the edges of the blobs.
    for(int w = 0; w < width; ++w)
    {               
        for(int h = 0; h < height; ++h)
        {
            averageColor += image.getRGB(w, h);
        }

        averageColor = Math.round(averageColor/(double)height);

        if( averageColor /*!=-1*/< threshold && !edgeDetected )
        {
            // Detected the beginning of the right blob
            edgeDetected = true;
            rightEdge = w;
        }else if( averageColor >= threshold && edgeDetected )
        {
            // Detected the end of the left blob
            edgeDetected = false;
            leftEdge = leftEdge==0? w:leftEdge;
        }

        averageColor = 0;
    }

    // Split the image at the middle of the inside distance.
    middle = (leftEdge + rightEdge)/2;

    // Crop the image
    BufferedImage leftImage = image.getSubimage(0, 0, middle, height);

    BufferedImage rightImage = image.getSubimage(middle, 0, (width-middle), height);

    // Save the image
    // Save to file -------------------------------------------
    ImageIO.write(leftImage, "jpeg", new File("leftImage.jpeg"));

    ImageIO.write(rightImage, "jpeg", new File("rightImage.jpeg"));
}

I'm wondering if there is a "smart" way of splitting an image based on certain features.

The images are 300x57, black and white (actually grayscale, but most colors are either black or white), it is comprised of two main features (let's call them blobs) separated by black space, each blob slightly varies in width and height, the position of the blobs also varies, the blobs NEVER overlap!

Here is what an image "looks" like:

-------------------------
----WWW---------WWWWW----
---WWWWWWW----WWWWWW-----
-----WWWW-------WWW------
-------------------------

The resulting split would be something like this:

------------     -------------
----WWW-----     ----WWWWW----
---WWWWWWW--     --WWWWWW-----
-----WWWW---     ----WWW------
------------     -------------

Steps I plan to take in order to split the image:

  1. Scan the image from one side to the other.
  2. Determine the edges of the blobs.
  3. Take the distance between the two inside edges.
  4. Split the image at the middle of the inside distance.
  5. Save the two images as separate files.

It would be nice if I normalize the image widths, so all of my images have a uniform width when they're saved.

I have no experience in image manipulation, so I don't know what's an efficient way to do this. I'm currently using a BufferedImage, getting the width/height, iterating over each pixel, etc. There is no wrong solution for my problem, but I'm looking for a more efficient one (less code + faster). I've also been looking into java.awt.Graphics...

I would appreciate if I get some ideas for more efficient ways to do this task. I want to stick with Java's built-in libraries, so is BufferedImage or Graphics2D the most efficient thing to use in this case?

EDIT:
Here is the code after reading the suggestions:

public void splitAndSaveImage( BufferedImage image ) throws IOException
{
    // Process image ------------------------------------------         
    int height = image.getHeight();
    int width = image.getWidth();
    boolean edgeDetected = false;
    double averageColor = 0;
    int threshold = -10;
    int rightEdge = 0;
    int leftEdge = 0;
    int middle = 0;

    // Scan the image and determine the edges of the blobs.
    for(int w = 0; w < width; ++w)
    {               
        for(int h = 0; h < height; ++h)
        {
            averageColor += image.getRGB(w, h);
        }

        averageColor = Math.round(averageColor/(double)height);

        if( averageColor /*!=-1*/< threshold && !edgeDetected )
        {
            // Detected the beginning of the right blob
            edgeDetected = true;
            rightEdge = w;
        }else if( averageColor >= threshold && edgeDetected )
        {
            // Detected the end of the left blob
            edgeDetected = false;
            leftEdge = leftEdge==0? w:leftEdge;
        }

        averageColor = 0;
    }

    // Split the image at the middle of the inside distance.
    middle = (leftEdge + rightEdge)/2;

    // Crop the image
    BufferedImage leftImage = image.getSubimage(0, 0, middle, height);

    BufferedImage rightImage = image.getSubimage(middle, 0, (width-middle), height);

    // Save the image
    // Save to file -------------------------------------------
    ImageIO.write(leftImage, "jpeg", new File("leftImage.jpeg"));

    ImageIO.write(rightImage, "jpeg", new File("rightImage.jpeg"));
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

就是爱搞怪 2024-08-26 00:50:58

执行此操作的一种简单方法是将每列(向下)中的像素值相加,以创建平均值的单个数组(与输入图像的宽度相同)。从数组的中间开始,搜索最小值。这将是您可以分割图像的列。

此列可能不会是斑点之间间隙的中心。您可以从此列进行另一次向外搜索,首先向左查找所有相似的列,然后向右搜索。

-------------------------
----WWW---------WWWWW----
---WWWWWWW----WWWWWW-----
-----WWWW-------WWW------
-------------------------

col avg:

---wwWWwww-----wWWWWww---

根据两个斑点之间的空间(像素值方面)的空白程度,您可以将阈值设置得相当低。如果有一些噪音,就必须高一点。

找到正确的阈值可能是一件苦差事,除非您可以通过算法确定它。

A simple way to do this is to sum the pixel values in each column (going down) to create a single array (the same width as your input image) of average values. Starting in the middle of the array, search for the minimum value. This will be the column where you can split the image.

This column probably won't be the center of the gap between your blobs. You can do another outward search from this column, going left first to find all similar columns, and then going right.

-------------------------
----WWW---------WWWWW----
---WWWWWWW----WWWWWW-----
-----WWWW-------WWW------
-------------------------

col avg:

---wwWWwww-----wWWWWww---

Depending on how blank the space is (pixel value wise) between the two blobs, you can set your threshold value pretty low. If there is some noise, it will have to be a little higher.

Finding the right threshold value can be a chore, unless you can determine it algorithmically.

疑心病 2024-08-26 00:50:58

我不知道边缘检测 算法不需要迭代像素,因此您当前的方法可能是最佳的。根据其他因素,您也许可以利用 ImageJ,它拥有广泛的分析插件

附录:考虑到避免外部依赖的偏好, BufferedImage 是一个不错的选择。一旦确定了边缘,getSubimage() 方法就很方便了。您也许可以使用 < 之一code>Raster getPixels() 方法在卷积中有效。 ImageIO可以写出结果。

I'm not aware of an edge detection algorithm that doesn't require iterating through the pixels, so your present approach may be optimal. Depending on other factors, you may be able to leverage ImageJ, which has an extensive collection of analytical plugins.

Addendum: Given a preference for avoiding external dependencies, BufferedImage is a good choice. Once you identify the edges, the getSubimage() method is convenient. You may be able to use one of the Raster getPixels() methods effectively in the convolution. ImageIO can write the results.

生生漫 2024-08-26 00:50:58

斑点之间的间隙重要吗?如果您不需要平衡空白,则只需在斑点之间找到一条垂直白线就需要更少的工作。检查中心垂直线是否只有白色像素。如果中间的线有黑色像素,则左右扫描第一条只有白色像素的线。要检查两个斑点都位于中心一侧的情况,请扫描水平线以查找黑-白-黑间隔。如果选定的垂直线位于由黑色间隔包围的白色间隔内,您就会知道图像分割的每一侧至少有一个斑点。

如果这些检查失败,则需要扫描额外的行,但对于所有形状良好的图像(其中斑点集中在图像的右半部分和左半部分),此方法将只需要两次行扫描。对于其他图像,此方法可能需要更长的时间,甚至对于边缘情况图像会中断。对于这个例子来说,这将被打破:

-------------------------
----WWW----WWWWWWWWWW----
---WWWWWWW----WWWWWW-----
-----WWWWWWWW---WWW------
-------------------------

但问题似乎表明这种情况是不可能的。如果此图像分割背后的原因需要处理每个图像,则您将需要一种后备方法。如果可以拒绝边缘情况,则不需要后备方法。一旦扫描发现图像超出可接受的范围,您可以停止检查图像。例如,如果在图像的中心三分之一处找不到垂直的全白线,您可能可以拒绝该图像。或者您可以使用此方法作为优化,仅在两行上运行此检查以查找并分割形状良好的图像,然后将形状不良的图像传递给更彻底的算法。

Does the gap between blobs matter? If you don't need to balance the white space, less work would be needed to just find a vertical white line between blobs. Check if the center vertical line has only white pixels. If the middle line has a black pixel, scan left and right for the first line that has only white pixels. To check for situations where both blobs are to one side of center, scan a horizontal line for black-white-black intervals. If the selected vertical line is within a white interval surrounded by black intervals, you'll know there's at least one blob on each side of the image split.

Failing these checks would require scanning additional lines, but for all well formed images, where the blobs are centered in the right and left halves of the image, this method will take only two line scans. This method may take longer for other images, or even break, for edge case images. This would break for this example:

-------------------------
----WWW----WWWWWWWWWW----
---WWWWWWW----WWWWWW-----
-----WWWWWWWW---WWW------
-------------------------

But the question seems to indicate this situation is impossible. If the reason behind this image splitting requires processing every image, you'll need a fall back method. You wouldn't need a fall back method if the edge cases can be rejected. Once the scanning finds that the image falls outside of acceptable ranges, you can stop checking the image. For example, if a vertical all white line can't be found in the center third of the image, you may be able to reject the image. Or you can just use this method as an optimization, running this check on just two lines to find and split the well formed images, then passing the poorly formed images to a more thorough algorithm.

这样的小城市 2024-08-26 00:50:58

我认为除了扫描每一行并在获得白色->黑色->白色过渡时停止(不需要扫描整行!)之外,没有任何理由做任何事情。
如果您可以对斑点的位置做出任何猜测,您可能可以通过在图像中间选择一个起点,然后从那里左右搜索来对其进行一些改进。
但我严重怀疑这是否值得付出努力。

也无需首先在图像上运行边缘检测算法。只需扫描线条即可!

编辑:伯纳先生指出,这不适用于凹面物体。

I don't think there is any reason to do anything other than scanning each line and stop when you have gotten a white->black->white transition (no need to scan the entire line!).
If you can make any guess about the position of the blobs you might be able to refine it a little by picking a starting point in the middle of the image and then searching left and right from there.
But I seriously doubt it would be worth the effort.

There is also no need to first run an edge detection algorithm on the image. Just scan the lines!

EDIT: Mr. Berna pointed out that this will not work with concave objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文