0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux开发_BMP图片编程(翻转、添加水印)

DS小龙哥-嵌入式技术 2022-09-17 15:33 次阅读

【摘要】 接收BMP图片编程,完成BMP图片翻转、水印添加等知识点。

一、摄像头练习

1.​改进拍照程序,至少BMP图片数据要正常,还可以将.c文件分隔成3个.c文件。

24位的真彩色BMP图片的构造:分为3个部分。

(1)​存放BMP头数据—主要存放BMP属性,RGB数据偏移量

(2)​存放BMP信息数据—存放宽度、高度、颜色位数

(3)​存放BMP图片源数据—RGB888

注意:

(1)​BMP图片一行(宽度)的数据必须是4的倍数。

(2)​BMP图片的源数据是从下到上,从左到右

BMP、JPG、PNG、MP4、MP3

2.​课后扩展知识: 拍照图片上加水印。比如:时间水印、或者自己的名字

(1)​第一种图片水印: 图片重叠。

(2)​在控制台终端屏幕上使用*号打印自己名字。

要求:

(1)​添加文字水印和数字水印。比如: 添加自己的名字和日期

(2)​实现任意位置添加任意的水印。

(3)​实现缩放功能,将图片可以任意放大和缩小。

3.​精简远程摄像头监控的代码文件。

将网页监控相关的代码文件单独提取出来进行编译,实现远程监控的效果。

使用gcc单独编译实现。(uvc)

分析: 程序是依靠Makefile文件已经编译。首先从Makefile文件入手。

主要修改的文件:mjpg_streamer.c

1.​改进拍照程序,至少BMP图片数据要正常,还可以将.c文件分隔成3个.c文件。

24位的真彩色BMP图片的构造:分为3个部分。

(1)​存放BMP头数据—主要存放BMP属性,RGB数据偏移量

(2)​存放BMP信息数据—存放宽度、高度、颜色位数

(3)​存放BMP图片源数据—RGB888

注意:

(1)​BMP图片一行(宽度)的数据必须是4的倍数。

(2)​BMP图片的源数据是从下到上,从左到右

BMP、JPG、PNG、MP4、MP3

2.​课后扩展知识: 拍照图片上加水印。比如:时间水印、或者自己的名字

(1)​第一种图片水印: 图片重叠。

(2)​在控制台终端屏幕上使用*号打印自己名字。

pYYBAGMlP4mAUtE9AACFGMXStLI331.png

要求:

(1)​添加文字水印和数字水印。比如: 添加自己的名字和日期

(2)​实现任意位置添加任意的水印。

(3)​实现缩放功能,将图片可以任意放大和缩小。

3.​精简远程摄像头监控的代码文件。

将网页监控相关的代码文件单独提取出来进行编译,实现远程监控的效果。

使用gcc单独编译实现。(uvc)

分析: 程序是依靠Makefile文件已经编译。首先从Makefile文件入手。

任务2:汉字库的制作与使用

poYBAGMlP4mABBmSAAB9DhpyxMg380.png

需要解决的问题:

1.​如何从汉字库里提取自己想要的点阵数据 。比如: 万邦易嵌

汉字库编码还是GBK编码: 有特定的公式可以计算点阵码的位置。

2.​如何区分中文还有英文

数据大于0x80就是中文,其他就是英文数据。 变量必须是: 无符号类型

注意: 写代码需要在中文编码的情况下编写!

3.​英文数据该如何取模?

从空格开始到~号结束 ,连续95个数据。

2.1 BMP图片缩放

#include 
#include 

int PicZoom(unsigned char *s_buff,unsigned int s_width,unsigned int s_height,unsigned char *buff,unsigned int width,unsigned int height);
void *my_memcpy(void *v_dst,const void *v_src,unsigned char c);

#pragma pack(1)   /* 必须在结构体定义之前使用,这是为了让结构体中各成员按1字节对齐 */
/*需要文件信息头:14个字节 */
struct BITMAPFILEHEADER
{
	unsigned short bfType;      //保存图片类似。 'BM'
	unsigned long  bfSize;      //图片的大小
	unsigned short bfReserved1;
	unsigned short bfReserved2;
	unsigned long  bfOffBits;  //RGB数据偏移地址
};

