// Tests the FFT implementation against the naive DFT, returning the base-10 logarithm of the RMS error. This number should be less than -10.
static double test_fft_log_error(int n) {
	double *inputreal, *inputimag;
	double *refoutreal, *refoutimag;
	double *actualoutreal, *actualoutimag;
	
	inputreal = random_reals(n);
	inputimag = random_reals(n);
	
	refoutreal = malloc(n * sizeof(double));
	refoutimag = malloc(n * sizeof(double));
	naive_dft(inputreal, inputimag, refoutreal, refoutimag, 0, n);
	
	actualoutreal = memdup(inputreal, n * sizeof(double));
	actualoutimag = memdup(inputimag, n * sizeof(double));
	void *fftTables = fft_init(n);
	if (fftTables == NULL)
		return 99;
	fft_transform(fftTables, actualoutreal, actualoutimag);
	fft_destroy(fftTables);
	double result = log10_rms_err(refoutreal, refoutimag, actualoutreal, actualoutimag, n);
	
	free(inputreal);
	free(inputimag);
	free(refoutreal);
	free(refoutimag);
	free(actualoutreal);
	free(actualoutimag);
	return result;
}
int main(int argc, char **argv) {
	// Self-test to check correct computation of values
	srand(time(NULL));
	int i;
	for (i = 2; i <= 10; i++) {  // Test FFT sizes 4, 8, 16, ..., 512, 1024
		if (test_fft_log_error(1 << i) > -10) {
			printf("Self-test failed\n");
			return 1;
		}
	}
	printf("Self-test passed\n");
	
	// Speed benchmark
	const int64_t TARGET_TIME = 100000000;  // In nanoseconds
	const int TRIALS = 10;
	printf("%9s    %s\n", "Size", "Time per FFT (ns)");
	size_t n;
	for (n = 4; n <= (size_t)1 << 26; n *= 2) {
		// Initialize data sets
		void *fftTables = fft_init(n);
		double *real = random_reals(n);
		double *imag = random_reals(n);
		if (fftTables == NULL || real == NULL || imag == NULL) {
			printf("Memory allocation failed\n");
			return 1;
		}
		
		// Determine number of iterations to run to spend TARGET_TIME
		uint64_t iterations = 1;
		while (1) {
			int64_t time = benchmark_time(fftTables, real, imag, iterations);
			if (time >= TARGET_TIME) {
				iterations = (uint64_t)((double)TARGET_TIME / time * iterations + 0.5);
				if (iterations == 0)
					iterations = 1;
				break;
			}
			iterations *= 2;
		}
		
		// Run trials and store timing
		double *runtimes = malloc(TRIALS * sizeof(double));
		int i;
		for (i = 0; i < TRIALS; i++)
			runtimes[i] = (double)benchmark_time(fftTables, real, imag, iterations) / iterations;
		fft_destroy(fftTables);
		free(real);
		free(imag);
		
		// Compute statistics
		double min = 1e300;
		double sum = 0;
		for (i = 0; i < TRIALS; i++) {
			double t = runtimes[i];
			if (t < min)
				min = t;
			sum += t;
		}
		double mean = sum / TRIALS;
		double sqrdiffsum = 0;
		for (i = 0; i < TRIALS; i++) {
			double t = runtimes[i];
			sqrdiffsum += (t - mean) * (t - mean);
		}
		double stddev = sqrt(sqrdiffsum / TRIALS);
		free(runtimes);
		printf("%9zu    min=%"PRIu64"  mean=%"PRIu64"  sd=%.2f%%\n",
			n, (uint64_t)(min + 0.5), (uint64_t)(mean + 0.5), stddev / mean * 100);
	}
	return 0;
}
Beispiel #3
0
// Run the plugin
static void
run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals)
{
	// Return values
  static GimpParam values[1];

  gint sel_x1, sel_y1, sel_x2, sel_y2, w, h, padding;
	PluginData         pd;
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;

  *nreturn_vals = 1;
  *return_vals  = values;

  if (param[0].type!= GIMP_PDB_INT32) 
	  status=GIMP_PDB_CALLING_ERROR;
  if (param[2].type!=GIMP_PDB_DRAWABLE)
	  status=GIMP_PDB_CALLING_ERROR;

  run_mode = (GimpRunMode) param[0].data.d_int32;
	
	pd.drawable = gimp_drawable_get(param[2].data.d_drawable);
	gimp_drawable_mask_bounds(pd.drawable->drawable_id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
	pd.selection_width = sel_x2 - sel_x1;
  pd.selection_height = sel_y2 - sel_y1;
  pd.selection_offset_x = sel_x1;
  pd.selection_offset_y = sel_y1;
  pd.image_width = gimp_drawable_width(pd.drawable->drawable_id);
  pd.image_height = gimp_drawable_height(pd.drawable->drawable_id);
  pd.channel_count = gimp_drawable_bpp(pd.drawable->drawable_id);

  pd.point_grabbed = -1;

	if (run_mode == GIMP_RUN_INTERACTIVE)
	{
		// Interactive call with dialog
		dialog(&pd);
		if (pd.curve_user.count > 0)
		{
			gimp_set_data (PLUG_IN_BINARY, pd.curve_user.user_points, sizeof (GdkPoint) * pd.curve_user.count);
		}
	}
	else if (run_mode == GIMP_RUN_WITH_LAST_VALS)
	{
		// Read a saved curve and apply it
		fft_prepare(&pd);
		gimp_get_data(PLUG_IN_BINARY, pd.curve_user.user_points);
		pd.curve_user.count = gimp_get_data_size(PLUG_IN_BINARY) / sizeof (GdkPoint);
		gimp_pixel_rgn_init(&pd.region, pd.drawable, 0, 0, pd.image_width, pd.image_height, TRUE, TRUE);
		fft_apply(&pd);
		gimp_pixel_rgn_set_rect(&pd.region, pd.img_pixels, 0, 0, pd.image_width, pd.image_height);
		gimp_drawable_flush(pd.drawable);
		gimp_drawable_merge_shadow(pd.drawable->drawable_id, TRUE);
		gimp_drawable_update(pd.drawable->drawable_id, pd.selection_offset_x, pd.selection_offset_y, pd.selection_width, pd.selection_height);
		fft_destroy(&pd);
		gimp_displays_flush();
	}
	else
	{
	  status = GIMP_PDB_CALLING_ERROR;	
	}
  values[0].type = GIMP_PDB_STATUS;
  values[0].data.d_status = status;
  gimp_drawable_detach(pd.drawable);
}
Beispiel #4
0
// Create and handle the plugin's dialog
gboolean dialog(PluginData *pd)
{
	gimp_ui_init (PLUG_IN_BINARY, FALSE);
	GtkWidget *dialog, *main_hbox, *hbox_buttons, *preview, *graph, *vbox, *preview_button, *preview_hd_checkbox;
	dialog = gimp_dialog_new ("Frequency Curves", PLUG_IN_BINARY,
														NULL, (GtkDialogFlags)0,
														gimp_standard_help_func, PLUG_IN_NAME,
														GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
														GTK_STOCK_OK,     GTK_RESPONSE_OK,
														NULL);
	
	main_hbox = gtk_hbox_new (FALSE, 12);
	gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
	gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), main_hbox);
	
	curve_init(&pd->curve_user);
	curve_copy(&pd->curve_user, &pd->curve_fft);
	
	pd->preview = gimp_drawable_preview_new (pd->drawable, 0);
	gtk_box_pack_start (GTK_BOX (main_hbox), pd->preview, TRUE, TRUE, 0);
	gtk_widget_show (pd->preview);
	
	g_signal_connect_swapped (pd->preview, "invalidated", G_CALLBACK (preview_invalidated), pd);
	
	vbox = gtk_vbox_new (FALSE, 12);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
	gtk_container_add (GTK_CONTAINER(main_hbox), vbox);
	gtk_widget_show(vbox);
	
	graph = pd->graph = gtk_drawing_area_new();
	pd->graph_pixmap = NULL;
	gtk_widget_set_size_request (graph, GRAPH_WIDTH, GRAPH_HEIGHT);
	gtk_widget_set_events (graph, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | 
	                              GDK_POINTER_MOTION_HINT_MASK | GDK_ENTER_NOTIFY_MASK | 
	                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | 
	                              GDK_BUTTON1_MOTION_MASK);
	gtk_container_add (GTK_CONTAINER (vbox), graph);
	gtk_widget_show (graph);
	g_signal_connect (graph, "event", G_CALLBACK (graph_events), pd);
	
	hbox_buttons = gtk_hbox_new (FALSE, 12);
	gtk_container_set_border_width (GTK_CONTAINER (hbox_buttons), 12);
	gtk_container_add (GTK_CONTAINER(vbox), hbox_buttons);
	gtk_widget_show(hbox_buttons);
	
	preview_button = gtk_button_new_with_mnemonic ("HD _Preview");
	gtk_box_pack_start (GTK_BOX (hbox_buttons), preview_button, FALSE, FALSE, 0);
	gtk_widget_show (preview_button);
	g_signal_connect (preview_button, "clicked", G_CALLBACK (preview_hd), pd);
	
	preview_hd_checkbox = gtk_check_button_new_with_label("Always preview HD");
	gtk_box_pack_start (GTK_BOX (hbox_buttons), preview_hd_checkbox, FALSE, FALSE, 0);
	gtk_widget_show (preview_hd_checkbox);
	pd->do_preview_hd = FALSE;
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(preview_hd_checkbox), FALSE);
	g_signal_connect (preview_hd_checkbox, "toggled", G_CALLBACK (preview_hd_toggled), pd);
	
	gtk_widget_show(main_hbox);
	gtk_widget_show(dialog);
	fft_prepare(pd);
	histogram_generate(pd);
	wavelet_prepare(pd);
	gboolean run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
	if (run)
	{
		// set the region mode to actual writing
		gimp_pixel_rgn_init(&pd->region, pd->drawable, 0, 0, pd->image_width, pd->image_height, TRUE, TRUE);
		fft_apply(pd);
		gimp_pixel_rgn_set_rect(&pd->region, pd->img_pixels, 0, 0, pd->image_width, pd->image_height);
		// show the result
		gimp_drawable_flush(pd->drawable);
		gimp_drawable_merge_shadow(pd->drawable->drawable_id, TRUE);
		gimp_drawable_update(pd->drawable->drawable_id, pd->selection_offset_x, pd->selection_offset_y, pd->selection_width, pd->selection_height);
		gimp_displays_flush();
	}
	fft_destroy(pd);
	wavelet_destroy(pd);
	gtk_widget_destroy (dialog);
	return run;
}