顯示具有 C 標籤的文章。 顯示所有文章
顯示具有 C 標籤的文章。 顯示所有文章

2011年9月11日 星期日

[C&C++ NOTE] [轉] 函數指標

本文 http://pydoing.blogspot.com/2010/06/c-funptr.html

函數雖然並非資料,然而編譯器實際會替函數的程式碼配置記憶體位址,當呼叫函數時,也就是控制權移轉給被呼叫函數的時候,程式執行便會轉移到該記憶體位址的地方。因此,呼叫函數可以直接用指標,這是 C 語言提供的動態機制,有許多不可取代的地方。


如下例為選單驅動程式的簡化版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
 
void fun1(void);
void fun2(void);
void fun3(void);
 
int main(void)
{
    void (*f[3])(void) = {fun1, fun2, fun3};
    int i;
     
    for (i = 0; i < 3; i++) {
        (*f[i])();
    }
     
    printf("\n上面以函數指標各自呼叫fun1、fun2、fun3...\n");
     
    return 0;
}
 
void fun1(void)
{
    printf("選項1...\n");
}
 
void fun2(void)
{
    printf("選項2...\n");
}
 
void fun3(void)
{
    printf("選項3...\n");
}
 
/* 《程式語言:教學誌》的範例程式
    檔名:funptr.c
    功能:示範函數指標的使用
    作者:張凱慶
    時間:西元2010年4月 */


編譯後執行,如下



第 3 到第 5 行
3
4
5
void fun1(void);
void fun2(void);
void fun3(void);


一共宣告了三個函數,然後第 9 行
9
void (*f[3])(void) = {fun1, fun2, fun3};


這是宣告一個名為 f 存放函數指標的陣列,由於指標宣告的星號 * ,運算優先次序比用為函數參數列的小括弧為低,所以這裡指標陣列 *f[3] 要用小括弧圍起來。


因為是存放函數指標的陣列,所以連同函數的回傳值型態及參數列都要加進宣告之中。


其後的大括弧裡面放的即為函數名稱,呼叫函數以陣列的使用方式即可。


這個程式的目的主要為示範函數指標的使用,因此第 12 到第 15 行
12
13
14
for (i = 0; i < 3; i++) {
        (*f[i])();
    }


用迴圈依序呼叫三個函數。

[C&C++ NOTE][轉] Structure 結構

本文 http://pydoing.blogspot.com/2010/06/c-structure.html



C 語言中的結構為異質的資料結構,這意思是說結構中可以存放不同資料型態的資料體,每個資料體被稱為結構的成員,定義格式如下




宣告定義關鍵字 struct ,接著是結構名稱,然後用大括弧圍起來的成員宣告,須留意結構定義完的右大括弧其後要接分號 ; 。


結構有關的運算子如下表

結構成員運算子.
結構指標運算子->


宣告為某結構的變數就可以用結構成員運算子存取該成員,而結構指標運算子可以讓指向某結構的指標變數存取其成員,如下例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
 
struct point {
    int x;
    int y;
};
 
int main(void)
{
    struct point a;
    struct point *aPtr = &a;
     
    a.x = 3;
    a.y = 4;
     
    printf("a = (%d, %d)\n", a.x, a.y); 
    printf("*aPtr = (%d, %d)\n", aPtr->x, aPtr->y);
 
    return 0;
}
 
/* 《程式語言:教學誌》的範例程式
    檔名:structopt.<b style="color:black;background-color:#ffff66">c</b>
    功 能:示範<b style="color:black;background-color:#a0ffff">結構</b>定義、宣告及<b style="color:black;background-color:#a0ffff">結構</b>運算子的使用
    作者:張凱慶
    時間:西元2010年4月 */


編譯後執行,如下



第 10 行
10
struct point a;


宣告變數 a 為 point 結構的變數,第 11 行
11
struct point *aPtr = &a;


宣告並設定 aPtr 為指向變數 a 的指標,接著第 13 及 14 行
13
14
a.x = 3;
a.y = 4;


便是利用結構成員運算子指派初值給變數 a ,最後第 16 及 17 行
16
17
printf("a = (%d, %d)\n", a.x, a.y); 
printf("*aPtr = (%d, %d)\n", aPtr->x, aPtr->y);


分別用結構成員運算子及結構指標運算子取出 a 的值並印在螢幕上。


第 13 及 14 行替結構設值可以用以下
13
struct point a = {3, 4};


來替代,利用大括弧指派初值,大括弧內依序是各個結構成員。


利用關鍵字 typedef 可以替結構建立新型態名稱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
struct point {
    int x;
    int y;
};
 
typedef struct point Point;
 
int main(void)
{
    Point a = {3, 4};
     
    printf("a = (%d, %d)\n", a.x, a.y); 
 
    return 0;
}
 
/* 《程式語言:教學誌》的範例程式
    檔名:structtypedef.<b style="color:black;background-color:#ffff66">c</b>
    功能:示範利用 typedef 建立新型態名稱
    作者:張凱慶
    時間:西元2010年4月 */


編譯後執行,如下



第 8 行
8
typedef struct point Point;


結構 point 建立 Point 型態名稱,因此往後可以直接使用 Point ,無須 struct point 兩字連用。


typedef 也可以直接寫進結構定義中,第 3 到 8 行可用以下寫法代替
3
4
5
6
typedef struct point {
    int x;
    int y;
} Point;


