diff options
| author | Matthias Vogelgesang <matthias.vogelgesang@gmail.com> | 2012-10-12 14:47:07 +0200 | 
|---|---|---|
| committer | Matthias Vogelgesang <matthias.vogelgesang@gmail.com> | 2012-10-12 14:47:07 +0200 | 
| commit | ef7ff789e9c15f4f4b8c65585de7e7fa4f9c9438 (patch) | |
| tree | 2f6a105b6a4c1325b2aba7c1fa3d9a2b26856a7c /tools/gui | |
| parent | 6b82a9a175c92c39f25e9b5965b2e9f5c35f65c2 (diff) | |
Add ring buffer recording for assessment
Diffstat (limited to 'tools/gui')
| -rw-r--r-- | tools/gui/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tools/gui/control.c | 190 | ||||
| -rw-r--r-- | tools/gui/control.glade | 33 | ||||
| -rw-r--r-- | tools/gui/ring-buffer.c | 64 | ||||
| -rw-r--r-- | tools/gui/ring-buffer.h | 28 | 
5 files changed, 243 insertions, 73 deletions
| diff --git a/tools/gui/CMakeLists.txt b/tools/gui/CMakeLists.txt index ff5e9f6..1000ac6 100644 --- a/tools/gui/CMakeLists.txt +++ b/tools/gui/CMakeLists.txt @@ -25,6 +25,7 @@ if (GTK2_FOUND)      add_executable(control          control.c +        ring-buffer.c          egg-property-cell-renderer.c          egg-property-tree-view.c          egg-histogram-view.c) diff --git a/tools/gui/control.c b/tools/gui/control.c index 214e4da..84799cb 100644 --- a/tools/gui/control.c +++ b/tools/gui/control.c @@ -22,11 +22,17 @@  #include <math.h>  #include "config.h" +#include "ring-buffer.h"  #include "uca-camera.h"  #include "uca-plugin-manager.h"  #include "egg-property-tree-view.h"  #include "egg-histogram-view.h" +typedef enum { +    IDLE, +    RUNNING, +    RECORDING +} State;  typedef struct {      UcaCamera   *camera; @@ -35,13 +41,14 @@ typedef struct {      GtkWidget   *start_button;      GtkWidget   *stop_button;      GtkWidget   *record_button; -    GtkWidget   *histogram_view; + +    GtkWidget       *histogram_view;      GtkToggleButton *histogram_button; +    GtkAdjustment   *frame_slider; -    guchar      *buffer; +    RingBuffer  *buffer;      guchar      *pixels; -    gboolean     running; -    gboolean     store; +    State        state;      int          timestamp;      int          width; @@ -53,70 +60,66 @@ static UcaPluginManager *plugin_manager;  static void -convert_8bit_to_rgb (guchar *output, guchar *input, gint width, gint height, gdouble min, gdouble max) +convert_grayscale_to_rgb (ThreadData *data, gpointer buffer)  { -    gdouble factor = 255.0 / (max - min); - -    for (int i = 0, j = 0; i < width*height; i++) { -        guchar val = (guchar) ((input[i] - min) * factor); -        output[j++] = val; -        output[j++] = val; -        output[j++] = val; +    gdouble min; +    gdouble max; +    gdouble factor; +    guint8 *output; + +    egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min, &max); +    factor = 255.0 / (max - min); +    output = data->pixels; + +    if (data->pixel_size == 1) { +        guint8 *input = (guint8 *) buffer; + +        for (int i = 0, j = 0; i < data->width * data->height; i++) { +            guchar val = (guchar) ((input[i] - min) * factor); +            output[j++] = val; +            output[j++] = val; +            output[j++] = val; +        } +    } +    else if (data->pixel_size == 2) { +        guint16 *input = (guint16 *) buffer; + +        for (int i = 0, j = 0; i < data->width * data->height; i++) { +            guchar val = (guint8) ((input[i] - min) * factor); +            output[j++] = val; +            output[j++] = val; +            output[j++] = val; +        }      }  }  static void -convert_16bit_to_rgb (guchar *output, guchar *input, gint width, gint height, gdouble min, gdouble max) +update_pixbuf (ThreadData *data)  { -    gdouble factor = 255.0 / (max - min); +    gdk_flush (); +    gtk_image_clear (GTK_IMAGE (data->image)); +    gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); +    gtk_widget_queue_draw_area (data->image, 0, 0, data->width, data->height); -    for (int i = 0, j = 0; i < width*height; i++) { -        guchar val = (guint8) ((input[i] - min) * factor); -        output[j++] = val; -        output[j++] = val; -        output[j++] = val; -    } +    if (gtk_toggle_button_get_active (data->histogram_button)) +        gtk_widget_queue_draw (data->histogram_view);  } -static void * -grab_thread (void *args) +static gpointer +preview_frames (void *args)  {      ThreadData *data = (ThreadData *) args; -    gchar filename[FILENAME_MAX] = {0,};      gint counter = 0; -    while (data->running) { -        gdouble min_value; -        gdouble max_value; +    while (data->state == RUNNING) { +        gpointer buffer; -        uca_camera_grab (data->camera, (gpointer) &data->buffer, NULL); - -        if (data->store) { -            snprintf (filename, FILENAME_MAX, "frame-%i-%08i.raw", data->timestamp, counter); -            FILE *fp = fopen (filename, "wb"); -            fwrite (data->buffer, data->width*data->height, data->pixel_size, fp); -            fclose (fp); -        } - -        /* FIXME: We should actually check if this is really a new frame and -         * just do nothing if it is an already displayed one. */ -        egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min_value, &max_value); - -        if (data->pixel_size == 1) -            convert_8bit_to_rgb (data->pixels, data->buffer, data->width, data->height, min_value, max_value); -        else if (data->pixel_size == 2) -            convert_16bit_to_rgb (data->pixels, data->buffer, data->width, data->height, min_value, max_value); +        buffer = ring_buffer_get_current_pointer (data->buffer); +        uca_camera_grab (data->camera, &buffer, NULL); +        convert_grayscale_to_rgb (data, buffer);          gdk_threads_enter (); - -        gdk_flush (); -        gtk_image_clear (GTK_IMAGE (data->image)); -        gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); -        gtk_widget_queue_draw_area (data->image, 0, 0, data->width, data->height); - -        if (gtk_toggle_button_get_active (data->histogram_button)) -            gtk_widget_queue_draw (data->histogram_view); - +        update_pixbuf (data);          gdk_threads_leave ();          counter++; @@ -124,6 +127,33 @@ grab_thread (void *args)      return NULL;  } +static gpointer +record_frames (gpointer args) +{ +    ThreadData *data; +    gpointer buffer; +    guint n_frames = 0; + +    data = (ThreadData *) args; +    ring_buffer_reset (data->buffer); + +    while (data->state == RECORDING) { +        buffer = ring_buffer_get_current_pointer (data->buffer); +        uca_camera_grab (data->camera, &buffer, NULL); +        ring_buffer_proceed (data->buffer); +        n_frames++; +    } + +    n_frames = ring_buffer_get_num_blocks (data->buffer); + +    gdk_threads_enter (); +    gtk_adjustment_set_upper (data->frame_slider, n_frames - 1); +    gtk_adjustment_set_value (data->frame_slider, n_frames - 1); +    gdk_threads_leave (); + +    return NULL; +} +  gboolean  on_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)  { @@ -134,17 +164,39 @@ void  on_destroy (GtkWidget *widget, gpointer data)  {      ThreadData *td = (ThreadData *) data; -    td->running = FALSE; + +    td->state = IDLE;      g_object_unref (td->camera); +    ring_buffer_free (td->buffer); +      gtk_main_quit ();  }  static void  set_tool_button_state (ThreadData *data)  { -    gtk_widget_set_sensitive (data->start_button, !data->running); -    gtk_widget_set_sensitive (data->stop_button, data->running); -    gtk_widget_set_sensitive (data->record_button, !data->running); +    gtk_widget_set_sensitive (data->start_button, +                              data->state == IDLE); +    gtk_widget_set_sensitive (data->stop_button, +                              data->state == RUNNING || data->state == RECORDING); +    gtk_widget_set_sensitive (data->record_button, +                              data->state == IDLE); +} + +static void +on_frame_slider_changed (GtkAdjustment *adjustment, gpointer user_data) +{ +    ThreadData *data = (ThreadData *) user_data; + +    if (data->state == IDLE) { +        gpointer buffer; +        gint index; +          +        index = (gint) gtk_adjustment_get_value (adjustment); +        buffer = ring_buffer_get_pointer (data->buffer, index); +        convert_grayscale_to_rgb (data, buffer); +        update_pixbuf (data); +    }  }  static void @@ -153,7 +205,7 @@ on_start_button_clicked (GtkWidget *widget, gpointer args)      ThreadData *data = (ThreadData *) args;      GError *error = NULL; -    data->running = TRUE; +    data->state = RUNNING;      set_tool_button_state (data);      uca_camera_start_recording (data->camera, &error); @@ -163,7 +215,7 @@ on_start_button_clicked (GtkWidget *widget, gpointer args)          return;      } -    if (!g_thread_create (grab_thread, data, FALSE, &error)) { +    if (!g_thread_create (preview_frames, data, FALSE, &error)) {          g_printerr ("Failed to create thread: %s\n", error->message);          return;      } @@ -175,8 +227,7 @@ on_stop_button_clicked (GtkWidget *widget, gpointer args)      ThreadData *data = (ThreadData *) args;      GError *error = NULL; -    data->running = FALSE; -    data->store = FALSE; +    data->state = IDLE;      set_tool_button_state (data);      uca_camera_stop_recording (data->camera, &error); @@ -192,13 +243,12 @@ on_record_button_clicked (GtkWidget *widget, gpointer args)      GError *error = NULL;      data->timestamp = (int) time (0); -    data->store = TRUE; -    data->running = TRUE; +    data->state = RECORDING;      set_tool_button_state (data);      uca_camera_start_recording (data->camera, &error); -    if (!g_thread_create (grab_thread, data, FALSE, &error)) +    if (!g_thread_create (record_frames, data, FALSE, &error))          g_printerr ("Failed to create thread: %s\n", error->message);  } @@ -240,18 +290,19 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name)      td.pixel_size = bits_per_sample > 8 ? 2 : 1;      td.image  = image;      td.pixbuf = pixbuf; -    td.buffer = (guchar *) g_malloc (td.pixel_size * td.width * td.height); +    td.buffer = ring_buffer_new (td.pixel_size * td.width * td.height, 256);      td.pixels = gdk_pixbuf_get_pixels (pixbuf); -    td.running = FALSE; -    td.store = FALSE; +    td.state  = IDLE;      td.camera = camera;      td.histogram_view = egg_histogram_view_new ();      td.histogram_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "histogram-checkbutton")); +    td.frame_slider = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "frames-adjustment"));      histogram_box = GTK_BOX (gtk_builder_get_object (builder, "histogram-box"));      gtk_box_pack_start (histogram_box, td.histogram_view, TRUE, TRUE, 6);      egg_histogram_view_set_data (EGG_HISTOGRAM_VIEW (td.histogram_view), -                                 td.buffer, td.width * td.height, bits_per_sample, 256); +                                 ring_buffer_get_current_pointer (td.buffer),                       +                                 td.width * td.height, bits_per_sample, 256);      window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));      g_signal_connect (window, "destroy", G_CALLBACK (on_destroy), &td); @@ -261,16 +312,17 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name)      td.record_button = GTK_WIDGET (gtk_builder_get_object (builder, "record-button"));      set_tool_button_state (&td); -    g_object_bind_property (gtk_builder_get_object (builder, "min_bin_value_adjustment"), "value", +    g_object_bind_property (gtk_builder_get_object (builder, "min-bin-value-adjustment"), "value",                              td.histogram_view, "minimum-bin-value",                              G_BINDING_DEFAULT); -    max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max_bin_value_adjustment")); +    max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max-bin-value-adjustment"));      gtk_adjustment_set_value (max_bin_adjustment, pow (2, bits_per_sample) - 1);      g_object_bind_property (max_bin_adjustment, "value",                              td.histogram_view, "maximum-bin-value",                              G_BINDING_DEFAULT); +    g_signal_connect (td.frame_slider, "value-changed", G_CALLBACK (on_frame_slider_changed), &td);      g_signal_connect (td.start_button, "clicked", G_CALLBACK (on_start_button_clicked), &td);      g_signal_connect (td.stop_button, "clicked", G_CALLBACK (on_stop_button_clicked), &td);      g_signal_connect (td.record_button, "clicked", G_CALLBACK (on_record_button_clicked), &td); diff --git a/tools/gui/control.glade b/tools/gui/control.glade index ebe2cc8..c3008ec 100644 --- a/tools/gui/control.glade +++ b/tools/gui/control.glade @@ -230,7 +230,7 @@                                  <property name="visible">True</property>                                  <property name="can_focus">True</property>                                  <property name="invisible_char">•</property> -                                <property name="adjustment">min_bin_value_adjustment</property> +                                <property name="adjustment">min-bin-value-adjustment</property>                                </object>                                <packing>                                  <property name="left_attach">1</property> @@ -260,7 +260,7 @@                                  <property name="visible">True</property>                                  <property name="can_focus">True</property>                                  <property name="invisible_char">•</property> -                                <property name="adjustment">max_bin_value_adjustment</property> +                                <property name="adjustment">max-bin-value-adjustment</property>                                </object>                                <packing>                                  <property name="left_attach">1</property> @@ -309,6 +309,27 @@                          <property name="tab_fill">False</property>                        </packing>                      </child> +                    <child> +                      <object class="GtkHScale" id="hscale1"> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="adjustment">frames-adjustment</property> +                        <property name="digits">0</property> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                    <child type="tab"> +                      <object class="GtkLabel" id="label4"> +                        <property name="visible">True</property> +                        <property name="label" translatable="yes">Preview</property> +                      </object> +                      <packing> +                        <property name="position">1</property> +                        <property name="tab_fill">False</property> +                      </packing> +                    </child>                    </object>                    <packing>                      <property name="resize">True</property> @@ -425,15 +446,19 @@        </object>      </child>    </object> -  <object class="GtkAdjustment" id="min_bin_value_adjustment"> +  <object class="GtkAdjustment" id="min-bin-value-adjustment">      <property name="upper">65535</property>      <property name="step_increment">1</property>      <property name="page_increment">10</property>    </object> -  <object class="GtkAdjustment" id="max_bin_value_adjustment"> +  <object class="GtkAdjustment" id="max-bin-value-adjustment">      <property name="value">256</property>      <property name="upper">65535</property>      <property name="step_increment">1</property>      <property name="page_increment">10</property>    </object> +  <object class="GtkAdjustment" id="frames-adjustment"> +    <property name="step_increment">1</property> +    <property name="page_increment">10</property> +  </object>  </interface> diff --git a/tools/gui/ring-buffer.c b/tools/gui/ring-buffer.c new file mode 100644 index 0000000..56c7620 --- /dev/null +++ b/tools/gui/ring-buffer.c @@ -0,0 +1,64 @@ + +#include <math.h> +#include "ring-buffer.h" + +RingBuffer * +ring_buffer_new (gsize block_size, +                 gsize n_blocks) +{ +    RingBuffer *buffer; + +    buffer = g_new0 (RingBuffer, 1); +    buffer->block_size = block_size; +    buffer->n_blocks_total = n_blocks; +    buffer->n_blocks_used = 0; +    buffer->start_index = 0; +    buffer->data = g_malloc0 (n_blocks * buffer->block_size); + +    return buffer; +} + +void +ring_buffer_free (RingBuffer *buffer) +{ +    g_free (buffer->data); +    g_free (buffer); +} + +void +ring_buffer_reset (RingBuffer *buffer) +{ +    buffer->n_blocks_used = 0; +    buffer->start_index = 0; +} + +gpointer +ring_buffer_get_current_pointer (RingBuffer *buffer) +{ +    return ring_buffer_get_pointer (buffer, 0); +} + +gpointer +ring_buffer_get_pointer (RingBuffer *buffer, +                         guint       index) +{ +    g_assert (index < buffer->n_blocks_total); +    return buffer->data + ((buffer->start_index + index) % buffer->n_blocks_total) * buffer->block_size; +} + +guint +ring_buffer_get_num_blocks (RingBuffer *buffer) +{ +    return buffer->n_blocks_used; +} + +void +ring_buffer_proceed (RingBuffer *buffer) +{ +    buffer->start_index++; + +    if (buffer->n_blocks_used < buffer->n_blocks_total) +        buffer->n_blocks_used++; +    else +        buffer->start_index = buffer->start_index % buffer->n_blocks_total; +} diff --git a/tools/gui/ring-buffer.h b/tools/gui/ring-buffer.h new file mode 100644 index 0000000..22cbde7 --- /dev/null +++ b/tools/gui/ring-buffer.h @@ -0,0 +1,28 @@ +#ifndef RING_BUFFER_H +#define RING_BUFFER_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct { +    guchar  *data; +    gsize    block_size; +    guint    n_blocks_total; +    guint    n_blocks_used; +    guint    start_index; +} RingBuffer; + +RingBuffer * ring_buffer_new                  (gsize       block_size, +                                               gsize       n_blocks); +void         ring_buffer_free                 (RingBuffer *buffer); +void         ring_buffer_reset                (RingBuffer *buffer); +gpointer     ring_buffer_get_current_pointer  (RingBuffer *buffer); +gpointer     ring_buffer_get_pointer          (RingBuffer *buffer, +                                               guint       index); +guint        ring_buffer_get_num_blocks       (RingBuffer *buffer); +void         ring_buffer_proceed              (RingBuffer *buffer); + +G_END_DECLS + +#endif | 
