很遺憾第一次實戰並沒有達到我想要的效果,想要達到的目的是把一個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= =。暫時無法解決這個問題,不過分割的思想已經有了 == 希望有用
額,剛剛修改了一下,可以使用了,不要問我修改了哪裏,都是微不足道的地方,太不認真了,需要的可以直接用啦