結構中的成員也可以是結構,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
 
typedef struct point {
    int x;
    int y;
} Point;
 
typedef struct line {
    Point start;
    Point end;
} Line;
 
int main(void)
{
    Line a = {3, 4, 5, 6};
     
    printf("start = (%d, %d)\n", a.start.x, a.start.y); 
    printf("end = (%d, %d)\n", a.end.x, a.end.y);
 
    return 0;
}
 
/* 《程式語言:教學誌》的範例程式
    檔名:structstruct.<b style="color:black;background-color:#ffff66">c</b>
    功 能:示範在<b style="color:black;background-color:#a0ffff">結構</b>中使用其他<b style="color:black;background-color:#a0ffff">結構</b>當作成員
    作者:張凱慶
    時間:西元2010年4月 */


編譯後執行,如下








自我參考的結構

結構中不能有與自己相同識別字名稱的結構,但可以有指向相同識別字名稱結構的指標,這是說 C 語言可以簡單的利用結構與指標把資料連接起來,形成複雜的資料結構。



如下例定義了一個結構 list ,其中有一個成員宣告為指向 list 的指標
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
 
struct list {
    char *name;
    struct list *nextPtr;
};
 
typedef struct list List;
 
int main(void)
{
    List a, b, c, *startPtr;
     
    a.name = "John";
    b.name = "Mary";
    c.name = "Tony";
     
    startPtr = &a;
    a.nextPtr = &b;
    b.nextPtr = &c;
    c.nextPtr = NULL;
     
    while (startPtr != NULL) {
        printf("%s\n", startPtr->name);
        startPtr = startPtr->nextPtr;
    }
 
    return 0;
}
 
/* 《程式語言:教學誌》的範例程式
    檔名:structlist.c
    功能:示範自我參考的結構
    作者:張凱慶
    時間:西元2010年4月 */


編譯後執行,結果如下



第 12 行
12
List a, b, c, *startPtr;


我們分別宣告了三個 List 型態的結構,另外一個指向 List 型態的指標。


接下來從第 14 行到第 16 行
14
15
16
a.name = "John";
b.name = "Mary";
c.name = "Tony";


各自替三個 List 型態結構的成員 name 給值,然後第 18 行
18
startPtr = &a;


將 a 的位址指派給額外宣告的指標 startPtr ,這是說我們將 a 作為此資料結構的起始點,同時利用額外的指標進行操作。因為從指標可以輕易的取所指向結構的成員,指標也可以當成控制變數,因而使用指標得以容易遊走在資料結構中。


List 型態結構的另一個成員 nextPtr ,便是指向下一筆資料項目的位址。第 19 到第 21 行
19
20
21
a.nextPtr = &b;
b.nextPtr = &c;
c.nextPtr = NULL;


此例中,我們把 a 當成第一筆資料,也就是資料結構的起始項目, b 是第二筆資料, c 為第三筆資料,因此 a 的 nextPtr 指向 b 的位址,而 b 的 nextPtr 指向 c 。由於 c 為最後一筆資料,我們將 c 的 nextPtr 設為 NULL , NULL 是 C 語言標準函數庫所定義的一個特別的值,代表無效的指標。


第 23 到第 26 行
23
24
25
26
while (startPtr != NULL) {
    printf("%s\n", startPtr->name);
    startPtr = startPtr->nextPtr;
}


我們使用了一個迴圈,這個迴圈的結束條件為 startPtr 等於 NULL 。此迴圈的任務為印出指標所指向結構的 name 成員,然後把 startPtr 重新指派給所指向結構的 nextPtr 成員。


由於 startPtr 最初的值為 a 的位址,所以第一次進入迴圈會印出 a 的 name 成員,同時 a 的 nextPtr 成員的值會重新指派給 startPtr 。我們之前是將 a 的 nextPtr 設定成 b 的位址,所以第二次進入迴圈時, startPtr 指向的是 b ,之後的動作皆可類推。


這樣的資料結構稱之為鍵結串列 (linked list) ,所有的資料可以分開儲存在不同非連續的記憶體位址。需留意一點,我們用一個簡單的方式說明 C 語言的結構與指標的一般應用,而非建立實際可有效運用的資料結構。





本文 http://programming.im.ncnu.edu.tw/Chapter13.htm

說明:
指能夠結合多個彼此相關的變數在一個名稱之下,且可以包含數個不同資料型態的變數。換句話說,結構是一種使用者自定的型態,它可將不同的資料型態串在一起。舉例而言:「學生個人資料表」,裡頭有姓名(字串型態)、年齡(整數型態)、生日(日期型態)…等等。
格式:
struct 結構型態
{
    欄項資料型態 欄項變數名稱;
    欄項資料型態 欄項變數名稱;
    欄項資料型態 欄項變數名稱;
       :     :
} 變數Ⅰ,變數Ⅱ……;
示意圖:
範例:
struct Student_PersonalData {
    char name[4];
    int age;
    char address[30];
} SP_Data;
應用範例:
#include <stdio.h>
#include <string.h>
void main() {
    struct Student_Perosnal_Data {
        char name[10];
        int age;
        char address[50];
        char interest[11];
    } stu;
    strcpy(stu.name,"張三");
    stu.age = 25;
    strcpy(stu.address, "南投縣埔里鎮大學路一號");
    strcpy(stu.interest, "basketball");
    printf("The student's name is: %s\n", stu.name);
    printf("The student's age is: %d\n", stu.age);
    printf("The student's address is: %s\n", stu.address);
    printf("The student's interest is: %s\n", stu.interest);
}
上述的struct Student_PersonalData一經定義以後,就可以比照C的內建資料型別來宣告和處理。
struct內也可以其他的struct
struct Student_Detail {
    int age;
    char *name;
    char *address;
};
struct Student_Data {
    int stuid;
    struct Student_Detail detail;
};
void main() {
    struct Student_Data x;
    x.stuid = 100;
    x.detail.age = 20;
    x.detail.name = "Johnson Lee";
    x.detail.address = "Nation Chi Nan University";
}

