#!/usr/bin/env python3
#
# BSD 3-Clause License
#
# This file is part of the Basalt project.
# https://gitlab.com/VladyslavUsenko/basalt.git
#
# Copyright (c) 2019-2021, Vladyslav Usenko and Nikolaus Demmel.
# All rights reserved.
#

import sys
import math
import os
import cv2
import argparse

import numpy as np
from matplotlib import pyplot as plt

parser = argparse.ArgumentParser(description='Response calibration.')
parser.add_argument('-d', '--dataset-path', required=True, help="Path to the dataset in Euroc format")
args = parser.parse_args()


dataset_path = args.dataset_path

print(dataset_path)

timestamps = np.loadtxt(dataset_path + '/mav0/cam0/data.csv', usecols=[0], delimiter=',', dtype=np.int64)
exposures = np.loadtxt(dataset_path + '/mav0/cam0/exposure.csv', usecols=[1], delimiter=',', dtype=np.int64).astype(np.float64) * 1e-6
pixel_avgs = list()

if timestamps.shape[0] != exposures.shape[0]: print("timestamps and exposures do not match")

imgs = []

# check image data.
for timestamp in timestamps:
    path = dataset_path + '/mav0/cam0/data/' + str(timestamp)
    img = cv2.imread(dataset_path + '/mav0/cam0/data/' + str(timestamp) + '.webp', cv2.IMREAD_GRAYSCALE)
    if len(img.shape) == 3: img = img[:,:,0]
    imgs.append(img)
    pixel_avgs.append(np.mean(img))

imgs = np.array(imgs)
print(imgs.shape)
print(imgs.dtype)



num_pixels_by_intensity = np.bincount(imgs.flat)
print('num_pixels_by_intensity', num_pixels_by_intensity)

inv_resp = np.arange(num_pixels_by_intensity.shape[0], dtype=np.float64)
inv_resp[-1] = -1.0 # Use negative numbers to detect saturation


def opt_irradiance():
    corrected_imgs = inv_resp[imgs] * exposures[:, np.newaxis, np.newaxis]
    times = np.ones_like(corrected_imgs) * (exposures**2)[:, np.newaxis, np.newaxis]

    times[corrected_imgs < 0] = 0
    corrected_imgs[corrected_imgs < 0] = 0

    denom = np.sum(times, axis=0)
    idx = (denom != 0)
    irr = np.sum(corrected_imgs, axis=0)
    irr[idx] /= denom[idx]
    irr[denom == 0] = -1.0
    return irr

def opt_inv_resp():
    generated_imgs = irradiance[np.newaxis, :, :] * exposures[:, np.newaxis, np.newaxis]

    num_pixels_by_intensity = np.bincount(imgs.flat, generated_imgs.flat >= 0)

    generated_imgs[generated_imgs < 0] = 0
    sum_by_intensity = np.bincount(imgs.flat, generated_imgs.flat)

    new_inv_resp = inv_resp

    idx = np.nonzero(num_pixels_by_intensity > 0)
    new_inv_resp[idx] = sum_by_intensity[idx] / num_pixels_by_intensity[idx]
    new_inv_resp[-1] = -1.0 # Use negative numbers to detect saturation
    return new_inv_resp

def print_error():
    generated_imgs = irradiance[np.newaxis, :, :] * exposures[:, np.newaxis, np.newaxis]
    generated_imgs -= inv_resp[imgs]
    generated_imgs[imgs == 255] = 0
    print('Error', np.sum(generated_imgs**2))

for iter in range(5):
    print('Iteration', iter)
    irradiance = opt_irradiance()
    print_error()
    inv_resp = opt_inv_resp()
    print_error()



fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.plot(inv_resp[:-1])
ax1.set(xlabel='Image Intensity', ylabel='Irradiance Value')
ax1.set_title('Inverse Response Function')


ax2.imshow(irradiance)
ax2.set_title('Irradiance Image')
plt.show()
