Three-dimensional image to achieve cutting in any direction and size

Study-VTK: Realize the cutting of any plane

0 背景:
1 案例:
2切割类介绍
    3.0 vtkExtractVOI
    3.1 vtkImageReslice
3 案例实现
    1.1 切割的实现:
    1.2 计算焦点点:
    1.3 一般输入影像都是dcm,需要保存成vti:

0 Background:

vtk has two categories for 3D image (picture) extraction, vtkExtractVOI and vtkImageReslice. Reasonable use of these two can realize 3D image cutting of any size and direction.
  doxygen document: vtkExtractVOI, vtkImageReslice
1 Case:

For the demo implemented with these two classes, first use vtkImageReslice to pass through the image center, and cut the image with the process coordinate system. Then calculate the distance from the four points to the center and convert it to the corresponding coordinates on the new image, and then use vtkExtractVOI to cut. Realize model cutting in any direction and size. This method can only have two axes at a time, so repeat it again (the second time x and y are exactly opposite). The gap cannot be ignored when calculating, otherwise the newly cut out will be deformed.

process x轴:长轴(鼠标选的前两个点)方向;
process y轴:original 的Z轴方向;
process z轴:鼠标选的后两个点方向。

The four point selection uses vtkBiDimensionalWidget

Please add a picture description Please add a picture description
2 Cutting class introduction
3.0 vtkExtractVOI

vtkExtractVOI is a filter used to select a part of the input structured point data set or to subsample the input data set. (The selected part of interest is called the volume of interest or VOI.) The output of this filter is a structured point data set. This filter processes input data of any topological size (ie point, line, image or volume), and can generate output data of any topological size.
  To use this filter, set VOI ivar, which are ijk minimum/maximum indexes, which are used to specify a rectangular area in the data. (Note that these are 0 offsets.) You can also specify the sampling rate to subsample the data.
  Typical applications of this filter are to extract slices from a volume for image processing, sub-sampling a large volume to reduce the data size, or use the data of interest to extract a volume area.
  usage:

virtual void SetVOI (int[6])
// Extract the diagonal coordinates of roi in the original image coordinate system
virtual void SetSampleRate (int, int, int)
// sampling frequency on the three axes of xyz

1
2
3
4

3.1 vtkImageReslice

Recut the volume along a new set of axes.
  vtkImageReslice is the Swiss Army Knife of image geometry filters: it can replace, rotate, flip, scale, resample, deform and fill image data in any combination with very high efficiency.
  usage:

SetOutputDimensionality (int);
// 设置输出 结果是几维的 1/2/3
SetResliceAxes (vtkMatrix4x4 *);
// 设置变换矩阵
SetInterpolationModeToLinear();
// 设置采样方法

1
2
3
4
5
6

The transformation matrix has many interface inputs
. Insert picture description here.
  Resampling method: linear, cubic linear, nearest neighbor
. Insert picture description here.
3 Case realization
1.1 The realization of cutting:

Calculate the new coordinate system and cut with vtkImageReslice

QList<QList<double>>points = original_bidimensional_->GetDisplayPosition();
QList<double> center = vti_original_widget_->GetCenter();
QList<double> origin = vti_original_widget_->GetOrigin();
QList<double> spacing = vti_original_widget_->GetSpacing();
QList<qint32> extent = vti_original_widget_->GetExtent();
double k = (points.at(0).at(1) - points.at(1).at(1)) /
           (points.at(0).at(0) - points.at(1).at(0));
double jiaodu = atan(k);
double axialElements[16] = {
    cos(jiaodu), 0, cos(jiaodu + 1.57), 0,
    sin(jiaodu), 0,  sin(jiaodu + 1.57), 0,
    0, 1, 0, 0,
    0, 0, 0, 1
};
vtkNew<vtkMatrix4x4> resliceAxes ;
resliceAxes->DeepCopy(axialElements);
resliceAxes->SetElement(0, 3, center.at(0));
resliceAxes->SetElement(1, 3, center.at(1));
resliceAxes->SetElement(2, 3, center.at(2));
vtkNew<vtkImageReslice> reslice;
reslice->SetInputData(vti_original_widget_->GetVtkImageData());
reslice->SetOutputDimensionality(3);
reslice->SetResliceAxes(resliceAxes);
reslice->SetInterpolationModeToLinear();
reslice->Update();

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

Calculate the distance and extract it with vtkExtractVOI.

int dims[3];
reslice->GetOutput()->GetDimensions(dims);
vtkNew extract_voi;
extract_voi->SetInputData(reslice->GetOutput());
qint32 new_z1, new_z2, new_y1, new_y2;
{
double b = center.at(1) - center.at(0) * k;
double line_a, line_b, line_c;
line_a = k;
line_b = -1;
line_c = b;
double length1, length2;
length1 = abs(line_a * points.at(2).at(0) +
line_b * points.at(2).at(1) +
line_c)
/ sqrt(line_a * line_a + line_b * line_b);
length2 = abs(line_a * points.at(3).at(0) +
line_b * points.at(3).at(1) +
line_c)
/ sqrt(line_a * line_a + line_b * line_b);
new_z1 =
static_cast(0.5 * (extent.at(1) - extent.at(0)) - length1 / spacing.at(0));
new_z2 =
static_cast(0.5 * (extent.at(1) - extent.at(0)) + length2 / spacing.at(0));
}
{
double b = center.at(1) - center.at(0) * (-1 / k);
double line_a, line_b, line_c;
line_a = -1 / k;
line_b = -1;
line_c = b;
double length1, length2;
length1 = abs(line_a * points.at(1).at(0) +
line_b * points.at(1).at(1) +
line_c)
/ sqrt(line_a * line_a + line_b * line_b);
length2 = abs(line_a * points.at(0).at(0) +
line_b * points.at(0).at(1) +
line_c)
/ sqrt(line_a * line_a + line_b * line_b);
new_y1 =
static_cast(0.5 * (extent.at(3) - extent.at(2)) -
(length1 / spacing.at(0)));
new_y2 =
static_cast(0.5 * (extent.at(3) - extent.at(2)) +
(length2 / spacing.at(0)));
}
extract_voi->SetVOI(
new_y1 > new_y2 ? new_y2 : new_y1,
new_y1 > new_y2 ? new_y1 : new_y2,
0, dims[1],
new_z1 > new_z2 ? new_z2 : new_z1,
new_z1 > new_z2 ? new_z1 : new_z2
);
extract_voi->Update();
vti_process_widget_->SetVtkImageData(extract_voi->GetOutput());
vti_process_widget_->BuildView();

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

