C语言 修改JPEG图片属性

首先介绍一下JPEG图片信息的格式:

JPEG的格式和标记

每一个JPEG文件的内容都开始于一个二进制的值'0xFFD8',结束与'0xFFD9'。在JPEG数据中有好几种类似于二进制0xFFXX的数据,它们统称为“标记”,并且它们代表了一段JPEG的信息数据。"0xFFD8"和"0xFFD9"后面不跟数据,而其它的标记后面则会附带数据,基本格式如下:

0xFF+标记号(1个字节)+数据大小描述符(2个字节)+数据内容(n个字节)

数据大小描述符是"Motorola"的字节顺序,及大端序,数据的低位被存放在高地址。请注意数据大小包括:数据大小描述符(2个字节)+数据内容(n个字节)。比如:

FF C1 00 0C

它表示这个标记(0xFFC1)的数据占0x000C(等于12)字节,这12个字节中包括了0x000C这两个字节。

Exif使用的标记

先来简单介绍一下exif(来自维基百科):

可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。Exif最初由日本电子工业发展协会在1996年制定,版本为1.0。1998年,升级到2.1,增加了对音频文件的支持。2002年3月,发表了2.2版。Exif可以附加于JPEG、TIFF、RIFF等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。Windows 7操作系统具备对Exif的原生支持,通过鼠标右键点击图片打开菜单,点击属性并切换到详细信息标签下即可直接查看Exif信息。Exif信息是可以被任意编辑的,因此只有参考的功能。

0xFFE0~0xFFEF之间的标记被叫做"应用标记",它们在JPEG图像的解码中不是必须存在的。它们被使用与用户的应用程序中。

Exif使用应用程序标记APP1(0xFFE1)来插入数据。

JPEG头APP1标记其他标记数据部分JPEG尾
FFD8FFE1 XXXX...FFEX XXX...FFDB XXXX...FFD9

Exif数据结构

Exif的数据结构大致如下,这是"intel"字节序的情况(即小端序)。Exif数据开始于ASCII字符"Exif"和2个字节的0x0000,后面才是Exif的数据,Exif使用TIFF格式来存储数据。

FFE1APP1 标记
SSSSAPP1 数据APP1 数据大小
45786966 0000Exif 头
49492A00 08000000TIFF 头
XXXX. . . .IFD0 (主图像)目录
LLLLLLLL连接到 IFD1
XXXX. . . .IFD0的数据域
XXXX. . . .  Exif 子IFD目录
00000000连接结束
XXXX. . . .Exif 子IFD的数据域
XXXX. . . .  Interoperability IFDDirectory
00000000连接结束
XXXX. . . .Interoperability IFD的数据域
XXXX. . . .Makernote IFDDirectory
00000000连接结束
XXXX. . . .Makernote IFD的数据域
XXXX. . . .IFD1(缩略图像)目录
00000000连接结束
XXXX. . . .IFD1的数据域
FFD8XXXX. . . XXXXFFD9缩略图像

TIFF头的结构

TIFF格式中前8个字节是TIFF头:

其中前2个字节定义了字节序。其中0x4949代表"intel"字节序,即小端序,0x4d4d代表"motorola"字节序,即大端序。

随后的2个字节是一个固定值,在intel字节序下是0x002a,在motorola字节序下是0x2a00。

最后4个字节是到第一个IFD(图像文件目录/Image File Directory)的偏移量,这个偏移量是从TIFF头开始计算的。由于第一个IFD是紧挨着TIFF头出现的,所以偏移量都是固定的0x00000008。

IFD:图像文件目录

紧挨着TIFF头,就是第一个IFD:图像文件目录。它包含了图像信息的数据。在下面的表格中,前两个字节('EEEE')表示在IFD中有多少个目录项。它后面存放的就是目录项(每个项目大小为12个字节)。在最后一个目录项之后,有个4字节大小的数据(表格中'LLLL'),它意味着到下一个IFD的偏移量。如果这个值是'0x00000000',则表示它是最后一个IFD。

EEEE目录项的号码
TTTTffffNNNNNNNNDDDDDDDD项目 0
TTTTffffNNNNNNNNDDDDDDDD项目 1
. . . . . . . . .. . . . . .
TTTTffffNNNNNNNNDDDDDDDD项目 EEEE-1
LLLLLLLL到下一个IFD的偏移量

