home *** CD-ROM | disk | FTP | other *** search
Wrap
/* * logconv.c 1.2 * * Copyright (C) 1998 Alessandro Baldoni * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This is version 1.2 * LogConv for RGB images works on their luminance. */ #include <gtk/gtk.h> #include <libgimp/gimp.h> #include <math.h> #ifdef __MSVCRT__ #define isnan(x) _isnan(x) #endif #define KD(sigma, ag, al) ((sigma * G_PI)/ (sqrt (pow (ag, 2.0) + pow(al, 2.0)))) #define KS(sigma, kd, al) ((sigma * G_PI) / (al * kd)) #define SG(sigma, ks) (sigma * sqrt (1.0 - (1.0 / pow (ks, 2.0)))) #define SL(sigma, ks, kd) (sigma / (ks * kd)) typedef struct _entry { gdouble Ag, Al; } entry; static void ZoomImage (gdouble *, gdouble *, gint, gint, gint); static gdouble *LoGConvolution (GDrawable *, gdouble, gint); static gint LoGZero (GDrawable *, gdouble, gint, gint); static gdouble *MakeFilter (gdouble, gint, gint); static gint oddsupport (gdouble); static void query (); static void run (char *, int, GParam *, int *, GParam **); static gint LoGdialog (void); static void close_callback (GtkWidget *, gpointer); static void ok_callback (GtkWidget *, gpointer); static inline gdouble SobelX (gdouble *, gint, gint, gint); static inline gdouble SobelY (gdouble *, gint, gint, gint); static inline gdouble RobertsX (gdouble *, gint, gint, gint); static inline gdouble RobertsY (gdouble *, gint, gint, gint); static inline gdouble H1 (gdouble, gdouble); static inline gdouble H2 (gdouble, gdouble); static inline gdouble grad (gdouble, gdouble); /* * Precomputed values of Al e Ag for a given pa */ static entry Table1[11] = { {3.554140, 4.205983}, /* pa = 0.0001 */ {3.402057, 4.061595}, /* pa = 0.0003 */ {3.227792, 3.896178}, /* pa = 0.0010 */ {3.060844, 3.737694}, /* pa = 0.0030 */ {2.867757, 3.554300}, /* pa = 0.0100 */ {2.712526, 3.406712}, /* pa = 0.0250 */ {2.461219, 3.167270}, /* pa = 0.1000 */ {2.244686, 2.960158}, /* pa = 0.3000 */ {1.984301, 2.709568}, /* pa = 1.0000 */ {1.718008, 2.450782}, /* pa = 3.0000 */ {1.378026, 2.115151} /* pa = 10.0000 */ }; typedef struct _LoGDialog { gint run, pa, pc1, pc2, which; gdouble sd; } LoGDialog; static LoGDialog ldial = { FALSE, 0, 25, 60, 0, 2.0 }; static GtkWidget *toggle[11], *logentry, *ttoggle[3], *pc1entry, *pc2entry; GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; MAIN () static void query () { static GParamDef log_args[] = { {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, {PARAM_IMAGE, "image", "Input image"}, {PARAM_DRAWABLE, "drawable", "Input drawable"}, {PARAM_INT32, "pa", "Selected pa (0..10)"}, {PARAM_FLOAT, "sigma", "Standard deviation"}, {PARAM_INT32, "type", "0: Standard LoG, 1: LoG with Roberts, 2: LoG with Sobel"} }; static int nlog_args = sizeof (log_args) / sizeof (log_args[0]); gimp_install_procedure ("plug_in_LoG", "Apply the LoG filter", "", "Alessandro Baldoni", "Alessandro Baldoni", "1998", "<Image>/Filters/Edge-Detect/LoG", "RGB,GRAY", PROC_PLUG_IN, nlog_args, 0, log_args, NULL); } static void run (char *name, int nparams, GParam * param, int *nreturn_vals, GParam ** return_vals) { static GParam values[1]; GRunModeType run_mode; GDrawable *draw; run_mode = param[0].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = PARAM_STATUS; values[0].data.d_status = STATUS_CALLING_ERROR; draw = gimp_drawable_get (param[2].data.d_int32); switch (run_mode) { case RUN_INTERACTIVE: gimp_get_data ("plug_in_LoG", &ldial); if (LoGdialog ()) { if (LoGZero (draw, ldial.sd, ldial.pa, ldial.which) != -1) { values[0].data.d_status = STATUS_SUCCESS; gimp_displays_flush (); } else values[0].data.d_status = STATUS_EXECUTION_ERROR; } break; case RUN_NONINTERACTIVE: if (LoGZero (draw, param[4].data.d_float, param[3].data.d_int32, param[5].data.d_int32) != -1) values[0].data.d_status = STATUS_SUCCESS; else values[0].data.d_status = STATUS_EXECUTION_ERROR; break; case RUN_WITH_LAST_VALS: gimp_get_data ("plug_in_LoG", &ldial); if (LoGZero (draw, ldial.sd, ldial.pa, ldial.which) != -1) values[0].data.d_status = STATUS_SUCCESS; else values[0].data.d_status = STATUS_EXECUTION_ERROR; break; default: break; } gimp_drawable_detach (draw); } static gint LoGdialog () { GtkWidget *dlg, *button, *frame, *toggle_vbox, *hbox, *label, *hhbox; GSList *group; gchar **argv, *buf; gint argc; GtkTooltips *tips; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("save"); ldial.run = FALSE; gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); tips = gtk_tooltips_new (); dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), "LoG filter"); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_window_set_wmclass (GTK_WINDOW (dlg), "LoG", "log"); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) close_callback, NULL); gtk_container_border_width (GTK_CONTAINER (dlg), 5); /* Action area */ button = gtk_button_new_with_label ("OK"); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) ok_callback, dlg); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_grab_default (button); gtk_widget_show (button); button = gtk_button_new_with_label ("Cancel"); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dlg)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_show (button); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, FALSE, TRUE, 0); gtk_widget_show (hbox); /* compression */ frame = gtk_frame_new ("Allowable PA"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0); toggle_vbox = gtk_table_new (6, 2, FALSE); gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5); gtk_container_add (GTK_CONTAINER (frame), toggle_vbox); group = NULL; toggle[0] = gtk_radio_button_new_with_label (group, " 0.0001%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[0])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[0], 0, 1, 0, 1); gtk_widget_show (toggle[0]); toggle[1] = gtk_radio_button_new_with_label (group, " 0.0003%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[1])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[1], 0, 1, 1, 2); gtk_widget_show (toggle[1]); toggle[2] = gtk_radio_button_new_with_label (group, " 0.0010%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[2])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[2], 0, 1, 2, 3); gtk_widget_show (toggle[2]); toggle[3] = gtk_radio_button_new_with_label (group, " 0.0030%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[3])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[3], 0, 1, 3, 4); gtk_widget_show (toggle[3]); toggle[4] = gtk_radio_button_new_with_label (group, " 0.0100%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[4])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[4], 0, 1, 4, 5); gtk_widget_show (toggle[4]); toggle[5] = gtk_radio_button_new_with_label (group, " 0.0250%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[5])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[5], 0, 1, 5, 6); gtk_widget_show (toggle[5]); toggle[6] = gtk_radio_button_new_with_label (group, " 0.1000%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[6])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[6], 1, 2, 0, 1); gtk_widget_show (toggle[6]); toggle[7] = gtk_radio_button_new_with_label (group, " 0.3000%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[7])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[7], 1, 2, 1, 2); gtk_widget_show (toggle[7]); toggle[8] = gtk_radio_button_new_with_label (group, " 1.0000%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[8])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[8], 1, 2, 2, 3); gtk_widget_show (toggle[8]); toggle[9] = gtk_radio_button_new_with_label (group, " 3.0000%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[9])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[9], 1, 2, 3, 4); gtk_widget_show (toggle[9]); toggle[10] = gtk_radio_button_new_with_label (group, "10.0000%"); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle[10])); gtk_table_attach_defaults (GTK_TABLE (toggle_vbox), toggle[10], 1, 2, 4, 5); gtk_widget_show (toggle[10]); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle[ldial.pa]), TRUE); gtk_widget_show (toggle_vbox); gtk_widget_show (frame); /* LoG type */ frame = gtk_frame_new ("LoG type"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0); toggle_vbox = gtk_vbox_new (FALSE, 5); gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5); gtk_container_add (GTK_CONTAINER (frame), toggle_vbox); group = NULL; ttoggle[0] = gtk_radio_button_new_with_label (group, "Standard LoG"); gtk_tooltips_set_tip (tips, ttoggle[0], "Do not apply any gradient or thresholding", ""); group = gtk_radio_button_group (GTK_RADIO_BUTTON (ttoggle[0])); gtk_box_pack_start (GTK_BOX (toggle_vbox), ttoggle[0], FALSE, FALSE, 0); gtk_widget_show (ttoggle[0]); ttoggle[1] = gtk_radio_button_new_with_label (group, "LoG with Roberts"); gtk_tooltips_set_tip (tips, ttoggle[1], "Apply a Roberts gradient and a threshold", ""); group = gtk_radio_button_group (GTK_RADIO_BUTTON (ttoggle[1])); gtk_box_pack_start (GTK_BOX (toggle_vbox), ttoggle[1], FALSE, FALSE, 0); gtk_widget_show (ttoggle[1]); ttoggle[2] = gtk_radio_button_new_with_label (group, "LoG with Sobel"); gtk_tooltips_set_tip (tips, ttoggle[2], "Apply a Sobel gradient and a threshold", ""); group = gtk_radio_button_group (GTK_RADIO_BUTTON (ttoggle[2])); gtk_box_pack_start (GTK_BOX (toggle_vbox), ttoggle[2], FALSE, FALSE, 0); gtk_widget_show (ttoggle[2]); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttoggle[ldial.which]), TRUE); gtk_widget_show (toggle_vbox); gtk_widget_show (frame); /* Standard deviation */ hhbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hhbox, FALSE, TRUE, 0); gtk_widget_show (hhbox); label = gtk_label_new ("Standard deviation: "); gtk_box_pack_start (GTK_BOX (hhbox), label, FALSE, TRUE, 0); gtk_widget_show (label); buf = g_new (gchar, 10); sprintf (buf, "%f", ldial.sd); logentry = gtk_entry_new (); gtk_tooltips_set_tip (tips, logentry, "This is the standard deviation: the smaller it is, the finer details you'll get. The bigger it is, the coarser details you'll get. If this is smaller than 2.0, you must choose a big (3.0, 10.0) PA", ""); gtk_box_pack_start (GTK_BOX (hhbox), logentry, FALSE, TRUE, 0); gtk_entry_set_text (GTK_ENTRY (logentry), buf); g_free (buf); gtk_widget_show (logentry); /* pc1 */ label = gtk_label_new ("Thresholds used after gradient computation "); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), label, FALSE, TRUE, 0); gtk_widget_show (label); hhbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hhbox, FALSE, TRUE, 0); gtk_widget_show (hhbox); label = gtk_label_new ("PC1: "); gtk_box_pack_start (GTK_BOX (hhbox), label, FALSE, TRUE, 0); gtk_widget_show (label); buf = g_new (gchar, 10); sprintf (buf, "%d", ldial.pc1); pc1entry = gtk_entry_new (); gtk_tooltips_set_tip (tips, pc1entry, "If you choosed to apply a gradient, values below this threshold will be skipped", ""); gtk_box_pack_start (GTK_BOX (hhbox), pc1entry, FALSE, TRUE, 0); gtk_entry_set_text (GTK_ENTRY (pc1entry), buf); g_free (buf); gtk_widget_show (pc1entry); /* pc2 */ hhbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hhbox, FALSE, TRUE, 0); gtk_widget_show (hhbox); label = gtk_label_new ("PC2: "); gtk_box_pack_start (GTK_BOX (hhbox), label, FALSE, TRUE, 0); gtk_widget_show (label); buf = g_new (gchar, 10); sprintf (buf, "%d", ldial.pc2); pc2entry = gtk_entry_new (); gtk_tooltips_set_tip (tips, pc2entry, "If you choosed to apply a gradient, values exceeding this threshold will be skipped", ""); gtk_box_pack_start (GTK_BOX (hhbox), pc2entry, FALSE, TRUE, 0); gtk_entry_set_text (GTK_ENTRY (pc2entry), buf); g_free (buf); gtk_widget_show (pc2entry); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return ldial.run; } static void close_callback (GtkWidget * widget, gpointer data) { gtk_main_quit (); } static void ok_callback (GtkWidget * widget, gpointer data) { gint i; for (i = 0; i < 11; i++) if (GTK_TOGGLE_BUTTON (toggle[i])->active) ldial.pa = i; for (i = 0; i < 3; i++) if (GTK_TOGGLE_BUTTON (ttoggle[i])->active) ldial.which = i; ldial.sd = atof (gtk_entry_get_text (GTK_ENTRY (logentry))); ldial.pc1 = atoi (gtk_entry_get_text (GTK_ENTRY (pc1entry))); ldial.pc2 = atoi (gtk_entry_get_text (GTK_ENTRY (pc2entry))); ldial.run = TRUE; gimp_set_data ("plug_in_LoG", &ldial, sizeof (LoGDialog)); gtk_widget_destroy (GTK_WIDGET (data)); } /* * Computes the convolution of draw with a LoG of constant sigma and * aliasing pa. */ static gdouble * LoGConvolution (GDrawable * draw, gdouble sigma, gint pa) { gint i, j, k, addr, MSL, W, H, W1, H1, s2, s3, kd, MG, i1, j1, j2, gap; gdouble *temp, *temp2, *h1, *h2, *g, h1x, h1y, h2x, h2y, *temp1, Al, Ag, ks, sigmal, sigmag, *temp3, t; GPixelRgn src; guchar *buf; W = gimp_drawable_width (draw->id); H = gimp_drawable_height (draw->id); gap = (gimp_drawable_type (draw->id) == RGB_IMAGE) ? 3 : 1; buf = g_new (guchar, W * H * gap); gimp_pixel_rgn_init (&src, draw, 0, 0, W, H, FALSE, FALSE); gimp_pixel_rgn_get_rect (&src, buf, 0, 0, W, H); gimp_progress_init ("Computing LoG..."); /* * 1. Operator design */ /* Cutoff constants */ Ag = Table1[pa].Ag; Al = Table1[pa].Al; /* Decimation factor */ kd = (gint) floor (KD (sigma, Ag, Al)); /* Reconstruction constant */ ks = KS (sigma, (gdouble) kd, Al); /* Gaussian space constant */ sigmag = SG (sigma, ks); /* LoG space constant */ sigmal = SL (sigma, ks, kd); if ((isnan (sigmag) != 0) || (isnan (sigmal) != 0)) return (gdouble *) NULL; /* LoG MSL width */ MSL = oddsupport (sigmal); /* LoG filters computation */ h1 = MakeFilter (sigmal, MSL, 1); h2 = MakeFilter (sigmal, MSL, 2); /* Gaussian MSL width */ MG = 6 * (gint) sigmag; if (MG % 2 == 0) MG++; /* Gaussian filter computation */ g = MakeFilter (sigmag, MG, 3); /* * 2. Convolution process */ /* Gaussian convolution and decimation */ W1 = (gint) ceil ((gdouble) W / (gdouble) kd); H1 = (gint) ceil ((gdouble) H / (gdouble) kd); s3 = MG / 2; temp1 = g_new (gdouble, W1 * (H + s3 * 2)); temp = g_new (gdouble, (W + s3 * 2) * H); i1 = W + s3 * 2; if (gap == 1) /* Gray images */ for (i = 0; i < H; i++) { j1 = i * i1; for (j = 0; j < W; j++) temp[(j + s3) + j1] = ((gdouble) buf[j + i * W]); } else /* RGB images */ for (i = 0; i < H; i++) { j1 = i * i1; for (j2 = 0, j = 0; j < W; j++, j2 += 3) /* Luminance */ temp[(j + s3) + j1] = (gdouble) (buf[j2 + i * W * 3] * 0.299 + buf[j2 + 1 + i * W * 3] * 0.587 + buf[j2 + 2 + i * W * 3] * 0.114); } g_free (buf); gimp_progress_update (1.0 / 7.0); /* DC-padding */ for (i = 0; i < H; i++) { /* Left */ j1 = i * (W + s3 * 2); i1 = s3 + j1; for (j = 0; j < s3; j++) temp[j + j1] = temp[i1]; } for (i = 0; i < H; i++) { /* Right */ j1 = i * (W + s3 * 2); i1 = (W + s3 - 1) + j1; for (j = W + s3; j < (W + s3 * 2); j++) temp[j + j1] = temp[i1]; } for (i = 0, i1 = s3; i < H; i++, i1++) /* Row convolution */ for (j = s3, j1 = 0; j < W + s3; j += kd, j1++) { t = 0.0; for (k = 0; k < MG; k++) t += temp[(j - s3 + k) + i * (W + s3 * 2)] * g[k]; temp1[j1 + i1 * W1] = t; } g_free (temp); gimp_progress_update (2.0 / 7.0); /* DC-padding */ i1 = s3 * W1; for (i = 0; i < s3; i++) { /* Up */ j1 = i * W1; for (j = 0; j < W1; j++) temp1[j + j1] = temp1[j + i1]; } i1 = (H + s3 - 1) * W1; for (i = H + s3; i < (H + s3 * 2); i++) { /* Down */ j1 = i * W1; for (j = 0; j < W1; j++) temp1[j + j1] = temp1[j + i1]; } temp2 = g_new (gdouble, W1 * H1); for (i = s3, i1 = 0; i < H + s3; i += kd, i1++) /* Column convolution */ for (j = 0; j < W1; j++) { t = 0.0; for (k = 0; k < MG; k++) t += temp1[j + (i - s3 + k) * W1] * g[k]; temp2[j + i1 * W1] = t; } g_free (temp1); gimp_progress_update (3.0 / 7.0); /* LoG convolution */ s2 = MSL / 2; temp = g_new (gdouble, (W1 + s2 * 2) * (H1 + s2 * 2)); i1 = W1 + s2 * 2; for (i = 0; i < H1; i++) for (j = 0; j < W1; j++) temp[(j + s2) + (i + s2) * i1] = temp2[j + i * W1]; gimp_progress_update (4.0 / 7.0); /* DC-padding */ i1 = W1 + s2 * 2; for (i = s2; i < H1 + s2; i++) { /* Left */ j1 = i * i1; for (j = 0; j < s2; j++) temp[j + j1] = temp[s2 + j1]; } for (i = s2; i < H1 + s2; i++) { /* Right */ j1 = i * i1; for (j = W1 + s2; j < (W1 + s2 * 2); j++) temp[j + j1] = temp[(W1 + s2 - 1) + j1]; } g_free (temp2); temp1 = g_new (gdouble, W1 * (H1 + s2 * 2)); temp3 = g_new (gdouble, W1 * (H1 + s2 * 2)); for (i = s2, i1 = 0; i < H1 + s2; i++, i1++) { /* Row convolution */ for (j = s2, j1 = 0; j < W1 + s2; j++, j1++) { addr = i1 * W1; h1x = h2x = 0.0; for (k = 0; k < MSL; k++) { h1x += temp[(j - s2 + k) + i * (W1 + s2 * 2)] * h1[k]; h2x += temp[(j - s2 + k) + i * (W1 + s2 * 2)] * h2[k]; } temp1[j1 + i * W1] = h1x; temp3[j1 + i * W1] = h2x; } } g_free (temp); gimp_progress_update (5.0 / 7.0); /* DC-padding */ i1 = s2 * W1; for (i = 0; i < s2; i++) { /* Up */ j1 = i * W1; for (j = 0; j < W1; j++) { temp1[j + j1] = temp1[j + i1]; temp3[j + j1] = temp3[j + i1]; } } i1 = (H1 + s2 - 1) * W1; for (i = H1 + s2; i < (H1 + s2 * 2); i++) { /* Down */ j1 = i * W1; for (j = 0; j < W1; j++) { temp1[j + j1] = temp1[j + i1]; temp3[j + j1] = temp3[j + i1]; } } temp = g_new (gdouble, W1 * H1); for (i = s2, i1 = 0; i < H1 + s2; i++, i1++) { /* Column convolution */ for (j = 0; j < W1; j++) { h1y = h2y = 0.0; for (k = 0; k < MSL; k++) { h1y += temp3[j + (i - s2 + k) * W1] * h1[k]; h2y += temp1[j + (i - s2 + k) * W1] * h2[k]; } temp[j + i1 * W1] = h1y + h2y; } } g_free (temp1); g_free (temp3); gimp_progress_update (6.0 / 7.0); /* Expansion */ temp1 = g_new (gdouble, W * H); if (kd > 1) ZoomImage (temp, temp1, kd, W, H); else for (i = 0; i < H * W; i++) temp1[i] = temp[i]; gimp_progress_update (7.0 / 7.0); g_free (temp); g_free (h2); g_free (h1); g_free (g); return temp1; } /* * Computes zero crossings for an image convoluted with a LoG of constant * sigma and aliasing pa. * If gradient is specified, it performs a gradient thresholding as in * A. Basu et al. (1995). * gradient = 1 -> Roberts, = 2 -> Sobel */ static gint LoGZero (GDrawable * draw, gdouble sigma, gint pa, gint gradient) { gdouble *temp1, pix0, pix1, pix2, pix3, pix4, gradx, grady, *gradval, max, pc1, pc2; gint W, H, addr, i, j, gap, j1, i1; gdouble (*funcx) (gdouble *, gint, gint, gint), (*funcy) (gdouble *, gint, gint, gint); GPixelRgn src; guchar *buf; gboolean do_zero; gradval = (gdouble *) NULL; max = 0.0; funcx = NULL; funcy = NULL; W = gimp_drawable_width (draw->id); H = gimp_drawable_height (draw->id); gap = (gimp_drawable_type (draw->id) == RGB_IMAGE) ? 3 : 1; if ((temp1 = LoGConvolution (draw, sigma, pa)) == (gdouble *) NULL) { g_message ("LoG: Please choose a bigger PA."); return -1; } /* * Zero crossing detection and gradient computation. */ if (gradient != 0) { max = 0.0; gradval = g_new (gdouble, W * H); memset (gradval, 0, sizeof (gdouble) * W * H); if (gradient == 1) { funcx = RobertsX; funcy = RobertsY; } else { funcx = SobelX; funcy = SobelY; } } buf = g_new (guchar, W * H * gap); memset (buf, 255, sizeof (guchar) * W * H * gap); gimp_progress_init ("Zero crossing..."); for (i = 1; i < H - 1; i++) { for (j1 = 3, j = 1; j < W - 1; j++, j1 += 3) { do_zero = FALSE; addr = j + i * W; /* * Chessboard metric * 1 * 2 0 3 * 4 */ pix0 = temp1[addr]; pix1 = temp1[j + (i - 1) * W]; pix2 = temp1[j - 1 + i * W]; pix3 = temp1[j + 1 + i * W]; pix4 = temp1[j + (i + 1) * W]; /* * Zero crossing test by Simon A. J. Winder (c) 1994 */ if (pix0 > 0.0 && (pix1 < 0.0 || pix2 < 0.0 || pix3 < 0.0 || pix4 < 0.0)) do_zero = TRUE; if (pix0 == 0.0) { if ((pix1 > 0.0 && pix4 < 0.0) || (pix1 < 0.0 && pix4 > 0.0) || (pix2 > 0.0 && pix3 < 0.0) || (pix2 < 0.0 && pix3 > 0.0)) do_zero = TRUE; else { pix1 = temp1[j - 1 + (i + 1) * W]; pix2 = temp1[j + 1 + (i + 1) * W]; pix3 = temp1[j - 1 + (i - 1) * W]; pix4 = temp1[j + 1 + (i - 1) * W]; if ((pix1 > 0.0 && pix4 < 0.0) || (pix1 < 0.0 && pix4 > 0.0) || (pix2 > 0.0 && pix3 < 0.0) || (pix2 < 0.0 && pix3 > 0.0)) do_zero = TRUE; } } if (do_zero) { if (gradient != 0) { gradx = funcx (temp1, j, i, W); grady = funcy (temp1, j, i, W); gradval[addr] = fabs (gradx) + fabs (grady); if (max < gradval[addr]) max = gradval[addr]; } else { if (gap == 3) buf[j1 + i * W * 3] = buf[j1 + 1 + i * W * 3] = buf[j1 + 2 + i * W * 3] = 0; else buf[addr] = 0; } } } gimp_progress_update ((gdouble) i / (gdouble) (H - 1)); } if (gradient != 0) { /* * Gradient weighting */ pc1 = (max * (gdouble) ldial.pc1) / 100.0; /* 25% */ pc2 = (max * (gdouble) ldial.pc2) / 100.0; /* 60% */ /* * We skip all pixels below the 25% gradient threshold */ if (gap == 1) /* Gray images */ { for (i = 0; i < W * H; i++) if ((gradval[i] > pc1) && (gradval[i] < pc2)) buf[i] = 0; } else /* RGB images */ for (i1 = 0, i = 0; i < W * H; i++, i1 += 3) if ((gradval[i] > pc1) && (gradval[i] < pc2)) buf[i1] = buf[i1 + 1] = buf[i1 + 2] = 0; g_free (gradval); } gimp_pixel_rgn_init (&src, draw, 0, 0, W, H, TRUE, TRUE); gimp_pixel_rgn_set_rect (&src, buf, 0, 0, W, H); gimp_drawable_flush (draw); gimp_drawable_merge_shadow (draw->id, TRUE); gimp_drawable_update (draw->id, 0, 0, W, H); g_free (buf); g_free (temp1); return 0; } /* * Computes the filter's support. It must be odd. */ static gint oddsupport (gdouble sigma) { gint support; support = 4 * 2 * G_SQRT2 * sigma; if ((support % 2) == 0) return (support + 1); else return support; } /* * Creates filters for LoG decomposition */ static gdouble * MakeFilter (gdouble sigma, gint support, gint which) { gint x; gdouble *filter; gdouble (*func) (gdouble, gdouble); func = NULL; switch (which) { case 1: func = H1; break; case 2: func = H2; break; case 3: func = grad; break; } filter = g_new (gdouble, support); for (x = 0; x <= support / 2; x++) filter[support / 2 + x] = filter[support / 2 - x] = func ((gdouble) x, sigma); return filter; } static inline gdouble SobelX (gdouble * temp1, gint j, gint i, gint W) { return (-temp1[(j - 1) + (i - 1) * W] - 2.0 * temp1[j + (i - 1) * W] - temp1[(j + 1) + (i - 1) * W] + temp1[(j - 1) + (i + 1) * W] + 2.0 * temp1[j + (i + 1) * W] + temp1[(j + 1) + (i + 1) * W]); } static inline gdouble SobelY (gdouble * temp1, gint j, gint i, gint W) { return (-temp1[(j - 1) + (i - 1) * W] - 2.0 * temp1[(j - 1) + i * W] - temp1[(j - 1) + (i + 1) * W] + temp1[(j + 1) + (i - 1) * W] + 2.0 * temp1[(j + 1) + i * W] + temp1[(j + 1) + (i + 1) * W]); } static inline gdouble RobertsX (gdouble * temp1, gint j, gint i, gint W) { return (temp1[j + i * W] - temp1[(j + 1) + (i + 1) * W]); } static inline gdouble RobertsY (gdouble * temp1, gint j, gint i, gint W) { return (temp1[(j + 1) + i * W] - temp1[j + (i + 1) * W]); } static inline gdouble H1 (gdouble xi, gdouble sigma) { return (1.0 / (sqrt (2.0 * G_PI) * pow (sigma, 2.0))) * (1.0 - (pow (xi, 2.0) / pow (sigma, 2.0))) * exp (-pow (xi, 2.0) / (2.0 * pow (sigma, 2.0))); } static inline gdouble H2 (gdouble xi, gdouble sigma) { return (1.0 / (sqrt (2.0 * G_PI) * pow (sigma, 2.0))) * exp (-pow (xi, 2.0) / (2.0 * pow (sigma, 2.0))); } static inline gdouble grad (gdouble x, gdouble sigma) { return ((1.0 / (sqrt (2.0 * G_PI) * sigma)) * exp (-pow (x, 2.0) / (2 * pow (sigma, 2.0)))); } /* * A C-port from LAPACK... */ static inline void dgefac (gdouble a[4][4], gdouble b[4]) { gdouble t, dmax, ipvt[4]; gint k, i, l, j; /* * Gaussian elimination with partial pivoting */ for (k = 0; k < 3; k++) { /* * Look for pivot l */ l = k; dmax = fabs (a[k][k]); for (i = k + 1; i < 4; i++) if (fabs (a[i][k]) > dmax) { dmax = fabs (a[i][k]); l = i; } ipvt[k] = l; /* * Swap if needed */ if (l != k) { t = a[l][k]; a[l][k] = a[k][k]; a[k][k] = t; } /* * Multipliers computation */ t = -1.0 / a[k][k]; for (i = k + 1; i < 4; i++) a[i][k] *= t; /* * Row elimination */ for (j = k + 1; j < 4; j++) { t = a[l][j]; if (l != k) { a[l][j] = a[k][j]; a[k][j] = t; } for (i = k + 1; i < 4; i++) a[i][j] += (t * a[i][k]); } } ipvt[3] = 3; /* * Solving L y = b */ for (k = 0; k < 3; k++) { l = ipvt[k]; t = b[l]; if (l != k) { b[l] = b[k]; b[k] = t; } for (i = k + 1; i < 4; i++) b[i] += (t * a[i][k]); } /* * Then U x = y */ for (k = 3; k >= 0; k--) { b[k] /= a[k][k]; t = -b[k]; for (i = 0; i < k; i++) b[i] += (a[i][k] * t); } } /* * Upsampling through bilinear interpolation. */ static void ZoomImage (gdouble * src, gdouble * dest, gint DF, gint width, gint height) { gdouble b[4], *temp; gdouble a[4][4]; gint i, j, l, k, W, H, addr, addr2; /* * Buffer initialization */ temp = g_new (gdouble, (width + DF) * (height + DF)); memset (temp, 0, sizeof (gdouble) * (width + DF) * (height + DF)); W = (gint) ceil ((gdouble) width / (gdouble) DF); H = (gint) ceil ((gdouble) height / (gdouble) DF); for (i = 0, k = 0; k < H; i += DF, k++) for (j = 0, l = 0; l < W; j += DF, l++) temp[j + i * (width + DF)] = src[l + k * W]; /* * DC-padding */ k = W * DF; l = (W - 1) * DF; for (i = 0; i < height + DF; i += DF) temp[k + i * (width + DF)] = temp[l + i * (width + DF)]; k = H * DF * (width + DF); l = (H - 1) * DF * (width + DF); for (j = 0; j < width + DF; j += DF) temp[j + k] = temp[j + l]; W = width + DF; /* * Row bilinear interpolation */ for (i = 0; i < height; i += DF) { addr = i * W; addr2 = (i + DF) * W; for (j = 1; j < width; j += DF) for (k = j, l = DF - 1; k < j + DF - 1; k++, l--) { a[0][3] = a[1][3] = a[2][3] = a[3][3] = 1.0; a[0][0] = (gdouble) (k - 1); a[1][0] = (gdouble) (k + l); a[2][0] = (gdouble) (j - 1); a[3][0] = (gdouble) (j + DF - 1); a[0][1] = a[1][1] = (gdouble) i; a[2][1] = a[3][1] = (gdouble) (i + DF); a[0][2] = a[0][0] * a[0][1]; a[1][2] = a[1][0] * a[1][1]; a[2][2] = a[2][0] * a[2][1]; a[3][2] = a[3][0] * a[3][1]; b[0] = temp[(k - 1) + addr]; b[1] = temp[(k + l) + addr]; b[2] = temp[(j - 1) + addr2]; b[3] = temp[(j + DF - 1) + addr2]; dgefac (a, b); temp[k + addr] = (b[0] * (gdouble) k + b[1] * (gdouble) i + b[2] * (gdouble) (i * k) + b[3]); } } /* * Column bilinear interpolation */ for (i = 1; i < height; i += DF) for (j = 0; j < width; j++) for (k = i, l = DF - 1; k < i + DF - 1; k++, l--) { a[0][3] = a[1][3] = a[2][3] = a[3][3] = 1.0; a[0][0] = a[2][0] = (gdouble) j; a[1][0] = a[3][0] = (gdouble) (j + 1); a[0][1] = a[1][1] = (gdouble) (k - 1); a[2][1] = a[3][1] = (gdouble) (k + l); a[0][2] = a[0][0] * a[0][1]; a[1][2] = a[1][0] * a[1][1]; a[2][2] = a[2][0] * a[2][1]; a[3][2] = a[3][0] * a[3][1]; b[0] = temp[j + (k - 1) * W]; b[1] = temp[(j + 1) + (k - 1) * W]; b[2] = temp[j + (k + l) * W]; b[3] = temp[(j + 1) + (k + l) * W]; dgefac (a, b); temp[j + k * W] = (b[0] * (gdouble) j + b[1] * (gdouble) k + b[2] * (gdouble) (k * j) + b[3]); } for (i = 0; i < height; i++) { addr = i * W; for (j = 0; j < width; j++) dest[j + i * width] = temp[j + addr]; } g_free (temp); }