用於struct的運算符號

在如下的結構定義裡,next前面的*不可省略,否則就遞迴定義了,Compiler將無法決定struct list的大小。
struct list {
    int data;
    struct list *next; // a pointer to struct list
};

struct list listOne, listTwo, listThree;

listOne.next = &listTwo;
listTwo.next = &listThree;
// 以下想要由listOne設定到listThree的data
listOne.next.next.data = 0; // 這不合法, 因為.的左邊必須是struct,不可以是pointer
(*(*listOne.next).next).data = 0; // 這樣寫才對
你會發現上面的例子中, 如果struct裡面有pointer to struct, 而我們想要用該pointer來存取結構成員時, 就必須很小心的用*和()來表達。由於結構成員包括指向結構的指標(define a pointer to struct in a struct), 是很常見的事情, 這樣的(*(*listOne.next).next).data語法既難寫又難懂, 因此C語言定義了->運算符號。此符號的左邊是一個pointer to struct, 右邊則是該pointer指到的結構成員。->為第一優先權左結合, 因此
(*(*listOne.next).next).data = 0; //這樣寫才對
listOne.next->next->data = 0; // 這樣寫更漂亮

動態空間分配

所謂動態空間分配指的是,在執行期間由程式向作業系統或程式庫要求後才分配的空間,這塊記憶體區域稱為Heap(堆積)。C語言的動態空間分配主要透過malloc和free兩函數來處理。這兩個函數的宣告如下:
void *malloc(size_t size);
void free(void *ptr);
透過malloc()所分配出來的空間必須由使用者呼叫free()才能歸還給系統。初學者常犯的錯誤之一,就是忘了用free()歸還空間,這會造成程 式佔用太多記憶體,此現象稱為memory leakage。相反的,如果空間已用free()歸還了,卻還試著去使用那塊記憶體,則會發生Segmentation Fault (core dumped)的錯誤。 Linked Stack
typedef struct items {
    int data;
    struct items *link;
} ITEM;

typedef struct stack {
    ITEM *top;
} STACK;

void initStack(STACK *s) {
    s->top = NULL;
}

void pushStack(STACK *s, int y) {
    ITEM *x; // x will point to the new ITEM
    x = (ITEM *) malloc(sizeof(ITEM)); // allocate memory for the new ITEM
    x->data = y; // store data
    x->link = s->top; // x->link points to where s->top points
    s->top = x; // stack's top points to x
}

int popStack(STACK *s) {
    ITEM * x = s->top;
 int d = x->data;
    s->top = s->top->link;
    free(x);
    return d;
}
int stackIsEmpty(STACK *s) {
    return s->top == NULL;
}
void main() {
    STACK s;
    int i;
    initStack(&s);
    for (i = 1; i < 10; i++) {
        pushStack(&s, i);
    }
    while (!stackIsEmpty(&s)) {
        printf("%d\n", popStack(&s));
    }
}
Linked Queue
typedef struct items {
    int data;
    struct items *link; // points to next element
} ITEM;

typedef struct queue {
    int size;
    ITEM *front, *rear;
} QUEUE;

void initQueue(QUEUE *q) {
    q->size = 0;
    q->front = q->rear = NULL;
}
int queueIsEmpty(QUEUE *q) {
    return q->front == NULL;
}
int queueLength(QUEUE *q) {
    return q->size;
}
void addQueue(QUEUE *q, int y) {
    ITEM * x = (ITEM *) malloc(sizeof(ITEM));
    x->data = y;
 x->link = NULL;
    if (q->front == NULL)
        q->front = x;
    else
        q->rear->link = x;
    q->rear = x;
    q->size++;
}
int deleteQueue(QUEUE *q) {
    ITEM * x = q->front;
    int rel = x->data;
    q->front = x->link;
    if (q->front == NULL)
        q->rear = NULL;
    q->size--;
    free(x);
    return rel;
}
void main() {
    QUEUE q;
    int i;
    initQueue(&q);
    for (i = 1; i < 10; i++) {
        addQueue(&q, i);
    }
    while (!queueIsEmpty(&q)) {
        printf("%d\n", deleteQueue(&q));
    }
}
以下範例定義了矩陣結構,並透過動態空間分配的方式來做矩陣的加法和乘法
/**
 * Author: Shiuh-Sheng Yu
 * Department of Information  Management
 * National Chi Nan University
 * Subject: 矩陣相加與相乘
 * Toolkit: gcc
 * Modified Date:2002/08/20
 */
#include <stdio.h>
// 以巨集(macro)定義矩陣元素和動態分配空間的對應關係
// 所謂巨集指的是經由preprocessor(前置處理器)取代原始檔內的字串
#define M(x,i,j) *(x->data + i*x->col + j)

