c YUV 转 JPEG(准备霍夫曼编码)

先取yuv 文件中一个16×8的块,跑通全流程

理解与思路:

1.块分割

   YUV 文件分为:YUV444   YUV 422   YUV420。444:就是:12个char 有4个Y,4个U,4个    U,422:8个char  中有4个Y ,U,V各两个,420:意思就是8char里有6个Y,1个U,1个V。444与422  中的三分量多是交错存储的,420则是先存储Y,再存储U,V。

YUV存储也是线型存储的,不是平面块存储的。

对应到jpeg,也要按YUV的三种格式分别分块。jpeg协议中有一概念MCU:最小编码块。jpeg就是按MCU 为单位循环存储的。MCU中又有若干8×8 的block。这些block  就是Y  ,U ,V  分量的8×8  块。

我理解:对应YUV444,  一行内取24字节(这是指水平行,程序处理同时要取8个水平行,意思MCU=8×24字节)。含有Y,U,V各8个字节。也就是说:程序一次读取8行24个char ,处理成3张8×8的表,直到读完整个yuv文件

YUV422:一行 8个char中有4个Y,UV各2个。必须把Y凑成8那就要乘2。那就是一行为16个字节,MCU=16×8,取8行生成一张8×8的Y,UV 各半张,UV要补0成为8×8。

YUV420:一行8个字节中有6个Y,UV各一个,Y×8成为8的倍数水平要取64字节,所以,MCU=8×8×8,意思就是有6张Y表,UV 各一张。

离散余弦变换

找了一组数据来验证,发现余弦转换除了直流分量外都正确,DC不正确,也就是转换后第一个数不正确,才想到第一个数的取值应该超出了char的取值范围-128<char<127,所以换用int存储所以数值

  2.量化

Jpeg几乎对Y分量不压缩处理,只压缩UV彩色分量。把几个Y拼在一起余弦处理因为余弦转化是没有损失的,也不会让数据失真。真正让数据失真是量化这个环节,如果量化表全为1,则是无损量化。

下面的程序为借用成品图片的量化表。

3. Z形排序

采用查表法,先按Z顺序生成一个表,读数时按表的数值作为读取位置。

4. 去0

    64个数中按位读取每一个数有4种情况:

    a)   本为是0,下一位也是0,但下一位不是数据结尾

     b)本位是0,下一位是0,但下一位是结尾

     c)本位是0,下一位不是0

     d)本位不是0

    判断生成的结果存储到2位数组中,第一位存储非0数字,第二位存储0的个数

5.  规范RLC 格式:去0后的数如中间有超过15个0,必须拆分; 结尾如有多个0改写为(0,0)

     Z 型排序这理还有一个细节错误,应该把直流DC 分出来,只排63个交流系数。

    等霍夫曼编码时再修改过来    

6. 范式霍夫曼编码

     准备借用成品jpeg图片的霍夫曼表来编码。

 这里还有几个问题没有搞明白,编码是变长度的比特流不是字节,而c操作的最小单位是字节char, 是怎样把比特流写入内存甚至文件的,难道是以比特流按8个比特为一个单位组成一个字节再写入?

从网上下载了一数据表验证本程序,从余弦转换开始到霍夫曼编码环节止数据只有4舍5入造成的微小差别。

