Извлечение коэффициентов DCT из закодированных изображений и видео

Есть ли способ легко извлечь коэффициенты DCT (и параметры квантования) из закодированных изображений и видео? Любое программное обеспечение декодера должно использовать их для декодирования изображений и видео, закодированных блочным DCT. Так что я почти уверен, что декодер знает, что это такое. Есть ли способ показать их тем, кто использует декодер?

Я реализую некоторые алгоритмы оценки качества видео, которые работают непосредственно в домене DCT. В настоящее время большая часть моего кода использует OpenCV, поэтому было бы здорово, если бы кто-нибудь знал решение, использующее эту структуру. Я не возражаю против использования других библиотек (возможно, libjpeg, но это, похоже, только для неподвижных изображений), но моя главная задача — сделать как можно меньше работы, зависящей от формата (я не хочу изобретать велосипед и писать мои собственные декодеры). Я хочу иметь возможность открывать любое видео/изображение (H.264, MPEG, JPEG и т. д.), которое может открыть OpenCV, и если оно кодируется блоком DCT, чтобы получить коэффициенты DCT.

В худшем случае я знаю, что могу написать свой собственный блочный код DCT, запустить через него распакованные кадры/изображения, и тогда я вернусь в домен DCT. Это вряд ли элегантное решение, и я надеюсь, что смогу добиться большего.

В настоящее время я использую довольно распространенный шаблон OpenCV для открытия изображений:

IplImage *image = cvLoadImage(filename);
// Run quality assessment metric

Код, который я использую для видео, столь же тривиален:

CvCapture *capture = cvCaptureFromAVI(filename);    
while (cvGrabFrame(capture))
{
    IplImage *frame = cvRetrieveFrame(capture);
    // Run quality assessment metric on frame
}
cvReleaseCapture(&capture);

В обоих случаях я получаю 3-х канальный IplImage в формате BGR. Можно ли как-то получить коэффициенты DCT?


person mpenkov    schedule 17.12.2010    source источник


Ответы (2)


Что ж, я немного почитал, и мой первоначальный вопрос кажется примером принятия желаемого за действительное.

По сути, невозможно получить коэффициенты DCT из видеокадров H.264 по той простой причине, что H.264 не использует DCT. Он использует другое преобразование (целочисленное преобразование). Далее, коэффициенты для этого преобразования не обязательно меняются от кадра к кадру — H.264 умнее, потому что он разбивает кадры на фрагменты. Должна быть возможность получить эти коэффициенты через специальный декодер, но я сомневаюсь, что OpenCV предоставляет его пользователю.

Для JPEG дела обстоят чуть более позитивно. Как я и подозревал, libjpeg предоставляет вам коэффициенты DCT. Я написал небольшое приложение, чтобы показать, что оно работает (источник в конце). Он создает новое изображение, используя термин DC из каждого блока. Поскольку член DC равен среднему значению блока (после надлежащего масштабирования), изображения DC являются версиями входного изображения JPEG с пониженной частотой дискретизации.

EDIT: исправлено масштабирование в исходном коде.

Исходное изображение (512 x 512):

jpeg image

Изображения DC (64x64): яркость Cr Cb RGB

DC lumaDC Cb DC CrDC RGB

Источник (С++):

#include <stdio.h>
#include <assert.h>

#include <cv.h>    
#include <highgui.h>

extern "C"
{
#include "jpeglib.h"
#include <setjmp.h>
}

#define DEBUG 0
#define OUTPUT_IMAGES 1

/*
 * Extract the DC terms from the specified component.
 */