// 定義MATRIX為 struct matrix *
// 也就是說MATRIX之型態為 a pointer to struct matrix
// 至於struct則是C語言讓使用者 "自訂型態" 的關鍵字
typedef struct matrix {
    int row, col;
    double* data;
} *MATRIX;

/**
 * 由檔案讀入一個矩陣
 */
MATRIX readMatrix(FILE* f) {
    int x, y, i, j;
    char keyword[256];
    MATRIX m;
    /* read in keyword "matrix" */
    fscanf(f, "%255s", keyword);
    if (strcmp(keyword,"matrix")!=0) {
        printf("keyword error: %s",keyword);
        return NULL;
    }
    // 動態分配一塊struct matrix大小的空間
    m =  (MATRIX) malloc(sizeof(struct matrix));
    /* read in matrix dimension to x y */
    fscanf(f,"%d", &x);
    fscanf(f,"%d", &y);
    m->row = x;
    m->col = y;
    m->data = (double*)malloc(x * y * sizeof(double));
    /* read in x*y double and store them to m->data */
    for (i = 0; i < x; i++) {
        for (j = 0; j < y; j++) {
            fscanf(f,"%lf",m->data + i*y + j);
        }
    }
    return m;
}

/**
 * 印出矩陣的內容 
 */
void printMatrix(MATRIX x) {
    int i, j;
    for (i = 0; i < x->row; i++) {
        for ( j= 0; j < x->col; j++) {
            printf("%lf", M(x,i,j));
        }
        printf("\n");
    }
}
/**
 * 矩陣相加
 * 傳回一新矩陣為x,y之和
 */
MATRIX addMatrix(MATRIX x, MATRIX y) {
    int i, j;
    MATRIX m;
    // 檢查兩矩陣的大小是否能相加
    if ((x->row != y->row) || (x->col != y->col)) {
        printf("Matrix dimension mismatch.\n");
        return NULL;
    }
    // 產生新矩陣所需的記憶體空間
    m = (MATRIX) malloc(sizeof(struct matrix));
    m->row = x->row;
    m->col = x->col;
    //產生存放資料所需的空間
    m->data = (double*)malloc(m->row * m->col * sizeof(double));
    // 進行矩陣的加法運算
    for (i =  0; i < m->row; i++) {
        for (j =  0; j < m->col; j++) {
            M(m,i,j) =  M(x,i,j) + M(y,i,j); // 使用macro
        }
    }
    return m;
}
MATRIX multiplyMatrix(MATRIX x, MATRIX y) {
    /* 自己練習看看吧 */

}
/**
 * 將動態分配矩陣的空間還給系統
 */
void freeMatrix(MATRIX x) {
    free(x->data);
    free(x);
}

int main() {
    char buf[100];
    MATRIX a, b, c;
    // 持續讀入運算符號
    // stdin定義於stdio.h, 代表standard input. 在沒有透過作業系統重新指定
    // 的情形下, 一般為鍵盤
    for (; fscanf(stdin,"%99s",buf) != EOF;) {
        if (buf[0] == '+') {
            if ((a = readMatrix(stdin)) == NULL) {
                break; // 有錯誤則跳離開最接近的迴圈或switch敘述(此處為for迴圈)
            }
            printMatrix(a);
            if ((b = readMatrix(stdin)) == NULL) {
                break;
            }
            printf("+\n");
            printMatrix(b);
            printf("=\n");
            if ((c = addMatrix(a, b)) == NULL) {
                break;
            }
            printMatrix(c);
            printf("\n");
            freeMatrix(a); // 釋放動態分配的矩陣空間
            freeMatrix(b);
            freeMatrix(c);
        } else if (buf[0]=='*') {
            /* 練習看看吧 */
        } else {
            printf("Operator error\n");
            break;
        }
    }
}

[C&C++ NOTE][轉]& 的使用


在網路上看到的

======================================================
用來表示同一份 memory address 但卻不同 name

ex: int i=10;

       int & refertoi=i;
       改變refertoi就等於改變i, 在參數傳遞上就等於傳遞指標的效果. 若把address印出來
       會發現兩個的位置是一樣的(&i == &refertoi)
       也可用來access private class member:
        class A
        {
          .........
               private:
                   int y;
               public:
                   int& fun(){return y;};
        };
        int main()
        {
              A a;
              a.fun()=10;      <=就等於a.y=10
              .........
        }

2011年9月10日 星期六

[轉] 談談RGB、YUY2、YUYV、YVYU、UYVY、AYUV

DirectShow中常見的RGB/YUV格式

文章來源: http://hqtech.nease.net
原文作者: 陸其明

小知識:RGB與YUV----摘自《DirectShow實務精選》作者:陸其明

計 算機彩色顯示器顯示色彩的原理與彩色電視機一樣,都是採用R(Red)、G(Green)、B(Blue)相加混色的原理:通過發射出三種不同強度的電子 束,使屏幕內側覆蓋的紅、綠、藍磷光材料發光而產生色彩。這種色彩的表示方法稱為RGB色彩空間表示(它也是多媒體計算機技術中用得最多的一種色彩空間表 示方法)。
根據三基色原理,任意一種色光F都可以用不同份量的R、G、B三色相加混合而成。

F = r [ R ] + g [ G ] + b [ B ]