660bbe5a8e064257ba72903c19708f15.jpeg


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>  //v4l2 头文件
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h>
#define PI 3.1415926#define  pic_width   1280     //1280*720  640*360  960*540   320*240   424*240  848*480 640*480
#define  pic_heigth  720#define filename  "/home/wjs/Pictures/1.yuv"
#define file1  "/home/wjs/Pictures/1.jpg"        //借用成品图片的量化表
//int fdct(char (*i)[8], char (*o)[8] );int  main(void) {
//-----------FDCT()函数------------------------------------int fdct(char (*i)[8], int(*o)[8] ) {  //i 为输入   o 为参数传入的输出转换后的数据double s;double au;double av;for (int u = 0; u < 8; u++) {for (int v = 0; v < 8; v++) {for (int y = 0; y < 8; y++) {for (int x = 0; x < 8; x++) {s = s + (1.0 / 4) * i[y][x] * cos((2 * y + 1) * u * PI / 16) * cos((2 * x + 1) * v * PI / 16);}}if (u == 0) {au = 1.0 / sqrt(2);} else {au = 1.0;}if (v == 0) {av = 1.0 / sqrt(2);} else {av = 1.0;}s = s * au * av;   //-30.1856int s1 = round(s * 100); //-3019s = s1 / 100.0;    //-30.19o[u][v] = s;       //double 转为char 类型s = 0;}}return 0;}
//-----------规范RLC格式---------------------int zl(int len,int (*i)[2],int (*o)[2]){int t=0;                        //如果中间有一次超过15个0,o的下标要加一,因为增加了(15,0)for(int a=0;a<len;a++){if((a<len)&&(i[a][1]>=16)&&(i[a][0]!=0)){o[a+t][0]=0;o[a+t][1]=15;o[a+1+t][0]=i[a][0];o[a+1+t][1]=i[a][1]-15;t++;}if((a<len)&&(i[a][1]<16)){        memcpy(&(o[a+t][0]),&(i[a][0]),8);  //一行为单位复制}if((a==len)&&(i[a][0]==0)){o[a+t][0]=0;o[a+t][1]=0;break;}}return len+t;}//-----------去0-----------------------------int q0(int i[64], int (*o)[2]) {int t = 0;         //输出数组序号int z = 0;         //计算连续的0for (int a = 0; a < 64; a++) {                               //  aif ((i[a] == 0) && (i[a + 1] == 0) && ((a + 1) < 63)) { //000001z++;}                                                        // aif ((i[a] == 0) && (i[a + 1] == 0) && ((a + 1) == 63)){ //0000结束z++;             //本次的0o[t][0] =0;o[t][1] = z+1;   //加a+1的0break;           //判断完成}                                               //  aif ((i[a] == 0) && (i[a + 1] != 0)) {           //000100z++;                  //加上本次的一个0o[t][0] = i[a + 1]; o[t][1] = z;z = 0;           //清0,计算下次的连续0t++;a = a + 1;   }                  if ((i[a] != 0)&&(a==0)) {   //第一个数非0o[t][0] = i[a];o[t][1] = 0;t++;}if ((a>0)&&(i[a] != 0)&&(i[a-1]!=0)) {   //防止第3种重复读取,这种是读取连续的非0o[t][0] = i[a];o[t][1] = 0;t++;}}return t+1;}//--------Z 排序--------------------------------int zz(int (*i)[8], int o[64]) {int zb[64] = {0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63};int *p = (int *)i;for (int a = 0; a < 64; a++) {int c = zb[a];o[a] = p[c];}return 0;}
// -----量化函数---------------int lh(int (*i)[8], char (*lhb)[8], int (*o)[8]) {for (int a = 0; a < 8; a++) {for (int b = 0; b < 8; b++) {o[a][b] = round((i[a][b]) / (lhb[a][b]));}}return 0;}FILE *f1 = fopen(file1, "rb");  //如用mmap 必须以读写方式打开文件if (f1 == NULL) {puts("filename error");exit(-1);}fseek(f1, 0, SEEK_END);int len1 = ftell(f1);fseek(f1, 0, SEEK_SET);int fd1 = fileno(f1);unsigned char *mp1 = mmap(NULL, len1, PROT_READ, MAP_SHARED, fd1, 0); //必须要读,写
//---------读量化表-------------------------------char lh00[64] = {};                 //提取量化表char lh10[64] = {};for (int a = 0; a < len1; a++) {if ((mp1[a] == 0xff) && (mp1[a + 1] == 0xdb) && (mp1[a + 2] == 0)) {for (int b = 0; b < 65; b++) {if (mp1[a + b + 4] == 0) {memcpy(lh00, &(mp1[a + b + 5]), 64);}if (mp1[a + b + 4] == 1) {memcpy(lh10, &(mp1[a + b + 5]), 64);}}//	printf("\n");}}//---------------------------------------------------------------------------FILE *f = fopen(filename, "rb");  //如用mmap 必须以读写方式打开文件if (f == NULL) {puts("filename error");exit(-1);}fseek(f, 0, SEEK_END);int len_file = ftell(f);fseek(f, 0, SEEK_SET);int fd = fileno(f);unsigned char *mp = mmap(NULL, len_file, PROT_READ, MAP_SHARED, fd, 0); //必须要读,写int width_pic = 1280;int heigth_pic = 720;//out:1.文件总长度 len_file// 2.图片宽度 1280  width_pic// 3.图片高度 720   heigth_pic// 4.图片数据 *mp//--------------------------------------------------------//因为yuv422数据是4个字节生成2个像素点,所以图片的width*heigth*2=len_file//摄像头输出的分辨率图片宽度×2都是8的倍数,高度X2也是8的倍数。所以yuv数据都不用补列。最多补文件末尾的空字节//摄像头的分辨率末尾数X2=0,8,6,4,2,就是8的倍数// 1.----------检查yuv文件长度末尾是否有空字节,有,补0-------------------if ((width_pic * heigth_pic * 2) != len_file) {memset(&mp[len_file], 0, (width_pic * heigth_pic * 2 - len_file));}// 2.----------取16*8--------------------------------------//YUV422 字节排列:[0]=Y0 [1]=U0 [2]=Y1 [3]=V0 [4]=Y2 [5]=U1 [6]=Y3 [7]=V1    4个Y  2个U  2个V//所以取16个字节的yuv 数据,含8个y ,4个u,4个vunsigned char (*i_mp)[width_pic] = (unsigned char (*)[width_pic])mp;int m = 0;     //  行  取1个(0,0)开始的16×8 块int n = 0;     //列int heigth = heigth_pic;   //行int width = width_pic;     //列int fheigth = 8;int fwidth = 16;unsigned char o[8][16] = {};    //取出16×8的块if ((m + fheigth) > heigth) {puts("fheigth error");exit(-1);}if ((n + fwidth) > width) {puts("fwidth error");exit(-1);}for (int a = 0; a < fheigth; a++) {for (int b = 0; b < fwidth; b++) {o[a][b] = i_mp[a + m][b + n];}}munmap(mp, len_file);
//----------分离Y---------------------char y[8][8] = {};for (int a = 0; a < 8; a++) {for (int b = 0; b < 8; b++) {y[a][b] = o[a][2*b]-128;}}/* char y1[8][8]={{140,144,147,140,140,155,179,179},//验证程序数据{144,152,140,147,140,148,167,179},{152,155,136,167,163,162,152,172},{168,145,156,160,152,155,136,160},{162,148,156,148,140,136,147,162},{147,167,140,155,155,140,136,162},{136,156,123,167,162,144,140,147},{148,155,136,155,152,147,147,136}  };char y[8][8]={};for(int a=0;a<8;a++){for(int b=0;b<8;b++){y[b][a]=y1[b][a]-128;}}*///----------分离U-----------------char u[8][8] = {};memset(&u, 0, 64);               //补0for (int a = 0; a < 8; a++) {for (int b = 0; b < 8; b++) {if ((4 * b + 1) <= 16) {u[a][b] = o[a][4 * b + 1] - 128;}}}//-----------分离v-----------------char v[8][8] = {};memset(&v, 0, 64);                 //补0for (int a = 0; a < 8; a++) {for (int b = 0; b < 8; b++) {if ((4 * b + 3) <= 16) {v[a][b] = o[a][4 * b + 3] - 128;}}}//--------------FDCY Y- U  V----------------------------int y_fdct[8][8] = {};fdct(y,y_fdct);int u_fdct[8][8] = {};fdct(u, u_fdct);int v_fdct[8][8] = {};fdct(v, v_fdct);//----------------量化 Y U  V  -----------------------------------//借用成品jpg图片量化表,lh0,lh1char (*lh0)[8] = (char (*)[8])lh00;char (*lh1)[8] = (char (*)[8])lh10;/*	char lh0[8][8]={            // 验证数据用量化表{3,5,7,9,11,13,15,17},{5,7,9,11,13,15,17,19},{7,9,11,13,15,17,19,21},{9,11,13,15,17,19,21,23},{11,13,15,17,19,21,23,25},{13,15,17,19,21,23,25,27},{15,17,19,21,23,25,27,29},{17,19,21,23,25,27,29,31}};*/int y_lh[8][8] = {};int u_lh[8][8] = {};int v_lh[8][8] = {};lh(y_fdct, lh0, y_lh);lh(u_fdct, lh1, u_lh);lh(v_fdct, lh1, v_lh);//---------Z排序--------------------------int y_z[64] = {};int u_z[64] = {};int v_z[64] = {};zz(y_lh, y_z);zz(u_lh, u_z);zz(v_lh, v_z);
//--------去0-------------------------int y_0[64][2] = {};int u_0[64][2] = {};int v_0[64][2] = {};int len_y_0 = q0(y_z, y_0);int len_u_0 = q0(u_z, u_0);int len_v_0 = q0(v_z, v_0);//-----整理规范------------------------int y_zl[64][2]={};         //定义是必须指定数组内存大小,下标不能用变量,64是此数组的最大值int u_zl[64][2]={};int v_zl[64][2]={};int len_y_zl=zl(len_y_0,y_0,y_zl);int len_u_zl=zl(len_u_0,u_0,u_zl);int len_v_zl=zl(len_v_0,v_0,v_zl);//---------------------------------------------------------------------for (int a = 0; a <len_y_zl;a++) {for (int b = 0; b < 2; b++) {printf("%d  ,",y_zl[a][b]);}printf("\n");}return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/224126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis——01,服务器购买、安装Redis