IplImage *
extract_dc(j_decompress_ptr cinfo, jvirt_barray_ptr *coeffs, int ci)
{
    jpeg_component_info *ci_ptr = &cinfo->comp_info[ci];
    CvSize size = cvSize(ci_ptr->width_in_blocks, ci_ptr->height_in_blocks);
    IplImage *dc = cvCreateImage(size, IPL_DEPTH_8U, 1);
    assert(dc != NULL);

    JQUANT_TBL *tbl = ci_ptr->quant_table;
    UINT16 dc_quant = tbl->quantval[0];

#if DEBUG
    printf("DCT method: %x\n", cinfo->dct_method);
    printf
    (
        "component: %d (%d x %d blocks) sampling: (%d x %d)\n", 
        ci, 
        ci_ptr->width_in_blocks, 
        ci_ptr->height_in_blocks,
        ci_ptr->h_samp_factor, 
        ci_ptr->v_samp_factor
    );

    printf("quantization table: %d\n", ci);
    for (int i = 0; i < DCTSIZE2; ++i)
    {
        printf("% 4d ", (int)(tbl->quantval[i]));
        if ((i + 1) % 8 == 0)
            printf("\n");
    }

    printf("raw DC coefficients:\n");
#endif

    JBLOCKARRAY buf =
    (cinfo->mem->access_virt_barray)
    (
        (j_common_ptr)cinfo,
        coeffs[ci],
        0,
        ci_ptr->v_samp_factor,
        FALSE
    );
    for (int sf = 0; (JDIMENSION)sf < ci_ptr->height_in_blocks; ++sf)
    {
        for (JDIMENSION b = 0; b < ci_ptr->width_in_blocks; ++b)
        {
            int intensity = 0;

            intensity = buf[sf][b][0]*dc_quant/DCTSIZE + 128;
            intensity = MAX(0,   intensity);
            intensity = MIN(255, intensity);

            cvSet2D(dc, sf, (int)b, cvScalar(intensity));

#if DEBUG
            printf("% 2d ", buf[sf][b][0]);                        
#endif
        }
#if DEBUG
        printf("\n");
#endif
    }

    return dc;

}

IplImage *upscale_chroma(IplImage *quarter, CvSize full_size)
{
    IplImage *full = cvCreateImage(full_size, IPL_DEPTH_8U, 1);
    cvResize(quarter, full, CV_INTER_NN);
    return full;
}

GLOBAL(int)
read_JPEG_file (char * filename, IplImage **dc)
{
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;

  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * infile;        /* source file */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  cinfo.err = jpeg_std_error(&jerr);

  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  jvirt_barray_ptr *coeffs = jpeg_read_coefficients(&cinfo);

  IplImage *y    = extract_dc(&cinfo, coeffs, 0);
  IplImage *cb_q = extract_dc(&cinfo, coeffs, 1);
  IplImage *cr_q = extract_dc(&cinfo, coeffs, 2);

  IplImage *cb = upscale_chroma(cb_q, cvGetSize(y));
  IplImage *cr = upscale_chroma(cr_q, cvGetSize(y));

  cvReleaseImage(&cb_q);
  cvReleaseImage(&cr_q);

#if OUTPUT_IMAGES
  cvSaveImage("y.png",   y);
  cvSaveImage("cb.png", cb);
  cvSaveImage("cr.png", cr);
#endif

  *dc = cvCreateImage(cvGetSize(y), IPL_DEPTH_8U, 3);
  assert(dc != NULL);

  cvMerge(y, cr, cb, NULL, *dc);

  cvReleaseImage(&y);
  cvReleaseImage(&cb);
  cvReleaseImage(&cr);

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  fclose(infile);

  return 1;
}

int 
main(int argc, char **argv)
{
    int ret = 0;
    if (argc != 2)
    {
        fprintf(stderr, "usage: %s filename.jpg\n", argv[0]);
        return 1;
    }
    IplImage *dc = NULL;
    ret = read_JPEG_file(argv[1], &dc);
    assert(dc != NULL);

    IplImage *rgb = cvCreateImage(cvGetSize(dc), IPL_DEPTH_8U, 3);
    cvCvtColor(dc, rgb, CV_YCrCb2RGB);

#if OUTPUT_IMAGES
    cvSaveImage("rgb.png", rgb);
#else
    cvNamedWindow("DC", CV_WINDOW_AUTOSIZE); 
    cvShowImage("DC", rgb);
    cvWaitKey(0);
#endif

    cvReleaseImage(&dc);
    cvReleaseImage(&rgb);

    return 0;
}
person mpenkov    schedule 21.12.2010
comment
Что это за DC_SIZE и откуда он взялся. Когда я скомпилировал ваш исходный код, я получил ошибку main_read.c:85:48: ошибка: «DC_SIZE» не был объявлен в этой области - person Dimitar Slavchev; 19.03.2012
comment
Я думаю, это опечатка. Если вы посмотрите на историю редактирования, вы обнаружите, что в предыдущем редактировании это было DCTSIZE. У меня нет возможности подтвердить это прямо сейчас, но когда я это сделаю, я обновлю свой ответ. Спасибо, что указали на эту проблему. - person mpenkov; 21.03.2012
comment
На самом деле DCTSIZE является правильным. Я могу подтвердить это после некоторых испытаний. - person Dimitar Slavchev; 22.03.2012

