一位网友给我发了几张灰度图像,说是他们单位的工业相机拍摄的,画质非常的清楚,他们
单位是农业科研单位,特别想知道种子的数量,他想知道的是每次工业相机拍摄种子图片中
有多少颗粒种子,想到了用图像处理的办法解决他们的问题,看了他给我照片,以大米种子
为例。实现了一个简单的算法流程,可以得到种子的数目。
大致算法分为以下三个步骤:
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<Integer, Integer> labelMap = new HashMap<Integer, Integer>();
- for(int d=0; d<outData.length; d++) {
- if(outData[d] != 0) {
- if(labelMap.containsKey(outData[d])) {
- Integer count = labelMap.get(outData[d]);
- count+=1;
- labelMap.put(outData[d], count);
- } else {
- labelMap.put(outData[d], 1);
- }
- }
- }
- // try to find the max connected component
- Integer[] keys = labelMap.keySet().toArray(new Integer[0]);
- Arrays.sort(keys);
- int threshold = 10;
- ArrayList<Integer> listKeys = new ArrayList<Integer>();
- 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<height; row++) {
- int ta = 0, tr = 0, tg = 0, tb = 0;
- for(int col=0; col<width; col++) {
- index = row * width + col;
- ta = (inPixels[index] >> 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<Integer> listKeys) {
- for(Integer key : listKeys) {
- if(key == i) {
- return false;
- }
- }
- return true;
- }
- }
- 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<height; row++) {
- int ta = 255, tr = 0, tg = 0, tb = 0;
- for(int col=0; col<width; col++) {
- index = row * width + col;
- // ta = (inPixels[index] >> 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;
- }
- }
- 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...
- }
- }
- }