Explorar el Código

Actually build an HDR-image and do something with it

Johannes Hofmann hace 8 años
padre
commit
e8c219e240
Se han modificado 11 ficheros con 1444 adiciones y 59 borrados
  1. 1
    0
      .gitignore
  2. 5
    5
      Makefile
  3. 382
    0
      csv_reader.cpp
  4. 115
    0
      csv_reader.h
  5. 342
    0
      exif.cpp
  6. 36
    0
      exif.h
  7. 365
    28
      haader.cpp
  8. 97
    4
      haader.h
  9. 26
    7
      image.cpp
  10. 14
    6
      image.h
  11. 61
    9
      main.cpp

+ 1
- 0
.gitignore Ver fichero

@@ -1,3 +1,4 @@
1 1
 haader
2 2
 .*.swp
3 3
 *.o
4
+*.ppm

+ 5
- 5
Makefile Ver fichero

@@ -1,15 +1,15 @@
1 1
 CXXFLAGS= -std=c++98 -pedantic -Wall -O3
2 2
 LIBS= -ljpeg
3
-OBJS= haader.o image.o
4
-
5
-run: haader
6
-	./haader
3
+OBJS= haader.o image.o exif.o csv_reader.o
7 4
 
8 5
 haader: main.cpp $(OBJS)
9 6
 	$(CXX) main.cpp -o haader $(OBJS) $(CXXFLAGS) $(LIBS)
10 7
 
8
+run: haader
9
+	./haader
10
+
11 11
 %.o: %.c %.h
12
-	gcc $< -c -o $@ $(CFLAGS)
12
+	$(CXX) $< -c -o $@ $(CFLAGS)
13 13
 
14 14
 clean:
15 15
 	rm -f haader $(OBJS)

+ 382
- 0
csv_reader.cpp Ver fichero

