Извлечение коэффициентов 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 как ну?

2 ответов


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

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

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

EDIT: исправлено масштабирование в source

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

jpeg image

DC изображения (64x64): luma Cr Cb RGB

DC lumaDC CbDC CrDC RGB

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

#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;
}

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

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

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


#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;
}