2010-12-17 23 views
16

Gibt es eine Möglichkeit, die DCT-Koeffizienten (und Quantisierungsparameter) aus codierten Bildern und Videos einfach zu extrahieren? Jede Decoder-Software muss diese verwenden, um block-DCT-codierte Bilder und Videos zu dekodieren. Ich bin mir ziemlich sicher, dass der Decoder weiß, was sie sind. Gibt es eine Möglichkeit, sie denen zu zeigen, die den Decoder benutzen?DCT-Koeffizienten aus codierten Bildern und Videos extrahieren

Ich implementiere einige Algorithmen zur Bewertung der Videoqualität, die direkt in der DCT-Domäne funktionieren. Derzeit verwendet der Großteil meines Codes OpenCV, daher wäre es großartig, wenn jemand eine Lösung mit diesem Framework kennt. Es macht mir nichts aus, andere Bibliotheken zu benutzen (vielleicht Libjpeg, aber das scheint nur für Standbilder zu sein), aber mein Hauptanliegen ist es, so wenig formatspezifische Arbeit wie möglich zu machen (ich will das Rad nicht neu erfinden und schreiben meine eigenen Decoder). Ich möchte in der Lage sein, jedes Video/Bild (H.264, MPEG, JPEG usw.) zu öffnen, das OpenCV öffnen kann, und wenn es DCT-codiert ist, um die DCT-Koeffizienten zu erhalten.

Im schlimmsten Fall weiß ich, dass ich meinen eigenen DCT-Blockcode schreiben kann, die dekomprimierten Frames/Images durchführe und dann wieder in der DCT-Domäne bin. Das ist kaum eine elegante Lösung, und ich hoffe, ich kann es besser machen.

Derzeit verwende ich die ziemlich häufig OpenCV vorformulierten Bilder zu öffnen:

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

Der Code, den ich für Videos bin ist ebenso trivial:

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

In beiden Fällen bekomme ich ein 3-Kanal IplImage im BGR-Format. Gibt es eine Möglichkeit, die DCT-Koeffizienten auch zu bekommen?

Antwort

18

Nun, ich habe ein wenig gelesen und meine ursprüngliche Frage scheint ein Wunschdenken zu sein.

Grundsätzlich ist es nicht möglich, die DCT-Koeffizienten von H.264-Video-Frames aus dem einfachen Grund zu erhalten, dass H.264 doesn't use DCT. Es verwendet eine andere Transformation (ganzzahlige Transformation). Als nächstes ändern sich die Koeffizienten für diese Transformation nicht notwendigerweise auf einer Frame-für-Frame-Basis - H.264 ist intelligenter, da es Frames in Slices aufteilt. Es sollte möglich sein, diese Koeffizienten durch einen speziellen Decoder zu bekommen, aber ich bezweifle, dass OpenCV es für den Benutzer verfügbar macht.

Für JPEG sind die Dinge ein bisschen positiver. Wie ich vermutet habe, stellt libjpeg die DCT-Koeffizienten für Sie zur Verfügung. Ich habe eine kleine App geschrieben, um zu zeigen, dass sie funktioniert (Quelle am Ende). Es erstellt ein neues Bild unter Verwendung des DC-Terms von jedem Block. Da der DC-Term gleich dem Blockmittelwert ist (nach richtiger Skalierung), sind die DC-Bilder abwärts abgetastete Versionen des eingegebenen JPEG-Bildes.

EDIT: feste Skalierung in Quelle

Originalbild (512 x 512):

jpeg image

DC Bilder (64x64): Luma Cr Cb RGB

DC luma DC Cb DC Cr DC RGB

Quelle (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

Was ist das DC_SIZE und wo es herkommt. Als ich Ihre Quelle kompilierte, bekam ich einen Fehler main_read.c: 85: 48: Fehler: 'DC_SIZE' wurde in diesem Bereich nicht erklärt –

+1

Ich denke, es ist ein Tippfehler. Wenn Sie sich den Bearbeitungsverlauf ansehen, werden Sie feststellen, dass es sich bei der vorherigen Bearbeitung um DCTSIZE handelte. Ich kann das jetzt nicht bestätigen, aber wenn ich das tue, werde ich meine Antwort aktualisieren. Danke, dass Sie auf dieses Problem hingewiesen haben. – misha

+2

DCTSIZE ist eigentlich die richtige. Ich kann es nach einigen Versuchen bestätigen. –

0

Sie verwenden können, libjpeg dct Daten Ihrer JPEG-Datei zu extrahieren, aber für h.264 Video-Datei, kann ich nicht offen Quellcode, der Ihnen dct-Daten liefert (Actually Integer dct data). Aber Sie können h.264 Open-Source-Software wie JM, JSVM oder x264 verwenden. In diesen beiden Quelldateien müssen Sie ihre spezifische Funktion finden, die die dct-Funktion nutzt, und sie in Ihr Wunschformular ändern, um Ihre ausgegebenen Daten zu erhalten.

Für Image: den folgenden Code, und nach read_jpeg_file(infilename, v, quant_tbl), v und quant_tbl wird jeweils dct data und quantization table Ihrer JPEG-Bild haben.

I verwendet QVector meine Ausgangsdaten zu speichern, ändern Sie auf Ihre bevorzugte C++ Array-Liste.


#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 dies funktioniert nicht für alle JPEG-Bilder Dies ist die einfache Version der Funktion, Sie müssen manchmal mehr Zeilen als eine basierend auf Sample-Faktoren abrufen – AngryDuck