服务器购买、安装Redis 一、随便去一个主流的国内主流的云服务提供商&#xff0c;购买一个服务器。二、Redis安装&#xff1a;————————创作不易&#xff0c;如觉不错&#xff0c;随手点赞&#xff0c;关注&#xff0c;收藏(*&#xffe3;︶&#xffe3;)&#xff0c;谢…

[论文阅读]Multimodal Virtual Point 3D Detection

Multimodal Virtual Point 3D Detection 多模态虚拟点3D检测 论文网址&#xff1a;MVP 论文代码&#xff1a;MVP 论文简读 方法MVP方法的核心思想是将RGB图像中的2D检测结果转换为虚拟的3D点&#xff0c;并将这些虚拟点与原始的Lidar点云合并。具体步骤如下&#xff1a; (1)…

VINS-MONO代码解读6----pose_graph

开始pose_graph部分&#xff0c;本部分记住一句话无论是快速重定位还是正常重定位&#xff0c;求出 T w 1 w 2 T_{w_1w_2} Tw1​w2​​就是终极目标。 还剩一个整体Pipeline~~ 1. pose_graph_node.cpp 注意&#xff0c;定义全局变量时即实例化了一个对象 PoseGraph posegra…

C++之模板

目录 泛型编程 模板 函数模板 函数模板的实例化 隐式实例化 显示实例化 类模板 我们知道STL&#xff08;标准模板库&#xff09;是C学习的精华所在&#xff0c;在学习STL之前我们得先学习一个新的知识点-------模板。那么模板究竟是什么呢&#xff1f;围绕着这个问题&a…

