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

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

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

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

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

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

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

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

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

         可以

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

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


1338044865_9608.png

源图像如下:

1338044918_5375.png

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


1338044952_5758.png

二值化处理参见以前的文章 - 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;
  }
 
}

大米着色处理很简单,只是简单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;  
//                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;
  
  // 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

相关文章
|
6月前
|
机器学习/深度学习 监控 算法
yolov5无人机视频检测与计数系统(创新点和代码)
yolov5无人机视频检测与计数系统(创新点和代码)
|
18天前
|
存储 传感器 小程序
跳绳计数小程序开发系统
首先,我们需要明确跳绳计数小程序的核心功能:为用户提供跳绳计数的便捷方式。这意味着小程序需要能够准确地记录用户的跳绳次数,并为用户提供清晰、直观的计数展示。
|
17天前
|
机器学习/深度学习 数据采集 消息中间件
使用Python实现智能火山活动监测模型
使用Python实现智能火山活动监测模型
32 1
|
5月前
|
算法 计算机视觉
图像处理之应用篇-大米计数续
图像处理之应用篇-大米计数续
29 1
|
6月前
|
算法 数据挖掘
R语言面板数据回归:含时间固定效应混合模型分析交通死亡率、酒驾法和啤酒税
R语言面板数据回归:含时间固定效应混合模型分析交通死亡率、酒驾法和啤酒税
|
6月前
|
固态存储 数据安全/隐私保护 计算机视觉
C++医院影像科PACS源码:三维重建、检查预约、胶片打印、图像处理、测量分析等
C++医院影像科PACS源码:三维重建、检查预约、胶片打印、图像处理、测量分析等
108 0
|
计算机视觉 C++ Python
Python OpenCV高速公路道路汽车车辆侦测检测识别统计数量
Python OpenCV高速公路道路汽车车辆侦测检测识别统计数量
265 0
Python OpenCV高速公路道路汽车车辆侦测检测识别统计数量
|
机器学习/深度学习 数据采集 人工智能
【时序预测】之水质净化厂工艺控制-曝气量预测(上)
【时序预测】之水质净化厂工艺控制-曝气量预测(上)
265 1
【时序预测】之水质净化厂工艺控制-曝气量预测(上)
|
算法 数据格式
DeepSORT算法实现车辆和行人跟踪计数和是否道路违规检测(毕业设计)
DeepSORT算法实现车辆和行人跟踪计数和是否道路违规检测(毕业设计)
|
智慧交通
智慧交通day02-车流量检测实现05:小车匀加速案例
智慧交通day02-车流量检测实现05:小车匀加速案例
100 0