GOEXIF读取和写入EXIF信息

能够读取和写入EXIF信息,使得图像处理程序在不改变原有图片内容、不添加多余文件的情况下可以保存参数信息,非常有价值。

这方面中英文资料比较少,经过较长时间的研究和集成,我在网络相关资料的基础上完成了GOEXIF,实现了MFC上的直接读取和写入。

读取

EXIFINFO m_exifinfo;
FILE* hFile=fopen(FilePathName.c_str(),"rb");
if (hFile){
memset(&m_exifinfo,0,sizeof(EXIFINFO));
Cexif exif(&m_exifinfo);
exif.DecodeExif(hFile);
fclose(hFile);

写入

Cexif ceif;
sprintf_s(cbuf2,"123");
int iret = ceif.WriteExif2Image(FilePathName.c_str(),PropertyTagCopyright,cbuf2);
if(iret == 0)
AfxMessageBox("写入EXIF信息成功!");
else
AfxMessageBox("写入EXIF信息失败!");

已经能够投入实际使用,但是由于是多种资料融合,代码还是需要重构。我会结合自己工作来做,如果哪位有兴趣继续开发,这应该是一个很好的基础。

/*

* File:    exif.h

* Purpose:    cpp EXIF reader

* 16/Mar/2003 <[email protected]>

* based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>

*/

//2016年11月25日 jsxyhelu 修改添加版权信息

//2017年1月21日  jsxyhelu 添加写入exif的功能

//使用例子 (基于MFC)

/*读取

EXIFINFO m_exifinfo;

FILE* hFile=fopen(FilePathName.c_str(),"rb");

if (hFile){

memset(&m_exifinfo,0,sizeof(EXIFINFO));

Cexif exif(&m_exifinfo);

exif.DecodeExif(hFile);

fclose(hFile);

写入

Cexif ceif;

sprintf_s(cbuf2,"123");

int iret = ceif.WriteExif2Image(FilePathName.c_str(),PropertyTagCopyright,cbuf2);

if(iret == 0)

AfxMessageBox("写入EXIF信息成功!");

else

AfxMessageBox("写入EXIF信息失败!");

*/

#if !defined(__exif_h)

#define __exif_h

#include <stdlib.h>

#include <memory.h>

#include <string.h>

#include <stdio.h>

#include <math.h>

#include <windows.h>

#include <gdiplus.h>

#include <stdio.h>

#pragma comment(lib, "gdiplus.lib")

#define MAX_COMMENT 65535

#define MAX_SECTIONS 20

typedef struct tag_ExifInfo {

char  Version      [5];

char  CameraMake   [32];

char  CameraModel  [40];

char  DateTime     [20];

char  CopyRight    [MAX_COMMENT];

int   Height, Width;

int   Orientation;

int   IsColor;

int   Process;

int   FlashUsed;

float FocalLength;

float ExposureTime;

float ApertureFNumber;

float Distance;

float CCDWidth;

float ExposureBias;

int   Whitebalance;

int   MeteringMode;

int   ExposureProgram;

int   ISOequivalent;

int   CompressionLevel;

float FocalplaneXRes;

float FocalplaneYRes;

float FocalplaneUnits;

float Xresolution;

float Yresolution;

float ResolutionUnit;

float Brightness;

char  Comments[MAX_COMMENT];

unsigned char * ThumbnailPointer;  /* Pointer at the thumbnail */

unsigned ThumbnailSize;     /* Size of thumbnail. */

bool  IsExif;

} EXIFINFO;

//--------------------------------------------------------------------------

// JPEG markers consist of one or more 0xFF unsigned chars, followed by a marker

// code unsigned char (which is not an FF).  Here are the marker codes of interest

// in this program.  (See jdmarker.c for a more complete list.)

//--------------------------------------------------------------------------

#define M_SOF0  0xC0            // Start Of Frame N

#define M_SOF1  0xC1            // N indicates which compression process

#define M_SOF2  0xC2            // Only SOF0-SOF2 are now in common use

#define M_SOF3  0xC3

#define M_SOF5  0xC5            // NB: codes C4 and CC are NOT SOF markers

#define M_SOF6  0xC6

#define M_SOF7  0xC7

#define M_SOF9  0xC9

#define M_SOF10 0xCA

#define M_SOF11 0xCB

#define M_SOF13 0xCD

#define M_SOF14 0xCE

#define M_SOF15 0xCF

#define M_SOI   0xD8            // Start Of Image (beginning of datastream)

#define M_EOI   0xD9            // End Of Image (end of datastream)

#define M_SOS   0xDA            // Start Of Scan (begins compressed data)

#define M_JFIF  0xE0            // Jfif marker

#define M_EXIF  0xE1            // Exif marker

#define M_COM   0xFE            // COMment

class Cexif

{

typedef struct tag_Section_t{

unsigned char*    Data;

int      Type;

unsigned Size;

} Section_t;

public:

EXIFINFO* m_exifinfo;

char m_szLastError[256];

Cexif(EXIFINFO* info = NULL);

~Cexif();

bool DecodeExif(FILE* hFile);

protected:

bool process_EXIF(unsigned char * CharBuf, unsigned int length);

void process_COM (const unsigned char * Data, int length);

void process_SOFn (const unsigned char * Data, int marker);

int Get16u(void * Short);

int Get16m(void * Short);

long Get32s(void * Long);

unsigned long Get32u(void * Long);

double ConvertAnyFormat(void * ValuePtr, int Format);

bool ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength,

EXIFINFO * const pInfo, unsigned char ** const LastExifRefdP);

int ExifImageWidth;

int MotorolaOrder;

Section_t Sections[MAX_SECTIONS];

int SectionsRead;

bool freeinfo;

public:

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

Gdiplus::Bitmap* LoadBitmapFromMemory(const void* memory, DWORD size);

Gdiplus::Bitmap* LoadBitmapFromFile(const TCHAR* file_name);

int WriteExif2Image(CString strFileName, int PropertyTag, CString propertyValue);

};

