Linux系统USB摄像头测试程序(四)_视频旋转及缩放

news2024/11/14 9:07:14

下面的程序实现了视频的旋转及缩放,窗口中点击鼠标左键视频向左旋转,点击鼠标右键视频向右旋转并且视频缩小了二分之一。程序中首先把yvyv422转换成了RGB24,然后利用opencv进行了旋转和缩放,其后用sdl2进行了渲染。使用了ffmpeg、sdl2、gtk、opencv四个开源组件,编译时请先安装他们,下面是完整的代码及编译命令。
 

//rotate_camera.c
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <zconf.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gtk-3.0/gtk/gtk.h>
#include <gdk/gdkx.h>

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>

#include <SDL2/SDL.h>

//dhl:gtk窗口组件
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *button1,*button2,*button3;
GtkWidget *text,*text1,*text2,*text3;
GtkTextBuffer *buffer,*buffer1,*buffer2,*buffer3;
GtkWidget *label1,*label2,*label3,*label_line;

//dhl:sdl窗口组件
SDL_Window *sdl_window; 
SDL_Renderer *sdl_renderer;
SDL_Texture *sdl_texture;


//dhl:消息缓存
char disp[2048]={0};
char temp[128]={0};

//dhl:获取窗口控件的值
int getWHR(char *device_name,char *win_size,int *win_width,int *win_height, char *frame_rate_t)
{
	GtkTextIter start,end;
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer1),&start,&end);
	GtkTextIter s=start,e=end;
	sprintf(device_name,"%s",gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer1),&s,&e,FALSE));
	
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer2),&start,&end);
	s=start,e=end;
	gchar *win_size1 = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer2),&s,&e,FALSE);
	sprintf(win_size,"%s",gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer2),&s,&e,FALSE));
    
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer3),&start,&end);
	s=start,e=end;
    sprintf(frame_rate_t,"%s",gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer3),&s,&e,FALSE));
    
    
    if (!strcmp(device_name,"")|| !strcmp(win_size,"")||!strcmp(frame_rate_t,"")) {
		GtkWidget * dialog= dialog = gtk_message_dialog_new (NULL,
                                 GTK_DIALOG_DESTROY_WITH_PARENT,
                                 GTK_MESSAGE_INFO,
                                 GTK_BUTTONS_CLOSE,
                                 "请输入摄像头设备名称、窗口尺寸、帧速率");
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		return -1;
		
	}
    
    sscanf(win_size1, "%dx%d", win_width, win_height);
	return 0;
}

//yuv422 转 RGB24(subfunction)
int convert_yuv_to_rgb_pixel(int y, int u, int v)
{
        unsigned int pixel32 = 0;
        unsigned char *pixel = (unsigned char *)&pixel32;
        int r, g, b;
        r = y + (1.370705 * (v-128));
        g = y - (0.698001 * (v-128)) - (0.337633 * (u-128));
        b = y + (1.732446 * (u-128));
        if(r > 255) r = 255;
        if(g > 255) g = 255;
        if(b > 255) b = 255;
        if(r < 0) r = 0;
        if(g < 0) g = 0;
        if(b < 0) b = 0;
        pixel[0] = r ;
        pixel[1] = g ;
        pixel[2] = b ;
        return pixel32;
}

//yuv422 转 RGB24
int convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height)
{
        unsigned int in, out = 0;
        unsigned int pixel_16;
        unsigned char pixel_24[3];
        unsigned int pixel32;
        int y0, u, y1, v;

        for(in = 0; in < width * height * 2; in += 4)
        {
                pixel_16 = yuv[in + 3] << 24 |
                           yuv[in + 2] << 16 |
                           yuv[in + 1] <<  8 |
                           yuv[in + 0];
                y0 = (pixel_16 & 0x000000ff);
                u  = (pixel_16 & 0x0000ff00) >>  8;
                y1 = (pixel_16 & 0x00ff0000) >> 16;
                v  = (pixel_16 & 0xff000000) >> 24;
                pixel32 = convert_yuv_to_rgb_pixel(y0, u, v);
                pixel_24[0] = (pixel32 & 0x000000ff);
                pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
                pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
                rgb[out++] = pixel_24[0];
                rgb[out++] = pixel_24[1];
                rgb[out++] = pixel_24[2];
                pixel32 = convert_yuv_to_rgb_pixel(y1, u, v);
                pixel_24[0] = (pixel32 & 0x000000ff);
                pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
                pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
                rgb[out++] = pixel_24[0];
                rgb[out++] = pixel_24[1];
                rgb[out++] = pixel_24[2];
        }
        return 0;

}


