Source code for raytraverse.lightfield.lightplanekd

# -*- coding: utf-8 -*-
# Copyright (c) 2020 Stephen Wasilewski, HSLU and EPFL
# =======================================================================
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# =======================================================================
import numpy as np
from scipy.interpolate import LinearNDInterpolator

from raytools import io
from raytraverse.evaluate import MetricSet
from raytraverse.lightfield.sets import LightPointSet
from raytraverse.lightfield.lightfield import LightField
from raytraverse.lightpoint.lightpointkd import calc_voronoi_area


[docs] class LightPlaneKD(LightField): """collection of lightpoints with KDtree structure for positional query""" @property def data(self): """LightPointSet""" return self._data @ data.setter def data(self, idx): self._data = LightPointSet(self.scene, self.vecs, idx, self.src, self.pm.name) @property def omega(self): """representative area of each point :getter: Returns array of areas :setter: sets areas :type: np.array """ if self._omega is None: self._omega = calc_voronoi_area(self.vecs, self.pm) return self._omega @omega.setter def omega(self, oga): """wait to calculate area until needed""" self._omega = None
[docs] def evaluate(self, skyvec, points=None, vm=None, metricclass=MetricSet, metrics=None, mask=True, **kwargs): # qidx are the unique query indices and midx are the mapping indices # to restore full results from the qidx results if points is None: qidx = midx = np.arange(len(self.vecs)) else: ridx, d = self.query(points) if mask: if hasattr(self.pm, "mask"): omask = self.pm.mask self.pm.mask = True ridx = ridx[self.pm.in_view(points, False)] self.pm.mask = omask else: ridx = ridx[self.pm.in_view(points, False)] qidx, midx = np.unique(ridx, return_inverse=True) results = [] for qi in qidx: lp = self.data[qi] vol = lp.evaluate(skyvec, vm=vm) if vm is None: vm = lp.vm results.append(metricclass(*vol, vm, metricset=metrics, **kwargs)()) return np.array(results)[midx]
[docs] def make_image(self, outf, vals, res=1024, interp=False, showsample=False): """make an image from precomputed values for every point in LightPlane Parameters ---------- outf: str the file to write vals: np.array shape (len(self.points),) the values computed for each point res: int, optional image resolution (the largest dimension interp: bool, optional apply linear interpolation, points outside convex hull of results fall back to nearest showsample: bool, optionaal color pixel at sample location red """ img, vecs, mask, _, header = self.pm.init_img(res) if interp: xyp = vecs[mask] interp = LinearNDInterpolator(self.vecs[:, 0:2], vals, fill_value=-1) lum = interp(xyp[:, 0], xyp[:, 1]) neg = lum < 0 i, d = self.query(xyp[neg]) lum[neg] = vals[i] img[mask] = lum else: i, d = self.query(vecs[mask]) img[mask] = vals[i] if showsample: img = np.repeat(img[None, ...], 3, 0) img = self.pm.add_vecs_to_img(img, self.vecs, channels=(1, 0, 0)) io.carray2hdr(img, outf, header) else: io.array2hdr(img, outf, header)
[docs] def direct_view(self, res=512, showsample=True, vm=None, area=False, metricclass=MetricSet, metrics=('avglum',), interp=False): """create a summary image of lightplane showing samples and areas""" if area: outf = self._datadir.replace("/", "_") + f"{self.src}_area.hdr" self.make_image(outf, self.omega, res=res, showsample=showsample, interp=False) if metrics is not None: result = self.evaluate(1, vm=vm, metricclass=metricclass, metrics=metrics, scale=1).T for r, m in zip(result, metrics): outf = self._datadir.replace("/", "_") + f"{self.src}_{m}.hdr" self.make_image(outf, r, res=res, showsample=showsample, interp=interp)