其中,r、g、b分別為三基色參與混合的係數。當三基色份量都為0(最弱)時混合為黑色光;而當三基色份量都為k(最強)時混合為白色光。調整r、g、b三個係數的值,可以混合出介於黑色光和白色光之間的各種各樣的色光。
那 麼YUV又從何而來呢?在現代彩色電視系統中,通常採用三管彩色攝像機或彩色CCD攝像機進行攝像,然後把攝得的彩色圖像信號經分色、分別放大校正後得到 RGB,再經過矩陣變換電路得到亮度信號Y和兩個色差信號R-Y(即U)、B-Y(即V),最後發送端將亮度和色差三個信號分別進行編碼,用同一信道發送 出去。這種色彩的表示方法就是所謂的YUV色彩空間表示。
採用YUV色彩空間的重要性是它的亮度信號Y和色度信號U、V是分離的。如果只有Y信號份量而沒有U、V份量,那麼這樣表示的圖像就是黑白灰度圖像。彩色電視採用YUV空間正是為了用亮度信號Y解決彩色電視機與黑白電視機的兼容問題,使黑白電視機也能接收彩色電視信號。
YUV與RGB相互轉換的公式如下(RGB取值範圍均為0-255):

Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B

R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U

在DirectShow中,常見的RGB格式有RGB1、RGB4、RGB8、RGB565、RGB555、RGB24、RGB32、ARGB32等;常見的YUV格式有YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411、YUV420等。作為視頻媒體類型的輔助說明類型(Subtype),它們對應的GUID見表2.3。

表2.3 常見的RGB和YUV格式

GUID 格式描述
MEDIASUBTYPE_RGB1 2色,每個像素用1位表示,需要調色板
MEDIASUBTYPE_RGB4 16色,每個像素用4位表示,需要調色板
MEDIASUBTYPE_RGB8 256色,每個像素用8位表示,需要調色板
MEDIASUBTYPE_RGB565 每個像素用16位表示,RGB份量分別使用5位、6位、5位
MEDIASUBTYPE_RGB555 每個像素用16位表示,RGB份量都使用5位(剩下的1位不用)
MEDIASUBTYPE_RGB24 每個像素用24位表示,RGB份量各使用8位
MEDIASUBTYPE_RGB32 每個像素用32位表示,RGB份量各使用8位(剩下的8位不用)
MEDIASUBTYPE_ARGB32 每個像素用32位表示,RGB份量各使用8位(剩下的8位用於表示Alpha通道值)
MEDIASUBTYPE_YUY2 YUY2格式,以4:2:2方式打包
MEDIASUBTYPE_YUYV YUYV格式(實際格式與YUY2相同)
MEDIASUBTYPE_YVYU YVYU格式,以4:2:2方式打包
MEDIASUBTYPE_UYVY UYVY格式,以4:2:2方式打包
MEDIASUBTYPE_AYUV 帶Alpha通道的4:4:4 YUV格式
MEDIASUBTYPE_Y41P Y41P格式,以4:1:1方式打包
MEDIASUBTYPE_Y411 Y411格式(實際格式與Y41P相同)
MEDIASUBTYPE_Y211 Y211格式
MEDIASUBTYPE_IF09 IF09格式
MEDIASUBTYPE_IYUV IYUV格式
MEDIASUBTYPE_YV12 YV12格式
MEDIASUBTYPE_YVU9 YVU9格式

下面分別介紹各種RGB格式。

¨ RGB1、RGB4、RGB8都是調色板類型的RGB格式,在描述這些媒體類型的格式細節時,通常會在BITMAPINFOHEADER數據結構後面跟著 一個調色板(定義一系列顏色)。它們的圖像數據並不是真正的顏色值,而是當前像素顏色值在調色板中的索引。以RGB1(2色位圖)為例,比如它的調色板中 定義的兩種顏色值依次為0x000000(黑色)和0xFFFFFF(白色),那麼圖像數據001101010111…(每個像素用1位表示)表示對應各 像素的顏色為:黑黑白白黑白黑白黑白白白…。

¨ RGB565使用16位表示一個像素,這16位中的5位用於R,6位用於G,5位用於B。程序中通常使用一個字(WORD,一個字等於兩個字節)來操作一個像素。當讀出一個像素後,這個字的各個位意義如下:
高字節 低字節
R R R R R G G G G G G B B B B B
可以組合使用屏蔽字和移位操作來得到RGB各份量的值:

#define RGB565_MASK_RED 0xF800
#define RGB565_MASK_GREEN 0x07E0
#define RGB565_MASK_BLUE 0x001F
R = (wPixel & RGB565_MASK_RED) >> 11; // 取值範圍0-31
G = (wPixel & RGB565_MASK_GREEN) >> 5; // 取值範圍0-63
B = wPixel & RGB565_MASK_BLUE; // 取值範圍0-31

¨ RGB555是另一種16位的RGB格式,RGB份量都用5位表示(剩下的1位不用)。使用一個字讀出一個像素後,這個字的各個位意義如下:
高字節 低字節
X R R R R G G G G G B B B B B (X表示不用,可以忽略)
可以組合使用屏蔽字和移位操作來得到RGB各份量的值:

#define RGB555_MASK_RED 0x7C00
#define RGB555_MASK_GREEN 0x03E0
#define RGB555_MASK_BLUE 0x001F
R = (wPixel & RGB555_MASK_RED) >> 10; // 取值範圍0-31
G = (wPixel & RGB555_MASK_GREEN) >> 5; // 取值範圍0-31
B = wPixel & RGB555_MASK_BLUE; // 取值範圍0-31

¨ RGB24使用24位來表示一個像素,RGB份量都用8位表示,取值範圍為0-255。注意在內存中RGB各份量的排列順序為:BGR BGR BGR…。通常可以使用RGBTRIPLE數據結構來操作一個像素,它的定義為:

typedef struct tagRGBTRIPLE {
BYTE rgbtBlue; // 藍色份量
BYTE rgbtGreen; // 綠色份量
BYTE rgbtRed; // 紅色份量
} RGBTRIPLE;

¨ RGB32使用32位來表示一個像素,RGB份量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是帶Alpha通道的 RGB32。)注意在內存中RGB各份量的排列順序為:BGRA BGRA BGRA…。通常可以使用RGBQUAD數據結構來操作一個像素,它的定義為:

typedef struct tagRGBQUAD {
BYTE rgbBlue; // 藍色份量
BYTE rgbGreen; // 綠色份量
BYTE rgbRed; // 紅色份量
BYTE rgbReserved; // 保留字節(用作Alpha通道或忽略)
} RGBQUAD;

下 面介紹各種YUV格式。YUV格式通常有兩大類:打包(packed)格式和平面(planar)格式。前者將YUV份量存放在同一個數組中,通常是幾個 相鄰的像素組成一個宏像素(macro-pixel);而後者使用三個數組分開存放YUV三個份量,就像是一個三維平面一樣。表2.3中的YUY2到Y211都是打包格式,而IF09到YVU9都是平面格式。(注意:在介紹各種具體格式時,YUV各份量都會帶有下標,如Y0、U0、V0表示第一個像素的YUV份量,Y1、U1、V1表示第二個像素的YUV份量,以此類推。)

¨ YUY2(和YUYV)格式為每個像素保留Y份量,而UV份量在水平方向上每兩個像素採樣一次。一個宏像素為4個字節,實際表示2個像素。(4:2:2的意思為一個宏像素中有4個Y份量、2個U份量和2個V份量。)圖像數據中YUV份量排列順序如下:
Y0 U0 Y1 V0 Y2 U2 Y3 V2 …

¨ YVYU格式跟YUY2類似,只是圖像數據中YUV份量的排列順序有所不同:
Y0 V0 Y1 U0 Y2 V2 Y3 U2 …

¨ UYVY格式跟YUY2類似,只是圖像數據中YUV份量的排列順序有所不同:
U0 Y0 V0 Y1 U2 Y2 V2 Y3 …

¨ AYUV格式帶有一個Alpha通道,並且為每個像素都提取YUV份量,圖像數據格式如下:
A0 Y0 U0 V0 A1 Y1 U1 V1 …

¨ Y41P(和Y411)格式為每個像素保留Y份量,而UV份量在水平方向上每4個像素採樣一次。一個宏像素為12個字節,實際表示8個像素。圖像數據中YUV份量排列順序如下:
U0 Y0 V0 Y1 U4 Y2 V4 Y3 Y4 Y5 Y6 Y8 …

¨ Y211格式在水平方向上Y份量每2個像素採樣一次,而UV份量每4個像素採樣一次。一個宏像素為4個字節,實際表示4個像素。圖像數據中YUV份量排列順序如下:
Y0 U0 Y2 V0 Y4 U4 Y6 V4 …

¨ YVU9格式為每個像素都提取Y份量,而在UV份量的提取時,首先將圖像分成若干個4 x 4的宏塊,然後每個宏塊提取一個U份量和一個V份量。圖像數據存儲時,首先是整幅圖像的Y份量數組,然後就跟著U份量數組,以及V份量數組。IF09格式與YVU9類似。

¨ IYUV格式為每個像素都提取Y份量,而在UV份量的提取時,首先將圖像分成若干個2 x 2的宏塊,然後每個宏塊提取一個U份量和一個V份量。YV12格式與IYUV類似。

¨ YUV411、YUV420格式多見於DV數據中,前者用於NTSC制,後者用於PAL制。YUV411為每個像素都提取Y份量,而UV份量在水平方向上 每4個像素採樣一次。YUV420並非V份量採樣為0,而是跟YUV411相比,在水平方向上提高一倍色差採樣頻率,在垂直方向上以U/V間隔的方式減小 一半色差採樣,如圖2.12所示。






顏色問題:
我們在DVDRIP或內嵌的時候,通常會遇到一些關於顏色方面的術語,比如YUV、RGB、YV12、4:2:2、4:2:0等等。不少人剛接觸到這些東西的時候,會覺得暈頭轉向,不知所云。
再如,不少文章中強調影片在VDM處理的過程中要選Fast recompress,但是Fast recompress、Normal recompress、Full processing mode之間又有什麼區別呢?
本文來一一為您解答這些問題。
本 文是一篇總結性的文章,所以不少段落都是直接摘自其他的文章的。在這裡向原作者表示謝意。本文參考了原載於DVD Benchmark由Don Munsil & Stacey Spears原作的《The Chroma Upsampling Error(顏色Upsampling錯誤)》和Silky的文章。

1.什麼是RGB?
RGB是紅綠藍三原色的意思,R=Red、G=Green、B=Blue。

2.什麼是YUV/YCbCr/YPbPr?
亮 度信號經常被稱作Y,色度信號是由兩個互相獨立的信號組成。視顏色系統和格式不同,兩種色度信號經常被稱作U和V或Pb和Pr或Cb和Cr。這些都是由不 同的編碼格式所產生的,但是實際上,他們的概念基本相同。在DVD中,色度信號被存儲成Cb和Cr(C代表顏色,b代表藍色,r代表紅色)。

3.什麼是4:4:4、4:2:2、4:2:0?
在 最近十年中,視頻工程師發現人眼對色度的敏感程度要低於對亮度的敏感程度。在生理學中,有一條規律,那就是人類視網膜上的視網膜桿細胞要多於視網膜錐細 胞,說得通俗一些,視網膜桿細胞的作用就是識別亮度,而視網膜錐細胞的作用就是識別色度。所以,你的眼睛對於亮和暗的分辨要比對顏色的分辨精細一些。正是 因為這個,在我們的視頻存儲中,沒有必要存儲全部顏色信號。既然眼睛看不見,那為什麼要浪費存儲空間(或者說是金錢)來存儲它們呢?
像Beta或VHS之類的消費用錄像帶就得益於將錄像帶上的更多帶寬留給黑—白信號(被稱作「亮度」),將稍少的帶寬留給彩色信號(被稱作「色度」)。
在MPEG2(也就是DVD使用的壓縮格式)當中,Y、Cb、Cr信號是分開儲存的(這就是為什麼份量視頻傳輸需要三條電纜)。其中Y信號是黑白信號,是以全分辨率存儲的。但是,由於人眼對於彩色信息的敏感度較低,色度信號並不是用全分辨率存儲的。
色 度信號分辨率最高的格式是4:4:4,也就是說,每4點Y採樣,就有相對應的4點Cb和4點Cr。換句話說,在這種格式中,色度信號的分辨率和亮度信號的 分辨率是相同的。這種格式主要應用在視頻處理設備內部,避免畫面質量在處理過程中降低。當圖像被存儲到Master Tape,比如D1或者D5,的時候,顏色信號通常被削減為4:2:2。
[center]
在圖一中,你可以看到4:4:4格式的亮度、色度採樣分佈。就像圖中所表示的,畫面中每個像素都有與之對應的色度和亮度採樣信息。[/center]
其 次就是4:2:2,就是說,每4點Y採樣,就有2點Cb和2點Cr。在這種格式中,色度信號的掃瞄線數量和亮度信號一樣多,但是每條掃瞄線上的色度採樣點 數卻只有亮度信號的一半。當4:2:2信號被解碼的時候,「缺失」的色度採樣,通常由一定的內插補點算法通過它兩側的色度信息運算補充。
[center]
圖 二表示了4:2:2格式亮度、色度採樣的分佈情況。在這裡,每個像素都有與之對應的亮度採樣,同時一半的色度採樣被丟棄,所以我們看到,色度採樣信號每隔 一個採樣點才有一個。當著張畫面顯示的時候,缺少的色度信息會由兩側的顏色通過內插補點的方式運算得到。就像上面提到的那樣,人眼對色度的敏感程度不如亮 度,大多數人並不能分辨出4:2:2和4:4:4顏色構成的畫面之間的不同。[/center]
色度信號分辨率最低的格式,也就是DVD所使用的 格式,就是4:2:0了。事實上4:2:0是一個混亂的稱呼,按照字面上理解,4:2:0應該是每4點Y採樣,就有2點Cb和0點Cr,但事實上完全不是 這樣。事實上,4:2:0的意思是,色度採樣在每條橫向掃瞄線上只有亮度採樣的一半,掃瞄線的條數上,也只有亮度的一半!換句話說,無論是橫向還是縱向, 色度信號的分辨率都只有亮度信號的一半。舉個例子,如果整張畫面的尺寸是720*480,那麼亮度信號是720*480,色度信號只有360*240。在 4:2:0中,「缺失」的色度採樣不單單要由左右相鄰的採樣通過內插補點計算補充,整行的色度採樣也要通過它上下兩行的色度採樣通過內插補點運算獲得。這 樣做的原因是為了最經濟有效地利用DVD的存儲空間。誠然,4:4:4的效果很棒,但是如果要用4:4:4存儲一部電影,我們的DVD盤的直徑至少要有兩 英呎(六十多釐米)!
[center]
圖三表示了概念上4:2:0顏色格式非交錯畫面中亮度、色度採樣信號的排列情況。同4:2:2格式 一樣,每條掃瞄線中,只有一半的色度採樣信息。與4:2:2不同的是,不光是橫向的色度信息被「扔掉」了一半,縱向的色度信息也被「扔掉」了一半,整個屏 幕中色度採樣只有亮度採樣的四分之一。請注意,在4:2:0顏色格式中,色度採樣被放在了兩條掃瞄線中間。為什麼會這樣呢?很簡單:DVD盤上的顏色採樣 是由其上下兩條掃瞄線的顏色信息「平均」而來的。比如,圖三中,第一行顏色採樣(Line 1和Line 2中間夾著的那行)是由Line 1和Line 2「平均」得到的,第二行顏色採樣(Line 3和Line 4中間夾著的那行)也是同樣的道理,是由Line 3和Line 4得到的。
雖然文章中多次提到「平均」這個概念,但是這個「平均」可不是我們通常意義上的(a+B)/2的平均。顏色的處理有極其複雜的算法保證其最大限度地減少失真,接近原始質量。[/center]

