【问题标题】:Reliably identifying a JPG?可靠地识别 JPG?
【发布时间】:2019-01-04 14:21:51
【问题描述】:

为了识别和比较从相机拍摄的 JPG 图像,我想计算 JPG 内图像扫描部分的 MD5 哈希值。我的想法是获取 SOS 和 EOI 标记之间的字节并对这些字节执行哈希运算,假设这些字节永远不会改变,除非实际图像被处理和更改。

显然这个问题已经出现过好几次了1,2,3。有人提出了相当复杂的解决方案,这一事实让我看到我相当简单但显然有效的方法感到恼火。 (或者是不是太简单了?)

我知道一个 JPG 文件中可以有多对 SOS ($FFDA) 和 EOI ($FFD9),在我目前的文件中有 3 个:缩略图、实际图像和额外的 1920x1080 图像 (Sony)。我目前的做法是解析流并定位下一个 SOS,然后寻找 EOI,计算大小,如果大小超过文件大小的 50%,则假定为实际图像。

这种方法适用于我目前的文件。我使用exiftool -all= image.jpg 从 JPG 文件中剥离了所有元数据,发现 MD5 哈希是相同的。然而,算法对我来说似乎相当粗糙。 所以这是我的问题:

是否存在仅检查 SOS 和 EOI 之间的空间会失败的风险?我已阅读this,但仍不确定。

从实际图像的 SOS 中解析每个字节需要花费大量时间。我从here 中得知,没有捷径可以找到压缩数据的结尾。但我可能会从第二个 SOS 标记向前跃进 80% 左右。我说的是来自相机的图像 - 我能在多大程度上依赖这样一个事实:先是缩略图,然后是实际图像?

我应该在 SOS 之后 6 个字节开始 (here?)

有什么更好的方法吗?

