为现有图像处理程序添加读写exif的功能

Posted GreenOpen专注图像处理

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为现有图像处理程序添加读写exif的功能相关的知识,希望对你有一定的参考价值。

为现有图像处理程序添加读取exif的功能

exif是图片的重要参数,在使用过程中很关键的一点是exif的数据能够和图片一起存在。exif的相关功能在操作系统中就集成了,在csharp中也似乎有了实现。但是使用mfc来实现这个功能,的确费了我的一些时间。下面是我的实现方式。
读取exif,参考了网络资料
添加cexif.h和cexif.cpp,添加入现有程序
在需要读取的地方添加相关代码
 
FILE* hFile=fopen(FilePathName.c_str(),"rb");
        if (hFile){
            memset(&m_exifinfo,0,sizeof(EXIFINFO));
            Cexif exif(&m_exifinfo);
            exif.DecodeExif(hFile);
            fclose(hFile);
            if (m_exifinfo.IsExif) 
            {
                string cameramaker =  m_exifinfo.CopyRight;//获得CopyRight
                vector<string> vecParams;
                if ("@@" == cameramaker.substr(0,2)) //确认为带参数图片
                {
                    cameramaker = cameramaker.substr(2,cameramaker.length()-2);
                    SplitString(cameramaker,vecParams,"|");//保存各参数到vecParams中去
                    //updateGuides给的是图像真实像素值
                    if (vecParams.size()<13)
                        return;
                    m_freal2screen = Str2RealPixF(vecParams[12]);//获得之前保存的比值
                    if (m_freal2screen<0)
                        return;
                    UpdateGuides(Str2RealPix(vecParams[0]),Str2RealPix(vecParams[1]),Str2RealPix(vecParams[2]),Str2RealPix(vecParams[3]),Str2RealPix(vecParams[4]),Str2RealPix(vecParams[5]),Str2RealPix(vecParams[6]),Str2RealPix(vecParams[7]),Str2RealPix(vecParams[8]),Str2RealPix(vecParams[9]),Str2RealPix(vecParams[10]),Str2RealPix(vecParams[11]));
                }
            }
            else 
                MessageBox("没有找到矢量数据信息!","EXIF",MB_OK);
        }
        fclose(hFile);

  

 
但是这里的exif读取的信息不实很全
同时找到另一个代码,但是版本比较老,只能在vc6下面运行
这个时候,我需要考虑的就是写入的数据是否够长的问题。我进行了边界测试
经过测试,在这种模式下是没有问题的
对原有代码进行初步修改就可以。考虑到还是现有集成的比较好用,还是用集成好的。但是集成好的版本没有 版权这个段?这个如何办了?
反思一想,问题不就出现在位置信息吗?我将正确的版权信息覆盖掉make信息,问题解决。
最后提供我修改完成的带版权信息的exif.c。
解决问题的思路很重要。

附全部代码

/*
 * File:    exif.cpp
 * Purpose:    cpp EXIF reader
 * 16/Mar/2003 <ing.davide.pizzolato@libero.it>
 * based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>
 */
#include "stdafx.h"
#include "Exif.h"

////////////////////////////////////////////////////////////////////////////////
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_CP              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;
            case TAG_CP:
                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

以上是关于为现有图像处理程序添加读写exif的功能的主要内容,如果未能解决你的问题,请参考以下文章

使用 libexif 和 libjpeg 在现有 JPEG 上设置 exif 标签

使用C ++修改EXIF数据

使用 EXIF 或光标的图像方向始终为 0

Swift如何修改从移动相机拍摄的图像中的exif信息

手机safari图片上传竖变横处理

从图库中选择的 Android 图像方向始终为 0:Exif TAG