4.什麼是YV12,什麼是YUY2
在個人計算機上,這些YUV讀出來以後會以一些格式包裝起來,送給軟件或硬件處理。包裝的方式分成兩種,一種是Packed format,把Y和相對應的UV包在一起。另一種是Planar format,把Y和U和V三種分別包裝,拆成三個plane(平面)。
其中YV12和YUY2都是一種YUV的包裝格式,而且兩種都是Packed format(實際上,只有YUY2才是Packed format,而YV12則是屬於Planar format。)
YV12和YUY2的不同,在於YV12是YUV4:2:0格式,也就是DVD/VCD上原本儲存的格式。YUY2則是YUV4:2:2格式。

5.為什麼影片在VDM處理的過程中要選Fast recompress?
選擇Fast recompress的原因,現得從Avisynth 2.5講起。
Avisynth 2.5最大的特色,就是支持YV12直接處理。我們知道原始MPEG數據是YUV4:2:0,也就是YV12的格式,以前我們在做DivX/XviD壓縮的時候,處理流程是:
DVD/VCD(YUV 4:2:0) -> DVD2AVI(YUV 4:2:0 ->YUV4:2:2 ->YUV4:4:4 -> RGB24) -> VFAPI(RGB24) -> TMPGEnc/AviUtl/VirtualDub(RGB24) -> DivX/XviD Codec(RGB24 ->YUV4:2:0) -> MPEG-4(YUV 4:2:0)
ps. VFAPI 內部只能以 RGB24 傳遞數據,所以會轉成 RGB24 輸出
或是
DVD/VCD(YUV 4:2:0) -> MPG2DEC.DLL(YUV 4:2:0 ->YUV4:2:2) -> Avisynth 2.0.x(只能用支援YUV4:2:2 的濾鏡,不能用 RGB24/32 的 filter) -> VirtualDub(YUV 4:2:2,不能使用 VD 的 filter,因為 VD 的 filetr 都是在 RGB32 上處理,壓縮時要選 Fast recompress,才會直接原封不動的送YUV4:2:2,也就是 YUY2 的數據給 Codec 壓縮) -> DivX/XviD Codec(YUV 4:2:2 ->YUV4:2:0) -> MPEG-4(YUV 4:2:0)
所以以前的處理流程中間要經過好幾次YUV<-> RGB 的轉換。這個轉換是有損的,做得越多次,原始的色彩信息就損失的越嚴重。而且這個轉換的計算又耗時(這就可以解釋為什麼我們將YV12轉為RGB輸出時會卡的多,不過,RGB的品質真的更高的多:))。那麼有人(Marc FD)就想到,反正最後轉成 MPEG 都要存成YUV4:2:0 的格式,那麼為什麼不乾脆一路到底,全程都以YV12處理,也就是所有的 filter 都改寫成YV12的版本,直接在YV12上做調整色彩、濾噪訊、IVTC 等工作,這樣:
1. 處理的數據量少。(YV12的資料,UV 比YUY2少一半,比RGB 24/32少更多)
2. 不用轉換計算
所以速度快。再加上又可以避免YUV<-> RGB 轉換的損失,豈不是一舉兩得?
所以支持YV12的 Avisynth 2.5 就誕生了。
但是目前VirtualDub還是不支持 YV12,即使選 Fast recompress,VD還是會將YV12的輸入轉為 YUY2。所以要得到全程YV12處理的好處,必須使用VirtualDubMod才行,這個改版才有支持YV12。只有在選擇Fast recompress的時候,VDM才不會進行任何處理,直接將數據丟給編碼器壓縮,這樣就能保留YV12,實現了全程YV12。

YUV

YUV是編譯true-color顏色空間(color space)的種類,Y'UV, YUV, YCbCrYPbPr等專有名詞都可以稱為YUV,彼此有重疊。「Y」表示明亮度(Luminance、Luma),「U」和「V」則是色度濃度(Chrominance、 Chroma),Y'UV, YUV, YCbCr, YPbPr 常常有些混用的情況,其中 YUV 和 Y'UV 通常用來描述類比訊號,而相反的 YCbCr 與 YPbPr 則是用來描述數位的影像訊號,例如在一些壓縮格式內 MPEG、JPEG 中,但在現今,YUV 通常已經在電腦系統上廣泛使用。YUV Formats分成兩個格式:
  • 緊縮格式(packed formats):將Y、U、V值儲存成Macro Pixels陣列,和RGB的存放方式類似。
  • 平面格式(planar formats):將Y、U、V的三個份量分別存放在不同的矩陣中。
緊縮格式(packed format)中的YUV是混合在一起的,對於YUV4:4:4格式而言,用緊縮格式很合適的,因此就有了UYVY、YUYV等。平面格式(planar formats)是指每Y份量,U份量和V份量都是以獨立的平面組織的,也就是說所有的U份量必須在Y份量後面,而V份量在所有的U份量後面,此一格式適 用於採樣(subsample)。平面格式(planar format)有I420(4:2:0)、YV12、IYUV等。




Ref
維基百科 - 
http://webcache.googleusercontent.com/search?q=cache:HZAhPc2OK0MJ:zh.wikipedia.org/zh-hant/YUV+yuy2&cd=1&hl=zh-TW&ct=clnk&client=firefox-a