上表中的'TTTT'(2个字节)是一个标签号码,代表数据的种类。'ffff'(2个字节)表示数据格式。'NNNNNNNN'(4个字节)表示组件的数目。'DDDDDDDD'(4个字节)则是数据的值或者到数据值的偏移量。

数据格式 (上面表格中的'ffff') 的定义如下表示. "rational" 的意思是说明数据的 内容是一个分数, 它含有2个有符号/无符号的长整形(signed/unsigned long integer)值, 并且第一个值表示的是分子, 第二个值则是分母. 

数据的值123456
格式unsigned byteascii stringsunsigned shortunsigned longunsigned rationalsigned byte
组件的大小(字节数)112481
 
数据的值789101112
格式undefinedsigned shortsigned longsigned rationalsingle floatdouble float
组件的大小(字节数)124848

通过多个存储在'NNNNNNNN'数据区的'组件的大小(字节数)'你能得到所有数据的字节长度. 如果数据的长度小于4个字节, 则'DDDDDDDD' 就表示的是标签的值. 如果长度超过4字节, 则'DDDDDDDD' 里存放的就是所要存储数据的偏移量地址. 

IFD数据结构

在Exif格式中, 第一个IFD是IFD0(主图像IFD), 然后它连接到IFD1(缩略图IFD) 并且IFD连接在此结束. 但是 IFD0/IFD1 不包含任何的数字相机的信息例如快门速度, 焦距等. IFD0 总是包含一个特殊的标签 Exif偏移量(0x8769), 它表示到 Exif子IFD 的偏移量. Exif子IFD也是一个IFD格式化的数据, 它包含了数字相机的信息。

Exif/TIFF使用的标签数

下面显示了 Exif/TIFF 使用的标签数. 如果这个标签组件数目的上限, CompoNo 一栏就代表这一数值. 如果这个数值没有, 则说明这儿没有上限值.

IFD0 (主图像)使用的标签

标签号标签名格式组件数描述
0x010eImageDescriptionascii string 用来描述图像. 双字节的字符码不能使用, 如 中文/韩文/日文.
0x010fMakeascii string 表示数字相机的制造商. 在 Exif 标准中, 这个标签是可选的, 但是在DCF中它是必需的.
0x0110Modelascii string 表示数字相机的模块代码. 在 Exif 标准中, 这个标签是可选的, 但在DCF中它也是必需的.
0x0112Orientationunsigned short1
Value0th Row0th Column
1topleft side
2topright side
3bottomright side
4bottomleft side
5left sidetop
6right sidetop
7right sidebottom
8left sidebottom
当拍照时, 相机相对于场景的方向. 在右边表示的是'0th row' 以及 '0th column' 在视觉位置上的关系.
0x011aXResolutionunsigned rational1图像的 显示/打印 分辨率. 缺省值是 1/72英寸, 但是它没有意义因为个人PC在 显示/打印 图像的时候不使用这个值.
0x011bYResolutionunsigned rational1
0x0128ResolutionUnitunsigned short1XResolution(0x011a)/YResolution(0x011b)的单位. '1' 表示没有单位, '2' 意味着英寸, '3' 表示厘米. 缺省值是 '2'(英寸).
0x0131Softwareascii string 显示固件的版本号(数字相机的内部控制软件).
0x0132DateTimeascii string20图像最后一次被修改时的日期/时间. 日期的格式是 "YYYY:MM:DD HH:MM:SS"+0x00, 一共 20个字节. 如果没有设置时钟或者数字相机没有时钟, 则这个域是用空格来填充. 通常, 它和DateTimeOriginal(0x9003)具有相同的值
0x013eWhitePointunsigned rational2定义图像白点(white point/白点:在彩色分色、照相或摄影时作为色彩平衡测量用途的参考点) 的色度(chromaticity). 如果图像是用CIE标准照度 D65(著名的是 '光线/daylight'的国际标准), 这个值是 '3127/10000,3290/10000'.
0x013fPrimaryChromaticitiesunsigned rational6定义图像的原始色度. 如果图像使用 CCIR 推荐 709原始色度, 则这个值是 '640/1000,330/1000,300/1000,600/1000,150/1000,0/1000'.
0x0211YCbCrCoefficientsunsigned rational3当图像的格式是 YCbCr(JPEG的格式), 这个值表示转换成 RGB格式的一个常量. 通常, 这个值是'0.299/0.587/0.114'.
0x0213YCbCrPositioningunsigned short1当图像的格式是 YCbCr 并且使用 '子采样/Subsampling'(色度数据的剪切值, 所有的数字相机都使用), 定义了subsampling 像素阵列的色度采样点. '1'表示像素阵列的中心, '2' 表示基准点.
0x0214ReferenceBlackWhiteunsigned rational6表示黑点(black point)/白点 的参考值. 在YCbCr 格式中,前两个值是 Y的黑点/白点, 下两个值是 Cb, 最后两个值是 Cr. 而在 RGB 格式中, 前两个表示R的黑点/白点, 下两个是 G, 最后两个是 B.
0x8298Copyrightascii string 表示版权信息
0x8769ExifOffsetunsigned long1Exif 子IFD的偏移量


