【问题标题】:How large is a jpeg file without all its codec information?没有所有编解码器信息的 jpeg 文件有多大?
【发布时间】:2017-12-31 14:49:57
【问题描述】:

我想知道没有 jpeg 的 meta、header 和 codec 信息的 jpeg 文件有多大。所以最后你将只检索我认为由 DCT 系数和量化和霍夫曼表组成的压缩像素数据。

但是如何用 Python 或 C/C++ 提取这些数组的大小呢?

我确实尝试使用 libjpeg,但没有找到计算压缩数据大小的方法。

【问题讨论】:

  • 我很确定 libjpeg [我刚刚阅读了它的文档] 不会给你这个信息(直接,不改变源)。由于 Python 的 JPEG 代码基于 libjpeg,我怀疑它是否会有所帮助。您显然可以编写代码来读取标题等,然后从整体大小中减去元数据大小。
  • 你为什么想知道?没有元数据,其余的无论如何都是无用的。
  • 我相信 JPEG 文件的开头大约有 9 个字节的纯标识数据。解码需要其他所有内容。
  • @JohnAnderson:有很多可选字段允许文件制作者添加额外的元数据——例如相机的曝光设置[以及型号、镜头焦距、闪光灯是否使用等]、用户版权信息、照片编辑器数据和许多其他内容。这种信息绝对不是解码实际图像内容所需信息的一部分。因此,虽然最小 JPEG 的开销可能非常低,但这并不意味着在某些情况下不能添加大量信息。
  • @JesperJuhl 我想将 jpeg 与利用生成对抗网络的压缩方法进行比较,该方法仅输出单个向量作为压缩数据。因为我只会处理 64x64x3 的图像,所以与神经网络方法相比,编解码器信息的开销对压缩率有重要影响。

标签: python c++ compression jpeg


【解决方案1】:

这是一段代码,大致完成了所要求的工作。我不相信任何图书馆都有“现成”的解决方案。

它不是很整洁,也没有我希望的那么简单。我从我的“~/Pictures”文件夹(以及其他一些地方)中运行了数百张随机图像,但不能保证它会处理“任何”图像 - 我认为我所有的图像都是由两个不同的应用程序创建的。 “其他一些人”可能不会,但同样,不同的生产者可能会使用不同的字段,或者以该代码无法处理的形式生成数据。如果它坏了,您可以保留所有部件,但不予退款。

我将留给读者来确定哪些是实际图像数据,哪些不是。请注意,块大小不包括块头本身,即顶部的另外 2 个字节。

哦,是的,这是 C++ 和 C 的混合体。我只是将它与我​​为不同目的而放置的部分代码一起破解,并整理它,所以它不是一个完整的混乱,但它是由不意味着我最好的每一个编码......

我也在这里添加了代码: https://github.com/Leporacanthicus/jpegrd

#include <fstream>
#include <ios>
#include <vector>
#include <cstdint>


#define die(str, ...) \
    do { printf(str, __VA_ARGS__); exit(1); } while(0)

void read_bytes(std::ifstream &f, uint8_t *buffer, std::streamsize sz)
{
    if(!f.read(reinterpret_cast<char*>(buffer), sz))
    {
        die("Expected to read %zd bytes\n", sz);
    }
}

uint32_t read_size(std::ifstream &f)
{
    uint8_t buffer[2];
    read_bytes(f, buffer, 2);
    uint32_t size = buffer[0] << 8 | buffer[1];
    return size;
}

void skip_size(std::ifstream &f, std::streamsize to_skip)
{
    f.seekg(to_skip - 2, std::ios_base::cur);
}

void check_buffer(uint8_t *buffer, const std::vector<uint8_t> &val)
{
    uint8_t *b = buffer;
    for(auto v : val)
    {
        if (*b != v)
        {
            die("Mismatch! Expected %02x, got %02x\n", v, *b);
        }
        b++;
    }
}