/* 位图信息头 */
struct BITMAPINFOHEADER { /* bmih */
	unsigned long  biSize;      //结构体大小
	unsigned long  biWidth;		//宽度
	unsigned long  biHeight;	//高度
	unsigned short biPlanes;
	unsigned short biBitCount;	//颜色位数
	unsigned long  biCompression;
	unsigned long  biSizeImage;
	unsigned long  biXPelsPerMeter;
	unsigned long  biYPelsPerMeter;
	unsigned long  biClrUsed;
	unsigned long  biClrImportant;
};

#define NEW_FILE_NAME "new.bmp" //缩放后的新图片名称
#define SRC_FILE_NAME "src.bmp" //源图片名称
/*
图片放大与缩小示例
*/
int main()
{
	struct BITMAPFILEHEADER src_head;  //源文件头数据
	struct BITMAPINFOHEADER src_info;  //源文件参数结构
	struct BITMAPFILEHEADER new_head;  //新文件头数据
	struct BITMAPINFOHEADER new_info;  //新文件参数结构
	unsigned int new_Width;			   //缩放后的宽度
	unsigned int new_Height;		   //缩放后的高度
	unsigned char *new_buff;           //存放新图片的数据
	unsigned char *src_buff;           //存放源图片的数据
	unsigned int cnt=0;

	/*1. 打开图片文件*/
	FILE *src_file=fopen(SRC_FILE_NAME,"rb");
	FILE *new_file=fopen(NEW_FILE_NAME,"wb");
	if(src_file==NULL||new_file==NULL)
	{
		printf("%s 源文件打开失败!\r\n",SRC_FILE_NAME);
		return;
	}
	
	/*2. 读取源图片参数*/
	fread(&src_head,sizeof(struct BITMAPFILEHEADER),1,src_file);
	fread(&src_info,sizeof(struct BITMAPINFOHEADER),1,src_file);
	printf("源图片尺寸:w=%d h=%d\r\n",src_info.biWidth,src_info.biHeight);

	/*3. 获取新图片的尺寸*/
	printf("输入新图片的宽度: ");
	scanf("%d",&new_Width);
	printf("输入新图片的高度: ");
	scanf("%d",&new_Height);
	printf("新图片尺寸:w=%d h=%d\r\n",new_Width,new_Height);

	/*4. 申请存放图片数据的空间*/
	src_buff=malloc(src_info.biWidth*src_info.biHeight*3);
	new_buff=malloc(new_Width*new_Height*3);
	if(new_buff==NULL||src_buff==NULL)
	{
		printf("malloc申请空间失败!\r\n");
		return -1;
	}

	/*5. 读取源图片RGB数据*/
	fseek(src_file,src_head.bfOffBits,SEEK_SET); //移动文件指针到RGB数据位置
	fread(src_buff,1,src_info.biWidth*src_info.biHeight*3,src_file); //读取源数据

	/*6. 缩放图片*/
	if(PicZoom(src_buff,src_info.biWidth,src_info.biHeight,new_buff,new_Width,new_Height))
	{
		printf("图片缩放处理失败!\r\n");
		return -1;
	}

	/*7. 写入新图片数据*/
	//填充文件头
	memset(&new_head,0,sizeof(struct BITMAPFILEHEADER));
	new_head.bfType=0x4d42;
	new_head.bfSize=54+new_Width*new_Height*3;
	new_head.bfOffBits=54;
	//填充文件参数
	memset(&new_info,0,sizeof(struct BITMAPINFOHEADER));
	new_info.biSize=sizeof(struct BITMAPINFOHEADER);
	new_info.biWidth=new_Width;
	new_info.biHeight=new_Height;
	new_info.biPlanes=1;
	new_info.biBitCount=24;
	//写入文件数据
	fwrite(&new_head,sizeof(struct BITMAPFILEHEADER),1,new_file);
	fwrite(&new_info,sizeof(struct BITMAPINFOHEADER),1,new_file);
	fseek(new_file,new_head.bfOffBits,SEEK_SET); //移动文件指针到RGB数据位置
	cnt=fwrite(new_buff,1,new_info.biWidth*new_info.biHeight*3,new_file); //写数据

	/*8. 关闭图片文件*/
	fclose(new_file);
	fclose(src_file);

	printf("%s 新图片创建成功! 路径:程序运行路径下\r\n",NEW_FILE_NAME);
	return 0;
}
/**********************************************************************
* 函数名称: PicZoom
* 功能描述: 近邻取样插值方法缩放图片
*            注意该函数会分配内存来存放缩放后的图片,用完后要用free函数释放掉
*            "近邻取样插值"的原理请参考网友"lantianyu520"所著的"图像缩放算法"
* 输入参数:  ptOriginPic - 内含原始图片的象素数据
*             ptZoomPic    - 内含缩放后的图片的象素数据
* 输出参数: 无
* 返 回 值: 0 - 成功, 其他值 - 失败
***********************************************************************/
int PicZoom(unsigned char *ptOriginPic_aucPixelDatas,unsigned int ptOriginPic_iWidth,unsigned int ptOriginPic_iHeight,unsigned char *ptZoomPic_aucPixelDatas,unsigned int ptZoomPic_iWidth,unsigned int ptZoomPic_iHeight)
{
	unsigned int ptOriginPic_iLineBytes=ptOriginPic_iWidth*3; //一行的字节数
	unsigned int ptZoomPic_iLineBytes=ptZoomPic_iWidth*3;  //一行的字节数

	unsigned long dwDstWidth=ptZoomPic_iWidth;
	unsigned long* pdwSrcXTable;
	unsigned long x;
	unsigned long y;
	unsigned long dwSrcY;
	unsigned char *pucDest;
	unsigned char *pucSrc;
	unsigned long dwPixelBytes=3; //像素字节
	pdwSrcXTable=malloc(sizeof(unsigned long) * dwDstWidth);
	if(NULL==pdwSrcXTable)
	{
		return -1;
	}

	for(x=0; x < dwDstWidth; x++)//生成表 pdwSrcXTable
	{
		pdwSrcXTable[x]=(x*ptOriginPic_iWidth/ptZoomPic_iWidth);
	}

	for(y=0; y < ptZoomPic_iHeight; y++)
	{
		dwSrcY=(y * ptOriginPic_iHeight/ptZoomPic_iHeight);

		pucDest=ptZoomPic_aucPixelDatas + y * ptZoomPic_iLineBytes;
		pucSrc=ptOriginPic_aucPixelDatas+dwSrcY * ptOriginPic_iLineBytes;

		for(x=0; x ;>

2.2 BMP图片添加水印

#include "savebmp.h"
#include "yuvtorgb.h"

/* 图片的象素数据 */
typedef struct PixelDatas {
	int iWidth;      /* 宽度: 一行有多少个象素 */
	int iHeight;     /* 高度: 一列有多少个象素 */
	int iBpp;        /* 一个象素用多少位来表示 */
	int iLineBytes;  /* 一行数据有多少字节 */
	int iTotalBytes; /* 所有字节数 */ 
	unsigned char *VideoBuf; //存放一帧摄像头的数据
	//指向了存放摄像头数据的空间地址
}T_PixelDatas;

T_PixelDatas Pixedata; //存放实际的图像数据

/*
		USB摄像头相关参数定义
*/
struct v4l2_buffer tV4l2Buf;
int iFd;
int ListNum;
unsigned char* pucVideBuf[4];  // 视频BUFF空间地址
void camera_pthread(void);

int main(int argc ,char *argv[])
{
	if(argc!=2)
	{
		printf("./app /dev/videoX\n");
		return -1;
	}
	
	camera_init(argv[1]);  //摄像头设备初始化

	//开始采集摄像头数据,并编码保存为BMP图片
	camera_pthread();
	return 0;
}
//摄像头设备的初始化
int camera_init(char *video)
{
	int i=0;
	int cnt=0;
	//定义摄像头驱动的BUF的功能捕获视频
	int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	
	/* 1、打开视频设备 */
	iFd = open(video,O_RDWR);
	if(iFd < 0)
	{
		printf("摄像头设备打开失败!\n");
		return 0;
	}
	
	struct v4l2_format  tV4l2Fmt;
	
	/* 2、 VIDIOC_S_FMT 设置摄像头使用哪种格式 */
	memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
	tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获

	//设置摄像头输出的图像格式
    tV4l2Fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;

	/*设置输出的尺寸*/
	tV4l2Fmt.fmt.pix.width       = 640;
	tV4l2Fmt.fmt.pix.height      = 480;
	tV4l2Fmt.fmt.pix.field       = V4L2_FIELD_ANY;
	
    //VIDIOC_S_FMT 设置摄像头的输出参数
    ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt); 

	//打印摄像头实际的输出参数
	printf("Support Format:%d\n",tV4l2Fmt.fmt.pix.pixelformat);
	printf("Support width:%d\n",tV4l2Fmt.fmt.pix.width);
	printf("Support height:%d\n",tV4l2Fmt.fmt.pix.height);

	/* 3、VIDIOC_REQBUFS  申请buffer */
	/* 初始化Pixedata结构体,为转化做准备 */
	Pixedata.iBpp =24;								
	//高度 和宽度的赋值
	Pixedata.iHeight = tV4l2Fmt.fmt.pix.height;
	Pixedata.iWidth = tV4l2Fmt.fmt.pix.width;

	//一行所需要的字节数
	Pixedata.iLineBytes = Pixedata.iWidth*Pixedata.iBpp/8;
	//一帧图像的字节数
	Pixedata.iTotalBytes = Pixedata.iLineBytes * Pixedata.iHeight;
	
	Pixedata.VideoBuf=malloc(Pixedata.iTotalBytes); //申请存放图片数据空间

	//v412请求命令
	struct v4l2_requestbuffers tV4l2ReqBuffs;
	
	memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));

	/* 分配4个buffer:实际上由VIDIOC_REQBUFS获取到的信息来决定 */
	tV4l2ReqBuffs.count   = 4; /*在内核空间里开辟4个空间*/
	
	/*支持视频捕获功能*/
	tV4l2ReqBuffs.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	
	/* 表示申请的缓冲是支持MMAP(内存映射) */
	tV4l2ReqBuffs.memory  = V4L2_MEMORY_MMAP;
	
	/* 为分配buffer做准备 */
	ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
	
	for (i = 0; i < tV4l2ReqBuffs.count; i++) 
	{
		memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
		tV4l2Buf.index = i; // 0  1 2 3
		tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;  /*支持视频捕获*/
		tV4l2Buf.memory = V4L2_MEMORY_MMAP;  /*支持内存映射*/

		/* 6、VIDIOC_QUERYBUF 确定每一个buffer的信息 并且 mmap */
		ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
		//映射空间地址
		pucVideBuf[i] = mmap(   	  /*返回用户空间的地址*/
		          0,   				  /*表示系统自己制定地址*/
				  tV4l2Buf.length,   /*映射的长度*/
				  PROT_READ,          /*空间数据只读*/
				  MAP_SHARED,         /*空间支持共享*/
				  iFd,                /*将要映射的文件描述符*/
				  tV4l2Buf.m.offset  /*从哪个位置开始映射,表示起始位置*/
				  );
		printf("mmap %d addr:%p\n",i,pucVideBuf[i]);
	}

	/* 4、VIDIOC_QBUF  放入队列*/
    for (i = 0; i 在LCD上显示:rgb888 */
	initLut();
	printf("开始采集数据.......\n");
//	while(1)
//	{
		FD_ZERO(&readfds);
		FD_SET(iFd,&readfds);
		select(iFd+1,&readfds,NULL,NULL,NULL);  /*检测文件描述符是否发生了读写事件*/
	  
		memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
		tV4l2Buf.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE; //类型
		tV4l2Buf.memory  = V4L2_MEMORY_MMAP; //存储空间类型

		/* 9、VIDIOC_DQBUF    从队列中取出 */
		error = ioctl(iFd, VIDIOC_DQBUF, &tV4l2Buf); //取出一帧数据
		ListNum = tV4l2Buf.index; //索引编号   0
		
		//将YUV转换为RGB
		Pyuv422torgb32(pucVideBuf[ListNum],Pixedata.iWidth,Pixedata.iHeight,Pixedata.VideoBuf);
		
		//保存BMP
		save_bmp(Pixedata.VideoBuf,Pixedata.iWidth,Pixedata.iHeight); 
		
		memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
		tV4l2Buf.index  = ListNum;
		tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		tV4l2Buf.memory = V4L2_MEMORY_MMAP;
		error = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);  /*将缓冲区再次放入队列*/
//	}
};>
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 摄像头
    +关注

    关注

    59

    文章

    4791

    浏览量

    95248
  • BMP
    BMP
    +关注

    关注

    0

    文章

    48

    浏览量

    17024
