很遗憾第一次实战并没有达到我想要的效果,想要达到的目的是把一个n帧的3840*1920的YUV420p的图像均分成64份。
首先来看一下YUV4:2:0格式的存储格式,首先可以参照大神写的http://blog.csdn.net/lin453701006/article/details/53053185这篇博文了解一下YUV格式。
简单来讲,假设有个4*4的像素点,对于420P而言,不妨假设这四个像素点Y值都为1,U值为2,V值为3
那么YUV的实际存储方式为
1111
1111
1111
1111
22
22
33
33
也就是一个4*4的矩阵加两个长宽为这个矩阵一半的矩阵。
下面就是实际的代码:
首先是主函数,其中while(1)和readsize控制按帧读取,读取到3840*1920的一帧后,要把它们分成64个小文件输出,因此设置了ij表示第i行第j列的第几个小块。其中filename函数可以根据ij设置输出的文件名。YUVslice函数的功能是将图像的64分之一放到output_buff中。
#include "YUV.h"
#define _CRT_SECURE_NO_WARNINGS
#define IMAGEWIDTH 3840 //图像的宽
#define IMAGEHEIGHT 1920 //高
#define NUM 64
//#define Y_SIZE (IMAGEWIDTH*IMAGEHEIGHT)
#define YUV420_SIZE (Y_SIZE*3/2) //4:2:0格式
int main()
{
FILE * input_yuvfile; //输入YUV420文件的指针
if (NULL == (input_yuvfile = fopen("Driving_in_Country_3840x1920_388p.yuv", "rb")))
{
printf("File input is can't open!\n");
return -1;
}
int readsize;
unsigned char *input_buff;
input_buff = (unsigned char *)malloc(YUV420_SIZE * sizeof(unsigned char));
while (1)
{
readsize = fread(input_buff, 1, YUV420_SIZE , input_yuvfile);
if (readsize<YUV420_SIZE) //读取的数据量少于YUV420_SIZE时跳出
break;
FILE *fq = NULL;
for (int i = 0; i <= 7; i++)//i代表分成8*8之后的第几行
{
for (int j = 0; j <= 7; j++)//j代表分成8*8之后的
{
unsigned char *output_buff;
output_buff = (unsigned char *)malloc(YUV420_SIZE * sizeof(unsigned char)/64);
char output_yuvfile[15] = { 0 };
filename(i, j, output_yuvfile);//设置输出文件名
YUVslice(i, j, input_buff, output_buff, IMAGEWIDTH, IMAGEHEIGHT);//将原图的64分之一输入到output_buff中
fq = fopen(output_yuvfile, "a+");// 这里打开写入文件,注意使用的是a模式
fwrite(output_buff, 1, YUV420_SIZE/64, fq);
free(output_buff);
output_buff = NULL;
fclose(fq);
}
}
}
free(input_buff);
input_buff = NULL;
fclose(input_yuvfile);
return 0;
}
下面是函数的具体形式:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<malloc.h>
#include<memory.h>
#include <math.h>
#define _CRT_SECURE_NO_WARNINGS
#define IMAGEWIDTH 3840 //图像的宽
#define IMAGEHEIGHT 1920 //高
#define NUM 64
#define Y_SIZE ( IMAGEWIDTH*IMAGEHEIGHT)
#define YUV420_SIZE (Y_SIZE*3/2) //4:2:0格式
char filename(int i, int j, char *output_yuvfile)
{
//unsigned char frame_num[3];//标示输出的是第几帧
char output_yuvfile_i[3];//表示第几行的块
char output_yuvfile_j[3];//表示第几列的块
memset(output_yuvfile_i, 0, sizeof(output_yuvfile_i));
memset(output_yuvfile_j, 0, sizeof(output_yuvfile_j));
_itoa(i, output_yuvfile_i, 10);
_itoa(j, output_yuvfile_j, 10);
char outputfile_tail[5] = ".YUV";
strcat(output_yuvfile, output_yuvfile_i);
strcat(output_yuvfile, output_yuvfile_j);
strcat(output_yuvfile, outputfile_tail);//得到要输出的文件名
return 0;
}
int YUVslice(int i,int j,unsigned char* input_buff,unsigned char* output_buff,int PIC_W,int PIC_H)
{
unsigned char *y_buf = NULL;
y_buf = (unsigned char *)malloc(YUV420_SIZE * sizeof(unsigned char) / NUM);//y_buff是个大小为原图64分之一的数组
if (y_buf == NULL)
{
printf("Error: malloc buf.\n");
exit(1);
}
int h, v;
//Y分量切割;
for (v = 0 + i*PIC_H / 8; v<PIC_H / 8 + i*PIC_H / 8; v++)
{
for (h = 0 + j*PIC_W / 8; h<PIC_W / 8 + j*PIC_W / 8; h++)
{
y_buf[(v - i*PIC_H / 8)*(PIC_W / 8) + h-j*PIC_W / 8] = input_buff[(v*PIC_W + h)];//每次调用YUVslice函数就将原图第v行第第h列的Y值拷贝到y_buff数组的前PIC_H / sqrt(NUM)*PIC_W / sqrt(NUM)个值中。
}
}
for (v = 0 + i*PIC_H / 16; v<PIC_H / 16 + i*PIC_H / 16; v++)
{
for (h = 0 + j*PIC_W / 16; h<PIC_W / 16 + j*PIC_W / 16; h++)//这里原理和Y相同,但是其长宽减半,且将uv的值拷贝到y_buff的(v - i*PIC_H / 16)*(PIC_W / 16) + h+偏移的位置
{
y_buf[Y_SIZE / 64 + (v - i*PIC_H / 16)*(PIC_W / 16) + h- i*PIC_H / 16] = input_buff[Y_SIZE + (v*PIC_W / 2 + h)];//U
y_buf[Y_SIZE * 5 / 256 + (v - i*PIC_H / 16)*(PIC_W / 16) + h- i*PIC_H / 16] = input_buff[Y_SIZE * 5 / 4 + (v*PIC_W / 2 + h)];//V
}
}
memcpy(output_buff, y_buf, YUV420_SIZE / 64);
free(y_buf);
return 1;
}
为什么说是一个失败的实战呢,是因为它可以输出第一帧的左上角的64分之一图像,但是无法输出左上角第二个64分之一的图像。使用断点调试,发现在运行到i=0,j=1时,在YUVslice函数中input_buff的值没有复制到y_buff中,而且,在运行到free (y_buff)时会弹出捕捉到一个break= =。暂时无法解决这个问题,不过分割的思想已经有了 == 希望有用
额,刚刚修改了一下,可以使用了,不要问我修改了哪里,都是微不足道的地方,太不认真了,需要的可以直接用啦