#endif

/*

* File:    exif.h

* Purpose:    cpp EXIF reader

* 16/Mar/2003 <[email protected]>

* based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>

*/

//2016年11月25日 jsxyhelu 修改添加版权信息

//2017年1月21日  jsxyhelu 添加写入exif的功能

#include "stdafx.h"

#include "Exif.h"

using namespace Gdiplus;

////////////////////////////////////////////////////////////////////////////////

Cexif::Cexif(EXIFINFO* info)

{

if (info) {

m_exifinfo = info;

freeinfo = false;

} else {

m_exifinfo = new EXIFINFO;

memset(m_exifinfo,0,sizeof(EXIFINFO));

freeinfo = true;

}

m_szLastError[0]=‘\0‘;

ExifImageWidth = MotorolaOrder = 0;

SectionsRead=0;

memset(&Sections, 0, MAX_SECTIONS * sizeof(Section_t));

}

////////////////////////////////////////////////////////////////////////////////

Cexif::~Cexif()

{

for(int i=0;i<MAX_SECTIONS;i++) if(Sections[i].Data) free(Sections[i].Data);

if (freeinfo) delete m_exifinfo;

}

////////////////////////////////////////////////////////////////////////////////

bool Cexif::DecodeExif(FILE * hFile)

{

int a;

int HaveCom = 0;

a = fgetc(hFile);

if (a != 0xff || fgetc(hFile) != M_SOI){

return 0;

}

for(;;){

int itemlen;

int marker = 0;

int ll,lh, got;

unsigned char * Data;

if (SectionsRead >= MAX_SECTIONS){

strcpy(m_szLastError,"Too many sections in jpg file");

return 0;

}

for (a=0;a<7;a++){

marker = fgetc(hFile);

if (marker != 0xff) break;

if (a >= 6){

printf("too many padding unsigned chars\n");

return 0;

}

}

if (marker == 0xff){

// 0xff is legal padding, but if we get that many, something‘s wrong.

strcpy(m_szLastError,"too many padding unsigned chars!");

return 0;

}

Sections[SectionsRead].Type = marker;

// Read the length of the section.

lh = fgetc(hFile);

ll = fgetc(hFile);

itemlen = (lh << 8) | ll;

if (itemlen < 2){

strcpy(m_szLastError,"invalid marker");

return 0;

}

Sections[SectionsRead].Size = itemlen;

Data = (unsigned char *)malloc(itemlen);

if (Data == NULL){

strcpy(m_szLastError,"Could not allocate memory");

return 0;

}

Sections[SectionsRead].Data = Data;

// Store first two pre-read unsigned chars.

Data[0] = (unsigned char)lh;

Data[1] = (unsigned char)ll;

got = fread(Data+2, 1, itemlen-2,hFile); // Read the whole section.

if (got != itemlen-2){

strcpy(m_szLastError,"Premature end of file?");

return 0;

}

SectionsRead += 1;

switch(marker){

case M_SOS:   // stop before hitting compressed data

// If reading entire image is requested, read the rest of the data.

/*if (ReadMode & READ_IMAGE){

int cp, ep, size;

// Determine how much file is left.

cp = ftell(infile);

fseek(infile, 0, SEEK_END);

ep = ftell(infile);

fseek(infile, cp, SEEK_SET);

size = ep-cp;

Data = (uchar *)malloc(size);

if (Data == NULL){

strcpy(m_szLastError,"could not allocate data for entire image");

return 0;

}

got = fread(Data, 1, size, infile);

if (got != size){

strcpy(m_szLastError,"could not read the rest of the image");

return 0;

}

Sections[SectionsRead].Data = Data;

Sections[SectionsRead].Size = size;

Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;

SectionsRead ++;

HaveAll = 1;

}*/

return 1;

case M_EOI:   // in case it‘s a tables-only JPEG stream

printf("No image in jpeg!\n");

return 0;

case M_COM: // Comment section

if (HaveCom){

// Discard this section.

free(Sections[--SectionsRead].Data);

Sections[SectionsRead].Data=0;

}else{

process_COM(Data, itemlen);

HaveCom = 1;

}

break;

case M_JFIF:

// Regular jpegs always have this tag, exif images have the exif

// marker instead, althogh ACDsee will write images with both markers.

// this program will re-create this marker on absence of exif marker.

// hence no need to keep the copy from the file.

free(Sections[--SectionsRead].Data);

Sections[SectionsRead].Data=0;

break;

case M_EXIF:

// Seen files from some ‘U-lead‘ software with Vivitar scanner

// that uses marker 31 for non exif stuff.  Thus make sure

// it says ‘Exif‘ in the section before treating it as exif.

if (memcmp(Data+2, "Exif", 4) == 0){

m_exifinfo->IsExif = process_EXIF((unsigned char *)Data+2, itemlen);

}else{

// Discard this section.

free(Sections[--SectionsRead].Data);

Sections[SectionsRead].Data=0;

}

break;

case M_SOF0:

case M_SOF1:

case M_SOF2:

case M_SOF3:

case M_SOF5:

case M_SOF6:

case M_SOF7:

case M_SOF9:

case M_SOF10:

case M_SOF11:

case M_SOF13:

case M_SOF14:

case M_SOF15:

process_SOFn(Data, marker);

break;

default:

// Skip any other sections.

//if (ShowTags) printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen);

break;

}

}

return 1;

}