uint32_t find_next_header(std::ifstream &f)
{
    uint8_t b;
    bool found = false;
    uint32_t count = 0;
    do
    {
        read_bytes(f, &b, 1);
        if (b == 0xFF)
        {
            if (f.peek() == 0x00)
            {
                read_bytes(f, &b, 1);
                count+= 2;
            }
            else
            {
                f.unget();
                found = true;
            }
        }
        else
        {
            count++;
        }
    } while(!found);

    return count;
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("Expected filename as argument\n");
        exit(1);
    }

    std::ifstream f(argv[1], std::ios_base::in|std::ios_base::binary);
    if (!f)
    {
        die("Couldn't open the file %s\n", argv[1]);
    }

    uint8_t buffer[2];
    uint32_t total = 0;

    read_bytes(f, buffer, 2);
    check_buffer(buffer, {0xFF, 0xd8});
    total += 2;

    bool eoi = false;
    do
    {
        uint32_t size;
        read_bytes(f, buffer, 2);
        if (buffer[0] != 0xff)
        {
            die("Expected 0xFF byte, got %02x at offset %zu\n", 
                buffer[0], (size_t)f.tellg());
        }
        total += 2;
        switch(buffer[1])
        {
        case 0xE0:
        case 0xE1:
        case 0xE2:
        case 0xE3:
        case 0xE4:
        case 0xE5:
        case 0xE6:
        case 0xE7:
        case 0xE8:
        case 0xE9:
        case 0xEA:
        case 0xEB:
        case 0xEC:
        case 0xED:
        case 0xEE:
        case 0xEF:
            size = read_size(f);
            total += size;
            printf("APP Data Type %02x: %u bytes of application data\n",
                   buffer[1], size);
            skip_size(f, size);
            break;

        case 0xDB:
            size = read_size(f);
            total += size;
            printf("DQT: %u bytes of quantization data\n", size);
            skip_size(f, size);
            break;

        case 0xC0:
        case 0xC2:
            size = read_size(f);
            total += size;
            printf("SOF: %u bytes of frame data\n", size);
            skip_size(f, size);
            break;

        case 0xC4:
            size = read_size(f);
            total += size;
            printf("DHT: %u bytes of huffman tables\n", size);
            skip_size(f, size);
            break;

        case 0xDA:
            size = read_size(f);
            skip_size(f, size);
            size += find_next_header(f);
            total += size;
            printf("SOS: %u bytes of scan data\n", size);
            break;

        case 0xD9:
            printf("EOI: end of image\n");
            eoi = true;
            break;

        case 0xFE:
            size = read_size(f);
            skip_size(f, size);
            total += size;
            printf("COM: comment %u bytes\n", size);
            break;

        default:
            die("Expected known encoding byte, got %02x\n", buffer[1]);
            break;
        }
    } while(!eoi);
    printf("Total size = %u\n", total);
}

【讨论】:

  • 不幸的是,它仅适用于某些图像。像这样的一些失败:imgur.com/a/YvrOc.
  • “不起作用”是什么意思?我下载了这些图像,它们都通过程序并带有一些输出——我没有对它应该是什么以及它是什么付出任何努力,但输出看起来一般。这当然可能是图像实际上与您上传的内容不同(许多网站在上传过程中重新编码其图像,主要是为了减小大小),因此您可能需要首先检查问题是否可重现从您的链接下载的图像。然后更详细地描述出了什么问题。
  • 对不起,这是我的代码中的一个错误 :( 一切正常。
【解决方案2】:

元数据的数量完全取决于编码器。 JPEG 流中唯一需要的标头是 2 字节 SOI 标记。唯一的页脚是 2 字节的 EOI 标记。

其他的就是编码器放入的任何东西。

【讨论】:

  • 实际上,该编码器添加了合理的数量。我开始编写一个程序来从标头字段中读取 JPEG 文件中的内容,但这比仅读取标记和长度要复杂一些。但是,我的 4.1K 数据文件中有大约 3160 字节的元数据(据我现在所知)——来自 GIMP。相同的输入文件,具有较低的压缩设置(图片中的更多细节)似乎具有相同的 3160 字节元数据。
  • 那是哪一个?还是你自己自制的?我假设 Baudcode 想知道它是多少,因为没有来自非自制编码器的已知数量的开销。
  • 顺便说一句,在测试我的代码时,我浏览了数百张图片,虽然我没有检查每一张图片,但似乎每一张都存储了一些 APP1 或 COM 数据,在至少。在某些情况下几十千字节(我怀疑这是“预览”图像,但我根本没有尝试分析内容)。
猜你喜欢
  • 2014-09-11
  • 2016-05-30
  • 1970-01-01
  • 1970-01-01
  • 2021-08-17
  • 1970-01-01
  • 1970-01-01
  • 2012-10-15
  • 2017-12-02
相关资源
最近更新 更多