文章目录

  • 1. 立体匹配
    • 1.1 概述
    • 1.2 主要立体匹配算法分类
    • 1.3 立体匹配的基本步骤
  • 2. 归一化互相关(NCC)视差匹配法
    • 2.1 原理
    • 2.2 匹配流程
    • 2.3 代码实现
  • 3. 不同窗口值对匹配结果的影响
  • 4. 实验遇到的问题与解决

1. 立体匹配

1.1 概述

立体匹配是立体视觉研究中的关键部分。其目标是在两个或多个视点中匹配相应像素点,计算视差。通过建立一个能量代价函数,对其最小化来估计像素点的视差,求得深度

点P和Q,映射到左相机OR像面上的同一点p≡q,只要找到p和q在右相机OT像面上的对应点就可以通过三角计算估计深度找到对应点的过程,即立体匹配

为了找到对应点,需要增加约束,最常用的是极线约束


P和Q映射到左相机QR像面上的同一点p≡q,直线pq上的点对应点一定位于右像面的直线p’q’上,p’q’即为直线pq的极线,这就是极线约束

接下来就可以根据视差估计深度,然后通过Graph cuts算法给每一个像素点分配视差从而得到深度图,不再详细说明。

1.2 主要立体匹配算法分类

1)根据采用图像表示的基元不同,立体匹配算法分为:
A、区域立体匹配算法:可获取稠密视差图。缺点:受图像的仿射畸变和辐射畸变影响较大;像素点约束窗口的大小与形状选择比较困难,选择过大,在深度不连续处,视差图中会出现过度平滑现象;选择过小,对像素点的约束比较少,图像信息没有得到充分利用,容易产生误匹配。
B、基于特征的立体匹配算法:可获得稀疏的视差图,经差值估计可获得稠密视差图。可提取点、线、面等局部特征,也可提取多边形和图像结构等全局特征。缺点:特征提取易受遮挡、光线、重复纹理等影响较大;差值估计计算量大。
C、基于相位立体匹配算法:假定在图像对应点中,其频率范围内,其局部相位是相等的,在频率范围内进行视差估计。
2)依据采用最优化理论方法的不同,立体匹配算法可以分为:
A、局部的立体匹配算法
B、全局的立体匹配算法

还有立体匹配算法介绍的更详细内容:参考博客

1.3 立体匹配的基本步骤

立体匹配过程:

  1. 匹配代价计算: 一般是通过计算左右两图对应像素3个通道的灰度值差来决定匹配代价的,常用的就是基于像素点匹配代价计算,一般有AD,SD,TAD什么的,基于区域的匹配代价计算一般有SAD,SSD, STAD之类的。匹配代价计算会生成一个disparity space image,也就是DSI。这个DSI是一个三维的空间,也就是每一个视差,得到一张代价图。假如视差范围是0~16,则会得到17幅代价图。视差搜索范围就是MiddleBurry网站上的stereopair值,也就是说在视差范围内(比如0-16)内搜索匹配代价,得到17张匹配代价图,然后找到匹配代价最小的对应的视差值就是此像素对应的视差
  2. 代价聚合:其实就是一个滤波的过程,对每一幅代价图进行聚合,最简单的就是采用boxfilter。第一步代价计算只是得到了图像上所有孤立像素的视差值,但是这些时差值都是孤立的,引入了过多噪声,比如一片区域的视差值都是10,可是引入噪声后就会导致这一片的视差值都不一样,那么就需要一个滤波的过程,也就是我们所说的局部立体匹配方法,即采用窗口卷积达到局部滤波的目的
  3. 计算视差:常用的方法就是WTA算法(局部),对于图像中的同一个点,选出17幅代价图中匹配代价最小的那张图,该幅图对应的视差值就选取为最终的视差。或者在全局立体匹配中采用能量函数的方法,分为数据项和平滑项,数据项其实就是代价计算,平滑项就是代价聚合,只不过窗口大小是整幅图像,也可以试试如果把平滑项前面的系数lamda设为0,那么得到的结果和单纯代价计算的局部立体匹配是一样的。
  4. 视差精化:也就是对得到的视差进行优化的过程,如:左右一致性检测、区域投票等;这步其实是很多立体匹配的遮羞布,比如用遮挡处理,中值滤波,左右一致性检测等,都能使最后的是视差图提升1%左右,它是很多论文的遮羞布。但是不可否认的是,立体匹配最关键的步骤仍然是代价计算和代价聚合步骤。
    在立体匹配方法中,基于全局和局部的算法有些区别。不过基本步骤都差不多。有些时候,基于局部的算法,第一步和第二步是合并在一起进行的,基于全局的算法,会跳过第二步。

