DvD / utils_flow /util_optical_flow.py
hanquansanren's picture
Add application file
05fb4ab
from functools import wraps
import cv2
import numpy as np
from matplotlib import pyplot as plt
MAX_VALUES_BY_DTYPE = {
np.dtype('uint8'): 255,
np.dtype('uint16'): 65535,
np.dtype('uint32'): 4294967295,
np.dtype('float32'): 1.0,
}
UNKNOWN_FLOW_THRESH = 1e7
SMALLFLOW = 0.0
LARGEFLOW = 1e8
def flow2rgb(flow_map, max_value):
if isinstance(flow_map,np.ndarray):
if flow_map.shape[2] == 2:
flow_map = flow_map.transpose(2,0, 1)
flow_map_np = flow_map
else:
if flow_map.shape[2] == 2:
# shape is HxWx2
flow_map = flow_map.permute(2, 0, 1)
flow_map_np = flow_map.detach().cpu().numpy()
_, h, w = flow_map_np.shape
flow_map_np[:,(flow_map_np[0] == 0) & (flow_map_np[1] == 0)] = float('nan')
rgb_map = np.ones((3,h,w)).astype(np.float32)
if max_value is not None:
normalized_flow_map = flow_map_np / max_value
else:
normalized_flow_map = flow_map_np / (np.abs(flow_map_np).max())
rgb_map[0] += normalized_flow_map[0]
rgb_map[1] -= 0.5*(normalized_flow_map[0] + normalized_flow_map[1])
rgb_map[2] += normalized_flow_map[1]
return rgb_map.clip(0,1)
def flow_to_image(flow, maxrad=None):
"""
Convert flow into middlebury color code image
:param flow: optical flow map
:return: optical flow image in middlebury color
"""
h,w, _ = flow.shape
u = flow[:, :, 0]
v = flow[:, :, 1]
maxu = -999.
maxv = -999.
minu = 999.
minv = 999.
idxUnknow = (abs(u) > UNKNOWN_FLOW_THRESH) | (abs(v) > UNKNOWN_FLOW_THRESH)
u[idxUnknow] = 0
v[idxUnknow] = 0
if maxrad is None:
rad = np.sqrt(u ** 2 + v ** 2)
maxrad = max(-1, np.max(rad))
#print("max flow: %.4f\nflow range:\nu = %.3f .. %.3f\nv = %.3f .. %.3f" % (maxrad, minu,maxu, minv, maxv))
u = u/(maxrad + np.finfo(float).eps)
v = v/(maxrad + np.finfo(float).eps)
img = compute_color(u, v)
idx = np.repeat(idxUnknow[:, :, np.newaxis], 3, axis=2)
img[idx] = 0
valid = np.ones((h,w), np.uint8)
valid[np.logical_and(u == 0 , v == 0)] = 0
return np.uint8(img)*np.expand_dims(valid, axis=2)
def show_flow(flow):
"""
visualize optical flow map using matplotlib
:param filename: optical flow file
:return: None
"""
img = flow_to_image(flow)
plt.imshow(img)
plt.show()
def visualize_flow(flow, mode='Y'):
"""
this function visualize the input flow
:param flow: input flow in array
:param mode: choose which color mode to visualize the flow (Y: Ccbcr, RGB: RGB color)
:return: None
"""
if mode == 'Y':
# Ccbcr color wheel
img = flow_to_image(flow)
plt.imshow(img)
plt.show()
elif mode == 'RGB':
(h, w) = flow.shape[0:2]
du = flow[:, :, 0]
dv = flow[:, :, 1]
valid = flow[:, :, 2]
max_flow = max(np.max(du), np.max(dv))
img = np.zeros((h, w, 3), dtype=np.float64)
# angle layer
img[:, :, 0] = np.arctan2(dv, du) / (2 * np.pi)
# magnitude layer, normalized to 1
img[:, :, 1] = np.sqrt(du * du + dv * dv) * 8 / max_flow
# phase layer
img[:, :, 2] = 8 - img[:, :, 1]
# clip to [0,1]
small_idx = img[:, :, 0:3] < 0
large_idx = img[:, :, 0:3] > 1
img[small_idx] = 0
img[large_idx] = 1
# convert to rgb
img = cl.hsv_to_rgb(img)
# remove invalid point
img[:, :, 0] = img[:, :, 0] * valid
img[:, :, 1] = img[:, :, 1] * valid
img[:, :, 2] = img[:, :, 2] * valid
# show
plt.imshow(img)
plt.show()
def compute_color(u, v):
"""
compute optical flow color map
:param u: optical flow horizontal map
:param v: optical flow vertical map
:return: optical flow in color code
"""
[h, w] = u.shape
img = np.zeros([h, w, 3])
nanIdx = np.isnan(u) | np.isnan(v)
u[nanIdx] = 0
v[nanIdx] = 0
colorwheel = make_color_wheel()
ncols = np.size(colorwheel, 0)
rad = np.sqrt(u**2+v**2)
a = np.arctan2(-v, -u) / np.pi
fk = (a+1) / 2 * (ncols - 1) + 1
k0 = np.floor(fk).astype(int)
k1 = k0 + 1
k1[k1 == ncols+1] = 1
f = fk - k0
for i in range(0, np.size(colorwheel,1)):
tmp = colorwheel[:, i]
col0 = tmp[k0-1] / 255
col1 = tmp[k1-1] / 255
col = (1-f) * col0 + f * col1
idx = rad <= 1
col[idx] = 1-rad[idx]*(1-col[idx])
notidx = np.logical_not(idx)
col[notidx] *= 0.75
img[:, :, i] = np.uint8(np.floor(255 * col*(1-nanIdx)))
return img
def make_color_wheel():
"""
Generate color wheel according Middlebury color code
:return: Color wheel
"""
RY = 15
YG = 6
GC = 4
CB = 11
BM = 13
MR = 6
ncols = RY + YG + GC + CB + BM + MR
colorwheel = np.zeros([ncols, 3])
col = 0
# RY
colorwheel[0:RY, 0] = 255
colorwheel[0:RY, 1] = np.transpose(np.floor(255*np.arange(0, RY) / RY))
col += RY
# YG
colorwheel[col:col+YG, 0] = 255 - np.transpose(np.floor(255*np.arange(0, YG) / YG))
colorwheel[col:col+YG, 1] = 255
col += YG
# GC
colorwheel[col:col+GC, 1] = 255
colorwheel[col:col+GC, 2] = np.transpose(np.floor(255*np.arange(0, GC) / GC))
col += GC
# CB
colorwheel[col:col+CB, 1] = 255 - np.transpose(np.floor(255*np.arange(0, CB) / CB))
colorwheel[col:col+CB, 2] = 255
col += CB
# BM
colorwheel[col:col+BM, 2] = 255
colorwheel[col:col+BM, 0] = np.transpose(np.floor(255*np.arange(0, BM) / BM))
col += + BM
# MR
colorwheel[col:col+MR, 2] = 255 - np.transpose(np.floor(255 * np.arange(0, MR) / MR))
colorwheel[col:col+MR, 0] = 255
return colorwheel
def show_flow(disp_x, disp_y):
norm_flow=np.sqrt(np.power(disp_x,2) + np.power(disp_y,2) )
return norm_flow
def diff_neighboring_OF(disp_x, disp_y):
diff=np.zeros((disp_x.shape[0], disp_x.shape[1],5), dtype=np.float32)
print(disp_x.shape[0])
for y in range(2, disp_x.shape[0]-2):
for x in range(2, disp_x.shape[1]-2):
diff[y, x, 0] = np.sqrt((disp_x[y, x] - disp_x[y+1, x])**2 + (disp_y[y,x]-disp_y[y+1, x])**2)
diff[y, x, 1] = np.sqrt((disp_x[y, x] - disp_x[y - 1, x]) ** 2 + (disp_y[y, x] - disp_y[y - 1, x]) ** 2 )
diff[y, x, 2] = np.sqrt((disp_x[y, x] - disp_x[y, x+1]) ** 2 + (disp_y[y, x] - disp_y[y, x+1]) ** 2 )
diff[y, x, 3] = np.sqrt((disp_x[y, x] - disp_x[y, x-1]) ** 2 + (disp_y[y, x] - disp_y[y, x-1]) ** 2 )
diff[y,x,4]=1/4*(diff[y, x, 1]+diff[y, x, 2]+diff[y, x, 3]+diff[y, x, 0])
return diff
def preserve_shape(func):
"""Preserve shape of the image."""
@wraps(func)
def wrapped_function(img, *args, **kwargs):
shape = img.shape
result = func(img, *args, **kwargs)
result = result.reshape(shape)
return result
return wrapped_function
def preserve_channel_dim(func):
"""Preserve dummy channel dim."""
@wraps(func)
def wrapped_function(img, *args, **kwargs):
shape = img.shape
result = func(img, *args, **kwargs)
if len(shape) == 3 and shape[-1] == 1 and len(result.shape) == 2:
result = np.expand_dims(result, axis=-1)
return result
return wrapped_function
def center_crop(img, size):
"""
Get the center crop of the input image
Args:
img: input image [BxCxHxW]
size: load_size of the center crop (tuple)
Output:
img_pad: center crop
x, y: coordinates of the crop
"""
if not isinstance(size, tuple):
size = (size, size)
img = img.copy()
#w, h = img.shape[1::-1]
w, h=img.shape[:2]
pad_w = 0
pad_h = 0
if w < size[0]:
pad_w = np.uint16((size[0] - w) / 2)
if h < size[1]:
pad_h = np.uint16((size[1] - h) / 2)
img_pad = cv2.copyMakeBorder(img,
pad_h,
pad_h,
pad_w,
pad_w,
cv2.BORDER_CONSTANT,
value=[0, 0, 0])
w, h = img_pad.shape[1::-1]
x1 = w // 2 - size[0] // 2
y1 = h // 2 - size[1] // 2
img_pad = img_pad[y1:y1 + size[1], x1:x1 + size[0], :]
return img_pad, x1, y1
def crop_images_and_rescale_OF(I, I_prime, map_x, map_y, size):
I_cropped, x1, y1=center_crop(I, size)
I_prime_cropped, x1, y1=center_crop(I_prime, size)
map_x=map_x-x1 # warped image starts at a new index x1 in horizontal direction
map_y=map_y-y1
map_x_modified=map_x[y1:y1 + size[1], x1:x1 + size[0]]
map_y_modified = map_y[y1:y1 + size[1], x1:x1 + size[0]]
return I_cropped, I_prime_cropped, map_x_modified, map_y_modified
@preserve_channel_dim
def pad(img, min_height, min_width, border_mode=cv2.BORDER_REFLECT_101, value=None):
height, width = img.shape[:2]
if height < min_height:
h_pad_top = int((min_height - height) / 2.0)
h_pad_bottom = min_height - height - h_pad_top
else:
h_pad_top = 0
h_pad_bottom = 0
if width < min_width:
w_pad_left = int((min_width - width) / 2.0)
w_pad_right = min_width - width - w_pad_left
else:
w_pad_left = 0
w_pad_right = 0
img = pad_with_params(img, h_pad_top, h_pad_bottom, w_pad_left, w_pad_right, border_mode, value)
assert img.shape[0] == max(min_height, height)
assert img.shape[1] == max(min_width, width)
return img
@preserve_channel_dim
def pad_with_params(img, h_pad_top, h_pad_bottom, w_pad_left, w_pad_right, border_mode=cv2.BORDER_REFLECT_101,
value=None):
img = cv2.copyMakeBorder(img, h_pad_top, h_pad_bottom, w_pad_left, w_pad_right, border_mode, value=value)
return img
def crop(img, x_min, y_min, x_max, y_max):
height, width = img.shape[:2]
if x_max <= x_min or y_max <= y_min:
raise ValueError(
'We should have x_min < x_max and y_min < y_max. But we got'
' (x_min = {x_min}, y_min = {y_min}, x_max = {x_max}, y_max = {y_max})'.format(
x_min=x_min,
x_max=x_max,
y_min=y_min,
y_max=y_max
)
)
if x_min < 0 or x_max > width or y_min < 0 or y_max > height:
raise ValueError(
'Values for crop should be non negative and equal or smaller than image sizes'
'(x_min = {x_min}, y_min = {y_min}, x_max = {x_max}, y_max = {y_max}'
'height = {height}, width = {width})'.format(
x_min=x_min,
x_max=x_max,
y_min=y_min,
y_max=y_max,
height=height,
width=width
)
)
return img[y_min:y_max, x_min:x_max]
def get_center_crop_coords(height, width, crop_height, crop_width):
y1 = (height - crop_height) // 2
y2 = y1 + crop_height
x1 = (width - crop_width) // 2
x2 = x1 + crop_width
return x1, y1, x2, y2
def center_crop(img, crop_height, crop_width):
height, width = img.shape[:2]
if height < crop_height or width < crop_width:
raise ValueError(
'Requested crop load_size ({crop_height}, {crop_width}) is '
'larger than the image load_size ({height}, {width})'.format(
crop_height=crop_height,
crop_width=crop_width,
height=height,
width=width,
)
)
x1, y1, x2, y2 = get_center_crop_coords(height, width, crop_height, crop_width)
img = img[y1:y2, x1:x2]
return img
def get_random_crop_coords(height, width, crop_height, crop_width, h_start, w_start):
y1 = int((height - crop_height) * h_start)
y2 = y1 + crop_height
x1 = int((width - crop_width) * w_start)
x2 = x1 + crop_width
return x1, y1, x2, y2
def random_crop(img, crop_height, crop_width, h_start, w_start):
height, width = img.shape[:2]
if height < crop_height or width < crop_width:
raise ValueError(
'Requested crop load_size ({crop_height}, {crop_width}) is '
'larger than the image load_size ({height}, {width})'.format(
crop_height=crop_height,
crop_width=crop_width,
height=height,
width=width,
)
)
x1, y1, x2, y2 = get_random_crop_coords(height, width, crop_height, crop_width, h_start, w_start)
img = img[y1:y2, x1:x2]
return img
def clamping_crop(img, x_min, y_min, x_max, y_max):
h, w = img.shape[:2]
if x_min < 0:
x_min = 0
if y_min < 0:
y_min = 0
if y_max >= h:
y_max = h - 1
if x_max >= w:
x_max = w - 1
return img[int(y_min):int(y_max), int(x_min):int(x_max)]