//dhl:发现摄像头
void button1_clicked(GtkWidget *widget, gpointer data)
{
	char camera_devices_name[128]={0};
	for (int loop=0;loop<10;loop++) {
		sprintf(camera_devices_name,"/dev/video%d",loop);
		int fd = open(camera_devices_name, O_RDWR);
		if(fd < 0)
		{
			printf("打开设备失败(%s)\n",camera_devices_name);
			close(fd);
			continue;
		}
	    close(fd);
		sprintf(temp,"发现摄像头:%s\n",camera_devices_name);
		strcat(disp,temp);
		gtk_text_buffer_set_text(buffer,disp,-1);
	}
	sprintf(disp,"%s","");
}

//dhl:查询配置信息
void button2_clicked(GtkWidget *widget, gpointer data)
{
	int line_num=0;
	gchar *device_name;
	GtkTextIter start,end;
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer1),&start,&end);
	const GtkTextIter s=start,e=end;
	device_name=gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer1),&s,&e,FALSE);
	if (!strcmp(device_name,""))
	{
		sprintf(temp,"请输入摄像头设备文件名\n");
		strcat(disp,temp);
		gtk_text_buffer_set_text(buffer,disp,-1);
		sprintf(disp,"%s","");
		return;
	}
		
		
	//dhl:查询摄像头支持的视频格式
	int fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		printf("打开设备失败\n");
		return;
	}

	struct v4l2_fmtdesc v4fmt;
	v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
	int i=0;

	while(1)
	{
		v4fmt.index = i++;
		int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
		if(ret < 0)
		{
			printf("获取摄像头格式失败");
			break;
		}
		printf("index=%d\n", v4fmt.index);
		printf("flags=%d\n", v4fmt.flags);
		printf("description=%s\n", v4fmt.description);
		unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
		printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
		printf("reserved=%d\n", v4fmt.reserved[0]);
		
		sprintf(temp,"摄像头支持的视频格式(%d)\n",i);
		strcat(disp,temp);
		
		sprintf(temp,"index=%d,", v4fmt.index);
		strcat(disp,temp);
		
		sprintf(temp,"flags=%d,", v4fmt.flags);
		strcat(disp,temp);
		
		sprintf(temp,"description=%s,", v4fmt.description);
		strcat(disp,temp);
		
		sprintf(temp,"pixelformat=%c%c%c%c,", p[0],p[1],p[2],p[3]);
		strcat(disp,temp);
		
		sprintf(temp,"reserved=%d\n", v4fmt.reserved[0]);
		strcat(disp,temp);
		
	}
	
	close(fd);
	
	//dhl:MJPG支持的所有分辨率如下
	fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return;
	}
	struct v4l2_frmsizeenum frmsize;
	frmsize.index = 0;
	frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("MJPEG格式支持所有分辨率如下:\n");
	//frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
	frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
	sprintf(temp,"MJPG支持的分辨率:\n");
	strcat(disp,temp);
	line_num=1;
	while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
		printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		frmsize.index++;
		if ((line_num % 5) != 0) { 
			sprintf(temp,"MJPEG frame_size<%d*%d>, ",frmsize.discrete.width,frmsize.discrete.height);
		}else {
			sprintf(temp,"MJPEG frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		}
		strcat(disp,temp);
		line_num++;
		if (line_num == 6) {
			line_num = 1;
		}
	}
	close(fd);
	sprintf(temp,"%s","\n");
	strcat(disp,temp);
	
	//dhl:YUV支持所有分辨率如下
	fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return;
	}
	//struct v4l2_frmsizeenum frmsize;
	frmsize.index = 0;
	frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("MJPEG格式支持所有分辨率如下:\n");
	frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
	//frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
	sprintf(temp,"YUV支持的分辨率:\n");
	strcat(disp,temp);
	line_num = 1;
	while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
		printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		frmsize.index++;
		if ((line_num % 5) != 0) { 
			sprintf(temp,"YUYV frame_size<%d*%d>, ",frmsize.discrete.width,frmsize.discrete.height);
		}else {
			sprintf(temp,"YUYV frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
			
		}
		strcat(disp,temp);
		line_num++;
		if (line_num == 6) {
			line_num = 1;
		}
	}
	
	close(fd);
	gtk_text_buffer_set_text(buffer,disp,-1);
	sprintf(disp,"%s","");
	
}