绘图示例---QT手动调用绘图事件,按钮控制图片

效果&#xff1a; 点击 “移动” 图片向右移动20&#xff0c;点击 “西理win嘛” 图片每秒向右移动20 QQ录屏20231212164128 下面时代码详解&#xff1a; 注意使用UI和代码实现按钮的不同 UI: ui->pushButton->setGeometry(windowWidth-105, windowHeight-25, 100, 20);…

【思考】只有实对称矩阵才能正交对角化吗?【矩阵的合同】

1&#xff1a;命题改写&#xff08;A可以正交对角化&#xff09; 2&#xff1a;左乘Q右乘Q逆&#xff08;Q转置&#xff09; 3&#xff1a;取转置 4&#xff1a;得证 总结 可以看到&#xff0c;矩阵如果可以正交对角化&#xff0c;那么一定是实对称矩阵。 另外&#xff0c;这…

【期末复习向】长江后浪推前浪之ChatGPT概述

参考文章&#xff1a;GPT系列模型技术路径演进-CSDN博客 这篇文章讲了之前称霸NLP领域的预训练模型bert&#xff0c;它是基于预训练理念&#xff0c;采用完形填空和下一句预测任务2个预训练任务完成特征的提取。当时很多的特定领域的NLP任务&#xff08;如情感分类&#xff0c…

LLM之Agent(六)| 使用AutoGen、LangChian、RAG以及函数调用构建超级对话系统

本文我们将尝试AutoGen集成函数调用功能。函数调用最早出现在Open AI API中&#xff0c;它允许用户调用外部API来增强系统的整体功能和效率。例如&#xff0c;在对话过程中根据需要调用天气API。 函数调用和Agent有各种组合&#xff0c;在这里我们将通过函数调用调用RAG检索增强…

【SpringBoot】配置文件

配置文件官网 1. 配置方式 application.propertiesapplication.yml / application.yaml 2. 自定义配置信息 将实体类中的本应该写死的信息写在属性配置文件中。 可以使用 Value("${键名}") 获取&#xff0c;也可以使用 ConfigurationProperties(prefix"前…

