当前位置:网站首页>[VTK] vtkPolydataToImageStencil 源码解读
[VTK] vtkPolydataToImageStencil 源码解读
2022-07-03 10:04:00 【comedate】
1. 类的作用:
模板类将多边形数据转换为图像模板。
多段数据可以是封闭曲面网格,也可以是一系列多段线轮廓(每个切片一个轮廓)
2. 主要步骤:
Description of algorithm:
- cut the polydata at each z slice to create polylines
沿 Z 切片方向创建折线 - find all “loose ends” and connect them to make polygons
(if the input polydata is closed, there will be no loose ends)
找到所以未了结的部分,并连接成多边形 - go through all line segments, and for each integer y value on a line segment,
store the x value at that point in a bucket
遍历所有线段,对于每个线段上的每个整数y值,存储在该点的x值 - for each z integer index, find all the stored x values and use them to create one z slice of the vtkStencilData
对于每个z整数索引,查找所有存储的x值,并使用它们来创建一个z切片的 vtkstencildata
vtkImageStencilRaster raster(&extent[2]);
此光栅通过记录曲面上每个y整数位置的所有“x”位置来存储所有线段。
3. 代码详解:
源码中,最主要的实现函数是: 线程执行函数:
void vtkPolyDataToImageStencil::ThreadedExecute(
vtkImageStencilData *data,
int extent[6],
int threadId)
{
// the spacing and origin of the generated stencil
double *spacing = data->GetSpacing();
double *origin = data->GetOrigin();
// if we have no data then return
if (!this->GetInput()->GetNumberOfPoints())
{
return;
}
// Only divide once
double invspacing[3];
invspacing[0] = 1.0/spacing[0];
invspacing[1] = 1.0/spacing[1];
invspacing[2] = 1.0/spacing[2];
// get the input data
vtkPolyData *input = this->GetInput();
// the output produced by cutting the polydata with the Z plane
vtkPolyData *slice = vtkPolyData::New();
// This raster stores all line segments by recording all "x"
// positions on the surface for each y integer position.
vtkImageStencilRaster raster(&extent[2]);
raster.SetTolerance(this->Tolerance);
// The extent for one slice of the image
int sliceExtent[6];
sliceExtent[0] = extent[0]; sliceExtent[1] = extent[1];
sliceExtent[2] = extent[2]; sliceExtent[3] = extent[3];
sliceExtent[4] = extent[4]; sliceExtent[5] = extent[4];
// Loop through the slices
for (int idxZ = extent[4]; idxZ <= extent[5]; idxZ++)
{
if (threadId == 0)
{
this->UpdateProgress((idxZ - extent[4])*1.0/(extent[5] - extent[4] + 1));
}
double z = idxZ*spacing[2] + origin[2];
slice->PrepareForNewData();
raster.PrepareForNewData();
// Step 1: Cut the data into slices
if (input->GetNumberOfPolys() > 0 || input->GetNumberOfStrips() > 0)
{
this->PolyDataCutter(input, slice, z);
}
else
{
// if no polys, select polylines instead
this->PolyDataSelector(input, slice, z, spacing[2]);
}
if (!slice->GetNumberOfLines())
{
continue;
}
// convert to structured coords via origin and spacing
vtkPoints *points = slice->GetPoints();
vtkIdType numberOfPoints = points->GetNumberOfPoints();
for (vtkIdType j = 0; j < numberOfPoints; j++)
{
double tempPoint[3];
points->GetPoint(j, tempPoint);
tempPoint[0] = (tempPoint[0] - origin[0])*invspacing[0];
tempPoint[1] = (tempPoint[1] - origin[1])*invspacing[1];
tempPoint[2] = (tempPoint[2] - origin[2])*invspacing[2];
points->SetPoint(j, tempPoint);
}
// Step 2: Find and connect all the loose ends
std::vector<vtkIdType> pointNeighbors(numberOfPoints);
std::vector<vtkIdType> pointNeighborCounts(numberOfPoints);
std::fill(pointNeighborCounts.begin(), pointNeighborCounts.end(), 0);
// get the connectivity count for each point
vtkCellArray *lines = slice->GetLines();
vtkIdType npts = 0;
vtkIdType *pointIds = nullptr;
vtkIdType count = lines->GetNumberOfConnectivityEntries();
for (vtkIdType loc = 0; loc < count; loc += npts + 1)
{
lines->GetCell(loc, npts, pointIds);
if (npts > 0)
{
pointNeighborCounts[pointIds[0]] += 1;
for (vtkIdType j = 1; j < npts-1; j++)
{
pointNeighborCounts[pointIds[j]] += 2;
}
pointNeighborCounts[pointIds[npts-1]] += 1;
if (pointIds[0] != pointIds[npts-1])
{
// store the neighbors for end points, because these are
// potentially loose ends that will have to be dealt with later
pointNeighbors[pointIds[0]] = pointIds[1];
pointNeighbors[pointIds[npts-1]] = pointIds[npts-2];
}
}
}
// use connectivity count to identify loose ends and branch points
std::vector<vtkIdType> looseEndIds;
std::vector<vtkIdType> branchIds;
for (vtkIdType j = 0; j < numberOfPoints; j++)
{
if (pointNeighborCounts[j] == 1)
{
looseEndIds.push_back(j);
}
else if (pointNeighborCounts[j] > 2)
{
branchIds.push_back(j);
}
}
// remove any spurs 删除支线
for (size_t b = 0; b < branchIds.size(); b++)
{
for (size_t i = 0; i < looseEndIds.size(); i++)
{
if (pointNeighbors[looseEndIds[i]] == branchIds[b])
{
// mark this pointId as removed
pointNeighborCounts[looseEndIds[i]] = 0;
looseEndIds.erase(looseEndIds.begin() + i);
i--;
if (--pointNeighborCounts[branchIds[b]] <= 2)
{
break;
}
}
}
}
// join any loose ends 连接所以的未了结的点
while (looseEndIds.size() >= 2)
{
size_t n = looseEndIds.size();
// search for the two closest loose ends
double maxval = -VTK_FLOAT_MAX;
vtkIdType firstIndex = 0;
vtkIdType secondIndex = 1;
bool isCoincident = false;
bool isOnHull = false;
for (size_t i = 0; i < n && !isCoincident; i++)
{
// first loose end
vtkIdType firstLooseEndId = looseEndIds[i];
vtkIdType neighborId = pointNeighbors[firstLooseEndId];
double firstLooseEnd[3];
slice->GetPoint(firstLooseEndId, firstLooseEnd);
double neighbor[3];
slice->GetPoint(neighborId, neighbor);
for (size_t j = i+1; j < n; j++)
{
vtkIdType secondLooseEndId = looseEndIds[j];
if (secondLooseEndId != neighborId)
{
double currentLooseEnd[3];
slice->GetPoint(secondLooseEndId, currentLooseEnd);
// When connecting loose ends, use dot product to favor
// continuing in same direction as the line already
// connected to the loose end, but also favour short
// distances by dividing dotprod by square of distance.
double v1[2], v2[2];
v1[0] = firstLooseEnd[0] - neighbor[0];
v1[1] = firstLooseEnd[1] - neighbor[1];
v2[0] = currentLooseEnd[0] - firstLooseEnd[0];
v2[1] = currentLooseEnd[1] - firstLooseEnd[1];
double dotprod = v1[0]*v2[0] + v1[1]*v2[1];
double distance2 = v2[0]*v2[0] + v2[1]*v2[1];
// check if points are coincident
if (distance2 == 0)
{
firstIndex = static_cast<vtkIdType>(i);
secondIndex = static_cast<vtkIdType>(j);
isCoincident = true;
break;
}
// prefer adding segments that lie on hull
double midpoint[2], normal[2];
midpoint[0] = 0.5*(currentLooseEnd[0] + firstLooseEnd[0]);
midpoint[1] = 0.5*(currentLooseEnd[1] + firstLooseEnd[1]);
normal[0] = currentLooseEnd[1] - firstLooseEnd[1];
normal[1] = -(currentLooseEnd[0] - firstLooseEnd[0]);
double sidecheck = 0.0;
bool checkOnHull = true;
for (size_t k = 0; k < n; k++)
{
if (k != i && k != j)
{
double checkEnd[3];
slice->GetPoint(looseEndIds[k], checkEnd);
double dotprod2 = ((checkEnd[0] - midpoint[0])*normal[0] +
(checkEnd[1] - midpoint[1])*normal[1]);
if (dotprod2*sidecheck < 0)
{
checkOnHull = false;
}
sidecheck = dotprod2;
}
}
// check if new candidate is better than previous one
if ((checkOnHull && !isOnHull) ||
(checkOnHull == isOnHull && dotprod > maxval*distance2))
{
firstIndex = static_cast<vtkIdType>(i);
secondIndex = static_cast<vtkIdType>(j);
isOnHull |= checkOnHull;
maxval = dotprod/distance2;
}
}
}
}
// get info about the two loose ends and their neighbors
vtkIdType firstLooseEndId = looseEndIds[firstIndex];
vtkIdType neighborId = pointNeighbors[firstLooseEndId];
double firstLooseEnd[3];
slice->GetPoint(firstLooseEndId, firstLooseEnd);
double neighbor[3];
slice->GetPoint(neighborId, neighbor);
vtkIdType secondLooseEndId = looseEndIds[secondIndex];
vtkIdType secondNeighborId = pointNeighbors[secondLooseEndId];
double secondLooseEnd[3];
slice->GetPoint(secondLooseEndId, secondLooseEnd);
double secondNeighbor[3];
slice->GetPoint(secondNeighborId, secondNeighbor);
// remove these loose ends from the list
looseEndIds.erase(looseEndIds.begin() + secondIndex);
looseEndIds.erase(looseEndIds.begin() + firstIndex);
if (!isCoincident)
{
// create a new line segment by connecting these two points
lines->InsertNextCell(2);
lines->InsertCellPoint(firstLooseEndId);
lines->InsertCellPoint(secondLooseEndId);
}
}
// Step 3: Go through all the line segments for this slice,
// and for each integer y position on the line segment,
// drop the corresponding x position into the y raster line.
count = lines->GetNumberOfConnectivityEntries();
for (vtkIdType loc = 0; loc < count; loc += npts + 1)
{
lines->GetCell(loc, npts, pointIds);
if (npts > 0)
{
vtkIdType pointId0 = pointIds[0];
double point0[3];
points->GetPoint(pointId0, point0);
for (vtkIdType j = 1; j < npts; j++)
{
vtkIdType pointId1 = pointIds[j];
double point1[3];
points->GetPoint(pointId1, point1);
// make sure points aren't flagged for removal
if (pointNeighborCounts[pointId0] > 0 &&
pointNeighborCounts[pointId1] > 0)
{
raster.InsertLine(point0, point1);
}
pointId0 = pointId1;
point0[0] = point1[0];
point0[1] = point1[1];
point0[2] = point1[2];
}
}
}
// Step 4: Use the x values stored in the xy raster to create
// one z slice of the vtkStencilData
sliceExtent[4] = idxZ;
sliceExtent[5] = idxZ;
raster.FillStencilData(data, sliceExtent);
}
slice->Delete();
}
4. 总结技术点:
- 平面与 polydata 相交得到线段
- 线段根据规则连接成多边形
- 遍历 y, 使用 raster.InsertLine 方法填充直线上的所以的点
- 遍历所有的 z, 得到 Z slice
从而得到所以的点;
边栏推荐
- Differences among norm, normalize and normalized in eigen
- Touch and screen automatic rotation debugging
- IIS修改配置信息后不生效
- 大厂技术专家:工程师如何提升沟通能力?
- 线性表的双链表
- Hal - General
- The highest monthly salary of 18K has a good "mentality and choice", and success is poor "seriousness and persistence"~
- Inexplicable problems in the nesting of constraintlayout and relativelayout
- What kind of living condition is a tester with a monthly salary of more than 10000?
- 数据库增量备份 - DB INCR DB FULL
猜你喜欢

