# Path: bssrdf_estimate/tools/vector3d.py
# class Vector3D(object):
#     def __init__(self, x=0.0, y=0.0, z=0.0):
#         self._x = x
#         self._y = y
#         self._z = z
# 
#     @property
#     def x(self):
#         return self._x
# 
#     @property
#     def y(self):
#         return self._y
# 
#     @property
#     def z(self):
#         return self._z
# 
#     def dot(self, v):
#         return self._x * v.x + self._y * v.y + self._z * v.z
# 
#     def norm(self):
#         return math.sqrt(self.dot(self))
# 
#     def normalized(self):
#         return self / self.norm()
# 
#     def __neg__(self):
#         return Vector3D(-self._x, -self._y, -self._z)
# 
#     def __add__(self, v):
#         return Vector3D(self._x + v.x, self._y + v.y, self._z + v.z)
# 
#     def __sub__(self, v):
#         return Vector3D(self._x - v.x, self._y - v.y, self._z - v.z)
# 
#     def __mul__(self, s):
#         return Vector3D(self._x * s, self._y * s, self._z * s)
# 
#     def __rmul__(self, s):
#         return self.__mul__(s)
# 
#     def __truediv__(self, s):
#         return self * (1.0 / s)
# 
# Path: bssrdf_estimate/tools/diffuse_bssrdf.py
# class DiffuseBSSRDF(object):
#     def __init__(self):
#         self.distances = None
#         self.colors    = None
# 
#     @staticmethod
#     def load(filename):
#         ret = DiffuseBSSRDF()
#         with open(filename, 'rb') as fp:
#             ret.colors = [None] * 3
#             sz = struct.unpack('i', fp.read(4))[0]
#             ret.distances = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#             ret.colors = np.zeros((sz,3), dtype='float32')
#             ret.colors[:,0] = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#             ret.colors[:,1] = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#             ret.colors[:,2] = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#         return ret
# 
#     def save(self, filename):
#         with open(filename, 'wb') as fp:
#             sz = self.distances.size
#             fp.write(struct.pack('i', sz))
#             fp.write(struct.pack('f' * sz, *self.distances))
#             fp.write(struct.pack('f' * sz, *self.colors[:,0]))
#             fp.write(struct.pack('f' * sz, *self.colors[:,1]))
#             fp.write(struct.pack('f' * sz, *self.colors[:,2]))
# 
#     def scaled(self, sc):
#         ret = DiffuseBSSRDF()
#         ret.distances = self.distances / sc
#         ret.colors    = self.colors / (sc * sc)
#         return ret


import math
import struct
import numpy as np
from itertools import product
from random import *
from .vector3d import Vector3D
from .diffuse_bssrdf import DiffuseBSSRDF
from .solvers import *
        self.back_E  = 0.0

class BSSRDFEstimator(object):
    def __init__(self):
        pass

    def process(self, image, mask, depth, lights):
        width = image.shape[1]
        height = image.shape[0]

        self.pixels = self.extract_pixel_constraints(image, mask, depth)
        self.translucent_shadowmap(lights)

        # Build up system of linear equations
        inv_pi = 1.0 / math.pi
        num_pixels = len(self.pixels)
        dscale = 1.0 / max(width, height)
        A = np.zeros((num_pixels, NUM_BASES))
        for i in range(num_pixels):
            for k in range(num_pixels):
                # Front side
                p_f = self.pixels[k].pos
                r_f = (self.pixels[i].pos - p_f).norm()
                j_f = int(r_f * dscale)

                if j_f >= 0 and j_f < NUM_BASES:
                    delta_A = 1.0 / (abs(self.pixels[i].normal.z) + EPS)
                    A[i,j_f] += self.pixels[k].front_E * delta_A

                # Back side

# Path: bssrdf_estimate/tools/vector3d.py
# class Vector3D(object):
#     def __init__(self, x=0.0, y=0.0, z=0.0):
#         self._x = x
#         self._y = y
#         self._z = z
# 
#     @property
#     def x(self):
#         return self._x
# 
#     @property
#     def y(self):
#         return self._y
# 
#     @property
#     def z(self):
#         return self._z
# 
#     def dot(self, v):
#         return self._x * v.x + self._y * v.y + self._z * v.z
# 
#     def norm(self):
#         return math.sqrt(self.dot(self))
# 
#     def normalized(self):
#         return self / self.norm()
# 
#     def __neg__(self):
#         return Vector3D(-self._x, -self._y, -self._z)
# 
#     def __add__(self, v):
#         return Vector3D(self._x + v.x, self._y + v.y, self._z + v.z)
# 
#     def __sub__(self, v):
#         return Vector3D(self._x - v.x, self._y - v.y, self._z - v.z)
# 
#     def __mul__(self, s):
#         return Vector3D(self._x * s, self._y * s, self._z * s)
# 
#     def __rmul__(self, s):
#         return self.__mul__(s)
# 
#     def __truediv__(self, s):
#         return self * (1.0 / s)
# 
# Path: bssrdf_estimate/tools/diffuse_bssrdf.py
# class DiffuseBSSRDF(object):
#     def __init__(self):
#         self.distances = None
#         self.colors    = None
# 
#     @staticmethod
#     def load(filename):
#         ret = DiffuseBSSRDF()
#         with open(filename, 'rb') as fp:
#             ret.colors = [None] * 3
#             sz = struct.unpack('i', fp.read(4))[0]
#             ret.distances = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#             ret.colors = np.zeros((sz,3), dtype='float32')
#             ret.colors[:,0] = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#             ret.colors[:,1] = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#             ret.colors[:,2] = np.array(struct.unpack('f' * sz, fp.read(4 * sz)), dtype='float32')
#         return ret
# 
#     def save(self, filename):
#         with open(filename, 'wb') as fp:
#             sz = self.distances.size
#             fp.write(struct.pack('i', sz))
#             fp.write(struct.pack('f' * sz, *self.distances))
#             fp.write(struct.pack('f' * sz, *self.colors[:,0]))
#             fp.write(struct.pack('f' * sz, *self.colors[:,1]))
#             fp.write(struct.pack('f' * sz, *self.colors[:,2]))
# 
#     def scaled(self, sc):
#         ret = DiffuseBSSRDF()
#         ret.distances = self.distances / sc
#         ret.colors    = self.colors / (sc * sc)
#         return ret


# Path: bssrdf_estimate/tools/bssrdf_estimator.py
import math
import struct
import numpy as np
from itertools import product
from random import *
from .vector3d import Vector3D
from .diffuse_bssrdf import DiffuseBSSRDF
from .solvers import *

        self.back_E  = 0.0

class BSSRDFEstimator(object):
    def __init__(self):
        pass

    def process(self, image, mask, depth, lights):
        width = image.shape[1]
        height = image.shape[0]

        self.pixels = self.extract_pixel_constraints(image, mask, depth)
        self.translucent_shadowmap(lights)

        # Build up system of linear equations
        inv_pi = 1.0 / math.pi
        num_pixels = len(self.pixels)
        dscale = 1.0 / max(width, height)
        A = np.zeros((num_pixels, NUM_BASES))
        for i in range(num_pixels):
            for k in range(num_pixels):
                # Front side
                p_f = self.pixels[k].pos
                r_f = (self.pixels[i].pos - p_f).norm()
                j_f = int(r_f * dscale)

                if j_f >= 0 and j_f < NUM_BASES:
                    delta_A = 1.0 / (abs(self.pixels[i].normal.z) + EPS)
                    A[i,j_f] += self.pixels[k].front_E * delta_A

                # Back side

                p_b = Vector3D(p_f.x, p_f.y, 0.0)