uvgVPCCenc 1.1.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-present, 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 <sys/types.h>
38
39#include <cstdint>
40#include <vector>
41
42#include "robin_hood.h"
43#include "utils/constants.hpp"
44#include "uvgutils/utils.hpp"
45#include "uvgutils/log.hpp"
46
47
48using namespace uvgvpcc_enc;
49
50// TODO(lf): how to handle this type of lut table variable ?
51// NOLINTNEXTLINE(cert-err58-cpp)
52static const std::array<std::vector<std::array<int, 3>>, 9> adjacentPointsSearch = {{
53 // Adjacent shift for squared distance 1
54 {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}},
55 // Adjacent shift for squared distance 2
56 {{1, 1, 0},
57 {1, -1, 0},
58 {-1, 1, 0},
59 {-1, -1, 0},
60 {0, 1, 1},
61 {0, 1, -1},
62 {0, -1, 1},
63 {0, -1, -1},
64 {1, 0, 1},
65 {-1, 0, 1},
66 {1, 0, -1},
67 {-1, 0, -1}},
68 // Adjacent shift for squared distance 3
69 {{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1}, {-1, 1, 1}, {-1, 1, -1}, {-1, -1, 1}, {-1, -1, -1}},
70 // Adjacent shift for squared distance 4
71 {{2, 0, 0}, {-2, 0, 0}, {0, 2, 0}, {0, -2, 0}, {0, 0, 2}, {0, 0, -2}},
72 // Adjacent shift for squared distance 5
73 {{2, 1, 0}, {2, -1, 0}, {1, 2, 0}, {1, -2, 0}, {-1, 2, 0}, {-1, -2, 0}, {-2, 1, 0}, {-2, -1, 0},
74 {0, 2, 1}, {0, 2, -1}, {0, 1, 2}, {0, 1, -2}, {0, -1, 2}, {0, -1, -2}, {0, -2, 1}, {0, -2, -1},
75 {1, 0, 2}, {-1, 0, 2}, {2, 0, 1}, {-2, 0, 1}, {2, 0, -1}, {-2, 0, -1}, {1, 0, -2}, {-1, 0, -2}},
76 // Adjacent shift for squared distance 6
77 {{2, 1, 1}, {2, 1, -1}, {2, -1, 1}, {2, -1, -1}, {1, 2, 1}, {1, 2, -1}, {1, 1, 2}, {1, 1, -2},
78 {1, -1, 2}, {1, -1, -2}, {1, -2, 1}, {1, -2, -1}, {-1, 2, 1}, {-1, 2, -1}, {-1, 1, 2}, {-1, 1, -2},
79 {-1, -1, 2}, {-1, -1, -2}, {-1, -2, 1}, {-1, -2, -1}, {-2, 1, 1}, {-2, 1, -1}, {-2, -1, 1}, {-2, -1, -1}},
80 // Adjacent shift for squared distance 7 (does not exist in an integer grid)
81 {},
82 // Adjacent shift for squared distance 8
83 {{2, 2, 0},
84 {2, -2, 0},
85 {-2, 2, 0},
86 {-2, -2, 0},
87 {0, 2, 2},
88 {0, 2, -2},
89 {0, -2, 2},
90 {0, -2, -2},
91 {2, 0, 2},
92 {-2, 0, 2},
93 {2, 0, -2},
94 {-2, 0, -2}},
95 // Adjacent shift for squared distance 9
96 {{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},
97 {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},
98 {-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}},
99}};
100
101
102// All 122 shift vectors stored in a single contiguous block of memory
103static constexpr std::array<std::array<int, 3>, 122> adjacentPointsSearchFlat = {{
104 // Adjacent shift for squared distance 1 (Offset 0, Size 6)
105 {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1},
106
107 // Adjacent shift for squared distance 2 (Offset 6, Size 12)
108 {1, 1, 0}, {1, -1, 0}, {-1, 1, 0}, {-1, -1, 0}, {0, 1, 1}, {0, 1, -1},
109 {0, -1, 1}, {0, -1, -1}, {1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1},
110
111 // Adjacent shift for squared distance 3 (Offset 18, Size 8)
112 {1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1},
113 {-1, 1, 1}, {-1, 1, -1}, {-1, -1, 1}, {-1, -1, -1},
114
115 // Adjacent shift for squared distance 4 (Offset 26, Size 6)
116 {2, 0, 0}, {-2, 0, 0}, {0, 2, 0}, {0, -2, 0}, {0, 0, 2}, {0, 0, -2},
117
118 // Adjacent shift for squared distance 5 (Offset 32, Size 24)
119 {2, 1, 0}, {2, -1, 0}, {1, 2, 0}, {1, -2, 0}, {-1, 2, 0}, {-1, -2, 0}, {-2, 1, 0}, {-2, -1, 0},
120 {0, 2, 1}, {0, 2, -1}, {0, 1, 2}, {0, 1, -2}, {0, -1, 2}, {0, -1, -2}, {0, -2, 1}, {0, -2, -1},
121 {1, 0, 2}, {-1, 0, 2}, {2, 0, 1}, {-2, 0, 1}, {2, 0, -1}, {-2, 0, -1}, {1, 0, -2}, {-1, 0, -2},
122
123 // Adjacent shift for squared distance 6 (Offset 56, Size 24)
124 {2, 1, 1}, {2, 1, -1}, {2, -1, 1}, {2, -1, -1}, {1, 2, 1}, {1, 2, -1}, {1, 1, 2}, {1, 1, -2},
125 {1, -1, 2}, {1, -1, -2}, {1, -2, 1}, {1, -2, -1}, {-1, 2, 1}, {-1, 2, -1}, {-1, 1, 2}, {-1, 1, -2},
126 {-1, -1, 2}, {-1, -1, -2}, {-1, -2, 1}, {-1, -2, -1}, {-2, 1, 1}, {-2, 1, -1}, {-2, -1, 1}, {-2, -1, -1},
127
128 // Adjacent shift for squared distance 7 does not exist in an integer grid.
129
130 // Adjacent shift for squared distance 8 (Offset 80, Size 12)
131 {2, 2, 0}, {2, -2, 0}, {-2, 2, 0}, {-2, -2, 0}, {0, 2, 2}, {0, 2, -2},
132 {0, -2, 2}, {0, -2, -2}, {2, 0, 2}, {-2, 0, 2}, {2, 0, -2}, {-2, 0, -2},
133
134 // Adjacent shift for squared distance 9 (Offset 92, Size 30)
135 {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},
136 {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},
137 {-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}
138}};
139
140static constexpr std::array<size_t, 9> adjacentPointsSearchFlatOffsets = {
141 0,
142 6,
143 18,
144 26,
145 32,
146 56,
147 80,
148 80,
149 92
150};
151
152
153const std::array<uvgutils::VectorN<uint8_t, 3>, 114> patchColors = {{
154 // Red color is for points not being part of a patch before the 2D projection.
155 {139, 0, 0}, {165, 42, 42}, {178, 34, 34}, {220, 20, 60}, {255, 99, 71}, {255, 127, 80}, {205, 92, 92}, {240, 128, 128},
156 {233, 150, 122}, {250, 128, 114}, {255, 160, 122}, {255, 69, 0}, {255, 140, 0}, {255, 165, 0}, {255, 215, 0}, {184, 134, 11},
157 {218, 165, 32}, {238, 232, 170}, {189, 183, 107}, {240, 230, 140}, {255, 255, 0}, {32, 178, 170}, {0, 128, 128}, {0, 139, 139},
158 {0, 255, 255}, {0, 255, 255}, {224, 255, 255}, {0, 206, 209}, {72, 209, 204}, {175, 238, 238}, {176, 224, 230}, {95, 158, 160},
159 {70, 130, 180}, {100, 149, 237}, {0, 191, 255}, {30, 144, 255}, {173, 216, 230}, {135, 206, 235}, {135, 206, 250}, {25, 25, 112},
160 {0, 0, 128}, {0, 0, 139}, {0, 0, 205}, {0, 0, 255}, {65, 105, 225}, {138, 43, 226}, {75, 0, 130}, {72, 61, 139},
161 {106, 90, 205}, {123, 104, 238}, {147, 112, 219}, {139, 0, 139}, {148, 0, 211}, {153, 50, 204}, {186, 85, 211}, {128, 0, 128},
162 {216, 191, 216}, {221, 160, 221}, {238, 130, 238}, {255, 0, 255}, {218, 112, 214}, {199, 21, 133}, {219, 112, 147}, {255, 20, 147},
163 {255, 105, 180}, {255, 182, 193}, {255, 192, 203}, {250, 235, 215}, {245, 245, 220}, {255, 228, 196}, {255, 235, 205}, {245, 222, 179},
164 {255, 248, 220}, {255, 250, 205}, {250, 250, 210}, {255, 255, 224}, {139, 69, 19}, {160, 82, 45}, {210, 105, 30}, {205, 133, 63},
165 {244, 164, 96}, {222, 184, 135}, {210, 180, 140}, {188, 143, 143}, {255, 228, 181}, {255, 222, 173}, {255, 218, 185}, {255, 228, 225},
166 {255, 240, 245}, {250, 240, 230}, {253, 245, 230}, {255, 239, 213}, {255, 245, 238}, {245, 255, 250}, {112, 128, 144}, {119, 136, 153},
167 {176, 196, 222}, {230, 230, 250}, {255, 250, 240}, {240, 248, 255}, {248, 248, 255}, {240, 255, 240}, {255, 255, 240}, {240, 255, 255},
168 {255, 250, 250}, {0, 0, 0}, {105, 105, 105}, {128, 128, 128}, {169, 169, 169}, {192, 192, 192}, {211, 211, 211}, {220, 220, 220},
169 {245, 245, 245}, {255, 255, 255},
170}};
171
172// Look Up Table of the dot product's values depending on the projection plane and the point's PPI
173 // normalsDotProducts[i][j] is the dot product value of the current point's corresponding normal of PPI = i with the corresponding normal of PPI = j
174static const std::array<std::array<int8_t,6>, 6> normalsDotProducts = {{
175
176 {1, 0, 0, -1, 0, 0},
177
178 {0, 1, 0, 0, -1, 0},
179
180 {0, 0, 1, 0, 0, -1},
181
182 {-1, 0, 0, 1, 0, 0},
183
184 {0, -1, 0, 0, 1, 0},
185
186 {0, 0, -1, 0, 0, 1}
187
188}};
189
190
191template <typename T, typename TT>
192inline double dotProduct(const std::array<T, 3>& arr1, const std::array<TT, 3>& arr2) {
193 return arr1[0] * arr2[0] + arr1[1] * arr2[1] + arr1[2] * arr2[2];
194}
195
196// Hash function for vector3
197template <typename T>
199 size_t operator()(const uvgutils::VectorN<T, 3>& vector) const {
200 std::hash<T> hasher;
201 size_t hash = 0;
202 for (const auto& elem : vector) {
203 // Use a simple hash combining algorithm
204 hash ^= hasher(elem) + 0x9e3779b9 + (hash << 6U) + (hash >> 2U);
205 }
206 return hash;
207 }
208};
209
210
211template <typename keyType>
212inline keyType location1DFromCoordinates(const int x, const int y, const int z, const size_t gdb, const size_t gdb2) {
213 return static_cast<keyType>(x) + (static_cast<keyType>(y) << gdb) + (static_cast<keyType>(z) << gdb2);
214}
215
216template <typename keyType>
217inline keyType location1DFromPoint(const uvgutils::VectorN<typeGeometryInput, 3>& point, const size_t gdb, const size_t gdb2) {
218 return location1DFromCoordinates<keyType>(point[0],point[1],point[2],gdb,gdb2);
219}
220
221// The voxelization is done so to have those three vectors sharing the same order (that is sharing the same voxel index) :
222// -> A list of voxel (vector of coordinates)
223// -> A list of list of points (vector of vector of point index). This associate a voxel with the points inside it.
224// -> A list of voxel PPI (vector of PPI (int))
225//
226// Example with a voxel index i:
227// voxelizedPointsGeometry[i] stores the coordinate of the voxel i
228// voxelIdToPointsId[i] stores the index of the points inside the voxel i
229// voxelsPPIs[i] stores the PPI of the voxel i
230//
231// 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
232// that has already been created. For an input point j: voxelCoordToVoxelIndex[ voxelCoordsOf(j) ] = i
233//
234// This structure makes computation easier for the patch generation and for applying voxel data to points data.
235// To achieve this, we need a temporary map that interfaces the voxel coordinates with the voxel index in those three list.
236
237// voxelizedPointsGeometry -> list of the voxels. That is, a list of the coordinates of the voxels.
238// voxelIdToPointsId -> For each voxel, stores the index of the points inside. A voxel is defined by a voxel index, that is given by the
239// filling order of the vector. Thus, the third voxel to be added to this structure will have a voxel index of 2.
240// voxelCoordToVoxelIndex -> Temporary structure. Associate a voxel to its index in 'voxelIdToPointsId' (its voxel index). This structure is a
241// map whose key is a voxel coordinate and the value is a voxel index.
242//
243// For each point if the input point cloud :
244// Compute the coordinates of the voxel inside which the current point should be.
245// If those voxel coordinates are already in the map 'voxelCoordToVoxelIndex':
246// Add the current input point index to the list of points index of the found voxel.
247// That is, add the current input point index to the list of points of the voxel in 'voxelIdToPointsId'
248// If those voxel coordinates are not in the map 'voxelCoordToVoxelIndex':
249// Create a new item in the map 'voxelCoordToVoxelIndex'. This associates the voxel coordinates to its voxel index (the current size
250// of the list of voxel === the index of this voxel in the list of voxel). Add a new voxel to 'voxelIdToPointsId'. This associates the
251// 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
252// point index. Create a new voxel by adding the voxel coordinates in the voxel list 'voxelizedPointsGeometry'. Add the current input
253// 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
254// voxel in 'voxelIdToPointsId'
255
256// Notice that in the refined segmentation, the inputPointsGeometry can be the voxelizedPointsGeometry. Indeed, when the voxelization is
257// activated, the refined segmentation is applying a second voxelization step. The created voxelized point cloud is then the result of two
258// voxelizations.
259inline void voxelization(
260 const std::vector<uvgutils::VectorN<typeGeometryInput, 3>>& inputPointsGeometry,
261 std::vector<uvgutils::VectorN<typeGeometryInput, 3>>& voxelizedPointsGeometry,
262 std::vector<size_t>& pointsIdToVoxelId,
263 const size_t inputBitResolution,
264 const size_t outputBitResolution
265)
266{
267 uvgutils::Logger::log<uvgutils::LogLevel::TRACE>("PATCH GENERATION", "Voxelization from " + std::to_string(inputBitResolution) + " to " +
268 std::to_string(outputBitResolution) + " bits of resolution.\n");
269 pointsIdToVoxelId.resize(inputPointsGeometry.size());
270
271 const size_t voxelizationShift = inputBitResolution - outputBitResolution;
272 const typeGeometryInput shift = static_cast<typeGeometryInput>(voxelizationShift);
273
274 const size_t approximateVoxelCount = 1U << (outputBitResolution * 2U);
275 voxelizedPointsGeometry.reserve(approximateVoxelCount);
276
278 voxelCoordToVoxelIndex.reserve(approximateVoxelCount);
279 size_t voxelIndex = 0;
280 for (size_t inputPointIndex = 0; inputPointIndex < inputPointsGeometry.size(); ++inputPointIndex) {
281 const uvgutils::VectorN<typeGeometryInput, 3> & inputPoint = inputPointsGeometry[inputPointIndex];
282
283 uvgutils::VectorN<typeGeometryInput, 3> voxCoord{static_cast<typeGeometryInput>(static_cast<uint32_t>(inputPoint[0]) >> shift),
284 static_cast<typeGeometryInput>(static_cast<uint32_t>(inputPoint[1]) >> shift),
285 static_cast<typeGeometryInput>(static_cast<uint32_t>(inputPoint[2]) >> shift)};
286
287 auto [it, inserted] = voxelCoordToVoxelIndex.try_emplace(voxCoord, voxelIndex);
288 if (inserted) {
289 voxelizedPointsGeometry.emplace_back(voxCoord);
290 voxelIndex++;
291 }
292 pointsIdToVoxelId[inputPointIndex] = voxelCoordToVoxelIndex[voxCoord];
293 }
294}
Definition robin_hood.h:921
std::pair< iterator, bool > try_emplace(const key_type &key, Args &&... args)
Definition robin_hood.h:1835
void reserve(size_t c)
Definition robin_hood.h:2084
Definition utils.hpp:49
Definition uvgvpcc.hpp:56
uint16_t typeGeometryInput
Definition constants.hpp:48
int y
Definition slicingComputation.cpp:172
int x
Definition slicingComputation.cpp:172
Definition utilsPatchGeneration.hpp:198
size_t operator()(const uvgutils::VectorN< T, 3 > &vector) const
Definition utilsPatchGeneration.hpp:199
keyType location1DFromCoordinates(const int x, const int y, const int z, const size_t gdb, const size_t gdb2)
Definition utilsPatchGeneration.hpp:212
double dotProduct(const std::array< T, 3 > &arr1, const std::array< TT, 3 > &arr2)
Definition utilsPatchGeneration.hpp:192
const std::array< uvgutils::VectorN< uint8_t, 3 >, 114 > patchColors
Definition utilsPatchGeneration.hpp:153
keyType location1DFromPoint(const uvgutils::VectorN< typeGeometryInput, 3 > &point, const size_t gdb, const size_t gdb2)
Definition utilsPatchGeneration.hpp:217
void voxelization(const std::vector< uvgutils::VectorN< typeGeometryInput, 3 > > &inputPointsGeometry, std::vector< uvgutils::VectorN< typeGeometryInput, 3 > > &voxelizedPointsGeometry, std::vector< size_t > &pointsIdToVoxelId, const size_t inputBitResolution, const size_t outputBitResolution)
Definition utilsPatchGeneration.hpp:259