图像处理之简单综合实例(大米计数)

一位网友给我发了几张灰度图像,说是他们单位的工业相机拍摄的,画质非常的清楚,他们

单位是农业科研单位,特别想知道种子的数量,他想知道的是每次工业相机拍摄种子图片中

有多少颗粒种子,想到了用图像处理的办法解决他们的问题,看了他给我照片,以大米种子

为例。实现了一个简单的算法流程,可以得到种子的数目。

大致算法分为以下三个步骤:

1.      将灰度图像二值化,二值化方法可以参考以前的文章,求取像素平均值,灰度直方图都

          可以

2.      去掉二值化以后的图像中干扰噪声。

3.      得到种子数目,用彩色标记出来。


源图像如下:


程序处理中间结果及最终效果如下:


二值化处理参见以前的文章 - http://blog.csdn.net/jia20003/article/details/7392325

大米计数与噪声块消去算法基于连通组件标记算法,源代码如下:

package com.gloomyfish.rice.analysis;  import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap;  import com.gloomyfish.face.detection.AbstractBufferedImageOp; import com.gloomyfish.face.detection.FastConnectedComponentLabelAlg;  public class FindRiceFilter extends AbstractBufferedImageOp { 	 	private int sumRice; 	 	public int getSumRice() { 		return this.sumRice; 	}  	@Override 	public BufferedImage filter(BufferedImage src, BufferedImage dest) { 		int width = src.getWidth();         int height = src.getHeight();          if ( dest == null )             dest = createCompatibleDestImage( src, null );          int[] inPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB(src, 0, 0, width, height, inPixels );          		FastConnectedComponentLabelAlg fccAlg = new FastConnectedComponentLabelAlg(); 		fccAlg.setBgColor(0); 		int[] outData = fccAlg.doLabel(inPixels, width, height); 		// labels statistic 		HashMap labelMap = new HashMap(); 		for(int d=0; d listKeys = new ArrayList(); 		for(Integer key : keys) { 			if(labelMap.get(key) <=threshold){ 				listKeys.add(key); 			} 			System.out.println( "Number of " + key + " = " + labelMap.get(key)); 		} 		sumRice = keys.length - listKeys.size(); 		         // calculate means of pixel           int index = 0;             for(int row=0; row> 24) & 0xff;                   tr = (inPixels[index] >> 16) & 0xff;                   tg = (inPixels[index] >> 8) & 0xff;                   tb = inPixels[index] & 0xff;                 if(outData[index] != 0 && validRice(outData[index], listKeys)) {                 	tr = tg = tb = 255;                 } else {                 	tr = tg = tb = 0;                 }                 outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;             }         }         setRGB( dest, 0, 0, width, height, outPixels );         return dest; 	}  	private boolean validRice(int i, ArrayList listKeys) { 		for(Integer key : listKeys) { 			if(key == i) { 				return false; 			} 		} 		return true; 	}  } 
大米着色处理很简单,只是简单RGB固定着色,源码如下:

package com.gloomyfish.rice.analysis;  import java.awt.image.BufferedImage;  import com.gloomyfish.face.detection.AbstractBufferedImageOp;  public class ColorfulRiceFilter 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 );          int[] inPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB(src, 0, 0, width, height, inPixels );                  int index = 0, srcrgb;         for(int row=0; row> 24) & 0xff;   //                tr = (inPixels[index] >> 16) & 0xff;   //                tg = (inPixels[index] >> 8) & 0xff;   //                tb = inPixels[index] & 0xff;               	srcrgb = inPixels[index] & 0x000000ff;             	if(srcrgb > 0 && row < 140) {             		tr = 0;             		tg = 255;             		tb = 0;             	} else if(srcrgb > 0 && row >= 140 && row <=280) {             		tr = 0;             		tg = 0;             		tb = 255;             	} else if(srcrgb > 0 && row >=280) {             		tr = 255;             		tg = 0;             		tb = 0;             	}             	else {             		tr = tg = tb = 0;             	}             	outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;             }         }         setRGB( dest, 0, 0, width, height, outPixels );         return dest; 	} } 
测试程序UI代码如下:

package com.gloomyfish.rice.analysis;  import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;  import javax.imageio.ImageIO; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JPanel;  public class MainFrame extends JComponent implements ActionListener { 	/** 	 *  	 */ 	private static final long serialVersionUID = 1518574788794973574L; 	public final static String BROWSE_CMD = "Browse..."; 	public final static String NOISE_CMD = "Remove Noise"; 	public final static String FUN_CMD = "Colorful Rice"; 	 	private BufferedImage rawImg; 	private BufferedImage resultImage; 	private MediaTracker tracker; 	private Dimension mySize; 	 	// JButtons 	private JButton browseBtn; 	private JButton noiseBtn; 	private JButton colorfulBtn;  	// rice number.... 	private int riceNum = -1; 	 	 	public MainFrame() { 		JPanel btnPanel = new JPanel(); 		btnPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); 		browseBtn = new JButton("Browse..."); 		noiseBtn = new JButton("Remove Noise"); 		colorfulBtn = new JButton("Colorful Rice"); 		 		browseBtn.setToolTipText("Please select image file..."); 		noiseBtn.setToolTipText("find connected region and draw red rectangle"); 		colorfulBtn.setToolTipText("Remove the minor noise region pixels..."); 		 		// buttons 		btnPanel.add(browseBtn); 		btnPanel.add(noiseBtn); 		btnPanel.add(colorfulBtn); 		 		// setup listener... 		browseBtn.addActionListener(this); 		noiseBtn.addActionListener(this); 		colorfulBtn.addActionListener(this); 		 		browseBtn.setEnabled(true); 		noiseBtn.setEnabled(true); 		colorfulBtn.setEnabled(true); 		 //		minX = minY =  10000; //		maxX = maxY = -1; 		 		mySize = new Dimension(500, 300); 		JFrame demoUI = new JFrame("Rice Detection Demo"); 		demoUI.getContentPane().setLayout(new BorderLayout()); 		demoUI.getContentPane().add(this, BorderLayout.CENTER); 		demoUI.getContentPane().add(btnPanel, BorderLayout.SOUTH); 		demoUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 		demoUI.pack(); 		demoUI.setVisible(true); 	} 	 	public void paint(Graphics g) { 		Graphics2D g2 = (Graphics2D) g; 		if(rawImg != null) { 			Image scaledImage = rawImg.getScaledInstance(200, 200, Image.SCALE_FAST); 			g2.drawImage(scaledImage, 0, 0, 200, 200, null); 		} 		if(resultImage != null) { 			Image scaledImage = resultImage.getScaledInstance(200, 200, Image.SCALE_FAST); 			g2.drawImage(scaledImage, 210, 0, 200, 200, null); 		} 		 		g2.setPaint(Color.RED); 		if(riceNum > 0) { 			g2.drawString("Number of Rice : " + riceNum, 100, 300); 		} else { 			g2.drawString("Number of Rice : Unknown", 100, 300); 		} 	} 	public Dimension getPreferredSize() { 		return mySize; 	} 	 	public Dimension getMinimumSize() { 		return mySize; 	} 	 	public Dimension getMaximumSize() { 		return mySize; 	} 	 	public static void main(String[] args) { 		new MainFrame(); 	} 	 	@Override 	public void actionPerformed(ActionEvent e) { 		if(BROWSE_CMD.equals(e.getActionCommand())) { 			JFileChooser chooser = new JFileChooser(); 			chooser.showOpenDialog(null); 			File f = chooser.getSelectedFile(); 			BufferedImage bImage = null; 			if(f == null) return; 			try { 				bImage = ImageIO.read(f); 				 			} catch (IOException e1) { 				e1.printStackTrace(); 			} 			 			tracker = new MediaTracker(this); 			tracker.addImage(bImage, 1); 			 			// blocked 10 seconds to load the image data 			try { 				if (!tracker.waitForID(1, 10000)) { 					System.out.println("Load error."); 					System.exit(1); 				}// end if 			} catch (InterruptedException ine) { 				ine.printStackTrace(); 				System.exit(1); 			} // end catch 			BinaryFilter bfilter = new BinaryFilter(); 			rawImg = bfilter.filter(bImage, null); 			repaint(); 		} else if(NOISE_CMD.equals(e.getActionCommand())) { 			FindRiceFilter frFilter = new FindRiceFilter(); 			resultImage = frFilter.filter(rawImg, null); 			riceNum = frFilter.getSumRice(); 			repaint(); 		} else if(FUN_CMD.equals(e.getActionCommand())) { 			ColorfulRiceFilter cFilter = new ColorfulRiceFilter(); 			resultImage = cFilter.filter(resultImage, null); 			repaint(); 		} else { 			// do nothing... 		} 		 	} } 
关于连通组件标记算法,我实现一个优化过的快速版本,可以参见

http://blog.csdn.net/jia20003/article/details/7596443