【问题标题】:How to specify bitrate for JPEG compression?如何为 JPEG 压缩指定比特率?
【发布时间】:2011-06-08 07:58:43
【问题描述】:

有没有办法以特定比特率对 JPEG 进行编码?

目前,我正在使用 imagemagick 的convert

convert Lenna-gray-100.jpeg -quality 1.1111 test.jpeg

比特率随着质量的增加而增加,但它是非线性的。我想明确控制比特率。它不必很精确,但我希望它相当接近(在指定设置的 0.1 bpp 以内)。

那里是否有任何编码器允许以特定比特率对图像进行编码?它不一定是 imagemagick,我会采用任何可行的方法(最好在 Linux 上)。

一种愚蠢的方法是使用-quality 参数的小数值,直到出现接近目标比特率的东西,但我希望有一个更优雅的解决方案。

编辑:

所以我觉得无聊,决定以快速(但愚蠢)的方式做事。

首先,这是 imagemagick 的 -quality 与比特率的关系图:

顺便说一句,这是我使用的图像:

因此,对于较低质量的值,比特率的变化非常好,但在大约 80 之后变得粗糙。

以下是一些示例代码,用于以某个目标比特率对图像进行编码。我使用了 OpenCV,因为它允许内存中的 JPEG 编码(不需要 I/O)。虽然我最初打算用 Python 来模拟它,但不幸的是 Python OpenCV 包装器没有公开内存中编码功能。所以我用 C++ 写的。

最后,我正在考虑对质量使用线性插值以更接近目标比特率,但由于cv::imencode 只接受整数参数,因此无法设置非整数 JPEG 质量。 OpenCV 和 imagemagick 之间的质量尺度似乎也有所不同,因此从 OpenCV 中获取插值质量参数并在 imagemagick 的 convert 中使用效果不佳。

这意味着输出比特率不等于目标比特率,尤其是在较高比特率 (> 1) 时。但已经很接近了。

谁能提出更好的建议?

代码:

#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#include <assert.h>
#include <vector>

using cv::Mat;
using std::vector;

#define IMENCODE_FMT   ".jpeg"
#define QUALITY_UBOUND 101
#define BITS_PER_BYTE  8

int
main(int argc, char **argv)
{
    if (argc != 4)
    {
        fprintf(stderr, "usage: %s in.png out.jpeg bpp\n", argv[0]);
        return 1;
    }

    char *fname_in = argv[1];
    char *fname_out = argv[2];
    float target;
    sscanf(argv[3], "%f", &target);

    Mat orig = cv::imread(fname_in);
    int pixels = orig.size().width * orig.size().height * orig.channels();

    vector<unsigned char> buf;
    vector<int> params = vector<int>(2);
    params[0] = CV_IMWRITE_JPEG_QUALITY;
    int q;
    double bpp = 0.0;

    for (q = 1; q < QUALITY_UBOUND; ++q)
    {
        params[1] = q;
        cv::imencode(IMENCODE_FMT, orig, buf, params);
        bpp = (double)buf.size() * BITS_PER_BYTE / pixels;
        if (bpp > target)
            break;
    }

    cv::imwrite(fname_out, orig, params);
    printf("wrote %s at %d%% quality, %.2fbpp\n", fname_out, q, bpp);

    return 0;
}

编译并运行使用:

g++ -c -Wall -ggdb -I../c -I../blur `pkg-config --cflags opencv` -Wno-write-strings jpeg-bitrate.cpp -o jpeg-bitrate.o
g++ -I../c `pkg-config --cflags opencv` `pkg-config --libs opencv` -lboost_filesystem jpeg-bitrate.o -o jpeg-bitrate.out
rm jpeg-bitrate.o
misha@misha-desktop:~/co/cpp$ ./jpeg-bitrate.out Lenna-gray.png test.jpeg 0.53
wrote test.jpeg at 88% quality, 0.55bpp

【问题讨论】:

  • 建议:去掉for循环,换成search。我不敢相信有人会想要一个质量因子在 1->~30 或~99->100 范围内编码的 JPEG。您还可以为各种不同的图像类型创建图表,并为搜索制定一个更好的初始起点。这一切都非常幼稚,因为您甚至不考虑质量(例如 PSNR);选择不同的量化表可能会获得您想要的比特率,但质量会更高。
  • 感谢您的建议。搜索会提高代码的效率,但目前这并不是一个真正的问题,因为它已经足够快了。您对质量的看法是正确的——大多数普通人不需要质量那么低的 JPEG,因为它们看起来像垃圾。然而,我自己对这样的图像感兴趣,因为我正在研究图像退化。关于量化表的观点很有趣——我想我会研究一下。它可能需要远离 OpenCV 并使用 ijg 之类的东西,因为 OpenCV 似乎没有公开量化表。

标签: image-processing compression opencv imagemagick jpeg


【解决方案1】:

我知道在控制 JPEG 编码器(例如 1st paper ; 2nd paper)的输出比特率方面存在大量工作,并且此类控制存在于 JPEG2000 中。不幸的是,我不确定任何类型的比特率控制是针对 JPEG 进行标准化的,还是在公共库中实现的。您可能必须编写自己的方法,例如使用某种二进制搜索...

但同样,我可能弄错了 - 如果是这样,我很想听听这样的图书馆。

出于好奇,您使用的是什么语言?

【讨论】:

  • 感谢您的回答和链接。不幸的是,我的大学没有订阅 SpringerLink,但我阅读了第二篇论文。回答您的问题:对于严肃的图像工作,我使用 C/C++,但如果我最终为此编写自己的方法,我可能只会使用 Python/Bash,因为我很懒。
  • 这样也会更简单。无论如何 - 如果你偶然发现了解决这个特定问题的好方法,请告诉我们:)
  • 我用示例代码更新了问题。我不会称其为好的解决方案,但它是我目前唯一拥有的解决方案。
  • +1。我正在考虑发布相同的答案,但我不确定自己。这是有道理的,因为 JPEG 比特流不是像 EZW、SPIHT 或 JPEG2000 那样的嵌入式比特流。换句话说,您不能简单地截断 JPEG 比特流来接收质量较低的图像版本,但是使用前面提到的其他编解码器,您(有点)可以。
【解决方案2】:

JPG 中的比特率质量比很大程度上取决于内容。如果你想以特定的比特率编码,我建议你做两遍: 1. 以固定质量因子编码(越接近目标比特率越好,可能基于您的图表) 2. 根据其大小,以更高或更低的质量重新编码原件。同样,这可以基于您的图表或类似的东西。

您也可以无限期地重复最后一步以获得所需的准确比特率。

我会在各种极端情况下对此进行测试,例如非常嘈杂/繁忙的图像、黑色矩形或平滑渐变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-26
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    相关资源
    最近更新 更多