123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- 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 normalize, vectProduct, dotProduct
- from vtkmodules.web import iteritems
- import json, os, math, array
- # -----------------------------------------------------------------------------
- # Helper function
- # -----------------------------------------------------------------------------
- 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")
- 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(memoryview(outputArray))
- return size
- 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
- # -----------------------------------------------------------------------------
- class CompositeJSON(object):
- def __init__(self, numberOfLayers):
- self.nbLayers = numberOfLayers
- self.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- 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
- def getImageSize(self):
- return self.imageSize
- def getStackSize(self):
- return self.stackSize
- 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()
- 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
- # -----------------------------------------------------------------------------
- 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))
- def listData(self):
- return self.data
- def convert(self):
- for root, dirs, files in os.walk(self.basePath):
- if "rgb.png" in files:
- print("Process", root)
- self.processDirectory(root)
- 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(memoryview(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(memoryview(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(memoryview(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(memoryview(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
- # -----------------------------------------------------------------------------
- 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)
- def listData(self):
- return self.data
- def convert(self):
- for root, dirs, files in os.walk(self.basePath):
- if "depth_0.float32" in files:
- print("Process", root)
- self.processDirectory(root)
- 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(memoryview(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(memoryview(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(memoryview(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))
|