• GTK渲染摄像头图像数据


    GTK渲染摄像头图像数据

    1.GDK简介

      绘图工具集(GDK,Graphics Drawing Kit)是在GTK+ 和特定操作系统的应用编程接口(API,Application Program Interface)之间的低级绘图制层—— Linux的API是Xlib。由于GTK和机器的API之间没有直接接口,移植GTK+就成为移植GLIB和GDK的问题。GDK提供像元的绘制能力以及低层的窗口建立和处理能力。对许多应用来说,使用构件时最方便的,但是,如果你想用GTK+编写模拟时钟的应用程序,由于它没有绘制时钟表面的能力,实现这样的任务就有困难了。使用带GDK的绘图区(drawing area)构件代替封装(canned)的构件,即可绘制出任何你需要绘制的内容。

    2.绘图子程序

      使用GDK子程序编写应用程序并不比直接使用Xlib好多少。幸运的是,GTK+提供一种构件,即绘图区(drawing area)构件。你可以用它来建立需要的手工绘图的应用。可以像使用其它GTK+构件一样使用绘图区构件。而且,使用该构件建立依赖图形的应用也足够灵活,这种方法的优点是可以在同一应用中使用GTK+和GDK。GTK+提供菜单、工具条和其它构件,支持在绘图区构件内进行绘图,而GDK则提供用于绘制线、框、像元、圆和其它图形的API。
      每个GDK子程序至少取两个参数:可绘制区(GdkDrawable)和GDKGC。GdkDrawable表示可以在他上面进行绘图的区域,GDKGC包函数颜色和字体信息以及其它绘图信息。

    3.Linux下摄像头编程

      V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。
      v4L2是针对uvc免驱usb设备的编程框架 ,主要用于采集usb摄像头等。

    • 摄像头框架编程步骤

      (1)打开摄像头设备(/dev/video0 、/dev/video1 )。
      (2)设置图像格式:VIDIOC_S_FMT(视频捕获格式、图像颜色数据格式、图像宽和高)。
      (3)申请缓冲区:VIDIOC_REQBUFS(缓冲区数量、缓冲映射方式、视频捕获格式)。
      (4)将缓冲区映射到进程空间:VIDIOC_QUERYBUF(要映射的缓冲区下标、缓冲映射方式、视频捕获格式)。
      (5)将缓冲区添加到队列中:VIDIOC_QBUF(映射的缓冲区下标、缓冲映射方式、视频捕获格式)。
      (6)开启摄像头采集:VIDIOC_STREAMON (视频捕获格式)。
      (7)从采集队列中取出图像数据VIDIOC_DQBUF,进行图像渲染。

    摄像头编程示例参考: https://blog.csdn.net/weixin_44453694/article/details/126488841

    • 摄像头采集示例
    #include 
    #include  //摄像头头文件
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "camera.h"
    /*摄像头初始化*/
    int Camera_Init(struct _CAMERA *video_info)
    {
    	int i=0;
    	/*1.打开摄像头设备*/
    	int fd=open(VIDEO_DEV,2);
    	if(fd<0)return -1;//摄像头打开失败
    	/*2.设置摄像头捕获格式*/
    	struct v4l2_format v4l2fmt;
    	memset(&v4l2fmt,0,sizeof(v4l2fmt));//初始化结构体
    	v4l2fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
    	v4l2fmt.fmt.pix.width=640;//图像宽度
    	v4l2fmt.fmt.pix.height=480;//图像高度
    	v4l2fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;//YUYV颜色编码格式
    	if(ioctl(fd,VIDIOC_S_FMT,&v4l2fmt))return -2;//设置格式失败
    	printf("采集图像大小:%d*%d\n",v4l2fmt.fmt.pix.width,v4l2fmt.fmt.pix.height);
    	video_info->image_w=v4l2fmt.fmt.pix.width;
    	video_info->image_h=v4l2fmt.fmt.pix.height;
    	/*3.申请缓冲区*/
    	struct v4l2_requestbuffers v4l2reqbuf;
        memset(&v4l2reqbuf,0,sizeof(v4l2reqbuf));//初始化结构体
    	v4l2reqbuf.count=4;//申请的缓冲区个数
    	v4l2reqbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
    	v4l2reqbuf.memory=V4L2_MEMORY_MMAP;//内存映射
    	if(ioctl(fd,VIDIOC_REQBUFS,&v4l2reqbuf))return -3;//申请缓冲区失败
    	printf("申请的缓冲区个数:%d\n",v4l2reqbuf.count);
    	/*4.将缓冲区映射到进程空间*/
    	struct v4l2_buffer v4l2buf;
    	memset(&v4l2buf,0,sizeof(v4l2buf));//初始化结构体
    	v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
    	v4l2buf.memory=V4L2_MEMORY_MMAP;//内存映射
    	for(i=0;i<v4l2reqbuf.count;i++)
    	{
    		v4l2buf.index=i;/*缓冲区下标*/
    		if(ioctl(fd,VIDIOC_QUERYBUF,&v4l2buf))return -4;//申请缓冲区失败
    		video_info->video_buff[i]=mmap(NULL,v4l2buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,v4l2buf.m.offset);
    		printf("video_buff[%d]=%p\n",i,video_info->video_buff[i]);
    	}
    	video_info->video_len=v4l2buf.length;//yuv图像数据字节数
    	printf("yuv_size=%d\n",video_info->video_len);
    	/*5.将映射的空间添加到采集队列*/
    	memset(&v4l2buf,0,sizeof(v4l2buf));//初始化结构体
    	v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
    	v4l2buf.memory=V4L2_MEMORY_MMAP;//内存映射
    	for(i=0;i<v4l2reqbuf.count;i++)
    	{
    		v4l2buf.index=i;/*缓冲区下标*/
    		if(ioctl(fd,VIDIOC_QBUF,&v4l2buf))return -5;//添加到采集队列失败
    	}
    	/*6.开启摄像头*/
    	int type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
    	if(ioctl(fd,VIDIOC_STREAMON,&type))return -6;//启动摄像头失败
        video_info->video_fd=fd;//摄像头描述符
    	return 0;//初始化成功返回0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    4.GDK渲染

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include "camera.h"
    struct _CAMERA video_info;/*设备结构体信息*/
    int width,height;//图像宽高
    guchar *rgbbuf=NULL;//保存rgb颜色数据
    GtkWidget *drawarea;
    
    int on_delete_event(GtkWidget *widget,GdkEvent *event,gpointer data)
    {  
    	/*释放资源*/
    	g_print("释放资源\n");
    	for(int i=0;i<4;i++)
    	{
    		munmap(video_info.video_buff[i],video_info.video_len);
    	}
    	free(rgbbuf);
    	close(video_info.video_fd); 
    	gtk_main_quit();
    	return FALSE;
    }
    
    
    int draw_image(GtkWidget *widget,GdkEvent *event,gpointer data)
    { 
     	gdk_draw_rgb_image(widget->window,widget->style->fg_gc[GTK_STATE_NORMAL],0,0,width,height,GDK_RGB_DITHER_NONE,rgbbuf,width*3);
    	return FALSE;
    }
    /*YUYV转RGB888*/
    void yuv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
    {
    	int x;
    	int z=0;
    	unsigned char *ptr = rgb_buffer;
    	unsigned char *yuyv= yuv_buffer;
    	for (x = 0; x < iWidth*iHeight; x++)
    	{
    		int r, g, b;
    		int y, u, v;
    		if (!z)
    		y = yuyv[0] << 8;
    		else
    		y = yuyv[2] << 8;
    		u = yuyv[1] - 128;
    		v = yuyv[3] - 128;
    		b = (y + (359 * v)) >> 8;
    		g = (y - (88 * u) - (183 * v)) >> 8;
    		r = (y + (454 * u)) >> 8;
    		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
    		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
    		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
    		if(z++)
    		{
    			z = 0;
    			yuyv += 4;
    		}
    	}
    }
    
    
    int read_data(GtkWidget * widget,GdkEvent * event,gpointer data)
    {    
    	/*读取摄像头图像数据*/
    	struct v4l2_buffer v4l2buf;
    	memset(&v4l2buf,0,sizeof(v4l2buf));
    	//初始化结构体
    	v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
    	v4l2buf.memory=V4L2_MEMORY_MMAP;//内存映射
    	if(ioctl(video_info.video_fd,VIDIOC_DQBUF,&v4l2buf)==-1)
    	{
    		printf("Dqbuf failed\n");
    		return FALSE;
    	}
    	//将yuyv转换为rgb24
    	yuv_to_rgb(video_info.video_buff[v4l2buf.index],rgbbuf,width,height);
    	/*保证线程安全,上锁*/
    	gdk_threads_enter();
    	gdk_draw_rgb_image(drawarea->window,drawarea->style->fg_gc[GTK_STATE_NORMAL],0,0,width,height,GDK_RGB_DITHER_NONE,rgbbuf,width*3);
    	/*保证线程安全,释放*/
    	gdk_threads_leave();
    	ioctl(video_info.video_fd,VIDIOC_QBUF,&v4l2buf);//将缓冲区添加回采集队列
    	return TRUE;
    }
    
    
    int main (int argc,char *argv[])
    {
    	/*摄像头初始化*/
    	int ret=Camera_Init(&video_info);
    	if(ret)
    	{
    		printf("摄像头打开失败ret=%d\n",ret);
    		return 0;
    	}
    	/*图像宽高*/
    	width=video_info.image_w;
    	height=video_info.image_h;
    	rgbbuf=malloc(width*height*3);
    	/* init Gtk */
    	GtkWidget *window;
    	/*gtk初始化*/
    	gtk_init(&argc,&argv);
    	/*创建窗口*/
    	window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    	gtk_window_set_title(GTK_WINDOW(window),"摄像头采集");
    	/*固定窗口大小*/
    	gtk_window_set_resizable (GTK_WINDOW(window),FALSE);
    	
    	/*连接信号*/
    	g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(on_delete_event),NULL);
    
    	/*初始化gdk的rgb*/
    	gdk_rgb_init();
    	gtk_widget_push_visual(gdk_rgb_get_visual());
    	gtk_widget_push_colormap(gdk_rgb_get_cmap());
    	/*创建绘图区域*/
    	drawarea=gtk_drawing_area_new();
    	gtk_widget_pop_visual();
    	gtk_widget_pop_colormap();
    	
    	gtk_container_add(GTK_CONTAINER(window),drawarea);
    	g_signal_connect(G_OBJECT(drawarea),"expose_event",G_CALLBACK(draw_image),NULL);
    	guint id=gtk_idle_add((GtkFunction)read_data,NULL);
    	/*设置窗口大小*/
    	gtk_widget_set_size_request(GTK_WIDGET(drawarea),width,height);
    	gtk_widget_show_all(window);
    	gtk_main();
    	gdk_threads_leave();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    在这里插入图片描述

  • 相关阅读:
    设计模式-行为型模式-中介者模式
    数据库的备份和恢复
    SpringMVC基础篇(一)
    音频频谱显示-显示音频文件静态频谱图(一)
    LEADTOOLS 入门教程: 使用 AWS Lambda 转换文档 - C# .NET Core
    C++ 求幂函数pow()的输出问题
    【.net core】yisha框架单页面双列表联动效果示例
    zabbix邮件报警和微信报警
    Docker常用命令
    每日三题 9.28
  • 原文地址:https://blog.csdn.net/weixin_44453694/article/details/127571695