//dhl:视频预览并实现视频帧任意角度旋转
void button3_clicked(GtkWidget *widget, gpointer data)
{
	AVFormatContext *pFormatCtx;
    int             i, videoindex;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    int ret = -1;

    int win_width=0, win_height=0;
    gchar device_name[128];
    gchar win_size[128];
    gchar frame_rate_t[128];
    
    //dhl:获取gtk窗口数据
    int iRet=getWHR(device_name,win_size,&win_width,&win_height, frame_rate_t);
    if (iRet!=0) {
		return;
	}
    
    if (SDL_Init(SDL_INIT_VIDEO)) {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return;
    }else{
		
		printf("initialize SDL ok\n");
	}
    
    sdl_window = SDL_CreateWindow("视频预览"
			,SDL_WINDOWPOS_CENTERED
			,SDL_WINDOWPOS_CENTERED
			,win_width
			,win_height
			,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
    if(sdl_window == NULL){
        printf("SDL_window创建失败\n");
        return;
    }



	sdl_renderer = SDL_CreateRenderer(sdl_window
            ,-1
            ,SDL_RENDERER_ACCELERATED);
 
	/*dhl:旋转后图片的宽,高也变了,应根据新的尺寸创建纹理,否则图片变形
	sdl_texture = SDL_CreateTexture(sdl_renderer
			//,SDL_PIXELFORMAT_IYUV
			//,SDL_PIXELFORMAT_YUY2
			,SDL_PIXELFORMAT_RGB24
            ,SDL_TEXTUREACCESS_TARGET
            ,win_width
            ,win_height);
    */
    
    sdl_texture = SDL_CreateTexture(sdl_renderer
			//,SDL_PIXELFORMAT_IYUV
			//,SDL_PIXELFORMAT_YUY2
			,SDL_PIXELFORMAT_RGB24
            ,SDL_TEXTUREACCESS_TARGET
            ,390
            ,400);
    
    
	SDL_Rect sdl_rect;
    char *buffer_pixels = malloc(win_width*win_height*4);
	char *buffer_rgb = malloc(win_width*win_height*3);
    char *buffer_rgb_reversal = malloc(win_width*win_height*3);

    pFormatCtx = avformat_alloc_context();
    const AVInputFormat *ifmt = av_find_input_format("v4l2");
    
    AVDictionary *option =NULL;
    av_dict_set(&option,"video_size",win_size,0);
    av_dict_set(&option,"framerate",frame_rate_t,0);
    //av_dict_set(&option,"pixel_format","yuv420p12be",0); //指定格式
   
    avformat_open_input(&pFormatCtx, device_name, ifmt,&option);
    
    
    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("Couldn't find stream information.\n");
        return;
    }
    
    videoindex = -1;
    for(i = 0; i < pFormatCtx->nb_streams; i++){
        if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex = i;
            break;
        }
    }
    if(videoindex == -1)
    {
        printf("Didn't find a video stream.\n");
        return;
    }
    else{
        printf("Find a video stream:%d.\n", videoindex);
    }
    
    pCodec = (AVCodec*)avcodec_find_decoder(pFormatCtx->streams[videoindex]->codecpar->codec_id);
    if(pCodec == NULL)
    {
        printf("Codec not found.\n");
        return;
    }
    
    pCodecCtx = avcodec_alloc_context3(pCodec);
    avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
    printf("VideoStream:Frame.Width=%d,Height=%d\n",
           pCodecCtx->width, pCodecCtx->height);

    if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Could not open codec.\n");
        return;
    }
    
    AVPacket *pkt = av_packet_alloc();
    if(!pkt){
        printf("av_packet_alloc错误\n");
        return;
    }

    AVFrame *frame_yuyv422 = av_frame_alloc();
    if(!frame_yuyv422){
        printf("av_frame_alloc错误\n");
        return;
    }


	int frame_count = 0;	//dhl:记录获取的帧数
    SDL_Event event;
    int degrees=50;    		//dhl:旋转角度
    
    while (av_read_frame(pFormatCtx, pkt) >= 0 ) {
		 if(pkt->stream_index == videoindex){
            //dhl:发送给解码器
            if(avcodec_send_packet(pCodecCtx,pkt) != 0){
                printf("avcodec_send_packet error ...\n");
                break;
            }
            
     
            //dhl:从解码器中得到摄像头的原始视频帧
            int loop=0;
            while(avcodec_receive_frame(pCodecCtx,frame_yuyv422) == 0){
				frame_count++;
				for(int i = 0;i < frame_yuyv422->height;i++){
					
                    memcpy(buffer_pixels+i * frame_yuyv422->linesize[0],frame_yuyv422->data[0] + i * frame_yuyv422->linesize[0],frame_yuyv422->linesize[0]);
				}
				
				//dhl:转换 yuyv422 到 RGB24
				convert_yuv_to_rgb_buffer(buffer_pixels, buffer_rgb, win_width, win_height);
				
				int w_width=win_width;
				int h_height=win_height;
				
				//dhl:按指定度数旋转图片
				any_rotate_degrees(buffer_rgb, buffer_pixels,&w_width, &h_height,degrees);  
				
				
				//SDL_UpdateTexture(sdl_texture,NULL,buffer_pixels,win_width*2);
				SDL_UpdateTexture(sdl_texture,NULL,buffer_pixels,h_height*3);
				
				
				//dhl:将纹理数据拷贝给渲染器
				sdl_rect.x = 0;
				sdl_rect.y = 0;
				sdl_rect.w = h_height;
				sdl_rect.h = w_width;
				
				
				//dhl:先清空帧画面,再重新绘制
				SDL_RenderClear(sdl_renderer);
				SDL_RenderCopy(sdl_renderer,sdl_texture,NULL,&sdl_rect);
				//dhl:显示帧画面
				SDL_RenderPresent(sdl_renderer);
				//dhl:延时渲染
				//SDL_Delay(frame_rate);
				
			}
		}
		av_packet_unref(pkt);
		for (int ll=0;ll<10;ll++) {
			SDL_PollEvent(&event);
				
		}	
		if (event.type == SDL_QUIT) {
			 break;
		}else if (SDL_MOUSEBUTTONDOWN == event.type) {
				if(SDL_BUTTON_LEFT == event.button.button)
				{
					int px = event.button.x;
					int py = event.button.y;
					printf("left x, y %d %d ...............\n", px, py);	
					printf("degrees %d\n",degrees);
					degrees=degrees+1;  //dhl:按下鼠标左键向左旋转一度
					
				}
				else if(SDL_BUTTON_RIGHT == event.button.button)
				{
					int px = event.button.x;
					int py = event.button.y;
					printf("right x, y %d %d ...............\n", px, py);
					printf("degrees %d\n",degrees);
					degrees=degrees-1;	//dhl:按下鼠标右键向右旋转一度
				}
		} 
		av_packet_unref(pkt);		
	}
    
    sprintf(disp,"预览总帧数:%d",frame_count);
	gtk_text_buffer_set_text(buffer,disp,-1);
	sprintf(disp,"%s","");
    
    av_free(frame_yuyv422);
    av_packet_free(&pkt);
    avformat_close_input(&pFormatCtx);
    free(buffer_pixels);
    free(buffer_rgb);
    free(buffer_rgb_reversal);
    
    SDL_DestroyTexture(sdl_texture);
    SDL_DestroyRenderer(sdl_renderer);
	SDL_DestroyWindow(sdl_window );		
	SDL_Quit();
	return;
}