2. 归一化互相关(NCC)视差匹配法

2.1 原理

对于原始的图像内任意一个像素点(px,py)(px,py)(px,py)构建一个n×nn×nn×n的邻域作为匹配窗口。然后对于目标相素位置(px+d,py)(px+d,py)(px+d,py)同样构建一个n×nn×nn×n大小的匹配窗口,对两个窗口进行相似度度量,注意这里的dd dd有一个取值范围。对于两幅图像来说,在进行NCCNCCNCC计算之前要对图像处理,也就是将两帧图像校正到水平位置,即光心处于同一水平线上,此时极线是水平的,否则匹配过程只能在倾斜的极线方向上完成,这将消耗更多的计算资源。

NCCNCCNCC计算公式如下:
NCC(p,d)=∑(x,y)∈Wp(I1(x,y)−I1‾(Px,Py))⋅(I1(x+d,y)−I2‾(Px+d,Py))∑(x,y)∈Wp(I1(x,y)−I1‾(Px,Py))2⋅∑(x,y)∈Wp(I2(x+d,y)−I2‾(Px+d,Py))2NCC(p,d)=\frac{\sum_{(x,y)\in W_{p}}(I_{1}(x,y)-\overline{I_{1}}(P_{x},P_{y}))\cdot (I_{1}(x+d,y)-\overline{I_{2}}(P_{x}+d,P_{y}))}{\sum_{(x,y)\in W_{p}}(I_{1}(x,y)-\overline{I_{1}}(P_{x},P_{y}))^2\cdot \sum_{(x,y)\in W_{p}}(I_{2}(x+d,y)-\overline{I_{2}}(P_{x}+d,P_{y}))^2}NCC(p,d)=∑(x,y)∈Wp​​(I1​(x,y)−I1​​(Px​,Py​))2⋅∑(x,y)∈Wp​​(I2​(x+d,y)−I2​​(Px​+d,Py​))2∑(x,y)∈Wp​​(I1​(x,y)−I1​​(Px​,Py​))⋅(I1​(x+d,y)−I2​​(Px​+d,Py​))​

其中NCC(p,d)NCC(p,d)NCC(p,d)得到的值的范围将在[−1,1]之间。
WpWpWp为之前提到的匹配窗口
I1(x,y)I_{1}(x,y)I1​(x,y)为原始图像的像素值
I1‾(px,py)\overline{I_{1}}(p_x,p_y)I1​​(px​,py​)为原始窗口内像素的均值
I2(x+d,y)I_{2}(x+d,y)I2​(x+d,y)为原始图像在目标图像上对应点位置在xxx方向上偏移ddd后的像素值
I2‾(px+d,py)\overline{I_{2}}(p_x+d, p_y)I2​​(px​+d,py​)为目标图像匹配窗口像素均值

若NCC=−1NCC=−1NCC=−1,则表示两个匹配窗口完全不相关,相反,若NCC=1NCC=1NCC=1时,表示两个匹配窗口相关程度非常高。