Exif 子IFD使用的标签

标签号标签名格式组件数描述
0x829aExposureTimeunsigned rational1曝光时间 (快门速度的倒数). 单位是秒.
0x829dFNumberunsigned rational1拍照时的光圈F-number(F-stop).
0x8822ExposureProgramunsigned short1拍照时相机使用的曝光程序. '1' 表示手动曝光, '2' 表示正常程序曝光, '3' 表示光圈优先曝光, '4' 表示快门优先曝光, '5' 表示创意程序(慢速程序), '6' 表示动作程序(高速程序), '7'表示 肖像模式, '8' 表示风景模式.
0x8827ISOSpeedRatingsunsigned short2CCD 的感光度, 等效于 Ag-Hr 胶片的速率.
0x9000ExifVersionundefined4Exif 的版本号. 用4个ASCII字符来存储. 如果图片是基于Exif V2.1的, 这个值是 "0210". 因为它不是一个用NULL(0x00)来终结的字符串,所以这里的类型是 'undefined'.
0x9003DateTimeOriginalascii string20照片在被拍下来的日期/时间. 使用用户的软件是不能被修改这个值的. 日期的格式是 "YYYY:MM:DD HH:MM:SS"+0x00, 一共占用20个字节. 如果数字相机没有设置时钟或者 数字相机没有时钟, 这个域使用空格来填充. 在Exif标准中, 这个标签是可选的, 但是在 DCF中是必需的.
0x9004DateTimeDigitizedascii string20照片被数字化时的日期/时间. 通常, 它与DateTimeOriginal(0x9003)具有相同的值. 数据格式是 "YYYY:MM:DD HH:MM:SS"+0x00, 一共占用20个字节. 如果数字相机没有设置时钟或者 数字相机没有时钟, 这个域使用空格来填充. 在Exif标准中, 这个标签是可选的, 但是在 DCF中是必需的.
0x9101ComponentsConfigurationundefined 表示的是像素数据的顺序. 大多数情况下RGB格式使用 '0x04,0x05,0x06,0x00' 而YCbCr 格式使用 '0x01,0x02,0x03,0x00'. 0x00:并不存在, 其他的对应关系为 0x01:Y, 0x02:Cb, 0x03:Cr, 0x04:Red, 0x05:Green, 0x06:Bllue.
0x9102CompressedBitsPerPixelunsigned rational1JPEG (粗略的估计)的平均压缩率.
0x9201ShutterSpeedValuesigned rational1用APEX表示出的快门速度. 为了转换成原始的 'Shutter Speed'; 则先要计算2的ShutterSpeedValue次幂, 然后求倒数. 例如, 如果 ShutterSpeedValue 是 '4', 快门速度则是1/(24)=1/16秒.
0x9202ApertureValueunsigned rational1拍照时镜头的光圈. 单位是 APEX. 为了转换成普通的 F-number(F-stop), 则要先计算出根号2 2 (=1.4142)的ApertureValue次幂. 例如, 如果ApertureValue 是 '5', F-number 就等于1.41425 = F5.6.
0x9203BrightnessValuesigned rational1被拍摄对象的明度, 单位是 APEX. 为了从BrigtnessValue(Bv)计算出曝光量(Ev), 你必须加上 SensitivityValue(Sv).
Ev=Bv+Sv   Sv=log2(ISOSpeedRating/3.125)
ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
0x9204ExposureBiasValuesigned rational1照片拍摄时的曝光补偿. 单位是APEX(EV).
0x9205MaxApertureValueunsigned rational1镜头的最大光圈值. 你可以通过计算根号2的MaxApertureValue次幂来转换成普通的光圈 F-number (跟ApertureValue:0x9202的处理过程一样).
0x9206SubjectDistancesigned rational1到焦点的距离, 单位是米.
0x9207MeteringModeunsigned short1曝光的测光方法. '0' 表示未知, '1' 为平均测光, '2' 为中央重点测光, '3' 是点测光, '4' 是多点测光, '5' 是多区域测光, '6' 部分测光, '255' 则是其他.
0x9208LightSourceunsigned short1光源, 实际上是表示白平衡设置. '0' 意味着未知, '1'是日光, '2'是荧光灯, '3' 白炽灯(钨丝), '10' 闪光灯, '17' 标准光A, '18' 标准光B, '19' 标准光C, '20' D55, '21' D65, '22' D75, '255' 为其他.
0x9209Flashunsigned short1'0' 表示闪光灯没有闪光, '1' 表示闪光灯闪光, '5' 表示闪光但没有检测反射光, '7' 表示闪光且检测了反射光.
0x920aFocalLengthunsigned rational1拍摄照片时的镜头的焦距长度. 单位是毫米.
0x927cMakerNoteundefined 制造商的内部数据. 一些制造商如 Olympus/Nikon/Sanyo 等在这个区域中使用IFD 格式的数据.
0x9286UserCommentundefined 存储用户的注释. 这个标签允许使用两字节的德字符或者 unicode. 前8 个字节描述的是字符集. 'JIS' 是日文 (著名的有 Kanji).
'0x41,0x53,0x43,0x49,0x49,0x00,0x00,0x00':ASCII
'0x4a,0x49,0x53,0x00,0x00,0x00,0x00,0x00':JIS
'0x55,0x4e,0x49,0x43,0x4f,0x44,0x45,0x00':Unicode
'0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00':Undefined
0x9290SubsecTimeascii string 一些数字相机每秒能拍摄 2~30 张照片, 但是DateTime/DateTimeOriginal/DateTimeDigitized 标签只能记录到秒单位的时间. SubsecTime 标签就是用来记录秒后面的数据(微秒).
例如, DateTimeOriginal = "1996:09:01 09:15:30", SubSecTimeOriginal = "130", 合并起来的原始的拍摄 时间就是 "1996:09:01 09:15:30.130" 
0x9291SubsecTimeOriginalascii string 
0x9292SubsecTimeDigitizedascii string 
0xa000FlashPixVersionundefined4存储FlashPix 的版本信息. 如果图像数据是基于 FlashPix formar Ver.1.0, 则这个值为 "0100". 因为它不是一个用NULL(0x00)来终结的字符串,所以这里的类型是 'undefined'.
0xa001ColorSpaceunsigned short1定义色彩空间. DCF 图像必须使用 sRGB 色彩空间因此这个值总是 '1'. 如果这个照片使用了 其他的色彩空间, 这个值是 '65535':未校准(Uncalibrated).
0xa002ExifImageWidthunsigned short/long1主图像的尺寸大小.
0xa003ExifImageHeightunsigned short/long1
0xa004RelatedSoundFileascii string 如果数字相机能够纪录图像的音频数据, 则表示音频数据的名字.
0xa005ExifInteroperabilityOffsetunsigned long1表示这是一个扩展"ExifR98", 细节未知. 这个值经常是IFD格式的数据. 当前这儿有两个 目录项, 第一个是 Tag0x0001, 值是"R98", 下一个是 Tag0x0002, 它的值为 "0100".
0xa20eFocalPlaneXResolutionunsigned rational1表示CCD的像素密度. 如果你的相机是百万像素的并且是用低分辨率(如VGA模式) 来拍摄照片, 这个值可以通过照片的分辨率来重新采样. 在这种情况下, FocalPlaneResolution 就不是CCD的实际的分辨率.
0xa20fFocalPlaneYResolutionunsigned rational1
0xa210FocalPlaneResolutionUnitunsigned short1FocalPlaneXResoluton/FocalPlaneYResolution的单位. '1' 表示没有单位, '2'是英寸inch, '3' 表示厘米. 