收藏 人收藏

    评论

    相关推荐

    Linux应用编程的基本概念

    Linux应用编程涉及到在Linux环境下开发和运行应用程序的一系列概念。以下是一些涵盖Linux应用
    的头像 发表于 10-24 17:19 159次阅读

    基于ArkTS语言的OpenHarmony APP应用开发图片处理

    (),rotate()接口实现对图片的缩放,裁剪,旋转功能。案例说明:发表评价页面点击添加图片/照片,页面跳转到图片选择页面。进入图片选择页
    的头像 发表于 09-20 08:07 339次阅读
    基于ArkTS语言的OpenHarmony APP应用<b class='flag-5'>开发</b>:<b class='flag-5'>图片</b>处理

    linux开发板与树莓派的区别

    操作系统的微型计算机,主要用于教育、编程、媒体播放等领域。 硬件配置 Linux开发板:Linux开发板的硬件配置因厂商和型号而异,通常包括
    的头像 发表于 08-30 15:34 620次阅读

    linux开发板和单片机开发的区别

    、PIC等,处理能力和内存容量相对较低。 操作系统 Linux开发板通常使用Linux操作系统,具有丰富的软件资源和开发工具。单片机开发则通
    的头像 发表于 08-30 15:30 555次阅读

    Linux 驱动开发与应用开发,你知道多少?

    一、Linux驱动开发与应用开发的区别开发层次不同:Linux驱动开发主要是针对硬件设备进行
    的头像 发表于 08-30 12:16 494次阅读
    <b class='flag-5'>Linux</b> 驱动<b class='flag-5'>开发</b>与应用<b class='flag-5'>开发</b>,你知道多少?

    如何向Buildroot内添加外部APP

    Linux项目开发,如何在BuildRoot框架内添加外部APP呢?本文以quect-CM拨号软件为例,做开发演示。
    的头像 发表于 07-22 17:48 283次阅读
    如何向Buildroot内<b class='flag-5'>添加</b>外部APP

    百度搜索重磅推出AI图片助手

    近日,百度搜索全新上线了一款名为“百度AI图片助手”的智能工具,该工具赋予了用户一键处理图片的强大能力。无论是搜索中的图片还是用户自主上传的图片,均可轻松实现涂抹消除、去
    的头像 发表于 05-06 10:20 1243次阅读

    请问解码bmp图片时是否可以使用dma2d功能 ?

    请教下,解码bmp图片时是否可以使用dma2d功能 ? 具体描述如下: 没有使用 emWin 就是先读取整副 bmp 的数据,之后解码暂存,之后使用 dma2d 功能显示, 有没有这个必要或者这个能否能否实现啊,如果确定
    发表于 04-30 06:39

    使用emwin解码bmp图片,为什么放大显示不行?

    请教下,使用 emwin 解码 bmp 图片图片 1:1 及缩小显示都没有问题,唯独放大显示不行 ? 使用emwin 版本如下: emwin v5.40 keil argb -ot
    发表于 04-29 06:00

    使用iconview控件时,能否直接从sd卡中读取bmp格式的图片进行显示啊 ?

    请教下,使用 iconview 控件时,能否直接从sd卡中读取bmp格式的图片进行显示啊 ? 我看很多貌似都是 先把 图片文件转换为 .c 或者是 .dat 格式的,有没有什么方法在
    发表于 04-28 08:11

    HarmonyOS开发案例:【图片编辑】

    基于ArkTS的声明式开发范式的样例,主要介绍了图片编辑实现过程。
    的头像 发表于 04-23 20:54 309次阅读
    HarmonyOS<b class='flag-5'>开发</b>案例:【<b class='flag-5'>图片</b>编辑】

    Linux添加磁盘创建分区、挂载

    Linux添加磁盘创建分区、挂载
    发表于 04-20 17:49 438次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>添加</b>磁盘创建分区、挂载

    fpga开发板与linux开发板区别

    (Field-Programmable Gate Array)是一种可编程逻辑器件,它可以通过编程改变硬件逻辑电路的功能和结构。FPGA采用了可编程的门极,可以根据需要重新配置内部电路,从而实现不同的功能和逻辑关系。而
    的头像 发表于 02-01 17:09 2002次阅读

    instance是何时翻转的?每次有多少instance在翻转

    在run dynamic vectorless IR时,instance是何时翻转的?每次有多少instance在翻转
    的头像 发表于 01-26 09:31 475次阅读
    instance是何时<b class='flag-5'>翻转</b>的?每次有多少instance在<b class='flag-5'>翻转</b>?

    RZ/G2L Linux系统如何添加新的内核模块

    RZ/G2L Linux系统的镜像基于yocto构建,本篇介绍如何添加新的内核模块。
    的头像 发表于 01-04 12:19 1640次阅读
    RZ/G2L <b class='flag-5'>Linux</b>系统如何<b class='flag-5'>添加</b>新的内核模块