2.2 匹配流程

  1. 采集图像:通过标定好的双目相机采集图像,当然也可以用两个单目相机来组合成双目相机。

  2. 极线校正:校正的目的是使两帧图像极线处于水平方向,或者说是使两帧图像的光心处于同一水平线上。通过校正极线可以方便后续的NCCNCCNCC操作。
    1)由标定得到的内参中畸变信息中可以对图像去除畸变
    2)通过校正函数校正以后得到相机的矫正变换R和新的投影矩阵P,接下来是要对左右视图进行去畸变,并得到重映射矩阵

  3. 特征匹配:这里便是我们利用NCCNCCNCC做匹配的步骤啦,匹配方法如上所述,右视图中与左视图待测像素同一水平线上相关性最高的即为最优匹配。完成匹配后,我们需要记录其视差ddd,即待测像素水平方向xlxlxl与匹配像素水平方向xrxrxr之间的差值d=xr−xld=xr−xld=xr−xl,最终我们可以得到一个与原始图像尺寸相同的视差图DDD

  4. 深度恢复:通过上述匹配结果得到的视差图DDD,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。计算原理如下图所示:

如图,TxTxTx为双目相机基线,fff为相机焦距,这些可以通过相机标定步骤得到。而xr−xlxr−xlxr−xl就是视差ddd。
通过公式z=f×Txdz=\frac{f×Tx}{d}z=df×Tx​可以很简单地得到以左视图为参考系的深度图了。
至此,我们便完成了双目立体匹配。倘若只是用于图像识别,那么到步骤3时已经可以结束了。

2.3 代码实现

NCCfaster.py