1.2 Calculate the focal point:

vtkBiDimensionalRepresentation2D seems to have only four point world coordinates and local coordinates and two straight line lengths. There is no central focus, so you need to find it yourself.

QList<QList > ImageBiDimensional::GetDisplayPosition() const {
double p1[3];
representation_->GetPoint1WorldPosition(p1);
double p2[3];
representation_->GetPoint2WorldPosition(p2);
double p3[3];
representation_->GetPoint3WorldPosition(p3);
double p4[3];
representation_->GetPoint4WorldPosition(p4);
QList<QList>points;
QList point1, point2, point3, point4, point5;
point1 << p1[0] << p1[1] << p1[2];
point2 << p2[0] << p2[1] << p2[2];
point3 << p3[0] << p3[1] << p3[2];
point4 << p4[0] << p4[1] << p4[2];
points << point1 << point2 << point3 << point4;
double p5[2];
double a1 = p2[1] - p1[1];
double b1 = p1[0] - p2[0];
double c1 = p1[0] * p2[1] - p2[0] * p1[1];
double a2 = p4[1] - p3[1];
double b2 = p3[0] - p4[0];
double c2 = p3[0] * p4[1] - p4[0] * p3[1];
double det = a1 * b2 - a2 * b1;
p5[0] = (c1 * b2 - c2 * b1) / det;
p5[1] = (a1 * c2 - a2 * c1) / det;
point5 << p5[0] << p5[1];
points << point5;
return points;
}

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

1.3 Generally, input images are dcm and need to be saved as vti:

It feels that vtkDICOMXXXXX is difficult to use, so dcm and nii images are read and written with itk, and then converted to vti. mhd can be read and written directly with vtk.

IntensityWindowingImageFilterType::Pointer intensityFilter =
    IntensityWindowingImageFilterType::New();
ReaderType::Pointer reader = ReaderType::New();
ImageIOType::Pointer dicomIO = ImageIOType::New();
reader->SetImageIO(dicomIO);
NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
nameGenerator->SetUseSeriesDetails(true);
nameGenerator->SetDirectory("/home/yx/Pictures/影像/Deeplv_测试影像/75%");
using SeriesIdContainer = std::vector< std::string >;
const SeriesIdContainer &seriesUID = nameGenerator->GetSeriesUIDs();
auto seriesItr = seriesUID.begin();
auto seriesEnd = seriesUID.end();
using FileNamesContainer = std::vector< std::string >;
FileNamesContainer fileNames ;
std::string seriesIdentifier;
while (seriesItr != seriesEnd) {
    seriesIdentifier = seriesItr->c_str();
    fileNames = nameGenerator->GetFileNames(seriesIdentifier);
    ++seriesItr;
}
reader->SetFileNames(fileNames);
try {
    reader->Update();
} catch (itk::ExceptionObject &ex) {
    Q_UNUSED(ex)
    qWarning() << "read error";
}
intensityFilter->SetInput(reader->GetOutput());
intensityFilter->SetWindowMinimum(-200);
intensityFilter->SetWindowMaximum(400);
intensityFilter->SetOutputMinimum(0);
intensityFilter->SetOutputMaximum(1);
intensityFilter->Update();
typedef itk::ImageToVTKImageFilter< ImageType> itkTovtkFilterType;
itkTovtkFilterType::Pointer itkTovtkImageFilter = itkTovtkFilterType::New();
itkTovtkImageFilter->SetInput(intensityFilter->GetOutput());
itkTovtkImageFilter->Update();
vtkSmartPointer<vtkImageData> double_image_;
if (double_image_ == nullptr) {
    double_image_ = vtkSmartPointer<vtkImageData>::New();
}
double_image_->DeepCopy(itkTovtkImageFilter->GetOutput());
qint32 extent[6];
double spacing[3];
double origin[3];
double_image_->GetExtent(extent);
double_image_->GetSpacing(spacing);
double_image_->GetOrigin(origin);
qDebug() << extent[0] << extent[1] << extent[2] << extent[3] << extent[4] << extent[5];
qDebug() << spacing[0] << spacing[1] << spacing[2];
qDebug() << origin[0] << origin[1] << origin[2];
vtkNew<vtkXMLImageDataWriter> writer;
writer->SetInputData(double_image_);
writer->SetFileName("/home/yx/Desktop/original.vti");
writer->Write();

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

vtk learning tutorial
Study-VTK

This case code:
https://gitee.com/yaoxin001/WorkDemo

Personal blog homepage
http://118.25.63.144/

Guess you like

Origin blog.csdn.net/qq_23158477/article/details/113091620