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

来源:互联网 发布:windows打开dos快捷键 编辑:程序博客网 时间:2024/04/27 17:05

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

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

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

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

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

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

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;}@Overridepublic 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 statisticHashMap<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 componentInteger[] 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;}}
大米着色处理很简单,只是简单RGB固定着色,源码如下:

package com.gloomyfish.rice.analysis;import java.awt.image.BufferedImage;import com.gloomyfish.face.detection.AbstractBufferedImageOp;public class ColorfulRiceFilter extends AbstractBufferedImageOp {@Overridepublic 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;}}
测试程序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;// JButtonsprivate 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...");// buttonsbtnPanel.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();}@Overridepublic 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 datatry {if (!tracker.waitForID(1, 10000)) {System.out.println("Load error.");System.exit(1);}// end if} catch (InterruptedException ine) {ine.printStackTrace();System.exit(1);} // end catchBinaryFilter 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