HEVC的四叉树块分割算法的核心是确定CU/PU/TU的最优分割模式。最优分割模式算法的代码块,即xCompressCu,网上有对此比较详细的阐释,个人的学习也是参考这些,这里不再赘述。
本文主要目的是画出某B帧的NonSquare PU分割模式图,即包括2NxN,Nx2N,以及四种AMP分割。HM编码器参数配置采用Random_access,对BasketballDrill序列,这里参考http://blog.csdn.net/hevc_cjl/article/details/8169182
在TEncCu中调用完xCompressCu后加入如下代码,将最终的PU分割模式导出至PUpartion文件中:
ofstream PupartionInfo;
TComDataCU* Pupartion= m_ppcBestCU[0];
PupartionInfo.open("PuPartion.txt", ios::app);
int iCount = 0;
int iWidthInPart = g_uiMaxCUWidth >> 2;
for(UInt iPartitionNum = 0; iPartitionNum < Pupartion->getTotalNumPart(); iPartitionNum++)
{
if ( (iCount & (iWidthInPart - 1)) == 0)
PupartionInfo << "\n";//每16个数据换行
iCount++;
PupartionInfo << Pupartion-> getPartitionSize(g_auiRasterToZscan[iPartitionNum]) << " ";//以4x4块为基本单位,导出PU分割模式信息,光栅扫描转换为Z扫描
}
PupartionInfo.close();
得到的文件数据如图
接下来在matlab中,利用上面输出的数据,画出PU分割模式图,定义函数:
function PU_View(YUV_FileName, Pu_Partion, Width, Height, NumFrame)
clc;
%% 参数设置
LCU_Size = 64;
MaxNumPartition = 256;
LCU_Width = 16;
LCU_Height = 16;
MinOperSize = 4;
NumLCU_Row = floor( (Width + LCU_Size-1) / LCU_Size ); % 行有多少LCU,不满64的也算一个
NumLCU_Col = floor( (Height+ LCU_Size-1) / LCU_Size ); % 列有多少LCU,不满64的也算一个
Fid_YUV = fopen(YUV_FileName,'rb');
Fid_Pu = fopen(Pu_Partion);
for uiFrame = 1 : NumFrame
%% 从buffer中读取数据
ImgData = fread(Fid_YUV, [Width, Height], 'uint8'); % image data Y
ImgData_U = fread(Fid_YUV ,[Width/2, Height/2], 'uint8'); % image data U
ImgData_V = fread(Fid_YUV ,[Width/2, Height/2], 'uint8'); % image data V
ImgData = ImgData';
PUData = fscanf(Fid_Pu,'%d',[NumLCU_Row * NumLCU_Col * MaxNumPartition,1]);%以十进制读入数据,保存在PUData,PUData为NumLCU_Row * NumLCU_Col * MaxNumPartition行1列的矩阵
%%画出LCU分界线
figure ; imshow(ImgData,[]);
for i = 1 : Width/LCU_Size
line([i*LCU_Size, i*LCU_Size], [1 Height],'Color', 'r','LineWidth',2) %划纵向线
end
for i = 1 : Height/LCU_Size
line([1 Width], [i*LCU_Size, i*LCU_Size],'Color', 'r','LineWidth',2)%划横向线
end
for i = 1 : NumLCU_Col %纵向扫描,表示第几行
for j = 1 : NumLCU_Row %横向扫描,表示第几列
Idx = (i-1) * NumLCU_Row + j;%获取当前LCU的ID
uiPelX = (j-1) * LCU_Size + 1;%获取当前LCU左上角第一个像素的横坐标
uiPelY = (i-1) * LCU_Size + 1;%获取当前LCU左上角第一个像素的纵坐标
PUData_Blk = PUData( (Idx-1)*MaxNumPartition + 1 : (Idx)*MaxNumPartition);%从存储PU分割尺寸信息的一维PUData文件中获取当期LCU的PU分割尺寸信息
uiPelXscan1 = uiPelX;
uiPelYscan1 = uiPelY;
uiPelXscan2 = uiPelX;
uiPelYscan2 = uiPelY;
for k=1 : LCU_Height %第k行
for l=1 : LCU_Width %第k行第l个元素
if(PUData_Blk(16*(k-1)+l,1)~=0 && PUData_Blk(16*(k-1)+l,1)~=3 && PUData_Blk(16*(k-1)+l,1)~=8 && (uiPelXscan1 ~= uiPelX + LCU_Size - 1) && (uiPelXscan1 ~= Width))%寻找非对称分割PU
uiPelXscan1 = uiPelX + (l-1) * MinOperSize;%获取NonSquare PU分割块左上角第一个像素点坐标
uiPelYscan1 = uiPelY + (k-1) * MinOperSize;
n=1;
for m = l+1:l+15
if(m>16)
break;
end
if(PUData_Blk(16*(k-1)+m,1) == PUData_Blk(16*(k-1)+l,1))
uiPelXscan2 = uiPelXscan1 + n * MinOperSize + 3;%获取当前NonSquare PU分割模式的块右上角像素的坐标
uiPelYscan2 = uiPelYscan1;
n=n+1;%n用于标识PU大小
else
break;
end
end
line([uiPelXscan1 uiPelXscan2], [uiPelYscan1 uiPelYscan2 ],'Color', 'g','LineWidth',1);
line([uiPelXscan1 uiPelXscan2], [uiPelYscan1 + n * MinOperSize uiPelYscan1 + n * MinOperSize ],'Color', 'g','LineWidth',1);
line([uiPelXscan1 uiPelXscan1], [uiPelYscan1 uiPelYscan1 + n * MinOperSize],'Color', 'g', 'LineWidth',1);
line([uiPelXscan2 uiPelXscan2], [uiPelYscan1 uiPelYscan1 + n * MinOperSize],'Color', 'g', 'LineWidth',1);%PU轮廓线
axis on;
switch PUData_Blk(16*(k-1)+l,1)
case 1 %2NxN
line([uiPelXscan1 uiPelXscan2], [uiPelYscan1 + n/2 * MinOperSize uiPelYscan1 + n/2 * MinOperSize ],'Color', 'g','LineWidth',1); %画2NxN中间分割线
case 2
line([uiPelXscan1 + n/2 * MinOperSize uiPelXscan1 + n/2 * MinOperSize], [uiPelYscan1 uiPelYscan1 + n * MinOperSize],'Color', 'g','LineWidth',1);
case 4
line([uiPelXscan1 uiPelXscan2], [uiPelYscan1 + n/4 * MinOperSize uiPelYscan1 + n/4 * MinOperSize ],'Color', 'g','LineWidth',1);
case 5
line([uiPelXscan1 uiPelXscan2], [uiPelYscan1 + n * 3/4 * MinOperSize uiPelYscan1 + n * 3/4 * MinOperSize ],'Color', 'g','LineWidth',1);
case 6
line([uiPelXscan1 + n/4 * MinOperSize uiPelXscan1 + n/4 * MinOperSize], [uiPelYscan1 uiPelYscan1 + n * MinOperSize],'Color', 'g','LineWidth',1);
case 7
line([uiPelXscan1 + n * 3/4 * MinOperSize uiPelXscan1 + n * 3/4 * MinOperSize], [uiPelYscan1 uiPelYscan1 + n * MinOperSize],'Color', 'g','LineWidth',1);
otherwise
break;
end
for m = k:k+n-1
for p = l:l+n-1
PUData_Blk(16*(m-1)+p,1)=0;%当前PU分割数据清零,防止多次遍历同一个PU
end
end
l=l+n;
end
end
end
end
end
end
fclose(Fid_YUV);
fclose(Fid_Pu);
编译通过后,在命令窗口输入 PU_View(‘….YUV文件路径BasketballDrill_832x480_50.yuv’,’…..Pupartion文件路径Win32\Debug\PuPartion.txt’,832,480,5),5代表帧数
得到一帧最终PU分割模式图: