#include #include #include #include #include #include struct rect { gint x, y, width, height; }; struct roi { GtkWidget *window; GtkWidget *drawing_area; struct key_press { guint8 state; guint32 key; } key_press; GdkPixbuf *input, *modified_input; struct rect motion; struct rect roi_area; struct rect previous_draw; }; void do_roi(struct roi *roi) { GdkPixbuf *match; int x, y, i; int width, height; gint stride, nch; guchar *pix, *row; gint s; gint middle[3] = { 0, 0, 0 }; gint sigma[3] = { 0, 0, 0 }; struct match_interval { gint min, max; } interval[3]; match = gdk_pixbuf_new_subpixbuf(roi->input, roi->roi_area.x, roi->roi_area.y, roi->roi_area.width, roi->roi_area.height); nch = gdk_pixbuf_get_n_channels(match); stride = gdk_pixbuf_get_rowstride(match); s = roi->roi_area.width * roi->roi_area.height; for (y = 0, row = gdk_pixbuf_get_pixels(match); y < roi->roi_area.height; ++y, row += stride) { for (x = 0, pix = row; x < roi->roi_area.width; ++x, pix += nch) { for (i = 0; i < 3; ++i) middle[i] += pix[i]; } } for (i = 0; i < 3; ++i) middle[i] /= s; for (y = 0, row = gdk_pixbuf_get_pixels(match); y < roi->roi_area.height; ++y, row += stride) { for (x = 0, pix = row; x < roi->roi_area.width; ++x, pix += nch) { for (i = 0; i < 3; ++i) sigma[i] += (pix[i] - middle[i]) * (pix[i] - middle[i]); } } for (i = 0; i < 3; ++i) sigma[i] = sqrt(sigma[i] / (s - 1)); printf("middle: %3d, %3d, %3d\n", middle[0], middle[1], middle[2]); printf("sigma: %3d, %3d, %3d\n", sigma[0], sigma[1], sigma[2]); for (i = 0; i < 3; ++i) { interval[i].min = middle[i] - 3 * sigma[i]; interval[i].max = middle[i] + 3 * sigma[i]; printf("interval %d: [%3d - %3d]\n", i, interval[i].min, interval[i].max); } nch = gdk_pixbuf_get_n_channels(roi->input); stride = gdk_pixbuf_get_rowstride(roi->input); width = gdk_pixbuf_get_width(roi->input); height = gdk_pixbuf_get_height(roi->input); if (roi->modified_input) g_object_unref(roi->modified_input); roi->modified_input = gdk_pixbuf_copy(roi->input); for (y = 0, row = gdk_pixbuf_get_pixels(roi->modified_input); y < height; ++y, row += stride) { for (x = 0, pix = row; x < width; ++x, pix += nch) { int H = 1; for (i = 0; i < 3; ++i) { if (!(pix[i] >= interval[i].min && pix[i] <= interval[i].max)) H = 0; } if (H) pix[0] = pix[1] = pix[2] = 0; } } g_object_unref(match); } gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer userdata) { struct roi *roi = userdata; GdkWindow *win = gtk_widget_get_window(widget); GdkRectangle rect; cairo_t *cr; rect.x = roi->motion.x; rect.y = roi->motion.y; rect.width = event->x - roi->motion.x; rect.height = event->y - roi->motion.y; /* gdk_window_begin_paint_rect(win, &rect); */ cr = gdk_cairo_create(win); cairo_set_line_width(cr, 2.0); gdk_cairo_set_source_pixbuf(cr, roi->input, 0, 0); cairo_rectangle(cr, roi->previous_draw.x, roi->previous_draw.y, roi->previous_draw.width, roi->previous_draw.height); cairo_fill_preserve(cr); cairo_stroke(cr); roi->previous_draw.x = roi->motion.x; roi->previous_draw.y = roi->motion.y; roi->previous_draw.width = event->x - roi->motion.x; roi->previous_draw.height = event->y - roi->motion.y; #if 0 const double dashed[] = {1.0}; cairo_set_dash(cr, dashed, 1, 0); #endif cairo_rectangle(cr, roi->previous_draw.x, roi->previous_draw.y, roi->previous_draw.width, roi->previous_draw.height); cairo_set_source_rgba(cr, 0.0, 0.0, 0.5, 0.3); cairo_stroke_preserve(cr); cairo_set_source_rgba(cr, 0.0, 0.0, 0.5, 0.5); cairo_fill(cr); cairo_destroy(cr); /* gdk_window_end_paint(win); */ return TRUE; } gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer userdata) { struct roi *roi = userdata; if (event->button != 1) return TRUE; roi->motion.x = event->x; roi->motion.y = event->y; return FALSE; } gboolean button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer userdata) { struct roi *roi = userdata; if (event->button != 1) return TRUE; roi->roi_area.x = roi->motion.x; roi->roi_area.y = roi->motion.y; roi->roi_area.width = event->x - roi->motion.x; roi->roi_area.height = event->y - roi->motion.y; if (roi->roi_area.width < 0) { roi->roi_area.x -= roi->roi_area.width; roi->roi_area.width = -roi->roi_area.width; } if (roi->roi_area.height < 0) { roi->roi_area.y -= roi->roi_area.height; roi->roi_area.height = -roi->roi_area.height; } do_roi(roi); roi->previous_draw.width = roi->previous_draw.height = 0; gtk_widget_queue_draw(roi->drawing_area); return FALSE; } gboolean key_event(GtkWidget *widget, GdkEventKey *event, gpointer userdata) { struct roi *roi = userdata; roi->key_press.state = (event->type == GDK_KEY_PRESS); roi->key_press.key = event->keyval; if (!roi->key_press.state) { switch (roi->key_press.key) { case 'd': g_object_unref(roi->modified_input); roi->modified_input = NULL; gtk_widget_queue_draw(roi->drawing_area); break; case 'q': gtk_main_quit(); break; } } return FALSE; } gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { return FALSE; } gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer userdata) { struct roi *roi = userdata; gdk_cairo_set_source_pixbuf(cr, roi->modified_input ? roi->modified_input : roi->input, 0, 0); cairo_paint(cr); return FALSE; } int main(int argc, char *argv[]) { struct roi roi; memset(&roi, 0, sizeof roi); gtk_init(&argc, &argv); roi.input = gdk_pixbuf_new_from_file(argv[1], NULL); roi.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); roi.drawing_area = gtk_drawing_area_new(); gtk_widget_set_size_request(roi.drawing_area, gdk_pixbuf_get_width(roi.input), gdk_pixbuf_get_height(roi.input)); gtk_container_add(GTK_CONTAINER(roi.window), roi.drawing_area); g_signal_connect(roi.window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(roi.drawing_area), "draw", G_CALLBACK(draw_cb), &roi); g_signal_connect(G_OBJECT(roi.drawing_area), "configure-event", G_CALLBACK(configure_event), &roi); gtk_widget_set_events(roi.drawing_area, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK); g_signal_connect(G_OBJECT(roi.window), "key-press-event", G_CALLBACK(key_event), &roi); g_signal_connect(G_OBJECT(roi.window), "key-release-event", G_CALLBACK(key_event), &roi); g_signal_connect(G_OBJECT(roi.drawing_area), "button-press-event", G_CALLBACK(button_press_event), &roi); g_signal_connect(G_OBJECT(roi.drawing_area), "button-release-event", G_CALLBACK(button_release_event), &roi); g_signal_connect (G_OBJECT (roi.drawing_area), "motion-notify-event", G_CALLBACK(motion_notify_event), &roi); gtk_widget_show_all(roi.window); gtk_main(); g_object_unref(roi.input); if (roi.modified_input) g_object_unref(roi.modified_input); return 0; }