int main(int argc,char *argv[])
{
	gtk_init(&argc,&argv);
	avdevice_register_all();
    
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window),"USB摄像头测试程序(v1.0.20230728)");
    //gtk_window_set_resizable(GTK_WINDOW(window),FALSE);
    gtk_window_set_default_size(GTK_WINDOW(window),1280,800);
    gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
	
    fixed = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(window),fixed);
    
    text = gtk_text_view_new();
    buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
    gtk_text_buffer_set_text(buffer,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text,20,480);
    gtk_widget_set_size_request(text,1240,300);
    
    button1 = gtk_button_new_with_label("查询摄像头");
    gtk_fixed_put(GTK_FIXED(fixed),button1,320,14);
    gtk_widget_set_size_request(button1,100,20);
    
    label1 = gtk_label_new("摄像头:");
    gtk_fixed_put(GTK_FIXED(fixed),label1,440,14);
    gtk_widget_set_size_request(label1,50,30);
    
    text1 = gtk_text_view_new();
    buffer1=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
    gtk_text_buffer_set_text(buffer1,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text1,500,16);
    gtk_widget_set_size_request(text1,100,28);
    sprintf(disp,"/dev/video0");
	gtk_text_buffer_set_text(buffer1,disp,-1);
    
    button2 = gtk_button_new_with_label("查询配置");
    gtk_fixed_put(GTK_FIXED(fixed),button2,610,14);
    gtk_widget_set_size_request(button2,80,35);
    
    
    label2 = gtk_label_new("分辨率:");
    gtk_fixed_put(GTK_FIXED(fixed),label2,740,18);
    gtk_widget_set_size_request(label2,50,30);
    text2 = gtk_text_view_new();
    buffer2=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text2));
    gtk_text_buffer_set_text(buffer2,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text2,800,16);
    gtk_widget_set_size_request(text2,100,28);
    sprintf(disp,"640x480");
	gtk_text_buffer_set_text(buffer2,disp,-1);
    
    
    label3 = gtk_label_new("帧率:");
    gtk_fixed_put(GTK_FIXED(fixed),label3,930,18);
    gtk_widget_set_size_request(label3,50,30);
    text3 = gtk_text_view_new();
    buffer3=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text3));
    gtk_text_buffer_set_text(buffer3,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text3,980,16);
    gtk_widget_set_size_request(text3,100,28);
    sprintf(disp,"30");
	gtk_text_buffer_set_text(buffer3,disp,-1);
	
	label_line = gtk_label_new("______________________________________________________________________________________________________________________________");
    gtk_fixed_put(GTK_FIXED(fixed),label_line,320,45);
    gtk_widget_set_size_request(label_line,800,3);
    
	GtkWidget *labelChild;
	PangoFontDescription *font;
	int fontSize = 10;
	font = pango_font_description_from_string("Sans");				//"Sans"字体名
	pango_font_description_set_size(font, fontSize * PANGO_SCALE);	//设置字体大小
	labelChild = gtk_bin_get_child(GTK_WIDGET(button1));			//取出GtkButton里的label
	gtk_widget_modify_font(GTK_WIDGET(labelChild), font);			//设置label的字体, 这样这个GtkButton上面显示的字体就变了
	
	labelChild = gtk_bin_get_child(GTK_WIDGET(button2));			
	gtk_widget_modify_font(GTK_WIDGET(labelChild), font);			


    button3 = gtk_button_new_with_label("视频预览");
    gtk_fixed_put(GTK_FIXED(fixed),button3,320,80);
    gtk_widget_set_size_request(button3,80,20);
    
    g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
    g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
    g_signal_connect(G_OBJECT(button1), "clicked", G_CALLBACK(button1_clicked),NULL);
    g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_clicked),NULL);
    g_signal_connect(G_OBJECT(button3), "clicked", G_CALLBACK(button3_clicked),NULL);	
    
    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}



//edit_image.cpp
#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace cv;
using namespace std;

/**
 * dhl:图片编辑模块,利用opencv4实现旋转等功能
 **/


//dhl:any Rotate  degrees
int any_rotate(unsigned char* src_data, unsigned char* des_data,int *width, int *height,int degrees)
{
	int w_wight=*width;
	int h_height=*height;
	Mat dst_mat(h_height,w_wight,CV_8UC3);
	Mat des,m;
	memcpy(dst_mat.data,  src_data ,w_wight*h_height*3*sizeof(unsigned char));
	Point2f center = Point(dst_mat.cols / 2, dst_mat.rows / 2);
	double angle = degrees,scale=0.5;
	int w = dst_mat.cols, h = dst_mat.rows;
	int bound_w = (h * fabs(sin(angle * CV_PI / 180)) + w * fabs(cos(angle * CV_PI / 180))) * scale;
	int bound_h = (h * fabs(cos(angle * CV_PI / 180)) + w * fabs(sin(angle * CV_PI / 180))) * scale;
	m = getRotationMatrix2D(center, angle, scale);
	m.at<double>(0, 2) += (bound_w - dst_mat.cols) / 2;
	m.at<double>(1, 2) += (bound_h - dst_mat.rows) / 2;
	warpAffine(dst_mat,des,m,Size2i(bound_h,bound_w));
	memcpy(des_data,  des.data ,bound_h*bound_w*3);
	*width=bound_w;
	*height=bound_h;
	return 0;
}