@@ -0,0 +1,382 @@
1
+#include "csv_reader.h"
2
+
3
+#include <errno.h>
4
+#include <limits.h>
5
+#include <stdlib.h>
6
+
7
+#include <algorithm>
8
+#include <fstream>
9
+#include <iostream>
10
+#include <sstream>
11
+
12
+using namespace std;
13
+
14
+
15
+CSVField::CSVField(int start, int length, bool hasQuotes): start(start),
16
+                                                           length(length),
17
+                                                           hasQuotes(hasQuotes)
18
+{
19
+}
20
+
21
+CSVRow::CSVRow(string *row,
22
+               const std::string * const filePath,
23
+               long lineNumber,
24
+               char delimiter,
25
+               char quote): m_row(row),
26
+                            m_filePath(filePath),
27
+                            m_lineNumber(lineNumber),
28
+                            m_quote(quote)
29
+{
30
+    parse(delimiter, quote);
31
+}
32
+
33
+string CSVRow::toString() const {
34
+    stringstream ss;
35
+
36
+    ss << "CSVRow (" << endl;
37
+
38
+    if (m_lineNumber > 0) {
39
+        ss << "  line #: \"" << m_lineNumber << "\"" << endl;
40
+    }
41
+
42
+    if (m_filePath) {
43
+        ss << "  path: \"" << *m_filePath << "\"" << endl;
44
+    }
45
+
46
+    ss << "  value: \"" << *m_row << "\"" << endl;
47
+
48
+    for (unsigned int i = 0; i < m_fields.size(); i++) {
49
+        string row;
50
+        getFieldAsString(i, &row);
51
+        ss << "  field " << i << ": '" << row << "'" << endl;
52
+    }
53
+
54
+    ss << ")";
55
+    return ss.str();
56
+}
57
+
58
+static bool string_to_long(const string &s, long *out) {
59
+    const char *cstr = s.c_str();
60
+    char *end = NULL;
61
+
62
+    errno = 0;
63
+
64
+    long x = strtol(cstr, &end, 10);
65
+
66
+    if (errno != 0) {
67
+        return false;
68
+    } else if (end == &cstr[s.size()]) {
69
+        *out = x;
70
+        return true;
71
+    } else {
72
+        return false;
73
+    }
74
+}
75
+
76
+static bool cstring_to_long(const char *start, const char *end, long *out) {
77
+    char *parse_end = NULL;
78
+
79
+    errno = 0;
80
+
81
+    long x = strtol(start, &parse_end, 10);
82
+
83
+    if (errno != 0) {
84
+        return false;
85
+    } else if (parse_end == end) {
86
+        *out = x;
87
+        return true;
88
+    } else {
89
+        return false;
90
+    }
91
+}
92
+
93
+static bool string_to_double(const string &s, double *out) {
94
+    const char *cstr = s.c_str();
95
+    char *end = NULL;
96
+
97
+    errno = 0;
98
+
99
+    double x = strtod(cstr, &end);
100
+
101
+    if (errno != 0) {
102
+        return false;
103
+    } else if (end == &cstr[s.size()]) {
104
+        *out = x;
105
+        return true;
106
+    } else {
107
+        return false;
108
+    }
109
+}
110
+
111
+static bool cstring_to_double(const char *start, const char *end, double *out) {
112
+    char *parse_end = NULL;
113
+
114
+    errno = 0;
115
+
116
+    double x = strtod(start, &parse_end);
117
+
118
+    if (errno != 0) {
119
+        return false;
120
+    } else if (parse_end == end) {
121
+        *out = x;
122
+        return true;
123
+    } else {
124
+        return false;
125
+    }
126
+}
127
+
128
+bool CSVRow::getFieldAsString(unsigned int fieldIndex, string *out) const {
129
+    if (fieldIndex < m_fields.size()) {
130
+        CSVField field = m_fields[fieldIndex];
131
+
132
+        if (field.length == 0) {
133
+            *out = "";
134
+        } else {
135
+            string s;
136
+
137
+            if (field.hasQuotes) {
138
+                s = m_row->substr(field.start, field.length);
139
+                s = unquote(s, m_quote);
140
+            } else {
141
+                s = m_row->substr(field.start, field.length);
142
+            }
143
+
144
+            // remove newlines
145
+            s.erase(std::remove(s.begin(), s.end(), '\n'), s.end());
146
+            s.erase(std::remove(s.begin(), s.end(), '\r'), s.end());
147
+
148
+            *out = s;
149
+        }
150
+
151
+        return true;
152
+    } else {
153
+        return false;
154
+    }
155
+}
156
+
157
+bool CSVRow::getFieldAsLong(unsigned int fieldIndex, long *out) const {
158
+    if (fieldIndex >= m_fields.size()) {
159
+        return false;
160
+    }
161
+
162
+    if (m_fields[fieldIndex].hasQuotes) {
163
+        string field;
164
+        if (!getFieldAsString(fieldIndex, &field)) {
165
+            return false;
166
+        }
167
+
168
+        return string_to_long(field, out);
169
+    } else {
170
+        const CSVField *field = &m_fields[fieldIndex];
171
+        int sep_index = field->start + field->length;
172
+        char sep = (*m_row)[sep_index];
173
+        (*m_row)[sep_index] = '\0';
174
+
175
+        bool ok = cstring_to_long(m_row->c_str() + field->start, m_row->c_str() + sep_index, out);
176
+
177
+        (*m_row)[sep_index] = sep;
178
+
179
+        return ok;
180
+    }
181
+}
182
+
183
+bool CSVRow::getFieldAsInt(unsigned int fieldIndex, int *out) const {
184
+    if (fieldIndex >= m_fields.size()) {
185
+        return false;
186
+    }
187
+
188
+    long x;
189
+    if (!getFieldAsLong(fieldIndex, &x)) {
190
+        return false;
191
+    }
192
+
193
+    if (x < INT_MIN || x > INT_MAX) {
194
+        // underflow or overflow
195
+        return false;
196
+    }
197
+
198
+    *out = (int)x;
199
+
200
+    return true;
201
+}
202
+
203
+bool CSVRow::getFieldAsDouble(unsigned int fieldIndex, double *out) const {
204
+    if (fieldIndex >= m_fields.size()) {
205
+        return false;
206
+    }
207
+
208
+    if (m_fields[fieldIndex].hasQuotes) {
209
+        string field;
210
+        if (!getFieldAsString(fieldIndex, &field)) {
211
+            return false;
212
+        }
213
+
214
+        return string_to_double(field, out);
215
+    } else {
216
+        const CSVField *field = &m_fields[fieldIndex];
217
+        int sep_index = field->start + field->length;
218
+        char sep = (*m_row)[sep_index];
219
+        (*m_row)[sep_index] = '\0';
220
+
221
+        bool ok = cstring_to_double(m_row->c_str() + field->start, m_row->c_str() + sep_index, out);
222
+
223
+        (*m_row)[sep_index] = sep;
224
+
225
+        return ok;
226
+    }
227
+}
228
+
229
+string CSVRow::getFilePath() const {
230
+    if (m_filePath) {
231
+        return string(*m_filePath);
232
+    } else {
233
+        return "";
234
+    }
235
+}
236
+
237
+long CSVRow::getLineNumber() const {
238
+    return m_lineNumber;
239
+}
240
+
241
+unsigned int CSVRow::getNumberOfFields() const {
242
+    return m_fields.size();
243
+}
244
+
245
+void CSVRow::parse(char delimiter, char quote) {
246
+    m_fields.clear();
247
+
248
+    //TODO: properly handle unicode strings (could use QString::fromUtf8 and iterate over that)
249
+    int start = 0;
250
+    int length = -1;
251
+    bool in_quotes = false;
252
+    bool has_quotes = false;
253
+
254
+    unsigned int size = m_row->size();
255
+
256
+    // handle trailing \n and \r chars
257
+    while (size > 0 && ((*m_row)[size - 1] == '\n' || (*m_row)[size - 1] == '\r')) {
258
+        size--;
259
+    }
260
+
261
+    for (unsigned int i = 0; i < size; i++) {
262
+        length++;
263
+        char c = (*m_row)[i];
264
+
265
+        if (c == delimiter && !in_quotes) {
266
+            m_fields.push_back(CSVField(start, length, has_quotes));
267
+            start = i + 1;
268
+            length = -1;
269
+            has_quotes = false;
270
+        }
271
+
272
+        if (c == quote) {
273
+            has_quotes = true;
274
+            if (in_quotes) {
275
+                in_quotes = false;
276
+            } else {
277
+                in_quotes = true;
278
+            }
279
+        }
280
+    }
281
+    if (!(start == 0 && length == -1)) {
282
+        length++;
283
+        m_fields.push_back(CSVField(start, length, has_quotes));
284
+    }
285
+}
286
+
287
+string CSVRow::unquote(const string &fieldStr, char quoteChar) {
288
+    string ret;
289
+    bool in_quotes = false;
290
+    bool last_char_was_quote = false;
291
+
292
+    //TODO: properly handle unicode strings here too
293
+    for (unsigned int i = 0; i < fieldStr.size(); i++) {
294
+        char c = fieldStr[i];
295
+
296
+        if (in_quotes) {
297
+            if (c == quoteChar) {
298
+                in_quotes = false;
299
+                last_char_was_quote = true;
300
+            } else {
301
+                ret.push_back(c);
302
+                last_char_was_quote = false;
303
+            }
304
+        } else {
305
+            if (c == quoteChar) {
306
+                in_quotes = true;
307
+                if (last_char_was_quote) {
308
+                    ret.push_back(quoteChar);
309
+                }
310
+            }
311
+            last_char_was_quote = false;
312
+        }
313
+    }
314
+
315
+    return ret;
316
+}
317
+
318
+string CSVRow::quote(const string &fieldStr, char quoteChar) {
319
+    string ret;
320
+
321
+    //TODO: properly handle unicode strings here too
322
+    //TODO: optimize using string::find
323
+    for (unsigned int i = 0; i < fieldStr.size(); i++) {
324
+        char c = fieldStr[i];
325
+
326
+        if (c == quoteChar) {
327
+            ret.append(2, quoteChar);
328
+        } else {
329
+            ret.push_back(c);
330
+        }
331
+    }
332
+
333
+    return ret;
334
+}
335
+
336
+bool CSVReader::readFromFile(const std::string filePath,
337
+                             CSVReader::RowCallback callback,
338
+                             void *userData,
339
+                             char delimiter,
340
+                             char quote)
341
+{
342
+    ifstream stream;
343
+    stream.open(filePath.c_str(), ios::in);
344
+    if (stream) {
345
+        bool ok = CSVReader::readFromStream(stream,
346
+                                            callback,
347
+                                            userData,
348
+                                            &filePath,
349
+                                            delimiter,
350
+                                            quote);
351
+        stream.close();
352
+        return ok;
353
+    } else {
354
+        cerr << "Error: Cannot read file \"" << filePath << "\"" << endl;
355
+        return false;
356
+    }
357
+
358
+    return true;
359
+}
360
+
361
+
362
+bool CSVReader::readFromStream(std::istream &stream,
363
+                               CSVReader::RowCallback callback,
364
+                               void *userData,
365
+                               const string * const filePath,
366
+                               char delimiter,
367
+                               char quote)
368
+{
369
+    string line = "";
370
+    long nr = 0;
371
+    while (getline(stream, line, '\n')) {
372
+        nr++;
373
+        if (line.size() > 0) {
374
+            CSVRow row(&line, filePath, nr, delimiter, quote);
375
+            if (!callback(row, userData)) {
376
+                return false;
377
+            }
378
+        }
379
+    }
380
+
381
+    return true;
382
+}

+ 115
- 0
csv_reader.h Ver fichero

@@ -0,0 +1,115 @@
1
+#ifndef CSVREADER_H
2
+#define CSVREADER_H
3
+
4
+#include <string>
5
+#include <vector>
6
+#include <istream>
7
+
8
+
9
+// A Field in a CSVRow
10
+class CSVField {
11
+public:
12
+    CSVField(int start, int length, bool hasQuotes);
13
+
14
+    // byte offset
15
+    int start;
16
+
17
+    // length in bytes
18
+    int length;
19
+
20
+    // contains quotes (use CSVRow::unquote)
21
+    bool hasQuotes;
22
+};
23
+
24
+// A Row in a CSV Document
25
+class CSVRow {
26
+public:
27
+    CSVRow(std::string *row,
28
+           const std::string * const filePath=0,
29
+           long lineNumber=-1,
30
+           char delimiter=',',
31
+           char quote='"');
32
+
33
+    // Return a human readable representation of a CSVRow.
34
+    std::string toString() const;
35
+
36
+    // Get the value of a field.
37
+    // fieldIndex starts at 0.
38
+    // Returns false if fieldIndex is out of bounds or
39
+    // field could not be converted to the specified type.
40
+    bool getFieldAsString(unsigned int fieldIndex, std::string *out) const;
41
+    bool getFieldAsLong(unsigned int fieldIndex, long *out) const;
42
+    bool getFieldAsInt(unsigned int fieldIndex, int *out) const;
43
+    bool getFieldAsDouble(unsigned int fieldIndex, double *out) const;
44
+
45
+    std::string getFilePath() const;
46
+
47
+    // Get the line number
48
+    // starting at 1 for the first line.
49
+    // Return a value < 0 if there is no line number specified.
50
+    long getLineNumber() const;
51
+
52
+    unsigned int getNumberOfFields() const;
53
+
54
+    // Get the original string that was quoted using some of the rules from RFC 4180
55
+    // Examples: unquote("'abc'", "'") == "abc"
56
+    //           unquote("'ab''c'", "'") == "ab'c"
57
+    // Fields containing line breaks are not supported.
58
+    static std::string unquote(const std::string &fieldStr, char quoteChar);
59
+
60
+    // Quotes a string using some of the rules from RFC 4180
61
+    // Example: quote("ab'c", "'") == "ab''c"
62
+    // Fields containing line breaks are not supported.
63
+    static std::string quote(const std::string &fieldStr, char quoteChar);
64
+
65
+private:
66
+    // fill m_fields
67
+    void parse(char delimiter, char quote);
68
+
69
+    // raw bytes from CSV document
70
+    std::string *m_row;
71
+
72
+    // each field
73
+    std::vector<CSVField> m_fields;
74
+
75
+    // file path of the CSV document or NULL
76
+    const std::string * const m_filePath;
77
+
78
+    long m_lineNumber;
79
+
80
+    // quote character
81
+    char m_quote;
82
+};
83
+
84
+
85
+// static functions to parse CSV data
86
+class CSVReader {
87
+public:
88
+
89
+    // callback function signature
90
+    typedef bool (*RowCallback) (const CSVRow &row, void *userData);
91
+
92
+    // Parse a CSV document given by filePath.
93
+    // Invokes callback function for each row with the supplied userData.
94
+    // Stop parsing if callback returns false.
95
+    // filePath: pointer to a file path or NULL if there is no file specified
96
+    // Returns false if file cannot be opened.
97
+    static bool readFromFile(const std::string filePath,
98
+                             RowCallback callback,
99
+                             void *userData,
100
+                             char delimiter=',',
101
+                             char quote='"');
102
+
103
+    // Parse CSV data from stream.
104
+    // Could be a std::stringstream or a std::fstream, etc.
105
+    // Invokes callback function for each row with the supplied userData.
106
+    // Stop parsing and return false if callback returns false.
107
+    static bool readFromStream(std::istream &stream,
108
+                               RowCallback callback,
109
+                               void *userData,
110
+                               const std::string * const filePath=0,
111
+                               char delimiter=',',
112
+                               char quote='"');
113
+};
114
+
115
+#endif //CSVREADER_H

+ 342
- 0
exif.cpp Ver fichero

