二值圖像中大於0的像素個數就是圖像的面積
坐標x乘以每個像素點的值 求和,除以圖像中像素值的和得到質心
注意圖像的寬高范圍從1-width,1-height。
http://blog.csdn.net/jia20003/article/details/17596645
圖像處理之計算二值連通區域的質心
一:幾何距(Geometric Moments)知識與質心尋找原理
1. Image Moments是圖像處理中非常有用的算法,可以用來計算區域圖像
的質心,方向等幾何特性,同時Mpq的高階具有旋轉不變性,可以用來
實現圖像比較分類,正是因為Moments有這些特性,很多手繪油畫效果
也會基於該算法來模擬實現。它的數學表達為:
它的低階M00,M01, M10可以用來計算質心,中心化以后M11,M02,M20
可以用來計算區域的方向/角度
2. 什么是質心
就是通過該點,區域達到一種質量上的平衡狀態,可能物理學上講的比較多,簡單點的
說就是規則幾何物體的中心,不規則的可以通過掛繩子的方法來尋找。
二:算法流程
1. 輸入圖像轉換為二值圖像
2. 通過連通組件標記算法找到所有的連通區域,並分別標記
3. 對每個連通區域運用計算幾何距算法得到質心
4. 用不同顏色繪制連通區域與質心,輸出處理后圖像
三:算法效果
左邊為原圖, 右邊藍色為連通組件標記算法處理以后結果,白色點為質心
四:關鍵代碼解析
1. 計算幾何距算法代碼
doublem00 = moments(pixels, width, height, 0, 0);
doublexCr = moments(pixels, width, height, 1, 0) / m00;// row
doubleyCr = moments(pixels, width, height, 0, 1) / m00;// column
return new double[]{xCr, yCr};
2. 連通組件標記算法代碼參見這里:
http://blog.csdn.net/jia20003/article/details/7628371
五:程序源代碼
- package com.gloomyfish.image.moments;
- import java.awt.image.BufferedImage;
- import com.gloomyfish.filter.study.AbstractBufferedImageOp;
- import com.gloomyfish.rice.analysis.FastConnectedComponentLabelAlg;
- // Geometric Moments Computing
- // low-order moments - calculate the center point
- // second-order moments - get angle size
- // projection -
- public class GeometricMomentsFilter extends AbstractBufferedImageOp {
- @Override
- public BufferedImage filter(BufferedImage src, BufferedImage dest) {
- int width = src.getWidth();
- int height = src.getHeight();
- if ( dest == null )
- dest = createCompatibleDestImage( src, null );
- // first step - make it as binary image output pixel
- int[] inPixels = new int[width*height];
- int[] outPixels = new int[width*height];
- getRGB( src, 0, 0, width, height, inPixels );
- int index = 0;
- for(int row=0; row<height; row++) {
- int tr = 0;
- for(int col=0; col<width; col++) {
- index = row * width + col;
- tr = (inPixels[index] >> 16) & 0xff;
- if(tr > 127)
- {
- outPixels[index] = 1;
- }
- else
- {
- outPixels[index] = 0;
- }
- }
- }
- // second step, connected component labeling algorithm
- FastConnectedComponentLabelAlg ccLabelAlg = new FastConnectedComponentLabelAlg();
- ccLabelAlg.setBgColor(0);
- int[] labels = ccLabelAlg.doLabel(outPixels, width, height);
- int max = 0;
- for(int i=0; i<labels.length; i++)
- {
- if(max < labels[i])
- {
- System.out.println("Label Index = " + labels[i]);
- max = labels[i];
- }
- }
- // third step, calculate center point of each region area(connected component)
- int[] input = new int[labels.length];
- GeometricMomentsAlg momentsAlg = new GeometricMomentsAlg();
- momentsAlg.setBACKGROUND(0);
- double[][] labelCenterPos = new double[max][2];
- for(int i=1; i<=max; i++)
- {
- for(int p=0; p<input.length; p++)
- {
- if(labels[p] == i)
- {
- input[p] = labels[p];
- }
- else
- {
- input[p] = 0;
- }
- }
- labelCenterPos[i-1] = momentsAlg.getGeometricCenterCoordinate(input, width, height);
- }
- // render the each connected component center position
- for(int row=0; row<height; row++) {
- for(int col=0; col<width; col++) {
- index = row * width + col;
- if(labels[index] == 0)
- {
- outPixels[index] = (255 << 24) | (0 << 16) | (0 << 8) | 0; // make it as black for background
- }
- else
- {
- outPixels[index] = (255 << 24) | (0 << 16) | (0 << 8) | 100; // make it as blue for each region area
- }
- }
- }
- // make it as white color for each center position
- for(int i=0; i<max; i++)
- {
- int crow = (int)labelCenterPos[i][0];
- int ccol = (int)labelCenterPos[i][1];
- index = crow * width + ccol;
- outPixels[index] = (255 << 24) | (255 << 16) | (255 << 8) | 255;
- }
- setRGB( dest, 0, 0, width, height, outPixels );
- return dest;
- }
- }
package com.gloomyfish.image.moments; import java.awt.image.BufferedImage; import com.gloomyfish.filter.study.AbstractBufferedImageOp; import com.gloomyfish.rice.analysis.FastConnectedComponentLabelAlg; // Geometric Moments Computing // low-order moments - calculate the center point // second-order moments - get angle size // projection - public class GeometricMomentsFilter extends AbstractBufferedImageOp { @Override public BufferedImage filter(BufferedImage src, BufferedImage dest) { int width = src.getWidth(); int height = src.getHeight(); if ( dest == null ) dest = createCompatibleDestImage( src, null ); // first step - make it as binary image output pixel int[] inPixels = new int[width*height]; int[] outPixels = new int[width*height]; getRGB( src, 0, 0, width, height, inPixels ); int index = 0; for(int row=0; row<height; row++) { int tr = 0; for(int col=0; col<width; col++) { index = row * width + col; tr = (inPixels[index] >> 16) & 0xff; if(tr > 127) { outPixels[index] = 1; } else { outPixels[index] = 0; } } } // second step, connected component labeling algorithm FastConnectedComponentLabelAlg ccLabelAlg = new FastConnectedComponentLabelAlg(); ccLabelAlg.setBgColor(0); int[] labels = ccLabelAlg.doLabel(outPixels, width, height); int max = 0; for(int i=0; i<labels.length; i++) { if(max < labels[i]) { System.out.println("Label Index = " + labels[i]); max = labels[i]; } } // third step, calculate center point of each region area(connected component) int[] input = new int[labels.length]; GeometricMomentsAlg momentsAlg = new GeometricMomentsAlg(); momentsAlg.setBACKGROUND(0); double[][] labelCenterPos = new double[max][2]; for(int i=1; i<=max; i++) { for(int p=0; p<input.length; p++) { if(labels[p] == i) { input[p] = labels[p]; } else { input[p] = 0; } } labelCenterPos[i-1] = momentsAlg.getGeometricCenterCoordinate(input, width, height); } // render the each connected component center position for(int row=0; row<height; row++) { for(int col=0; col<width; col++) { index = row * width + col; if(labels[index] == 0) { outPixels[index] = (255 << 24) | (0 << 16) | (0 << 8) | 0; // make it as black for background } else { outPixels[index] = (255 << 24) | (0 << 16) | (0 << 8) | 100; // make it as blue for each region area } } } // make it as white color for each center position for(int i=0; i<max; i++) { int crow = (int)labelCenterPos[i][0]; int ccol = (int)labelCenterPos[i][1]; index = crow * width + ccol; outPixels[index] = (255 << 24) | (255 << 16) | (255 << 8) | 255; } setRGB( dest, 0, 0, width, height, outPixels ); return dest; } }
Moment算法代碼:
- package com.gloomyfish.image.moments;
- public class GeometricMomentsAlg {
- private int BACKGROUND = 0; // background color
- private int labelIndex = 1;
- public GeometricMomentsAlg()
- {
- System.out.println("Geometric Moments Algorithm Initialziation...");
- }
- public int getLabelIndex() {
- return labelIndex;
- }
- public void setLabelIndex(int labelIndex) {
- this.labelIndex = labelIndex;
- }
- public int getBACKGROUND() {
- return BACKGROUND;
- }
- public void setBACKGROUND(int bACKGROUND) {
- BACKGROUND = bACKGROUND;
- }
- public double[] getGeometricCenterCoordinate(int[] pixels, int width, int height)
- {
- double m00 = moments(pixels, width, height, 0, 0);
- double xCr = moments(pixels, width, height, 1, 0) / m00; // row
- double yCr = moments(pixels, width, height, 0, 1) / m00; // column
- return new double[]{xCr, yCr};
- }
- public double moments(int[] pixels, int width, int height, int p, int q)
- {
- double mpq = 0.0;
- int index = 0;
- for(int row=0; row<height; row++)
- {
- for(int col=0; col<width; col++)
- {
- index = row * width + col;
- if(pixels[index] == BACKGROUND) continue;
- mpq += Math.pow(row, p) * Math.pow(col, q);
- }
- }
- return mpq;
- }
- public double centralMoments(int[] pixel, int width, int height, int p, int q)
- {
- double m00 = moments(pixel, width, height, 0, 0);
- double xCr = moments(pixel, width, height, 1, 0) / m00;
- double yCr = moments(pixel, width, height, 0, 1) / m00;
- double cMpq = 0.0;
- int index = 0;
- for(int row=0; row<height; row++)
- {
- for(int col=0; col<width; col++)
- {
- index = row * width + col;
- if(pixel[index] == BACKGROUND) continue;
- cMpq += Math.pow(row - xCr, p) * Math.pow(col - yCr, q);
- }
- }
- return cMpq;
- }
- public double normalCentralMoments(int[] pixel, int width, int height, int p, int q)
- {
- double m00 = moments(pixel, width, height, 0, 0);
- double normal = Math.pow(m00, ((double)(p+q+2))/2.0d);
- return centralMoments(pixel, width, height, p, q)/normal;
- }
- }