在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开个好头。