@@ -0,0 +1,342 @@
1
+#include "exif.h"
2
+
3
+#include <iostream>
4
+#include <fstream>
5
+#include <stdint.h>
6
+#include <math.h>
7
+
8
+using namespace std;
9
+
10
+
11
+urational_t::urational_t(): p(0), q(0)
12
+{}
13
+
14
+urational_t::urational_t(uint32_t p, uint32_t q): p(p), q(q)
15
+{}
16
+
17
+srational_t::srational_t(): p(0), q(0)
18
+{}
19
+
20
+srational_t::srational_t(int32_t p, int32_t q): p(p), q(q)
21
+{}
22
+
23
+
24
+ExifTags::ExifTags():
25
+    ifd0_parsed_ok(false),
26
+    exififd_parsed_ok(false),
27
+    exififd_offset(0)
28
+{
29
+}
30
+
31
+
32
+static uint16_t read_be_uint16(istream &stream) {
33
+    unsigned char buf[2];
34
+    stream.read((char*)buf, 2);
35
+    return ((uint16_t)buf[0]) * 256 + (uint16_t)buf[1];
36
+}
37
+
38
+static uint16_t read_le_uint16(istream &stream) {
39
+    unsigned char buf[2];
40
+    stream.read((char*)buf, 2);
41
+    return (uint16_t)buf[0] + ((uint16_t)buf[1]) * 256;
42
+}
43
+
44
+static uint16_t read_uint16(istream &stream, bool is_little_endian) {
45
+    if (is_little_endian) {
46
+        return read_le_uint16(stream);
47
+    } else {
48
+        return read_be_uint16(stream);
49
+    }
50
+}
51
+
52
+static uint32_t read_be_uint32(istream &stream) {
53
+    unsigned char buf[4];
54
+    stream.read((char*)buf, 4);
55
+    return ((uint32_t)buf[0]) * 16777216 +
56
+           ((uint32_t)buf[1]) * 65536 +
57
+           ((uint32_t)buf[2]) * 256 +
58
+           (uint32_t)buf[3];
59
+}
60
+
61
+static uint32_t read_le_uint32(istream &stream) {
62
+    unsigned char buf[4];
63
+    stream.read((char*)buf, 4);
64
+    return ((uint32_t)buf[3]) * 16777216 +
65
+           ((uint32_t)buf[2]) * 65536 +
66
+           ((uint32_t)buf[1]) * 256 +
67
+           (uint32_t)buf[0];
68
+}
69
+
70
+static uint32_t read_uint32(istream &stream, bool is_little_endian) {
71
+    if (is_little_endian) {
72
+        return read_le_uint32(stream);
73
+    } else {
74
+        return read_be_uint32(stream);
75
+    }
76
+}
77
+
78
+static int32_t read_be_int32(istream &stream) {
79
+    unsigned char buf[4];
80
+    stream.read((char*)buf, 4);
81
+    return (int32_t)(((uint32_t)buf[0]) * 16777216 +
82
+                     ((uint32_t)buf[1]) * 65536 +
83
+                     ((uint32_t)buf[2]) * 256 +
84
+                     (uint32_t)buf[3]);
85
+}
86
+
87
+static int32_t read_le_int32(istream &stream) {
88
+    unsigned char buf[4];
89
+    stream.read((char*)buf, 4);
90
+    return (int32_t)(((uint32_t)buf[3]) * 16777216 +
91
+                     ((uint32_t)buf[2]) * 65536 +
92
+                     ((uint32_t)buf[1]) * 256 +
93
+                     (uint32_t)buf[0]);
94
+}
95
+
96
+/*
97
+static int32_t read_int32(istream &stream, bool is_little_endian) {
98
+    if (is_little_endian) {
99
+        return read_le_int32(stream);
100
+    } else {
101
+        return read_be_int32(stream);
102
+    }
103
+}
104
+*/
105
+
106
+static urational_t read_urational(istream &stream, bool is_little_endian) {
107
+    if (is_little_endian) {
108
+        uint32_t p = read_le_uint32(stream);
109
+        uint32_t q = read_le_uint32(stream);
110
+        return urational_t(p, q);
111
+    } else {
112
+        uint32_t p = read_be_uint32(stream);
113
+        uint32_t q = read_be_uint32(stream);
114
+        return urational_t(p, q);
115
+    }
116
+}
117
+
118
+static srational_t read_srational(istream &stream, bool is_little_endian) {
119
+    if (is_little_endian) {
120
+        int32_t p = read_le_int32(stream);
121
+        int32_t q = read_le_int32(stream);
122
+        return srational_t(p, q);
123
+    } else {
124
+        int32_t p = read_be_int32(stream);
125
+        int32_t q = read_be_int32(stream);
126
+        return srational_t(p, q);
127
+    }
128
+}
129
+
130
+
131
+static bool read_exif_tags(istream &stream,
132
+                           unsigned int num_entries,
133
+                           ExifTags &tags,
134
+                           uint64_t tiff_start_pos,
135
+                           bool is_little_endian)
136
+{
137
+    for (unsigned int i = 0; i < num_entries; i++) {
138
+        uint16_t tag_num = read_uint16(stream, is_little_endian);
139
+        uint16_t data_format = read_uint16(stream, is_little_endian);
140
+        uint32_t num_components = read_uint32(stream, is_little_endian);
141
+        uint32_t tag_data = read_uint32(stream, is_little_endian);
142
+
143
+        if (tag_num == 34665) {
144
+            if (data_format == 4 && num_components == 1) {
145
+                if (tag_data > 65535) {
146
+                    cerr << "Exif IFD offset too big (" << tag_data << ")." << endl;
147
+                    return false;
148
+                }
149
+                tags.exififd_offset = tag_data;
150
+            } else {
151
+                cerr << "Exif IFD offset has strange format." << endl;
152
+                return false;
153
+            }
154
+
155
+        }
156
+
157
+        if (tag_num == 0x829a) {
158
+            uint64_t cur = stream.tellg();
159
+            stream.seekg(tiff_start_pos + tag_data, std::ios_base::beg);
160
+
161
+            tags.exposure_time = read_urational(stream, is_little_endian);
162
+
163
+            stream.seekg(cur, std::ios_base::beg);
164
+        }
165
+
166
+        if (tag_num == 0x9201) {
167
+            uint64_t cur = stream.tellg();
168
+            stream.seekg(tiff_start_pos + tag_data, std::ios_base::beg);
169
+
170
+            tags.shutter_speed_value = read_srational(stream, is_little_endian);
171
+
172
+            stream.seekg(cur, std::ios_base::beg);
173
+        }
174
+    }
175
+
176
+    return true;
177
+}
178
+
179
+static bool read_exif_ifd(istream &stream,
180
+                          uint64_t tiff_start_pos,
181
+                          uint16_t ifd_offset,
182
+                          ExifTags &tags,
183
+                          bool is_little_endian)
184
+{
185
+    if (ifd_offset == 0) {
186
+        return true;
187
+    }
188
+
189
+    {
190
+        uint64_t cur = stream.tellg();
191
+        if (cur < tiff_start_pos) {
192
+            cerr << "Read backwards?" << endl;
193
+            return false;
194
+        }
195
+
196
+        if (ifd_offset < (cur - tiff_start_pos)) {
197
+            cerr << "IFD offset too small." << endl;
198
+            return false;
199
+        }
200
+    }
201
+
202
+    stream.seekg(tiff_start_pos + ifd_offset, std::ios_base::beg);
203
+
204
+    uint16_t num_entries = read_le_uint16(stream);
205
+
206
+    return read_exif_tags(stream, num_entries, tags, tiff_start_pos, is_little_endian);
207
+}
208
+
209
+static bool parse_exif(istream &stream, uint16_t length, ExifTags &output) {
210
+    if (length < 8) {
211
+        return false;
212
+    }
213
+
214
+    {
215
+        char buf[6];
216
+        const char *compare = "Exif\0\0";
217
+        stream.read(buf, 6);
218
+        for (unsigned int i = 0; i < 6; i++) {
219
+            if (buf[i] != compare[i]) {
220
+                return false;
221
+            }
222
+        }
223
+    }
224
+
225
+    uint64_t tiff_start_pos = stream.tellg();
226
+
227
+    bool is_little_endian = false;
228
+    {
229
+
230
+        char buf[2];
231
+        stream.read(buf, 2);
232
+
233
+        if (buf[0] == 'I' && buf[1] == 'I') {
234
+            is_little_endian = true;
235
+        } else if (buf[0] == 'M' && buf[1] == 'M') {
236
+            is_little_endian = false;
237
+        } else {
238
+            cerr << "Endian fail" << endl;
239
+            return false;
240
+        }
241
+    }
242
+
243
+    {
244
+        uint16_t magic = read_uint16(stream, is_little_endian);
245
+
246
+        if (magic != 0x002a) {
247
+            cerr << "TIFF fail" << endl;
248
+            return false;
249
+        }
250
+    }
251
+
252
+    uint32_t ifd0_offset = read_uint32(stream, is_little_endian);
253
+
254
+    // Read IFD0
255
+    output.ifd0_parsed_ok = read_exif_ifd(stream, tiff_start_pos, ifd0_offset, output, is_little_endian);
256
+
257
+    if (output.exififd_offset != 0) {
258
+        // Read Exif IFD
259
+        output.exififd_parsed_ok = read_exif_ifd(stream, tiff_start_pos, output.exififd_offset, output, is_little_endian);
260
+    }
261
+
262
+    if (stream.fail()) {
263
+        cerr << "stream failed" << endl;
264
+        return false;
265
+    } else {
266
+        return true;
267
+    }
268
+}
269
+
270
+bool ExifTags::read_from_jpeg(istream &stream, ExifTags &output) {
271
+    unsigned char buf[2];
272
+    stream.read((char*)buf, 2);
273
+    if (!(buf[0] == 0xff && buf[1] == 0xd8)) {
274
+        return false;
275
+    }
276
+
277
+    while (stream.good()) {
278
+        stream.read((char*)buf, 2);
279
+        if (buf[0] != 0xff) {
280
+            cerr << "not a tag" << endl;
281
+            return false;
282
+        }
283
+        if (buf[1] == 0xff || buf[1] == 0x00) {
284
+            cerr << "not a marker" << endl;
285
+            return false;
286
+        }
287
+
288
+        if (buf[1] == 0xd9) {
289
+            break;
290
+        }
291
+
292
+        if (buf[1] == 0xda || (buf[1] >= 0xd0 && buf[1] <= 0xd7)) {
293
+            // SOS marker or segment without length
294
+            unsigned char c = 0;
295
+            while(stream.good()) {
296
+                c = stream.get();
297
+                if (c == 0xff) {
298
+                    c = stream.get();
299
+                    if (c != 0x00) {
300
+                        // end of section
301
+                        stream.seekg(-2, std::ios_base::cur);
302
+                        break;
303
+                    }
304
+                }
305
+            }
306
+        } else {
307
+            // read size, seek to next marker
308
+
309
+            uint16_t s = read_be_uint16(stream);
310
+
311
+            if (s < 2) {
312
+                cerr << "invalid size" << endl;
313
+                return false;
314
+            }
315
+
316
+            if (buf[1] == 0xe1) {
317
+                return parse_exif(stream, s, output);
318
+            }
319
+
320
+            stream.seekg(s - 2, std::ios_base::cur);
321
+        }
322
+    }
323
+
324
+    if (stream.fail()) {
325
+        cerr << "stream failed" << endl;
326
+        return false;
327
+    } else {
328
+        return true;
329
+    }
330
+}
331
+
332
+bool ExifTags::get_exposure_time(double &output) {
333
+    if (exposure_time.p != 0 or exposure_time.q != 0) {
334
+        output = (double)exposure_time.p / (double)exposure_time.q;
335
+        return true;
336
+    } else if (shutter_speed_value.p != 0 or shutter_speed_value.q != 0) {
337
+        output = 1.0 / pow(2.0, (double)shutter_speed_value.p / (double)shutter_speed_value.q);
338
+        return true;
339
+    } else {
340
+        return false;
341
+    }
342
+}

+ 36
- 0
exif.h Ver fichero

@@ -0,0 +1,36 @@
1
+#include <stdint.h>
2
+#include <iostream>
3
+
4
+// unsigned rational number: p/q
5
+struct urational_t {
6
+    urational_t();
7
+    urational_t(uint32_t p, uint32_t q);
8
+
9
+    uint32_t p; // numerator
10
+    uint32_t q; // denominator
11
+};
12
+
13
+// signed rational number: p/q
14
+struct srational_t {
15
+    srational_t();
16
+    srational_t(int32_t p, int32_t q);
17
+
18
+    int32_t p; // numerator
19
+    int32_t q; // denominator
20
+};
21
+
22
+struct ExifTags {
23
+    ExifTags();
24
+
25
+    static bool read_from_jpeg(std::istream &stream, ExifTags &output);
26
+
27
+    // Returns true => output reference is set to exposure time or shutter speed in seconds.
28
+    // Returns false => do not set output because there is no valid value.
29
+    bool get_exposure_time(double &output);
30
+
31
+    bool ifd0_parsed_ok;
32
+    bool exififd_parsed_ok;
33
+    uint16_t exififd_offset;
34
+    urational_t exposure_time;
35
+    srational_t shutter_speed_value;
36
+};

+ 365
- 28
haader.cpp Ver fichero

@@ -1,13 +1,247 @@
1 1
 #include <iostream>
2 2
 #include <fstream>
3
+#include <algorithm>
3 4
 #include <stdlib.h>
4 5
 #include <time.h>
6
+#include <math.h>
5 7
 
6 8
 #include "haader.h"
7 9
 
8 10
 using namespace std;
9 11
 using namespace haader;
10 12
 
13
+
14
+int ResponseFunction::lookup(double value) const {
15
+    // + 0.5 for rounding to nearest integer
16
+    int bin = (double)m_lookup_table.size() * (value - m_min_value) * m_recip_range + 0.5;
17
+
18
+    if (bin < 0) {
19
+        return 0;
20
+    } else if ((unsigned int)bin >= m_lookup_table.size()) {
21
+        return 255;
22
+    } else {
23
+        return m_lookup_table[bin];
24
+    }
25
+}
26
+
27
+double InverseResponseFunction::lookup(int index) const {
28
+    return m_lookup_table[index];
29
+}
30
+
31
+int InverseResponseFunction::inverse_lookup(double value) const {
32
+    if (value <= m_lookup_table[0]) {
33
+        return 0;
34
+    }
35
+    if (value >= m_lookup_table[255]) {
36
+        return 255;
37
+    }
38
+
39
+    for (unsigned int i = 1; i < 256; i++) {
40
+        if (value > m_lookup_table[i - 1] && value <= m_lookup_table[i]) {
41
+            double diff_a = value - m_lookup_table[i - 1];
42
+            double diff_b = m_lookup_table[i] - value;
43
+            if (diff_a < diff_b) {
44
+                return i - 1;
45
+            } else {
46
+                return i;
47
+            }
48
+        }
49
+    }
50
+
51
+    // should not happen
52
+    return 0;
53
+}
54
+
55
+ResponseFunction InverseResponseFunction::to_response_function(int bins) {
56
+    if (!is_monotonic()) {
57
+        cerr << "InverseResponseFunction::to_response_function: not monotonic." << endl;
58
+        return ResponseFunction();
59
+    }
60
+
61
+    double min_value = m_lookup_table[0];
62
+    double max_value = m_lookup_table[m_lookup_table.size() - 1];
63
+
64
+    ResponseFunction rf;
65
+    rf.m_min_value = min_value;
66
+    rf.m_max_value = max_value;
67
+    rf.m_recip_range = 1.0 / (max_value - min_value);
68
+    rf.m_lookup_table.resize(bins);
69
+
70
+
71
+    //TODO reduce quadratic complexity to linear complexity
72
+    for (int i = 0; i < bins; i++) {
73
+        double y = min_value + ((max_value - min_value) * i) / (double)(bins - 1.0);
74
+        rf.m_lookup_table[i] = inverse_lookup(y);
75
+    }
76
+
77
+    return rf;
78
+}
79
+
80
+bool InverseResponseFunction::repair() {
81
+
82
+    // Fill missing values
83
+    fill_zeros();
84
+
85
+    if (is_monotonic()) {
86
+        return true;
87
+    }
88
+
89
+    for (unsigned int i = 0; i < 128; i++) {
90
+        cout << "low pass filter " << i + 1 << endl;
91
+        low_pass_filter();
92
+        if (is_monotonic()) {
93
+            // repair success
94
+
95
+            // offset values so that m_lookup_table[128] is 0.0 again
96
+            double offset = -m_lookup_table[128];
97
+            for (unsigned int i = 0; i < 256; i++) {
98
+                m_lookup_table[i] += offset;
99
+            }
100
+
101
+            return true;
102
+        }
103
+    }
104
+
105
+    // repair failed
106
+    return false;
107
+}
108
+
109
+void InverseResponseFunction::fill_zeros() {
110
+    //TODO test correctness
111
+
112
+    // fill at front
113
+    if (m_lookup_table[0] == 0) {
114
+        for (unsigned int i = 0; i < 128; i++) {
115
+            if (m_lookup_table[i] != 0.0) {
116
+                for (unsigned int k = 0; k <= i; k++) {
117
+                    m_lookup_table[k] = m_lookup_table[i];
118
+                }
119
+                break;
120
+            }
121
+        }
122
+    }
123
+
124
+    // fill at back
125
+    if (m_lookup_table[255] == 0) {
126
+        for (unsigned int i = 255; i > 128; i--) {
127
+            if (m_lookup_table[i] != 0.0) {
128
+                for (unsigned int k = 255; k >= i; k--) {
129
+                    m_lookup_table[k] = m_lookup_table[i];
130
+                }
131
+                break;
132
+            }
133
+        }
134
+    }
135
+
136
+    {
137
+        bool inside_zeros = false;
138
+        int start_index = 0;
139
+        for (unsigned int i = 1; i < 256; i++) {
140
+            if (m_lookup_table[i] == 0.0 && !inside_zeros && i != 128) {
141
+                start_index = i;
142
+                inside_zeros = true;
143
+            } else if ((m_lookup_table[i] != 0.0 || i == 128) && inside_zeros) {
144
+                inside_zeros = false;
145
+                double a_val = m_lookup_table[start_index - 1];
146
+                double b_val = m_lookup_table[i];
147
+                for (unsigned int k = start_index; k < i; k++) {
148
+                    double t = (double)(k - start_index + 1) / (double)(i - start_index + 1);
149
+                    m_lookup_table[k] = (1.0 - t) * a_val + t * b_val;
150
+                }
151
+            }
152
+        }
153
+    }
154
+}
155
+
156
+void InverseResponseFunction::low_pass_filter() {
157
+    vector<double> newvals;
158
+    newvals.resize(256);
159
+
160
+    newvals[0] = m_lookup_table[0];
161
+    newvals[255] = m_lookup_table[255];
162
+
163
+    for (unsigned int i = 1; i < 255; i++) {
164
+        newvals[i] = 0.25 * m_lookup_table[i - 1] +  0.5 * m_lookup_table[i] + 0.25 * m_lookup_table[i + 1];
165
+    }
166
+
167
+    for (unsigned int i = 0; i < 256; i++) {
168
+        m_lookup_table[i] = newvals[i];
169
+    }
170
+}
171
+
172
+bool InverseResponseFunction::is_monotonic() {
173
+    for (unsigned int i = 1; i < 256; i++) {
174
+        if (m_lookup_table[i - 1] > m_lookup_table[i]) {
175
+            return false;
176
+        }
177
+    }
178
+    return true;
179
+}
180
+
181
+
182
+HdrImage::HdrImage():
183
+    m_width(0),
184
+    m_height(0),
185
+    m_components(0)
186
+{
187
+}
188
+
189
+Image HdrImage::get_log_image() {
190
+    if (m_image_data.size() == 0) {
191
+        return Image();
192
+    }
193
+
194
+    Image output;
195
+    output.m_image_data.resize(m_image_data.size());
196
+    output.m_width = m_width;
197
+    output.m_height = m_height;
198
+    output.m_components = m_components;
199
+
200
+    double min_value = m_image_data[0];
201
+    double max_value = m_image_data[0];
202
+
203
+    unsigned int size = m_image_data.size();
204
+    for (unsigned int i = 1; i < size; i++) {
205
+        double val = m_image_data[i];
206
+        if (val < min_value) {
207
+            min_value = val;
208
+        }
209
+        if (val > max_value) {
210
+            max_value = val;
211
+        }
212
+    }
213
+
214
+    double scale = 256.0 / (max_value - min_value);
215
+
216
+    for (unsigned int i = 0; i < size; i++) {
217
+        output.m_image_data[i] = (int)((m_image_data[i] - min_value) * scale);
218
+    }
219
+
220
+    return output;
221
+}
222
+
223
+Image HdrImage::expose(double exposure_time, const ResponseFunction &rf, double compression) {
224
+    if (m_image_data.size() == 0) {
225
+        return Image();
226
+    }
227
+
228
+    Image output;
229
+    output.m_image_data.resize(m_image_data.size());
230
+    output.m_width = m_width;
231
+    output.m_height = m_height;
232
+    output.m_components = m_components;
233
+
234
+    double log_exposure = log(exposure_time);
235
+
236
+    unsigned int size = m_image_data.size();
237
+    for (unsigned int i = 0; i < size; i++) {
238
+        output.m_image_data[i] = rf.lookup((m_image_data[i] + log_exposure) * compression);
239
+    }
240
+
241
+    return output;
242
+}
243
+
244
+
11 245
 HdrImageStack::HdrImageStack() {
12 246
 }
