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

简介: 图像处理之应用篇-大米计数续 背景介绍: 请看博客文章《图像处理之简单综合实例(大米计数)》 其实拍出来的照片更多的是含有大米颗粒相互接触,甚至于有点重叠的照片 要准确计算大米的颗粒数非常困难,通过图像形态学开闭操作,腐蚀等手 段尝试以后效果不是很好。

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

背景介绍:

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

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

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

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

方法。照相机图片:


算法思想:

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

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

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

数目的统计:

缺点:

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

二个样本数量。算法结果跟实际结果有一定的误差,但是误差在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);

 

程序时序图如下:


程序运行效果如下:


实际大米颗粒数目为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;
	}

}
转载文章请务必注明出处

目录
相关文章
Qt rcc静态资源编译及使用 QtInstallframework 打包成安装包后图标丢失问题
Qt rcc静态资源编译及使用 QtInstallframework 打包成安装包后图标丢失问题
191 0
|
机器学习/深度学习 人工智能 数据库
【表面缺陷检测】表面缺陷检测数据集汇总
本文收集整理了16个表面缺陷检测相关的数据集,并对每个数据集的特点进行了简单的介绍。
【表面缺陷检测】表面缺陷检测数据集汇总
|
人工智能 算法 自动驾驶
使用OpenCV实现Halcon算法(2)形状匹配开源项目,shape_based_matching
使用OpenCV实现Halcon算法(2)形状匹配开源项目,shape_based_matching
4872 0
使用OpenCV实现Halcon算法(2)形状匹配开源项目,shape_based_matching
|
11月前
|
编解码 UED
Qt侧边栏的动态切换:隐藏与显示技术详解
在现代用户界面设计中,侧边栏(Sidebar)是一个常见的组件,它为用户提供了导航和工具面板的功能。在某些应用场景下,我们可能需要动态地隐藏或显示侧边栏,以优化界面布局或提供更灵活的用户体验。本文将分享如何在Qt框架下实现侧边栏的隐藏与呈现,包括技术细节和代码示例。
978 3
|
12月前
|
机器学习/深度学习 监控 算法
基于计算机视觉(opencv)的运动计数(运动辅助)系统-源码+注释+报告
基于计算机视觉(opencv)的运动计数(运动辅助)系统-源码+注释+报告
306 3
|
机器学习/深度学习 人工智能 BI
解析Python解释器:从基础到应用的完整指南
解析Python解释器:从基础到应用的完整指南
282 2
【qt】Tool Box组件
【qt】Tool Box组件
221 0
|
12月前
|
C# Windows
C#实现指南:将文件夹与exe合并为一个exe
C#实现指南:将文件夹与exe合并为一个exe
693 9
|
算法 API 计算机视觉
基于opencv的大米计数统计(详细处理流程+代码)
基于opencv的大米计数统计(详细处理流程+代码)
基于opencv的大米计数统计(详细处理流程+代码)
|
存储 vr&ar 图形学
如何导出带有材质的GLB模型?
GLB格式作为一种优秀的3D文件格式,具备小巧高效、支持材质纹理、跨平台兼容等优点,在从设计到展示的整个工作流程中起到重要作用,促进了3D内容的创建、分享和交互。
626 0