图像处理之应用篇-大米计数续

简介: 图像处理之应用篇-大米计数续

图像处理之应用篇-大米计数续


背景介绍:


请看博客文章《图像处理之简单综合实例(大米计数)》


其实拍出来的照片更多的是含有大米颗粒相互接触,甚至于有点重叠的照片


要准确计算大米的颗粒数非常困难,通过图像形态学开闭操作,腐蚀等手


段尝试以后效果不是很好。最终发现一种简单明了但是有微小误差的计数


方法。照相机图片:

1342538469_7927.png


算法思想:


主要是利用连通区域发现算法,发现所有连通区域,使用二分法,截取较小


部分的连通区域集合,求取平均连通区域面积,根据此平均连通区域面积,


作为单个大米大小,从而求取出粘连部分的大米颗粒数,完成对整个大米


数目的统计:


缺点:


平均连通区域面积的计算受制于两个因素,一个是最小连通区域集合的选取算法,


二个样本数量。算法结果跟实际结果有一定的误差,但是误差在1%左右。



程序算法代码详解


将输入图像转换为黑白二值图像,求得连通区域的算法代码如下:


src = super.filter(src, null);


getRGB(src, 0, 0, width,height, inPixels );


FastConnectedComponentLabelAlgfccAlg = new FastConnectedComponentLabelAlg();


fccAlg.setBgColor(0);


int[] outData = fccAlg.doLabel(inPixels, width, height);



获取平均大米颗粒连通区域的代码如下:


Integer[] values =labelMap.values().toArray(new Integer[0]);


Arrays.sort(values);


int minRiceNum = values.length/4;


float sum = 0;


for(int v= offset; v<minRiceNum + offset; v++) {


sum += values[v].intValue();


}


float minMeans = sum / (float)minRiceNum;


System.out.println(" minMeans = " + minMeans);



程序时序图如下:

1342538494_5263.png


程序运行效果如下:


1342538600_4197.png

实际大米颗粒数目为202,正确率为99%


完成大米数目统计的源代码如下(其它相关代码见以前的图像处理系列文章):

public class FindRiceFilter extends BinaryFilter {
  
  private int sumRice;
  private int offset = 10;
  
  public int getSumRice() {
    return this.sumRice;
  }
  
  public void setOffset(int pos) {
    this.offset = pos;
  }
 
  @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];
        src = super.filter(src, null);
        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);
        }
      }
    }
    
    Integer[] values = labelMap.values().toArray(new Integer[0]);
    Arrays.sort(values);
    int minRiceNum = values.length/4;
    float sum = 0;
    for(int v= offset; v<minRiceNum + offset; v++) {
      sum += values[v].intValue();
    }
    float minMeans = sum / (float)minRiceNum;
    System.out.println(" minMeans = " + minMeans);
    
    // 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);
      } else {
        float xx = labelMap.get(key);
        float intPart = (float)Math.floor(xx / minMeans + 0.5f);
        sumRice += intPart;
      }
    }
    System.out.println( "Number of rice  = " + sumRice);
    // 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;
  }
 
}

转载文章请务必注明出处

相关文章
|
机器学习/深度学习 物联网 开发者
秒级生图,SDXL-turbo、LCM-SDXL魔搭社区最佳实践
最近一个月,快速生图成为文生图领域的热点,其中比较典型的两种方式的代表模型分别为SDXL-turbo 和 LCM-SDXL。
|
Linux
【杂七杂八】简单粗暴处理linux下.nsf导致的目录无法删除
【杂七杂八】简单粗暴处理linux下.nsf导致的目录无法删除
979 0
|
机器学习/深度学习 人工智能 自然语言处理
《鸿蒙Next的AI声音修复功能:多类型音频处理的卓越表现》
鸿蒙Next的AI声音修复功能针对不同类型的音频文件提供卓越的处理效果。对于语音类音频,它能显著提升发音清晰度和可懂度,改善交流质量;音乐类音频则通过优化音质和增强细节,还原原始情感;环境音类音频中,AI有效去除背景噪音,提取纯净自然声音;对特殊格式如MIDI,先转换为实际音频再进行优化。总体而言,该功能根据不同音频特点进行针对性修复,全面提升用户体验。
1095 62
|
算法 API 计算机视觉
基于opencv的大米计数统计(详细处理流程+代码)
基于opencv的大米计数统计(详细处理流程+代码)
基于opencv的大米计数统计(详细处理流程+代码)
|
Windows
.Net Framework 0x800b0109 -已处理证书链,但是在不受信任提供程序信任的根证书中终止。
.Net Framework 0x800b0109 -已处理证书链,但是在不受信任提供程序信任的根证书中终止。
2452 0
.Net Framework 0x800b0109 -已处理证书链,但是在不受信任提供程序信任的根证书中终止。
|
Java Spring
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException(Spring循环依赖问题)
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException(Spring循环依赖问题)
635 0
|
存储 运维 JavaScript
SaaS云HIS平台源码 采用云部署模式,部署一套可支持多家医院共同使用
通过基于SaaS模式的医院管理系统,院内的医护人员、患者可快速建立互联协同。不仅如此,通过SaaS模式提供的解决方案,医院机构可实现远程医疗,从而为不同地区的患者带来优质医疗资源,促进医疗公平。
565 5
|
JavaScript 前端开发 算法
【从0配置JAVA项目相关环境2】node.js + 前端 从配置到运行
【从0配置JAVA项目相关环境2】node.js + 前端 从配置到运行
468 0
|
NoSQL Linux C++
百度搜索:蓝易云【Linux常用gdb命令详解。】
使用gdb调试程序时,可以通过设置断点、执行程序、观察变量值等操作来逐步调试代码,定位问题并进行修复。熟练掌握gdb的使用可以提高程序调试的效率和准确性。
187 0
|
存储 算法
带你读《图解算法小抄》二十二、贪心算法(1)
带你读《图解算法小抄》二十二、贪心算法(1)
393 0