////////////////////////////////////////////////////////////////////////////////

/*--------------------------------------------------------------------------

Process a EXIF marker

Describes all the drivel that most digital cameras include...

--------------------------------------------------------------------------*/

bool Cexif::process_EXIF(unsigned char * CharBuf, unsigned int length)

{

m_exifinfo->FlashUsed = 0;

/* If it‘s from a digicam, and it used flash, it says so. */

m_exifinfo->Comments[0] = ‘\0‘;  /* Initial value - null string */

ExifImageWidth = 0;

{   /* Check the EXIF header component */

static const unsigned char ExifHeader[] = "Exif\0\0";

if (memcmp(CharBuf+0, ExifHeader,6)){

strcpy(m_szLastError,"Incorrect Exif header");

return 0;

}

}

if (memcmp(CharBuf+6,"II",2) == 0){

MotorolaOrder = 0;

}else{

if (memcmp(CharBuf+6,"MM",2) == 0){

MotorolaOrder = 1;

}else{

strcpy(m_szLastError,"Invalid Exif alignment marker.");

return 0;

}

}

/* Check the next two values for correctness. */

if (Get16u(CharBuf+8) != 0x2a){

strcpy(m_szLastError,"Invalid Exif start (1)");

return 0;

}

int FirstOffset = Get32u(CharBuf+10);

if (FirstOffset < 8 || FirstOffset > 16){

// I used to ensure this was set to 8 (website I used indicated its 8)

// but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002)

strcpy(m_szLastError,"Suspicious offset of first IFD value");

return 0;

}

unsigned char * LastExifRefd = CharBuf;

/* First directory starts 16 unsigned chars in.  Offsets start at 8 unsigned chars in. */

if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))

return 0;

/* This is how far the interesting (non thumbnail) part of the exif went. */

