量子化テーブル、グローバル パラメーター、ハフマン テーブル、および回復テーブルのエンコーディングの読み取りは、現時点では実装のアイデアにすぎません。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <linux/fb.h>
#include <stdlib.h>
static unsigned char h0[100];
int main(void) {
FILE *f = fopen("/home/wjs/Pictures/1.jpg", "rb");
if (f == NULL) {
puts("file_in error");
exit(-1);
}
fseek(f, 0, SEEK_END);
int len = ftell(f);
fseek(f, 0, SEEK_SET);
int fd = fileno(f);
unsigned char *p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
puts("-----------量化表---------------------");
for (int t = 0; t < len; t++) {
if ((*(p + t) == 0xff) && (*(p + t + 1) == 0xdb)) {
// printf("ffdb:%d\n", t);
}
}
puts("-----------帧全局---------------------");
for (int t = 0; t < len; t++) {
if ((*(p + t) == 0xff) && (*(p + t + 1) == 0xc0)) {
// printf("ffc0:%d\n", t);
}
}
puts("------------霍夫曼表--------------------");
for (int t = 0; t < len; t++) {
if ((*(p + t) == 0xff) && (*(p + t + 1) == 0xc4)) {
// printf("ffc4:%d\n",t); //ff c4 (固定)0 1f(长度-2)0(表id)
int cd = (*(p + t + 2)) * 256 + *(p + t + 3) - 3;
unsigned char *hp = malloc(cd * (sizeof(char)));
for (int n = 0; n < cd; n++) {
*(hp + n) = *(p + t + 5 + n);
// printf("%d ",*(hp+n));
}
if (*(p + t + 4) == 0) { //表1
unsigned char hfm0[cd];
// memcpy(&h0, hp, cd);
}
if (*(p + t + 4) == 16) { //2
unsigned char hfm1[cd];
// memcpy(&h0,hp,cd);
}
if (*(p + t + 4) == 1) { //3
unsigned char hfm2[cd];
// memcpy(&h0,hp,cd);
}
if (*(p + t + 4) == 17) { //4
unsigned char hfm3[cd];
memcpy(&h0,hp,cd);
}
printf("\n");
free(hp);
}
}
puts("------------差分数据--------------------");
for (int t = 0; t < len; t++) {
if ((*(p + t) == 0xff) && (*(p + t + 1) == 0xdd)) {
// printf("ffdd:%d\n", t);
}
}
puts("------------扫描数据--------------------");
for (int t = 0; t < len; t++) {
if ((*(p + t) == 0xff) && (*(p + t + 1) == 0xda)) {
// printf("ffda:%d\n", t);
}
}
//----------------------------------------------------------------------
//h0[26]
// 0 1 2 3 4 5 6 7 8 9 10 15
//0 1 5 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 4 5 6 7 3 2 8 9
int bm[100];
int t = 0; //输出数组递增数
int n = 1; //内存递增数
if (h0[n] == 1) {
bm[t] = 0;
} else if (h0[n] == 2) {
bm[t] = 0; //生成2 位 0,1
t = t + 1;
bm[t] = 1;
}
for(int q=0;q<15;q++){
n = n + 1;
if (h0[n] != 0) { //3 2,3,4,5,6
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
}
/* n = n + 1;
if (h0[n] != 0) { //4 (6+1)*2=14
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //5 (14+1)*2=30
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //6 (30+1)*2=62
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //7位 (62+1)*2=126
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //8
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //9
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //10
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //11
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //12
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //13
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //14
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
n = n + 1;
if (h0[n] != 0) { //15
t = t + 1;
bm[t] = 2 * (bm[t - 1] + 1);
for (int z = 0; z < (h0[n] - 1); z++) {
t = t + 1;
bm[t] = bm[t - 1] + 1;
}
}
*/
for (int t = 0; t < 100; t++) {
printf("%d ", bm[t]);
}
return 0;
}
Jpeg は正規形ハフマンを使用しており、コード表は 00 から計算できます。
7 ここで別の質問がありますが、fda のスキャン ストリームには AC と DC、Y と UV の 4 つの部分が含まれていますが、これら 4 つの部分をどのように区別すればよいでしょうか。
コード ストリームの構造を理解した後でのみ、上記のコード テーブルを使用してデコードを試みることができます。
通常のJPEGコードストリームはSOI(FFD8)マークで始まりEOI(FFD9)マークで終わり、その真ん中に各種データ(ハフマンテーブルFFC4部分、量子化テーブルFFC0部分など)を含む画像情報のフレームが配置されています。 、APPやCOMなど)やSCAN部分(FFDA部分)など。 JPEG 画像には 1 つ以上の SCAN が含まれており (プログレッシブ モードには複数の SCAN が含まれます)、1 つの SCAN には 1 つ以上の RST (再起動間隔) があり、1 つの RST には 1 つ以上の MCU があり、1 つの MCU には 1 つ以上のブロックがあります。
rfc2435 jpeg 定義規格をよく読んでください
主にSOSセグメントの詳細情報を求めています
https://www.w3.org/graphics/jpeg/itu-t81.pdf
jpeg 形式の情報技術 –
デジタル圧縮とエンコーディング
連続静止画像 –
要件とガイドライン
=========================================
AC コード テーブルの構造
AC AC テーブル内のゼロ以外の各 AC 番号は、次の形式の複合 8 ビット値 RS によって記述されます。
RS = バイナリ「RRRRSSSS」
1.RRRRは2進法で表現された10進数です。
2.SSSSはハフマンコード番号です。
3. RRRR は、ゼロ以外の数値間のゼロの数を表します。 2 つの特別な値: 16 個のゼロ RS==15,0==0b11110000
ブロック内の残りの係数がすべて 0 の場合、RS=0
4.SSSSハフマンテーブル
SSSS AC系数
1 -1. 1
2 -3,-2, 2,3
3 -7...-4 4...7
-15....-8。 8....15
-31...-16, 16....31
6 –63.–32、 32... .63
–127..–64、 64....127
8 –255..–128 128....255 10 -1 023..-512 512....1 023
-511..-256 256....511
ここまで、エンコードを理解するプロセスを整理してみました。
1. YCbCr 信号のフレームは、コサインサンプリング後に 8*8 を使用して多数の小さなグリッドに分割され、各小さなグリッドはブロックと呼ばれます。ブロック分割の基準は必ずしも8*8であるわけではなく、どのヘッドが定量化されるセルの数を決定するかはまだ明らかではありません。コサインサンプリングはどうでしょうか。
2. 2 つの量子化テーブルを使用して、各ブロックの Y と UV を定量化します。
3 つの量子化テーブルを生成し、My、Mu、Mv と名付けます。
3. My、Mu、MvをZスキャン順に並べ替えます
4. RRRRSSSS のバイナリ形式を使用して、z 配列 My、Mu、Mv を再エンコードし、3 つのテーブル内のすべての 0 を圧縮します。
3 つのテーブルの最初の数値は DC 係数で、他の数値は AC 係数です。
5. 次に、2 つの DC および 2 AC ハフマン テーブルを使用して、上記の 3 つのテーブルの DC 係数と AC 係数をそれぞれエンコードし、3 つのテーブルのビット ストリームを生成します。SOS ヘッダーには、どのテーブルを使用してエンコードするかを指定するパラメータがあります。 yuv コンポーネント。したがって、これら 4 つのハフマン テーブルは jpeg ユニバーサル テーブルだと思います。コーディング時に最初にカウントされることはありません。最後に、DC を最初に、AC を最後に配置して表を作成します。
現在の質問は、エンコードするときに、Y、U、V の 3 ビットのハフマン テーブルも yuv422 の順序で配置され、ファイルに格納されるかということです。平たく言えば、Y1、U、Y2、V の形式になります。デコード中、これら 3 つのコンポーネントのビット ストリームも YCbCr インターリーブ方式に従って分割され、再組み立てされますか? 0 は各 2 つのブロックのビット ストリーム間の区切り文字として使用されますか、それとも 8*8 の数値のカウントを復元してブロックを完成する必要がありますか? 各ブロックのデータ ブロックも左から右、上から下に配置されます。 . 1つの写真フレームに結合しますか?コサインをサンプリングする方法は言うまでもありません。
正直に言うと、小さな絵の中に多くの知識が含まれており、このような国際標準を策定するのは非常に困難です。深い数学的知識が必要ですが、このエンコードとデコードのプロセスをよく理解していれば、h264 などの他の圧縮方式を簡単に理解できるはずです。
現在の問題は、これらの標準プロトコル文書を誰も翻訳していないことです。中国語の教材はインターネット上にほとんどなく、どれも似たような内容で、基本的なものばかりで深い理解はありません。
私の見解は、プロトコル標準で問題が発生した場合は、rfc から始まる英語の標準プロトコル文書に直接アクセスし、翻訳して理解するだけでよく、オンラインで中国語の情報を調べる必要はありません。