Distance Transformation for image processing
Overview
Distance transformation is a common means of processing and operating binary images. It is often used in skeleton extraction and image narrowing. Distance
The conversion result is a grayscale image similar to the input image, but the grayscale value only appears in the foreground area. And
The larger the pixel gray value, the farther away from the background edge.
Basic Ideas
Based on different distance measurement methods, distance transformation involves several different methods. Assume that the pixel p1 (x1, Y1 ),
P2 (X2, Y2) distance calculation methods are common:
1. Euclidean distance, distance =
2. Manhattan distance (city block distance), the formula is as follows: distance = | x2-x1 | + | y2-y1 |
3. chessboard distance, formula: distance = max (| x2-x1 |, | y2-y1 |)
Once the Distance Measurement Formula is selected, it can be used in the distance transformation of the binary image. One of the most common distance Transformations
The algorithm is implemented through continuous corrosion operations. The Stop Condition of corrosion operations is that all foreground pixels are completely
Corrosion. In this way, according to the order of corrosion, we can obtain the foreground pixel to the foreground center skeleton pixel.
Distance. Set the gray value to a different value based on the distance of each pixel. This completes the distance between two-value images.
Transform.
Note:
The selection of corrosive operation struct affects the effect of distance transformation. In this example, the 3*3 matrix is used. There are many fast
Distance Transformation Algorithm. If you are interested, you can study it yourself.
Running result:
Key code analysis:
Initializes a binary image, reads pixels, and obtains foreground edge pixels and background edge pixels.
public DistanceTransform(float scaleValue, float offsetValue, BufferedImage src){this.scaleValue = scaleValue;this.offsetValue = offsetValue;this.inputImage = src;this.width = src.getWidth();this.height = src.getHeight(); int[] inPixels = new int[width*height]; getRGB( src, 0, 0, width, height, inPixels ); int index = 0; pixels2D = new int[height][width]; // row, column greyLevel = new int[height][width]; for(int row=0; row < height; row++) { for(int col=0; col<width; col++) { index = row * width + col; int grayValue = (inPixels[index] >> 16) & 0xff; pixels2D[row][col] = grayValue; greyLevel[row][col] = 0; } } generateForegroundEdge(); generateBackgroundEdgeFromForegroundEdge(); }
The actual distance conversion code is as follows:
@Overridepublic BufferedImage filter(BufferedImage src, BufferedImage dest) {// calculate the distance here!!int index = 1; while (foregroundEdgePixels.size() > 0) { distanceSingleIteration(index); ++index; } // loop the each pixel and assign the color value according to distance valuefor (int row = 0; row < inputImage.getHeight(); row++) { for (int col = 0; col < inputImage.getWidth(); col++) { if(greyLevel[row][col] > 0) { int colorValue = (int)Math.round(greyLevel[row][col] * scaleValue + offsetValue); colorValue = colorValue > 255 ? 255 : ((colorValue < 0) ? 0 : colorValue); this.pixels2D[row][col] = colorValue; } }}// build the result pixel data at here !!! if ( dest == null ) dest = createCompatibleDestImage(inputImage, null ); index = 0; int[] outPixels = new int[width*height]; for(int row=0; row
The code for generating foreground edge pixels and background edge pixels is as follows:
private void generateForegroundEdge() { foregroundEdgePixels.clear(); for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) if (this.pixels2D[row][col] == foreground) { Point localPoint = new Point(col, row); for (int k = -1; k < 2; ++k) // 3*3 matrix for (int l = -1; l < 2; ++l) { if ((localPoint.x + l < 0) || (localPoint.x + l >= this.width) || (localPoint.y + k < 0) || (localPoint.y + k >= this.height) || (this.pixels2D[(localPoint.y + k)][(localPoint.x + l)] != background) || (this.foregroundEdgePixels.contains(localPoint))) continue; this.foregroundEdgePixels.add(localPoint); } } } private void generateBackgroundEdgeFromForegroundEdge() { this.backgroundEdgePixels.clear(); Iterator<Point> localIterator = this.foregroundEdgePixels.iterator(); while (localIterator.hasNext()) { Point localPoint1 = new Point((Point)localIterator.next()); for (int i = -1; i < 2; ++i) for (int j = -1; j < 2; ++j) if ((localPoint1.x + j >= 0) && (localPoint1.x + j < this.width) && (localPoint1.y + i >= 0) && (localPoint1.y + i < this.height)) { Point localPoint2 = new Point(localPoint1.x + j, localPoint1.y + i); if (this.pixels2D[localPoint2.y][localPoint2.x] == background) this.backgroundEdgePixels.add(localPoint2); } } }