// int ExifSettingsLength = LastExifRefd - CharBuf;

/* Compute the CCD width, in milimeters. */

if (m_exifinfo->FocalplaneXRes != 0){

m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes);

}

return 1;

}

//--------------------------------------------------------------------------

// Get 16 bits motorola order (always) for jpeg header stuff.

//--------------------------------------------------------------------------

int Cexif::Get16m(void * Short)

{

return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];

}

////////////////////////////////////////////////////////////////////////////////

/*--------------------------------------------------------------------------

Convert a 16 bit unsigned value from file‘s native unsigned char order

--------------------------------------------------------------------------*/

int Cexif::Get16u(void * Short)

{

if (MotorolaOrder){

return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];

}else{

return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0];

}

}

////////////////////////////////////////////////////////////////////////////////

/*--------------------------------------------------------------------------

Convert a 32 bit signed value from file‘s native unsigned char order

--------------------------------------------------------------------------*/

long Cexif::Get32s(void * Long)

{

if (MotorolaOrder){

return  ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16)

| (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 );

}else{

return  ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16)

| (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 );

}

}

////////////////////////////////////////////////////////////////////////////////

/*--------------------------------------------------------------------------

Convert a 32 bit unsigned value from file‘s native unsigned char order

--------------------------------------------------------------------------*/

unsigned long Cexif::Get32u(void * Long)

{

return (unsigned long)Get32s(Long) & 0xffffffff;

}

////////////////////////////////////////////////////////////////////////////////

/* Describes format descriptor */

static const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};

#define NUM_FORMATS 12

#define FMT_BYTE       1

#define FMT_STRING     2

#define FMT_USHORT     3

#define FMT_ULONG      4

#define FMT_URATIONAL  5

#define FMT_SBYTE      6

#define FMT_UNDEFINED  7

#define FMT_SSHORT     8

#define FMT_SLONG      9

#define FMT_SRATIONAL 10

#define FMT_SINGLE    11

#define FMT_DOUBLE    12

/* Describes tag values */

#define TAG_EXIF_VERSION      0x9000

#define TAG_EXIF_OFFSET       0x8769

#define TAG_INTEROP_OFFSET    0xa005

//2016年11月25日15:55:46 修改TAG_MAKE

#define TAG_MAKE              0x010F

#define TAG_COPYRIGHT         0x8298

#define TAG_MODEL             0x0110

#define TAG_ORIENTATION       0x0112

#define TAG_XRESOLUTION       0x011A

#define TAG_YRESOLUTION       0x011B

#define TAG_RESOLUTIONUNIT    0x0128

#define TAG_EXPOSURETIME      0x829A

#define TAG_FNUMBER           0x829D

#define TAG_SHUTTERSPEED      0x9201

#define TAG_APERTURE          0x9202

#define TAG_BRIGHTNESS        0x9203

#define TAG_MAXAPERTURE       0x9205

#define TAG_FOCALLENGTH       0x920A

#define TAG_DATETIME_ORIGINAL 0x9003

#define TAG_USERCOMMENT       0x9286

#define TAG_SUBJECT_DISTANCE  0x9206

#define TAG_FLASH             0x9209

#define TAG_FOCALPLANEXRES    0xa20E

#define TAG_FOCALPLANEYRES    0xa20F

#define TAG_FOCALPLANEUNITS   0xa210

#define TAG_EXIF_IMAGEWIDTH   0xA002

#define TAG_EXIF_IMAGELENGTH  0xA003

/* the following is added 05-jan-2001 vcs */

#define TAG_EXPOSURE_BIAS     0x9204

#define TAG_WHITEBALANCE      0x9208

#define TAG_METERING_MODE     0x9207

#define TAG_EXPOSURE_PROGRAM  0x8822

#define TAG_ISO_EQUIVALENT    0x8827

#define TAG_COMPRESSION_LEVEL 0x9102

#define TAG_THUMBNAIL_OFFSET  0x0201

#define TAG_THUMBNAIL_LENGTH  0x0202

/*--------------------------------------------------------------------------

Process one of the nested EXIF directories.

--------------------------------------------------------------------------*/

bool Cexif::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength,

EXIFINFO * const m_exifinfo, unsigned char ** const LastExifRefdP )

{

int de;

int a;

int NumDirEntries;

unsigned ThumbnailOffset = 0;

unsigned ThumbnailSize = 0;

NumDirEntries = Get16u(DirStart);

if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){

strcpy(m_szLastError,"Illegally sized directory");

return 0;

}

