一位網友給我發了幾張灰度圖像,說是他們單位的工業相機拍攝的,畫質很是的清楚,他們java
單位是農業科研單位,特別想知道種子的數量,他想知道的是每次工業相機拍攝種子圖片中算法
有多少顆粒種子,想到了用圖像處理的辦法解決他們的問題,看了他給我照片,以大米種子ide
爲例。實現了一個簡單的算法流程,能夠獲得種子的數目。測試
大體算法分爲如下三個步驟:this
1. 將灰度圖像二值化,二值化方法能夠參考之前的文章,求取像素平均值,灰度直方圖都url
能夠spa
2. 去掉二值化之後的圖像中干擾噪聲。.net
3. 獲得種子數目,用彩色標記出來。component

源圖像以下:orm

程序處理中間結果及最終效果以下:

二值化處理參見之前的文章 - 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);
-
- 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);
- }
- }
- }
-
-
- 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();
-
-
- 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 {
-
- @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;
-
-
-
-
- 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;
-
-
- private JButton browseBtn;
- private JButton noiseBtn;
- private JButton colorfulBtn;
-
-
- 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...");
-
-
- btnPanel.add(browseBtn);
- btnPanel.add(noiseBtn);
- btnPanel.add(colorfulBtn);
-
-
- browseBtn.addActionListener(this);
- noiseBtn.addActionListener(this);
- colorfulBtn.addActionListener(this);
-
- browseBtn.setEnabled(true);
- noiseBtn.setEnabled(true);
- colorfulBtn.setEnabled(true);
-
-
-
-
- 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);
-
-
- try {
- if (!tracker.waitForID(1, 10000)) {
- System.out.println("Load error.");
- System.exit(1);
- }
- } catch (InterruptedException ine) {
- ine.printStackTrace();
- System.exit(1);
- }
- 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 {
-
- }
-
- }
- }