| import os |
|
|
| import cv2 |
| import imageio |
| import numpy as np |
| import torch |
|
|
|
|
| def load_flow(path): |
| if path.endswith(".png"): |
| |
| |
| |
| |
| flo_file = cv2.imread(path, -1) |
| flo_img = flo_file[:, :, 2:0:-1].astype(np.float32) |
| invalid = flo_file[:, :, 0] == 0 |
| flo_img = flo_img - 32768 |
| flo_img = flo_img / 64 |
| flo_img[np.abs(flo_img) < 1e-10] = 1e-10 |
| flo_img[invalid, :] = 0 |
| return flo_img, np.expand_dims(flo_file[:, :, 0], 2) |
| else: |
| with open(path, "rb") as f: |
| magic = np.fromfile(f, np.float32, count=1) |
| assert 202021.25 == magic, "Magic number incorrect. Invalid .flo file" |
| h = np.fromfile(f, np.int32, count=1)[0] |
| w = np.fromfile(f, np.int32, count=1)[0] |
| data = np.fromfile(f, np.float32, count=2 * w * h) |
| |
| data2D = np.resize(data, (w, h, 2)) |
| return data2D |
|
|
|
|
| def load_mask(path): |
| |
| mask = imageio.imread(path).astype(np.float32) / 255.0 |
| if len(mask.shape) == 3: |
| mask = mask[:, :, 0] |
| return np.expand_dims(mask, -1) |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| def resize_flow(flow, new_shape): |
| _, _, h, w = flow.shape |
| new_h, new_w = new_shape |
| flow = torch.nn.functional.interpolate( |
| flow, (new_h, new_w), mode="bilinear", align_corners=True |
| ) |
| scale_h, scale_w = h / float(new_h), w / float(new_w) |
| flow[:, 0] /= scale_w |
| flow[:, 1] /= scale_h |
| return flow |
|
|
|
|
| |
| def writeFlowSintel(filename, uv, v=None): |
| """Write optical flow to file. |
| |
| If v is None, uv is assumed to contain both u and v channels, |
| stacked in depth. |
| Original code by Deqing Sun, adapted from Daniel Scharstein. |
| """ |
| nBands = 2 |
| TAG_CHAR = np.array([202021.25], np.float32) |
|
|
| if v is None: |
| assert uv.ndim == 3 |
| assert uv.shape[2] == 2 |
| u = uv[:, :, 0] |
| v = uv[:, :, 1] |
| else: |
| u = uv |
|
|
| assert u.shape == v.shape |
| height, width = u.shape |
|
|
| os.makedirs(os.path.dirname(filename), exist_ok=True) |
| with open(filename, "wb") as f: |
| |
| f.write(TAG_CHAR) |
| np.array(width).astype(np.int32).tofile(f) |
| np.array(height).astype(np.int32).tofile(f) |
| |
| tmp = np.zeros((height, width * nBands)) |
| tmp[:, np.arange(width) * 2] = u |
| tmp[:, np.arange(width) * 2 + 1] = v |
| tmp.astype(np.float32).tofile(f) |
|
|
|
|
| |
| def writeFlowKITTI(filename, uv): |
| uv = 64.0 * uv + 2**15 |
| valid = np.ones([uv.shape[0], uv.shape[1], 1]) |
| uv = np.concatenate([uv, valid], axis=-1).astype(np.uint16) |
| cv2.imwrite(filename, uv[..., ::-1]) |
|
|
|
|
| def evaluate_flow(gt_flows, pred_flows, moving_masks=None): |
| |
| def calculate_error_rate(epe_map, gt_flow, mask): |
| bad_pixels = np.logical_and( |
| epe_map * mask > 3, |
| epe_map * mask > 0.05 * np.sqrt(np.sum(np.square(gt_flow), axis=2)), |
| ) |
| return bad_pixels.sum() / mask.sum() * 100.0 |
|
|
| ( |
| error, |
| error_noc, |
| error_occ, |
| error_move, |
| error_static, |
| error_rate, |
| error_rate_noc, |
| ) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) |
| error_move_rate, error_static_rate = 0.0, 0.0 |
| B = len(gt_flows) |
| for gt_flow, pred_flow, i in zip(gt_flows, pred_flows, range(B)): |
| H, W = gt_flow.shape[:2] |
| h, w = pred_flow.shape[:2] |
|
|
| |
| |
| |
|
|
| |
|
|
| pred_flow = torch.from_numpy(pred_flow)[None].permute(0, 3, 1, 2) |
| flo_pred = resize_flow(pred_flow, (H, W)) |
| flo_pred = flo_pred[0].numpy().transpose(1, 2, 0) |
|
|
| epe_map = np.sqrt( |
| np.sum(np.square(flo_pred[:, :, :2] - gt_flow[:, :, :2]), axis=2) |
| ) |
| if gt_flow.shape[-1] == 2: |
| error += np.mean(epe_map) |
|
|
| elif gt_flow.shape[-1] == 4: |
| error += np.sum(epe_map * gt_flow[:, :, 2]) / np.sum(gt_flow[:, :, 2]) |
| noc_mask = gt_flow[:, :, -1] |
| error_noc += np.sum(epe_map * noc_mask) / np.sum(noc_mask) |
|
|
| error_occ += np.sum(epe_map * (gt_flow[:, :, 2] - noc_mask)) / max( |
| np.sum(gt_flow[:, :, 2] - noc_mask), 1.0 |
| ) |
|
|
| error_rate += calculate_error_rate( |
| epe_map, gt_flow[:, :, 0:2], gt_flow[:, :, 2] |
| ) |
| error_rate_noc += calculate_error_rate( |
| epe_map, gt_flow[:, :, 0:2], noc_mask |
| ) |
| if moving_masks is not None: |
| move_mask = moving_masks[i] |
|
|
| error_move_rate += calculate_error_rate( |
| epe_map, gt_flow[:, :, 0:2], gt_flow[:, :, 2] * move_mask |
| ) |
| error_static_rate += calculate_error_rate( |
| epe_map, gt_flow[:, :, 0:2], gt_flow[:, :, 2] * (1.0 - move_mask) |
| ) |
|
|
| error_move += np.sum(epe_map * gt_flow[:, :, 2] * move_mask) / np.sum( |
| gt_flow[:, :, 2] * move_mask |
| ) |
| error_static += np.sum( |
| epe_map * gt_flow[:, :, 2] * (1.0 - move_mask) |
| ) / np.sum(gt_flow[:, :, 2] * (1.0 - move_mask)) |
|
|
| if gt_flows[0].shape[-1] == 4: |
| res = [ |
| error / B, |
| error_noc / B, |
| error_occ / B, |
| error_rate / B, |
| error_rate_noc / B, |
| ] |
| if moving_masks is not None: |
| res += [error_move / B, error_static / B] |
| return res |
| else: |
| return [error / B] |
|
|