注意:一些Fujifilm的数码相机(如.FX2700,FX2900,Finepix4700Z/40i 等) 使用的值是 '3' 所以它的单位一定是 '厘米' , 但是它们的分辨率单位就变成'8.3mm?'(1/3in.?). 这是Fuji 的 BUG? 从Finepix4900Z 开始这个值就使用 '2' 了但仍然跟实际的值不吻合.
0xa215ExposureIndexunsigned rational1跟ISOSpeedRatings(0x8827)一样但是数据类型是 unsigned rational. 只有Kodak的数字相机使用 这个标签来替代 ISOSpeedRating, 我不知道这是为什么(历史原因?).
0xa217SensingMethodunsigned short1表示图像传感器单元的类型. '2' 意味着这是一个芯片颜色区域传感器, 几乎所有的数字相机都 使用这个类型.
0xa300FileSourceundefined1显示图像来源. 值 '0x03' 表示图像源是数字定格相机.
0xa301SceneTypeundefined1表示拍摄场景的类型. 值 '0x01' 表示图像是通过相机直接拍摄出来的.
0xa302CFAPatternundefined 表示色彩过滤阵列(CFA) 几何模式.
长度类型意义
2shortHorizontal repeat pixel unit = n
2shortVertical repeat pixel unit = m
1byteCFA value[0,0]

:

:

:

1byteCFA value[n-1,0]
1byteCFA value[0,1]

:

:

:

1byteCFA value[n-1,m-1]

色彩过滤和CFA值之间的关系.
Filter ColorRedGreenBlueCyanMagentaYellowWhite
CFA value0123456
 
RG
GB
例如, 普通的 RGB 过滤器使用左表的副本, 这个值是 '0x0002,0x0002,0x00,0x01,0x01,0x02'. 

下面是一段修改JPEG拍摄时间的代码:

int main(void)
{
	FILE *fp = fopen("test.jpg", "rb");
	if (fp == NULL)
	{
		printf("打开test.jpg失败\n");
		return -1;
	}

	fseek(fp, 0, SEEK_END);
	int nFileLen = ftell(fp);
	printf("文件长度: %d\n", nFileLen);
	fseek(fp, 0, SEEK_SET);

	int nE0Length = 0;
	unsigned char *pOriginFileBuffer = (unsigned char *)malloc(nFileLen);
	memset(pOriginFileBuffer, 0, nFileLen);
	int ret = fread(pOriginFileBuffer, 1, nFileLen, fp);
	if (ret != nFileLen)
	{
		printf("读取文件失败: %d\n", ret);
		fclose(fp);
		return -1;
	}

	fclose(fp);

	printf("%x %x...%x %x\n", pOriginFileBuffer[0], pOriginFileBuffer[1], pOriginFileBuffer[nFileLen - 2], pOriginFileBuffer[nFileLen - 1]);

	if (pOriginFileBuffer[2] == 0xFF && pOriginFileBuffer[3] == 0xE0)
	{
		nE0Length = pOriginFileBuffer[4] | (pOriginFileBuffer[5] << 8);
		printf("nE0Length: %d\n", nE0Length);
		nE0Length += 2;
	}

	int nOriginalHeadCopyLength = nE0Length + 2;

	int nNewFileLen = nFileLen + 100;

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

	unsigned char *pTempPosition = pNewFileBuffer;
	int nCopyLength = 0;

	//拷贝JPG头包括APPE0
	memcpy(pTempPosition, pOriginFileBuffer, nOriginalHeadCopyLength);
	pTempPosition += nOriginalHeadCopyLength;
	nCopyLength += nOriginalHeadCopyLength;

	/* 构 造 APPE1 */
	/* FF E1 LLLL 45 79 69 66 00 00 49 49 2A 00 08 00 00 00*/  /* 其 中 长 度 LLLL 需 要 确 定 下 来  计算长度的时候需要包含LLLL*/
															   /*            e  x  i  f  \0 \0	I  I					*/
	unsigned char szAPPE1FixHeader[] = {
		0xFF, 0xE1,
		0x00, 0x48,  /* APPE1 数 据 长 度 */
		0x45, 0x78, 0x69, 0x66, 0x00, 0x00,/*Exif头*/
		0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00/*TIFF头*/
	};

	int nAPPE1FixHeaderLength = sizeof(szAPPE1FixHeader) / sizeof(unsigned char);
	memcpy(pTempPosition, szAPPE1FixHeader, nAPPE1FixHeaderLength);
	pTempPosition += nAPPE1FixHeaderLength;
	nCopyLength += nAPPE1FixHeaderLength;
	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;

	//拍摄时间20字节
	char szTime[20] = "2019:04:04 13:22:22";
	memcpy(pTempPosition, szTime, 20);
	pTempPosition += 20;
	nCopyLength += 20;

	/* 拷 贝 原 照 片 剩 余 大 小 */
	memset(pTempPosition, 0, 8);
	memcpy(pTempPosition + 8, (pOriginFileBuffer + nOriginalHeadCopyLength), (nFileLen - nOriginalHeadCopyLength));

	FILE *fwp = fopen("new.jpg", "wb");
	if (fwp)
	{
		fwrite(pNewFileBuffer, 1, nNewFileLen, fwp);
		fclose(fwp);
	}

	free(pOriginFileBuffer);

	return 0;
}

