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.1856
				int s1 = round(s * 100); //-3019
				s = s1 / 100.0;    //-30.19
			  
				o[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;         //计算连续的0
		for (int a = 0; a < 64; a++) {                               //  a
			if ((i[a] == 0) && (i[a + 1] == 0) && ((a + 1) < 63)) { //000001
				z++;
				
			}                                                        // a
			if ((i[a] == 0) && (i[a + 1] == 0) && ((a + 1) == 63)){ //0000结束
				z++;             //本次的0
				o[t][0] =0;
				o[t][1] = z+1;   //加a+1的0
				break;           //判断完成

			}                                               //  a
			if ((i[a] == 0) && (i[a + 1] != 0)) {           //000100
				z++;                  //加上本次的一个0
				o[t][0] = i[a + 1]; 
				o[t][1] = z;
				z = 0;           //清0,计算下次的连续0
				t++;
				a = a + 1;   
				
			}                  
			if ((i[a] != 0)&&(a==0)) {   //第一个数非0
				o[t][0] = i[a];
				o[t][1] = 0;
				t++;

			}
			if ((a>0)&&(i[a] != 0)&&(i[a-1]!=0)) {   //防止第3种重复读取,这种是读取连续的非0
				o[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个v

	unsigned 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);               //补0
	for (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);                 //补0
	for (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,lh1
	char (*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;
}


 

 

 

 

 

 

 

 

 

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Oracle MongoDB

听课的时候第一次碰到&#xff0c;可以了解一下吧&#xff0c;就直接开了墨者学院的靶场 #oracle数据库 Oracle数据库注入全方位利用 - 先知社区 这篇写的真的很好 1.判断注入点 当时找了半天没找到 看样子是找到了&#xff0c;测试一下看看 id1 and 11 时没有报错 2.判断字段…

开发人员必须掌握的几个高级命令

xargs命令 在平时的使用中,我认为 xargs 这个命令还是较为重要和方便的。我们可以通过使用这个命令,将命令输出的结果作为参数传递给另一个命令。 比如说我们想找出某个路径下以 .conf 结尾的文件,并将这些文件进行分类,那么普通的做法就是先将以 .conf 结尾的文件先找出…

linux(centos7)mysql8.0主从集群搭建(两台机器)

docker安装:&#xff08;转载&#xff09;centos7安装Docker详细步骤&#xff08;无坑版教程&#xff09;-CSDN博客 环境信息 主数据库服务器&#xff1a;192.168.1.10 从数据库服务器&#xff1a;192.168.1.11 1. mysql8.0镜像下载 docker pull mysql:8.0.23 2.创建docke…

SaaS 电商设计 (五) 私有化部署-实现 binlog 中间件适配

一、 背景 具体的中间件私有化背景在上文 SaaS 电商设计 (二) 私有化部署-缓存中间件适配 已有做相关介绍.这里具体讨论的场景是通过解析mysql binlog 来实现mysql到其他数据源的同步.具体比如:在电商的解决方案业务流中经常有 ES 的使用场景,用以解决一些复杂的查询和搜索商品…

17--异常处理

1、异常概述 1.1 什么是异常 异常&#xff1a;指的是程序在执行过程中&#xff0c;出现的非正常情况&#xff0c;如果不处理最终会导致JVM的非正常停止。 异常指的并不是语法错误和逻辑错误。语法错了&#xff0c;编译不通过&#xff0c;不会产生字节码文件&#xff0c;根本运…

01.前言

前言 1.什么是前端开发 前端开发是创建 Web 页面或 app 等前端界面呈现给用户的过程核心技术&#xff1a;HTML&#xff0c;CSS&#xff0c;JavaScript 以及衍生出的各种技术&#xff0c;框架等 2.前端开发应用场景 3.前端职业路线 4.什么是CS架构与BS架构 介绍 应用软件&a…

SuperMap iClient3D for Cesium 实现鼠标移动选中模型并显示模型对应字段

SuperMap iClient3D for cesium 实现鼠标移动选中模型并显示模型对应字段 一、实现思路二、数据制作1. 计算出模型中心点并保存到属性表中2. 计算出模型顶部高程3. 模型数据切缓存4. 发布三维服务. 三、代码编写 作者&#xff1a;xkf 一、实现思路 将模型属性数据存储到前端&a…

c++面经总结

C基础语法 C和c的区别 c中new和delete是对内存分配的运算符&#xff0c;取代了c中的malloc和free 标准c中的字符串类取代了标准c函数库头文件中的字符数组处理函数(c中没有字符串类型). 在c中&#xff0c;允许有相同的函数名&#xff0c;不过他们的参数类型不能完全相同&…

JAVA:深入探讨Java 8 Stream的强大功能与用法

1、简述 Java 8引入了Stream API&#xff0c;为处理集合数据提供了一种更为强大和灵活的方式。Stream是一种抽象的数据结构&#xff0c;它允许你以一种声明性的方式处理数据集合。与传统的集合操作不同&#xff0c;Stream并不是一个存储数据的数据结构&#xff0c;而是在源数据…

Leetcode—78.子集【中等】

2023每日刷题&#xff08;五十九&#xff09; Leetcode—78.子集 算法思想 实现代码 class Solution { public:vector<vector<int>> subsets(vector<int>& nums) {int len nums.size();vector<int> path;vector<vector<int>> ans;f…

20231215给AIO-3399J适配Rockchip的原始Andoroid10的挖掘机开发板01

20231215给AIO-3399J适配Rockchip的原始Andoroid10的挖掘机开发板01 2023/12/15 10:49 【请严重注意&#xff1a;】如果刷不适配的SDK&#xff0c;可能会引起您的开发板【硬件发生物理】损坏&#xff01; 如果您按照本步骤刷机引起的一切后果&#xff0c;请自行承担责任&#x…

状态的一致性和FlinkSQL

状态一致性 一致性其实就是结果的正确性。精确一次是指数据有可能被处理多次&#xff0c;但是结果只有一个。 三个级别&#xff1a; 最多一次&#xff1a;1次或0次&#xff0c;有可能丢数据至少一次&#xff1a;1次或n次&#xff0c;出错可能会重试 输入端只要可以做到数据重…

vue3 添加编辑页使用 cron 表达式生成

示例效果图 1、添加组件 <template><div class"v3c"><ul class"v3c-tab"><li class"v3c-tab-item" :class"{ v3c-active: tabActive 1 }" click"onHandleTab(1)">秒</li><li class&qu…

【Linux系统编程二十一】:(进程通信3)--消息队列/信号量(system v标准的内核数据结构的设计模式)

【Linux系统编程二十】&#xff1a;消息队列/信号量(system v标准的内核数据结构的设计模式&#xff09; 一.消息队列二.system v标准的内核数据结构的设计三.四个概念(互斥/临界)四.信号量1.多线程并发访问2.计数器3.原子的4.总结 一.消息队列 一个叫做a进程啊&#xff0c;一个…

乐益达教育网页

目录 一、网页效果 二、html代码 三、CSS代码 四、JS代码 一、网页效果 二、html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, in…

Milesight VPN server.js 任意文件读取漏洞(CVE-2023-23907)

0x01 产品简介 MilesightVPN 是一款软件&#xff0c;一个 Milesight 产品的 VPN 通道设置过程更加完善&#xff0c;并可通过网络服务器界面连接状态。 0x02 漏洞概述 MilesightVPN server.js接口处存在文件读取漏洞&#xff0c;攻击者可通过该漏洞读取系统重要文件&#xff…

LeetCode-42. 接雨水【栈 数组 双指针 动态规划 单调栈】

LeetCode-42. 接雨水【栈 数组 双指针 动态规划 单调栈】 题目描述&#xff1a;解题思路一&#xff1a;单调栈&#xff0c;维护一个单调递减栈。每当遇到当前元素大于栈顶元素就出栈&#xff0c;在出栈时更新答案。当遇到出栈的情况&#xff0c;若单调栈栈左边有一个元素则必有…

Java医院信息化建设云HIS系统源码

云HIS提供标准化、信息化、可共享的医疗信息管理系统&#xff0c;实现医患事务管理和临床诊疗管理等标准医疗管理信息系统的功能。优化就医、管理流程&#xff0c;提升患者满意度、基层首诊率&#xff0c;通过信息共享、辅助诊疗等手段&#xff0c;提高基层医生的服务能力构建和…

Ubuntu20.04 下编译安装 ffmpeg 和 ffplay

Ubuntu20.04 下编译安装 ffmpeg 和 ffplay 一、下载源码包二、安装依赖库三、编译四、添加环境变量五、验证是否成功六、问题 一、下载源码包 1.1 官方下载链接&#xff1a;http://ffmpeg.org/download.html 最新版本为6.1&#xff0c;点击 Download Source Code下载即可 &…

【深度学习】强化学习(二)马尔可夫决策过程

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略&#xff08;Policy&#xff09;4、马尔可夫决策过程1. 基本元素2. 交互过程的表示3. 马尔可夫过程&#xff08;Markov Process&#xff09;4. 马尔可夫决策过程&#xff08;MDP&#xff09;5. 轨迹的概率计…