GTK renderiza los datos de imagen de la cámara

GTK renderiza los datos de imagen de la cámara

1. Introducción a GDK

  Graphics Drawing Kit (GDK, Graphics Drawing Kit) es una capa de dibujo de bajo nivel entre GTK+ y la interfaz de programación de aplicaciones (API, Application Program Interface) de un sistema operativo específico: la API para Linux es Xlib. Dado que no existe una interfaz directa entre GTK y la API de la máquina, la migración de GTK+ se convierte en una cuestión de migración de GLIB y GDK. GDK proporciona capacidades de dibujo de píxeles y capacidades de procesamiento y creación de ventanas de bajo nivel. Para muchas aplicaciones, es más conveniente usar widgets, pero si desea escribir una aplicación que simule un reloj en GTK+, es difícil hacerlo porque no tiene la capacidad de dibujar la esfera del reloj. Use el widget de área de dibujo con GDK en lugar del widget enlatado para dibujar cualquier cosa que necesite dibujar.

2. Subrutina de dibujo

  Escribir aplicaciones usando subrutinas GDK no es mucho mejor que usar Xlib directamente. Afortunadamente, GTK+ proporciona un widget, el widget del área de dibujo. Puede usarlo para crear aplicaciones que requieran dibujo manual. El widget de área de dibujo se puede usar como cualquier otro widget de GTK+. Además, usar este componente para crear aplicaciones que dependen de gráficos también es lo suficientemente flexible.La ventaja de este método es que GTK+ y GDK se pueden usar en la misma aplicación. GTK+ proporciona menús, barras de herramientas y otros componentes para admitir el dibujo dentro de los componentes del área de dibujo, mientras que GDK proporciona API para dibujar líneas, cuadros, celdas, círculos y otros gráficos.
  Cada subrutina GDK toma al menos dos parámetros: el área dibujable (GdkDrawable) y GDKGC. GdkDrawable representa el área que se puede dibujar en él, el color de la función del paquete GDKGC y la información de fuente y otra información de dibujo.

3. Programación de cámaras bajo Linux

  V4L2 es la abreviatura de Video for linux2, que es el controlador del kernel para dispositivos de video en Linux. En Linux, un dispositivo de video es un archivo de dispositivo, que se puede leer y escribir como un archivo ordinario. La cámara está en /dev/video*, y si solo hay un dispositivo de video, generalmente es /dev/video0.
  v4L2 es un marco de programación para dispositivos USB sin unidad uvc, que se utiliza principalmente para capturar cámaras USB, etc.

  • Pasos de programación del marco de la cámara

  (1) Abra el dispositivo de la cámara (/dev/video0, /dev/video1).
  (2) Configure el formato de imagen: VIDIOC_S_FMT (formato de captura de video, formato de datos de color de imagen, ancho y alto de imagen).
  (3) Solicite un búfer: VIDIOC_REQBUFS (número de búferes, método de asignación de búfer, formato de captura de video).
  (4) Asigne el búfer al espacio de proceso: VIDIOC_QUERYBUF (subíndice del búfer a mapear, método de mapeo de búfer, formato de captura de video).
  (5) Agregue el búfer a la cola: VIDIOC_QBUF (subíndice de búfer asignado, método de asignación de búfer, formato de captura de video).
  (6) Active la captura de cámara: VIDIOC_STREAMON (formato de captura de video). (7) Extraiga los datos de la imagen VIDIOC_DQBUF
  de la cola de adquisición y realice la representación de la imagen.

Referencia de ejemplo de programación de cámara: https://blog.csdn.net/weixin_44453694/article/details/126488841

  • Ejemplo de captura de cámara
#include <stdio.h>
#include <linux/videodev2.h> //摄像头头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#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
}

4. Representación GDK

#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <gtk/gtk.h>

#include <time.h>
#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;
}

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_44453694/article/details/127571695
Recomendado
Clasificación