Conversion of cv::Mat to GstBuffer

Michael Gruner michael.gruner at ridgerun.com
Thu Mar 14 21:21:52 UTC 2019


Can you run your app with warning logging enabled? Any of the following should work:

./app --gst-debug=2

Or

GST_DEBUG=2 ./app



> On Mar 14, 2019, at 2:17 PM, Saurabh Bora <saurabh9bora at gmail.com> wrote:
> 
> Hi Experts!, 
> 
> Thanks!! Michael, I tried the approach suggested by you and it works fine. I could verify the conversion of cv::Mat to GstBuffer and vice versa. On cv::write I get exact image that I had copied to GstBuffer.
> 
> However, I am unable to get correct image on autovidesink. With current code below, I see a skewed and distorted grayscale image. Note that I had to set width and height of videocaps on appsrc element as 133, 133 respectively, even though dimesnions of my input image is 134*134. If I set width and height as 134, 134 in videocaps, I see a black videosink.
> 
> //----------------------------------------------------------
> #include <gst/gst.h>
> #include <opencv2/opencv.hpp>
> #include <opencv2/highgui.hpp>
> 
> static GMainLoop *loop;
> 
> void buffer_destroy(gpointer data)
> {
> 	cv::Mat* done = (cv::Mat*)data;
> 	delete done;
> }
> 
> static void
> cb_need_data(GstElement *appsrc,
> 	guint       unused_size,
> 	gpointer    user_data)
> {
> 	static GstClockTime timestamp = 0;
> 	GstBuffer *buffer;
> 	GstFlowReturn ret;
> 	char *data1;
> 	GstMapInfo map;
> 
> 	cv::Mat* img = new cv::Mat(cv::imread("C:/Users/212730892.LOGON/Desktop/lena.jpg"));
> 
> 	std::cout << "cb_need_data called" << std::endl;
> 
> 	gsize sizeInBytes = img->total() * img->elemSize();
> 	std::cout << "Size getting copied: " << sizeInBytes << std::endl;
> 	buffer = gst_buffer_new_wrapped_full((GstMemoryFlags)0, (gpointer)(img->data), sizeInBytes, 0, sizeInBytes, (gpointer)img, (GDestroyNotify)buffer_destroy);
> 
> 	gsize bufferSize = gst_buffer_get_size(buffer);
> 	std::cout << "Buffer Size: " << bufferSize << std::endl;
> 
> 	//Convert GstBuffer back to cv::Mat, to check if data was correctly copied
> 	GstMapInfo info;
> 	gst_buffer_map(buffer, &info, GST_MAP_READ);
> 	cv::Mat img2(cv::Size(img->size().height, img->size().width), CV_8UC3, (char*)(info.data));
> 	cv::imwrite("C:/Users/212730892.LOGON/Desktop/dummy1.jpg", img2);
> 
> 	GST_BUFFER_OFFSET(buffer) = 0;
> 	GST_BUFFER_PTS(buffer) = timestamp;
> 	GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 30);
> 	//GST_BUFFER_DURATION(buffer) = GST_CLOCK_TIME_NONE;
> 
> 	timestamp += GST_BUFFER_DURATION(buffer);
> 
> 	g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
> 	gst_buffer_unref(buffer);
> 
> 	if (ret != GST_FLOW_OK) {
> 		/* something wrong, stop pushing */
> 		g_main_loop_quit(loop);
> 	}
> }
> 
> gint
> main(gint   argc,
> 	gchar *argv[])
> {
> 	GstElement *pipeline, *appsrc, *conv, *videosink;
> 
> 	/* init GStreamer */
> 	gst_init(&argc, &argv);
> 	loop = g_main_loop_new(NULL, FALSE);
> 
> 	/* setup pipeline */
> 	pipeline = gst_pipeline_new("pipeline");
> 	appsrc = gst_element_factory_make("appsrc", "source");
> 	conv = gst_element_factory_make("videoconvert", "conv");
> 	videosink = gst_element_factory_make("autovideosink", "videosink");
> 
> 	/* setup */
> 	g_object_set(G_OBJECT(appsrc), "caps",
> 		gst_caps_new_simple("video/x-raw",
> 			"format", G_TYPE_STRING, "RGB",
> 			"width", G_TYPE_INT, 133,
> 			"height", G_TYPE_INT, 133,
> 			"framerate", GST_TYPE_FRACTION, 1, 30,
> 			NULL), NULL);
> 	gst_bin_add_many(GST_BIN(pipeline), appsrc, conv, videosink, NULL);
> 	gst_element_link_many(appsrc, conv, videosink, NULL);
> 
> 	/* setup appsrc */
> 	g_object_set(G_OBJECT(appsrc),
> 		"stream-type", 0,
> 		"format", GST_FORMAT_TIME, NULL);
> 	g_signal_connect(appsrc, "need-data", G_CALLBACK(cb_need_data), NULL);
> 
> 	/* play */
> 	gst_element_set_state(pipeline, GST_STATE_PLAYING);
> 	g_main_loop_run(loop);
> 
> 	if (gst_element_set_state(pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)
> 	{
> 		g_printerr("Unable to set the pipeline to the playing state.\n");
> 	}
> 
> 	// Free resources
> 	gst_object_unref(GST_OBJECT(pipeline));
> 	g_main_loop_unref(loop);
> 
> 	return 0;
> }
> //-------------------------------------------------------------------------
> 
> I am new to gstreamer framework and it would be a great help if someone could point me in right direction.
> 
> Thanks and Regards,
> Saurabh Bora
> 
> 
> On Wed, Mar 13, 2019 at 11:48 PM Michael Gruner <michael.gruner at ridgerun.com <mailto:michael.gruner at ridgerun.com>> wrote:
> It looks like img2, being a local variable, ends  its life when “cb_need_data” ends. That leaves the pointer in “buffer” invalid. For this one you’re better off with the copy. 
> 
> Another option is to alloc img2 on the heap, and use gst_buffer_new_wrapped_full to carry img2 along with buffer and free then together.
> 
> Michael
> www.ridgerun.com <http://www.ridgerun.com/>
> 
> On Mar 13, 2019, at 10:51 AM, Saurabh Bora <saurabh9bora at gmail.com <mailto:saurabh9bora at gmail.com>> wrote:
> 
>> Hi Michael, 
>> 
>> Thanks! for your input. I tried what you suggested as below:
>> 
>> //---------------------------------------------------------------
>> #include <gst/gst.h>
>> #include <opencv2/opencv.hpp>
>> #include <opencv2/highgui.hpp>
>> #include <opencv2/imgproc/imgproc.hpp>
>> 
>> static GMainLoop *loop;
>> 
>> static void
>> cb_need_data(GstElement *appsrc,
>> 	guint       unused_size,
>> 	gpointer    user_data)
>> {
>> 	static GstClockTime timestamp = 0;
>> 	GstBuffer *buffer;
>> 	guint size, depth, height, width, step, channels;
>> 	GstFlowReturn ret;
>> 	guchar *data1;
>> 	GstMapInfo map;
>> 
>> 	cv::Mat img2;
>> 	cv::Mat img = cv::imread("C:/Users/212730892.LOGON/Desktop/lena.jpg");
>> 	cv::cvtColor(img, img2, cv::COLOR_BGR2RGB);
>> 
>> 	height = img.size().height;
>> 	width = img.size().width;
>> 	//step = img.widthStep;
>> 	channels = img.channels();
>> 	//depth = img->depth;
>> 	data1 = (guchar *)img.data;
>> 	size = height * width * channels * sizeof(guchar);
>> 
>> 	std::cout << "cb_need_data called" << std::endl;
>> 
>> 	buffer = gst_buffer_new_wrapped(img2.data, img2.total() * img2.elemSize());
>> 
>> 	GstMapInfo info;
>> 	gst_buffer_map(buffer, &info, GST_MAP_READ);
>> 	cv::Mat mat(height, width, CV_8UC3, info.data);
>> 	cv::imshow("Output", mat);
>> 	//gst_buffer_unmap(buffer, &info);
>> 
>> 	GST_BUFFER_PTS(buffer) = timestamp;
>> 	GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 2);
>> 
>> 	timestamp += GST_BUFFER_DURATION(buffer);
>> 
>> 	g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
>> 	//gst_buffer_unref(buffer);
>> 
>> 	if (ret != GST_FLOW_OK) {
>> 		/* something wrong, stop pushing */
>> 		g_main_loop_quit(loop);
>> 	}
>> }
>> 
>> gint
>> main(gint   argc,
>> 	gchar *argv[])
>> {
>> 	GstElement *pipeline, *appsrc, *conv, *videosink;
>> 
>> 	/* init GStreamer */
>> 	gst_init(&argc, &argv);
>> 	loop = g_main_loop_new(NULL, FALSE);
>> 
>> 	/* setup pipeline */
>> 	pipeline = gst_pipeline_new("pipeline");
>> 	appsrc = gst_element_factory_make("appsrc", "source");
>> 	conv = gst_element_factory_make("videoconvert", "conv");
>> 	videosink = gst_element_factory_make("autovideosink", "videosink");
>> 
>> 	/* setup */
>> 	g_object_set(G_OBJECT(appsrc), "caps",
>> 		gst_caps_new_simple("video/x-raw",
>> 			"format", G_TYPE_STRING, "RGB",
>> 			"width", G_TYPE_INT, 134,
>> 			"height", G_TYPE_INT, 134,
>> 			"framerate", GST_TYPE_FRACTION, 1, 1,
>> 			NULL), NULL);
>> 	gst_bin_add_many(GST_BIN(pipeline), appsrc, conv, videosink, NULL);
>> 	gst_element_link_many(appsrc, conv, videosink, NULL);
>> 	//g_object_set (videosink, "device", "/dev/video0", NULL);
>> 	/* setup appsrc */
>> 	g_object_set(G_OBJECT(appsrc),
>> 		"stream-type", 0,
>> 		"format", GST_FORMAT_TIME, NULL);
>> 	g_signal_connect(appsrc, "need-data", G_CALLBACK(cb_need_data), NULL);
>> 
>> 	/* play */
>> 	gst_element_set_state(pipeline, GST_STATE_PLAYING);
>> 	g_main_loop_run(loop);
>> 
>> 	if (gst_element_set_state(pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)
>> 	{
>> 		g_printerr("Unable to set the pipeline to the playing state.\n");
>> 	}
>> 
>> 	// Free resources
>> 	gst_object_unref(GST_OBJECT(pipeline));
>> 	g_main_loop_unref(loop);
>> 
>> 	return 0;
>> }
>> //----------------------------------------------------------------------------------------------
>> 
>> It still does not work as expected. It would be a great help if you could point out where I am going wrong.
>> Thanks!!
>> 
>> On Wed, Mar 13, 2019 at 7:40 PM Michael Gruner <michael.gruner at ridgerun.com <mailto:michael.gruner at ridgerun.com>> wrote:
>> Hi Saurabh
>> 
>> Yo may convert from a cv::Mat to a GstBuffer without a memory copy by using gst_buffer_new_wrapped:
>> 
>> https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstBuffer.html#gst-buffer-new-wrapped <https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstBuffer.html#gst-buffer-new-wrapped>
>> 
>> Something like: 
>> buffer = gst_buffer_new_wrapped (mat.data, mat.total()*mat.elemSize());
>> 
>> In order to make a cv::Mat from a GstBuffer you first need to take the data out of the buffer. 
>> 
>> GstMapInfo info;
>> gst_buffer_map (buffer, &info, GST_MAP_READ); // or write
>> mat = cv::Mat(height, width, CV_8C3, map.data); // change your format accordingly
>> ...
>> gst_buffer_unmap (buffer, &info);
>> 
>> Hope it helps.
>> 
>> Michael
>> www.ridgerun.com <http://www.ridgerun.com/>
>> 
>> On Mar 13, 2019, at 7:15 AM, Saurabh Bora <saurabh9bora at gmail.com <mailto:saurabh9bora at gmail.com>> wrote:
>> 
>>> Hi Experts!!, 
>>> 
>>> I am trying to read opencv images locally and push them into gstreamer pipeline. I am using appsrc element with "need-data" signal attached to call-back function.
>>> Please look at the code below. 
>>> 
>>> //------------------------------------------------------------------------------------------------------
>>> #include <gst/gst.h>
>>> #include <opencv2/opencv.hpp>
>>> #include <opencv2/highgui.hpp>
>>> 
>>> static GMainLoop *loop;
>>> 
>>> static void
>>> cb_need_data(GstElement *appsrc,
>>> 	guint       unused_size,
>>> 	gpointer    user_data)
>>> {
>>> 	static GstClockTime timestamp = 0;
>>> 	GstBuffer *buffer;
>>> 	guint size, depth, height, width, step, channels;
>>> 	GstFlowReturn ret;
>>> 	guchar *data1;
>>> 	GstMapInfo map;
>>> 
>>> 	cv::Mat img = cv::imread("C:/Users/212730892.LOGON/Desktop/lena.jpg");
>>> 	height = img.size().height;
>>> 	width = img.size().width;
>>> 	//step = img.widthStep;
>>> 	channels = img.channels();
>>> 	//depth = img->depth;
>>> 	data1 = (guchar *)img.data;
>>> 	size = height * width* channels;
>>> 
>>> 	// Copy cv::Mat to GstBuffer
>>> 	buffer = gst_buffer_new_allocate(NULL, size, NULL);
>>> 	gst_buffer_map(buffer, &map, GST_MAP_WRITE);
>>> 	memcpy((guchar *)map.data, data1, gst_buffer_get_size(buffer));
>>> 
>>> 	//Convert GstBuffer back to cv::Mat and write it, to check if data was correctly copied. //This is where I get to know that data is not correct.
>>> 	cv::Mat img2(cv::Size(134, 134), CV_8UC3, (char*)(buffer));
>>> 	cv::imwrite("C:/Users/212730892.LOGON/Desktop/dummy1.jpg", img2);
>>> 
>>> 	GST_BUFFER_PTS(buffer) = timestamp;
>>> 	GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 2);
>>> 
>>> 	timestamp += GST_BUFFER_DURATION(buffer);
>>> 
>>> 	g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
>>> 	//gst_buffer_unref(buffer);
>>> 
>>> 	if (ret != GST_FLOW_OK) {
>>> 		/* something wrong, stop pushing */
>>> 		g_main_loop_quit(loop);
>>> 	}
>>> }
>>> 
>>> gint
>>> main(gint   argc,
>>> 	gchar *argv[])
>>> {
>>> 	GstElement *pipeline, *appsrc, *conv, *videosink;
>>> 
>>> 	/* init GStreamer */
>>> 	gst_init(&argc, &argv);
>>> 	loop = g_main_loop_new(NULL, FALSE);
>>> 
>>> 	/* setup pipeline */
>>> 	pipeline = gst_pipeline_new("pipeline");
>>> 	appsrc = gst_element_factory_make("appsrc", "source");
>>> 	conv = gst_element_factory_make("videoconvert", "conv");
>>> 	videosink = gst_element_factory_make("autovideosink", "videosink");
>>> 
>>> 	/* setup */
>>> 	g_object_set(G_OBJECT(appsrc), "caps",
>>> 		gst_caps_new_simple("video/x-raw",
>>> 			"format", G_TYPE_STRING, "RGB",
>>> 			"width", G_TYPE_INT, 134,
>>> 			"height", G_TYPE_INT, 134,
>>> 			"framerate", GST_TYPE_FRACTION, 1, 1,
>>> 			NULL), NULL);
>>> 	gst_bin_add_many(GST_BIN(pipeline), appsrc, conv, videosink, NULL);
>>> 	gst_element_link_many(appsrc, conv, videosink, NULL);
>>> 	//g_object_set (videosink, "device", "/dev/video0", NULL);
>>> 	/* setup appsrc */
>>> 	g_object_set(G_OBJECT(appsrc),
>>> 		"stream-type", 0,
>>> 		"format", GST_FORMAT_TIME, NULL);
>>> 	g_signal_connect(appsrc, "need-data", G_CALLBACK(cb_need_data), NULL);
>>> 
>>> 	/* play */
>>> 	gst_element_set_state(pipeline, GST_STATE_PLAYING);
>>> 	g_main_loop_run(loop);
>>> 
>>> 	if (gst_element_set_state(pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)
>>> 	{
>>> 		g_printerr("Unable to set the pipeline to the playing state.\n");
>>> 	}
>>> 
>>> 	// Free resources
>>> 	gst_object_unref(GST_OBJECT(pipeline));
>>> 	g_main_loop_unref(loop);
>>> 
>>> 	return 0;
>>> }
>>> //-------------------------------------------------------------------------------------------------------------------------------------
>>> 
>>> In cb_need_data, the part where copying I am copying to GstBuffer seems to be wrong and it does not work as expected. 
>>> Can anyone, please help me understand, how can I copy the image data perfectly and pass it downstream to other elements without losing any data?
>>> 
>>> -- 
>>> Thanks and Regards,
>>> Saurabh Bora
>>> 
>>> PH NO : 7038166900
>>> EMAIL : saurabh9bora at gmail.com <mailto:saurabh9bora at gmail.com>
>>>              saurabh9bora at outlook.com <mailto:saurabh9bora at outlook.com>_______________________________________________
>>> gstreamer-devel mailing list
>>> gstreamer-devel at lists.freedesktop.org <mailto:gstreamer-devel at lists.freedesktop.org>
>>> https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel <https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel>_______________________________________________
>> gstreamer-devel mailing list
>> gstreamer-devel at lists.freedesktop.org <mailto:gstreamer-devel at lists.freedesktop.org>
>> https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel <https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel>
>> 
>> -- 
>> Thanks and Regards,
>> Saurabh Bora
>> 
>> PH NO : 7038166900
>> EMAIL : saurabh9bora at gmail.com <mailto:saurabh9bora at gmail.com>
>>              saurabh9bora at outlook.com <mailto:saurabh9bora at outlook.com>_______________________________________________
>> gstreamer-devel mailing list
>> gstreamer-devel at lists.freedesktop.org <mailto:gstreamer-devel at lists.freedesktop.org>
>> https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel <https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel>_______________________________________________
> gstreamer-devel mailing list
> gstreamer-devel at lists.freedesktop.org <mailto:gstreamer-devel at lists.freedesktop.org>
> https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel <https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel>
> 
> -- 
> Thanks and Regards,
> Saurabh Bora
> 
> PH NO : 7038166900
> EMAIL : saurabh9bora at gmail.com <mailto:saurabh9bora at gmail.com>
>              saurabh9bora at outlook.com <mailto:saurabh9bora at outlook.com>_______________________________________________
> gstreamer-devel mailing list
> gstreamer-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/gstreamer-devel/attachments/20190314/51153ed8/attachment-0001.html>


More information about the gstreamer-devel mailing list