笔记——H264移植中从DDR读出的YUV数据处理
待处理的数据
图片中的数据是 720P YUV420YV12格式下,解码端在DDR存储的数据。这是第一个 BLOCK的数据(16*16像素),所以要有 Y值16*16个 ,UV 各有8*8个(由YUV44行列均隔行抽样得到YUV420格式)。如图中示意,16行Y,8行V,8行U。
整个decoder_data_720p.txt中的数据是按 BLOCK 存储的,而且是按列遍历整张照片。一张720p(1280*720像素)照片的BLOCK示意图,即先存满BLOCK1,1~45,到第二行的BLOCK2,1~45。在解析时要注意存储顺序。
问题来了:本来每个像素值应该是8bit,刚好2个十六进制数,但是由于FPGA系统内部的处理,每个值占了10bits 。因此如下的数据是需要解析bit的。
图1 从DDR导出的待处理数据
如何解析出数据呢?
以第一行数据为例:300c0300c0300c0300c0300c0300c0300c0300c0,16个Y值,有16*10 bits(高8位有效,补2个0) = 20 Byte 数据。
假设给第一行数据按字节编号:B0B1B2...B19。可以看出每5个字节呈现出一定的规律,阴影表示补的0。20Byte/5 = 4次循环。
从单行来看像素Y值,
Y0 = B0,
Y1高4位数据 = (B1>>2)<<4,Y1低4位数据 = (B1&0x03) + (B2>>6)&0x03 (这里要注意右移时最高位是否市符号位拓展,保险起见 &0X03), Y1=Y1高4位数据 +Y1低4位数据。
Y2 = B2<<4 + B3>>4
Y3 = (B3&0x03)<<6 + (B4>>2)
...
这样循环4次就能遍历一行,取出16个Y值。
其他UV等数据类似取出。
看看如何取出1个BLOCK?
读取数据
posy = 1;posv = 2;posu = 3; %根据数据来定义的顺序
inblock = zeros(16,20,3); inblock(:,:,posy) = fscanf(fid_txtin,'%02x',[20 16])'; tempuv = fscanf(fid_txtin,'%02x',[20 8])';
取出V,前10列数据是V,然后补满 inblock 其他位置,方便处理。
inblock(1:8,1:10,posv) = tempuv(1:8,1:10); inblock(1:8,11:20,posv) = tempuv(1:8,1:10); inblock(9:16,:,posv) = inblock(1:8,:,posv);
取出U,后10列数据是U,然后补满 inblock 其他位置,方便处理。
inblock(1:8,1:10,posu) = tempuv(1:8,11:20); inblock(1:8,11:20,posu) = inblock(1:8,1:10,posu); inblock(9:16,:,posu) = inblock(1:8,:,posu); inblock = uint8(inblock);
这样YUV的数据按相应位置存储到数组 inblock里面了。维度 16x20。
解析inblock的数据 存入到 imgblock,分别解析 YUV数据。列遍历时(每次循环5个字节),解析出4个YUV值。解释见上面的字节图形说明。
imgblock = zeros(16,16,3); for yuv=1:3 for row=1:16 incol = 1; for column=1:4:16 % imgblock(row,column+0,yuv) = inblock(row,incol+0,yuv); % tempH = bitshift(inblock(row,incol+1,yuv),-2); tempH = bitshift(tempH,4); tempL = bitand(inblock(row,incol+1,yuv),3); tempL = bitshift(tempL,2); tempL = tempL + bitshift(inblock(row,incol+2,yuv),-6); temp = tempH+tempL; imgblock(row,column+1,yuv) = temp; % tempH = bitshift(inblock(row,incol+2,yuv),4); tempL = bitshift(inblock(row,incol+3,yuv),-4); temp = tempH+tempL; imgblock(row,column+2,yuv) = temp; % tempH = bitand(inblock(row,incol+3,yuv),3); tempH = bitshift(tempH,6); tempL = bitshift(inblock(row,incol+4,yuv),-2); temp = tempH + tempL; imgblock(row,column+3,yuv) = temp; incol = incol+5; end end end
取出解析后的一个BLOCK YUV数据,fliplr 用于将矩阵对折,这个是和李师兄对数据时,发现的其输出的数据格式问题。
tempy = imgblock(:,:,1); tempu = imgblock(1:8,1:8,posu); tempv = imgblock(1:8,1:8,posv); tempy = fliplr(tempy); tempu = fliplr(tempu); tempv = fliplr(tempv);
如: tempv值如下,
对折后:
存下每 16行中所有列的BLOCK数据
Y = [Y tempy];
U = [U tempu];
V = [V tempv];
对这16行的BLOCK数据整理,以保证与输出数据的格式对应。 Y0,Y1,Y2...
Y = uint8(Y)'; Y = reshape(Y,1,16*16*Vs/16); U = uint8(U)'; U = reshape(U,1,16*16*Vs/16/4); V = uint8(V)'; V = reshape(V,1,16*16*Vs/16/4);
存下所有行的数据
YY = [YY Y]; UU = [UU U]; VV = [VV V];
处理数据的Matlab完整代码
% % 2016-01-18 hy % 1.读取文本中的第一个BLOCK,数据为4:2:0的YUV数据 % 2.从文本中取出Y,U,V的数据,然后输出到 *.yuv文件, % 用软件 “7yuv”打开,调整格式为 YVU420 planar YV12 可正常显示 % 3.运行程序时,注意修改输入、输出数据的文件名。 % 4.本程序读入的是一张完整的720p的图片数据,要用于1080P需要略作修改 % % 这个420的转换显示脚本先修改图片尺寸Vs*Hs (720P或1080P), % 然后直接运行,点击选择输入文件(不必拷贝到当前目录下),即可等待运行结果 clc;close all;clear %% % rgb2ycbcr posy = 1;posv = 2;posu = 3; %根据数据来定义的顺序 %%通过 GUI 选择 *.txt 输入文件 [FileName, PathName, FilterIndex] = uigetfile('*.txt'); filename = [PathName,FileName] fid_txtin = fopen(filename,'r'); % %86400/24 = 3600 %% %!!!! %%请更改图像对应的尺寸Vs*Hs FRAME_NUM = 1 %%帧数, txt文件中有几帧yuv数据 % Hs = 96; Vs = 128; %%128*96 Hs = 720; Vs = 1280; %%720p % Hs = 1080;Vs = 1920; %%1080p Hs = FRAME_NUM*Hs Vs = FRAME_NUM*Vs YY = []; UU=[]; VV=[]; for rcount = 1:Hs/16 %45*16 = 720 如果是1080P的照片请修改45为 1080/16=67 Y = []; U=[]; V=[]; for colcount = 1:Vs/16 %80*16=1280 如果是1080P的照片请修改80为 1920/16=120 inblock = zeros(16,20,3); inblock(:,:,posy) = fscanf(fid_txtin,'%02x',[20 16])'; tempuv = fscanf(fid_txtin,'%02x',[20 8])'; %inblock(1:8,:,posv) = fscanf(fid_txtin,'%2x',[8 20]); inblock(1:8,1:10,posv) = tempuv(1:8,1:10); inblock(1:8,11:20,posv) = tempuv(1:8,1:10); inblock(9:16,:,posv) = inblock(1:8,:,posv); inblock(1:8,1:10,posu) = tempuv(1:8,11:20); inblock(1:8,11:20,posu) = inblock(1:8,1:10,posu); inblock(9:16,:,posu) = inblock(1:8,:,posu); inblock = uint8(inblock); % Y = inblock(:,:,posy); % U = inblock(:,:,posu); % V = inblock(:,:,posv); %% imgblock = zeros(16,16,3); for yuv=1:3 for row=1:16 incol = 1; for column=1:4:16 % imgblock(row,column+0,yuv) = inblock(row,incol+0,yuv); % tempH = bitshift(inblock(row,incol+1,yuv),-2); tempH = bitshift(tempH,4); tempL = bitand(inblock(row,incol+1,yuv),3); tempL = bitshift(tempL,2); tempL = tempL + bitshift(inblock(row,incol+2,yuv),-6); temp = tempH+tempL; imgblock(row,column+1,yuv) = temp; % tempH = bitshift(inblock(row,incol+2,yuv),4); tempL = bitshift(inblock(row,incol+3,yuv),-4); temp = tempH+tempL; imgblock(row,column+2,yuv) = temp; % tempH = bitand(inblock(row,incol+3,yuv),3); tempH = bitshift(tempH,6); tempL = bitshift(inblock(row,incol+4,yuv),-2); temp = tempH + tempL; imgblock(row,column+3,yuv) = temp; incol = incol+5; end end end tempy = imgblock(:,:,1); tempu = imgblock(1:8,1:8,posu); tempv = imgblock(1:8,1:8,posv); tempy = fliplr(tempy); tempu = fliplr(tempu); tempv = fliplr(tempv); Y = [Y tempy]; U = [U tempu]; V = [V tempv]; %tempy = uint8(imgblock(:,:,1))'; %tempy = reshape(tempy,1,16*16); %tempu = uint8(imgblock(:,:,2))'; %tempu = reshape(tempu,1,16*16); %tempv = uint8(imgblock(:,:,3))'; %tempv = reshape(tempv,1,16*16); end Y = uint8(Y)'; Y = reshape(Y,1,16*16*Vs/16); U = uint8(U)'; U = reshape(U,1,16*16*Vs/16/4); V = uint8(V)'; V = reshape(V,1,16*16*Vs/16/4); YY = [YY Y]; UU = [UU U]; VV = [VV V]; end fclose(fid_txtin); % % imgYUV = uint8(cat(3,Y,U,V)); % % imgRGB = ycbcr2rgb(imgYUV); % % imshow(imgRGB(1:16,:)); %% % 输出数据的文件名 % datestr(now) 获取时间函数 % filename_out = ['yuvout_',datestr(now,30),'.yuv'] % fid_txtout = fopen(filename_out,'wb'); fwrite(fid_txtout,YY,'uint8'); fwrite(fid_txtout,VV,'uint8'); fwrite(fid_txtout,UU,'uint8'); fclose(fid_txtout); filename_buff = 'yuvout_dispbuff.yuv' cmd_str = ['copy ',filename_out,' ',filename_buff] dos(cmd_str) % yuvpath = [pwd,'\\',filename_buff] cmd_str = ['7yuv.exe ',filename_buff] %%添加7yuv 路径工具到用户环境变量 dos(cmd_str)
% % 2016-01-18 % 1.读取文本中的第一个BLOCK,数据为4:2:0的YUV数据 % 2.从文本中取出Y,U,V的数据,然后输出到 *.yuv文件, % 用软件 “7yuv”打开,调整显示格式为 YVU420 planar YV12 可正常显示 % 3.运行程序时,注意修改输入、输出数据的文件名。 % 4.本程序读入的是一张完整的720p的图片数据,要用于1080P需要略作修改 % % clc;close all;clear %% % rgb2ycbcr posy = 1;posv = 2;posu = 3; %根据数据来定义的顺序 % % 输入数据的文件名 filename = 'huyong.txt'; fid_txtin = fopen(filename,'r'); % %86400/24 = 3600 Hs = 720; Vs = 1280; %%720p % Hs = 1080;Vs = 1920; %%1080p YY = []; UU=[]; VV=[]; for rcount = 1:Hs/16 %45*16 = 720 如果是1080P的照片请修改45为 1080/16=67 Y = []; U=[]; V=[]; for colcount = 1:Vs/16 %80*16=1280 如果是1080P的照片请修改80为 1920/16=120 inblock = zeros(16,20,3); inblock(:,:,posy) = fscanf(fid_txtin,'%02x',[20 16])'; tempuv = fscanf(fid_txtin,'%02x',[20 8])'; %inblock(1:8,:,posv) = fscanf(fid_txtin,'%2x',[8 20]); inblock(1:8,1:10,posu) = tempuv(1:8,1:10); inblock(1:8,11:20,posu) = tempuv(1:8,1:10); inblock(9:16,:,posu) = inblock(1:8,:,posu); inblock(1:8,1:10,posv) = tempuv(1:8,11:20); inblock(1:8,11:20,posv) = inblock(1:8,1:10,posv) ; inblock(9:16,:,posv) = inblock(1:8,:,posv); inblock = uint8(inblock); % Y = inblock(:,:,posy); % U = inblock(:,:,posu); % V = inblock(:,:,posv); %% imgblock = zeros(16,16,3); for yuv=1:3 for row=1:16 incol = 1; for column=1:4:16 % imgblock(row,column+0,yuv) = inblock(row,incol+0,yuv); % tempH = bitshift(inblock(row,incol+1,yuv),-2); tempH = bitshift(tempH,4); tempL = bitand(inblock(row,incol+1,yuv),3); tempL = bitshift(tempL,2); tempL = tempL + bitshift(inblock(row,incol+2,yuv),-6); temp = tempH+tempL; imgblock(row,column+1,yuv) = temp; % tempH = bitshift(inblock(row,incol+2,yuv),4); tempL = bitshift(inblock(row,incol+3,yuv),-4); temp = tempH+tempL; imgblock(row,column+2,yuv) = temp; % tempH = bitand(inblock(row,incol+3,yuv),3); tempH = bitshift(tempH,6); tempL = bitshift(inblock(row,incol+4,yuv),-2); temp = tempH + tempL; imgblock(row,column+3,yuv) = temp; incol = incol+5; end end end tempy = imgblock(:,:,1); tempu = imgblock(1:8,1:8,2); tempv = imgblock(1:8,1:8,3); tempy = fliplr(tempy); tempu = fliplr(tempu); tempv = fliplr(tempv); Y = [Y tempy]; U = [U tempu]; V = [V tempv]; %tempy = uint8(imgblock(:,:,1))'; %tempy = reshape(tempy,1,16*16); %tempu = uint8(imgblock(:,:,2))'; %tempu = reshape(tempu,1,16*16); %tempv = uint8(imgblock(:,:,3))'; %tempv = reshape(tempv,1,16*16); end Y = uint8(Y)'; Y = reshape(Y,1,16*16*Vs/16); U = uint8(U)'; U = reshape(U,1,16*16*Vs/16/4); V = uint8(V)'; V = reshape(V,1,16*16*Vs/16/4); YY = [YY Y]; UU = [UU U]; VV = [VV V]; end fclose(fid_txtin); % imgYUV = uint8(cat(3,Y,U,V)); % imgRGB = ycbcr2rgb(imgYUV); % imshow(imgRGB(1:16,:)); %% %输出数据的文件名 fid_txtout = fopen('huyong_out_24.yuv','wb'); fwrite(fid_txtout,YY,'uint8'); fwrite(fid_txtout,VV,'uint8'); fwrite(fid_txtout,UU,'uint8'); fclose(fi
-
7yuv的显示
图2 用7yuv打开输出的 *.yuv文件