for (de=0;de<NumDirEntries;de++){

int Tag, Format, Components;

unsigned char * ValuePtr;

/* This actually can point to a variety of things; it must be

cast to other types when used.  But we use it as a unsigned char-by-unsigned char

cursor, so we declare it as a pointer to a generic unsigned char here.

*/

int BytesCount;

unsigned char * DirEntry;

DirEntry = DirStart+2+12*de;

Tag = Get16u(DirEntry);

Format = Get16u(DirEntry+2);

Components = Get32u(DirEntry+4);

if ((Format-1) >= NUM_FORMATS) {

/* (-1) catches illegal zero case as unsigned underflows to positive large */

strcpy(m_szLastError,"Illegal format code in EXIF dir");

return 0;

}

BytesCount = Components * BytesPerFormat[Format];

if (BytesCount > 4){

unsigned OffsetVal;

OffsetVal = Get32u(DirEntry+8);

/* If its bigger than 4 unsigned chars, the dir entry contains an offset.*/

if (OffsetVal+BytesCount > ExifLength){

/* Bogus pointer offset and / or unsigned charcount value */

strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");

return 0;

}

ValuePtr = OffsetBase+OffsetVal;

}else{

/* 4 unsigned chars or less and value is in the dir entry itself */

ValuePtr = DirEntry+8;

}

if (*LastExifRefdP < ValuePtr+BytesCount){

/* Keep track of last unsigned char in the exif header that was

actually referenced.  That way, we know where the

discardable thumbnail data begins.

*/

*LastExifRefdP = ValuePtr+BytesCount;

}

/* Extract useful components of tag */

switch(Tag){

case TAG_MAKE:

strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31);

break;

//jsxyhelu添加的代码

case TAG_COPYRIGHT:

strncpy(m_exifinfo->CopyRight, (char*)ValuePtr,MAX_COMMENT-1);

break;

case TAG_MODEL:

strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39);

break;

case TAG_EXIF_VERSION:

strncpy(m_exifinfo->Version,(char*)ValuePtr, 4);

break;

case TAG_DATETIME_ORIGINAL:

strncpy(m_exifinfo->DateTime, (char*)ValuePtr, 19);

break;

case TAG_USERCOMMENT:

// Olympus has this padded with trailing spaces. Remove these first.

for (a=BytesCount;;){

a--;

if (((char*)ValuePtr)[a] == ‘ ‘){

((char*)ValuePtr)[a] = ‘\0‘;

}else{

break;

}

if (a == 0) break;

}

/* Copy the comment */

if (memcmp(ValuePtr, "ASCII",5) == 0){

for (a=5;a<10;a++){

char c;

c = ((char*)ValuePtr)[a];

if (c != ‘\0‘ && c != ‘ ‘){

strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, 199);

break;

}

}

}else{

strncpy(m_exifinfo->Comments, (char*)ValuePtr, 199);

}

break;

case TAG_FNUMBER:

/* Simplest way of expressing aperture, so I trust it the most.

(overwrite previously computd value if there is one)

*/

m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_APERTURE:

case TAG_MAXAPERTURE:

/* More relevant info always comes earlier, so only

use this field if we don‘t have appropriate aperture

information yet.

*/

if (m_exifinfo->ApertureFNumber == 0){

m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);

}

break;

case TAG_BRIGHTNESS:

m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_FOCALLENGTH:

/* Nice digital cameras actually save the focal length

as a function of how farthey are zoomed in.

*/

m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_SUBJECT_DISTANCE:

/* Inidcates the distacne the autofocus camera is focused to.

Tends to be less accurate as distance increases.

*/

m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_EXPOSURETIME:

/* Simplest way of expressing exposure time, so I

trust it most.  (overwrite previously computd value

if there is one)

*/

m_exifinfo->ExposureTime =

(float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_SHUTTERSPEED:

/* More complicated way of expressing exposure time,

so only use this value if we don‘t already have it

from somewhere else.

*/

if (m_exifinfo->ExposureTime == 0){

m_exifinfo->ExposureTime = (float)

(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));

}

break;

case TAG_FLASH:

if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){

m_exifinfo->FlashUsed = 1;

}else{

m_exifinfo->FlashUsed = 0;

}

break;

case TAG_ORIENTATION:

m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);