【问题讨论】:

    标签: parsing delphi jpeg md5


    【解决方案1】:

    在进行了一些研究并在这里运行了一堆测试之后,我提出了我的问题的解决方案。

    首先,我想澄清一下,我们不是在谈论法医调查。可能有一些方法可以处理 JPG 图像,使标记出现在它们不应该出现的位置,并且不会出现在根据规范必须出现的位置。

    我们也谈论图像身份或相似性。如果您无损地旋转 JPG,您仍然拥有相同的图像信息,但不再是相同的图像。我们也不是在谈论以任何其他方式调整大小、优化或更改的图像。

    什么我们正在谈论的是识别简单的重复或 JPG 已重命名或元数据已被修改或删除,但图像本身从未被处理过或以任何方式篡改。

    SOS 和 EOI 标记之间的字节散列是唯一识别图像的可靠方法吗?

    是的,是的。在合理的范围内,图像扫描数据的具有相同 MD5 校验和的两个文件不可能包含不同的图像,反之亦然。
    我检查了使用来自 12 个不同制造商的相机拍摄的示例照片,并编辑/剥离了元数据。实际上,这并不是真正必要的,因为从规范和代码中您知道所有元数据都驻留在单独的块中(这就是为什么您可以在 JPG 中隐藏所有类型的东西)和扫描数据元数据操作永远不会触及,但是是的,到处都是相同的 MD5 校验和。

    有什么方法可以快速找到(正确的)SOS 标记?

    当然。 JPG规格是一团糟和一种惩罚。在尝试了相当多的代码之后,我发现Nils HaeckNativeJPG 是最直接的。 本文改编自sdJpegImage

    function FindSOSPos(S: TStream): Cardinal;
    var
      B, MarkerTag, BytesRead: byte;
      Size,W: word;
    const
      mkNone = 0; mkSOF0 = $c0; mkSOF1 = $c1; mkSOF2 = $c2; mkSOF3 = $c3; mkSOF5 = $c5; 
      mkSOF6 = $c6; mkSOF7 = $c7; mkSOF9 = $c9; mkSOF10 = $ca; mkSOF11 = $cb; mkSOF13 = $cd; 
      mkSOF14 = $ce; mkSOF15 = $cf; mkDHT = $c4; mkDAC = $cc; mkSOI = $d8; mkEOI = $d9; mkSOS = $da; 
      mkDQT = $db; mkDNL = $dc; mkDRI = $dd; mkDHP = $de; mkEXP = $df; mkAPP0 = $e0; mkAPP15 = $ef; mkCOM = $fe; 
    begin
      Repeat
        Result := 0;
        // Read markers from the stream, until a non $FF is encountered
        If S.Read(B, 1) = 0 then
          exit;
        // Do we have a marker?
        if B = $FF then
        begin
          BytesRead := S.Read(MarkerTag, 1);
          while (BytesRead > 0) and (MarkerTag = $FF) do
          begin
            MarkerTag := mkNone;
            BytesRead := S.Read(MarkerTag, 1);
          end;
          Size := 0;
          if MarkerTag in [mkAPP0..mkAPP15, mkDHT, mkDQT, mkDRI,
            mkSOF0, mkSOF1, mkSOF2, mkSOF3, mkSOF5, mkSOF6, mkSOF7, mkSOF9, mkSOF10, mkSOF11, mkSOF13, mkSOF14, mkSOF15,
            mkCOM, mkDNL] then
          begin
            // Read length of marker
            If S.Read(W, 2) = 2 then
              Size := Swap(W) - 2
            else exit;
          end else
            If MarkerTag = mkSOS
              then break;
          S.Position := S.Position + Size;
        end else
        begin
          // B <> $FF is an error, we try to be flexible
          repeat
            BytesRead := S.Read(B, 1);
          until (BytesRead = 0) or (B = $FF);
          if BytesRead = 0 then
            exit;
          S.Seek(-1, soFromCurrent);
        end;
      Until (MarkerTag = mkSOS) or (MarkerTag = mkNone);
      Result := S.Position;
    end; 
    

    省略 SOS 标记后的前 6 个字节?

    我决定对 SOS 和 EOI 之间的所有内容进行哈希处理,不包括标记本身。

    有没有快速定位尾随 EOI 标记的方法?

    没有。但这无关紧要,因为要执行哈希,无论如何您都必须读取每个字节。

    这种方法的可靠性如何?

    正如我所说,我相信在合理范围内这种方法不会产生误报的可能性实际上是 100%。至于找到正确的图像:NativeJPG 已经存在了 10 多年,你发现很少有人抱怨,如果他们处理图像解码,而不是丢失它。

    在我的应用程序中,我提供了在 UserComment 字段中存储原始文件名、EXIF DateTimeDigitized、相机品牌、GPS 坐标和扫描数据的 MD5 散列(完整和前 16 kB)的选项。我非常有信心这将允许以后在大多数情况下识别文件(如果 UserComment 保持不变)。

    【讨论】:

      【解决方案2】:

      是否存在仅检查 SOS 和 EOI 之间的空间会失败的风险?

      是的,如果您只是对扫描数据进行校验和,则出于您的目的。它们之间可能有多个 SOS 标记和其他标记。

      【讨论】:

      • 我不确定我是否正确,但是一旦您找到了正确的 SOS IMO,您所要做的就是解析下一个 EOI 标记。是否有任何标记以及介于两者之间的标记都没有关系,必须在扫描数据的末尾有一个 EOI 标记。
      • 是的,但是有些标记的位置可以移动,而不会影响其中可能出现的图像内容。
      • 如果是这样的话,如果不以某种方式处理图像,它就永远不会发生。更改元数据可以以任何方式触及扫描数据吗?据我所知,它们都是独立的、独立的块。我会对数据身份感到满意,即使我可能会错过图像身份。但后者不是纯粹理论上的可能性吗?
      • 更改数据超出理论范围但在实践中并不常见,除非您在其上运行一些图像处理程序。但是您显然没有看到任何变化。
      猜你喜欢
      • 2011-05-21
      • 1970-01-01
      • 2011-07-24
      • 2012-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-26
      • 1970-01-01
      相关资源
      最近更新 更多