uvgVPCCenc 1.0.0
uvgVPCCenc is an open-source real-time V-PCC encoder library written in C++ from scratch.
Loading...
Searching...
No Matches
utilsPatchGeneration.hpp
Go to the documentation of this file.
1/*****************************************************************************
2 * This file is part of uvgVPCCenc V-PCC encoder.
3 *
4 * Copyright (c) 2024, Tampere University, ITU/ISO/IEC, project contributors
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without modification,
8 * are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice, this
14 * list of conditions and the following disclaimer in the documentation and/or
15 * other materials provided with the distribution.
16 *
17 * * Neither the name of the Tampere University or ITU/ISO/IEC nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON
28 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
31 ****************************************************************************/
32
34
35#pragma once
36
37#include "utils/utils.hpp"
38#include "uvgvpcc/log.hpp"
39#include <cstdint>
40#include <fstream>
41#include <vector>
42#include "robin_hood.h"
43
44using namespace uvgvpcc_enc;
45
46
47// TODO(lf): how to handle this type of lut table variable ?
48// NOLINTNEXTLINE(cert-err58-cpp)
49static const std::array<std::vector<Vector3<int32_t>>, 9> adjacentPointsSearch = {{
50 // Adjacent shift for squared distance 1
51 {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}},
52 // Adjacent shift for squared distance 2
53 {{1, 1, 0},
54 {1, -1, 0},
55 {-1, 1, 0},
56 {-1, -1, 0},
57 {0, 1, 1},
58 {0, 1, -1},
59 {0, -1, 1},
60 {0, -1, -1},
61 {1, 0, 1},
62 {-1, 0, 1},
63 {1, 0, -1},
64 {-1, 0, -1}},
65 // Adjacent shift for squared distance 3
66 {{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1}, {-1, 1, 1}, {-1, 1, -1}, {-1, -1, 1}, {-1, -1, -1}},
67 // Adjacent shift for squared distance 4
68 {{2, 0, 0}, {-2, 0, 0}, {0, 2, 0}, {0, -2, 0}, {0, 0, 2}, {0, 0, -2}},
69 // Adjacent shift for squared distance 5
70 {{2, 1, 0}, {2, -1, 0}, {1, 2, 0}, {1, -2, 0}, {-1, 2, 0}, {-1, -2, 0}, {-2, 1, 0}, {-2, -1, 0},
71 {0, 2, 1}, {0, 2, -1}, {0, 1, 2}, {0, 1, -2}, {0, -1, 2}, {0, -1, -2}, {0, -2, 1}, {0, -2, -1},
72 {1, 0, 2}, {-1, 0, 2}, {2, 0, 1}, {-2, 0, 1}, {2, 0, -1}, {-2, 0, -1}, {1, 0, -2}, {-1, 0, -2}},
73 // Adjacent shift for squared distance 6
74 {{2, 1, 1}, {2, 1, -1}, {2, -1, 1}, {2, -1, -1}, {1, 2, 1}, {1, 2, -1}, {1, 1, 2}, {1, 1, -2},
75 {1, -1, 2}, {1, -1, -2}, {1, -2, 1}, {1, -2, -1}, {-1, 2, 1}, {-1, 2, -1}, {-1, 1, 2}, {-1, 1, -2},
76 {-1, -1, 2}, {-1, -1, -2}, {-1, -2, 1}, {-1, -2, -1}, {-2, 1, 1}, {-2, 1, -1}, {-2, -1, 1}, {-2, -1, -1}},
77 // Adjacent shift for squared distance 7 (does not exist in an integer grid)
78 {},
79 // Adjacent shift for squared distance 8
80 {{2, 2, 0},
81 {2, -2, 0},
82 {-2, 2, 0},
83 {-2, -2, 0},
84 {0, 2, 2},
85 {0, 2, -2},
86 {0, -2, 2},
87 {0, -2, -2},
88 {2, 0, 2},
89 {-2, 0, 2},
90 {2, 0, -2},
91 {-2, 0, -2}},
92 // Adjacent shift for squared distance 9
93 {{3, 0, 0}, {-3, 0, 0}, {0, 3, 0}, {0, -3, 0}, {0, 0, 3}, {0, 0, -3}, {2, 2, 1}, {2, 2, -1}, {2, 1, 2}, {2, 1, -2},
94 {2, -1, 2}, {2, -1, -2}, {2, -2, 1}, {2, -2, -1}, {1, 2, 2}, {1, 2, -2}, {1, -2, 2}, {1, -2, -2}, {-1, 2, 2}, {-1, 2, -2},
95 {-1, -2, 2}, {-1, -2, -2}, {-2, 2, 1}, {-2, 2, -1}, {-2, 1, 2}, {-2, 1, -2}, {-2, -1, 2}, {-2, -1, -2}, {-2, -2, 1}, {-2, -2, -1}},
96}};
97
98const std::array<Vector3<uint8_t>, 6> ppiColors = {{
99 {51, 51, 51}, // Charcoal Gray
100 {0, 102, 51}, // Forest Green
101 {153, 0, 0}, // Rich Crimson
102 {0, 51, 102}, // Deep Blue
103 {255, 204, 0}, // Golden Yellow
104 {102, 204, 204} // Muted Cyan
105}};
106
107const std::array<Vector3<uint8_t>, 114> patchColors = {{
108 // Red color is for points not being part of a patch before the 2D projection.
109{139,0,0},{165,42,42},{178,34,34},{220,20,60},{255,99,71},{255,127,80},{205,92,92},{240,128,128},{233,150,122},{250,128,114},{255,160,122},{255,69,0},{255,140,0},{255,165,0},{255,215,0},{184,134,11},{218,165,32},{238,232,170},{189,183,107},{240,230,140},{255,255,0},{32,178,170},{0,128,128},{0,139,139},{0,255,255},{0,255,255},{224,255,255},{0,206,209},{72,209,204},{175,238,238},{176,224,230},{95,158,160},{70,130,180},{100,149,237},{0,191,255},{30,144,255},{173,216,230},{135,206,235},{135,206,250},{25,25,112},{0,0,128},{0,0,139},{0,0,205},{0,0,255},{65,105,225},{138,43,226},{75,0,130},{72,61,139},{106,90,205},{123,104,238},{147,112,219},{139,0,139},{148,0,211},{153,50,204},{186,85,211},{128,0,128},{216,191,216},{221,160,221},{238,130,238},{255,0,255},{218,112,214},{199,21,133},{219,112,147},{255,20,147},{255,105,180},{255,182,193},{255,192,203},{250,235,215},{245,245,220},{255,228,196},{255,235,205},{245,222,179},{255,248,220},{255,250,205},{250,250,210},{255,255,224},{139,69,19},{160,82,45},{210,105,30},{205,133,63},{244,164,96},{222,184,135},{210,180,140},{188,143,143},{255,228,181},{255,222,173},{255,218,185},{255,228,225},{255,240,245},{250,240,230},{253,245,230},{255,239,213},{255,245,238},{245,255,250},{112,128,144},{119,136,153},{176,196,222},{230,230,250},{255,250,240},{240,248,255},{248,248,255},{240,255,240},{255,255,240},{240,255,255},{255,250,250},{0,0,0},{105,105,105},{128,128,128},{169,169,169},{192,192,192},{211,211,211},{220,220,220},{245,245,245},{255,255,255},
110}};
111
112template <typename T, typename TT>
113inline double dotProduct(const std::array<T, 3>& arr1, const std::array<TT, 3>& arr2) {
114 return arr1[0] * arr2[0] + arr1[1] * arr2[1] + arr1[2] * arr2[2];
115}
116
117inline void exportPointCloud(const std::string& plyFilePath, const std::vector<Vector3<typeGeometryInput>>& geometries,
118 const std::vector<Vector3<uint8_t>>& attributes,
119 const std::vector<Vector3<double>>& normals = {}) {
120 const bool hasNormals = !normals.empty();
121
122 std::ofstream fout(plyFilePath, std::ofstream::out | std::ofstream::trunc);
123 if (!fout.is_open()) {
124 throw std::runtime_error("Error : can't create a stream from : " + plyFilePath);
125 }
126
127 fout << "ply";
128 fout << "\nformat ascii 1.0";
129 fout << "\nelement vertex " << geometries.size();
130 fout << "\nproperty int x";
131 fout << "\nproperty int y";
132 fout << "\nproperty int z";
133 if (hasNormals) {
134 fout << "\nproperty double nx";
135 fout << "\nproperty double ny";
136 fout << "\nproperty double nz";
137 }
138 fout << "\nproperty uchar red";
139 fout << "\nproperty uchar green";
140 fout << "\nproperty uchar blue";
141 fout << "\nend_header\n";
142
143 fout << std::setprecision(std::numeric_limits<double>::max_digits10);
144 for (size_t i = 0; i < geometries.size(); ++i) {
145 fout << geometries[i][0] << " " << geometries[i][1] << " " << geometries[i][2];
146 if (hasNormals) {
147 fout << " " << normals[i][0] << " " << normals[i][1] << " " << normals[i][2];
148 }
149 fout << " " << static_cast<int>(attributes[i][0]) << " " << static_cast<int>(attributes[i][1]) << " "
150 << static_cast<int>(attributes[i][2]);
151 fout << std::endl;
152 }
153 fout.close();
154}
155
156// Hash function for vector3
157template <typename T>
159 size_t operator()(const Vector3<T>& vector) const {
160 std::hash<T> hasher;
161 size_t hash = 0;
162 for (const auto& elem : vector) {
163 // Use a simple hash combining algorithm
164 hash ^= hasher(elem) + 0x9e3779b9 + (hash << 6U) + (hash >> 2U);
165 }
166 return hash;
167 }
168};
169
170
171
172
173
174
175// The voxelization is done so to have those three vectors sharing the same order (that is sharing the same voxel index) :
176// -> A list of voxel (vector of coordinates)
177// -> A list of list of points (vector of vector of point index). This associate a voxel with the points inside it.
178// -> A list of voxel PPI (vector of PPI (int))
179//
180// Example with a voxel index i:
181// voxelizedPointsGeometry[i] stores the coordinate of the voxel i
182// voxelIdToPointsId[i] stores the index of the points inside the voxel i
183// voxelsPPIs[i] stores the PPI of the voxel i
184//
185// The use of a map is needed as several points can be inside one voxel. We want to find in a fast way if the current point resides in a voxel
186// that has already been created. For an input point j: voxelCoordToVoxelIndex[ voxelCoordsOf(j) ] = i
187//
188// This structure makes computation easier for the patch generation and for applying voxel data to points data.
189// To achieve this, we need a temporary map that interfaces the voxel coordinates with the voxel index in those three list.
190
191// voxelizedPointsGeometry -> list of the voxels. That is, a list of the coordinates of the voxels.
192// voxelIdToPointsId -> For each voxel, stores the index of the points inside. A voxel is defined by a voxel index, that is given by the
193// filling order of the vector. Thus, the third voxel to be added to this structure will have a voxel index of 2.
194// voxelCoordToVoxelIndex -> Temporary structure. Associate a voxel to its index in 'voxelIdToPointsId' (its voxel index). This structure is a
195// map whose key is a voxel coordinate and the value is a voxel index.
196//
197// For each point if the input point cloud :
198// Compute the coordinates of the voxel inside which the current point should be.
199// If those voxel coordinates are already in the map 'voxelCoordToVoxelIndex':
200// Add the current input point index to the list of points index of the found voxel.
201// That is, add the current input point index to the list of points of the voxel in 'voxelIdToPointsId'
202// If those voxel coordinates are not in the map 'voxelCoordToVoxelIndex':
203// Create a new item in the map 'voxelCoordToVoxelIndex'. This associates the voxel coordinates to its voxel index (the current size
204// of the list of voxel === the index of this voxel in the list of voxel). Add a new voxel to 'voxelIdToPointsId'. This associates the
205// voxel index (the current size of the list of voxel === the index of this voxel in the list of voxel) to an empty list of input
206// point index. Create a new voxel by adding the voxel coordinates in the voxel list 'voxelizedPointsGeometry'. Add the current input
207// point index to the list of points index of the new voxel. That is, add the current input point index to the list of points of the
208// voxel in 'voxelIdToPointsId'
209
210
211// Notice that in the refined segmentation, the inputPointsGeometry can be the voxelizedPointsGeometry. Indeed, when the voxelization is
212// activated, the refined segmentation is applying a second voxelization step. The created voxelized point cloud is then the result of two
213// voxelizations.
214inline void voxelization(
215 const std::vector<Vector3<typeGeometryInput>>& inputPointsGeometry,
216 std::vector<Vector3<typeGeometryInput>>& voxelizedPointsGeometry,
217 std::vector<std::vector<size_t>>& voxelIdToPointsId,
218 const size_t inputBitResolution,
219 const size_t outputBitResolution)
220{
222 LogLevel::TRACE, "PATCH GENERATION",
223 "Voxelization from " + std::to_string(inputBitResolution) + " to " + std::to_string(outputBitResolution) + " bits of resolution.\n");
224
225 const size_t voxelizationShift = inputBitResolution - outputBitResolution;
226 const typeGeometryInput shift = static_cast<typeGeometryInput>(voxelizationShift);
227
228 const size_t approximateVoxelCount = 1U << (outputBitResolution * 2U);
229 voxelizedPointsGeometry.reserve(approximateVoxelCount);
230 voxelIdToPointsId.reserve(approximateVoxelCount);
231
233 voxelCoordToVoxelIndex.reserve(approximateVoxelCount);
234
235 const size_t inputSize = inputPointsGeometry.size();
236 for (size_t inputPointIndex = 0; inputPointIndex < inputSize; ++inputPointIndex) {
237 const Vector3<typeGeometryInput> & inputPoint = inputPointsGeometry[inputPointIndex];
238
240 static_cast<typeGeometryInput>(static_cast<uint32_t>(inputPoint[0]) >> shift),
241 static_cast<typeGeometryInput>(static_cast<uint32_t>(inputPoint[1]) >> shift),
242 static_cast<typeGeometryInput>(static_cast<uint32_t>(inputPoint[2]) >> shift)
243 };
244
245 size_t voxelIndex = voxelizedPointsGeometry.size();
246 auto [it, inserted] = voxelCoordToVoxelIndex.try_emplace(voxCoord, voxelIndex);
247 if (inserted) {
248 voxelizedPointsGeometry.emplace_back(voxCoord);
249 voxelIdToPointsId.emplace_back();
250 voxelIdToPointsId.back().reserve(16);
251 }
252
253 voxelIdToPointsId[it->second].push_back(inputPointIndex);
254 }
255}
Definition robin_hood.h:916
std::pair< iterator, bool > try_emplace(const key_type &key, Args &&... args)
Definition robin_hood.h:1830
void reserve(size_t c)
Definition robin_hood.h:2079
static void log(LogLevel level, const std::string &context, const std::string &message)
Definition log.cpp:84
Definition utils.hpp:59
Definition log.hpp:43
uint16_t typeGeometryInput
Definition utils.hpp:49
Definition utilsPatchGeneration.hpp:158
size_t operator()(const Vector3< T > &vector) const
Definition utilsPatchGeneration.hpp:159
void voxelization(const std::vector< Vector3< typeGeometryInput > > &inputPointsGeometry, std::vector< Vector3< typeGeometryInput > > &voxelizedPointsGeometry, std::vector< std::vector< size_t > > &voxelIdToPointsId, const size_t inputBitResolution, const size_t outputBitResolution)
Definition utilsPatchGeneration.hpp:214
double dotProduct(const std::array< T, 3 > &arr1, const std::array< TT, 3 > &arr2)
Definition utilsPatchGeneration.hpp:113
const std::array< Vector3< uint8_t >, 114 > patchColors
Definition utilsPatchGeneration.hpp:107
void exportPointCloud(const std::string &plyFilePath, const std::vector< Vector3< typeGeometryInput > > &geometries, const std::vector< Vector3< uint8_t > > &attributes, const std::vector< Vector3< double > > &normals={})
Definition utilsPatchGeneration.hpp:117
const std::array< Vector3< uint8_t >, 6 > ppiColors
Definition utilsPatchGeneration.hpp:98