参考文章:

桃源谷-Exif文件格式描述

要将JPEG图片从480x320改为320x240,你可以使用C语言中的图像处理库来实现。下面是一个使用libjpeg库的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <jpeglib.h> void resizeJPEG(const char* inputFilename, const char* outputFilename, int width, int height) { struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; struct jpeg_error_mgr jsrcerr, jdsterr; FILE *inputFile, *outputFile; JSAMPARRAY buffer; int row_stride; inputFile = fopen(inputFilename, "rb"); if (inputFile == NULL) { printf("Failed to open input file\n"); return; } outputFile = fopen(outputFilename, "wb"); if (outputFile == NULL) { printf("Failed to open output file\n"); fclose(inputFile); return; } srcinfo.err = jpeg_std_error(&jsrcerr); jpeg_create_decompress(&srcinfo); jpeg_stdio_src(&srcinfo, inputFile); (void) jpeg_read_header(&srcinfo, TRUE); (void) jpeg_start_decompress(&srcinfo); dstinfo.err = jpeg_std_error(&jdsterr); jpeg_create_compress(&dstinfo); jpeg_stdio_dest(&dstinfo, outputFile); dstinfo.image_width = width; dstinfo.image_height = height; dstinfo.input_components = srcinfo.output_components; dstinfo.in_color_space = srcinfo.out_color_space; (void) jpeg_set_defaults(&dstinfo); (void) jpeg_start_compress(&dstinfo, TRUE); row_stride = srcinfo.output_width * srcinfo.output_components; buffer = (*srcinfo.mem->alloc_sarray)((j_common_ptr) &srcinfo, JPOOL_IMAGE, row_stride, 1); while (srcinfo.output_scanline < srcinfo.output_height) { (void) jpeg_read_scanlines(&srcinfo, buffer, 1); (void) jpeg_write_scanlines(&dstinfo, buffer, 1); } (void) jpeg_finish_compress(&dstinfo); (void) jpeg_finish_decompress(&srcinfo); jpeg_destroy_compress(&dstinfo); jpeg_destroy_decompress(&srcinfo); fclose(inputFile); fclose(outputFile); } int main() { const char* inputFilename = "input.jpg"; const char* outputFilename = "output.jpg"; int newWidth = 320; int newHeight = 240; resizeJPEG(inputFilename, outputFilename, newWidth, newHeight); return 0; } ``` 以上代码使用了libjpeg库来读取和写入JPEG文件。你需要将要处理的JPEG图片命名为"input.jpg",处理后的图片将保存为"output.jpg"。你可以根据需要修改输入输出文件名和目标宽度和高度。 请确保已经安装了libjpeg库并将其链接到你的编译环境中。你可以使用以下命令编译代码: ``` gcc -o resizejpeg resizejpeg.c -ljpeg ``` 执行编译后的可执行文件即可进行图片尺寸调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值