2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

vtkimagereader.cpp 11.8 KB
Newer Older
1
2
3
4
// ================================================================================================
// 
// This file is part of the CAMPVis Software Framework.
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
6
7
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
//      Chair for Computer Aided Medical Procedures
8
9
//      Technische Universitaet Muenchen
//      Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
10
// 
11
12
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
// 
13
14
15
16
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
17
// 
18
19
20
21
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
22
23
24
25
26
27
28
29
30
31
// 
// ================================================================================================

#include "vtkimagereader.h"

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

32
#include "cgt/filesystem.h"
33
34
#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagerepresentationdisk.h"
35
#include "core/datastructures/indexedmeshgeometry.h"
36
37
38
39
40
41
42
43
44
45
46
#include "core/datastructures/genericimagerepresentationlocal.h"
#include "core/tools/stringutils.h"

/*
 * Full format specification at http://www.vtk.org/VTK/img/file-formats.pdf
 */

namespace campvis {
    const std::string VtkImageReader::loggerCat_ = "CAMPVis.modules.io.VtkImageReader";

    VtkImageReader::VtkImageReader() 
47
        : AbstractImageReader()
48
49
        , p_imageOffset("ImageOffset", "Image Offset in mm", cgt::vec3(0.f), cgt::vec3(-10000.f), cgt::vec3(10000.f), cgt::vec3(0.1f))
        , p_voxelSize("VoxelSize", "Voxel Size in mm", cgt::vec3(1.f), cgt::vec3(-100.f), cgt::vec3(100.f), cgt::vec3(0.1f))
50
    {
51
        this->_ext.push_back("vtk");
52
        this->p_targetImageID.setValue("VtkImageReader.output");
53

54
55
56
57
        addProperty(p_url);
        addProperty(p_targetImageID);
        addProperty(p_imageOffset);
        addProperty(p_voxelSize);
58
59
60
61
62
63
64
65
66
67
68
69
    }

    VtkImageReader::~VtkImageReader() {

    }

    std::string getTrimmedLine(std::ifstream& file, char delim = '\n') {
        std::string toReturn;
        std::getline(file, toReturn, delim);
        return StringUtils::trim(toReturn);
    }

70
    void VtkImageReader::updateResult(DataContainer& data) {
71
        try {
schultezub's avatar
schultezub committed
72
            std::ifstream file(p_url.getValue().c_str(), std::ifstream::in);
73
            if (!file.is_open() || file.bad())
74
                throw cgt::FileException("Could not open file " + p_url.getValue() + " for reading.", p_url.getValue());
75
76

            std::string curLine = getTrimmedLine(file);
77
            // cppcheck-suppress stlIfStrFind
78
            if (curLine.find("# vtk DataFile Version") != 0)
79
                throw cgt::FileException("Unknown identifier in vtk file.", p_url.getValue());
80
81

            // next line is the header - contains only unimportant data
82
            getTrimmedLine(file);
83
84
85

            // this line is the format
            curLine = StringUtils::lowercase(getTrimmedLine(file));
86
            if (curLine == "binary")
87
                throw cgt::FileException("Binary data format currently unsupported.", p_url.getValue());
88
            else if (curLine != "ascii")
89
                throw cgt::FileException("Unsupported format in vtk file - expected binary or ascii.", p_url.getValue());
90
91
92
93

            // now comes the dataset structure
            curLine = StringUtils::lowercase(getTrimmedLine(file));
            std::vector<std::string> splitted = StringUtils::split(curLine, " ");
94
95
96
97
98
99
            if (splitted.size() == 2 && splitted[0] == "dataset") {
                if (splitted[1] == "structured_points")
                    parseStructuredPoints(data, file);
                else if (splitted[1] == "polydata")
                    parsePolydata(data, file);
                else 
100
                    throw cgt::FileException("Unsupported dataset structure in vtk file - expected \"DATASET STRUCTURED_POINTS\" or \"DATASET POLYDATA\".", p_url.getValue());
101
102
            }
            else {
103
                throw cgt::FileException("Unexpected tokens in vtk file.", p_url.getValue());
104
105
            }

106
        }
107
        catch (cgt::Exception& e) {
108
109
110
111
112
113
114
115
            LERROR("Error while parsing VTK file: " << e.what());
            return;
        }
        catch (std::exception& e) {
            LERROR("Error while parsing VTK file: " << e.what());
            return;
        }
    }
116

117
    void VtkImageReader::parseStructuredPoints(DataContainer& data, std::ifstream& file) throw (cgt::Exception, std::exception) {
118
119
        // init optional parameters with sane default values
        size_t dimensionality = 3;
120
        cgt::svec3 size(static_cast<size_t>(0));
121

122
123
        cgt::vec3 voxelSize(1.f);
        cgt::vec3 imageOffset(0.f);
124

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
        std::string curLine;
        std::vector<std::string> splitted;

        // now comes dimensions, spacing and origin:
        for (size_t i = 0; i < 3; ++i) {
            std::stringstream ss(StringUtils::lowercase(getTrimmedLine(file)));
            ss >> curLine;
            if (curLine == "dimensions") {
                ss >> size.x >> size.y >> size.z;
                if (size.z == 1)
                    dimensionality = 2;
                if (size.y == 1)
                    dimensionality = 1;
            }
            else if (curLine == "spacing") {
                ss >> voxelSize.x >> voxelSize.y >> voxelSize.z;
            }
            else if (curLine == "origin") {
                ss >> imageOffset.x >> imageOffset.y >> imageOffset.z;
            }
            else {
146
                throw cgt::FileException("Unsupported dataset structure field '" + curLine + "' in vtk file.", p_url.getValue());
147
148
149
150
151
152
153
            }
        }

        // now come the dataset attributes "POINT_DATA ..."
        curLine = StringUtils::lowercase(getTrimmedLine(file));
        splitted = StringUtils::split(curLine, " ");
        if (splitted.size() != 2 || splitted[0] != "point_data")
154
            throw cgt::FileException("Unsupported dataset attribute '" + splitted[0] + "' in vtk file - expected \"POINT_DATA n\".", p_url.getValue());
155
        size_t numPoints = StringUtils::fromString<size_t>(splitted[1]);
156

157
158
        if (numPoints < cgt::hmul(size))
            throw cgt::FileException("Number of points in dataset (" + splitted[0] + ") doesn't match dimensions: " + StringUtils::toString(size), p_url.getValue());
159

160
161
162
163
        // now comes the data description block "FIELD ..."
        curLine = StringUtils::lowercase(getTrimmedLine(file));
        splitted = StringUtils::split(curLine, " ");
        if (splitted.size() != 3 || splitted[0] != "field")
164
            throw cgt::FileException("Unsupported dataset attribute '" + splitted[0] + "' in vtk file - expected \"FIELD ...\".", p_url.getValue());
165
166
167
        size_t numArrays = StringUtils::fromString<size_t>(splitted[2]);

        if (numArrays != 1)
168
            throw cgt::FileException("Multiple arrays in data set currently not supported - too lazy...", p_url.getValue());
169
170
171
172

        curLine = StringUtils::lowercase(getTrimmedLine(file));
        splitted = StringUtils::split(curLine, " ");
        size_t numTuples = StringUtils::fromString<size_t>(splitted[1]);
173
        size_t numProcessors = StringUtils::fromString<size_t>(splitted[2]);
174

175
        if (numTuples * numProcessors != numPoints)
176
            throw cgt::FileException("Number of points in dataset doesn't match dimensions of data field", p_url.getValue());
177
178
179

        ImageData* image = new ImageData(dimensionality, size, 1);
        ImageRepresentationLocal* rep = 0;
180
181
182
183

#define DISPATCH_PARSING(VTK_TYPE, C_TYPE, TMP_TYPE) \
    do { \
        if (splitted[3] == VTK_TYPE) { \
184
            C_TYPE* dataArray = new C_TYPE[numPoints]; \
185
186
187
            TMP_TYPE tmp; \
            for (size_t i = 0; i < numPoints && file.good(); ++i) { \
                file >> tmp; \
188
                dataArray[i] = static_cast<C_TYPE>(tmp); \
189
            } \
190
            rep = GenericImageRepresentationLocal<C_TYPE, 1>::create(image, dataArray); \
191
192
193
        } \
    } while (0)

194
195
196
197
198
199
200
201
202
203
204
205
206
207
        DISPATCH_PARSING("unsigned_char"    , uint8_t, uint16_t);
        DISPATCH_PARSING("char"             , int8_t, int16_t);
        DISPATCH_PARSING("unsigned_short"   , uint16_t, uint16_t);
        DISPATCH_PARSING("short"            , int16_t, int16_t);
        DISPATCH_PARSING("unsigned_int"     , uint32_t, uint32_t);
        DISPATCH_PARSING("int"              , int32_t, int32_t);
        DISPATCH_PARSING("float"            , float, float);

        if (rep != 0) {
            // all parsing done - lets create the image:
            image->setMappingInformation(ImageMappingInformation(size, imageOffset + p_imageOffset.getValue(), voxelSize + p_voxelSize.getValue()));
            data.addData(p_targetImageID.getValue(), image);
        }
        else {
208
            throw cgt::FileException("Error while parsing the data.", p_url.getValue());
209
210
211
        }
    }

212
    void VtkImageReader::parsePolydata(DataContainer& data, std::ifstream& file) throw (cgt::Exception, std::exception) {
213
214
215
216
        std::string curLine;
        std::vector<std::string> splitted;

        std::vector<uint16_t> indices;
217
218
        std::vector<cgt::vec3> vertices;
        std::vector<cgt::vec3> normals;
219
220
221
222
223
224
225

        while (file.good()) {
            curLine = StringUtils::lowercase(getTrimmedLine(file));
            splitted = StringUtils::split(curLine, " ");

            if (splitted.size() == 3 && splitted[0] == "points") {
                size_t numVertices = StringUtils::fromString<size_t>(splitted[1]);
226
                vertices.resize(numVertices, cgt::vec3(0.f));
227
228
229
230

                // TODO: make parsing more robust...
                for (size_t i = 0; i < numVertices; ++i) {
                    file >> vertices[i].x >> vertices[i].y >> vertices[i].z;
231
                }
232
233
234
235
236
237
238
239
240
241
242
243
244
            }
            else if (splitted.size() == 3 && splitted[0] == "polygons") {
                size_t numPolygons = StringUtils::fromString<size_t>(splitted[1]);
                size_t numIndices = StringUtils::fromString<size_t>(splitted[2]);
                indices.resize(numIndices, 0);
                size_t i = 0;

                // TODO: make parsing more robust...
                for (size_t j = 0; j < numPolygons && i < numIndices; ++j) {
                    int tmp;
                    file >> tmp >> indices[i++];
                    file >> indices[i++];
                    file >> indices[i++];
245
246
                }
            }
247
248
249
250
251
252
253
            if (splitted.size() == 2 && splitted[0] == "point_data") {
                size_t numPoints = StringUtils::fromString<size_t>(splitted[1]);

                curLine = StringUtils::lowercase(getTrimmedLine(file));
                splitted = StringUtils::split(curLine, " ");

                if (splitted.size() == 3 && splitted[0] == "normals") {
254
                    normals.resize(numPoints, cgt::vec3(0.f));
255
256
257
258
259
260

                    // TODO: make parsing more robust...
                    for (size_t i = 0; i < numPoints; ++i) {
                        file >> normals[i].x >> normals[i].y >> normals[i].z;
                    }
                }
261
262
263
            }
        }

264
        // all parsing done - lets create the image:
265
        IndexedMeshGeometry* g = new IndexedMeshGeometry(indices, vertices, std::vector<cgt::vec3>(), std::vector<cgt::vec4>(), normals);
266
        data.addData(p_targetImageID.getValue(), g);
267
    }
268
    
269
}