如何用java将图像分成两部分

时间:2010-01-27 17:30:52

标签: java graphics image-processing image-manipulation

我想知道是否存在基于某些功能分割图像的“智能”方式。

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

以下是图片“看起来”的样子:

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

产生的分裂将是这样的:

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

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

  1. 将图像从一侧扫描到另一侧。
  2. 确定blob的边缘。
  3. 取两条内边缘之间的距离。
  4. 将图像分割为内部距离的中间位置。
  5. 将两张图片另存为单独的文件。
  6. 如果我将图像宽度标准化会很好,所以我的所有图像在保存时都具有统一的宽度。

    我没有图像处理经验,所以我不知道这是一种有效的方法。我目前正在使用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"));
    }
    

4 个答案:

答案 0 :(得分:5)

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

此列可能不会成为blob之间差距的中心。您可以从此列进行另一次向外搜索,向左移动以查找所有类似的列,然后向右移动。

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

col avg:

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

根据两个斑点之间的空白空白(像素值),您可以将阈值设置得非常低。如果有一些噪音,它必须要高一点。

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

答案 1 :(得分:1)

我不知道edge detection algorithm不需要迭代像素,因此您目前的方法可能是最佳的。根据其他因素,您可以使用ImageJ,其中包含analytical plugins的大量集合。​​

附录:鉴于偏好避免外部依赖,BufferedImage是一个不错的选择。识别边缘后,getSubimage()方法很方便。您可以在卷积中有效地使用其中一种Raster getPixels()方法。 ImageIO可以写出结果。

答案 2 :(得分:1)

我认为除了扫描每一行之外没有任何理由可以做任何其他事情,当你获得白色&gt;黑色&gt;白色转换(不需要扫描整行!)时停止。 如果您可以猜测blob的位置,那么可能能够通过在图像中间选择一个起点然后从那里向左和向右搜索来稍微改进它。 但我非常怀疑这是值得的。

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

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

答案 3 :(得分:1)

blob之间的差距是否重要?如果您不需要平衡空白区域,则只需要在blob之间找到垂直白线就可以减少工作量。检查中心垂直线是否只有白色像素。如果中间线具有黑色像素,则向左和向右扫描仅具有白色像素的第一行。要检查两个斑点都在中心一侧的情况,请扫描水平线以获得黑白黑色间隔。如果所选垂直线位于由黑色间隔围绕的白色间隔内,则您将知道图像分割的每一侧至少有一个斑点。

如果未通过这些检查,则需要扫描额外的线条,但对于所有格式良好的图像,斑点位于图像左右两半的中心位置,此方法仅需扫描两行。对于边缘情况图像,该方法对于其他图像可能花费更长时间,或甚至中断。这会破坏这个例子:

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

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