视差图是神经网络里的x--feature,分左右眼
计算过程是先将左眼(或右眼)图片作为主视图(基准),然后用另外一眼图片缓慢的水平划过主视图,
这时候会有部分区域重合,相当于人看东西的时候双目同时注视.
记住这个水平移动的距离就可以计算出景深.计算公式还包含焦距,双眼距离.
小技巧,为了提高计算速度,可以将彩色图片转化为灰度图,如下.
小技巧,为了提高计算速度,可以将多张滑动重合图堆叠在一起,构成一张3D矩阵图,这里将3D矩阵图做个2D切片:
然后从层,行,列3个维度中,选取行的维度进行切片,再卷积,最后得到重合位置,这里做个2D切片:
用到的卷积核见程序中的 ---kernel. 卷积核还有继续改进的空间.
注意图片上边沿附近的2个小白点:
将多个重合位置重新降维到2D得到结果. 这样就得到了深度图, 可以作为 神经网络里的y--label, 可惜简单的算法只能 边沿涂色,需要后续处理才能将整个物体涂色.
# 改成 320 x 240 分辨率
# 用 卷积去除彩虹, 卷积核的作用是时间轴上的边界重叠位置,计算重合时刻的视差,
# todo 现在是边界有深度信息,面没有深度信息, 计算的结果仅仅是边沿, todo 将视差向右传播,方法是??(直到另外一个极值?)
# todo 滑动距离需要改变
''' 伪代码:
frame2 [0:240, 0:320] 左右眼照片,320x240
cv2.cvtColor(imgL,cv2.COLOR_BGR2GRAY) 灰度化
cv2.addWeighted() 融合左右眼算法,
# 将shift=1 shift=200 堆叠成3D 阵列
# 将3D 转换为2D,选择极值,并记录层数
# 将2D转换为深度图,
# 将深度图转换为色彩图
'''
##############
import cv2
import numpy as np
import time
start = time.time()
dx, dy = 0, -1 # 左眼图片滑动的dx 向右偏移量, dy=-1 制造偏差修正,固定的偏移量
# dx=-3 窗外的塔吊重合
# dx=-5 办公室远处的绿植重合
# dx=-99 距离摄像头15cm的鼻子重合
DATA3D =np.zeros((30,240,320), dtype=np.uint8) # 存储 # 2图相减的值,堆叠DATA3D。 每个单元格里是:左右眼差异 所有的视差融合图 高 x 宽 x 视差(像素), --一开始申请连续空间,数据搬运工作量少,比较节约cpu时间,
DATA3D2 =np.zeros((30,240,320), dtype=np.uint8) # 存储 视差融合点
DATA2D=np.zeros((240,320), dtype=np.uint8 ) # 存储 高 x 宽 ,每个单元格里是: 融合后最小值的视差(像素)
# print(DATA3D.shape)
keyInput=0
###########################################################################
kernel = np.array(( # 卷积核,用于查找左右眼重合点
[-1, -1, 1, 1, 0],
[-1, -1, 1, 0, -1],
[-1, -1, 0, -1, -1],
[-1, 0, 1, -1, -1],
[ 0, 1, 1, -1, -1]),
dtype="float32") / 1
############## 获取图片 ############################
for k in range(0, 1 ):
print("读取左右眼图片 -------------")
imgL =cv2.imread("imgL00009.png") #读取 图片L ,图片R
imgR =cv2.imread("imgR00009.png")
#########################################################
imgL_gray=cv2.cvtColor(imgL,cv2.COLOR_BGR2GRAY) #将彩色图片L转换至灰度图片
## rows, cols, ch = imgL.shape # for 彩色图片
rows, cols = imgL_gray.shape
## imgL_gray=imgL_gray.astype(np.int16)
imgR_gray=cv2.cvtColor(imgR,cv2.COLOR_BGR2GRAY) #将彩色图片R转换至灰度图片
cv2.imshow('imgR_gray',imgR_gray)
########################################################################################### 滑动 + 堆叠
for i in range(2,31): # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< todo 滑动距离需要改变
print("视差图相减,再堆叠-------------------",i)
MAT = np.float32([[1, 0, -2*i], [0, 1, dy]]) # 构造平移变换矩阵 ,[dx, dy]像素移动
imgL_gray_shift = cv2.warpAffine(imgL_gray, MAT, (cols, rows)) # 仿射变换,移动图片。默认为黑色填充
# imgL_gray_shift = cv2.warpAffine(imgL_gray, MAT, (cols, rows), borderValue=(255,255,255)) # 白色填充
imgMerge=cv2.absdiff(imgR_gray , imgL_gray_shift ) # 2图相减的值,
''' # <<<<<<< debug 注释符号' ' ' 需要对齐
## 将 imgMorphClose0 imgMorphClose1 做与运算
# imgMerge= cv2.bitwise_xor(imgR_Threshold ,imgL_Threshold) ## 做与或运算 xor
# imgMerge= cv2.bitwise_and(imgR_Threshold ,imgL_Threshold) ## 做与运算 and
# imgMerge= cv2.bitwise_or(imgR_Threshold ,imgL_Threshold) ## 做或运算 or
# imgMerge= cv2.add(imgR_Threshold ,imgL_Threshold) ## 做加运算 add,效果等同于 or
# imgMerge= cv2.bitwise_not(imgR_Threshold) ## 做非运算 not
'''
DATA3D[i-2,:,:] = imgMerge # 2图相减的值,--------------------------------------------堆叠DATA3D。
########################################################################################### 卷积 + 堆叠
for i in range(0,240):
probe2D= DATA3D[:, i, :] # 切片 -------------- 切片DATA3D, 方向在 层,行,列 的 行维度
imgProbe2DMin = cv2.filter2D(probe2D, -1, kernel) # 卷积 求重合点-------------- cv.filter2D(源图像,输出数据类型 ,卷积核) , 输出数据类型 =-1 表示输出类型和输入相同
DATA3D2[:,i,:] = imgProbe2DMin # 堆叠 --------------堆叠DATA3D2, 方向同上
############################################ # 显示 3D矩阵的剖面
imgTemp = DATA3D[:,200,:] *90 ## 未经卷积处理过的3D矩阵的剖面, *90 表示增强信号,否则看不到
cv2.imshow( 'imgTemp', imgTemp )
imgTemp1 = DATA3D2[:,200,:] *90 ## 已经卷积处理过的3D矩阵的剖面, *90 表示增强信号,否则看不到
cv2.imshow( 'imgTemp1', imgTemp1 )
########################### # 求极值
DATA2D = np.argmax( DATA3D2,axis = 0 ).astype(np.uint8) * 9 # np.argmin( ) 返回了最小值的位置
# debug <<<<<<<<<<<<<<<<<<<程序会将uint8 自动更改为 int64
# print(DATA2D.dtype)
# print( DATA2D.shape )
outPutColor = cv2.applyColorMap(DATA2D, cv2.COLORMAP_JET) # COLORMAP_JET = 2, 蓝到红
# COLORMAP_RAINBOW = 4,红到蓝
cv2.imshow( 'outPutColor', outPutColor ) # 如果 DATA2D 声明的时候没有指定数据类型,或者在计算过程中数据类型改变,可以在这里指定为 uint8
######### 键盘响应 ,兼计时器 ########################################3
keyInput = cv2.waitKey( 0) & 0xFF #等待键盘输入,间隔 xx us # debu g <<<<<<<<<<<<<<< 设置 waitKey(0) , 则表示程序会无限制的等待用户的按键事件
if (keyInput == ord('q')) | (keyInput == 27 ) : # 键盘上的按键 --q --ESC
break
cv2.destroyAllWindows()