import numpy as np
import cv2
from PCV.localdescriptors import siftim1 = 'C://Users//Garfield//PycharmProjects//untitled//NCC-master//im2.ppm'
im2 = 'C://Users//Garfield//PycharmProjects//untitled//NCC-master//im6.ppm'img1 = cv2.imread(im1, cv2.CV_8UC1)
img2 = cv2.imread(im2, cv2.CV_8UC1)
rows, cols = img1.shape
print(img1.shape)def translaton(image, shape):step = round((shape[0]-1)/2)print(step)shifted = []for i in range(0, step+1):for j in range(0, step+1):if i==0 and j==0:M1 = np.float32([[1, 0, i], [0, 1, j]])shifted.append(cv2.warpAffine(image, M1, (image.shape[1], image.shape[0])))elif i==0 and j!=0:M1 = np.float32([[1, 0, i], [0, 1, j]])M2 = np.float32([[1, 0, i], [0, 1, -j]])shifted.append(cv2.warpAffine(image, M1, (image.shape[1], image.shape[0])))shifted.append(cv2.warpAffine(image, M2, (image.shape[1], image.shape[0])))elif i!=0 and j==0:M1 = np.float32([[1, 0, i], [0, 1, j]])M2 = np.float32([[1, 0, -i], [0, 1, j]])shifted.append(cv2.warpAffine(image, M1, (image.shape[1], image.shape[0])))shifted.append(cv2.warpAffine(image, M2, (image.shape[1], image.shape[0])))else:M1 = np.float32([[1, 0, i], [0, 1, j]])M2 = np.float32([[1, 0, -i], [0, 1, -j]])M3 = np.float32([[1, 0, -i], [0, 1, j]])M4 = np.float32([[1, 0, i], [0, 1, -j]])shifted .append(cv2.warpAffine(image, M1, (image.shape[1], image.shape[0])))shifted.append(cv2.warpAffine(image, M2, (image.shape[1], image.shape[0])))shifted.append(cv2.warpAffine(image, M3, (image.shape[1], image.shape[0])))shifted.append(cv2.warpAffine(image, M4, (image.shape[1], image.shape[0])))print(len(shifted))return np.array(shifted)#I(x,y)-avg(I(x,y))
def img_sub_avg(img_shifted, avg_img):len, height, width = img1_shifted.shapetmp_ncc1 = np.zeros([len, height, width])for i in range(len):tmp_ncc1[i] = img_shifted[i] - avg_imgprint(tmp_ncc1)return tmp_ncc1def NCC(img1_sub_avg,img2_sub_avg, threshold, max_d):#设立阈值len, height, width = img1_sub_avg.shapethershould_shifted = np.zeros([len, height, width])ncc_max = np.zeros([height, width])ncc_d = np.zeros([height, width])for j in range(3, max_d):tmp_ncc1 = np.zeros([height, width])tmp_ncc2 = np.zeros([height, width])tmp_ncc3 = np.zeros([height, width])for k in range(len):M1 = np.float32([[1, 0, -j - 1], [0, 1, 0]])thershould_shifted[k] = cv2.warpAffine(img1_sub_avg[k], M1, (img1_sub_avg.shape[2], img1_sub_avg.shape[1]))for i in range(len):tmp_ncc1 += (img2_sub_avg[i])*(thershould_shifted[i])tmp_ncc2 += pow(img2_sub_avg[i], 2)tmp_ncc3 += pow(thershould_shifted[i], 2)tmp_ncc2 = tmp_ncc2*tmp_ncc3tmp_ncc2 = np.sqrt(tmp_ncc2)tmp_ncc4 = tmp_ncc1/tmp_ncc2for m in range(height):for n in range(width):if tmp_ncc4[m, n] > ncc_max[m ,n] and tmp_ncc4[m, n] > threshold:ncc_max[m, n] = tmp_ncc4[m, n]ncc_d[m , n] = jfor i in ncc_d:print(i)return ncc_max, ncc_dif __name__ == "__main__":disparity = np.zeros([rows, cols])NCC_value = np.zeros([rows, cols])deeps = np.zeros([rows, cols])# 用3*3卷积核做均值滤波avg_img1 = cv2.blur(img1, (7, 7))avg_img2 = cv2.blur(img2, (7, 7))fimg1 = img1.astype(np.float32)fimg2 = img2.astype(np.float32)avg_img1 = avg_img1.astype(np.float32)avg_img2  = avg_img2.astype(np.float32)img1_shifted = translaton(fimg1, [7, 7])img2_shifted = translaton(fimg2, [7, 7])img1_sub_avg = img_sub_avg(img1_shifted, avg_img1)img2_sub_avg = img_sub_avg(img2_shifted, avg_img2)ncc_max, ncc_d = NCC(img1_sub_avg,img2_sub_avg, threshold = 0.5, max_d = 64)print(img1_shifted.shape)disp = cv2.normalize(ncc_d, ncc_d, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX,dtype=cv2.CV_8U)cv2.imshow("left", img1)cv2.imshow("right", img2)cv2.imshow("depth", disp)cv2.waitKey(0)  # 等待按键按下cv2.destroyAllWindows()#清除所有窗口

原始图像:

运行结果:


代码2:

# -*- coding: utf-8 -*-
import scipy.misc
from PIL import Image
from pylab import *
import cv2
from numpy import *
from numpy.ma import array
from scipy.ndimage import filters
def plane_sweep_ncc(im_l,im_r,start,steps,wid):""" 使用归一化的互相关计算视差图像 """m,n = im_l.shape# 保存不同求和值的数组mean_l = zeros((m,n))mean_r = zeros((m,n))s = zeros((m,n))s_l = zeros((m,n))s_r = zeros((m,n))# 保存深度平面的数组dmaps = zeros((m,n,steps))# 计算图像块的平均值filters.uniform_filter(im_l,wid,mean_l)filters.uniform_filter(im_r,wid,mean_r)# 归一化图像norm_l = im_l - mean_lnorm_r = im_r - mean_r# 尝试不同的视差for displ in range(steps):# 将左边图像移动到右边,计算加和filters.uniform_filter(np.roll(norm_l, -displ - start) * norm_r, wid, s) # 和归一化filters.uniform_filter(np.roll(norm_l, -displ - start) * np.roll(norm_l, -displ - start), wid, s_l)filters.uniform_filter(norm_r*norm_r,wid,s_r) # 和反归一化# 保存 ncc 的分数dmaps[:,:,displ] = s / sqrt(s_l * s_r)# 为每个像素选取最佳深度return np.argmax(dmaps, axis=2)def plane_sweep_gauss(im_l,im_r,start,steps,wid):""" 使用带有高斯加权周边的归一化互相关计算视差图像 """m,n = im_l.shape# 保存不同加和的数组mean_l = zeros((m,n))mean_r = zeros((m,n))s = zeros((m,n))s_l = zeros((m,n))s_r = zeros((m,n))# 保存深度平面的数组dmaps = zeros((m,n,steps))# 计算平均值filters.gaussian_filter(im_l,wid,0,mean_l)filters.gaussian_filter(im_r,wid,0,mean_r)# 归一化图像norm_l = im_l - mean_lnorm_r = im_r - mean_r# 尝试不同的视差for displ in range(steps):# 将左边图像移动到右边,计算加和filters.gaussian_filter(np.roll(norm_l, -displ - start) * norm_r, wid, 0, s) # 和归一化filters.gaussian_filter(np.roll(norm_l, -displ - start) * np.roll(norm_l, -displ - start), wid, 0, s_l)filters.gaussian_filter(norm_r*norm_r,wid,0,s_r) # 和反归一化# 保存 ncc 的分数dmaps[:,:,displ] = s / np.sqrt(s_l * s_r)# 为每个像素选取最佳深度return np.argmax(dmaps, axis=2)im_l = array(Image.open(r'C://Users//Garfield//Desktop//towelmatch//jidian//1.jpg').convert('L'), 'f')
im_r = array(Image.open(r'C://Users//Garfield//Desktop//towelmatch//jidian//2.jpg').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 2000
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)imsave('C://Users//Garfield//PycharmProjects//untitled//NCC-master//depth2000.png',res)
show()

此代码可以修改窗口值的大小,具体结果与分析放在下一小节

3. 不同窗口值对匹配结果的影响

原图:

以下分别是当窗口值设定为15,20,50,100,200,500,700,1000,2000的运行结果:

总的来看,窗口值设置的越大,区域化分得越为明显,而设置的越小噪声越大,区块划分的越不明显。同时,从本次实验结果图来看,wid设置为700时,结果最为理想。
另外可以看到

瓶身光照最为强烈的地方会与瓶身其他其他地方像素值有较大区分,以及窗口大小设置越大,则会更多的提取出有光照的像素点并连结成块,区分出亮部暗部。

4. 实验遇到的问题与解决

问题:

  • ‘NoneType’ object has no attribute ‘shape’

求解cols,rows = img.shape时出现上述报错,是图片路径不对,程序无法获取到图片。

解决办法:
右键下图目录灰色处

—>copypath,再在原本的读取图片路径代码中粘贴
注意格式改成C://Users//…
参考博客:立体匹配_数据结构与算法
参考博客:立体匹配过程
参考博客:双目立体匹配算法–归一化互相关(NCC)详解和代码实现(python)
NCC的更详细相关
延伸学习:真实场景的双目立体匹配(Stereo Matching)获取深度图详解
原理详解:双目视觉(三)立体匹配算法

