#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fmt_jpeg.h"
//double数据的四舍五入
inline int round_double(double val)
{
double res = (val > 0.0) ? (val + 0.5) : (val - 0.5);
return (int)res;
}
//2Byte小端转换为大端
inline u16 rd_BigEnd16(u16 val)
{
u8 b1 = (u8)(val);
u8 b2 = (u8)(val >> 8);
u16 temp = (u16)b2 + ((u16)b1 << 8);
return temp;
}
//对数组全部元素进行求和,用于求DC/AC_NRCodes数组的sum来确定DC/AC_Values数组的长度
static u16 sum_arr(u8* arr, u8 len_arr)
{
u16 sum = 0;
for (u8 i = 0; i < len_arr; i++)
sum += arr[i];
return sum;
}
//为了方便对齐8/16pixel的MCU, 预读取SOF0的clrY_sample用于判断jpeg的采样比率
//为了方便开辟空间, 预读取SOF0的height/width
void rd_SOF0_2get_size(FILE* fp, u8* pY_sample, u16* pimw, u16* pimh)
{
u16 seg_label_t = 0;
u16 seg_size_t = 0;
u8 dump = 0; //占位字节,用于保存不需管的字节
fread(&seg_label_t, 2, 1, fp); //SOI
fread(&seg_label_t, 2, 1, fp); //find SOF0 label
while (seg_label_t != 0xC0FF)
{
if (seg_label_t == 0x01FF) //TEM
fread(&seg_label_t, 2, 1, fp);
else {
fread(&seg_size_t, 2, 1, fp);
fseek(fp, rd_BigEnd16(seg_size_t) - 2, SEEK_CUR);
fread(&seg_label_t, 2, 1, fp);
}
}
fread(&seg_size_t, 2, 1, fp); //seg_len
fread(&dump, 1, 1, fp); //acc
fread(pimh, 2, 1, fp);
*pimh = rd_BigEnd16(*pimh); //imheight
fread(pimw, 2, 1, fp);
*pimw = rd_BigEnd16(*pimw); //imwidth
fread(&dump, 1, 1, fp); //color_component
fread(&dump, 1, 1, fp); //clrY_id = 0x01
fread(pY_sample, 1, 1, fp); //0x11(444) or 0x22(420)
fseek(fp, 0, SEEK_SET); //rewind to the start of the file
return;
}
//======================== 解码核心函数 ======================== 解码核心函数 =========================
void Inverse_DCT(double res[][8], const double block[][8])
{
double temp[8][8] = { 0.0 };
for (u8 i = 0; i < 8; i++) {
for (u8 j = 0; j < 8; j++) {
for (u8 t = 0; t < 8; t++) {
temp[i][j] += DCT_T[i][t] * block[t][j];
}
}
}
for (u8 i = 0; i < 8; i++) {
for (u8 j = 0; j < 8; j++) {
for (u8 t = 0; t < 8; t++) {
res[i][j] += temp[i][t] * DCT[t][j];
}
}
}
}
//生成DHT的时候顺便计算相同码字长度下的(最大码字+1),生成由码长定位码字的映射表len_tbl(注意参数max_inThisLen[17], len_tbl[17][130])
void Build_Huffman_Table(const u8* nr_codes, const u8* std_tbl, HuffType* huffman_tbl, u16* max_inThisLen, u8* huff_len_tbl[])
{
u8 pos_in_tbl = 0;
u16 code_val = 0;
for (int k = 1; k <= 16; k++)
{
u8 num = 0;
for (int j = 1; j <= nr_codes[k - 1]; j++)
{
huff_len_tbl[k][num] = std_tbl[pos_in_tbl];
num++;
huffman_tbl[std_tbl[pos_in_tbl]].code = code_val;
huffman_tbl[std_tbl[pos_in_tbl]].len = k;
pos_in_tbl++;
code_val++;
}
max_inThisLen[k] = code_val;
code_val <<= 1;
}
//jpeg的Huffman编码不会出现1bit的码字,为了不在读取第一比特为1就因为[1]=0而错误地认为第一个比特是某个码字...
//保险起见应该无论第一个比特是啥都认为当前比特大于max(虽然[1]本来就是0),所以强制len=1时的max码字为0b
max_inThisLen[0] = 0; //此条语句其实冗余了
//max_in_ThisLen[1] = 0;
//<注意>
//就是因为上方论述才导致那张超清少女手机壁纸(phonepaper.jpeg)解码失败,根本原因是其LumiAC的Huffman码是有len=1的情况
//其实此图片的码长为1的码字只有一个,即0b,码长为2的码字为空,码长为3的码字为100b,101b,码长为4的...
//因此由于len=2没有归零所以得到结果: max[1]=1b=1, max[2]=10b=2, max[3]=110b=6, max[4]=...
//max[]是递增的,此jpeg的码表不存在2bit码字,大于3bit的码字必定也大于2bit的max,因此max[2]不归零并不影响解码正确性
//另外,huffman解码时首bit遇到0则判断属于1bit长度的码字,所以上面的语句要注释掉以允许max[2]=1
//这样当遇到bit流的首bit为1时,因为下面restore_RLE()函数用>=来判断max码字,因此可以保证不会坠入1bit码字的判定中
}
//newBytePos初始化为负数,当其为负数时说明进入函数时应该重新读取新的newByte
//回复为rle数组的同时顺便恢复成8×8的矩阵形式
void rd_restore_HuffBit_RLE_Mat(int* rle, int temp[][8], int* pnewByte, int* pnewBytePos, int* prev_DC, \
const u16* max_first_DC, const u16* max_first_AC, u8* huff_lentbl_DC[], u8* huff_lentbl_AC[], \
HuffType* rd_hufftbl_DC, HuffType* rd_hufftbl_AC, FILE* fp, const u8* dc_nrcodes, const u8* ac_nrcodes)
{
//首先出现的是RLE编码的Huffman码字,然后根据码字低字节得知下个码字的码长,并读取幅度码字
static u8 mmask[8] = { 1,2,4,8,16,32,64,128 };
int value = 0, code_len = 0;
u8 rle_idx = 0;
BitString bitcode_1, bitcode_2; //RLE编码对儿的第一项(修复前nrz/len修复后nrz)和第二项(magnitude)
bitcode_1.code = 0; bitcode_1.len = 0;
bitcode_2.code = 0; bitcode_2.len = 0;
u8 xx = 0, yy = 0;
//outer-func default newByte=0, newBytePos=-1
while (value >= (int)max_first_DC[code_len]) //必须是 >=, 因为max指示的值是当前最大码字+1,可能是后面码字的前缀
{
if (*pnewBytePos < 0) {
if (*pnewByte == 0xFF) fread(pnewByte, 1, 1, fp); //0xFF之后会紧跟一个无意义的0x00(忽略掉)
fread(pnewByte, 1, 1, fp);
*pnewBytePos = 7;
}
value <<= 1;
value = value + ((*pnewByte & mmask[*pnewBytePos]) ? 1 : 0);
(*pnewBytePos)--;
code_len++;
}
for (int p = 0; p < dc_nrcodes[code_len - 1]; p++) { //DC映射表其实不会太长(远小于130)
if (rd_hufftbl_DC[huff_lentbl_DC[code_len][p]].code == value) { //在码长映射表中寻找与value相符合的码字
bitcode_1.code = huff_lentbl_DC[code_len][p]; //这里没有存码字(如110b),而是直接存(如0x5)
bitcode_1.len = code_len; //注意DC部分的nrz肯定是0,故省略0x05成0x5
rle[rle_idx] = bitcode_1.code; //DC的nrz肯定是0,不过为了兼容AC还是保存了0
rle_idx++;
value = 0;
code_len = 0;
break;
}
}
if (bitcode_1.code == 0) {
bitcode_2.code = 0;
bitcode_2.len = 0;
rle[0] = 0; //将第一项从Bit编码修复为RLE编码的DC_nrz=0
rle[rle_idx] = bitcode_2.code + *prev_DC; //差分编码是0时表示当前实际DC值等于prev_DC
temp[0][0] = rle[rle_idx];
yy++;
rle_idx++;
}
else {
bitcode_2.len = bitcode_1.code & 0x0F;
rle[0] = 0; //修复第一项
for (int p = 0; p < bitcode_2.len; p++) { //第一项指明了幅度码字的码长
if (*pnewBytePos < 0) {
if (*pnewByte == 0xFF) fread(pnewByte, 1, 1, fp); //0xFF之后会紧跟一个无意义的0x00(忽略掉)
fread(pnewByte, 1, 1, fp);
*pnewBytePos = 7;
}
value <<= 1;
//value |= (*newByte & mmask[*newBytePos]);
value = value + ((*pnewByte & mmask[*pnewBytePos]) ? 1 : 0);
(*pnewBytePos)--;
code_len++;
if ((p == 0) && (value == 0))
bitcode_2.code = -1; //暂时把code置负,用于指示首个bit不是1(在幅度编码中代表负数)
}
if (bitcode_2.code < 0) {
int tt = (-1) * (1 << bitcode_2.len) + 1;
bitcode_2.code = value + tt + *prev_DC;
rle[rle_idx] = bitcode_2.code;
temp[0][0] = rle[rle_idx];
yy++;
rle_idx++;
}
else {
bitcode_2.code = value + *prev_DC;
rle[rle_idx] = bitcode_2.code;
temp[0][0] = rle[rle_idx];
yy++;
rle_idx++;
}
*prev_DC = bitcode_2.code; //更新prev_DC
value = 0;
code_len = 0;
bitcode_1.code = 0; bitcode_1.len = 0;
bitcode_2.code = 0; bitcode_2.len = 0;
}
//AC part
int nums = 0;
//记录RLE编码的原始数据中AC部分的长度(63),注意连零的个数也会记录...
//(不然长度无�
没有合适的资源?快使用搜索试试~ 我知道了~
jpg转yuv,jpg解码纯c实现,无任何第三方库
共5个文件
c:2个
yuv:1个
jpg:1个
需积分: 5 10 下载量 85 浏览量
2023-10-24
14:32:14
上传
评论
收藏 344KB RAR 举报
温馨提示
jpg转yuv420/yuv444 ,包括jpg头解析、Huffman解码 、IDCT等。纯C语言实现无任何第三方库。使用方法:代码编译后运行 ./main ./3.jpg ./4.yuv 444 第一个参数是输入jpg路径,第二个参数是输出yuv路径,第三个参数是输出格式。已经在windos和linux和arm上验证通过。已知BUG:通过photo shop转换出来的jpg文件解码会失败,原因是头部信息解析异常,建议通过windos自带的画图工具另存为jpg文件后解码。
资源推荐
资源详情
资源评论
收起资源包目录
jpg_to_yuv.rar (5个子文件)
jpg_to_yuv
4.yuv 1.12MB
fmt_jpeg.h 8KB
fmt_jpeg.c 34KB
main.c 2KB
3.jpg 89KB
共 5 条
- 1
资源评论
somnus、清澈
- 粉丝: 68
- 资源: 15
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功