extern "C" {
	
	//dhl:图片上下翻转
	int reversal_image(unsigned char* src_data, unsigned char* des_data,int data_len)
	{
		for (int i=0;i<data_len;i++) {
			des_data[i]=src_data[data_len-i-1];
		}
		return 0;
	}

	//dhl:left Rotate 90 degrees
	int left_rotate_image(unsigned char* src_data, unsigned char* des_data,int width, int height)
	{
	
	
		return 0;
	}

	//dhl:right Rotate 90 degrees
	int righht_rotate_image(unsigned char* src_data, unsigned char* des_data,int width, int height)
	{
	
	
		return 0;
	}
	
	//dhl:arbitrary degrees rotate
	int any_rotate_degrees(unsigned char* src_data, unsigned char* des_data,int *width, int *height,int degrees)
	{
		
		any_rotate(src_data, des_data,width, height,degrees);
		return 0;
	}
	
}

编译命令:gcc rotate_camera.c edit_image.cpp -o camera_rotate `pkg-config --cflags --libs  libavdevice libavfilter libavformat  libavcodec libavutil libpostproc  libswresample  libswscale` `pkg-config --cflags --libs sdl2` `pkg-config --cflags --libs gtk+-3.0` `pkg-config --cflags --libs opencv4`  -std=c++11  -lstdc++  -lpthread -lm -w
运行效果图:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/930255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

定制效果在线定制印刷系统源码 DIY在线定制系统源码 云印刷定制系统源码手机、PC端实时互通

支持各类产品的在线定制&#xff0c;无论是水杯雨伞U盘还是T恤衬衫四件套&#xff0c;均可轻松进行定制 独创制作间概念&#xff0c;同一套模板可以重复对应不同制作间 手机、PC端实时互通&#xff0c;客户可通过任意途径进行图片上传、编辑&#xff0c;一方修改另一方即时可见…

Linux操作系统--linux环境搭建(1)

在上一节课中,我们对Linux有了一个初步的认识,想要进一步的学习Linux,我们需要先把Linux需要的环境搭建出来。下面我们一起来看一下如何搭建Linux环境。 1.CentOS下载 官方下载地址: The CentOS Project 下载步骤如下所示: 选择内容 选择镜像 2.Vmware下载安装 一台电脑本…

【Kubernetes】Kubernetes 集群的组件介绍

基本概念控制平面组件&#xff08;Control Plane Components&#xff09;kube-apiserveretcdkube-schedulerkube-controller-managercloud-controller-manager&#xff08;可选&#xff09;kube-apiserver的重要性 ❗ Node 组件kubeletkube-proxy容器运行时&#xff08;Contain…

糟改押ong韵诗词,末三字改qiao ben zhong

题目给出诗词行的汉语拼音&#xff0c;糟改诗词押ong诗词行末三字“敲笨钟“。 (本笔记适合初通 Python 的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff…

Spark大数据分析与实战笔记(第一章 Scala语言基础-2)

文章目录 章节概要1.2 Scala的基础语法1.2.1 声明值和变量1.2.2 数据类型1.2.3 算术和操作符重载1.2.4 控制结构语句1.2.5 方法和函数 章节概要 Spark是专为大规模数据处理而设计的快速通用的计算引擎&#xff0c;它是由Scala语言开发实现的&#xff0c;关于大数据技术&#xf…

基于Java+SpringBoot+Vue前后端分离相亲网站系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

基于Java+SpringBoot+Vue前后端分离智能无人仓库管理设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

10 - 网络通信优化之通信协议:如何优化RPC网络通信?

微服务框架中 SpringCloud 和 Dubbo 的使用最为广泛&#xff0c;行业内也一直存在着对两者的比较&#xff0c;很多技术人会为这两个框架哪个更好而争辩。 我记得我们部门在搭建微服务框架时&#xff0c;也在技术选型上纠结良久&#xff0c;还曾一度有过激烈的讨论。当前 Sprin…

