图像处理之积分图应用三(基于NCC快速相似度匹配算法)

简介: 图像处理之积分图应用三(基于NCC快速相似度匹配算法)基于Normalized cross correlation(NCC)用来比较两幅图像的相似程度已经是一个常见的图像处理手段。在工业生产环节检测、监控领域对对象检测与识别均有应用。

图像处理之积分图应用三(基于NCC快速相似度匹配算法)

基于Normalized cross correlation(NCC)用来比较两幅图像的相似程度已经是一个常见的图像处理手段。在工业生产环节检测、监控领域对对象检测与识别均有应用。NCC算法可以有效降低光照对图像比较结果的影响。而且NCC最终结果在0到1之间,所以特别容易量化比较结果,只要给出一个阈值就可以判断结果的好与坏。传统的NCC比较方法比较耗时,虽然可以通过调整窗口大小和每次检测的步长矩形部分优化,但是对工业生产检测然后不能达到实时需求,通过积分图像实现预计算,比较模板图像与生产出电子版之间的细微差异,可以帮助企业提高产品质量,减少次品出厂率,把控质量。

一:NCC相关的数学知识

什么是NCC - (normalized cross correlation)归一化的交叉相关性,是数学上统计两组数据之间是否有关系的判断方法,貌似搞大数据分析比较流行相关性分析和计算。正常的计算公式如下:


mxn表示窗口大小,这样的计算复杂度就为O(m x n x M x N)。从上面公式就可以看出其中均值和平方和可以通过积分图预计算得到,对于模板和目标图像大小一致的应用场景来说

NCC的计算公式可以表示为如下:


其中根据积分图像可以提前计算出任意窗口大小和与平方和,这样就对


上述两个计算实现了窗口半径无关的常量时间计算,唯一缺少的是下面计算公式


通过积分图像建立起来窗口下面的待检测图像与模板图像的和与平方和以及他们的交叉乘积五个积分图索引之后,这样就完成了整个预计算生成。依靠索引表查找计算结果,NCC就可以实现线性时间的复杂度计算,而且时间消耗近似常量跟窗口半径大小无关,完全可以满足实时对象检测工业环境工作条件。

二:算法步骤

1. 预计算模板图像和目标图像的积分图

2. 根据输入的窗口半径大小使用积分图完成NCC计算

3. 根据阈值得到匹配或者不匹配区域。

4. 输出结果

为了减小计算量,我们可以要把输入的图像转换为灰度图像,在灰度图像的基础上完成整个NCC计算检测。我们这个给出的基于RGB图像的NCC计算完整代码,读者可以在此基础上修改实现单通道图像检测。

三: 运行结果:

输入的模板图像与待检测图像,左边是模板图像,右边是待检测图像,左上角有明显污点。图像显示如下:

  

输入待检测图像与模板比较以及检测计算出NCC的图像显示如下:


其中左侧是待检测图像,上面有黑色污点,右侧输出的非黑色区域表明,程序已经发现此区域与标准模板不同,越白的区域表示周围跟模板相同位置反差越大,越是可疑的污染点,这样就可以得到准确定位,最终带检测图像绘制最可疑红色矩形窗口区域

四:相关代码实现

1. 计算两张图像每个像素交叉乘积的积分图代码如下:

	public void caculateXYSum(byte[] x, byte[] y, int width, int height) {
		if(x.length != y.length)
			return;
		xysum = new float[width*height];
		this.width = width;
		this.height = height;
		// rows
		int px = 0, py = 0;
		int offset = 0, uprow=0, leftcol=0;
		float sp2=0, sp3=0, sp4=0;
		for(int row=0; row<height; row++ ) {
			offset = row*width;
			uprow = row-1;
			for(int col=0; col<width; col++) {
				leftcol=col-1;
				px=x[offset]&0xff;
				py=y[offset]&0xff;
				int p1 = px*py;
				// 计算平方查找表
				sp2=(leftcol<0) ? 0:xysum[offset-1]; // p(x-1, y)
				sp3=(uprow<0) ? 0:xysum[offset-width]; // p(x, y-1);
				sp4=(uprow<0||leftcol<0) ? 0:xysum[offset-width-1]; // p(x-1, y-1);
				xysum[offset]=p1+sp2+sp3-sp4;
				offset++;
			}
		}
	}
