/src/bag/api/bag_vrmetadata.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | #include "bag_hdfhelper.h" |
3 | | #include "bag_private.h" |
4 | | #include "bag_vrmetadata.h" |
5 | | #include "bag_vrmetadatadescriptor.h" |
6 | | |
7 | | #include <array> |
8 | | #include <cstring> //member |
9 | | #include <H5Cpp.h> |
10 | | |
11 | | |
12 | | namespace BAG { |
13 | | |
14 | | namespace { |
15 | | |
16 | | //! Generate an HDF5 CompType for the variable resolution metadata. |
17 | | /*! |
18 | | \return |
19 | | The HDF5 CompType for the variable resolution metadata. |
20 | | */ |
21 | | ::H5::CompType makeDataType() |
22 | 0 | { |
23 | 0 | const ::H5::CompType memDataType{sizeof(VRMetadataItem)}; |
24 | |
|
25 | 0 | memDataType.insertMember("index", HOFFSET(VRMetadataItem, index), |
26 | 0 | ::H5::PredType::NATIVE_UINT32); |
27 | 0 | memDataType.insertMember("dimensions_x", |
28 | 0 | HOFFSET(VRMetadataItem, dimensions_x), ::H5::PredType::NATIVE_UINT32); |
29 | 0 | memDataType.insertMember("dimensions_y", |
30 | 0 | HOFFSET(VRMetadataItem, dimensions_y), ::H5::PredType::NATIVE_UINT32); |
31 | 0 | memDataType.insertMember("resolution_x", |
32 | 0 | HOFFSET(VRMetadataItem, resolution_x), ::H5::PredType::NATIVE_FLOAT); |
33 | 0 | memDataType.insertMember("resolution_y", |
34 | 0 | HOFFSET(VRMetadataItem, resolution_y), ::H5::PredType::NATIVE_FLOAT); |
35 | 0 | memDataType.insertMember("sw_corner_x", |
36 | 0 | HOFFSET(VRMetadataItem, sw_corner_x), ::H5::PredType::NATIVE_FLOAT); |
37 | 0 | memDataType.insertMember("sw_corner_y", |
38 | 0 | HOFFSET(VRMetadataItem, sw_corner_y), ::H5::PredType::NATIVE_FLOAT); |
39 | |
|
40 | 0 | return memDataType; |
41 | 0 | } |
42 | | |
43 | | //! Read an HDF5 attribute. |
44 | | /*! |
45 | | \param h5file |
46 | | The HDF5 file. |
47 | | \param name |
48 | | The name of the attribute. |
49 | | |
50 | | \return |
51 | | The value in the attribute. |
52 | | */ |
53 | | template<typename T> |
54 | | T readAttribute( |
55 | | const ::H5::H5File& h5file, |
56 | | const char* const name) |
57 | 0 | { |
58 | 0 | const auto h5DataSet = h5file.openDataSet(VR_METADATA_PATH); |
59 | 0 | const auto attribute = h5DataSet.openAttribute(name); |
60 | |
|
61 | 0 | T value{}; |
62 | 0 | attribute.read(attribute.getDataType(), &value); |
63 | |
|
64 | 0 | return value; |
65 | 0 | } Unexecuted instantiation: bag_vrmetadata.cpp:unsigned int BAG::(anonymous namespace)::readAttribute<unsigned int>(H5::H5File const&, char const*) Unexecuted instantiation: bag_vrmetadata.cpp:float BAG::(anonymous namespace)::readAttribute<float>(H5::H5File const&, char const*) |
66 | | |
67 | | } // namespace |
68 | | |
69 | | //! Constructor |
70 | | /*! |
71 | | \param dataset |
72 | | The BAG Dataset this layer belongs to. |
73 | | \param descriptor |
74 | | The descriptor of this layer. |
75 | | \param pH5dataSet |
76 | | The HDF5 DataSet this class wraps. |
77 | | */ |
78 | | VRMetadata::VRMetadata( |
79 | | Dataset& dataset, |
80 | | VRMetadataDescriptor& descriptor, |
81 | | std::unique_ptr<::H5::DataSet, DeleteH5dataSet> pH5dataSet) |
82 | 0 | : Layer(dataset, descriptor) |
83 | 0 | , m_pH5dataSet(std::move(pH5dataSet)) |
84 | 0 | { |
85 | 0 | } |
86 | | |
87 | | //! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor() |
88 | | /*! |
89 | | \return |
90 | | The layer's descriptor. |
91 | | Will never be nullptr. |
92 | | */ |
93 | | std::shared_ptr<VRMetadataDescriptor> VRMetadata::getDescriptor() & noexcept |
94 | 0 | { |
95 | 0 | return std::dynamic_pointer_cast<VRMetadataDescriptor>(Layer::getDescriptor()); |
96 | 0 | } |
97 | | |
98 | | //! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor() |
99 | | /*! |
100 | | \return |
101 | | The layer's descriptor. |
102 | | Will never be nullptr. |
103 | | */ |
104 | 0 | std::shared_ptr<const VRMetadataDescriptor> VRMetadata::getDescriptor() const & noexcept { |
105 | 0 | return std::dynamic_pointer_cast<const VRMetadataDescriptor>(Layer::getDescriptor()); |
106 | 0 | } |
107 | | |
108 | | //! Create a new variable resolution metadata layer. |
109 | | /*! |
110 | | \param dataset |
111 | | The BAG Dataset this layer belongs to. |
112 | | \param chunkSize |
113 | | The chunk size the HDF5 DataSet will use. |
114 | | \param compressionLevel |
115 | | The compression level the HDF5 DataSet will use. |
116 | | |
117 | | \return |
118 | | The new variable resolution metadata. |
119 | | */ |
120 | | std::shared_ptr<VRMetadata> VRMetadata::create( |
121 | | Dataset& dataset, |
122 | | uint64_t chunkSize, |
123 | | int compressionLevel) |
124 | 0 | { |
125 | 0 | auto descriptor = VRMetadataDescriptor::create(dataset, chunkSize, |
126 | 0 | compressionLevel); |
127 | |
|
128 | 0 | auto h5dataSet = VRMetadata::createH5dataSet(dataset, *descriptor); |
129 | |
|
130 | 0 | return std::make_shared<VRMetadata>(dataset, |
131 | 0 | *descriptor, std::move(h5dataSet)); |
132 | 0 | } |
133 | | |
134 | | //! Open a variable resolution metadata layer. |
135 | | /*! |
136 | | \param dataset |
137 | | The BAG Dataset this layer belongs to. |
138 | | \param descriptor |
139 | | The descriptor of this layer. |
140 | | |
141 | | \return |
142 | | The variable resolution metadata. |
143 | | */ |
144 | | std::shared_ptr<VRMetadata> VRMetadata::open( |
145 | | Dataset& dataset, |
146 | | VRMetadataDescriptor& descriptor) |
147 | 0 | { |
148 | 0 | auto& h5file = dataset.getH5file(); |
149 | | |
150 | | // Read the attribute values from the file and set in the descriptor. |
151 | 0 | const auto minDimsX = readAttribute<uint32_t>(h5file, VR_METADATA_MIN_DIMS_X); |
152 | 0 | const auto minDimsY = readAttribute<uint32_t>(h5file, VR_METADATA_MIN_DIMS_Y); |
153 | |
|
154 | 0 | descriptor.setMinDimensions(minDimsX, minDimsY); |
155 | |
|
156 | 0 | const auto maxDimsX = readAttribute<uint32_t>(h5file, VR_METADATA_MAX_DIMS_X); |
157 | 0 | const auto maxDimsY = readAttribute<uint32_t>(h5file, VR_METADATA_MAX_DIMS_Y); |
158 | |
|
159 | 0 | descriptor.setMaxDimensions(maxDimsX, maxDimsY); |
160 | |
|
161 | 0 | const auto minResX = readAttribute<float>(h5file, VR_METADATA_MIN_RES_X); |
162 | 0 | const auto minResY = readAttribute<float>(h5file, VR_METADATA_MIN_RES_Y); |
163 | |
|
164 | 0 | descriptor.setMinResolution(minResX, minResY); |
165 | |
|
166 | 0 | const auto maxResX = readAttribute<float>(h5file, VR_METADATA_MAX_RES_X); |
167 | 0 | const auto maxResY = readAttribute<float>(h5file, VR_METADATA_MAX_RES_Y); |
168 | |
|
169 | 0 | descriptor.setMaxResolution(maxResX, maxResY); |
170 | |
|
171 | 0 | auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>( |
172 | 0 | new ::H5::DataSet{h5file.openDataSet(VR_METADATA_PATH)}, |
173 | 0 | DeleteH5dataSet{}); |
174 | |
|
175 | 0 | return std::make_shared<VRMetadata>(dataset, |
176 | 0 | descriptor, std::move(h5dataSet)); |
177 | 0 | } |
178 | | |
179 | | |
180 | | //! Create the HDF5 DataSet. |
181 | | /*! |
182 | | \param dataset |
183 | | The BAG Dataset this layer belongs to. |
184 | | \param descriptor |
185 | | The descriptor of this layer. |
186 | | |
187 | | \return |
188 | | The HDF5 Dataset for this variable resolution metadata layer. |
189 | | */ |
190 | | std::unique_ptr<::H5::DataSet, DeleteH5dataSet> |
191 | | VRMetadata::createH5dataSet( |
192 | | const Dataset& dataset, |
193 | | const VRMetadataDescriptor& descriptor) |
194 | 0 | { |
195 | 0 | std::array<hsize_t, kRank> fileDims{0, 0}; |
196 | 0 | const std::array<hsize_t, kRank> kMaxFileDims{H5S_UNLIMITED, H5S_UNLIMITED}; |
197 | 0 | const ::H5::DataSpace h5fileDataSpace{kRank, fileDims.data(), kMaxFileDims.data()}; |
198 | | |
199 | | // Create the creation property list. |
200 | 0 | const ::H5::DSetCreatPropList h5createPropList{}; |
201 | | |
202 | | // Use chunk size and compression level from the descriptor. |
203 | 0 | const auto compressionLevel = descriptor.getCompressionLevel(); |
204 | 0 | const auto chunkSize = descriptor.getChunkSize(); |
205 | 0 | if (chunkSize > 0) |
206 | 0 | { |
207 | 0 | std::array<hsize_t, kRank> chunkDims{chunkSize, chunkSize}; |
208 | 0 | h5createPropList.setChunk(kRank, chunkDims.data()); |
209 | |
|
210 | 0 | if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel) |
211 | 0 | h5createPropList.setDeflate(compressionLevel); |
212 | 0 | } |
213 | 0 | else if (compressionLevel > 0) |
214 | 0 | throw CompressionNeedsChunkingSet{}; |
215 | 0 | else |
216 | 0 | throw LayerRequiresChunkingSet{}; |
217 | | |
218 | 0 | h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC); |
219 | |
|
220 | 0 | const auto memDataType = makeDataType(); |
221 | | |
222 | | // Create the DataSet using the above. |
223 | 0 | const auto& h5file = dataset.getH5file(); |
224 | |
|
225 | 0 | const auto h5dataSet = h5file.createDataSet(VR_METADATA_PATH, |
226 | 0 | memDataType, h5fileDataSpace, h5createPropList); |
227 | | |
228 | | // Create attributes. |
229 | 0 | createAttributes(h5dataSet, ::H5::PredType::NATIVE_UINT32, |
230 | 0 | {VR_METADATA_MIN_DIMS_X, VR_METADATA_MIN_DIMS_Y, VR_METADATA_MAX_DIMS_X, |
231 | 0 | VR_METADATA_MAX_DIMS_Y}); |
232 | 0 | createAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT, |
233 | 0 | {VR_METADATA_MIN_RES_X, VR_METADATA_MIN_RES_Y, VR_METADATA_MAX_RES_X, |
234 | 0 | VR_METADATA_MAX_RES_Y}); |
235 | | |
236 | | // Set initial min/max values. |
237 | 0 | BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_UINT32, |
238 | 0 | std::numeric_limits<uint32_t>::max(), {VR_METADATA_MIN_DIMS_X, |
239 | 0 | VR_METADATA_MIN_DIMS_Y}); |
240 | |
|
241 | 0 | BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_UINT32, |
242 | 0 | std::numeric_limits<uint32_t>::lowest(), {VR_METADATA_MAX_DIMS_X, |
243 | 0 | VR_METADATA_MAX_DIMS_Y}); |
244 | |
|
245 | 0 | BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT, |
246 | 0 | std::numeric_limits<float>::max(), {VR_METADATA_MIN_RES_X, |
247 | 0 | VR_METADATA_MIN_RES_Y}); |
248 | |
|
249 | 0 | BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT, |
250 | 0 | std::numeric_limits<float>::lowest(), {VR_METADATA_MAX_RES_X, |
251 | 0 | VR_METADATA_MAX_RES_Y}); |
252 | |
|
253 | 0 | return std::unique_ptr<::H5::DataSet, DeleteH5dataSet>( |
254 | 0 | new ::H5::DataSet{h5dataSet}, DeleteH5dataSet{}); |
255 | 0 | } |
256 | | |
257 | | //! \copydoc Layer::read |
258 | | UInt8Array VRMetadata::readProxy( |
259 | | uint32_t rowStart, |
260 | | uint32_t columnStart, |
261 | | uint32_t rowEnd, |
262 | | uint32_t columnEnd) const |
263 | 0 | { |
264 | 0 | auto pDescriptor = std::dynamic_pointer_cast<const VRMetadataDescriptor>( |
265 | 0 | this->getDescriptor()); |
266 | 0 | if (!pDescriptor) |
267 | 0 | throw InvalidLayerDescriptor{}; |
268 | | |
269 | | // Query the file for the specified rows and columns. |
270 | 0 | const auto rows = (rowEnd - rowStart) + 1; |
271 | 0 | const auto columns = (columnEnd - columnStart) + 1; |
272 | 0 | const std::array<hsize_t, kRank> count{rows, columns}; |
273 | 0 | const std::array<hsize_t, kRank> offset{rowStart, columnStart}; |
274 | |
|
275 | 0 | const auto fileDataSpace = m_pH5dataSet->getSpace(); |
276 | 0 | fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data()); |
277 | |
|
278 | 0 | const auto bufferSize = pDescriptor->getReadBufferSize(rows, columns); |
279 | 0 | UInt8Array buffer{bufferSize}; |
280 | |
|
281 | 0 | const ::H5::DataSpace memDataSpace{kRank, count.data(), count.data()}; |
282 | |
|
283 | 0 | const auto memDataType = makeDataType(); |
284 | |
|
285 | 0 | m_pH5dataSet->read(buffer.data(), memDataType, memDataSpace, fileDataSpace); |
286 | |
|
287 | 0 | return buffer; |
288 | 0 | } |
289 | | |
290 | | //! \copydoc Layer::writeAttributes |
291 | | void VRMetadata::writeAttributesProxy() const |
292 | 0 | { |
293 | 0 | auto pDescriptor = std::dynamic_pointer_cast<const VRMetadataDescriptor>( |
294 | 0 | this->getDescriptor()); |
295 | 0 | if (!pDescriptor) |
296 | 0 | throw InvalidLayerDescriptor{}; |
297 | | |
298 | | // Write the attributes from the layer descriptor. |
299 | | // min X,Y dimensions |
300 | 0 | const auto minDims = pDescriptor->getMinDimensions(); |
301 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32, |
302 | 0 | std::get<0>(minDims), VR_METADATA_MIN_DIMS_X); |
303 | |
|
304 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32, |
305 | 0 | std::get<1>(minDims), VR_METADATA_MIN_DIMS_Y); |
306 | | |
307 | | // max X,Y dimensions |
308 | 0 | const auto maxDims = pDescriptor->getMaxDimensions(); |
309 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32, |
310 | 0 | std::get<0>(maxDims), VR_METADATA_MAX_DIMS_X); |
311 | |
|
312 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32, |
313 | 0 | std::get<1>(maxDims), VR_METADATA_MAX_DIMS_Y); |
314 | | |
315 | | // min X,Y resolution |
316 | 0 | const auto minRes = pDescriptor->getMinResolution(); |
317 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT, |
318 | 0 | std::get<0>(minRes), VR_METADATA_MIN_RES_X); |
319 | |
|
320 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT, |
321 | 0 | std::get<1>(minRes), VR_METADATA_MIN_RES_Y); |
322 | | |
323 | | // max X,Y resolution |
324 | 0 | const auto maxRes = pDescriptor->getMaxResolution(); |
325 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT, |
326 | 0 | std::get<0>(maxRes), VR_METADATA_MAX_RES_X); |
327 | |
|
328 | 0 | writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT, |
329 | 0 | std::get<1>(maxRes), VR_METADATA_MAX_RES_Y); |
330 | 0 | } |
331 | | |
332 | | //! \copydoc Layer::write |
333 | | void VRMetadata::writeProxy( |
334 | | uint32_t rowStart, |
335 | | uint32_t columnStart, |
336 | | uint32_t rowEnd, |
337 | | uint32_t columnEnd, |
338 | | const uint8_t* buffer) |
339 | 0 | { |
340 | 0 | auto pDescriptor = std::dynamic_pointer_cast<VRMetadataDescriptor>( |
341 | 0 | this->getDescriptor()); |
342 | 0 | if (!pDescriptor) |
343 | 0 | throw InvalidLayerDescriptor{}; |
344 | | |
345 | 0 | const auto rows = (rowEnd - rowStart) + 1; |
346 | 0 | const auto columns = (columnEnd - columnStart) + 1; |
347 | 0 | const std::array<hsize_t, kRank> count{rows, columns}; |
348 | 0 | const std::array<hsize_t, kRank> offset{rowStart, columnStart}; |
349 | 0 | const ::H5::DataSpace memDataSpace{kRank, count.data(), count.data()}; |
350 | |
|
351 | 0 | ::H5::DataSpace fileDataSpace = m_pH5dataSet->getSpace(); |
352 | | |
353 | | // Expand the file data space if needed. |
354 | 0 | std::array<hsize_t, kRank> fileDims{}; |
355 | 0 | std::array<hsize_t, kRank> maxFileDims{}; |
356 | 0 | fileDataSpace.getSimpleExtentDims(fileDims.data(), maxFileDims.data()); |
357 | |
|
358 | 0 | if ((fileDims[0] < (rowEnd + 1)) || |
359 | 0 | (fileDims[1] < (columnEnd + 1))) |
360 | 0 | { |
361 | 0 | const std::array<hsize_t, kRank> newDims{ |
362 | 0 | std::max<hsize_t>(fileDims[0], rowEnd + 1), |
363 | 0 | std::max<hsize_t>(fileDims[1], columnEnd + 1)}; |
364 | |
|
365 | 0 | m_pH5dataSet->extend(newDims.data()); |
366 | |
|
367 | 0 | fileDataSpace = m_pH5dataSet->getSpace(); |
368 | | |
369 | | // Update the dataset's dimensions. |
370 | 0 | if (this->getDataset().expired()) |
371 | 0 | throw DatasetNotFound{}; |
372 | | |
373 | 0 | auto pDataset = this->getDataset().lock(); |
374 | 0 | pDataset->getDescriptor().setDims(static_cast<uint32_t>(newDims[0]), |
375 | 0 | static_cast<uint32_t>(newDims[1])); |
376 | 0 | } |
377 | | |
378 | 0 | fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data()); |
379 | |
|
380 | 0 | const auto memDataType = makeDataType(); |
381 | |
|
382 | 0 | m_pH5dataSet->write(buffer, memDataType, memDataSpace, fileDataSpace); |
383 | | |
384 | | // Update any attributes that are affected by the data being written. |
385 | | // Get the current min/max from descriptor. |
386 | 0 | uint32_t minDimX = 0, minDimY = 0; |
387 | 0 | std::tie(minDimX, minDimY) = pDescriptor->getMinDimensions(); |
388 | |
|
389 | 0 | uint32_t maxDimX = 0, maxDimY = 0; |
390 | 0 | std::tie(maxDimX, maxDimY) = pDescriptor->getMaxDimensions(); |
391 | |
|
392 | 0 | float minResX = 0.f, minResY = 0.f; |
393 | 0 | std::tie(minResX, minResY) = pDescriptor->getMinResolution(); |
394 | |
|
395 | 0 | float maxResX = 0.f, maxResY = 0.f; |
396 | 0 | std::tie(maxResX, maxResY) = pDescriptor->getMaxResolution(); |
397 | | |
398 | | // Update the min/max from new data. |
399 | 0 | const auto* items = reinterpret_cast<const VRMetadataItem*>(buffer); |
400 | |
|
401 | 0 | auto* item = items; |
402 | 0 | const auto end = items + rows * columns; |
403 | |
|
404 | 0 | for (; item != end; ++item) |
405 | 0 | { |
406 | 0 | minDimX = item->dimensions_x < minDimX ? item->dimensions_x : minDimX; |
407 | 0 | minDimY = item->dimensions_y < minDimY ? item->dimensions_y : minDimY; |
408 | |
|
409 | 0 | maxDimX = item->dimensions_x > maxDimX ? item->dimensions_x : maxDimX; |
410 | 0 | maxDimY = item->dimensions_y > maxDimY ? item->dimensions_y : maxDimY; |
411 | |
|
412 | 0 | minResX = item->resolution_x < minResX ? item->resolution_x : minResX; |
413 | 0 | minResY = item->resolution_y < minResY ? item->resolution_y : minResY; |
414 | |
|
415 | 0 | maxResX = item->resolution_x > maxResX ? item->resolution_x : maxResX; |
416 | 0 | maxResY = item->resolution_y > maxResY ? item->resolution_y : maxResY; |
417 | 0 | } |
418 | |
|
419 | 0 | pDescriptor->setMinDimensions(minDimX, minDimY); |
420 | 0 | pDescriptor->setMaxDimensions(maxDimX, maxDimY); |
421 | 0 | pDescriptor->setMinResolution(minResX, minResY); |
422 | 0 | pDescriptor->setMaxResolution(maxResX, maxResY); |
423 | 0 | } |
424 | | |
425 | | } //namespace BAG |
426 | | |