Clion debug

I have been doing software testing for three years, and my salary is less than 20K. Today, I put forward my resignation

可以写进简历的软件测试电商项目,不进来get一下?

历经一个月,终于拿到金蝶Offer!分享一下四面面经+复习资料

Probability theory: application of convolution in calculating moving average

进程与线程

我对测试工作的一些认识(资深测试人员总结)

Multiple IO transfer - preamble

做软件测试三年,薪资不到20K,今天,我提出了辞职…

8年测试工程师总结出来的《测试核心价值》与《0基础转行软件测试超全学习指南》
随机推荐
How did I grow up in the past eight years as a test engineer of meituan? I hope technicians can gain something after reading it
11. Provider service registration of Nacos service registration source code analysis
How to realize automatic testing in embedded software testing?
Qt:qss custom qscrollbar instance
2022-07-02:以下go语言代码输出什么?A:编译错误;B:Panic;C:NaN。 package main import “fmt“ func mai
Logstash backup tracks the data records reported
使用onvif协议操作设备
Touch and screen automatic rotation debugging
Inexplicable problems in the nesting of constraintlayout and relativelayout
Tablespace creation management and control file management
Hard goods | write all the codes as soon as you change the test steps? Why not try yaml to realize data-driven?
在腾讯云容器服务Node上执行 kubectl
QT: QSS custom qtoolbar and qtoolbox instances
Oracle收回权限 & 创建角色
Project management essence reading notes (6)
Unity移动端游戏性能优化简谱之 画面表现与GPU压力的权衡
Summary of the history of Mathematics
8年测试工程师总结出来的《测试核心价值》与《0基础转行软件测试超全学习指南》
项目管理精华读书笔记(七)
EPS电动转向系统分析