获取任意窗口大小的交叉乘积的代码如下:

	public float getXYBlockSum(int x, int y, int m, int n) {
		int swx = x + n/2;
		int swy = y + m/2;
		int nex = x-n/2-1;
		int ney = y-m/2-1;
		float sum1, sum2, sum3, sum4;
		if(swx >= width) {
			swx = width - 1;
		}
		if(swy >= height) {
			swy = height - 1;
		}
		if(nex < 0) {
			nex = 0;
		}
		if(ney < 0) {
			ney = 0;
		}
		sum1 = xysum[ney*width+nex];
		sum4 = xysum[swy*width+swx];
		sum2 = xysum[swy*width+nex];
		sum3 = xysum[ney*width+swx];
		return ((sum1 + sum4) - sum2 - sum3);
	}
其余部分的积分图计算,参见本人博客《图像处理之积分图算法》
2. 预计算建立积分图索引的代码如下:

		// per-calculate integral image for targetImage
		byte[] R = new byte[width * height];
		byte[] G = new byte[width * height];
		byte[] B = new byte[width * height];
		getRGB(width, height, pixels, R, G, B);
		IntIntegralImage rii = new IntIntegralImage();
		rii.setImage(R);
		rii.process(width, height);
		IntIntegralImage gii = new IntIntegralImage();
		gii.setImage(G);
		gii.process(width, height);
		IntIntegralImage bii = new IntIntegralImage();
		bii.setImage(B);
		bii.process(width, height);

		// setup the refer and target image index sum table
		rii.caculateXYSum(R, referRGB[0].getImage(), width, height);
		gii.caculateXYSum(G, referRGB[1].getImage(), width, height);
		bii.caculateXYSum(B, referRGB[2].getImage(), width, height);
		int size = (xr * 2 + 1) * (yr * 2 + 1);
3. 通过积分图查找实现快速NCC计算的代码如下:

		int r1=0, g1=0, b1=0;
		int r2=0, g2=0, b2=0;
		
		float sr1=0.0f, sg1=0.0f, sb1 = 0.0f;
		float sr2=0.0f, sg2=0.0f, sb2 = 0.0f;
		
		float xyr = 0.0f, xyg = 0.0f, xyb = 0.0f;
		
		for (int row = yr; row < height - yr; row++) {
			for (int col = xr; col < width - xr; col++) {
				
				r1 = rii.getBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				g1 = gii.getBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				b1 = bii.getBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				
				r2 = referRGB[0].getBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				g2 = referRGB[1].getBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				b2 = referRGB[2].getBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				
				sr1 = rii.getBlockSquareSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				sg1 = gii.getBlockSquareSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				sb1 = bii.getBlockSquareSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				
				sr2 = referRGB[0].getBlockSquareSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				sg2 = referRGB[1].getBlockSquareSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				sb2 = referRGB[2].getBlockSquareSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				
				xyr = rii.getXYBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				xyg = gii.getXYBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				xyb = bii.getXYBlockSum(col, row, (yr * 2 + 1), (xr * 2 + 1));
				
				float nccr = calculateNCC(r1, r2, sr1, sr2, xyr, size);
				float nccg = calculateNCC(g1, g2, sg1, sg2, xyg, size);
				float nccb = calculateNCC(b1, b2, sb1, sb2, xyb, size);
				
				outPixels[row * width + col] = (nccr + nccg + nccb);
			}
		}
		
		System.out.println("time consum : " + (System.currentTimeMillis() - time));
4. 归一化输出NCC图像与结果代码如下:

		// normalization the data
		float max = 0.0f, min = 100.0f;
		for(int i=0; i<outPixels.length; i++) {
			max = Math.max(max, outPixels[i]);
			min = Math.min(min, outPixels[i]);
		}
		
		// create output image 
		float delta = max - min;
		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		int ry = -1;
		int rx = -1;
		for(int row = 0; row<height; row++) {
			for(int col=0; col<width; col++) {
				int gray = (int)(((outPixels[row*width+col]-min) / delta) *255);
				gray = 255 - gray;
				if(min == outPixels[row*width+col]) {
					bi.setRGB(col, row, Color.RED.getRGB());
					ry = row;
					rx = col;
				} else {
					int color = (0xff << 24) | (gray << 16) | (gray << 8) | gray;
					bi.setRGB(col, row, color);
				}
			}
		}
		if(rx > 0 && ry > 0) {
			Graphics2D g2d = image.createGraphics();
			g2d.setPaint(Color.RED);
			g2d.drawRect(rx-xr, ry-yr, xr*2, yr*2);
		}

