通过将标题尺寸与实际数据长度进行比较来检测截断的 jpeg 图像

Posted

技术标签:

【中文标题】通过将标题尺寸与实际数据长度进行比较来检测截断的 jpeg 图像【英文标题】:detect truncated jpeg images by comparing header dimensions to actual data length 【发布时间】:2015-07-25 04:03:37 【问题描述】:

PowerShell 脚本检索从手机发送的入站邮件消息,并将 jpeg 文件附件存储在数据库中。不幸的是,邮件消息通常是从小区服务较差的地区发送的,并且邮件消息被截断,通常是在附件中间。即使邮件消息已被截断,邮件服务器仍会接受它们。正如 Stack Overflow 和其他地方的一些帖子中所述,检查附件是否完整的一种可能方法是查找标记 jpeg 文件结尾的 FF D9 字节:

$binaryReader = New-Object BinaryReader([File]::Open($filePath, [FileMode]::Open)) 
$binaryReader.BaseStream.Seek(-2, [SeekOrigin]::End)
[byte[]]$bytes = New-Object byte[] 2
$binaryReader.Read($bytes, 0, 2)
if (($bytes[0] -eq 0xFF) -and ($bytes[1] -eq 0xD9)) 

不幸的是,对于某些移动运营商或可能是移动运营商和手机操作系统的组合,jpeg 图像似乎附加了额外的字节。生成的 jpeg 图像不会被截断,可以在 ImageMagick 中加载并使用标准图形查看器查看,但上述测试将失败。许多 jpeg 附件以可变数据块结尾,以以下八字节序列结尾:0x57 0x40 0x40 0x43 0x72 0x65 0x65 0x66,但还有其他变体。

我突然想到,如果 jpeg 标头指定图像的高度和宽度,也许有一种不同的方法来测试截断。代码可以加载图像并尝试读取右下角的像素并查看是否有错误。

$bitmap = [System.Drawing.Bitmap]::FromFile($filePath)
$pixelColor = $bitmap.GetPixel($bitmap.Width - 1, $bitmap.Height - 1)

我抓取了一个被严重截断的 jpeg 文件——该文件的文件大小很小,当在图像查看器中显示时,照片顶部有一个可见的矩形条,但其余部分是空白的。对文件运行上述代码时,位图对象的宽度和高度为 2560 x 1536,这是非压缩文件的典型尺寸。我希望检索最后一个像素颜色的 GetPixel 调用会返回 null 或抛出异常,但它没有。它返回一个 RGB 值,就像文件没有被截断一样。

我在 Windows Server 2012 上的 PowerShell 4 和 .NET Framework 4 下运行此代码。我认为在实例化位图对象时,.NET 可能分配了一个足够大的内存缓冲区,以根据以下尺寸保存位图jpeg 标头,然后加载尽可能多的数据。但是,当我对右下角附近的各种像素进行采样时,颜色对象有数据。这是位置 x=2559, y=1535 的颜色值:R:114, G:113, B:111。

这看起来不是在没有可用数据时使用的默认灰色,因为其他相邻像素具有不同的值。值得一提的是,我在空白区域看到的小样本像素的 RGB 值往往在 110 到 116 之间。相比之下,左上角的 RGB 值差异更大。

为什么这种方法不起作用?输入截断的文件时,为什么 .NET Framework 位图对象不会引发错误?幻象像素颜色值是否来自未初始化的内存?在提出可靠的截断测试方法方面我还应该尝试什么其他方法?

【问题讨论】:

【参考方案1】:

ImageMagick 将检测截断的 JPEG 文件。例如:

$ convert -regard-warnings truncated.jpg x.png
convert: Premature end of JPEG file `truncated.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.
convert: Corrupt JPEG data: premature end of data segment `truncated.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.
$ echo $?
1

-regard-warnings 标志使convert 在警告时返回非零退出代码。

另外,IJG JPEG decoder 将警告截断的文件。如果你准备写一些 C,你可以在你的图像上运行它。

这个过程大概是这样的:

    将解压器指向您的文件。

    重复获取扫描线,直到您看到整个图像。

    检查错误管理器中的num_warnings 字段。如果它> 0,你有问题。

分发中的example.c 非常有帮助。还有libjpeg-turbo,它与 IJG 解码器的 ABI 兼容,如果速度是个问题,它的速度要快得多。

【讨论】:

【参考方案2】:

其实,判断图片是否被截断很简单:不会有EOI市场。同样,如果在 EOS 行情之后有数据,则添加了一些额外的内容。

JPEG 解压缩过程将始终使用 SOF 标记中的图像大小来解码扫描。

【讨论】:

以上是关于通过将标题尺寸与实际数据长度进行比较来检测截断的 jpeg 图像的主要内容,如果未能解决你的问题,请参考以下文章

访问将通配符值与表进行比较

SQLServerException:将截断字符串或二进制数据的解决方法

显示尺寸最大时 parentWidth 返回错误值?

coco数据集图片尺寸不一

通过将 JSON 数据与数组进行比较来创建数组

如何将检测到的人脸从函数传递到 Qt 中的单独按钮槽