如何让ESP8266恢复出厂设置

1&#xff0c;安装python&#xff0c;2.7或者更高版本 2&#xff0c;运行&#xff1a;get-pip.py 3&#xff0c;运行&#xff1a;pip install esptool 4&#xff0c;运行的指令&#xff1a;esptool.exe --port COM3 erase_flash&#xff0c; 如果报错 就改成这样的命令试试&am…

算法通过村第三关-数组白银笔记|数组双指针

文章目录 前言什么是数组双指针数组中删除元素专题原地移除所有等值val的元素快慢双指针对撞双指针对撞覆盖 删除有序数组中的重复项 总结 前言 提示&#xff1a;世间从不缺少辉煌的花冠&#xff0c;缺少的是被花冠渲染的淡定。 什么是数组双指针 这是一种长期总结下来的思想&…

基于Java+SpringBoot+Vue前后端分离在线考试与学习交流网页平台设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

象小朋友学识字一样建构战略

战略认知派: 就像小孩子学识字的过程一样【安志强趣讲268期】 趣讲大白话&#xff1a;提高认知中长大 **************************** 基于认知心理学 战略的形成是认知构建的过程 最找连战略这个词都不一定知道 慢慢有些概念 慢慢形成整体的认知 战略认知派分两个分支 第一分支…

VMware安装OpenEuler

VMware安装OpenEuler 需要考虑的因素&#xff1a; 安装系统的用途&#xff1a;桌面开发、服务器、Linux学习使用&#xff1b;磁盘分区规划&#xff1a;最小120G&#xff0c;划分 boot, swap, 根分区等网络&#xff1a;是否对外提供服务&#xff08;NAT模式、桥接模式、仅主机…

ModaHub魔搭社区:WinPlan经营大脑数据采集

目录 WinPlan经营大脑数据采集介绍 WinPlan经营大脑数据采集模版 WinPlan经营大脑数据采集介绍 基于指标、维度来创建业务表单,通过业务表单的形式来采集实际数据,最终生成企业统一的经营数据库。由于需要客户创建数据采集模版(业务流程),然后可以基于各个业务模版作为…

WebSocket详解以及应用

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;websocket、网络、长连接、前端☀️每日 一言&#xff1a;任何一个你不喜欢而又离不开的地方&#xff0c;任何一种你不喜欢而又无法摆脱的生活&#xff0c;都是监狱&#xff01; 一、前言 我们在…

定州市社会保险待遇手机app-定州社会保障

社会保险的缴纳主要包括养老保险、医疗保险、生育保险&#xff0c;单位职工还有工伤保险和失业保险的缴纳&#xff0c;社会保险的缴纳一般都在当地人力资源和社会保障部门&#xff0c;在定州&#xff0c;社会保险登记的流程是怎样走的&#xff1f;下面由法律指南小编为大家解答…

改进YOLO系列:6.添加ECA注意力机制

添加ECA注意力机制 1. ECA注意力机制论文2. ECA注意力机制原理3. ECA注意力机制的配置3.1common.py配置3.2yolo.py配置3.3yaml文件配置1. ECA注意力机制论文 论文题目:ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 论文链接:ECA-N…

oracle 12c怎样修改varchar2允许的最大长度

12C单实例测试&#xff0c;varchar2在早期版本中最大长度限制为4000&#xff0c;当字段长度指定的比较长的时候会报错&#xff1a;ORA-00910: specified length too long for its datatype。 早期版本中虽然SQL数据类型限制为4000&#xff08;如表中的列的varchar2类型&#x…

抖音web频道爬虫

抖音web频道爬虫代码&#xff1a; <?php header(Content-Type:application/json; charsetutf-8);//抖音频道爬虫class DouyinChannel{private $app_id 1;private $spider_code 1;private $channels [["channel_name" > "热点","url"…

Java实现自定义注解

前言 &#xff08;1&#xff09;Java实现自定义注解其实很简单&#xff0c;跟类定义差不多&#xff0c;只是属性的定义可能跟我们平时定义的属性略有不同&#xff0c;这里会给大家详解&#xff0c;先来看代码&#xff1a; Target(ElementType.FIELD) Retention(RetentionPoli…