【问题标题】:Find perimeter of the image binary object查找图像二进制对象的周长
【发布时间】:2016-07-14 05:39:56
【问题描述】:

我正在尝试查找二进制对象的周长。

考虑下图

[ 0 0 0 0 1 1 0 ]
[ 0 0 1 0 0 0 0 ]
[ 0 1 1 0 1 1 0 ]
[ 0 1 1 0 0 1 0 ]
[ 0 1 1 1 0 0 0 ]
[ 0 1 0 0 1 1 0 ]
[ 0 0 0 0 1 1 0 ]

带标签的图像看起来像这样

[ 0 0 0 0 1 1 0 ]
[ 0 0 2 0 0 0 0 ]
[ 0 2 2 0 3 3 0 ]
[ 0 2 2 0 0 3 0 ]
[ 0 2 2 0 0 0 0 ]
[ 0 2 0 0 4 4 0 ]
[ 0 0 0 0 4 4 0 ]

我还收集了数组列表中的每个对象像素

例如,对于 4 个标记的对象,列表将是

{ (5,4), (5,5) , (6,4), (6,5) }

区域只是每个对象像素数组的大小,但是我如何找到周长,我应该再次遍历整个图像查找单元格的邻居检查它是否是对象的角像素或者有更简单的方法来做这只是基于坐标。

请建议最容易找到周界的方法,任何代码示例都将受到高度赞赏

【问题讨论】:

    标签: java image algorithm image-processing pattern-recognition


    【解决方案1】:

    尝试对图像进行广度优先搜索,(或者遍历点列表),并标记与另一个像素相邻的每个像素不属于同一组

    我不清楚你想要的周长是请求对象外边缘上的每个像素,还是对象边界的每个像素。我暂时假设前者。

    设置图片:

    这就是你如何去做的。首先,将图像设置为二维数组,每个像素都标有组号:

    [ 0 0 0 0 1 1 0 ]
    [ 0 0 2 0 0 0 0 ]
    [ 0 2 2 0 3 3 0 ]
    [ 0 2 2 0 0 3 0 ]
    [ 0 2 2 0 0 0 0 ]
    [ 0 2 0 0 4 4 0 ]
    [ 0 0 0 0 4 4 0 ]
    

    加载它的一个好方法是使用Scanner 对象逐个获取每个点:

    List<Point> points = new ArrayList<>();
    Scanner scanner = new Scanner( /* whatever your input source is */ );
    String pointRegex = "\\(\\d,\\d\\)"; //looks for something like "(#,#)"
    while(!scanner.hasNext(pointRegex)){
        String pointText = scanner.next(pointRegex); //For example, "(5,4)"
        Point point = getPointFromText(pointText); //turns a string into a point
        points.add(point);
    }
    

    注意Scanner.next(String pattern) 的使用。这个方法将返回下一个看起来像该模式的String。 (如果您想了解更多有关其工作原理的信息,请阅读正则表达式。)

    现在开始填充网格:

    boolean[][] binaryImage = new boolean[width][height];
    for(Point p : points){ //Iterate through each Point inside our List of Point objects
        binaryImage[p.getX()][p.getY()] = true;
    }
    

    这会将由我们的Point 对象集合“points”表示的对象放入booleans 的网格中。我们只需要担心这个对象,所以我们不需要加载任何其他对象。现在要找出周长上的点。

    递归方法:

    boolean[][] visitedBefore = new boolean[width][height];
    boolean[][] isOnPerimeter = new boolean[width][height];
    int[] deltaX = {-1,  0,  1, -1, 1, -1, 0, 1},
          deltaY = {-1, -1, -1,  0, 0,  1, 1, 1};
    Queue<Point> searchNext = new LinkedList<>();
    searchNext.add(points.get(0)); //Just need one point to get going
    while(!searchNext.isEmpty()){
        Point p = searchNext.remove(); //take what's waiting at the front of the queue
        if(visitedBefore[p.getX()][p.getY()]){
            continue; //already check this spot!
        }
    
        //mark that we've been here
        visited[p.getX()][p.getY()] = true;
    
        //look at all of this Point's neighbors
        for(int i = 0 ; i < deltaX.length ; i++){
            int newX = p.getX() + deltaX[i];
            int newY = p.getY() + deltaY[i];
    
            //make sure this isn't out of bounds
            if(newX < 0 || newX >= width || newY<0 || newY>=height){
                isOnPerimeter[p.getX()][p.getY()] = true; //if you decide bordering the edge of the image counts as being on the perimeter
                continue;
            }
    
            //check if this new point we're considering isn't part of the image
            if( binaryImage[p.getX()][p.getY()] != binaryImage[newX][newY] ){
                //if it isn't, then this Point p must be on the perimeter
                isOnPerimeter[p.getX()][p.getY()] = true;
            } else {
                /* otherwise, this new point we're considering is part of the
                 * same object, and could be part of the perimeter. */
                searchNext.add(new Point(newX, newY));
            }
        }
    }
    

    现在您有一个网格,周长上的每个点都标记为true。如果您需要这些作为列表,那么挑选这些要点很容易:

    List<Point> perimeter = new ArrayList<Point>();
    for(int x = 0 ; x < isOnPerimeter.length ; x++)
        for(int y = 0 ; y < isOnPerimeter[x].length ; y++)
            perimeter.add( new Point(x,y) );
    

    迭代法:

    这与上面的非常相似,但直接将周长点放入列表中。

    int[] deltaX = {-1,  0,  1, -1, 1, -1, 0, 1},
          deltaY = {-1, -1, -1,  0, 0,  1, 1, 1};
    outer: for(Point p : points){
        inner: for(int i = 0 ; i < deltaX.length ; i++){
            int newX = p.getX() + deltaX[i];
            int newY = p.getY() + deltaY[i];
            //check if this new point we're considering is outside the image
            if(newX < 0 || newX >= width || newY<0 || newY>=height){
                perimeter.add(p); //if you decide bordering the edge of the image counts as being on the perimeter
                continue outer;
            }
    
            //check if this new point we're considering isn't part of the image
            if( binaryImage[p.getX()][p.getY()] != binaryImage[newX][newY] ){
                //if it isn't, then this Point p must be on the perimeter
                perimeter.add(p);
                continue outer;
            }
        }
    }
    

    注意标签outer:inner:。当我们说continue outer; 时,这让我们可以选择跳过哪个 for 循环。

    给你!这应该可以帮助您以二进制图像或列表的形式获取任何对象的周长。

    【讨论】:

      猜你喜欢
      • 2014-06-12
      • 2016-04-22
      • 2013-09-02
      • 1970-01-01
      • 1970-01-01
      • 2014-05-02
      • 1970-01-01
      • 2017-07-19
      • 1970-01-01
      相关资源
      最近更新 更多