if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){

strcpy(m_szLastError,"Undefined rotation value");

m_exifinfo->Orientation = 0;

}

break;

case TAG_EXIF_IMAGELENGTH:

case TAG_EXIF_IMAGEWIDTH:

/* Use largest of height and width to deal with images

that have been rotated to portrait format.

*/

a = (int)ConvertAnyFormat(ValuePtr, Format);

if (ExifImageWidth < a) ExifImageWidth = a;

break;

case TAG_FOCALPLANEXRES:

m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_FOCALPLANEYRES:

m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_RESOLUTIONUNIT:

switch((int)ConvertAnyFormat(ValuePtr, Format)){

case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */

case 2:    m_exifinfo->ResolutionUnit = 1.0f; break;

case 3: m_exifinfo->ResolutionUnit = 0.3937007874f;    break;  /* 1 centimeter*/

case 4: m_exifinfo->ResolutionUnit = 0.03937007874f;   break;  /* 1 millimeter*/

case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f;  /* 1 micrometer*/

}

break;

case TAG_FOCALPLANEUNITS:

switch((int)ConvertAnyFormat(ValuePtr, Format)){

case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */

case 2:    m_exifinfo->FocalplaneUnits = 1.0f; break;

case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f;    break;  /* 1 centimeter*/

case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f;   break;  /* 1 millimeter*/

case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f;  /* 1 micrometer*/

}

break;

// Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>

case TAG_EXPOSURE_BIAS:

m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_WHITEBALANCE:

m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_METERING_MODE:

m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_EXPOSURE_PROGRAM:

m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_ISO_EQUIVALENT:

m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);

if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;

break;

case TAG_COMPRESSION_LEVEL:

m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_XRESOLUTION:

m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_YRESOLUTION:

m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_THUMBNAIL_OFFSET:

ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);

break;

case TAG_THUMBNAIL_LENGTH:

ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);

break;

}

if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){

unsigned char * SubdirStart;

SubdirStart = OffsetBase + Get32u(ValuePtr);

if (SubdirStart < OffsetBase ||

SubdirStart > OffsetBase+ExifLength){

strcpy(m_szLastError,"Illegal subdirectory link");

return 0;

}

ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP);

continue;

}

}

{

/* In addition to linking to subdirectories via exif tags,

there‘s also a potential link to another directory at the end

of each directory.  This has got to be the result of a

committee!

*/

unsigned char * SubdirStart;

unsigned Offset;

Offset = Get16u(DirStart+2+12*NumDirEntries);

if (Offset){

SubdirStart = OffsetBase + Offset;

if (SubdirStart < OffsetBase

|| SubdirStart > OffsetBase+ExifLength){

strcpy(m_szLastError,"Illegal subdirectory link");

return 0;

}

ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP);

}

}

if (ThumbnailSize && ThumbnailOffset){

if (ThumbnailSize + ThumbnailOffset <= ExifLength){

/* The thumbnail pointer appears to be valid.  Store it. */

m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset;

m_exifinfo->ThumbnailSize = ThumbnailSize;

}

}

return 1;

}

////////////////////////////////////////////////////////////////////////////////

/*--------------------------------------------------------------------------

Evaluate number, be it int, rational, or float from directory.

--------------------------------------------------------------------------*/

double Cexif::ConvertAnyFormat(void * ValuePtr, int Format)

{

double Value;

Value = 0;

switch(Format){

case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;

case FMT_BYTE:      Value = *(unsigned char *)ValuePtr;        break;

case FMT_USHORT:    Value = Get16u(ValuePtr);          break;

case FMT_ULONG:     Value = Get32u(ValuePtr);          break;

case FMT_URATIONAL:

case FMT_SRATIONAL:

{

int Num,Den;

Num = Get32s(ValuePtr);

Den = Get32s(4+(char *)ValuePtr);

if (Den == 0){

Value = 0;

}else{

Value = (double)Num/Den;

}

break;

}

case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;

case FMT_SLONG:     Value = Get32s(ValuePtr);                break;

/* Not sure if this is correct (never seen float used in Exif format)

*/

case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;

case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;

}

return Value;

}

////////////////////////////////////////////////////////////////////////////////

void Cexif::process_COM (const unsigned char * Data, int length)