访谈型软文写作方式,媒介盒子告诉你

访谈型软文一般用于维护企业形象&#xff0c;分享品牌故事。但是许多企业在写访谈型软文时经常容易跑偏或者写来写去没有逻辑&#xff0c;今天媒介盒子就来和大家分享访谈型软文的写作方式&#xff0c;看完这四点&#xff0c;小白也能写好访谈型软文&#xff01; 一、 访谈对象…

C++_构造函数与析构函数

目录 1、构造函数的写法 1.2 构造函数优化写法 2、默认构造函数与默认成员函数 2.1 默认成员函数对不同类型的处理 3、对内置类型的补丁 4、析构函数 4.1 析构函数的写法 5、默认析构函数 6、初始化列表 6.1 初始化列表的写法 6.2 初始化列表的作用 6.3 回顾与总结 …

【Proteus仿真】【51单片机】电子门铃设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使共阴数码管&#xff0c;按键、无源蜂鸣器等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管默认显示第一种门铃音调&#xff0c;可通过K1键切…

flutter的SingleChildScrollView控件详解

文章目录 SingleChildScrollView的介绍和使用场景详细介绍 SingleChildScrollView的介绍和使用场景 SingleChildScrollView 是 Flutter 中的一个小部件&#xff0c;用于创建一个可滚动的单个子部件。它通常用于处理内容超出屏幕可见区域的情况&#xff0c;允许用户通过滚动来查…

5.3 Linux DNS 服务

1、概念介绍 DNS&#xff08;Domain Name System&#xff09;域名系统&#xff0c;是互联网的一项核心服务&#xff0c;可以作为域名和IP地址相互映射的一个分布式数据库&#xff0c;提供域名与IP地址的解析服务&#xff0c;能够使人们更加方便的使用域名访问互联网而不是记住…

Vue3-11- 【v-for】循环数组

v-for的基本介绍 v-for 是一个指令&#xff0c; 它是用来在 html 模板中实现循环的。它可以循环 普通的数组、也可以直接循环一个范围值&#xff0c;也可以循环对象的每个属性。v-for 的语法介绍 <div v-for"(item,index) in arrayName" : key"index"…

有哪些好用的运维管理软件?哪个工单管理系统的操作简单一些?

运维管理软件可以帮助企业更有效地管理公司内外的事务&#xff0c;比如现在不少公司就引入了工单管理系统来处理后勤和售后的事务。那么&#xff0c;有哪些好用的运维管理软件&#xff1f;哪个的操作简单一些呢&#xff1f;   随着技术的发展和成熟&#xff0c;现在的工单管理…

vue_域名部署无法访问后端

前言 目前部署的比较另类&#xff0c;因为服务器为windows&#xff0c;目前还不是很会nginx&#xff0c;所以现在就只能在服务器上安装nodejs&#xff0c;然后直接使用npm run dev命令行的方式运行项目 遇到的坑 使用ip访问前端的时候&#xff0c;就可以访问&#xff0c;但是…

压缩照片怎么压缩?半分钟解决!

有时候我们在平台上传照片的时候&#xff0c;会有图片大小限制&#xff0c;想要将照片压缩到限制的大小范围内&#xff0c;可以使用专业的图片压缩软件、图片处理软件或者在线网站压缩&#xff0c;下面给大家分享三个方法&#xff0c;压缩照片的同时还能保持图片清晰度哦&#…

Windows10安装Node.js环境

Windows10安装Node.js环境 文章目录 1.下载安装包2.安装配置2.1安装2.2 配置全局的安装路径和缓存路径2.3配置环境变量2.4配置镜像源2.5包管理工具 3.查看版本4.编译跑项目5.总结 1.下载安装包 官方下载网址如下&#xff1a; https://nodejs.org/enInstaller表示是安装程序&a…

【回眸】Tessy 单元测试软件使用指南(三)怎么打桩和指针测试

目录 前言 Tessy 如何进行打桩操作 普通桩 高级桩 手写桩 Tessy单元测试之指针相关测试注意事项 有类型的指针&#xff08;非函数指针&#xff09;&#xff1a; 有类型的函数指针&#xff1a; void 类型的指针&#xff1a; 结语 前言 进行单元测试之后&#xff0c;但凡…