Source code for paraview.web.data_converter

from vtkmodules.vtkIOImage import vtkPNGReader
from vtkmodules.vtkCommonCore import vtkFloatArray, vtkUnsignedCharArray
from vtkmodules.vtkCommonDataModel import vtkImageData
from vtkmodules.vtkIOLegacy import vtkDataSetWriter

from vtkmodules.web.camera import *
from vtkmodules.web import iteritems, buffer

import json, os, math, gzip, shutil, array

# -----------------------------------------------------------------------------
# Helper function
# -----------------------------------------------------------------------------

[docs]def getScalarFromRGB(rgb, scalarRange=[-1.0, 1.0]): delta = (scalarRange[1] - scalarRange[0]) / 16777215.0 # 2^24 - 1 => 16,777,215 if rgb[0] != 0 or rgb[1] != 0 or rgb[2] != 0: # Decode encoded value return scalarRange[0] + delta * float(rgb[0]*65536 + rgb[1]*256 + rgb[2] - 1) else: # No value return float('NaN')
[docs]def convertImageToFloat(srcPngImage, destFile, scalarRange=[0.0, 1.0]): reader = vtkPNGReader() reader.SetFileName(srcPngImage) reader.Update() rgbArray = reader.GetOutput().GetPointData().GetArray(0) stackSize = rgbArray.GetNumberOfTuples() size = reader.GetOutput().GetDimensions() outputArray = vtkFloatArray() outputArray.SetNumberOfComponents(1) outputArray.SetNumberOfTuples(stackSize) for idx in range(stackSize): outputArray.SetTuple1( idx, getScalarFromRGB(rgbArray.GetTuple(idx), scalarRange) ) # Write float file with open(destFile, 'wb') as f: f.write(buffer(outputArray)) return size
[docs]def convertRGBArrayToFloatArray(rgbArray, scalarRange=[0.0, 1.0]): linearSize = rgbArray.GetNumberOfTuples() outputArray = vtkFloatArray() outputArray.SetNumberOfComponents(1) outputArray.SetNumberOfTuples(linearSize) for idx in range(linearSize): outputArray.SetTuple1( idx, getScalarFromRGB(rgbArray.GetTuple(idx), scalarRange) ) return outputArray
# ----------------------------------------------------------------------------- # Composite.json To order.array # -----------------------------------------------------------------------------
[docs]class CompositeJSON(object): def __init__(self, numberOfLayers): self.nbLayers = numberOfLayers self.encoding = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
[docs] def load(self, file): with open(file, "r") as f: composite = json.load(f) self.width = composite["dimensions"][0] self.height = composite["dimensions"][1] self.pixels = composite["pixel-order"].split('+') self.imageSize = self.width * self.height self.stackSize = self.imageSize * self.nbLayers
[docs] def getImageSize(self): return self.imageSize
[docs] def getStackSize(self): return self.stackSize
[docs] def writeOrderSprite(self,path): ds = vtkImageData() ds.SetDimensions(self.width, self.height, self.nbLayers) ds.GetPointData().AddArray(self.getSortedOrderArray()) writer = vtkDataSetWriter() writer.SetInputData(ds) writer.SetFileName(path) writer.Update()
[docs] def getSortedOrderArray(self): sortedOrder = vtkUnsignedCharArray() sortedOrder.SetName('layerIdx'); sortedOrder.SetNumberOfTuples(self.stackSize) # Reset content for idx in range(self.stackSize): sortedOrder.SetValue(idx, 255) idx = 0 for pixel in self.pixels: x = (idx % self.width) y = (idx / self.width) flipYIdx = self.width * (self.height - y - 1) + x if '@' in pixel: idx += int(pixel[1:]) else: # Need to decode the order layerIdx = 0 for layer in pixel: sortedOrder.SetValue(flipYIdx + self.imageSize * layerIdx, self.encoding.index(layer)) layerIdx += 1 # Move to next pixel idx += 1 return sortedOrder
# ----------------------------------------------------------------------------- # Composite Sprite to Sorted Composite Dataset Builder # -----------------------------------------------------------------------------
[docs]class ConvertCompositeSpriteToSortedStack(object): def __init__(self, directory): self.basePath = directory self.layers = [] self.data = [] self.imageReader = vtkPNGReader() # Load JSON metadata with open(os.path.join(directory, "config.json"), "r") as f: self.config = json.load(f) self.nbLayers = len(self.config['scene']) while len(self.layers) < self.nbLayers: self.layers.append({}) with open(os.path.join(directory, "index.json"), "r") as f: self.info = json.load(f) with open(os.path.join(directory, "offset.json"), "r") as f: offsets = json.load(f) for key, value in iteritems(offsets): meta = key.split('|') if len(meta) == 2: self.layers[int(meta[0])][meta[1]] = value elif meta[1] in self.layers[int(meta[0])]: self.layers[int(meta[0])][meta[1]][int(meta[2])] = value else: self.layers[int(meta[0])][meta[1]] = [value, value, value] self.composite = CompositeJSON(len(self.layers))
[docs] def listData(self): return self.data
[docs] def convert(self): for root, dirs, files in os.walk(self.basePath): if 'rgb.png' in files: print ('Process', root) self.processDirectory(root)
[docs] def processDirectory(self, directory): self.imageReader.SetFileName(os.path.join(directory, 'rgb.png')) self.imageReader.Update() rgbArray = self.imageReader.GetOutput().GetPointData().GetArray(0) self.composite.load(os.path.join(directory, 'composite.json')) orderArray = self.composite.getSortedOrderArray() imageSize = self.composite.getImageSize() stackSize = self.composite.getStackSize() # Write order (sorted order way) with open(os.path.join(directory, 'order.uint8'), 'wb') as f: f.write(buffer(orderArray)) self.data.append({'name': 'order', 'type': 'array', 'fileName': '/order.uint8'}) # Encode Normals (sorted order way) if 'normal' in self.layers[0]: sortedNormal = vtkUnsignedCharArray() sortedNormal.SetNumberOfComponents(3) # x,y,z sortedNormal.SetNumberOfTuples(stackSize) # Get Camera orientation and rotation information camDir = [0,0,0] worldUp = [0,0,0] with open(os.path.join(directory, "camera.json"), "r") as f: camera = json.load(f) camDir = normalize([ camera['position'][i] - camera['focalPoint'][i] for i in range(3) ]) worldUp = normalize(camera['viewUp']) # [ camRight, camUp, camDir ] will be our new orthonormal basis for normals camRight = vectProduct(camDir, worldUp) camUp = vectProduct(camRight, camDir) # Tmp structure to capture (x,y,z) normal normalByLayer = vtkFloatArray() normalByLayer.SetNumberOfComponents(3) normalByLayer.SetNumberOfTuples(stackSize) # Capture all layer normals layerIdx = 0 zPosCount = 0 zNegCount = 0 for layer in self.layers: normalOffset = layer['normal'] for idx in range(imageSize): normalByLayer.SetTuple3( layerIdx * imageSize + idx, getScalarFromRGB(rgbArray.GetTuple(idx + normalOffset[0] * imageSize)), getScalarFromRGB(rgbArray.GetTuple(idx + normalOffset[1] * imageSize)), getScalarFromRGB(rgbArray.GetTuple(idx + normalOffset[2] * imageSize)) ) # Re-orient normal to be view based vect = normalByLayer.GetTuple3(layerIdx * imageSize + idx) if not math.isnan(vect[0]): # Express normal in new basis we computed above rVect = normalize([ -dotProduct(vect, camRight), dotProduct(vect, camUp), dotProduct(vect, camDir) ]) # Need to reverse vector ? if rVect[2] < 0: normalByLayer.SetTuple3(layerIdx * imageSize + idx, -rVect[0], -rVect[1], -rVect[2]) else: normalByLayer.SetTuple3(layerIdx * imageSize + idx, rVect[0], rVect[1], rVect[2]) layerIdx += 1 # Sort normals and encode them as 3 bytes ( -1 < xy < 1 | 0 < z < 1) for idx in range(stackSize): layerIdx = int(orderArray.GetValue(idx)) if layerIdx == 255: # No normal => same as view direction sortedNormal.SetTuple3(idx, 128, 128, 255) else: offset = layerIdx * imageSize imageIdx = idx % imageSize vect = normalByLayer.GetTuple3(imageIdx + offset) if not math.isnan(vect[0]) and not math.isnan(vect[1]) and not math.isnan(vect[2]): sortedNormal.SetTuple3(idx, int(127.5 * (vect[0] + 1)), int(127.5 * (vect[1] + 1)), int(255 * vect[2])) else: print ('WARNING: encountered NaN in normal of layer ',layerIdx,': [',vect[0],',',vect[1],',',vect[2],']') sortedNormal.SetTuple3(idx, 128, 128, 255) # Write the sorted data with open(os.path.join(directory, 'normal.uint8'), 'wb') as f: f.write(buffer(sortedNormal)) self.data.append({'name': 'normal', 'type': 'array', 'fileName': '/normal.uint8', 'categories': ['normal']}) # Encode Intensity (sorted order way) if 'intensity' in self.layers[0]: intensityOffsets = [] sortedIntensity = vtkUnsignedCharArray() sortedIntensity.SetNumberOfTuples(stackSize) for layer in self.layers: intensityOffsets.append(layer['intensity']) for idx in range(stackSize): layerIdx = int(orderArray.GetValue(idx)) if layerIdx == 255: sortedIntensity.SetValue(idx, 255) else: offset = 3 * intensityOffsets[layerIdx] * imageSize imageIdx = idx % imageSize sortedIntensity.SetValue(idx, rgbArray.GetValue(imageIdx * 3 + offset)) with open(os.path.join(directory, 'intensity.uint8'), 'wb') as f: f.write(buffer(sortedIntensity)) self.data.append({'name': 'intensity', 'type': 'array', 'fileName': '/intensity.uint8', 'categories': ['intensity']}) # Encode Each layer Scalar layerIdx = 0 for layer in self.layers: for scalar in layer: if scalar not in ['intensity', 'normal']: offset = imageSize * layer[scalar] scalarRange = self.config['scene'][layerIdx]['colors'][scalar]['range'] delta = (scalarRange[1] - scalarRange[0]) / 16777215.0 # 2^24 - 1 => 16,777,215 scalarArray = vtkFloatArray() scalarArray.SetNumberOfTuples(imageSize) for idx in range(imageSize): rgb = rgbArray.GetTuple(idx + offset) if rgb[0] != 0 or rgb[1] != 0 or rgb[2] != 0: # Decode encoded value value = scalarRange[0] + delta * float(rgb[0]*65536 + rgb[1]*256 + rgb[2] - 1) scalarArray.SetValue(idx, value) else: # No value scalarArray.SetValue(idx, float('NaN')) with open(os.path.join(directory, '%d_%s.float32' % (layerIdx, scalar)), 'wb') as f: f.write(buffer(scalarArray)) self.data.append({'name': '%d_%s' % (layerIdx, scalar), 'type': 'array', 'fileName': '/%d_%s.float32' % (layerIdx, scalar), 'categories': ['%d_%s' % (layerIdx, scalar)]}) layerIdx += 1
# ----------------------------------------------------------------------------- # Composite Sprite to Sorted Composite Dataset Builder # -----------------------------------------------------------------------------
[docs]class ConvertCompositeDataToSortedStack(object): def __init__(self, directory): self.basePath = directory self.layers = [] self.data = [] self.imageReader = vtkPNGReader() # Load JSON metadata with open(os.path.join(directory, "config.json"), "r") as f: self.config = json.load(f) self.nbLayers = len(self.config['scene']) while len(self.layers) < self.nbLayers: self.layers.append({}) with open(os.path.join(directory, "index.json"), "r") as f: self.info = json.load(f)
[docs] def listData(self): return self.data
[docs] def convert(self): for root, dirs, files in os.walk(self.basePath): if 'depth_0.float32' in files: print ('Process', root) self.processDirectory(root)
[docs] def processDirectory(self, directory): # Load depth depthStack = [] imageSize = self.config['size'] linearSize = imageSize[0] * imageSize[1] nbLayers = len(self.layers) stackSize = nbLayers * linearSize layerList = range(nbLayers) for layerIdx in layerList: with open(os.path.join(directory, 'depth_%d.float32' % layerIdx), "rb") as f: a = array.array('f') a.fromfile(f, linearSize) depthStack.append(a) orderArray = vtkUnsignedCharArray() orderArray.SetName('layerIdx'); orderArray.SetNumberOfComponents(1) orderArray.SetNumberOfTuples(stackSize) pixelSorter = [(i, i) for i in layerList] for pixelId in range(linearSize): # Fill pixelSorter for layerIdx in layerList: if depthStack[layerIdx][pixelId] < 1.0: pixelSorter[layerIdx] = (layerIdx, depthStack[layerIdx][pixelId]) else: pixelSorter[layerIdx] = (255, 1.0) # Sort pixel layers pixelSorter.sort(key=lambda tup: tup[1]) # Fill sortedOrder array for layerIdx in layerList: orderArray.SetValue(layerIdx * linearSize + pixelId, pixelSorter[layerIdx][0]) # Write order (sorted order way) with open(os.path.join(directory, 'order.uint8'), 'wb') as f: f.write(buffer(orderArray)) self.data.append({'name': 'order', 'type': 'array', 'fileName': '/order.uint8'}) # Remove depth files for layerIdx in layerList: os.remove(os.path.join(directory, 'depth_%d.float32' % layerIdx)) # Encode Normals (sorted order way) if 'normal' in self.config['light']: sortedNormal = vtkUnsignedCharArray() sortedNormal.SetNumberOfComponents(3) # x,y,z sortedNormal.SetNumberOfTuples(stackSize) # Get Camera orientation and rotation information camDir = [0,0,0] worldUp = [0,0,0] with open(os.path.join(directory, "camera.json"), "r") as f: camera = json.load(f) camDir = normalize([ camera['position'][i] - camera['focalPoint'][i] for i in range(3) ]) worldUp = normalize(camera['viewUp']) # [ camRight, camUp, camDir ] will be our new orthonormal basis for normals camRight = vectProduct(camDir, worldUp) camUp = vectProduct(camRight, camDir) # Tmp structure to capture (x,y,z) normal normalByLayer = vtkFloatArray() normalByLayer.SetNumberOfComponents(3) normalByLayer.SetNumberOfTuples(stackSize) # Capture all layer normals zPosCount = 0 zNegCount = 0 for layerIdx in layerList: # Load normal(x,y,z) from current layer normalLayer = [] for comp in [0, 1, 2]: with open(os.path.join(directory, 'normal_%d_%d.float32' % (layerIdx, comp)), "rb") as f: a = array.array('f') a.fromfile(f, linearSize) normalLayer.append(a) # Store normal inside vtkArray offset = layerIdx * linearSize for idx in range(linearSize): normalByLayer.SetTuple3( idx + offset, normalLayer[0][idx], normalLayer[1][idx], normalLayer[2][idx] ) # Re-orient normal to be view based vect = normalByLayer.GetTuple3(layerIdx * linearSize + idx) if not math.isnan(vect[0]): # Express normal in new basis we computed above rVect = normalize([ -dotProduct(vect, camRight), dotProduct(vect, camUp), dotProduct(vect, camDir) ]) # Need to reverse vector ? if rVect[2] < 0: normalByLayer.SetTuple3(layerIdx * linearSize + idx, -rVect[0], -rVect[1], -rVect[2]) else: normalByLayer.SetTuple3(layerIdx * linearSize + idx, rVect[0], rVect[1], rVect[2]) # Sort normals and encode them as 3 bytes ( -1 < xy < 1 | 0 < z < 1) for idx in range(stackSize): layerIdx = int(orderArray.GetValue(idx)) if layerIdx == 255: # No normal => same as view direction sortedNormal.SetTuple3(idx, 128, 128, 255) else: offset = layerIdx * linearSize imageIdx = idx % linearSize vect = normalByLayer.GetTuple3(imageIdx + offset) if not math.isnan(vect[0]) and not math.isnan(vect[1]) and not math.isnan(vect[2]): sortedNormal.SetTuple3(idx, int(127.5 * (vect[0] + 1)), int(127.5 * (vect[1] + 1)), int(255 * vect[2])) else: print ('WARNING: encountered NaN in normal of layer ',layerIdx,': [',vect[0],',',vect[1],',',vect[2],']') sortedNormal.SetTuple3(idx, 128, 128, 255) # Write the sorted data with open(os.path.join(directory, 'normal.uint8'), 'wb') as f: f.write(buffer(sortedNormal)) self.data.append({'name': 'normal', 'type': 'array', 'fileName': '/normal.uint8', 'categories': ['normal']}) # Remove depth files for layerIdx in layerList: os.remove(os.path.join(directory, 'normal_%d_%d.float32' % (layerIdx, 0))) os.remove(os.path.join(directory, 'normal_%d_%d.float32' % (layerIdx, 1))) os.remove(os.path.join(directory, 'normal_%d_%d.float32' % (layerIdx, 2))) # Encode Intensity (sorted order way) if 'intensity' in self.config['light']: sortedIntensity = vtkUnsignedCharArray() sortedIntensity.SetNumberOfTuples(stackSize) intensityLayers = [] for layerIdx in layerList: with open(os.path.join(directory, 'intensity_%d.uint8' % layerIdx), "rb") as f: a = array.array('B') a.fromfile(f, linearSize) intensityLayers.append(a) for idx in range(stackSize): layerIdx = int(orderArray.GetValue(idx)) if layerIdx == 255: sortedIntensity.SetValue(idx, 255) else: imageIdx = idx % linearSize sortedIntensity.SetValue(idx, intensityLayers[layerIdx][imageIdx]) with open(os.path.join(directory, 'intensity.uint8'), 'wb') as f: f.write(buffer(sortedIntensity)) self.data.append({'name': 'intensity', 'type': 'array', 'fileName': '/intensity.uint8', 'categories': ['intensity']}) # Remove depth files for layerIdx in layerList: os.remove(os.path.join(directory, 'intensity_%d.uint8' % layerIdx))