在jpg图像文件的app2文件头中,写入四个角的地理坐标并读取

在jpg图像文件的app2文件头中,写入四个角的地理坐标并读取

导师分派的众多任务之一,要求是在jpg图像文件的app2文件头中,写入四个角的地理坐标,同时写一个函数来读取。

##一、 jpeg图像文件格式
在这里插入图片描述
要求在**app2的文件头中写入地理坐标数据(double类型) ,并写函数读出。**本示例并不是很复杂,主要涉及到一些基础的操作,如字符数组转为浮点数,向二进制图像文件内写入数据并读出等,由他人原先写过的代码进行部分修改而来。现将代码放在下面,供读者学习参考。

代码示例:

//对于fopen的编译器错误,在属性里面找到C/C++ -> 预处理器中的预处理定义项,添加_CRT_SECURE_NO_WARNINGS即可
#include <iostream>
#include<stdio.h>
#include<string>
#include<atlmem.h>
using namespace std;
const int val = 15;
//读取数据的函数,读取的坐标均放在传入的loc数组中
void getLocation(int pos, unsigned char* pOriginFileBuffer, double *loc);

	int main(void)
	{
		FILE* fp = fopen("1.jpeg", "rb");
		if (fp == NULL)
		{
			printf("打开test.jpg失败\n");
			return -1;
		}
		
        //当前fp指向文件尾部
		fseek(fp, 0, SEEK_END);
		int nFileLen = ftell(fp);
		printf("文件长度: %dB\n", nFileLen);
		//当前文件指针指向文件开头
		fseek(fp, 0, SEEK_SET);
		
		//app0区长度
		int nE0Length = 0;
		
		//开辟新的同原文件大小相同的空间
		unsigned char* pOriginFileBuffer = (unsigned char*)malloc(nFileLen);
		memset(pOriginFileBuffer, 0, nFileLen);
		int ret = fread(pOriginFileBuffer, 1, nFileLen, fp);//将整个数据读入pOriginFileBuffer中
		if (ret != nFileLen)
		{
			printf("读取文件失败: %d\n", ret);
			fclose(fp);
			return -1;
		}
		//读取完毕并关闭文件
		fclose(fp);

		//此步可以去掉注释,查看这里printf的输出,
		//正是jpeg的文件开头 0XFFD8 和jpeg文件的结尾 0X0XFFD9
		/*printf("%x %x...%x %x\n", pOriginFileBuffer[0], pOriginFileBuffer[1], 
		pOriginFileBuffer[nFileLen - 2], pOriginFileBuffer[nFileLen - 1]);*/
		
        //检查下一块区域是否为app0
		if (pOriginFileBuffer[2] == 0xFF && pOriginFileBuffer[3] == 0xE0)
		{
			nE0Length = pOriginFileBuffer[4] | (pOriginFileBuffer[5] << 8);
			//printf("nE0Length: %d\n", nE0Length);
			nE0Length += 2;
		}//得到app0的长度

		int nOriginalHeadCopyLength = nE0Length + 2;//加上首部的0XFFD8,得到app0后总的偏移量
		
		//文件大小扩大200字节,可以根据要输入数据的规模进行修改
		int nNewFileLen = nFileLen + 200;

		unsigned char* pNewFileBuffer = (unsigned char*)malloc(nNewFileLen);

		unsigned char* pTempPosition = pNewFileBuffer;
		
		int nCopyLength = 0;
		
		//拷贝JPG头包括APPE0
		//将从头部开始到app0结束的内容拷贝到*pTempPosition中
		
		memcpy(pTempPosition, pOriginFileBuffer, nOriginalHeadCopyLength);
		//地址位置移动到app0结尾处
		pTempPosition += nOriginalHeadCopyLength;
		nCopyLength += nOriginalHeadCopyLength;

		/* 构 造 APPE2 */
		/*第一,第二,第三部分可不进行修改,重点在第四部分*/
		/* FF E2 LLLL 45 79 69 66 00 00 49 49 2A 00 08 00 00 00*/  
		/* 其 中 长 度 LLLL 需 要 确 定 下 来  计算长度的时候需要包含LLLL*/

		/*第一部分*/
		unsigned char szAPPE2FixHeader[] = {
			0xFF, 0xE2,
			0x00, 0x48,  /* APPE2 数 据 长 度 */     //如何确定长度?
			0x45, 0x78, 0x69, 0x66, 0x00, 0x00,/*Exif头*/
			0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00/*TIFF头*/
		};
		int nAPPE2FixHeaderLength = sizeof(szAPPE2FixHeader) / sizeof(unsigned char);
		memcpy(pTempPosition, szAPPE2FixHeader, nAPPE2FixHeaderLength);
		pTempPosition += nAPPE2FixHeaderLength;
		nCopyLength += nAPPE2FixHeaderLength;

		/*第二部分*/
		unsigned char szIFD0HeaderTag[] = { 0x01, 0x00,  /* 目 录 个 数  2 个*/
			0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, /* Exif 子 IFD 的 偏 移 量 */
			0x00, 0x00, 0x00, 0x00 }; /* 连 接 结 束 */

		int nIFD0HeaderTagLength = sizeof(szIFD0HeaderTag) / sizeof(unsigned char);
		memcpy(pTempPosition, szIFD0HeaderTag, nIFD0HeaderTagLength);
		pTempPosition += nIFD0HeaderTagLength;
		nCopyLength += nIFD0HeaderTagLength;

		/*第三部分*/
		/* 填 充 子 IFD */
		unsigned char szSubIFD0HeaderTag[] = { 0x01, 0x00, /* 目 录 个 数  1 个*/
			0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, /* 拍 摄 时 间 */
			0x00, 0x00, 0x00, 0x00 }; /* 连 接 结 束 */

		int uiszSubIFD0HeaderTagLength = sizeof(szSubIFD0HeaderTag) / sizeof(unsigned char);
		memcpy(pTempPosition, szSubIFD0HeaderTag, uiszSubIFD0HeaderTagLength);
		pTempPosition = pTempPosition + uiszSubIFD0HeaderTagLength;
		nCopyLength = nCopyLength + uiszSubIFD0HeaderTagLength;

		//从此length下访问输入坐标
		int cnt_Length = nCopyLength;
		
		//开始向文件中app2中写入地理坐标
		printf("请输入地理坐标:\n");
		double sz[8] = { 0 };
		for (int i = 0; i < 8; i++)
		{
			double temp;
			cin >> temp;
			sz[i] = temp;
		}
		
		//str1的大小根据要输入的位数改变,此项目需要经度和纬度的坐标,并精确到小数点后8位,
		//一般都不会需要这么高的精度,若无特殊需求可直接使用
		unsigned char str1[val] = { '0' };
		int len = sizeof(sz) / sizeof(double);
		int dataIFD0HeaderTagLength = sizeof(str1) ;
		unsigned char* p = str1;
			for (int i = 0; i < len; i++)
		{
				sprintf((char*)p, "%.10f", sz[i]);
				memcpy(pTempPosition, str1, dataIFD0HeaderTagLength);
			    pTempPosition+= dataIFD0HeaderTagLength;
		}
		nCopyLength += dataIFD0HeaderTagLength;
		
		/* 拷 贝 原 照 片 剩 余 大 小 */
		memset(pTempPosition, 0, 8);
		memcpy(pTempPosition+8 , (pOriginFileBuffer + nOriginalHeadCopyLength), 
		(nFileLen - nOriginalHeadCopyLength));
		
		//写入新文件
		FILE* fwp = fopen("new.jpg", "wb");
		if (fwp)
		{
			fwrite(pNewFileBuffer, 1, nNewFileLen, fwp);
		}	
		
		//开始调用函数,找到坐标并输出
		double  Location[8] = {'0'};
		
		//此处一定要传入pNewFileBuffer而不是pOrginalFileBuffer
		//调用完成后,地址数据就放在Loaction数组中
		getLocation(cnt_Length,pNewFileBuffer,Location);

		//可以进行输出检验,只要str1的大小没问题,小数点后的位数可以随意输出,没有问题
		printf("利用函数读取到的地理坐标:\n");
		for (int i = 0; i < 8; i++)
		{
			//注意用cout时,输出的小数点位数有问题,没有输入时的多,
			//建议使用printf,可以检查到确实赋值成功
			printf("%.8f\n", Location[i]);
		}
		
		fclose(fwp);
		free(pOriginFileBuffer);
		return 0;
	} 
	//求坐标函数的定义
	//传入的参数pOrginalFileBuffer可以看作一个一维的char类型数组
	void getLocation(int pos, unsigned char* pOriginFileBuffer, double *loc)//二进制文件
{
	int offset = 0;
	
	for (int j = 0; j < 8; j++)
	{
	    //注意和main函数中定义的str1大小对应
	    
		unsigned char s[val] = { '0' };
		
		for (int i = 0; i < val; i++)
		{
			//将每val个字符赋值给s字符数组,将字符数组转为string类型的字符串
			//offset要从pos起递增,每val位输出为一个字符串
			s[i] = pOriginFileBuffer[pos + offset];
			offset++;
		}
		string ss = (char*)s;
		//再用c++函数atof()将string类型字符串转换为浮点数
		double d = atof(ss.c_str());
		loc[j] = d;
	}
}

代码执行结果如下:在这里插入图片描述
新人第一次发帖,注释的比较认真,希望能给2021开个好头。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值