获取JPEG图像分辨率的方法,无需解码图像

6
我将尝试在不解码文件的情况下获取JPEG图像的分辨率。我从互联网上获得了几个样本,但没有一个正常工作。这似乎是因为许多JPEG文件不是标准的,尽管任何图形应用程序(Irfan、PSP、Firefox等)都可以打开它们。
JPEG头应该是:
typedef struct _JFIFHeader
{
  BYTE SOI[2];          /* 00h  Start of Image Marker     */
  BYTE APP0[2];         /* 02h  Application Use Marker    */
  BYTE Length[2];       /* 04h  Length of APP0 Field      */
  BYTE Identifier[5];   /* 06h  "JFIF" (zero terminated) Id String */
  BYTE Version[2];      /* 07h  JFIF Format Revision      */
  BYTE Units;           /* 09h  Units used for Resolution */
  BYTE Xdensity[2];     /* 0Ah  Horizontal Resolution     */
  BYTE Ydensity[2];     /* 0Ch  Vertical Resolution       */
  BYTE XThumbnail;      /* 0Eh  Horizontal Pixel Count    */
  BYTE YThumbnail;      /* 0Fh  Vertical Pixel Count      */
} JFIFHEAD;

然而,当我查看其中一个非标准文件时,Xdensity和Ydensity字段是错误的。但是,所有图形应用程序都可以读取此非标准文件。

有人知道一段Delphi代码,可以实际上读取所有JPEG文件吗?


Delphi 7,Win 7 32位

4个回答

16

我不确定所有JPEG文件都是这样,但您需要处理JPEG的两种常见的文件格式。由于JPEG是一种压缩方法而不是文件格式,因此全球采用了几种将JPEG图像数据存储在文件中的方式。您最有可能遇到的是JFIF和EXIF。上面的代码覆盖了JFIF,但没有处理EXIF。这两者基本不兼容,但两者都是JPEG,因此如果使用标头信息,则需要检测并处理它们。

例如,对于分辨率问题。 EXIF的字段为x-Resolutiony-Resolution,与X / Y密度方法不同。

我的建议是:

  1. 阅读有关这两种格式(JFIF和EXIF)的一些文献资料。我发现维基百科是一个很好的起点。

    JFIF: http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format

    EXIF: http://en.wikipedia.org/wiki/Exif

  2. 编写代码来检测格式使用开头的标头

  3. 独立地处理每个格式

  4. 包装整个过程,以便您只需将JPEG投入其中即可获得密度。这还将为您提供一个很好的位置来放置其他帮助程序代码,以处理“有趣”的JPEG处理世界。


10

这里有一些代码可以帮助你获取想要的数据:

function GetJpegSize(jpeg: TMemoryStream; out width, height, BitDepth: integer): boolean;
var n: integer;
    b: byte;
    w: Word;
begin
  result := false;
  n := jpeg.Size-8;
  jpeg.Position := 0;
  if n<=0 then
    exit;
  jpeg.Read(w,2);
  if w<>$D8FF then
    exit; // invalid format
  jpeg.Read(b,1);
  while (jpeg.Position<n) and (b=$FF) do begin
    jpeg.Read(b,1);
    case b of
      $C0..$C3: begin
        jpeg.Seek(3,soFromCurrent);
        jpeg.Read(w,2);
        height := swap(w);
        jpeg.Read(w,2);
        width := swap(w);
        jpeg.Read(b,1);
        BitDepth := b*8;
        Result := true; // JPEG format OK
        exit;
      end;
      $FF:
        jpeg.Read(b,1);
      $D0..$D9, $01: begin
        jpeg.Seek(1,soFromCurrent);
        jpeg.Read(b,1);
      end;
      else begin
        jpeg.Read(w,2);
        jpeg.Seek(swap(w)-2, soFromCurrent);
        jpeg.Read(b,1);
      end;
    end;
  end;
end;

2
JPEG文件头中的单位(Units)、Xdensity和Ydensity成员指定了描述打印时使用的物理点密度的计量单位。
- 如果单位(Units)为1,则Xdensity和Ydensity以每英寸点数(dpi)为单位。 - 如果单位(Units)为2,则Xdensity和Ydensity以每厘米点数(dpcm)为单位。
关键在于图像文件中存储的点分辨率(缩放打印分辨率)在屏幕上根本不重要。因此,Windows程序对于任何文件都会在屏幕上显示96个逻辑ppi。请注意,某些应用程序更喜欢在屏幕上显示72个逻辑ppi,例如Adobe应用程序。
图形应用程序(如ACDSee、Adobe Photoshop、CorelDRAW等)在显示JPG文件时,通常会忽略单位XdensityYdensity成员,但如果这些成员存在,则在打印JPG文件时,图形应用程序会考虑这些成员的值。如果一个JPG文件没有单位XdensityYdensity成员,图形应用程序会使用它们自己的默认值(通常为150 dpi)来打印JPG文件。
因此,对于一个能够读取所有JPEG头文件的Delphi代码的问题,答案很简单,只需要读取JPG文件头信息;如果可选成员在文件中不存在,就忽略这些可选成员或告诉最终用户这些成员当前未在文件中指定。
有关DPI和PPI混淆的更多阅读: JPEG文件格式规范参考资料:

0

有一个TP/TPW/Delphi(1-4,但可能在没有大修改的情况下工作到unicode版本)包pasjp(e)g,可以读取大多数旧的JPG类型(但不包括JPEG2000)

FPC也包括了这个包。

J. Nommsi的原始网站已经消失,但该软件包仍然可用,例如从以下网址:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接