Skip to content

Instantly share code, notes, and snippets.

@angstyloop
Last active March 7, 2023 01:04
Show Gist options
  • Save angstyloop/9e8d808570eab3329471e67b71e01f41 to your computer and use it in GitHub Desktop.
Save angstyloop/9e8d808570eab3329471e67b71e01f41 to your computer and use it in GitHub Desktop.
Threshold an image with VIPS and a GTK slider UI.
/** threshold_scale.c
*
* Threshold an image with VIPS and a GTK slider UI.
*
* COMPILE
*
* gcc -Wall -o threshold_scale threshold_scale.c `pkg-config vips gtk+-3.0 --cflags --libs`
*
* EXAMPLE
*
* ./threshold_scale
*
*/
#include <vips/vips.h>
#include <gtk/gtk.h>
// The label text will match the current value of the slider.
static gchar label_text[4] = "0";
// Input and output image file paths
static gchar* input_image_path = "in.png";
static gchar* output_image_path = "out.png";
// The "value-change" signal handler
static void value_changed(GtkRange* range, gpointer window);
// The function called to update the image.
static void update_image(guint8 threshold);
// Define the image widget with a placeholder
static GtkWidget* image = NULL;
int main (int argc, char** argv) {
// Initialize VIPS.
if (VIPS_INIT(argv[0]))
vips_error_exit(NULL);
// Initialize GTK
gtk_init(&argc, &argv);
// Update the image for the first time, possibly creating it
update_image(0);
// Initialize the image
image = gtk_image_new_from_file("out.png");
// Create the window
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 50);
gtk_window_set_resizable (GTK_WINDOW(window), FALSE);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// Create the root widget, which contains all the other widgets
GtkWidget* root = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 20);
// Create the scale widget, a type of range widget.
GtkWidget* scale =
gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
gtk_widget_set_size_request(scale, 150, -1);
// Create label to show current value
GtkWidget* label = gtk_label_new("0");
// Pack the image widget, scale widget, and label into the root widget
gtk_box_pack_start(GTK_BOX(root), image, FALSE, FALSE, 20);
gtk_box_pack_start(GTK_BOX(root), scale, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(root), label, FALSE, FALSE, 20);
// Add the root widget to the window
gtk_container_add(GTK_CONTAINER(window), root);
// Connect signal handlers
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(scale, "value-changed", G_CALLBACK(value_changed), label);
// Show the window
gtk_widget_show_all(window);
// Start the GTK main loop
gtk_main();
return EXIT_SUCCESS;
}
static void value_changed(GtkRange* range, gpointer window) {
guint8 value = gtk_range_get_value(range);
value = value < 0 ? 0 : value > 100 ? 100 : value;
snprintf(label_text, 4, "%d", value);
gtk_label_set_text(GTK_LABEL(window), label_text);
update_image(value);
}
static void update_image(guint8 threshold) {
// Hang images off a dummy object, `context`
VipsObject* context = (VipsObject*) vips_image_new();
VipsImage** t = (VipsImage**) vips_object_local_array(context, 4);
// Threshold the image, producing a binary image with only black (#000) or
// white (#FFF) pixels
if (!(t[0] = vips_image_new_from_file(input_image_path,
"access", VIPS_ACCESS_SEQUENTIAL, NULL)) ||
vips_colourspace(t[0], &t[1], VIPS_INTERPRETATION_B_W, NULL) ||
vips_extract_band(t[1], &t[2], 0, "n", 1, NULL) ||
vips_moreeq_const1(t[2], &t[3], threshold, NULL) ||
vips_image_write_to_file(t[3], output_image_path, NULL)) {
g_object_unref(context);
vips_error_exit(NULL);
}
// Clean up the dummy object and all images.
g_object_unref(context);
// Update the image widget
gtk_image_set_from_file((GtkImage*) image, output_image_path);
}
@angstyloop
Copy link
Author

threshold_scale

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment