geometry_trisoup_encoder.cpp 11.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* The copyright in this software is being made available under the BSD
 * Licence, included below.  This software may be subject to other third
 * party and contributor rights, including patent rights, and no such
 * rights are granted under this licence.
 *
 * Copyright (c) 2017-2018, ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of the ISO/IEC nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "geometry_trisoup.h"

38
#include "pointset_processing.h"
39
40
41
42
43
44
45
46
47
48
49
50
#include "geometry.h"
#include "geometry_octree.h"

namespace pcc {

//============================================================================

void
encodeGeometryTrisoup(
  const GeometryParameterSet& gps,
  const GeometryBrickHeader& gbh,
  PCCPointSet3& pointCloud,
51
  EntropyEncoder* arithmeticEncoder)
52
53
54
55
56
{
  // trisoup uses octree coding until reaching the triangulation level.
  pcc::ringbuf<PCCOctree3Node> nodes;
  encodeGeometryOctree(gps, gbh, pointCloud, arithmeticEncoder, &nodes);

57
  int blockWidth = 1 << gps.trisoup_node_size_log2;
58
59
60
61
62
63
64
65
66

  uint32_t symbolCount;

  // Determine segind and vertices.
  std::vector<bool> segind;
  std::vector<uint8_t> vertices;
  determineTrisoupVertices(nodes, segind, vertices, pointCloud, blockWidth);

  // Encode segind to bitstream.
Satoru KUMA's avatar
Satoru KUMA committed
67
68
69
70
71
  //AdaptiveMAryModel multiSymbolSegindModel0(256);
  AdaptiveBitModel ctxTempSeg;

  symbolCount = segind.size();
  //segind.resize(8 * symbolCount, false);
72
73

  // todo(df): consider a more appropriate signalling method
74
75
76
  AdaptiveBitModel ctxTemp;
  StaticBitModel ctxBypass;
  arithmeticEncoder->encodeExpGolomb(symbolCount, 0, ctxBypass, ctxTemp);
77

Satoru KUMA's avatar
Satoru KUMA committed
78
  //int uniqueIndex = 0;
79
  for (uint32_t i = 0; i < symbolCount; i++) {
Satoru KUMA's avatar
Satoru KUMA committed
80
    arithmeticEncoder->encode((int)segind[i], ctxTempSeg);
81
82
83
  }

  // Encode vertices to bitstream.
84
  AdaptiveMAryModel multiSymbolVerticesModel0(blockWidth);
85
86
87
  symbolCount = vertices.size();

  // todo(df): consider a more appropriate signalling method
88
  arithmeticEncoder->encodeExpGolomb(symbolCount, 0, ctxBypass, ctxTemp);
89
90
91
92
93
94
95

  for (uint32_t i = 0; i < symbolCount; i++) {
    uint8_t c = vertices[i];
    arithmeticEncoder->encode(uint32_t(c), multiSymbolVerticesModel0);
  }

  // Decode refinedVertices from segind and vertices.
96
  int32_t maxval = (1 << gbh.geomMaxNodeSizeLog2(gps)) - 1;
97
  decodeTrisoupCommon(nodes, segind, vertices, pointCloud, blockWidth, maxval);
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
}

//---------------------------------------------------------------------------
// Determine where the surface crosses each leaf
// (i.e., determine the segment indicators and vertices)
// from the set of leaves and the points in each leaf.
//
// @param leaves, list of blocks containing the surface
// @param segind, indicators for edges of blocks if they intersect the surface
// @param vertices, locations of intersections

void
determineTrisoupVertices(
  const ringbuf<PCCOctree3Node>& leaves,
  std::vector<bool>& segind,
  std::vector<uint8_t>& vertices,
  const PCCPointSet3& pointCloud,
  const int defaultBlockWidth)
{
  // Put all leaves' edges into a list.
  std::vector<TrisoupSegmentEnc> segments;
  for (int i = 0; i < leaves.size(); i++) {
    const auto& leaf = leaves[i];

    // Width of block.
    // in future, may override with leaf blockWidth
124
    const int32_t blockWidth = defaultBlockWidth;
125
126

    // Eight corners of block.
127
128
129
130
131
132
133
134
    const Vec3<int32_t> pos000({0, 0, 0});
    const Vec3<int32_t> posW00({blockWidth, 0, 0});
    const Vec3<int32_t> pos0W0({0, blockWidth, 0});
    const Vec3<int32_t> posWW0({blockWidth, blockWidth, 0});
    const Vec3<int32_t> pos00W({0, 0, blockWidth});
    const Vec3<int32_t> posW0W({blockWidth, 0, blockWidth});
    const Vec3<int32_t> pos0WW({0, blockWidth, blockWidth});
    const Vec3<int32_t> posWWW({blockWidth, blockWidth, blockWidth});
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

    // x: left to right; y: bottom to top; z: far to near
    TrisoupSegmentEnc seg000W00 =  // far bottom edge
      {leaf.pos + pos000, leaf.pos + posW00, 12 * i + 0, -1, -1, 0, 0};
    TrisoupSegmentEnc seg0000W0 =  // far left edge
      {leaf.pos + pos000, leaf.pos + pos0W0, 12 * i + 1, -1, -1, 0, 0};
    TrisoupSegmentEnc seg0W0WW0 =  // far top edge
      {leaf.pos + pos0W0, leaf.pos + posWW0, 12 * i + 2, -1, -1, 0, 0};
    TrisoupSegmentEnc segW00WW0 =  // far right edge
      {leaf.pos + posW00, leaf.pos + posWW0, 12 * i + 3, -1, -1, 0, 0};
    TrisoupSegmentEnc seg00000W =  // bottom left edge
      {leaf.pos + pos000, leaf.pos + pos00W, 12 * i + 4, -1, -1, 0, 0};
    TrisoupSegmentEnc seg0W00WW =  // top left edge
      {leaf.pos + pos0W0, leaf.pos + pos0WW, 12 * i + 5, -1, -1, 0, 0};
    TrisoupSegmentEnc segWW0WWW =  // top right edge
      {leaf.pos + posWW0, leaf.pos + posWWW, 12 * i + 6, -1, -1, 0, 0};
    TrisoupSegmentEnc segW00W0W =  // bottom right edge
      {leaf.pos + posW00, leaf.pos + posW0W, 12 * i + 7, -1, -1, 0, 0};
    TrisoupSegmentEnc seg00WW0W =  // near bottom edge
      {leaf.pos + pos00W, leaf.pos + posW0W, 12 * i + 8, -1, -1, 0, 0};
    TrisoupSegmentEnc seg00W0WW =  // near left edge
      {leaf.pos + pos00W, leaf.pos + pos0WW, 12 * i + 9, -1, -1, 0, 0};
    TrisoupSegmentEnc seg0WWWWW =  // near top edge
      {leaf.pos + pos0WW, leaf.pos + posWWW, 12 * i + 10, -1, -1, 0, 0};
    TrisoupSegmentEnc segW0WWWW =  // near right edge
      {leaf.pos + posW0W, leaf.pos + posWWW, 12 * i + 11, -1, -1, 0, 0};

    // Each voxel votes for a position along each edge it is close to
    for (int j = leaf.start; j < leaf.end; j++) {
164
      Vec3<int> voxel;
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
      voxel.x() = int(pointCloud[j].x()) - leaf.pos.x();
      voxel.y() = int(pointCloud[j].y()) - leaf.pos.y();
      voxel.z() = int(pointCloud[j].z()) - leaf.pos.z();
      // parameter indicating threshold of how close voxels must be to edge
      // to be relevant
      int tmin = 1;
      int tmax = blockWidth - tmin - 1;
      if (voxel.y() < tmin && voxel.z() < tmin) {
        seg000W00.count++;
        seg000W00.distanceSum += voxel.x();
      }  // far bottom edge
      if (voxel.x() < tmin && voxel.z() < tmin) {
        seg0000W0.count++;
        seg0000W0.distanceSum += voxel.y();
      }  // far left edge
      if (voxel.y() > tmax && voxel.z() < tmin) {
        seg0W0WW0.count++;
        seg0W0WW0.distanceSum += voxel.x();
      }  // far top edge
      if (voxel.x() > tmax && voxel.z() < tmin) {
        segW00WW0.count++;
        segW00WW0.distanceSum += voxel.y();
      }  // far right edge
      if (voxel.x() < tmin && voxel.y() < tmin) {
        seg00000W.count++;
        seg00000W.distanceSum += voxel.z();
      }  // bottom left edge
      if (voxel.x() < tmin && voxel.y() > tmax) {
        seg0W00WW.count++;
        seg0W00WW.distanceSum += voxel.z();
      }  // top left edge
      if (voxel.x() > tmax && voxel.y() > tmax) {
        segWW0WWW.count++;
        segWW0WWW.distanceSum += voxel.z();
      }  // top right edge
      if (voxel.x() > tmax && voxel.y() < tmin) {
        segW00W0W.count++;
        segW00W0W.distanceSum += voxel.z();
      }  // bottom right edge
      if (voxel.y() < tmin && voxel.z() > tmax) {
        seg00WW0W.count++;
        seg00WW0W.distanceSum += voxel.x();
      }  // near bottom edge
      if (voxel.x() < tmin && voxel.z() > tmax) {
        seg00W0WW.count++;
        seg00W0WW.distanceSum += voxel.y();
      }  // near left edge
      if (voxel.y() > tmax && voxel.z() > tmax) {
        seg0WWWWW.count++;
        seg0WWWWW.distanceSum += voxel.x();
      }  // near top edge
      if (voxel.x() > tmax && voxel.z() > tmax) {
        segW0WWWW.count++;
        segW0WWWW.distanceSum += voxel.y();
      }  // near right edge
    }

    // Push segments onto list.
    segments.push_back(seg000W00);  // far bottom edge
    segments.push_back(seg0000W0);  // far left edge
    segments.push_back(seg0W0WW0);  // far top edge
    segments.push_back(segW00WW0);  // far right edge
    segments.push_back(seg00000W);  // bottom left edge
    segments.push_back(seg0W00WW);  // top left edge
    segments.push_back(segWW0WWW);  // top right edge
    segments.push_back(segW00W0W);  // bottom right edge
    segments.push_back(seg00WW0W);  // near bottom edge
    segments.push_back(seg00W0WW);  // near left edge
    segments.push_back(seg0WWWWW);  // near top edge
    segments.push_back(segW0WWWW);  // near right edge
  }

  // Copy list of segments to another list to be sorted.
  std::vector<TrisoupSegmentEnc> sortedSegments;
  for (int i = 0; i < segments.size(); i++)
    sortedSegments.push_back(segments[i]);

  // Sort the list and find unique segments.
  std::sort(sortedSegments.begin(), sortedSegments.end());
  std::vector<TrisoupSegmentEnc> uniqueSegments;
  uniqueSegments.push_back(sortedSegments[0]);
  segments[sortedSegments[0].index].uniqueIndex = 0;
  for (int i = 1; i < sortedSegments.size(); i++) {
    if (
      uniqueSegments.back().startpos != sortedSegments[i].startpos
      || uniqueSegments.back().endpos != sortedSegments[i].endpos) {
      // sortedSegment[i] is different from uniqueSegments.back().
      // Start a new uniqueSegment.
      uniqueSegments.push_back(sortedSegments[i]);  // unique segment
    } else {
      // sortedSegment[i] is the same as uniqueSegments.back().
      // Accumulate into uniqueSegment.back().
      uniqueSegments.back().count += sortedSegments[i].count;
      uniqueSegments.back().distanceSum += sortedSegments[i].distanceSum;
    }
    segments[sortedSegments[i].index].uniqueIndex = uniqueSegments.size() - 1;
  }

  // Compute vertex for each unique segment that intersects the surface.
  for (int i = 0; i < uniqueSegments.size(); i++) {
    if (uniqueSegments[i].count > 0) {  // intersects the surface
      segind.push_back(true);
      uint8_t vertex =
        (uniqueSegments[i].distanceSum + uniqueSegments[i].count / 2)
        / uniqueSegments[i].count;
      vertices.push_back(vertex);
      uniqueSegments[i].vertex = vertex;
    } else {  // does not intersect the surface
      segind.push_back(false);
      uniqueSegments[i].vertex = -1;
    }
  }

  // Copy vertices back to original (non-unique, non-sorted) segments.
  for (int i = 0; i < segments.size(); i++) {
    segments[i].vertex = uniqueSegments[segments[i].uniqueIndex].vertex;
  }
}

//============================================================================

}  // namespace pcc