/* * jpg header reader * JPEG 画像の セグメント情報・Exif 情報を読みとる * * version: 0.3 * author: itouh * Time-stamp: * Copyright (c) 2003 Itou Hiroki */ // '*marker' means necessary segments // マーカー名の前に「*」がついているものは必須セグメント #include #include /* calloc() */ #include #include #define errret { fprintf(stderr, "err\n"); exit(1); } int main(int argc, char *argv[]); unsigned char *segskip(unsigned char *p); void exifshow(unsigned char *exiftop); unsigned short big2ltls(void *v); unsigned long big2ltll(void *v); size_t filelen; unsigned char *src; int main(int argc, char *argv[]){ unsigned char *p; FILE *fin; struct stat info; char *fname; const unsigned char *marker[] = { "\xd8 *SOI(start of image)", "\xc4 *DHT(define huffman table)", "\xdb *DQT(define Quantization table)", //"\xe0 APP0(JFIF segment marker)", "\xfe COM(comment)", "\xcc DAC(define arithmetic table)", "\xc8 JPG(reserved)", }; if(argc<=1 || argc >=3){ printf("jpghd - reading JPEG header and Exif info\n" "usage: jpghd \n"); return 1; } fname = argv[1]; /* * file open & memory allocate */ if((fin=fopen(fname,"rb")) == NULL) errret; if(stat(fname, &info) != 0) errret; filelen = info.st_size; if((src=malloc(filelen)) == NULL) errret; if(fread(src,1,filelen,fin) != filelen){ free(src); errret; } /* * validity check * 最初の2バイトは SOI というmarker。 * marker とは、各セグメント最初の2バイトにある識別子。 * PNGでいうchunkname。 */ if(*src==0xff && *(src+1)==0xd8){ p = src + 2; //SOI(start of image)(2bytes) printf("ff d8 *SOI(start of image) Length: 0 bytes\n"); }else{ free(src); errret; } for(;;){ int i; int flag; /* * セグメント先頭の 'ff'をサーチ */ if(*p!=0xff){ //ヘッダ部ではなく、圧縮データ部 //printf(" compressed data\n"); while(*p!=0xff) p++; } /* * セグメント種を調べる */ flag = 0; for(i=0; i=0xc0 && n<=0xcf){ printf("ff %02x *SOF%d(start of frame)", n, n-0xc0); p = segskip(p); }else if(n>=0xe0 && n<=0xef){ unsigned char *exiftop; char *str = calloc(5, sizeof(char)); memcpy(str, p+4, 4); printf("ff %02x APP%d('%s' application data)", n, n-0xe0, str); exiftop = p + 2 + 2 + 6; //APP1marker + APP1size + APP1str p = segskip(p); if(strcmp(str, "Exif")==0) exifshow(exiftop); }else if(n==0xd9){ printf("ff d9 *EOI(end of image)\n"); free(src); return 0; }else if(n==0xda){ printf("ff da *SOS(start of scan)"); p = segskip(p); printf(" compressed data\n"); }else if(n==0xdd){ //restart printf("ff dd DRI(define restart interval)"); p = segskip(p); }else if(n>=0xd0 && n<=0xd7){ printf("ff %02x RST%d(restart interval termination)\n", n, n-0xd0); p += 2; printf(" compressed data\n"); }else if(n==0x00 || n==0xff){ // markerじゃなくて圧縮データの一部 //must be skipped //printf("ff %02x not marker, but compress data\n", n); p += 2; }else{ printf("ff %02x unknown marker\n", n); p += 2; } }//if(!flag }//for(;; } /* * 次のセグメントまでスキップする */ unsigned char *segskip(unsigned char *p){ unsigned short next; //次のセグメントまでの相対距離 /* * セグメントの長さを調べる * segment lengthの示す長さは、marker(0xff 0x??)の2バイトを * 含んでいない。 * segment length 自身の2バイトは含まれている。 */ p += 2; //skip marker next = *p<<8 | *(p+1); printf(" Length: %d bytes\n", next); if(p+next >= src+filelen){ fprintf(stderr, "filesize err\n"); exit(1); } return p + next; //skip this segment } /* * Exif データを読みとって表示する */ void exifshow(unsigned char *exiftop){ unsigned char *p = exiftop; int bigEndian = 0; unsigned short dentry = 0; unsigned char *exifsubifd = NULL; int i; char *exiftag[] = { "\x01\x0e ImageDescription", "\x01\x0f Make", "\x01\x10 Model", "\x01\x12 Orientation", "\x01\x1a XResolution", "\x01\x1b YResolution", "\x01\x28 ResolutionUnit", "\x01\x31 Software", "\x01\x32 DateTime", "\x01\x3e WhitePoint", "\x01\x3f PrimaryChromaticities", "\x02\x11 YCbCrCoefficients", "\x02\x13 YCbCrPositioning", "\x02\x14 ReferenceBlackWhite", "\x82\x98 Copyright", "\x87\x69 ExifIFDPointer (ExifOffset)", "\x82\x9a ExposureTime", "\x82\x9d FNumber", "\x88\x22 ExposureProgram", "\x88\x27 ISOSpeedRatings", "\x90\x00 ExifVersion", "\x90\x03 DateTimeOriginal", "\x90\x04 DateTimeDigitized", "\x91\x01 ComponentsConfiguration", "\x91\x02 CompressedBitsPerPixel", "\x92\x01 ShutterSpeedValue", "\x92\x02 ApartureValue", "\x92\x03 BrightnessValue", "\x92\x04 ExposureBiasesValue", "\x92\x05 MaxApertureValue", "\x92\x06 SubjectDistance", "\x92\x07 MeteringMode", "\x92\x08 LightSource", "\x92\x09 Flash", "\x92\x0a FocalLength", "\x92\x7c MakerNote", "\x92\x86 UserComment", "\x92\x90 SubsecTime", "\x92\x91 SubsecTimeOriginal", "\x92\x92 SubsecTimeDigitized", "\xa0\x00 FlashPixVersion", "\xa0\x01 ColorSpace", "\xa0\x02 ExifImageWidth", "\xa0\x03 ExifImageHeight", "\xa0\x04 RelatedSoundFile", "\xa0\x05 InteroperabilityIFDPointer", "\xa2\x0e FocalPlaneXResolution", "\xa2\x0f FocalPlaneYResolution", "\xa2\x10 FocalPlaneResolutionUnit", "\xa2\x15 ExposureIndex", "\xa2\x17 SensingMethod", "\xa3\x00 FileSource", "\xa3\x01 SceneType", "\xa3\x02 CFAPattern", "\xa4\x01 CustomRendered", "\xa4\x02 ExposureMode", "\xa4\x03 WhiteBalance", "\xa4\x04 DigitalZoomRatio", "\xa4\x05 FocalLengthIn35mmFilm", "\xa4\x06 SceneCaptureType", "\xa4\x07 GainControl", "\xa4\x08 Contrast", "\xa4\x09 Saturation", "\xa4\x0a Sharpness", "\xa4\x0b DeviceSettingDescription", "\xa4\x0c SubjectDistanceRange", "\xa4\x20 ImageUniqueID", }; //endian チェック if(*p=='M' && *(p+1)=='M'){ bigEndian = 1; //Motorola(BigEndian) }else if(*p=='I' && *(p+1)=='I'){ bigEndian = 0; //Intel(LittleEndian) } printf(" Exif Data (%s)\n", bigEndian==1 ? "BigEndian":"LittleEndian"); //get offset to first IFD(ImageFileDirectory) if(bigEndian){ p += big2ltll(p+4); }else{ p += *(unsigned long*)(p+4); } //get directory entry num IFDtop: if(bigEndian) dentry = big2ltls(p); else dentry = *(unsigned short *)p; printf(" directory entry:%d\n", dentry); p += 2; //get directory entry for(i=0; i