C语言实现YUV文件转JPEG格式

 更新时间:2023年12月13日 09:35:58   作者:乐山劲松  
这篇文章主要为大家详细介绍了如何利用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 各一张。

2.量化

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

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

3. Z形排序

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

#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/wzpc/Pictures/my.yuv"
#define file1  "/home/wzpc/Pictures/my.jpg"        //借用成品图片的量化表
//int fdct(char (*i)[8], char (*o)[8] );
 
int  main(void) {
//-----------FDCT()函数------------------------------------
	int fdct(char (*i)[8], char (*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;
	}	
//--------Z 排序--------------------------------
	
int zz(char (*i1)[8],char o1[64]){
    int zb[64]={0,8,1,2,9,16,24,17,10,3,4,11,18,25,32,40,33,26,19,12,5,6,13,20,27,34,41,48,56,
            49,42,35,28,21,14,7,15,22,29,36,43,50,57,58,51,44,37,30,23,31,38,45,52,59,60,
	        53,46,39,47,54,61,62,55,63
             };
	char *p1=(char *)i1;
	for(int a=0;a<64;a++){
		int c=zb[a];
		o1[a]=p1[c];
 
}	
	
	return 0;
}
// -----量化函数---------------
int lh(char (*i)[8],char (*lhb)[8], char (*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");
			
		}
	}
	for(int a=0;a<64;a++){
//		printf("%d  ,",lh00[a]);
	}
	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;
		}
	}
	
	
	//----------分离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----------------------------
	char y_fdct[8][8] = {};
	fdct(y, y_fdct);
	char u_fdct[8][8] = {};
	fdct(u, u_fdct);
	char 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 y_lh[8][8]={};
	char u_lh[8][8]={};
	char v_lh[8][8]={};
	
	lh(y_fdct,lh0,y_lh);
	lh(u_fdct,lh1,u_lh);
	lh(v_fdct,lh1,v_lh);
	
//---------Z排序--------------------------
    char y_z[64]={};
	char u_z[64]={};
	char v_z[64]={};
	zz(y_lh,y_z);
	zz(u_lh,u_z);
	zz(v_lh,v_z);
	
	
 
		for (int b = 0; b < 64; b++) {
			printf("%d  ,", v_z[b]);
		}
	
	
	
	
	return 0;
}

到此这篇关于C语言实现YUV文件转JPEG格式的文章就介绍到这了,更多相关C语言YUV转JPEG内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++二分查找在搜索引擎多文档求交的应用分析

    C++二分查找在搜索引擎多文档求交的应用分析

    这篇文章主要介绍了C++二分查找在搜索引擎多文档求交的应用,实例分析了二分查找的原理与C++的实现及应用技巧,需要的朋友可以参考下
    2015-06-06
  • C++ 实现静态链表的简单实例

    C++ 实现静态链表的简单实例

    这篇文章主要介绍了C++ 实现静态链表的简单实例的相关资料,需要的朋友可以参考下
    2017-06-06
  • C语言实现简单学生成绩管理系统

    C语言实现简单学生成绩管理系统

    这篇文章主要为大家详细介绍了C语言实现简单学生成绩管理系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • C++ Qt开发之使用QUdpSocket实现UDP网络通信

    C++ Qt开发之使用QUdpSocket实现UDP网络通信

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,本文主要介绍如何运用QUdpSocket组件实现基于UDP的网络通信功能,需要的可以参考下
    2024-03-03
  • C++的内联函数你了解吗

    C++的内联函数你了解吗

    这篇文章主要为大家详细介绍了C++的内联函数,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • C++利用SQLite实现命令行工具

    C++利用SQLite实现命令行工具

    这篇文章主要为大家详细介绍了一个基于 C++、SQLite 和 Boost 库的简单交互式数据库操作 Shell,该 Shell 允许用户通过命令行输入执行各种数据库操作,感兴趣的可以了解下
    2023-11-11
  • C++ Assert()断言机制原理以及使用方法

    C++ Assert()断言机制原理以及使用方法

    下面小编就为大家带来一篇C++ Assert()断言机制原理以及使用方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • C语言之循环语句详细介绍

    C语言之循环语句详细介绍

    大家好,本篇文章主要讲的是C语言之循环语句详细介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • C++数据结构之搜索二叉树的实现

    C++数据结构之搜索二叉树的实现

    了解搜索二叉树是为了STL中的map和set做铺垫,我们所熟知的AVL树和平衡搜索二叉树也需要搜索二叉树的基础。本文将详解如何利用C++实现搜索二叉树,需要的可以参考一下
    2022-05-05
  • 带你用C语言实现strtok和字符串分割函数

    带你用C语言实现strtok和字符串分割函数

    下面小编就为大家带来一篇c语言中字符串分割函数及实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-09-09

最新评论