{

int ch;

char Comment[MAX_COMMENT+1];

int nch;

int a;

nch = 0;

if (length > MAX_COMMENT) length = MAX_COMMENT; // Truncate if it won‘t fit in our structure.

for (a=2;a<length;a++){

ch = Data[a];

if (ch == ‘\r‘ && Data[a+1] == ‘\n‘) continue; // Remove cr followed by lf.

if ((ch>=0x20) || ch == ‘\n‘ || ch == ‘\t‘){

Comment[nch++] = (char)ch;

}else{

Comment[nch++] = ‘?‘;

}

}

Comment[nch] = ‘\0‘; // Null terminate

//if (ShowTags) printf("COM marker comment: %s\n",Comment);

strcpy(m_exifinfo->Comments,Comment);

}

////////////////////////////////////////////////////////////////////////////////

void Cexif::process_SOFn (const unsigned char * Data, int marker)

{

int data_precision, num_components;

data_precision = Data[2];

m_exifinfo->Height = Get16m((void*)(Data+3));

m_exifinfo->Width = Get16m((void*)(Data+5));

num_components = Data[7];

if (num_components == 3){

m_exifinfo->IsColor = 1;

}else{

m_exifinfo->IsColor = 0;

}

m_exifinfo->Process = marker;

//if (ShowTags) printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",

//               ImageInfo.Width, ImageInfo.Height, num_components, data_precision);

}

////////////////////////////////////////////////////////////////////////////////

//#include "stdafx.h"

//exif 写入函数

int Cexif::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)

{

UINT num= 0;

UINT size= 0;

ImageCodecInfo* pImageCodecInfo= NULL;

GetImageEncodersSize(&num, &size);

if(size== 0)

{

return -1;

}

pImageCodecInfo= (ImageCodecInfo*)(malloc(size));

if(pImageCodecInfo== NULL)

{

return -1;

}

GetImageEncoders(num, size, pImageCodecInfo);

for(UINT j=0; j< num; ++j)

{

if(wcscmp(pImageCodecInfo[j].MimeType, format)== 0)

{

*pClsid= pImageCodecInfo[j].Clsid;

free(pImageCodecInfo);

return j;

}

}

free(pImageCodecInfo);

return -1;

}

// 从内存加载图片,失败返回NULL

Bitmap* Cexif::LoadBitmapFromMemory(const void* memory, DWORD size)

{

Bitmap* bmp = NULL;

IStream* stream = NULL;

if (CreateStreamOnHGlobal(NULL, TRUE, &stream) == S_OK)

{

ULARGE_INTEGER uli;

uli.QuadPart = size;

stream->SetSize(uli);

if (stream->Write(memory, size, NULL) == S_OK)

bmp = new Bitmap(stream);

stream->Release();

}

return bmp;

}

// 从文件加载图片,不独占文件,失败返回NULL

Gdiplus::Bitmap* Cexif::LoadBitmapFromFile(const TCHAR* file_name)

{

Bitmap* bmp = NULL;

HANDLE file_handle = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (file_handle != INVALID_HANDLE_VALUE)

{

DWORD temp = 0;

DWORD file_size = GetFileSize(file_handle, &temp);

if (file_size && !temp)  // 不处理大于4G的文件

{

// 将图片文件读到内存后,再从内存创建Bitmap

unsigned char* buffer = new unsigned char[file_size];

if (ReadFile(file_handle, buffer, file_size, &temp, NULL))

bmp = LoadBitmapFromMemory(buffer, temp);

delete [] buffer;

}

CloseHandle(file_handle);

}

return bmp;

}

//写入exif信息到图片中去,目前只支持jpeg格式

//返回 0为正常 -1为异常

//各种tchar wchar cstring的转换,技巧较多.

int Cexif::WriteExif2Image(CString strFileName, int PropertyTag, CString pValue)

{

GdiplusStartupInput gdiplusStartupInput;

ULONG_PTR gdiplusToken;

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

Status stat;

CLSID  clsid;

char* propertyValue = new char[100];

strcpy(propertyValue,pValue);

Bitmap* bitmap = LoadBitmapFromFile(strFileName);

PropertyItem* propertyItem = new PropertyItem;

GetEncoderClsid(L"image/jpeg", &clsid);

propertyItem->id = PropertyTag;//给Tag赋值

propertyItem->length = 255;

propertyItem->type = PropertyTagTypeASCII;

propertyItem->value = propertyValue;

bitmap->SetPropertyItem(propertyItem);

WCHAR wcharbuf[100];

CStringW strWide = CT2W(strFileName);

wcscpy(wcharbuf,strWide);

stat = bitmap->Save(wcharbuf, &clsid, NULL);

if(stat != Ok)

return -1;

delete propertyItem;

delete bitmap;

GdiplusShutdown(gdiplusToken);

return 0;

}

