2010-12-17 10 views
16

エンコードされた画像とビデオからDCT係数(および量子化パラメータ)を簡単に抽出する方法はありますか?どのデコーダソフトウェアでもブロック-DCT符号化された画像およびビデオを復号するためにそれらを使用しなければならない。だから私はデコーダが彼らが何であるかを知っていると確信しています。デコーダを使用している誰にでもそれらを公開する方法はありますか?エンコードされた画像とビデオからDCT係数を抽出する

私は、DCTドメインで直接動作するビデオ品質評価アルゴリズムを実装しています。現在、私のコードの大部分はOpenCVを使用しています。だから誰でもそのフレームワークを使っている解決策が分かっていれば素晴らしいだろう。私は他のライブラリ(おそらくlibjpegですが、それはまだ静止画のみのようです)を気にしませんが、私の主な関心事はフォーマット固有の作業をできるだけ少なくすることです(私は、私自身のデコーダ)。私はOpenCVが開くことができるビデオ/画像(H.264、MPEG、JPEGなど)を開くことができるようにしたい、そして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); 

、私が手BGR形式の3チャンネルIplImage。 DCT係数も同様に得られますか?

答えて

18

私は少し読んでいました。私の元々の質問は、希望的な考え方の一例です。

基本的に、H.264ビデオフレームからDCT係数を得ることは、H.264 doesn't use DCTという単純な理由からできません。これは、異なる変換(整数変換)を使用します。次に、その変換の係数は必ずしもフレームごとに変化するわけではありません.H.264はよりスマートなので、フレームをスライスに分割します。これらの係数を特殊なデコーダで得ることは可能ですが、OpenCVがユーザのためにそれを公開するかどうかは疑問です。

JPEGの場合は、もう少しポジティブです。私が疑っているように、libjpegはあなたのためにDCT係数を公開します。私はそれが動作することを示すために小さなアプリを書いた(最後にソース)。各ブロックのDC項を使用して新しい画像を作成します。 DC項は(適切なスケーリングの後の)ブロック平均に等しいので、DC画像は入力JPEG画像のダウンサンプリングされたバージョンである。

EDIT:固定ソース

オリジナル画像(512×512)のスケーリング:

jpeg image

DC画像(64×64):輝度のCr CbのRGB

DC luma DC Cb DC Cr DC 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; 
} 
+0

。あなたのソースをコンパイルするとエラーが発生しましたmain_read.c:85:48:error: 'DC_SIZE'がこのスコープで宣言されていませんでした –

+1

私はそれが誤字だと思います。編集履歴を見ると、直前の編集でDCTSIZEだったことがわかります。私は今すぐ確認する機会がありませんが、私が答えると答えが更新されます。この問題を指摘してくれてありがとう。 – misha

+2

DCTSIZEは実際には適切なものです。私はいくつかの試行の後にそれを確認することができます。 –

0

あなたはあなたのJPEGファイルのDCTデータを抽出するためにのlibjpeg 、使用することができますが、ビデオファイルのために、私は開いているを見つけることができませんdctデータ(actully整数dctデータ)を与えるソースコードです。しかし、JM,JSVMまたはx264のようなh.264オープンソースソフトウェアを使用することができます。これらの2つのソースファイルでは、dct関数を使用する特定の関数を見つけ、それをあなたの出力形式に変更して出力dctデータを取得する必要があります。画像用の

は、次のコードを使用し、read_jpeg_file(infilename, v, quant_tbl)vquant_tbl後、それぞれ自分のJPEG画像のdct dataquantization tableを持つことになります。

私は、私の出力データを格納し、ご希望のC++配列リストにそれを変更するQvector
を使用しました。このDC_SIZEとどこから来ている何


#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これはすべてのJPEG画像では機能しません。これは関数の単純なバージョンです。時にはサンプル因子に基づいてより多くの行をフェッチする必要があります – AngryDuck

関連する問題