python计算机视觉——立体匹配与NCC算法相关推荐

  1. 立体匹配之NCC算法

    FROM:http://blog.csdn.net/tulun/article/details/6388759 NCC算法(Normal Cross Correlation),具体原理见相关图像处理书 ...

  2. python计算机视觉编程——立体图像之计算视差图

    计算视差图 一.立体图像 1.1概念 1.2关于图像配准算法 二.立体重建之计算视差图 2.1归一化及算法概念 2.2匹配流程 三.实验测试 3.1实验要求 3.2实验代码 3.3实验结果分析 3.4 ...

  3. 立体匹配:经典算法Fast Bilateral Solver

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨HawkWang 来源丨计算摄影学 点击进入->3D视觉工坊学习交流群 一. 前言 你好, ...

  4. Python计算机视觉——照相机模型与增强现实

    Python计算机视觉--照相机模型与增强现实 文章目录 Python计算机视觉--照相机模型与增强现实 1 针孔照相机模型 1.1 照相机矩阵 1.2 三维点的投影 1.3 照相机矩阵的分解 1.4 ...

  5. Python计算机视觉——图像到图像的映射

    Python计算机视觉--图像到图像的映射 文章目录 Python计算机视觉--图像到图像的映射 写在前面 1 单应性变换 1.1 直接线性变换算法 1.2 仿射变换 2 图像扭曲 2.1 图像中的图 ...

  6. Python计算机视觉——SIFT特征

    Python计算机视觉--SIFT特征 文章目录 Python计算机视觉--SIFT特征 写在前面 1 SIFT特征算法步骤 1.1 尺度空间的极值检测 1.2 特征点定位 1.3 特征方向赋值 1. ...

  7. Python 计算机视觉(十四)—— OpenCV 进行霍夫变换

    参考的一些文章以及论文我都会给大家分享出来 -- 链接就贴在原文,论文我上传到资源中去,大家可以免费下载学习,如果当天资源区找不到论文,那就等等,可能正在审核,审核完后就可以下载了.大家一起学习,一起 ...

  8. Python 计算机视觉(十二)—— OpenCV 进行图像分割

    参考的一些文章以及论文我都会给大家分享出来 -- 链接就贴在原文,论文我上传到资源中去,大家可以免费下载学习,如果当天资源区找不到论文,那就等等,可能正在审核,审核完后就可以下载了.大家一起学习,一起 ...

  9. python计算机视觉编程——基本的图像操作和处理

    python计算机视觉编程--第一章(基本的图像操作和处理) 第1章 基本的图像操作和处理 1.1 PIL:Python图像处理类库 1.1.1 转换图像格式--save()函数 1.1.2 创建缩略 ...

最新文章

  1. 70种芯片细分领域、国产MCU重要代表企业
  2. 报告视频录制:腾讯会议录屏+人像画中画特效
  3. 全国大学生智能汽车竞赛获奖证书文字
  4. 皮一皮:原来程序员也是要看天赋的...
  5. llinux基本操作
  6. 李洪强经典面试题10
  7. dao接口有什么好处_Java后端精选技术:我们为什么要使用AOP?
  8. 在CentOS上安装ZooKeeper集群
  9. Java中,为什么子类的构造方法中必须调父类的构造方法?
  10. dht11温湿度传感器_Arduino不调用库实现DHT11数据读取
  11. Java Byte类的compareTo()方法和示例
  12. code warri_我参加了有史以来的第一届Warri Tech宣传活动。 这是我学到的。
  13. const在C与C++中的区别
  14. 在CentOS7下安装MySQL8数据库
  15. Python使用Manager对象实现不同机器上的进程跨网络传输数据
  16. 【BZOJ3991】寻宝游戏(动态规划)
  17. 网络流二十四题之魔术球问题
  18. termux安装gcc
  19. Python爬虫实战之爬取链家广州房价_02把小爬虫变大
  20. win10很多软件显示模糊_Win7系统和Win10系统你会怎么选?

热门文章

  1. The value of the local variable xxx is not usedJava解决办法
  2. 研发项目购置的软件服务器属于无形资产吗,购买云服务器属于无形资产
  3. react 调用子(孙)组件方法
  4. Handle的详细用法
  5. 码农、程序员、开发者
  6. android之CardView的使用
  7. 还在为网速烦恼?你可能没有使用华为云CDN加速服务
  8. 实战:618/双11大促备战全流程点点滴滴
  9. 1.6Java-接口、抽象类
  10. 苹果基带坏了怎么办_iPhone12 上市,苹果这次有哪些改变