时间: 2024-10-13 01:13:49

GOEXIF读取和写入EXIF信息的相关文章

Java读取图片和EXIF信息

后台需要读取客户端上传的图片,记录图片长宽,客户端以后就可以根据记录的长宽,展示图片. 正常的图片,读取很简单,代码如下: BufferedImage originalImg = ImageIO.read(file.getInputStream()); int originalwidth = originalImg.getWidth(); //上传图片的宽 int originalheight = originalImg.getHeight(); //上传图片的高 但是,有一种情况,客户端拍摄的

EXIF.Js:读取图片的EXIF信息

首先,EXIF是什么? EXIF(Exchangeable Image File)是“可交换图像文件”的缩写,当中包含了专门为数码相机的照片而定制的元数据,可以记录数码照片的拍摄参数.缩略图及其他属性信息,简单来说,Exif信息是镶嵌在 JPEG/TIFF 图像文件格式内的一组拍摄参数,需要注意的是EXIF信息是不支持png,webp等图片格式的.(建议自己试的时候,现拍一张,把地理位置信息开启,这样得到的是完整的EXIF信息) 在脚手架下的使用: npm install exif-js --s

读取图片信息(exif),使用com.drew.metadata.Metadata

---恢复内容开始--- 最近在开发中用到了metadata-extractor-xxx.jar 和 xmpcore-xxx.jar这个玩意, 索性查阅大量文章了解学习,来分享分享.本身工作也是经常和处理大图片打交道,摸索摸索也是多多益善. 首先介绍一下什么是EXIF,EXIF是 Exchangeable Image File 的缩写,这是一种专门为数码相机照片设定的格式.这种格式可以用来记录数字照片的属性信息,如相机的品牌及型号.相片的拍摄时间.拍摄时所设置的光圈大小.快门速度.ISO等信息.

Android 获取图片exif信息

使用android api读取图片的exif信息 布局代码: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_p

JAVA基础-输入输出:1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上。

1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上. package Test03; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOExceptio

java读取图片的(尺寸、拍摄日期、标记)等EXIF信息

1.metadata-extractor是 处理图片EXIF信息的开源项目,最新代码及下载地址:https://github.com/drewnoakes/metadata-extractor 2.本demo工程的代码(包含所需的jar包)下载地址:http://files.cnblogs.com/files/haha12/readPic.rar 主要代码如下: package com.test; import java.io.File; import java.io.IOException;

改动图片exif信息

我们先了解一下EXIF: EXIF能够附加于JPEG.TIFF.RIFF等文件之中.为其添加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本号信息. 全部的JPEG文件以字符串"0xFFD8"开头.并以字符串"0xFFD9"结束. 文件头中有一系列"0xFF?? "格式的字符串,称为"标识",用来标记JPEG文件的信息段."0xFFD8"表示图像信息開始."0xFFD9"表示图像信

Android 图片Exif信息相关的获取与修改

1 Exif是什么 Exif是一种图像文件格式,它的数据存储于JPEG格式是完全相同的,实际上Exif格式就是JPEG格式头插入了 数码照片的信息,包括拍摄的光圈.快门.平衡白.ISO.焦距.日期时间等各种和拍摄条件以及相机品牌.型号.色彩编码以及GPS等.简单来 说,Exif=拍摄参数+JPED.因此,可以利用任何可以查看JPEG文件的看图软件浏览Exif信息,但是并不是所有图形程序都能处理Exif信息, 而自Android2.0之后,加入了对图片Exif数据的支持. 2 ExifInterf

(四十三)获取图片exif信息

1.什么是Exif Exif是一种图像文件格式,它的数据存储于JPEG格式是完全相同的,实际上Exif格式就是JPEG格式头插入了数码照片的信息,包括拍摄的光圈.快门.平衡白.ISO.焦距.日期时间等各种和拍摄条件以及相机品牌.型号.色彩编码以及GPS等.简单来说,Exif=拍摄参数+JPED.因此,可以利用任何可以查看JPEG文件的看图软件浏览Exif信息,但是并不是所有图形程序都能处理Exif信息,而自Android2.0之后,加入了对图片Exif数据的支持. 2.ExifInterface