Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

OpenCV - obrázek ve vlastním okně GTK

13.1.2021

Knihovna OpenCV obsahuje mnoho šikovných funkcí pro nejrůznější transformace a analýzu obrázků. Pokud jde o zobrazení výsledku, máme k disposici funkce cv::namedWindow a cv::imshow, kterými vytvoříme a zobrazíme jednoduché okno s obrázkem načteným do objektu cv::Mat

Když nastane potřeba zobrazit obrázek s kterým pracujeme pomocí OpenCV, musíme umět zobrazit ho v okně naší aplikace s různými dalšími ovládacími prvky. V tomto příspěvku si ukážeme jak zobrazit obrázek načtený do objektu cv::Mat v aplikaci vytvořené v GTK.

Kromě alespoň základní znalosti knihovny GTK (vytvoření aplikace, okna a dalších souvisejících) a OpenCV, což není předmětem tohoto příspěvku, je klíčové umět vytvořit objekt GdkPixbuf na základě objektu cv::Mat, do kterého máme načtený zvolený obrázek.

Do globálních proměnných si dáme point na objekt GdkPixbuf, který budeme potřebovat ve funkci obsluhující událost překreslení okna, resp widgetu GtkDrawingArea umístěného na okně aplikace.

GdkPixbuf* _pixbuf_orig = NULL; // globální proměnná

V hlavní funkci main si před vytvořením okna načteme požadovaný obrázek do objektu cv::Mat a na jeho základě vytvoříme objekt GdkPixBuff:

cv::Mat mat = cv::imread(IMAGE_FILE_PATH);
cv::Mat matrgb;
cvtColor(mat, matrgb, CV_BGR2RGB);
_pixbuf_orig = gdk_pixbuf_new_from_data((guchar*)matrgb.data,
	GDK_COLORSPACE_RGB, false, 8,
	matrgb.cols, matrgb.rows, matrgb.step,
	NULL, NULL);

Nyní už zbývá jen ve funkci on_draw, volané na signál "draw" přepočítat velikost vykreslovaného obrázku tak aby maximálně využil velikost okna samozřejmě při zachování poměru stran. Vše je v následujícím kompletním výpisu zdrojového kódu.

#include <gtk/gtk.h>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#define IMAGE_FILE_PATH "test.jpg"

GdkPixbuf* _pixbuf_orig = NULL;

void on_destroy(GtkWidget* w, gpointer* data);
void cleanup();
gboolean on_draw(GtkWidget* widget, cairo_t* cr, gpointer data);

gboolean on_draw(GtkWidget* widget, cairo_t* cr, gpointer data)
{
	if (NULL == _pixbuf_orig)
	{
		cairo_set_source_rgba(cr, 1.0, 1.0, 0.8, 1.0);
		cairo_paint(cr);
		return FALSE;
	}
	double pomer;
	double pocX = 0;
	double pocY = 0;
	int sirkacil = gtk_widget_get_allocated_width(widget);
	int vyskacil = (double)gtk_widget_get_allocated_height(widget);
	GdkPixbuf* pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
		sirkacil, vyskacil);
	if ( ((double)gdk_pixbuf_get_width(_pixbuf_orig) / (double)sirkacil) >
		 ((double)gdk_pixbuf_get_height(_pixbuf_orig) / (double)vyskacil) )
	{
		pomer = (double)sirkacil / gdk_pixbuf_get_width(_pixbuf_orig);
		vyskacil = ((double)gdk_pixbuf_get_height(_pixbuf_orig) * pomer);
		pocY = ((double)gtk_widget_get_allocated_height(widget) - vyskacil) / 2;
	}
	else
	{
		pomer = (double)vyskacil / gdk_pixbuf_get_height(_pixbuf_orig);
		sirkacil = ((double)gdk_pixbuf_get_width(_pixbuf_orig)*pomer);
		pocX = ((double)gtk_widget_get_allocated_width(widget) - sirkacil)/2;
	}
	gdk_pixbuf_scale(_pixbuf_orig, pb, 0, 0,
		gtk_widget_get_allocated_width(widget),
		gtk_widget_get_allocated_height(widget),
		0, 0, pomer, pomer,  GDK_INTERP_NEAREST);
	gdk_cairo_set_source_pixbuf(cr, pb, pocX, pocY);
	cairo_rectangle(cr, 0, 0,
		gtk_widget_get_allocated_width(widget) - pocX,
		gtk_widget_get_allocated_height(widget) - pocY);
	cairo_fill(cr);
	g_object_unref(pb);
	return FALSE;
}

void cleanup()
{
	if (_pixbuf_orig)
	{
		g_object_unref(_pixbuf_orig);
		_pixbuf_orig = NULL;
	}
}

void on_destroy(GtkWidget* w, gpointer* data)
{
	cleanup();
	gtk_main_quit();
}

int main(int argc, char** argv)
{
	GtkWidget* window;
	GtkWidget* drawing_area;

	gtk_init (&argc, &argv);
cv::Mat mat = cv::imread(IMAGE_FILE_PATH);
cv::Mat matrgb;
cvtColor(mat, matrgb, CV_BGR2RGB);
_pixbuf_orig = gdk_pixbuf_new_from_data((guchar*)matrgb.data,
	GDK_COLORSPACE_RGB, false, 8,
	matrgb.cols, matrgb.rows, matrgb.step,
	NULL, NULL);
	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	drawing_area = gtk_drawing_area_new();
	gtk_container_add(GTK_CONTAINER(window), drawing_area);
	g_signal_connect(drawing_area, "draw", G_CALLBACK(on_draw), NULL);
	g_signal_connect(window, "destroy", G_CALLBACK (on_destroy), NULL);
	gtk_window_set_default_size(GTK_WINDOW(window), 1027,768);
	gtk_window_set_position(GTK_WINDOW(window),
		GtkWindowPosition::GTK_WIN_POS_CENTER);
	gtk_widget_show(drawing_area);
	gtk_widget_show (window);
	gtk_main ();
	cleanup();
	return EXIT_SUCCESS;
}

Program lze sestavit překladačem GCC následujícím příkazem:

g++ gtk-opencv.cpp -g `pkg-config --cflags --libs gtk+-3.0` `pkg-config --cflags --libs opencv` -ogtk-opencv