Browse Source

Commit old changes from 2017-07-13

* Add GUI
* Write response functions to CSV
...
Johannes Hofmann 7 years ago
parent
commit
5ff201373f
7 changed files with 282 additions and 1 deletions
  1. 6
    0
      Makefile
  2. 55
    0
      haader.cpp
  3. 10
    0
      haader.h
  4. 176
    0
      haader_gui.cpp
  5. 8
    0
      image.cpp
  6. 2
    0
      image.h
  7. 25
    1
      main.cpp

+ 6
- 0
Makefile View File

@@ -2,9 +2,15 @@ CXXFLAGS= -std=c++98 -pedantic -Wall -O3
2 2
 LIBS=
3 3
 OBJS= haader.o image.o exif.o csv_reader.o
4 4
 
5
+GUI_CFLAGS= $$(pkg-config gtk+-3.0 --cflags)
6
+GUI_LIBS= $$(pkg-config gtk+-3.0 --libs)
7
+
5 8
 haader: main.cpp $(OBJS)
6 9
 	$(CXX) main.cpp -o haader $(OBJS) $(CXXFLAGS) $(LIBS)
7 10
 
11
+haader_gui: haader_gui.cpp $(OBJS)
12
+	$(CXX) haader_gui.cpp -o haader_gui $(OBJS) $(CXXFLAGS) $(GUI_CFLAGS) $(LIBS) $(GUI_LIBS)
13
+
8 14
 run: haader
9 15
 	./haader
10 16
 

+ 55
- 0
haader.cpp View File

@@ -1,6 +1,7 @@
1 1
 #include <iostream>
2 2
 #include <fstream>
3 3
 #include <algorithm>
4
+#include <clocale>
4 5
 #include <stdlib.h>
5 6
 #include <time.h>
6 7
 #include <math.h>
@@ -24,6 +25,19 @@ int ResponseFunction::lookup(double value) const {
24 25
     }
25 26
 }
26 27
 
28
+void ResponseFunction::to_csv_stream(ostream &stream) const {
29
+    // Set locale. Numbers should have a decimal point (not a comma)
30
+    std::setlocale(LC_NUMERIC, "C");
31
+
32
+    unsigned int size = m_lookup_table.size();
33
+    double recip_size = 1.0 / (double)(size - 1);
34
+    for (unsigned int i = 0; i < size; i++) {
35
+        double x = m_min_value + i * recip_size * (m_max_value - m_min_value);
36
+        int y = m_lookup_table[i];
37
+        stream << x << ',' << y << endl;
38
+    }
39
+}
40
+
27 41
 double InverseResponseFunction::lookup(int index) const {
28 42
     return m_lookup_table[index];
29 43
 }
@@ -77,6 +91,13 @@ ResponseFunction InverseResponseFunction::to_response_function(int bins) {
77 91
     return rf;
78 92
 }
79 93
 
94
+void InverseResponseFunction::to_csv_stream(ostream &stream) const {
95
+    unsigned int size = m_lookup_table.size();
96
+    for (unsigned int i = 0; i < size; i++) {
97
+        stream << i << ',' << m_lookup_table[i] << endl;
98
+    }
99
+}
100
+
80 101
 bool InverseResponseFunction::repair() {
81 102
 
82 103
     // Fill missing values
@@ -281,6 +302,35 @@ bool HdrImageStack::read_from_files(char * const* file_paths, int number_of_path
281 302
     return true;
282 303
 }
283 304
 
305
+bool HdrImageStack::add_from_file_path(const char* file_path) {
306
+    unsigned int i = m_images.size();
307
+    m_images.push_back(Image());
308
+
309
+    if (m_images[i].read_from_file(file_path)) {
310
+        cout << "Read \"" << file_path << "\"" << endl;
311
+        if (!m_images[0].has_equal_dimensions(m_images[i])) {
312
+            cerr << "HdrImageStack::add_from_file_path: Dimensions do not match (\""
313
+                 << file_path
314
+                 << "\")"
315
+                 << endl;
316
+            return false;
317
+        }
318
+        if (m_images[i].get_exposure_time() == 0.0) {
319
+            cerr << "HdrImageStack::read_from_files: Could not get exposure time (\""
320
+                 << file_path
321
+                 << "\")"
322
+                 << endl;
323
+            return false;
324
+        }
325
+    } else {
326
+        return false;
327
+    }
328
+
329
+    sort_by_exposure();
330
+
331
+    return true;
332
+}
333
+
284 334
 bool HdrImageStack::get_average_image_slow(Image &output) {
285 335
     if (m_images.size() == 0) {
286 336
         return false;
@@ -348,6 +398,11 @@ bool HdrImageStack::get_average_image(Image &output) {
348 398
 }
349 399
 
350 400
 bool HdrImageStack::get_inverse_response_function(InverseResponseFunction &output, unsigned int num_samples) {
401
+    if (m_images.size() == 0) {
402
+        cerr << "HdrImageStack::get_inverse_response_function: There are no images." << endl;
403
+        return false;
404
+    }
405
+
351 406
     srand(time(0));
352 407
 
353 408
     unsigned int images_size = m_images.size();

+ 10
- 0
haader.h View File

@@ -2,6 +2,7 @@
2 2
 #define HAADER_ILUCF4Z0
3 3
 
4 4
 #include <vector>
5
+#include <iostream>
5 6
 
6 7
 #include "image.h"
7 8
 
@@ -12,6 +13,9 @@ namespace haader {
12 13
             // Returns a pixel value (0-255) given the logarithm of a luminance value.
13 14
             int lookup(double value) const;
14 15
 
16
+            // Write the lookup table in CSV-format to the given stream
17
+            void to_csv_stream(std::ostream &stream) const;
18
+
15 19
         private:
16 20
             // minimum log of luminance value
17 21
             double m_min_value;
@@ -43,6 +47,9 @@ namespace haader {
43 47
             // bins: Size of the lookup table for the response function
44 48
             ResponseFunction to_response_function(int bins=1024);
45 49
 
50
+            // Write the lookup table in CSV-format to the given stream
51
+            void to_csv_stream(std::ostream &stream) const;
52
+
46 53
         private:
47 54
 
48 55
             // Returns true if a monotonically increasing lookup table without missing values
@@ -103,6 +110,9 @@ namespace haader {
103 110
             // Read images from the given array of file paths
104 111
             bool read_from_files(char * const* file_paths, int number_of_paths);
105 112
 
113
+            // Add an image from the given file path
114
+            bool add_from_file_path(const char* file_path);
115
+
106 116
             bool get_average_image(Image &output);
107 117
             bool get_average_image_slow(Image &output);
108 118
 

+ 176
- 0
haader_gui.cpp View File

@@ -0,0 +1,176 @@
1
+#include <math.h>
2
+#include <iostream>
3
+using namespace std;
4
+
5
+#include <gtk/gtk.h>
6
+#include <glib.h>
7
+
8
+#include "haader.h"
9
+#include "image.h"
10
+
11
+struct Gui {
12
+    GtkWidget *window;
13
+    GtkWidget *canvas;
14
+    haader::HdrImageStack stack;
15
+    haader::HdrImage hdr_image;
16
+    haader::ResponseFunction response_function;
17
+    haader::Image ldr_image;
18
+    cairo_surface_t *surface;
19
+    double exposure_time;
20
+    double compression;
21
+};
22
+
23
+static cairo_surface_t* image_to_cairo_surface(const haader::Image &image) {
24
+    unsigned int width = image.get_width();
25
+    unsigned int height = image.get_height();
26
+    const unsigned char *image_data = image.get_const_image_data();
27
+
28
+    cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
29
+    int stride = cairo_image_surface_get_stride(surf);
30
+    unsigned char *surf_data = cairo_image_surface_get_data(surf);
31
+
32
+    unsigned int i = 0;
33
+    unsigned int k = 0;
34
+    for (unsigned int y = 0; y < height; y++) {
35
+        for (unsigned int x = 0; x < width; x++) {
36
+            surf_data[i + x * 4] = image_data[k + x * 3 + 2];
37
+            surf_data[i + x * 4 + 1] = image_data[k + x * 3 + 1];
38
+            surf_data[i + x * 4 + 2] = image_data[k + x * 3];
39
+        }
40
+        i += stride;
41
+        k += width * 3;
42
+    }
43
+
44
+    return surf;
45
+}
46
+
47
+static void expose(Gui &gui) {
48
+    gui.ldr_image = gui.hdr_image.expose(gui.exposure_time, gui.response_function, gui.compression);
49
+    if (gui.surface) {
50
+        cairo_surface_destroy(gui.surface);
51
+        gui.surface = NULL;
52
+    }
53
+    gui.surface = image_to_cairo_surface(gui.ldr_image);
54
+}
55
+
56
+static gboolean canvas_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
57
+{
58
+    cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
59
+    cairo_paint(cr);
60
+
61
+    Gui *gui = (Gui*) user_data;
62
+    if (gui->surface) {
63
+        cairo_set_source_surface(cr, gui->surface, 0, 0);
64
+        cairo_paint(cr);
65
+    }
66
+
67
+    return FALSE;
68
+}
69
+
70
+void scale_exposure_changed(GtkRange *range, gpointer user_data) {
71
+    Gui *gui = (Gui*) user_data;
72
+
73
+    gui->exposure_time = pow(2.0, gtk_range_get_value(range));
74
+
75
+    expose(*gui);
76
+
77
+    gtk_widget_queue_draw(gui->canvas);
78
+}
79
+
80
+void scale_compression_changed(GtkRange *range, gpointer user_data) {
81
+    Gui *gui = (Gui*) user_data;
82
+
83
+    gui->compression = pow(2.0, gtk_range_get_value(range));
84
+
85
+    expose(*gui);
86
+
87
+    gtk_widget_queue_draw(gui->canvas);
88
+}
89
+
90
+void file_select_clicked(GtkButton *button, gpointer user_data) {
91
+    Gui *gui = (Gui*) user_data;
92
+
93
+    GtkWidget *dialog = gtk_file_chooser_dialog_new(
94
+            "Open File",
95
+            GTK_WINDOW(gui->window),
96
+            GTK_FILE_CHOOSER_ACTION_OPEN,
97
+            "Cancel",
98
+            GTK_RESPONSE_CANCEL,
99
+            "Open",
100
+            GTK_RESPONSE_ACCEPT,
101
+            NULL);
102
+
103
+    gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(dialog), true);
104
+
105
+    int res = gtk_dialog_run(GTK_DIALOG(dialog));
106
+    if (res == GTK_RESPONSE_ACCEPT) {
107
+        GSList *list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
108
+        vector<string> new_list;
109
+
110
+        GSList *element = list;
111
+        while (element != NULL) {
112
+            //TODO handle errors
113
+            gui->stack.add_from_file_path((const char*)(element->data));
114
+            g_free(element->data);
115
+            element = g_slist_next(element);
116
+        }
117
+
118
+        g_slist_free(list);
119
+    }
120
+
121
+    // create HDR-image
122
+    haader::InverseResponseFunction irf;
123
+    if (gui->stack.get_inverse_response_function(irf, 4096)) {
124
+        gui->hdr_image = gui->stack.get_hdr_image(irf);
125
+        gui->ldr_image = gui->hdr_image.get_log_image();
126
+        if (gui->surface) {
127
+            cairo_surface_destroy(gui->surface);
128
+            gui->surface = NULL;
129
+        }
130
+        gui->surface = image_to_cairo_surface(gui->ldr_image);
131
+        gui->response_function = irf.to_response_function(1024);
132
+    }
133
+
134
+    gtk_widget_queue_draw(gui->canvas);
135
+
136
+    gtk_widget_destroy (dialog);
137
+}
138
+
139
+int main(int argc, char *argv[])
140
+{
141
+    gtk_init(&argc, &argv);
142
+
143
+    Gui gui;
144
+    gui.surface = NULL;
145
+    gui.exposure_time = 0.0;
146
+    gui.compression = 1.0;
147
+
148
+    gui.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
149
+    gtk_window_set_title(GTK_WINDOW(gui.window), "haader");
150
+    gtk_window_set_default_size(GTK_WINDOW(gui.window), 800, 600);
151
+
152
+    GtkWidget *file_button = gtk_button_new_with_label("Select files");
153
+    gui.canvas = gtk_drawing_area_new();
154
+    GtkWidget *scale_exposure = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, -20.0, 4.0, 0.001);
155
+    GtkWidget *scale_compression = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, -4, 4.0, 0.001);
156
+
157
+    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
158
+
159
+    gtk_container_add(GTK_CONTAINER(gui.window), vbox);
160
+    gtk_box_pack_start(GTK_BOX(vbox), file_button, false, false, 0);
161
+    gtk_box_pack_start(GTK_BOX(vbox), gui.canvas, true, true, 0);
162
+    gtk_box_pack_start(GTK_BOX(vbox), scale_exposure, false, false, 0);
163
+    gtk_box_pack_start(GTK_BOX(vbox), scale_compression, false, false, 0);
164
+
165
+    g_signal_connect(gui.window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
166
+    g_signal_connect(gui.canvas, "draw", G_CALLBACK(canvas_draw), &gui);
167
+    g_signal_connect(file_button, "clicked", G_CALLBACK(file_select_clicked), &gui);
168
+    g_signal_connect(scale_exposure, "value-changed", G_CALLBACK(scale_exposure_changed), &gui);
169
+    g_signal_connect(scale_compression, "value-changed", G_CALLBACK(scale_compression_changed), &gui);
170
+
171
+    gtk_widget_show_all(gui.window);
172
+
173
+    gtk_main ();
174
+
175
+    return 0;
176
+}

+ 8
- 0
image.cpp View File

@@ -125,3 +125,11 @@ bool Image::has_equal_dimensions(const Image &other) const {
125 125
 double Image::get_exposure_time() const {
126 126
     return m_exposure_time;
127 127
 }
128
+
129
+const unsigned char* Image::get_const_image_data() const {
130
+    return m_image_data.data();
131
+}
132
+
133
+unsigned char* Image::get_image_data() {
134
+    return m_image_data.data();
135
+}

+ 2
- 0
image.h View File

@@ -24,6 +24,8 @@ namespace haader {
24 24
             unsigned int get_components() const;
25 25
             bool has_equal_dimensions(const Image &other) const;
26 26
             double get_exposure_time() const;
27
+            const unsigned char* get_const_image_data() const;
28
+            unsigned char* get_image_data();
27 29
 
28 30
         private:
29 31
             std::vector<unsigned char> m_image_data;

+ 25
- 1
main.cpp View File

@@ -5,6 +5,7 @@
5 5
 
6 6
 #include <iostream>
7 7
 #include <sstream>
8
+#include <fstream>
8 9
 #include <iomanip>
9 10
 
10 11
 using namespace std;
@@ -38,7 +39,13 @@ int main(int argc, char *argv[])
38 39
 
39 40
             // approximate inverse response function
40 41
             haader::InverseResponseFunction irf;
41
-            bool irf_ok = stack.get_inverse_response_function(irf, 2000);
42
+            bool irf_ok = stack.get_inverse_response_function(irf, 1000);
43
+
44
+            {
45
+                fstream s;
46
+                s.open("irf.csv", std::ios::out);
47
+                irf.to_csv_stream(s);
48
+            }
42 49
 
43 50
             if (irf_ok) {
44 51
                 haader::HdrImage hdr_img;
@@ -54,6 +61,22 @@ int main(int argc, char *argv[])
54 61
                 haader::ResponseFunction rf;
55 62
                 rf = irf.to_response_function(1024);
56 63
 
64
+                {
65
+                    fstream s;
66
+                    s.open("rf.csv", std::ios::out);
67
+                    rf.to_csv_stream(s);
68
+                }
69
+
70
+                {
71
+                    haader::Image x = hdr_img.expose(0.001, rf, 1.0);
72
+                    x.save_as_ppm_file("test_1000.ppm");
73
+                }
74
+
75
+                {
76
+                    haader::Image x = hdr_img.expose(1.0 / 1024.0, rf, 1.0);
77
+                    x.save_as_ppm_file("test_1024.ppm");
78
+                }
79
+
57 80
                 // create images by a simulated exposure of the HDR image with the response function
58 81
                 cout << "Expose..." << endl;
59 82
                 for (int i = 0; i < 20; i++) {
@@ -73,6 +96,7 @@ int main(int argc, char *argv[])
73 96
                     cout << "save as " << path << endl;
74 97
                 }
75 98
             }
99
+
76 100
             clock_t end = clock();
77 101
             double seconds = (end - start) / (double)CLOCKS_PER_SEC;
78 102
             cout << "\nsecs: " << seconds << endl;