All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.alg.filter.binary.LinearContourLabelChang2004 Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.filter.binary;

import boofcv.alg.misc.ImageMiscOps;
import boofcv.struct.ConnectRule;
import boofcv.struct.image.GrayS32;
import boofcv.struct.image.GrayU8;
import georegression.struct.point.Point2D_I32;
import org.ddogleg.struct.FastQueue;

import java.util.ArrayList;
import java.util.List;

/**
 * 

* Finds objects in a binary image by tracing their contours. The output is labeled binary image, set of external * and internal contours for each object/blob. Blobs can be defined using a 4 or 8 connect rule. The algorithm * works by processing the image in a single pass. When a new object is encountered its contour is traced. Then * the inner pixels are labeled. If an internal contour is found it will also be traced. See [1] for all * the details. The original algorithm has been modified to use different connectivity rules. *

* *

* Output: Background pixels (0 in input image) are assigned a value of 0, Each blob is then assigned a unique * ID starting from 1 up to the number of blobs. *

* *

* Internally, the input binary image is copied into another image which will have a 1 pixel border of all zeros * around it. This ensures that boundary checks will not need to be done, speeding up the algorithm by about 25%. *

* *

* [1] Fu Chang and Chun-jen Chen and Chi-jen Lu, "A linear-time component-labeling algorithm using contour * tracing technique" Computer Vision and Image Understanding, 2004 *

* * @author Peter Abeles */ public class LinearContourLabelChang2004 { // traces edge pixels private ContourTracer tracer; // binary image with a border of zero. private GrayU8 border = new GrayU8(1,1); // predeclared/recycled data structures private FastQueue storagePoints = new FastQueue(Point2D_I32.class,true); private FastQueue> storageLists = new FastQueue>((Class)ArrayList.class,true); private FastQueue contours = new FastQueue(Contour.class,true); // internal book keeping variables private int x,y,indexIn,indexOut; /** * Configures the algorithm. * * @param rule Connectivity rule. 4 or 8 */ public LinearContourLabelChang2004( ConnectRule rule ) { tracer = new ContourTracer(rule); } /** * Processes the binary image to find the contour of and label blobs. * * @param binary Input binary image. Not modified. * @param labeled Output. Labeled image. Modified. */ public void process(GrayU8 binary , GrayS32 labeled ) { // initialize data structures // ensure that the image border pixels are filled with zero by enlarging the image if( border.width != binary.width+2 || border.height != binary.height+2) { border.reshape(binary.width + 2, binary.height + 2); ImageMiscOps.fillBorder(border, 0, 1); } border.subimage(1,1,border.width-1,border.height-1, null).setTo(binary); // labeled image must initially be filled with zeros ImageMiscOps.fill(labeled,0); binary = border; storagePoints.reset(); storageLists.reset(); contours.reset(); tracer.setInputs(binary,labeled,storagePoints); // Outside border is all zeros so it can be ignored for( y = 1; y < binary.height-1; y++ ) { indexIn = binary.startIndex + y*binary.stride+1; indexOut = labeled.startIndex + (y-1)*labeled.stride; for( x = 1; x < binary.width-1; x++ , indexIn++ , indexOut++) { int bit = binary.data[indexIn]; // white pixels are ignored if( !(bit == 1) ) continue; int label = labeled.data[indexOut]; // long startTraceTime = System.currentTimeMillis(); boolean handled = false; if( label == 0 && binary.data[indexIn - binary.stride ] != 1 ) { handleStep1(); handled = true; label = contours.size; } // could be an external and internal contour if( binary.data[indexIn + binary.stride ] == 0 ) { handleStep2(labeled, label); handled = true; } if( !handled ) { handleStep3(labeled); } } } } public FastQueue getContours() { return contours; } /** * Step 1: If the pixel is unlabeled and the pixel above is white, then it * must be an external contour of a newly encountered blob. */ private void handleStep1() { Contour c = contours.grow(); c.reset(); c.id = contours.size(); tracer.trace(contours.size(),x,y,true,c.external); } /** * Step 2: If the pixel below is unmarked and white then it must be an internal contour * Same behavior it the pixel in question has been labeled or not already */ private void handleStep2(GrayS32 labeled, int label) { // if the blob is not labeled and in this state it cannot be against the left side of the image if( label == 0 ) label = labeled.data[indexOut-1]; Contour c = contours.get(label-1); List inner = storageLists.grow(); inner.clear(); c.internal.add(inner); tracer.trace(label,x,y,false,inner); } /** * Step 3: Must not be part of the contour but an inner pixel and the pixel to the left must be * labeled */ private void handleStep3(GrayS32 labeled) { if( labeled.data[indexOut] == 0 ) labeled.data[indexOut] = labeled.data[indexOut-1]; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy