GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/io/rasterimagereader.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 163 182 89.6%
Functions: 18 22 81.8%
Branches: 332 819 40.5%

Line Branch Exec Source
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 //
4 // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 /*!
8 * \file
9 * \ingroup InputOutput
10 * \brief A simple reader class for raster images.
11 */
12 #ifndef DUMUX_RASTER_IMAGE_READER_HH
13 #define DUMUX_RASTER_IMAGE_READER_HH
14
15 #include <cassert>
16 #include <cstdint>
17 #include <string>
18 #include <vector>
19 #include <fstream>
20 #include <sstream>
21 #include <algorithm>
22 #include <map>
23 #include <iterator>
24 #include <iostream>
25
26 #include <dune/common/exceptions.hh>
27 #include <dumux/common/stringutilities.hh>
28 #include <dumux/io/rasterimagedata.hh>
29
30 namespace Dumux {
31
32 /*!
33 * \ingroup InputOutput
34 * \brief A simple reader class for the Netpbm format (https://en.wikipedia.org/wiki/Netpbm_format).
35 * So far, only black and white (*.pbm) and grayscale (*pgm) images are supported.
36 */
37 class NetPBMReader
38 {
39 template<typename T>
40 using Result = Detail::RasterImageData::Result<T>;
41
42 using HeaderData = Detail::RasterImageData::HeaderData;
43 using Format = Detail::RasterImageData::Format;
44
45 public:
46
47 /*!
48 * \brief A helper function to retrieve the format from tokens of the file's first line.
49 *
50 * \param firstLineTokes The tokens extracted from the first line of the file
51 */
52 10 static Format getFormat(const std::vector<std::string_view>& firstLineTokes)
53 {
54 12 static const auto format = []{
55
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::map<std::string, Format> format;
56
13/28
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✓ Branch 33 taken 2 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 2 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
4 format["P1"] = Format{"P1", "Portable BitMap", "ASCII"};
57
13/28
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✓ Branch 33 taken 2 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 2 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
4 format["P2"] = Format{"P2", "Portable GrayMap", "ASCII"};
58
13/28
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✓ Branch 33 taken 2 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 2 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
4 format["P3"] = Format{"P3", "Portable PixMap", "ASCII"};
59
13/28
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✓ Branch 33 taken 2 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 2 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
4 format["P4"] = Format{"P4", "Portable BitMap", "binary"};
60
13/28
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✓ Branch 33 taken 2 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 2 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
4 format["P5"] = Format{"P5", "Portable GrayMap", "binary"};
61
10/22
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 2 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
4 format["P6"] = Format{"P6", "Portable PixMap", "binary"};
62 2 return format;
63
4/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
10 }();
64
65
1/4
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
30 const std::string& magicNumber = std::string(firstLineTokes[0]);
66
67 10 if (!format.count(magicNumber))
68 DUNE_THROW(Dune::IOError, magicNumber << " is not a valid magic number for the Netpbm format");
69
70
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 return format.at(magicNumber);
71 }
72
73 /*!
74 * \brief Reads a *pbm (black and white) in ASCII or binary encoding.
75 * Returns a struct that contains both the pixel values and the header data.
76 *
77 * \param fileName The file name (*.pbm).
78 * \param useDuneGridOrdering If set true, the ordering of the pixels will be changed according
79 * to Dune's convention, shifting the origin from upper left to lower left.
80 */
81 8 static Result<bool> readPBM(const std::string& fileName, const bool useDuneGridOrdering = true)
82 {
83 10 std::ifstream infile(fileName, std::ios::binary);
84
85
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
16 if (!infile)
86 DUNE_THROW(Dune::IOError, "Raster data file not found or corrupt");
87
88
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
14 HeaderData headerData = readHeader(infile);
89 12 std::vector<bool> values;
90
91
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
6 if (headerData.format.magicNumber == "P1")
92
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 values = readPBMDataASCII_(infile, headerData);
93
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 else if (headerData.format.magicNumber == "P4")
94
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 values = readPBMDataBinary_(infile, headerData);
95 else
96 DUNE_THROW(Dune::IOError, headerData.format.magicNumber << " not supported. Use readPBM for P1 and P4 or readPGM for P2 and P5");
97
98 6 Result<bool> result(std::move(values), std::move(headerData));
99
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 printInfo(result);
100
101
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (useDuneGridOrdering)
102
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 applyDuneGridOrdering(result);
103
104 12 return result;
105 }
106
107 /*!
108 * \brief Reads a *.pgm (grayscale) in ASCII or binary encoding.
109 * Returns a struct that contains both the pixel values and the header data.
110 *
111 * \tparam ValueType The value type representing the pixel data. By default, std::uint8_t (0-255) is used.
112 * Since this type is often defined as unsigned char, some conversion issues during I/O may occur.
113 * Hence the type may be changed, e.g., to std::size_t.
114 *
115 * \param fileName The file name (*.pbm).
116 * \param useDuneGridOrdering If set true, the ordering of the pixels will be changed according
117 * to Dune's convention, shifting the origin from upper left to lower left.
118 */
119 template<class ValueType = std::uint8_t>
120 2 static Result<ValueType> readPGM(const std::string& fileName, const bool useDuneGridOrdering = true)
121 {
122 2 std::ifstream infile(fileName, std::ios::binary);
123
124
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 if (!infile)
125 DUNE_THROW(Dune::IOError, "Raster data file not found or corrupt");
126
127
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 HeaderData headerData = readHeader(infile);
128
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4 std::vector<ValueType> values;
129
130
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (headerData.format.magicNumber == "P2")
131
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 values = NetPBMReader::template readPGMDataASCII_<ValueType>(infile, headerData);
132
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 else if (headerData.format.magicNumber == "P5")
133
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 values = NetPBMReader::template readPGMDataBinary_<ValueType>(infile, headerData);
134 else
135 DUNE_THROW(Dune::IOError, headerData.format.magicNumber << " not supported. Use readPBM for P1 and P4 or readPGM for P2 and P5");
136
137 2 Result<ValueType> result(std::move(values), std::move(headerData));
138
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 printInfo(result);
139
140
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (useDuneGridOrdering)
141
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 applyDuneGridOrdering(result);
142
143 2 return result;
144 }
145
146 /*!
147 * \brief Returns the header data of the image file.
148 *
149 * \param infile The input stream used to process the image file.
150 */
151 10 static HeaderData readHeader(std::ifstream& infile)
152 {
153
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 HeaderData headerData;
154
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
12 std::string inputLine;
155 10 std::size_t lineNumber = 0;
156
157 // First line : get format.
158
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 std::getline(infile, inputLine);
159 10 ++lineNumber;
160
161
6/10
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
22 const auto firstLineTokens = tokenize(inputLine, " ");
162
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 headerData.format = getFormat(firstLineTokens);
163
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
20 const auto magicNumber = headerData.format.magicNumber;
164
165 // dimensions could be given right after magic number (in same line)
166
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
20 if (firstLineTokens.size() > 2)
167 {
168
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (isBlackAndWhite_(magicNumber) && firstLineTokens.size() != 3)
169 DUNE_THROW(Dune::IOError, "Could not read first line for B/W image");
170
171
5/12
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
4 headerData.nCols = std::stoi(std::string(firstLineTokens[1]));
172
4/10
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
4 headerData.nRows = std::stoi(std::string(firstLineTokens[2]));
173
174
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (isGrayScale_(magicNumber))
175 {
176 if (firstLineTokens.size() == 4)
177 headerData.maxValue = std::stoi(std::string(firstLineTokens[3]));
178 if (firstLineTokens.size() > 4)
179 DUNE_THROW(Dune::IOError, "Could not read first line for grayscale image");
180 }
181 }
182 else
183 {
184 // Read dimensions and maximum value (for non-b/w images).
185
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
32 while (!infile.eof())
186 {
187
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 std::getline(infile, inputLine);
188 16 ++lineNumber;
189
190 // Skip comments.
191
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (isComment_(inputLine))
192 8 continue;
193
194
3/6
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
16 const auto tokens = tokenize(inputLine, " ");
195
196 // The first line after the comments contains the dimensions.
197
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 7 times.
16 if (tokens.size() != 2)
198
11/26
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 26 taken 1 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 1 times.
✗ Branch 30 not taken.
✓ Branch 32 taken 1 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 1 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
15 DUNE_THROW(Dune::IOError, "Expecting " << [](auto size){ return size < 2 ? "both" : "only"; }(tokens.size()) << " dimensions (2 numbers) in line " << lineNumber);
199
200
5/12
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 7 times.
✓ Branch 12 taken 7 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
14 headerData.nCols = std::stoi(std::string(tokens[0]));
201
4/10
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
14 headerData.nRows = std::stoi(std::string(tokens[1]));
202
203 // Grayscale images additionally contain a maximum value in the header.
204
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
7 if (isGrayScale_(magicNumber))
205 {
206
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::getline(infile, inputLine);
207 3 ++lineNumber;
208
209
4/8
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
8 const auto token = tokenize(inputLine, " ");
210
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
6 if (token.size() != 1)
211
11/26
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 26 taken 1 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 1 times.
✗ Branch 30 not taken.
✓ Branch 32 taken 1 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 1 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
15 DUNE_THROW(Dune::IOError, "Expecting" << [](auto size){ return size == 0 ? "" : " only"; }(token.size()) << " intensity (one number) in line " << lineNumber);
212
213
6/14
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
4 headerData.maxValue = std::stoi(std::string(token[0]));
214 }
215
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 break;
216 }
217 }
218
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
16 return headerData;
220 }
221
222 /*!
223 * \brief Change the ordering of the pixels according
224 * to Dune's convention, shifting the origin from upper left to lower left.
225 *
226 * \param result The image's pixel values ordered from top to bottom.
227 */
228 template<class T>
229 10 static void applyDuneGridOrdering(Result<T>& result)
230 {
231 20 auto tmp = result;
232
4/4
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 90 times.
✓ Branch 3 taken 6 times.
190 for (std::size_t i = 0; i < result.size(); i += result.header().nCols)
233
8/10
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 46 times.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 70 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 70 times.
672 std::swap_ranges((result.begin() + i), (result.begin() + i + result.header().nCols), (tmp.end() - i - result.header().nCols));
234 10 }
235
236 /*!
237 * \brief Print the data contained in the header.
238 *
239 * \param result The object storing both the image's pixel values and the header data.
240 */
241 template<class T>
242 14 static void printInfo(const Result<T>& result)
243 {
244 14 const Format& format = result.header().format;
245 56 std::cout << "Reading " << format.type << " file (" << format.encoding << ")" << std::endl;
246 42 std::cout << "Dimensions : " << result.header().nCols << " " << result.header().nRows << std::endl;
247 28 std::cout << "Maximum value : " << result.header().maxValue << std::endl;
248 14 }
249
250 /*!
251 * \brief Fill a pre-defined 2D image object, e.g. std::vector<std::vector<bool>>,
252 * with the pixel values stored in a 1D container.
253 *
254 * \param image The 2D image to be filled with values. Needs to have to correct dimensions (nCols x nRows).
255 * \param result The image's pixel values stored in a 1D container.
256 */
257 template<class Image, class T>
258 4 static void fillImage(Image& image, const Result<T>& result)
259 {
260 4 const auto nCols = result.header().nCols;
261 4 const auto nRows = result.header().nRows;
262 using RowType = std::decay_t<decltype(image[0])>;
263
4/10
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
10 image.resize(nRows, RowType(nCols));
264
265 4 std::size_t rowIdx = 0;
266 4 std::size_t colIdx = 0;
267
5/6
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 71 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
492 for (const auto val : result)
268 {
269
6/6
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 99 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 99 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 49 times.
600 image[rowIdx][colIdx] = val;
270
271 // start a new line after nCols entries
272
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 100 times.
240 if (++colIdx == nCols)
273 {
274 40 colIdx = 0;
275 40 ++rowIdx;
276 }
277 }
278 4 }
279
280 /*!
281 * \brief Flattens a 2D image object to a 1D container.
282 *
283 * \param image The 2D image to be flattened.
284 */
285 template<class Image>
286 1 static auto flattenImageToVector(const Image& image)
287 {
288 // deducing the type from the access operator fails for std::vector<bool>
289 // for some implementation since the access operator might return a proxy
290 // so we expect a STL container interface here with value_type defined
291
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::vector<std::decay_t<typename Image::value_type::value_type>> data;
292
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
3 data.reserve(image.size()*image[0].size());
293
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 1 times.
23 for (const auto& row : image)
294
5/10
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
50 data.insert(data.end(), row.begin(), row.end());
295
296 1 return data;
297 }
298
299 private:
300
301 2 static bool isBlackAndWhite_(const std::string& magicNumber)
302 {
303
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 return magicNumber == "P1" || magicNumber == "P4";
304 }
305
306 9 static bool isGrayScale_(const std::string& magicNumber)
307 {
308
4/4
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
9 return magicNumber == "P2" || magicNumber == "P5";
309 }
310
311 /*!
312 * \brief Reads the data block of a *.pbm (black and white) file in ASCII encoding.
313 * Returns a vector that contains the pixel values.
314 * \note readHeader(infile) must be called prior to calling this function. This assumes all values to be either 0 or 1.
315 *
316 * \param infile The input stream used to process the image file.
317 * \param headerData The data contained in the file's header.
318 */
319 5 static std::vector<bool> readPBMDataASCII_(std::ifstream& infile,
320 const HeaderData& headerData)
321 {
322
1/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
5 std::string inputLine;
323
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 std::vector<bool> data;
324 10 data.reserve(numPixel_(headerData));
325
326
4/4
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 95 times.
✓ Branch 3 taken 5 times.
200 while (!infile.eof())
327 {
328
1/2
✓ Branch 1 taken 95 times.
✗ Branch 2 not taken.
95 std::getline(infile, inputLine);
329
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 91 times.
95 if (!isComment_(inputLine))
330 {
331
11/16
✗ Branch 0 not taken.
✓ Branch 1 taken 1224 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1215 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1215 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1215 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 73 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 73 times.
✓ Branch 14 taken 49 times.
✓ Branch 15 taken 36 times.
1918 inputLine.erase(std::remove_if(inputLine.begin(), inputLine.end(), [](unsigned char c){ return std::isspace(c); }), inputLine.end());
332
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 86 times.
182 if (!inputLine.empty())
333 {
334
4/4
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 5068 times.
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 5068 times.
5326 for (const auto& value : inputLine)
335 {
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5068 times.
5068 assert(value == '0' || value == '1');
337
1/2
✓ Branch 1 taken 5068 times.
✗ Branch 2 not taken.
5068 data.push_back(value - '0'); // convert char to int
338 }
339 }
340 }
341 }
342
343
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
7 return data;
344 }
345
346 /*!
347 * \brief Reads the data block of a *.pbm (black and white) file in binary encoding.
348 * Returns a vector that contains the pixel values.
349 * \note readHeader(infile) must be called prior to calling this function.
350 *
351 * \param infile The input stream used to process the image file.
352 * \param headerData The data contained in the file's header.
353 */
354 1 static std::vector<bool> readPBMDataBinary_(std::ifstream& infile,
355 const HeaderData& headerData)
356 {
357 1 std::vector<bool> data(numPixel_(headerData));
358
359 // Skip potentially remaining comments in header section
360 // before reading binary content. We detect a comment by
361 // reading a line with std::getline and checking the resulting string.
362 // We continue reading new lines until no more comments are found. Then we
363 // need to set infile's current position to one line before the actual binary
364 // content, otherwise the following steps will fail.
365
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
1 std::string inputLine;
366
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 while (!infile.eof())
367 {
368 // store position before calling std::getline
369
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const auto lastPos = infile.tellg();
370
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::getline(infile, inputLine);
371
372 // stop the loop if no more comment is found and go back one line
373
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!isComment_(inputLine))
374 {
375
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 infile.seekg(lastPos);
376 1 break;
377 }
378 }
379
380 // read actual binary content
381 std::size_t nBytes = 0;
382 std::size_t bitIndex = 0;
383 using Bit = std::uint8_t;
384 Bit b = 0;
385
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 for (std::size_t j = 0; j < headerData.nRows; j++)
386 {
387
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 10 times.
70 for (std::size_t i = 0; i < headerData.nCols; i++)
388 {
389
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 50 times.
60 if (i%8 == 0)
390 {
391 10 char tmp;
392
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 infile.read(&tmp, 1);
393 10 b = static_cast<Bit>(tmp);
394
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
20 if (infile.eof())
395 DUNE_THROW(Dune::IOError, "Failed reading byte " << nBytes);
396
397 10 ++nBytes;
398 }
399
400 60 const Bit k = 7 - (i % 8);
401
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 49 times.
180 data[bitIndex++] = static_cast<bool>((b >> k) & 1);
402 }
403 }
404
405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 return data;
406 }
407
408 /*!
409 * \brief Reads the data block of a *.pgm (grayscale) file in ASCII encoding.
410 * Returns a vector that contains the pixel values.
411 * \note readHeader(infile) must be called prior to calling this function.
412 *
413 * \tparam ValueType The value type representing the pixel data. By default, std::uint8_t (0-255) is used.
414 * Since this type is often defined as unsigned char, some conversion issues during I/O may occur.
415 * Hence the type may be changed, e.g., to std::size_t.
416 *
417 * \param infile The input stream used to process the image file.
418 * \param headerData The data contained in the file's header.
419 */
420 template<class ValueType = std::uint8_t>
421 1 static std::vector<ValueType> readPGMDataASCII_(std::ifstream& infile,
422 const HeaderData& headerData)
423 {
424
2/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
2 std::string inputLine;
425
426
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::vector<ValueType> data;
427 2 data.reserve(numPixel_(headerData));
428
429
4/4
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 1 times.
124 while (!infile.eof())
430 {
431
1/2
✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
61 std::getline(infile, inputLine);
432
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 60 times.
122 if (inputLine.empty())
433 continue;
434
435 // if the line contains multiple comma-separated values, store them individually in a vector
436
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
120 if (inputLine.find(" ") != std::string::npos)
437 {
438 std::istringstream iss(inputLine);
439 std::vector<std::string> tokens;
440 std::copy(std::istream_iterator<std::string>(iss),
441 std::istream_iterator<std::string>(),
442 std::back_inserter(tokens));
443
444 for (const auto& t : tokens)
445 data.push_back(std::stoi(t)); // convert string to integer type
446 }
447 else
448
2/6
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
60 data.push_back(std::stoi(inputLine));
449 }
450
451 1 return data;
452 }
453
454 /*!
455 * \brief Reads the data block of a *.pgm (grayscale) file in binary encoding.
456 * Returns a vector that contains the pixel values.
457 * \note readHeader(infile) must be called prior to calling this function.
458 *
459 * \tparam ValueType The value type representing the pixel data. By default, std::uint8_t (0-255) is used.
460 * Since this type is often defined as unsigned char, some conversion issues during I/O may occur.
461 * Hence the type may be changed, e.g., to std::size_t.
462 *
463 * \param infile The input stream used to process the image file.
464 * \param headerData The data contained in the file's header.
465 */
466 template<class ValueType = std::uint8_t>
467 1 static std::vector<ValueType> readPGMDataBinary_(std::ifstream& infile,
468 const HeaderData& headerData)
469 {
470 // check the size of the binary part of the file
471 1 const auto curPos = infile.tellg();
472 1 infile.seekg(0, std::ios::end);
473 1 const auto endPos = infile.tellg();
474
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const auto size = endPos - curPos;
475 2 if (size != numPixel_(headerData))
476 DUNE_THROW(Dune::IOError, "Binary file size does not match with raster image size");
477
478 // reset to the current position
479 1 infile.seekg(curPos, std::ios::beg);
480
481 // extract the binary data
482 2 std::vector<std::uint8_t> data(size);
483
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 infile.read(reinterpret_cast<char*>(&data[0]), size);
484
485 // convert std::uint8_t to ValueType
486
6/14
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
5 return std::vector<ValueType>(data.begin(), data.end());
487 }
488
489 /*!
490 * \brief Returns the image's number of pixels.
491 *
492 * \param headerData The data contained in the file's header.
493 */
494 static std::size_t numPixel_(const HeaderData& headerData)
495 {
496
4/7
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
8 return headerData.nRows*headerData.nCols;
497 }
498
499 /*!
500 * \brief Returns true if a given line is a comment (starting with #)
501 */
502 static bool isComment_(const std::string_view line)
503 {
504
5/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 91 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 8 times.
112 return line[0] == '#';
505 }
506 };
507
508 } // namespace Dumux
509
510 #endif
511