相比传统的NCC计算方法,此方法的计算效率是传统方法几百倍提升,而且窗口越大效率提升越明显,有人对此作出的统计如下:


可见基于积分图快速NCC可以极大提升执行效率减少计算时间,实现窗口半径无关NCC比较。

最后

本文是关于积分图使用的第三篇文章,可以说积分图在实际图像处理中应用十分广泛,本人会继续努力深挖与大家分享。希望各位顶下次文以表支持, 谢谢!本人坚持分享有用实用的图像处理算法!需要大家多多支持。

目录
相关文章
WK
|
3天前
|
机器学习/深度学习 算法 数据挖掘
PSO算法的应用场景有哪些
粒子群优化算法(PSO)因其实现简单、高效灵活,在众多领域广泛应用。其主要场景包括:神经网络训练、工程设计、电力系统经济调度与配电网络重构、数据挖掘中的聚类与分类、控制工程中的参数整定、机器人路径规划、图像处理、生物信息学及物流配送和交通管理等。PSO能处理复杂优化问题,快速找到全局最优解或近似解,展现出强大的应用潜力。
WK
14 1
|
12天前
|
机器学习/深度学习 算法 Python
群智能算法:深入解读人工水母算法:原理、实现与应用
近年来,受自然界生物行为启发的优化算法备受关注。人工水母算法(AJSA)模拟水母在海洋中寻找食物的行为,是一种新颖的优化技术。本文详细解读其原理及实现步骤,并提供代码示例,帮助读者理解这一算法。在多模态、非线性优化问题中,AJSA表现出色,具有广泛应用前景。
|
19天前
|
机器学习/深度学习 算法 数据挖掘
R语言中的支持向量机(SVM)与K最近邻(KNN)算法实现与应用
【9月更文挑战第2天】无论是支持向量机还是K最近邻算法,都是机器学习中非常重要的分类算法。它们在R语言中的实现相对简单,但各有其优缺点和适用场景。在实际应用中,应根据数据的特性、任务的需求以及计算资源的限制来选择合适的算法。通过不断地实践和探索,我们可以更好地掌握这些算法并应用到实际的数据分析和机器学习任务中。
|
24天前
|
算法 C++
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
27天前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
56 1
|
1月前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
21 0
|
1月前
|
机器学习/深度学习 数据采集 算法
随机森林算法应用
8月更文挑战第20天
|
15天前
|
算法 BI Serverless
基于鱼群算法的散热片形状优化matlab仿真
本研究利用浴盆曲线模拟空隙外形,并通过鱼群算法(FSA)优化浴盆曲线参数,以获得最佳孔隙度值及对应的R值。FSA通过模拟鱼群的聚群、避障和觅食行为,实现高效全局搜索。具体步骤包括初始化鱼群、计算适应度值、更新位置及判断终止条件。最终确定散热片的最佳形状参数。仿真结果显示该方法能显著提高优化效率。相关代码使用MATLAB 2022a实现。
|
15天前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
|
16天前
|
资源调度 算法
基于迭代扩展卡尔曼滤波算法的倒立摆控制系统matlab仿真
本课题研究基于迭代扩展卡尔曼滤波算法的倒立摆控制系统,并对比UKF、EKF、迭代UKF和迭代EKF的控制效果。倒立摆作为典型的非线性系统,适用于评估不同滤波方法的性能。UKF采用无迹变换逼近非线性函数,避免了EKF中的截断误差;EKF则通过泰勒级数展开近似非线性函数;迭代EKF和迭代UKF通过多次迭代提高状态估计精度。系统使用MATLAB 2022a进行仿真和分析,结果显示UKF和迭代UKF在非线性强的系统中表现更佳,但计算复杂度较高;EKF和迭代EKF则更适合维数较高或计算受限的场景。