2010-12-17 12 views
16

Czy istnieje sposób na łatwe wyodrębnienie współczynników DCT (i parametrów kwantyzacji) z zakodowanych obrazów i wideo? Każde oprogramowanie dekodera musi je wykorzystywać do dekodowania obrazów kodowanych blokowo-DCT i wideo. Więc jestem prawie pewien, że dekoder wie, co to jest. Czy istnieje sposób na ich ujawnienie temu, kto używa dekodera?Wyodrębnianie współczynników DCT z zakodowanych obrazów i wideo

Implementuję niektóre algorytmy oceny jakości wideo, które działają bezpośrednio w domenie DCT. Obecnie większość mojego kodu używa OpenCV, więc byłoby świetnie, gdyby ktokolwiek znał rozwiązanie korzystające z tego środowiska. Nie mam nic przeciwko używaniu innych bibliotek (być może libjpeg, ale wydaje się, że jest to tylko w przypadku obrazów nieruchomych), ale moim głównym celem jest wykonanie jak najmniejszej pracy związanej z formatem (nie chcę wymyślać koła i pisać moje własne dekodery). Chcę móc otworzyć dowolny wideo/obraz (H.264, MPEG, JPEG, itp.), Który OpenCV może otworzyć, a jeśli jest blokowany kodowaniem DCT, aby uzyskać współczynniki DCT.

W najgorszym przypadku, wiem, że mogę napisać własny kod DCT bloku, uruchomić zdekompresowane ramki/obrazy za jego pośrednictwem, a następnie będę z powrotem w domenie DCT. To nie jest eleganckie rozwiązanie i mam nadzieję, że mogę zrobić to lepiej.

Obecnie używam dość często OpenCV Gotowa do otwierania obrazów:

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

Kod używam wideo jest równie banalna:

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

W obu przypadkach, otrzymuję 3-kanałowy IplImage w formacie BGR. Czy istnieje sposób, w jaki mogę uzyskać współczynniki DCT?

Odpowiedz

18

Cóż, trochę czytałem, a moje oryginalne pytanie wydaje się być przykładem myślenia życzeniowego.

Zasadniczo nie jest możliwe uzyskanie współczynników DCT z klatek wideo H.264 z tego prostego powodu, że H.264 doesn't use DCT. Używa innej transformacji (transformacja całkowita). Następnie współczynniki dla tej transformacji niekoniecznie zmieniają się na podstawie klatka po klatce - H.264 jest mądrzejszy, ponieważ dzieli ramki na plasterki. Powinno być możliwe uzyskanie tych współczynników przez specjalny dekoder, ale wątpię, czy OpenCV udostępnia go dla użytkownika.

Dla plików JPEG rzeczy są nieco bardziej pozytywne. Jak podejrzewałem, libjpeg odsłania dla Ciebie współczynniki DCT. Napisałem małą aplikację, aby pokazać, że działa (źródło na końcu). Tworzy nowy obraz za pomocą kodu DC z każdego bloku. Ponieważ termin DC jest równy średniej bloku (po prawidłowym skalowaniu), obrazy DC są próbkowane wersjami wejściowego obrazu JPEG.

EDIT: stałe skalowanie w źródle

oryginalny obraz (512 x 512):

jpeg image

zdjęć DC (64x64): Luma Kr Cb RGB

DC luma DC Cb DC Cr DC RGB

źródłowy (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; 
} 
+0

Co to DC_SIZE i skąd pochodzi. Kiedy skompilowałem twoje źródło, dostałem błąd main_read.c: 85: 48: error: 'DC_SIZE' nie został zadeklarowany w tym zakresie –

+1

Myślę, że to literówka. Jeśli spojrzysz na historię edycji, zobaczysz, że była to DCTSIZE w poprzedniej edycji. Nie mam teraz możliwości potwierdzenia tego, ale kiedy to zrobię, zaktualizuję odpowiedź. Dzięki za wskazanie tego problemu. – misha

+2

DCTSIZE jest właściwie właściwy. Potwierdzam to po kilku próbach. –

0

Można użyć, libjpeg wyodrębnić DCT dane z pliku JPEG, ale dla h.264 pliku wideo, nie mogę znaleźć żadnego otwarte kod źródłowy, który daje dane dct (actully Integer dct data). Możesz jednak użyć oprogramowania open source h.264, takiego jak JM, JSVM lub x264. W tych dwóch plikach źródłowych musisz znaleźć ich specyficzną funkcję, która korzysta z funkcji dct i zmienić ją na twoją formę pożądania, aby uzyskać dane wyjściowe dct.

Dla Image: użyć następującego kodu, a po read_jpeg_file(infilename, v, quant_tbl), v i quant_tbl będzie miał dct data i quantization table swojego JPEG odpowiednio.

użyłem Qvector przechowywanie moich danych wyjściowych, zmień go do preferowanej listy tablicy 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; 
} 
+0

FYI to nie działa dla wszystkich obrazów JPEG jest to prosta wersja funkcji, trzeba pobrać więcej wierszy niż jeden na podstawie czynników próbki czasami – AngryDuck

Powiązane problemy