Вы можете использовать libjpeg для извлечения данных dct из вашего файла jpeg, но для видеофайла h.264 я не могу найти открытый исходный код, который дает вам данные dct. (фактически целочисленные данные DCT). Но вы можете использовать программное обеспечение с открытым исходным кодом h.264, такое как JM, JSVM или x264. В этих двух исходных файлах вы должны найти их конкретную функцию, которая использует функцию dct, и изменить ее на желаемую форму, чтобы получить выходные данные dct.

Для изображения: используйте следующий код, и после read_jpeg_file( infilename, v, quant_tbl ), v и quant_tbl будет dct data и quantization table вашего изображения в формате jpeg соответственно.

Я использовал Qvector для хранения своих выходных данных, изменил их на предпочтительный список массивов С++.


#include <iostream>
#include <stdio.h>
#include <jpeglib.h>
#include <stdlib.h>
#include <setjmp.h>
#include <fstream>

#include <QVector>

int read_jpeg_file( char *filename, QVector<QVector<int> > &dct_coeff, QVector<unsigned short> &quant_tbl)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE * infile;

    if ((infile = fopen(filename, "rb")) == NULL) {
      fprintf(stderr, "can't open %s\n", filename);
      return 0;
    }

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, infile);
    (void) jpeg_read_header(&cinfo, TRUE);

    jvirt_barray_ptr *coeffs_array = jpeg_read_coefficients(&cinfo);
    for (int ci = 0; ci < 1; ci++)
    {
        JBLOCKARRAY buffer_one;
        JCOEFPTR blockptr_one;
        jpeg_component_info* compptr_one;
        compptr_one = cinfo.comp_info + ci;

        for (int by = 0; by < compptr_one->height_in_blocks; by++)
        {
            buffer_one = (cinfo.mem->access_virt_barray)((j_common_ptr)&cinfo, coeffs_array[ci], by, (JDIMENSION)1, FALSE);
            for (int bx = 0; bx < compptr_one->width_in_blocks; bx++)
            {
                blockptr_one = buffer_one[0][bx];
                QVector<int> tmp;
                for (int bi = 0; bi < 64; bi++)
                {
                    tmp.append(blockptr_one[bi]);
                }
                dct_coeff.push_back(tmp);
            }
        }
    }


    // coantization table
    j_decompress_ptr dec_cinfo  = (j_decompress_ptr) &cinfo;
    jpeg_component_info *ci_ptr = &dec_cinfo->comp_info[0];
    JQUANT_TBL *tbl = ci_ptr->quant_table;

    for(int ci =0 ; ci < 64; ci++){
        quant_tbl.append(tbl->quantval[ci]);
    }

    return 1;
}

int main()
{
    QVector<QVector<int> > v;
    QVector<unsigned short> quant_tbl;
    char *infilename = "your_image.jpg";

    std::ofstream out;
    out.open("out_dct.txt");


    if( read_jpeg_file( infilename, v, quant_tbl ) > 0 ){

        for(int j = 0; j < v.size(); j++ ){
                for (int i = 0; i < v[0].size(); ++i){
                    out << v[j][i] << "\t";
            }
            out << "---------------" << std::endl;
        }

        out << "\n\n\n" << std::string(10,'-') << std::endl;
        out << "\nQauntization Table:" << std::endl;
        for(int i = 0; i < quant_tbl.size(); i++ ){
            out << quant_tbl[i] << "\t";
        }
    }
    else{
        std::cout << "Can not read, Returned With Error";
        return -1;
    }

    out.close();

return 0;
}
person Hadi Rasekh    schedule 10.01.2015
comment
К вашему сведению, это не работает для всех изображений JPEG, это простая версия функции, иногда вам нужно получить больше строк, чем одна, на основе факторов выборки. - person AngryDuck; 04.05.2017