13 247
 
@@ -30,11 +264,20 @@ bool HdrImageStack::read_from_files(char * const* file_paths, int number_of_path
30 264
                      << endl;
31 265
                 return false;
32 266
             }
267
+            if (m_images[i].get_exposure_time() == 0.0) {
268
+                cerr << "HdrImageStack::read_from_files: Could not get exposure time (\""
269
+                     << file_paths[i]
270
+                     << "\")"
271
+                     << endl;
272
+                return false;
273
+            }
33 274
         } else {
34 275
             return false;
35 276
         }
36 277
     }
37 278
 
279
+    sort_by_exposure();
280
+
38 281
     return true;
39 282
 }
40 283
 
@@ -91,7 +334,7 @@ bool HdrImageStack::get_average_image(Image &output) {
91 334
         }
92 335
     }
93 336
 
94
-    // TODO test this with an image that is prime numbered dimensions
337
+    // TODO test this with an image that has prime numbered dimensions
95 338
     unsigned int rest = bytes_size % buf_size;
96 339
     for (unsigned int i = bytes_size - rest; i < bytes_size; i++) {
97 340
         int v = 0;
@@ -104,34 +347,86 @@ bool HdrImageStack::get_average_image(Image &output) {
104 347
     return true;
105 348
 }
106 349
 
107
-bool HdrImageStack::samples_to_csv(const char *file_path) {
108
-    if (m_images.size() == 0) {
109
-        return false;
110
-    }
111
-
350
+bool HdrImageStack::get_inverse_response_function(InverseResponseFunction &output, unsigned int num_samples) {
112 351
     srand(time(0));
113
-    ofstream output(file_path);
114
-
115
-    if (output.is_open()) {
116
-        unsigned int images_size = m_images.size();
117
-        unsigned int c = m_images[0].m_components;
118
-        unsigned int width = m_images[0].m_width;
119
-        unsigned int height = m_images[0].m_height;
120
-
121
-        for (unsigned int i = 0; i < 100; i++) {
122
-            unsigned int x = rand() % width;
123
-            unsigned int y = rand() % height;
124
-            unsigned int index = (y * width + x) * c;
125
-            for (unsigned int k = 0; k < images_size; k++) {
126
-                int val = m_images[k].m_image_data[index];
127
-                output << val;
128
-                if (k + 1 < images_size) {
129
-                    output << ',';
130
-                } else {
131
-                    output << '\n';
352
+
353
+    unsigned int images_size = m_images.size();
354
+    unsigned int c = m_images[0].m_components;
355
+    unsigned int width = m_images[0].m_width;
356
+    unsigned int height = m_images[0].m_height;
357
+
358
+    vector<vector<double> > resp;
359
+    resp.resize(256);
360
+
361
+    for (unsigned int i = 0; i < num_samples; i++) {
362
+        vector<int> values;
363
+        vector<double> exposure_times;
364
+        unsigned int x = rand() % width;
365
+        unsigned int y = rand() % height;
366
+        unsigned int index = (y * width + x) * c;
367
+        for (unsigned int k = 0; k < images_size; k++) {
368
+            int val = m_images[k].m_image_data[index];
369
+            if (val == 255) {
370
+                break;
371
+            }
372
+
373
+            values.push_back(val);
374
+            exposure_times.push_back(log(m_images[k].get_exposure_time()));
375
+        }
376
+
377
+        if (values.size() > 0 && values[0] < 128 && values[values.size() - 1] > 128) {
378
+            double offset = 0.0;
379
+            for (unsigned int k = 0; k < values.size(); k++) {
380
+                if (values[k] == 128) {
381
+                    offset = -exposure_times[k];
382
+                    break;
383
+                } else if (values[k] > 128) {
384
+                    double delta_val = values[k] - values[k -1];
385
+                    double t = (double)(128 - values[k - 1]) / delta_val;
386
+                    offset = -((1.0 - t) * exposure_times[k - 1] + t * exposure_times[k]);
387
+                    break;
132 388
                 }
133 389
             }
390
+
391
+            for (unsigned int k = 0; k < values.size(); k++) {
392
+                resp[values[k]].push_back(exposure_times[k] + offset);
393
+            }
394
+        }
395
+    }
396
+
397
+    output.m_lookup_table.resize(256);
398
+
399
+    for (unsigned int i = 0; i < 256; i++) {
400
+        double avg = 0.0;
401
+        for (unsigned int k = 0; k < resp[i].size(); k++) {
402
+            avg += resp[i][k];
403
+        }
404
+        if (resp[i].size() > 0) {
405
+            output.m_lookup_table[i] = avg / (double)resp[i].size();
406
+        } else {
407
+            output.m_lookup_table[i] = 0.0;
408
+        }
409
+    }
410
+
411
+    for (unsigned int i = 0; i < 256; i++) {
412
+        double avg = 0.0;
413
+        for (unsigned int k = 0; k < resp[i].size(); k++) {
414
+            avg += resp[i][k];
415
+        }
416
+        if (resp[i].size() > 0) {
417
+            output.m_lookup_table[i] = avg / (double)resp[i].size();
418
+        } else {
419
+            output.m_lookup_table[i] = 0.0;
134 420
         }
421
+    }
422
+
423
+    if (output.repair()) {
424
+        cout << "Inverse response function" << endl;
425
+        cout << endl;
426
+        for (unsigned int i = 0; i < 256; i++) {
427
+            cout << i << "," << output.m_lookup_table[i] << endl;
428
+        }
429
+        cout << endl;
135 430
 
136 431
         return true;
137 432
     } else {
@@ -139,8 +434,50 @@ bool HdrImageStack::samples_to_csv(const char *file_path) {
139 434
     }
140 435
 }
141 436
 
142
-bool HdrImageStack::recover_response_function(ResponseFunction &respFunc) {
143
-    //TODO implement
437
+HdrImage HdrImageStack::get_hdr_image(const InverseResponseFunction &invRespFunc) {
438
+    if (m_images.size() == 0) {
439
+        return HdrImage();
440
+    }
441
+
442
+    HdrImage output;
144 443
 
145
-    return false;
444
+    output.m_image_data.resize(m_images[0].m_image_data.size());
445
+    output.m_width = m_images[0].m_width;
446
+    output.m_height = m_images[0].m_height;
447
+    output.m_components = m_images[0].m_components;
448
+
449
+    unsigned int bytes_size = m_images[0].m_image_data.size();
450
+    unsigned int images_size = m_images.size();
451
+
452
+    // precompute log of exposure times
453
+    vector<double> log_exposures;
454
+    log_exposures.resize(images_size);
455
+    for (unsigned int k = 0; k < images_size; k++) {
456
+        log_exposures[k] = log(m_images[k].get_exposure_time());
457
+    }
458
+
459
+    for (unsigned int i = 0; i < bytes_size; i++) {
460
+
461
+        double scale_sum = 0.0;
462
+        double output_val = 0.0;
463
+
464
+        for (unsigned int k = 0; k < images_size; k++) {
465
+            int val = m_images[k].m_image_data[i];
466
+            double scale = 1.0 - fabs((double)(128.0 - val) * (1.0 / 128.0));
467
+            output_val += scale * (invRespFunc.lookup(val) - log_exposures[k]);
468
+            scale_sum += scale;
469
+        }
470
+
471
+        output.m_image_data[i] = output_val / scale_sum;
472
+    }
473
+
474
+    return output;
475
+}
476
+
477
+static bool compare_image_by_exposure(const Image &a, const Image &b) {
478
+    return a.get_exposure_time() < b.get_exposure_time();
479
+}
480
+
481
+void HdrImageStack::sort_by_exposure() {
482
+    std::sort(m_images.begin(), m_images.end(), compare_image_by_exposure);
146 483
 }

+ 97
- 4
haader.h Ver fichero

@@ -7,28 +7,121 @@
7 7
 
8 8
 namespace haader {
9 9
     class ResponseFunction {
10
+        friend class InverseResponseFunction;
11
+        public:
12
+            // Returns a pixel value (0-255) given the logarithm of a luminance value.
13
+            int lookup(double value) const;
14
+
15
+        private:
16
+            // minimum log of luminance value
17
+            double m_min_value;
18
+
19
+            // maximum log of luminance value
20
+            double m_max_value;
21
+
22
+            // Precomputed value:
23
+            // 1.0 / (m_max_value - m_min_value)
24
+            double m_recip_range;
25
+
26
+            // Response function is implemented as a lookup table of with 256 values
27
+            std::vector<int> m_lookup_table;
28
+    };
29
+
30
+    class InverseResponseFunction {
31
+        friend class HdrImageStack;
32
+        public:
33
+
34
+            // Return logarithm of luminance given a pixel value (0-255)
35
+            double lookup(int index) const;
36
+
37
+            // The inverse of the lookup function:
38
+            //   y == lookup(x)  ->  x == inverse_lookup(y)
39
+            // O(n) complexity, proportional to size of lookup table.
40
+            int inverse_lookup(double value) const;
41
+
42
+            // Construct a ResponseFunction object that allows efficient O(1) inverse_lookup.
43
+            // bins: Size of the lookup table for the response function
44
+            ResponseFunction to_response_function(int bins=1024);
45
+
10 46
         private:
47
+
48
+            // Returns true if a monotonically increasing lookup table without missing values
49
+            // can be constructed.
50
+            bool repair();
51
+
52
+            // Remove missing values by linear interpolation of neighboring values
53
+            void fill_zeros();
54
+
55
+            // Apply binomial low pass filter
56
+            void low_pass_filter();
57
+
58
+            // Returns true if values are monotonically increasing
59
+            bool is_monotonic();
60
+
61
+            // Inverse response function is implemented as a lookup table with 256 values
11 62
             std::vector<double> m_lookup_table;
12 63
     };
13 64
 
14
-    class HdrImageStack {
15
-        friend class ResponseFunction;
65
+    // High Dynamic Range Image.
66
+    // Stores the logarithm of the scene luminance for each pixel/channel in a high resolution.
67
+    class HdrImage {
68
+        friend class HdrImageStack;
69
+
70
+        public:
71
+            HdrImage();
72
+
73
+            // Construct low dynamic range image with the logarithm of the scene luminance.
74
+            Image get_log_image();
75
+
76
+            // Construct low dynamic range image by a simulated exposure with a given response function.
77
+            // exposure_time: Exposure time in seconds
78
+            // compression:
79
+            //   A value > 1.0 will compress the luminace values and give more contrast.
80
+            //   A value < 1.0 will reduce the contrast but lower the risk of
81
+            //     under-/overexposure in parts of the image.
82
+            Image expose(double exposure_time, const ResponseFunction &rf, double compression=1.0);
83
+
84
+        private:
85
+            // Pixel values (logarithm of scene luminance). The channels are interleaved (RGBRGBRGB...).
86
+            std::vector<double> m_image_data;
87
+
88
+            // Width in pixels
89
+            unsigned int m_width;
90
+
91
+            // Height in pixels
92
+            unsigned int m_height;
93
+
94
+            // Number of channels (usually 3 for RGB images)
95
+            unsigned int m_components;
96
+    };
16 97
 
98
+    // A stack of images/photographs of the same scene, with the same resolution but different exposure times.
99
+    class HdrImageStack {
17 100
         public:
18 101
             HdrImageStack();
19 102
 
103
+            // Read images from the given array of file paths
20 104
             bool read_from_files(char * const* file_paths, int number_of_paths);
21 105
 
22 106
             bool get_average_image(Image &output);
23 107
             bool get_average_image_slow(Image &output);
24 108
 
25
-            bool samples_to_csv(const char *file_path);
109
+            // Approximate the inverse response function from samples of random pixel locations.
110
+            // num_samples: Number of samples
111
+            // Returns true if a well-formed inverse response function could be found.
112
+            bool get_inverse_response_function(InverseResponseFunction &output, unsigned int num_samples);
26 113
 
27
-            bool recover_response_function(ResponseFunction &respFunc);
114
+            // Return a HDR-image given an inverse response function
115
+            HdrImage get_hdr_image(const InverseResponseFunction &invRespFunc);
28 116
 
29 117
         private:
118
+            // Sort the images by exposure time
119
+            void sort_by_exposure();
120
+
121
+            // The images
30 122
             std::vector<Image> m_images;
31 123
     };
124
+
32 125
 }
33 126
 
34 127
 #endif // HAADER_ILUCF4Z0

+ 26
- 7
image.cpp Ver fichero

@@ -14,6 +14,7 @@
14 14
 #include "stb_image.h"
15 15
 
16 16
 #include "image.h"
17
+#include "exif.h"
17 18
 
18 19
 using namespace std;
19 20
 using namespace haader;
@@ -22,11 +23,12 @@ using namespace haader;
22 23
 Image::Image():
23 24
     m_width(0),
24 25
     m_height(0),
25
-    m_components(0)
26
+    m_components(0),
27
+    m_exposure_time(0.0)
26 28
 {
27 29
 }
28 30
 
29
-void Image::to_string(string &s) {
31
+void Image::to_string(string &s) const {
30 32
     stringstream ss;
31 33
     ss << "Image(";
32 34
     ss << m_width << 'x' << m_height << 'x' << m_components << ")";
@@ -47,6 +49,19 @@ bool Image::read_from_file(const char *file_path) {
47 49
         size_t size = (size_t)x * (size_t)y * (size_t)components;
48 50
         m_image_data.assign(data, data + size);
49 51
 
52
+        // try to read EXIF data
53
+        {
54
+            ExifTags tags;
55
+            ifstream stream;
56
+            stream.open(file_path);
57
+
58
+            if (stream.is_open() and ExifTags::read_from_jpeg(stream, tags)) {
59
+                if (tags.get_exposure_time(m_exposure_time)) {
60
+                    cout << "Exposure time: " << m_exposure_time << " sec" << endl;
61
+                }
62
+            }
63
+        }
64
+
50 65
         return true;
51 66
     } else {
52 67
         cerr << "Can not read \"" << file_path << "\" as an image." << endl;
@@ -218,7 +233,7 @@ bool Image::read_from_jpeg_file(const char *file_path) {
218 233
     }
219 234
 }
220 235
 
221
-bool Image::save_as_ppm_file(const char *file_path, bool ascii) {
236
+bool Image::save_as_ppm_file(const char *file_path, bool ascii) const {
222 237
     if (ascii) {
223 238
         // ASCII
224 239
 
@@ -256,20 +271,24 @@ bool Image::save_as_ppm_file(const char *file_path, bool ascii) {
256 271
     }
257 272
 }
258 273
 
259
-unsigned int Image::get_width() {
274
+unsigned int Image::get_width() const {
260 275
     return m_width;
261 276
 }
262 277
 
263
-unsigned int Image::get_height() {
278
+unsigned int Image::get_height() const {
264 279
     return m_height;
265 280
 }
266 281
 
267
-unsigned int Image::get_components() {
282
+unsigned int Image::get_components() const {
268 283
     return m_components;
269 284
 }
270 285
 
271
-bool Image::has_equal_dimensions(const Image &other) {
286
+bool Image::has_equal_dimensions(const Image &other) const {
272 287
     return (m_width == other.m_width &&
273 288
             m_height == other.m_height &&
274 289
             m_components == other.m_components);
275 290
 }
291
+
292
+double Image::get_exposure_time() const {
293
+    return m_exposure_time;
294
+}

+ 14
- 6
image.h Ver fichero

@@ -6,28 +6,36 @@
6 6
 
7 7
 namespace haader {
8 8
 
9
+    // A low dynamic range image/photograph with an associated exposure time.
9 10
     class Image {
10 11
         friend class HdrImageStack;
12
+        friend class HdrImage;
11 13
 
12 14
         public:
13 15
             Image();
14 16
 
15
-            void to_string(std::string &s);
17
+            void to_string(std::string &s) const;
16 18
 
17 19
             bool read_from_file(const char *file_path);
18 20
             bool read_from_jpeg_file(const char *file_path);
19
-            bool save_as_ppm_file(const char *file_path, bool ascii=false);
21
+            bool save_as_ppm_file(const char *file_path, bool ascii=false) const;
20 22
 
21
-            unsigned int get_width();
22
-            unsigned int get_height();
23
-            unsigned int get_components();
24
-            bool has_equal_dimensions(const Image &other);
23
+            unsigned int get_width() const;
24
+            unsigned int get_height() const;
25
+            unsigned int get_components() const;
26
+            bool has_equal_dimensions(const Image &other) const;
27
+            double get_exposure_time() const;
25 28
 
26 29
         private:
27 30
             std::vector<unsigned char> m_image_data;
28 31
             unsigned int m_width;
29 32
             unsigned int m_height;
33
+
34
+            // Number of channels (usually 3 for RGB images)
30 35
             unsigned int m_components;
36
+
37
+            // Exposure time in seconds
38
+            double m_exposure_time;
31 39
     };
32 40
 
33 41
 }

+ 61
- 9
main.cpp Ver fichero

@@ -1,32 +1,84 @@
1 1
 #include "haader.h"
2 2
 
3 3
 #include <time.h>
4
+#include <math.h>
4 5
 
5 6
 #include <iostream>
7
+#include <sstream>
8
+#include <iomanip>
6 9
 
7 10
 using namespace std;
8 11
 
9 12
 
13
+void print_usage(const char *argv_0) {
14
+    cout << "Construct an HDR-image from a series of photographs\n";
15
+    cout << "with different exposure times and then synthesize\n";
16
+    cout << "new images from it.\n\n";
17
+    cout << "Usage:\n";
18
+    cout << argv_0 << " <image_path_1> <image_path_2> ...\n\n";
19
+}
20
+
10 21
 int main(int argc, char *argv[])
11 22
 {
12 23
     if (argc >= 2) {
13 24
         haader::HdrImageStack stack;
14 25
 
15
-        if (stack.read_from_files(argv + 1, argc - 1)) {
16
-            haader::Image img;
26
+        // read image files.
27
+        bool ok = stack.read_from_files(argv + 1, argc - 1);
28
+
29
+        if (ok) {
30
+            clock_t start = clock();
17 31
 
32
+            // construct and save average image
18 33
             {
19
-                clock_t start = clock();
34
+                haader::Image img;
20 35
                 stack.get_average_image(img);
21
-                clock_t end = clock();
22
-                double seconds = (end - start) / (double)CLOCKS_PER_SEC;
23
-                cout << "secs: " << seconds << endl;
36
+                img.save_as_ppm_file("average.ppm");
24 37
             }
25 38
 
26
-            img.save_as_ppm_file("average.ppm");
27
-            stack.samples_to_csv("samples.csv");
28
-        }
39
+            // approximate inverse response function
40
+            haader::InverseResponseFunction irf;
41
+            bool irf_ok = stack.get_inverse_response_function(irf, 2000);
42
+
43
+            if (irf_ok) {
44
+                haader::HdrImage hdr_img;
45
+                hdr_img = stack.get_hdr_image(irf);
46
+
47
+                // save logarithmic image of scene luminance values
48
+                {
49
+                    haader::Image x = hdr_img.get_log_image();
50
+                    x.save_as_ppm_file("log_image.ppm");
51
+                }
29 52
 
53
+                // convert irf to response function
54
+                haader::ResponseFunction rf;
55
+                rf = irf.to_response_function(1024);
56
+
57
+                // create images by a simulated exposure of the HDR image with the response function
58
+                cout << "Expose..." << endl;
59
+                for (int i = 0; i < 20; i++) {
60
+                    // exposure time
61
+                    double t = pow(2.0, (double)i * (-12.0 / 20.0));
62
+
63
+                    // create image
64
+                    haader::Image x = hdr_img.expose(t, rf, 1.0);
65
+
66
+                    // save image
67
+                    stringstream ss;
68
+                    ss << "expose_" << setw(5) << setfill('0') << i << ".ppm";
69
+                    string path = ss.str();
70
+                    x.save_as_ppm_file(path.c_str());
71
+
72
+                    cout << "\nexposure time: " << t << " sec" << endl;
73
+                    cout << "save as " << path << endl;
74
+                }
75
+            }
76
+            clock_t end = clock();
77
+            double seconds = (end - start) / (double)CLOCKS_PER_SEC;
78
+            cout << "\nsecs: " << seconds << endl;
79
+        }
80
+    } else {
81
+        print_usage(argv[0]);
30 82
     }
31 83
 
32 84
     return 0;