Loading src/main/java/com/elphel/imagej/common/MultiThreading.java 0 → 100644 +37 −0 Original line number Diff line number Diff line package com.elphel.imagej.common; public class MultiThreading { public static int THREADS_MAX = 100; /* Create a Thread[] array as large as the number of processors available. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ public static Thread[] newThreadArray() { return newThreadArray (THREADS_MAX); } public static Thread[] newThreadArray(int maxCPUs) { int n_cpus = Runtime.getRuntime().availableProcessors(); if (n_cpus>maxCPUs)n_cpus=maxCPUs; return new Thread[n_cpus]; } /* Start all given threads and wait on each of them until all are done. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ public static void startAndJoin(Thread[] threads) // USED in lwir { for (int ithread = 0; ithread < threads.length; ++ithread) { threads[ithread].setPriority(Thread.NORM_PRIORITY); threads[ithread].start(); } try { for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread].join(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } } } src/main/java/com/elphel/imagej/tileprocessor/MultiLayerDSL.java 0 → 100644 +28 −0 Original line number Diff line number Diff line /** ** MultiLayerDSL - Handle multilayer/multivalue disparity/strength/lma values ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** TexturedModel.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor; public class MultiLayerDSL { } src/main/java/com/elphel/imagej/tileprocessor/lwoc/LwocLeaf.java 0 → 100644 +65 −0 Original line number Diff line number Diff line /** ** ** LwocLeaf.java - Octree leaf of the world built from LWIR16 images ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** LwocLeaf.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor.lwoc; import java.util.ArrayList; public class LwocLeaf { ArrayList <LwocMesh> meshes; ArrayList <LwocMesh> mesh_centers; ArrayList <LwocScene> scenes; public LwocLeaf() { meshes = new ArrayList <LwocMesh>(); // all meshes BB intersecting this node mesh_centers = new ArrayList <LwocMesh>(); // mesh centers in this node scenes = new ArrayList <LwocScene>(); // cameras located in this node } public void addScene(LwocScene scene) { scenes.add(scene); } public void addMeshCenter(LwocMesh mesh) { mesh_centers.add(mesh); } public void addMesh(LwocMesh mesh, boolean check_existed) { if (!check_existed || meshes.contains(mesh)) { meshes.add(mesh); } } public ArrayList <LwocMesh> getMeshes(){ return meshes; } public ArrayList <LwocMesh> getMeshCenters(){ return mesh_centers; } public ArrayList <LwocScene> getScenes(){ return scenes; } } src/main/java/com/elphel/imagej/tileprocessor/lwoc/LwocMesh.java 0 → 100644 +61 −0 Original line number Diff line number Diff line /** ** ** LwocMesh.java - Single mesh of the world built from LWIR16 images ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** LwocMesh.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor.lwoc; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; public class LwocMesh { static AtomicInteger MESH_ID = new AtomicInteger(); static ArrayList<LwocMesh> LWOC_MESHES; int id; // assign unique ID String stimestamp; // mesh is always referenced to a single scene double [] world_xyz; // world coordinates of the center double [] hdims_xyz; // world bounding box half-dimensions along x,y,z public static void resetMeshes() { MESH_ID.set(0); LWOC_MESHES = new ArrayList<LwocMesh>(); } // maybe use mesh properties instead of id? public boolean equals (LwocMesh other_mesh) { return id == other_mesh.id; } public LwocMesh( String stimestamp ) { this.stimestamp = stimestamp; id = MESH_ID.getAndIncrement(); LWOC_MESHES.add(this); } public double [] getCenter() { return world_xyz; } public double [] getDims() { return hdims_xyz; } } src/main/java/com/elphel/imagej/tileprocessor/lwoc/LwocOctree.java 0 → 100644 +507 −0 Original line number Diff line number Diff line /** ** ** LwocOctree.java - Octree of the world built from LWIR16 images ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** LwocOctree.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor.lwoc; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; import com.elphel.imagej.common.MultiThreading; public class LwocOctree { public static final int NUM_CHILDREN = 8; public static double MIN_HSIZE = 0.3; // meter - do not subdivide more // can not be used as intersecting meshes' bb will not decrease number after splitting public static int MAX_MESH_CENTERS = 10; // maximal number of meshes in a leaf; public static int MAX_CAMERAS = 10; // maximal number of cameras in a leaf; public static LwocOctree lwoc_root = null; static ArrayList<LwocOctree> LWOK_OCTREE = new ArrayList<LwocOctree>(); static AtomicInteger OCTREE_ID = new AtomicInteger(); // synchronized lists to handle thread-unsafe operations: growing world and splitting leaves /// static List<LwocScene> pendingScenes; // synchronized - add not-yet-added scenes pending growing world /// static List<LwocMesh> pendingMeshes; // synchronized - add not-yet-added meshes pending growing world /// static List<LwocOctree> pendingLeafNodes;// synchronized - leaf nodes needed to be split static AtomicInteger numPendingScenes; static AtomicInteger numPendingMeshes; static AtomicInteger numPendingLeafNodes; int id; // assign unique ID LwocOctree parent; LwocOctree [] children; LwocLeaf leaf; // double [] center; // x-center, y-center, z-center double hsize; // just for testing // List<List<LwocScene>> group = new ArrayList<List<LwocScene>>(4); /** * Initialize Octree data and set initial world node. It is possible to grow * world as needed later. * @param center world center: x,y,z in meters. Usually {0,0,0} * @param world_hsize Half linear dimension of the world */ public static void initOctree(double [] center, double world_hsize) { LwocMesh.resetMeshes(); LwocScene.resetScenes(); resetPending(); LWOK_OCTREE = new ArrayList<LwocOctree>(); // Add a single leaf node lwoc_root = new LwocOctree(null, center, world_hsize); lwoc_root.leaf = new LwocLeaf(); } /** * Synchronized lists of pending scenes, meshes and nodes are used to delay modification of * the world octree and its elements from the multithreaded environment. These lists should * be tested after multithreaded methods that potentially can modify the world and then * processes by a single-thread method. */ public static void resetPending() { numPendingScenes.set(0); // = Collections.synchronizedList(new ArrayList<LwocScene>()); numPendingMeshes.set(0); // = Collections.synchronizedList(new ArrayList<LwocMesh>()); numPendingLeafNodes.set(0); // = Collections.synchronizedList(new ArrayList<LwocOctree>()); } // use per-thread lists and then combine them avoiding synchronizations? //Pass list to any function that may grow it /** * Test if it is leaf node * @return True if it is a leaf node */ public boolean isLeaf() { return children == null; } /** * Check if the world needs growing to accommodate new scenes or meshes * @return if world needs growing. */ public static boolean pendingGrow() { // return !pendingScenes.isEmpty() || !pendingMeshes.isEmpty(); return (numPendingScenes.get() + numPendingMeshes.get()) > 0; } /** * Check if any leaf nodes need splitting * @return if any leaf nodes require splitting */ public static boolean pendingSplit() { return numPendingLeafNodes.get() > 0; } /** * LwocOctree constructor * @param parent Parent node * @param xyz position of the node center in meters * @param hsize Node half size - each of the X,Y,Z coordinates of the internal * points are limited within +/-hsize from the node center */ public LwocOctree (LwocOctree parent, double [] xyz, double hsize) { id = OCTREE_ID.getAndIncrement(); this.parent = parent; this.hsize = hsize; LWOK_OCTREE.add(this); } /** * Find the leaf node that includes specified point. Thread safe. * @param xyz X, Y, and Z coordinates of the point * @return Octree node that includes the point or null if the point * is outside the root node (the whole world) */ public static LwocOctree getLeafNode( // if null - needs growing world double [] xyz) { // thread safe if (!lwoc_root.contains(xyz)) return null; // needs growing world LwocOctree node = lwoc_root; while (lwoc_root.children != null) { int indx = 0; for (int dm = 0; dm < 3; dm++) { if (xyz[dm] >= lwoc_root.center[dm] ) { indx += 1 << dm; } } node = node.children[indx]; } return node; } /** * Add scene to the world in a thread-safe mode, or put it to pending list if the world * needs growing. * @param scene Scene to add. * @param pendingScenes per thread list of scenes to be combined after merging threads * @param pendingLeafNodes per thread list of nodes to be combined after merging threads * @return An octree node where scene is added or null if the world needs growing */ public static LwocOctree addScene( LwocScene scene, ArrayList<LwocScene> pendingScenes, ArrayList<LwocOctree> pendingLeafNodes ) {// returns null and adds to pendingScenes if needs growing LwocOctree node = getLeafNode(scene.getCameraXYZ()); if (node == null) { if (pendingScenes != null) { pendingScenes.add(scene); } } else { node.leaf.addScene(scene); // Check if leaf node requires splitting if (node.leaf.getScenes().size() > MAX_CAMERAS) { if (node.hsize > MIN_HSIZE) { pendingLeafNodes.add(node); } } } return node; } /** * Tend to pending scenes list. Not thread-safe, should be run in a single-thread mode. * @param pendingScenes combined list of scenes to be added growing world (merged from per-thread ones) * @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one) */ public static void tendPendingScenes( ArrayList<LwocScene> pendingScenes, ArrayList<LwocOctree> pendingLeafNodes ) { if (pendingScenes.isEmpty()) { return; // nothing to do } else { for (LwocScene scene: pendingScenes) { growXYZ(scene.getCameraXYZ()); LwocOctree node= addScene( // should not be null scene, pendingScenes, pendingLeafNodes); assert node != null : "addScene() should not fail after growing world"; } } } /** * Tend to pending meshes list. Not thread-safe, should be run in a single-thread mode. * @param check_existed do not add duplicate meshes * @param pendingMeshes combined list of meshes to be added (merged from per-thread ones). Will only be read. * @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one). May grow. */ public static void tendPendingMeshCenters( boolean check_existed, ArrayList<LwocMesh> pendingMeshes, // will read ArrayList<LwocOctree> pendingLeafNodes // may add to ) { if (pendingMeshes.isEmpty()) { return; // nothing to do } else { for (LwocMesh mesh: pendingMeshes) { double [] center = mesh.getCenter(); double [] dims = mesh.getDims(); double [] corner_xyz = center.clone(); for (int dm = 0; dm < center.length; dm++) { corner_xyz[dm] += dims[dm]; growXYZ(corner_xyz); corner_xyz[dm] -= 2*dims[dm]; growXYZ(corner_xyz); corner_xyz[dm] += dims[dm]; } boolean ok= addMeshCenter( // should not be null mesh, check_existed, null, // pendingMeshes, pendingLeafNodes); assert ok : "addMeshCenter() should not fail after growing world"; // int num_added = lwoc_root.addMesh( mesh, check_existed); } } } public static void tendPendingLeafNodes( ArrayList<LwocOctree> pendingLeafNodesIn) { // remove any possible duplicates final ArrayList<LwocOctree> pendingLeafNodes = new ArrayList<LwocOctree>(); for (LwocOctree node:pendingLeafNodesIn) { if (!pendingLeafNodes.contains(node)) { pendingLeafNodes.add(node); } } // run splitting multithreaded final Thread[] threads = MultiThreading.newThreadArray(); final AtomicInteger ai = new AtomicInteger(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { for (int indx_node = ai.getAndIncrement(); indx_node < pendingLeafNodes.size(); indx_node = ai.getAndIncrement()) { LwocOctree node = pendingLeafNodes.get(indx_node); // Split recursively until satisfied conditions } // end of tile } }; } MultiThreading.startAndJoin(threads); ai.set(0); } // Split recursively until satisfied conditions public int splitNode( double min_hsize, int max_mesh_centers, int max_cameras ) { if ((hsize <= min_hsize) || (leaf == null)) { return 0; } if ((leaf.getScenes().size() <= max_cameras) && (leaf.getMeshCenters().size() <= max_mesh_centers)) { return 0; } // actually need splitting int num_added = NUM_CHILDREN - 1; children = new LwocOctree[NUM_CHILDREN]; double new_hsize = hsize/2; for (int nchild = 0; nchild < children.length; nchild++) { double [] xyz = center.clone(); for (int dm = 0; dm < center.length; dm++) { xyz[dm] +=new_hsize * ((((nchild >> dm) & 1) > 0)? 1 : -1); } children[nchild] = new LwocOctree( this, xyz, new_hsize); LwocLeaf new_leaf = new LwocLeaf(); children[nchild].leaf = new_leaf; } ArrayList <LwocMesh> meshes = leaf.getMeshes(); // distribute meshes between 8 children ArrayList <LwocMesh> mesh_centers = leaf.getMeshCenters(); // distribute mesh_centers between 8 children ArrayList <LwocScene> scenes = leaf.getScenes(); // distribute scnes (cameras) between 8 children // delete old leaf (if it is in a list) and its lists - no need leaf = null; for (int nchild = 0; nchild < children.length; nchild++) { num_added += children[nchild].splitNode( min_hsize, max_mesh_centers, max_cameras); } return num_added; } // not thread safe // grow world to include xyz (all 8 corners for a bounding box of as mesh /** * Grow the world to include specified point Will repeat growing twice (in each direction) * until the specified point gets inside. * @param xyz The point to be included in the world. */ public static void growXYZ(double [] xyz) { while (!lwoc_root.contains(xyz)) {// already fits, do not grow // grow once int indx=0; //direction to grow double [] new_center = new double[3]; for (int dm = 0; dm < new_center.length; dm++) { if (xyz[dm] >= lwoc_root.center[dm] ) { indx += 1 << dm; new_center[dm]= lwoc_root.center[dm] + lwoc_root.hsize; } else { new_center[dm]= lwoc_root.center[dm] - lwoc_root.hsize; } } LwocOctree new_root = new LwocOctree(null, new_center, lwoc_root.hsize * 2); new_root.children = new LwocOctree[NUM_CHILDREN]; int child = (~indx ) & (NUM_CHILDREN -1); // opposite direction, from new root to old root for (int ichild = 0; ichild < NUM_CHILDREN; ichild++) { if (ichild == child) { lwoc_root.parent = new_root; new_root.children[ichild] = lwoc_root; } else { // create empty leaf nodes new_root.children[ichild] = new LwocOctree( new_root, new double [] { new_center[0] + lwoc_root.hsize * ((((child >> 0) & 1) > 0)? 1 : -1), new_center[1] + lwoc_root.hsize * ((((child >> 1) & 1) > 0)? 1 : -1), new_center[2] + lwoc_root.hsize * ((((child >> 2) & 1) > 0)? 1 : -1) }, lwoc_root.hsize); // add leaf new_root.children[ichild].leaf = new LwocLeaf(); } } lwoc_root = new_root; } } /** * Test if the specified box intersects this node * @param xyz box center * @param half_whd half width(x), height(y) and depth(z) * @return True if it intersects */ public boolean intersects ( double [] xyz, double [] half_whd) { for (int dm = 0; dm < center.length; dm++) { if ((xyz[dm] - half_whd[dm]) >= (center[dm] + hsize)) { // semiinterval return false; } if ((xyz[dm] + half_whd[dm]) < (center[dm] - hsize)) { return false; } } return true; } /** * Test if the specified point fits in this node * @param xyz point * @return True if it intersects */ public boolean contains ( double [] xyz) { for (int dm = 0; dm < center.length; dm++) { if (xyz[dm] < (center[dm] - hsize)) { // semi-interval return false; } if (xyz[dm] >= (center[dm] + hsize)) { return false; } } return true; } /** * Test if the specified box completely fits in this node * @param xyz box center * @param half_whd half width(x), height(y) and depth(z) * @return True if it intersects */ public boolean contains ( double [] xyz, double [] half_whd) { for (int dm = 0; dm < center.length; dm++) { if ((xyz[dm] - half_whd[dm]) < (center[dm] - hsize)) { // semiinterval return false; } if ((xyz[dm] + half_whd[dm]) >= (center[dm] + hsize)) { return false; } } return true; } /** * Add mesh to each node intersecting with the mesh bounding box, * add to pending list if does not fit in a root node * @param mesh new mesh to add * @param check_existed Add only if the same mesh did not exist * @param pendingMeshes per thread list of meshes to be combined after merging threads * @param pendingLeafNodes per thread list of nodes to be combined after merging threads * @return number of nodes it was added to (intersecting) */ public static boolean addMeshCenter( // returns 0 and adds to pendingMeshes if needs growing LwocMesh mesh, boolean check_existed, ArrayList<LwocMesh> pendingMeshes, ArrayList<LwocOctree> pendingLeafNodes ) { // should in check before adding? // mesh.equals(mesh); double [] center = mesh.getCenter(); double [] dims = mesh.getDims(); // First - check that all 8 corners fit in the world double [] corner_xyz = center.clone(); for (int dm = 0; dm < center.length; dm++) { corner_xyz[dm] += dims[dm]; if (!lwoc_root.contains(corner_xyz)){ if (pendingMeshes != null) { pendingMeshes.add(mesh); } return false; } corner_xyz[dm] -= 2*dims[dm]; if (!lwoc_root.contains(corner_xyz)){ if (pendingMeshes != null) { pendingMeshes.add(mesh); } return false; } corner_xyz[dm] += dims[dm]; } // all corners fit - add center LwocOctree node = getLeafNode(center); node.leaf.addMeshCenter(mesh); // Check if leaf node requires splitting if (node.leaf.getMeshCenters().size() > MAX_MESH_CENTERS) { if (node.hsize > MIN_HSIZE) { pendingLeafNodes.add(node); } } // Now recursively add mesh to all nodes intersected by this mesh bounding box return true; } public int addMesh( // returns 0 and adds to pendingMeshes if needs growing LwocMesh mesh, boolean check_existed) { if (!intersects( mesh.getCenter(), mesh.getDims())) { return 0; } if (isLeaf()) { leaf.addMesh(mesh, check_existed); return 1; } int added=0; for (LwocOctree node: children) { added += node.addMesh(mesh, check_existed); } return added; } /** * Remove mesh to from each node intersecting with the mesh bounding box. * Should fit in a root node (as it was earlier added) * @param mesh new mesh to add * @param check_existed Add only if the same mesh did not exist * @return The number of nodes it was removed from */ public static int removeMesh( LwocMesh mesh, boolean remove_all) { // check and remove duplicates // should in check before adding? return 0; } } Loading
src/main/java/com/elphel/imagej/common/MultiThreading.java 0 → 100644 +37 −0 Original line number Diff line number Diff line package com.elphel.imagej.common; public class MultiThreading { public static int THREADS_MAX = 100; /* Create a Thread[] array as large as the number of processors available. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ public static Thread[] newThreadArray() { return newThreadArray (THREADS_MAX); } public static Thread[] newThreadArray(int maxCPUs) { int n_cpus = Runtime.getRuntime().availableProcessors(); if (n_cpus>maxCPUs)n_cpus=maxCPUs; return new Thread[n_cpus]; } /* Start all given threads and wait on each of them until all are done. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ public static void startAndJoin(Thread[] threads) // USED in lwir { for (int ithread = 0; ithread < threads.length; ++ithread) { threads[ithread].setPriority(Thread.NORM_PRIORITY); threads[ithread].start(); } try { for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread].join(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } } }
src/main/java/com/elphel/imagej/tileprocessor/MultiLayerDSL.java 0 → 100644 +28 −0 Original line number Diff line number Diff line /** ** MultiLayerDSL - Handle multilayer/multivalue disparity/strength/lma values ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** TexturedModel.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor; public class MultiLayerDSL { }
src/main/java/com/elphel/imagej/tileprocessor/lwoc/LwocLeaf.java 0 → 100644 +65 −0 Original line number Diff line number Diff line /** ** ** LwocLeaf.java - Octree leaf of the world built from LWIR16 images ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** LwocLeaf.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor.lwoc; import java.util.ArrayList; public class LwocLeaf { ArrayList <LwocMesh> meshes; ArrayList <LwocMesh> mesh_centers; ArrayList <LwocScene> scenes; public LwocLeaf() { meshes = new ArrayList <LwocMesh>(); // all meshes BB intersecting this node mesh_centers = new ArrayList <LwocMesh>(); // mesh centers in this node scenes = new ArrayList <LwocScene>(); // cameras located in this node } public void addScene(LwocScene scene) { scenes.add(scene); } public void addMeshCenter(LwocMesh mesh) { mesh_centers.add(mesh); } public void addMesh(LwocMesh mesh, boolean check_existed) { if (!check_existed || meshes.contains(mesh)) { meshes.add(mesh); } } public ArrayList <LwocMesh> getMeshes(){ return meshes; } public ArrayList <LwocMesh> getMeshCenters(){ return mesh_centers; } public ArrayList <LwocScene> getScenes(){ return scenes; } }
src/main/java/com/elphel/imagej/tileprocessor/lwoc/LwocMesh.java 0 → 100644 +61 −0 Original line number Diff line number Diff line /** ** ** LwocMesh.java - Single mesh of the world built from LWIR16 images ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** LwocMesh.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor.lwoc; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; public class LwocMesh { static AtomicInteger MESH_ID = new AtomicInteger(); static ArrayList<LwocMesh> LWOC_MESHES; int id; // assign unique ID String stimestamp; // mesh is always referenced to a single scene double [] world_xyz; // world coordinates of the center double [] hdims_xyz; // world bounding box half-dimensions along x,y,z public static void resetMeshes() { MESH_ID.set(0); LWOC_MESHES = new ArrayList<LwocMesh>(); } // maybe use mesh properties instead of id? public boolean equals (LwocMesh other_mesh) { return id == other_mesh.id; } public LwocMesh( String stimestamp ) { this.stimestamp = stimestamp; id = MESH_ID.getAndIncrement(); LWOC_MESHES.add(this); } public double [] getCenter() { return world_xyz; } public double [] getDims() { return hdims_xyz; } }
src/main/java/com/elphel/imagej/tileprocessor/lwoc/LwocOctree.java 0 → 100644 +507 −0 Original line number Diff line number Diff line /** ** ** LwocOctree.java - Octree of the world built from LWIR16 images ** ** Copyright (C) 2022 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** LwocOctree.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** -----------------------------------------------------------------------------** ** */ package com.elphel.imagej.tileprocessor.lwoc; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; import com.elphel.imagej.common.MultiThreading; public class LwocOctree { public static final int NUM_CHILDREN = 8; public static double MIN_HSIZE = 0.3; // meter - do not subdivide more // can not be used as intersecting meshes' bb will not decrease number after splitting public static int MAX_MESH_CENTERS = 10; // maximal number of meshes in a leaf; public static int MAX_CAMERAS = 10; // maximal number of cameras in a leaf; public static LwocOctree lwoc_root = null; static ArrayList<LwocOctree> LWOK_OCTREE = new ArrayList<LwocOctree>(); static AtomicInteger OCTREE_ID = new AtomicInteger(); // synchronized lists to handle thread-unsafe operations: growing world and splitting leaves /// static List<LwocScene> pendingScenes; // synchronized - add not-yet-added scenes pending growing world /// static List<LwocMesh> pendingMeshes; // synchronized - add not-yet-added meshes pending growing world /// static List<LwocOctree> pendingLeafNodes;// synchronized - leaf nodes needed to be split static AtomicInteger numPendingScenes; static AtomicInteger numPendingMeshes; static AtomicInteger numPendingLeafNodes; int id; // assign unique ID LwocOctree parent; LwocOctree [] children; LwocLeaf leaf; // double [] center; // x-center, y-center, z-center double hsize; // just for testing // List<List<LwocScene>> group = new ArrayList<List<LwocScene>>(4); /** * Initialize Octree data and set initial world node. It is possible to grow * world as needed later. * @param center world center: x,y,z in meters. Usually {0,0,0} * @param world_hsize Half linear dimension of the world */ public static void initOctree(double [] center, double world_hsize) { LwocMesh.resetMeshes(); LwocScene.resetScenes(); resetPending(); LWOK_OCTREE = new ArrayList<LwocOctree>(); // Add a single leaf node lwoc_root = new LwocOctree(null, center, world_hsize); lwoc_root.leaf = new LwocLeaf(); } /** * Synchronized lists of pending scenes, meshes and nodes are used to delay modification of * the world octree and its elements from the multithreaded environment. These lists should * be tested after multithreaded methods that potentially can modify the world and then * processes by a single-thread method. */ public static void resetPending() { numPendingScenes.set(0); // = Collections.synchronizedList(new ArrayList<LwocScene>()); numPendingMeshes.set(0); // = Collections.synchronizedList(new ArrayList<LwocMesh>()); numPendingLeafNodes.set(0); // = Collections.synchronizedList(new ArrayList<LwocOctree>()); } // use per-thread lists and then combine them avoiding synchronizations? //Pass list to any function that may grow it /** * Test if it is leaf node * @return True if it is a leaf node */ public boolean isLeaf() { return children == null; } /** * Check if the world needs growing to accommodate new scenes or meshes * @return if world needs growing. */ public static boolean pendingGrow() { // return !pendingScenes.isEmpty() || !pendingMeshes.isEmpty(); return (numPendingScenes.get() + numPendingMeshes.get()) > 0; } /** * Check if any leaf nodes need splitting * @return if any leaf nodes require splitting */ public static boolean pendingSplit() { return numPendingLeafNodes.get() > 0; } /** * LwocOctree constructor * @param parent Parent node * @param xyz position of the node center in meters * @param hsize Node half size - each of the X,Y,Z coordinates of the internal * points are limited within +/-hsize from the node center */ public LwocOctree (LwocOctree parent, double [] xyz, double hsize) { id = OCTREE_ID.getAndIncrement(); this.parent = parent; this.hsize = hsize; LWOK_OCTREE.add(this); } /** * Find the leaf node that includes specified point. Thread safe. * @param xyz X, Y, and Z coordinates of the point * @return Octree node that includes the point or null if the point * is outside the root node (the whole world) */ public static LwocOctree getLeafNode( // if null - needs growing world double [] xyz) { // thread safe if (!lwoc_root.contains(xyz)) return null; // needs growing world LwocOctree node = lwoc_root; while (lwoc_root.children != null) { int indx = 0; for (int dm = 0; dm < 3; dm++) { if (xyz[dm] >= lwoc_root.center[dm] ) { indx += 1 << dm; } } node = node.children[indx]; } return node; } /** * Add scene to the world in a thread-safe mode, or put it to pending list if the world * needs growing. * @param scene Scene to add. * @param pendingScenes per thread list of scenes to be combined after merging threads * @param pendingLeafNodes per thread list of nodes to be combined after merging threads * @return An octree node where scene is added or null if the world needs growing */ public static LwocOctree addScene( LwocScene scene, ArrayList<LwocScene> pendingScenes, ArrayList<LwocOctree> pendingLeafNodes ) {// returns null and adds to pendingScenes if needs growing LwocOctree node = getLeafNode(scene.getCameraXYZ()); if (node == null) { if (pendingScenes != null) { pendingScenes.add(scene); } } else { node.leaf.addScene(scene); // Check if leaf node requires splitting if (node.leaf.getScenes().size() > MAX_CAMERAS) { if (node.hsize > MIN_HSIZE) { pendingLeafNodes.add(node); } } } return node; } /** * Tend to pending scenes list. Not thread-safe, should be run in a single-thread mode. * @param pendingScenes combined list of scenes to be added growing world (merged from per-thread ones) * @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one) */ public static void tendPendingScenes( ArrayList<LwocScene> pendingScenes, ArrayList<LwocOctree> pendingLeafNodes ) { if (pendingScenes.isEmpty()) { return; // nothing to do } else { for (LwocScene scene: pendingScenes) { growXYZ(scene.getCameraXYZ()); LwocOctree node= addScene( // should not be null scene, pendingScenes, pendingLeafNodes); assert node != null : "addScene() should not fail after growing world"; } } } /** * Tend to pending meshes list. Not thread-safe, should be run in a single-thread mode. * @param check_existed do not add duplicate meshes * @param pendingMeshes combined list of meshes to be added (merged from per-thread ones). Will only be read. * @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one). May grow. */ public static void tendPendingMeshCenters( boolean check_existed, ArrayList<LwocMesh> pendingMeshes, // will read ArrayList<LwocOctree> pendingLeafNodes // may add to ) { if (pendingMeshes.isEmpty()) { return; // nothing to do } else { for (LwocMesh mesh: pendingMeshes) { double [] center = mesh.getCenter(); double [] dims = mesh.getDims(); double [] corner_xyz = center.clone(); for (int dm = 0; dm < center.length; dm++) { corner_xyz[dm] += dims[dm]; growXYZ(corner_xyz); corner_xyz[dm] -= 2*dims[dm]; growXYZ(corner_xyz); corner_xyz[dm] += dims[dm]; } boolean ok= addMeshCenter( // should not be null mesh, check_existed, null, // pendingMeshes, pendingLeafNodes); assert ok : "addMeshCenter() should not fail after growing world"; // int num_added = lwoc_root.addMesh( mesh, check_existed); } } } public static void tendPendingLeafNodes( ArrayList<LwocOctree> pendingLeafNodesIn) { // remove any possible duplicates final ArrayList<LwocOctree> pendingLeafNodes = new ArrayList<LwocOctree>(); for (LwocOctree node:pendingLeafNodesIn) { if (!pendingLeafNodes.contains(node)) { pendingLeafNodes.add(node); } } // run splitting multithreaded final Thread[] threads = MultiThreading.newThreadArray(); final AtomicInteger ai = new AtomicInteger(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { for (int indx_node = ai.getAndIncrement(); indx_node < pendingLeafNodes.size(); indx_node = ai.getAndIncrement()) { LwocOctree node = pendingLeafNodes.get(indx_node); // Split recursively until satisfied conditions } // end of tile } }; } MultiThreading.startAndJoin(threads); ai.set(0); } // Split recursively until satisfied conditions public int splitNode( double min_hsize, int max_mesh_centers, int max_cameras ) { if ((hsize <= min_hsize) || (leaf == null)) { return 0; } if ((leaf.getScenes().size() <= max_cameras) && (leaf.getMeshCenters().size() <= max_mesh_centers)) { return 0; } // actually need splitting int num_added = NUM_CHILDREN - 1; children = new LwocOctree[NUM_CHILDREN]; double new_hsize = hsize/2; for (int nchild = 0; nchild < children.length; nchild++) { double [] xyz = center.clone(); for (int dm = 0; dm < center.length; dm++) { xyz[dm] +=new_hsize * ((((nchild >> dm) & 1) > 0)? 1 : -1); } children[nchild] = new LwocOctree( this, xyz, new_hsize); LwocLeaf new_leaf = new LwocLeaf(); children[nchild].leaf = new_leaf; } ArrayList <LwocMesh> meshes = leaf.getMeshes(); // distribute meshes between 8 children ArrayList <LwocMesh> mesh_centers = leaf.getMeshCenters(); // distribute mesh_centers between 8 children ArrayList <LwocScene> scenes = leaf.getScenes(); // distribute scnes (cameras) between 8 children // delete old leaf (if it is in a list) and its lists - no need leaf = null; for (int nchild = 0; nchild < children.length; nchild++) { num_added += children[nchild].splitNode( min_hsize, max_mesh_centers, max_cameras); } return num_added; } // not thread safe // grow world to include xyz (all 8 corners for a bounding box of as mesh /** * Grow the world to include specified point Will repeat growing twice (in each direction) * until the specified point gets inside. * @param xyz The point to be included in the world. */ public static void growXYZ(double [] xyz) { while (!lwoc_root.contains(xyz)) {// already fits, do not grow // grow once int indx=0; //direction to grow double [] new_center = new double[3]; for (int dm = 0; dm < new_center.length; dm++) { if (xyz[dm] >= lwoc_root.center[dm] ) { indx += 1 << dm; new_center[dm]= lwoc_root.center[dm] + lwoc_root.hsize; } else { new_center[dm]= lwoc_root.center[dm] - lwoc_root.hsize; } } LwocOctree new_root = new LwocOctree(null, new_center, lwoc_root.hsize * 2); new_root.children = new LwocOctree[NUM_CHILDREN]; int child = (~indx ) & (NUM_CHILDREN -1); // opposite direction, from new root to old root for (int ichild = 0; ichild < NUM_CHILDREN; ichild++) { if (ichild == child) { lwoc_root.parent = new_root; new_root.children[ichild] = lwoc_root; } else { // create empty leaf nodes new_root.children[ichild] = new LwocOctree( new_root, new double [] { new_center[0] + lwoc_root.hsize * ((((child >> 0) & 1) > 0)? 1 : -1), new_center[1] + lwoc_root.hsize * ((((child >> 1) & 1) > 0)? 1 : -1), new_center[2] + lwoc_root.hsize * ((((child >> 2) & 1) > 0)? 1 : -1) }, lwoc_root.hsize); // add leaf new_root.children[ichild].leaf = new LwocLeaf(); } } lwoc_root = new_root; } } /** * Test if the specified box intersects this node * @param xyz box center * @param half_whd half width(x), height(y) and depth(z) * @return True if it intersects */ public boolean intersects ( double [] xyz, double [] half_whd) { for (int dm = 0; dm < center.length; dm++) { if ((xyz[dm] - half_whd[dm]) >= (center[dm] + hsize)) { // semiinterval return false; } if ((xyz[dm] + half_whd[dm]) < (center[dm] - hsize)) { return false; } } return true; } /** * Test if the specified point fits in this node * @param xyz point * @return True if it intersects */ public boolean contains ( double [] xyz) { for (int dm = 0; dm < center.length; dm++) { if (xyz[dm] < (center[dm] - hsize)) { // semi-interval return false; } if (xyz[dm] >= (center[dm] + hsize)) { return false; } } return true; } /** * Test if the specified box completely fits in this node * @param xyz box center * @param half_whd half width(x), height(y) and depth(z) * @return True if it intersects */ public boolean contains ( double [] xyz, double [] half_whd) { for (int dm = 0; dm < center.length; dm++) { if ((xyz[dm] - half_whd[dm]) < (center[dm] - hsize)) { // semiinterval return false; } if ((xyz[dm] + half_whd[dm]) >= (center[dm] + hsize)) { return false; } } return true; } /** * Add mesh to each node intersecting with the mesh bounding box, * add to pending list if does not fit in a root node * @param mesh new mesh to add * @param check_existed Add only if the same mesh did not exist * @param pendingMeshes per thread list of meshes to be combined after merging threads * @param pendingLeafNodes per thread list of nodes to be combined after merging threads * @return number of nodes it was added to (intersecting) */ public static boolean addMeshCenter( // returns 0 and adds to pendingMeshes if needs growing LwocMesh mesh, boolean check_existed, ArrayList<LwocMesh> pendingMeshes, ArrayList<LwocOctree> pendingLeafNodes ) { // should in check before adding? // mesh.equals(mesh); double [] center = mesh.getCenter(); double [] dims = mesh.getDims(); // First - check that all 8 corners fit in the world double [] corner_xyz = center.clone(); for (int dm = 0; dm < center.length; dm++) { corner_xyz[dm] += dims[dm]; if (!lwoc_root.contains(corner_xyz)){ if (pendingMeshes != null) { pendingMeshes.add(mesh); } return false; } corner_xyz[dm] -= 2*dims[dm]; if (!lwoc_root.contains(corner_xyz)){ if (pendingMeshes != null) { pendingMeshes.add(mesh); } return false; } corner_xyz[dm] += dims[dm]; } // all corners fit - add center LwocOctree node = getLeafNode(center); node.leaf.addMeshCenter(mesh); // Check if leaf node requires splitting if (node.leaf.getMeshCenters().size() > MAX_MESH_CENTERS) { if (node.hsize > MIN_HSIZE) { pendingLeafNodes.add(node); } } // Now recursively add mesh to all nodes intersected by this mesh bounding box return true; } public int addMesh( // returns 0 and adds to pendingMeshes if needs growing LwocMesh mesh, boolean check_existed) { if (!intersects( mesh.getCenter(), mesh.getDims())) { return 0; } if (isLeaf()) { leaf.addMesh(mesh, check_existed); return 1; } int added=0; for (LwocOctree node: children) { added += node.addMesh(mesh, check_existed); } return added; } /** * Remove mesh to from each node intersecting with the mesh bounding box. * Should fit in a root node (as it was earlier added) * @param mesh new mesh to add * @param check_existed Add only if the same mesh did not exist * @return The number of nodes it was removed from */ public static int removeMesh( LwocMesh mesh, boolean remove_all) { // check and remove duplicates // should in check before adding? return 0; } }