【论文#码率控制】ADAPTIVE RATE CONTROL FOR H.264

目录

  • 摘要
  • 1.前言
  • 2.基本知识
  • 2.1 蛋鸡悖论
    • 2.2 基本单元的定义
    • 2.3 线性MAD预测模型
  • 3.GOP级码率控制
    • 3.1 总比特数
    • 3.2 初始化量化参数
  • 4.帧级码率控制
    • 4.1 非存储图像的量化参数
    • 4.2 存储图像的目标比特
  • 5.基本单元级码率控制
  • 6.实验结果
  • 7.结论

《ADAPTIVE RATE CONTROL FOR H.264》
Author: Z.G. Li; F. Pan; K.P. Lim; X. Lin; S. Rahardja
Published in: 2004 International Conference on Image Processing, 2004. ICIP '04.
Date of Conference: 24-27 October 2004
Date Added to IEEE Xplore: 18 April 2005
Print ISBN:0-7803-8554-3
Print ISSN: 1522-4880
DOI: 10.1109/ICIP.2004.1419405
Publisher: IEEE
Conference Location: Singapore

摘要

本文提出了一种针对H.264的码率控制方案,通过引入基本单元和线性预测模型的概念。基本单元可以是宏块(MB)、切片或帧。它可以用于在整体编码效率和比特波动之间取得平衡。线性模型用于解决H.264码率控制中存在的“先有鸡还是先有蛋”的困境。本文研究了恒定比特率(CBR)和可变比特率(VBR)两种情况。我们的方案已被H.264采纳

1.前言

码率控制用于根据指定的比特率和当前帧的统计信息(如每个宏块(MB)的MAD和头部比特数)来计算当前帧的量化参数。H.264的码率控制比MPEG-2和TMN 8更复杂,因为在MPEG-2和TMN 8中,当前帧的统计信息是可用的,而在H.264的码率控制中则不可用[1, 2]。这是因为在H.264中,量化参数不仅涉及码率控制,还涉及率失真优化(RDO),而在MPEG-2、MPEG-4和H.263中,量化参数仅涉及码率控制。当在H.264中实现码率控制时,存在一个典型的“蛋鸡悖论”的困境:为了对一个宏块执行RDO,首先需要使用当前帧的统计信息来确定该宏块的量化参数。然而,当前帧的统计信息只有在执行RDO之后才可用。

一种直观的方法是通过前一帧中对应位置的宏块(MB)的统计信息来预测当前帧中每个宏块的统计信息。然而,这种方法在宏块级别存在一些问题。例如,假设当前帧中的一个宏块是I宏块(Intra-MB),而前一帧中对应位置的宏块是P宏块(Inter-MB)。为了解决这个问题,我们引入了“基本单元”的概念,基本单元可以是宏块(MB)、切片或帧。同一基本单元中的所有宏块共享一个共同的量化参数。基本单元的选择取决于编码图片缓冲区(CPB)的大小。然后,通过引入线性模型来预测当前存储图片中剩余基本单元的MAD值,利用前一存储图片中对应位置的基本单元的实际MAD值,提出了一个自适应基本单元层的码率控制方案,用于H.264。这解决了上述的“先有鸡还是先有蛋”的困境。通过线性模型和基本单元的概念,我们的方案详细描述如下:利用流体流量模型和线性跟踪理论,结合帧率、当前缓冲区占用、目标缓冲区水平和可用信道带宽,计算当前帧的目标比特数。目标比特数进一步由考虑假设参考解码器(HRD)得出的两个值进行限制。当前帧的剩余比特数根据预测的MAD值分配给剩余的基本单元。使用二次率失真(R-D)模型计算相应的量化参数,然后用于对当前基本单元中的每个宏块执行RDO。与使用固定量化参数的H.264编码器相比,我们的方案可以将平均PSNR提高多达0.78dB。在正常测试条件下,对于所有H.264测试序列,改进的平均PSNR为0.43dB。我们的方案已被H.264采纳。

PS:
(1) 蛋鸡悖论问题,为了执行RDO,需要统计信息确定量化参数,但统计信息只有在执行一遍RDO之后才可用
(2) 基本单元可以是宏块,slice或者是frame,同一个基本单元中的所有宏块使用同一个量化参数
(3) 引入线性模型来预测当前图片中剩余基本单元的MAD值,依据是前一帧图片对应位置的基本单元实际MAD值
(4) 目标比特数由HRD限制,当前帧剩余比特数由预测MAD值分配给剩余的基本单元
(5) 相对于CQP模式,性能增益为0.78dB;相对于常规环境,增益为0.43dB

2.基本知识

2.1 蛋鸡悖论

H.264码率控制相关的宏块(MB)编码过程如下[31]:当前帧的统计信息 → 码率控制 → 量化参数 → RDO → 当前帧的统计信息 → 熵编码。

显然,在实施码率控制时存在一个“先有鸡还是先有蛋”的困境。正因为如此,H.264的码率控制比TM 5、Q2和TMN 8更困难。为了研究H.264的码率控制,我们需要找到一种方法来估计当前帧的统计信息。除此之外,我们还需要确定共享一个量化参数的连续宏块的数量。

2.2 基本单元的定义

假设一帧由 N m b p i c N_{mbpic} Nmbpic 个宏块(MBs)组成。定义一个基本单元为一组连续的宏块,其中包含 N m b u n i t N_{mbunit} Nmbunit个宏块, N m b u n i t N_{mbunit} Nmbunit N m b p i c N_{mbpic} Nmbpic的一个部分[14]。用 N u n i t N_{unit} Nunit表示一帧中基本单元的总数,计算公式为
N u n i t s = N m b p i c / N m b u n i t N_{units}=N_{mbpic}/N_{mbunit} Nunits=Nmbpic/Nmbunit
PS:
(1) 类似于HEVC标准中量化组(quantization group,QG)
(2) 这里描述的基本单元对码率控制的功能,类似于x264中的行级码控,但x264中每个mb的qp可以不同,使用每一行的平均qp来控制

2.3 线性MAD预测模型

假设第j幅图像是一个存储的图像,且两个存储图像之间连续的非存储图像的数量为L。提出了以下线性模型(1)来解决“先有鸡还是先有蛋”的困境[14]。

σ ~ l , i ( j ) = a 1 ∗ σ l , i ( j − L + 1 ) + a 2 ( 1 ) \tilde{\sigma}_{l, i}(j)=a_1 * \sigma_{l, i}(j - L + 1) + a_2 (1) σ~l,i(j)=a1σl,i(jL+1)+a2(1)

其中 σ ~ l , i ( j ) \tilde{\sigma}_{l, i}(j) σ~l,i(j) 是第i个GOP中当前存储图像的第l个基本单元的预测MAD值。 σ l , i ( j − L + 1 ) \sigma_{l, i}(j - L + 1) σl,i(jL+1)是前一个存储图像的第i个基本单元的实际MAD值。 a 1 a_1 a1 a 2 a_2 a2是预测模型的两个系数。初始值 a 1 a_1 a1设为1, a 2 a_2 a2设为0。它们在编码每个基本单元后进行更新。

注释 1: 应该指出, σ ~ l , i ( j ) \tilde{\sigma}_{l, i}(j) σ~l,i(j) 是预测的参考值。参考值有许多其他选择。例如,可以在执行RDO之前,对当前图像中的所有宏块(MBs)进行16×16基于的运动估计和运动补偿。我们也可以使用前一帧中最可能的模式进行运动估计和补偿,以获得粗略的信息。所得的MAD也可以用作参考值。为了简化,我们选择\sigma_{l, i}(j - L + 1) 作为参考值。头部比特数也可以通过这种方式获得。同样,预测模型也有许多种,为了简化,我们选择了线性模型。

PS:
(1) 存储图像表示Intra帧、P帧这样会被存储在内存中,用于参考的帧;而B帧这样不会被用于参考的帧称之为非存储图像
(2) 将基本单元假设为mb易于理解,当前mb的MAD值与参考mb的MAD值用线性关系来描述

3.GOP级码率控制

在这个层级,计算分配给每个GOP的总比特数,并设置每个GOP的初始量化参数。

3.1 总比特数

分配给第i个GOP的初始比特数(第一个帧,即IDR帧)按如下方式计算
B i ( 1 ) = R i ( 1 ) f N i + B i − 1 ( N i − 1 ) ( 2 ) B_{i}(1)=\frac{R_{i}(1)}{f}N_{i} + B_{i-1}(N_{i-1}) (2) Bi(1)=fRi(1)Ni+Bi1(Ni1)(2)
其中 R i ( j ) R_{i}(j) Ri(j) 是可用的信道带宽,可以是恒定的或时变的, N i − 1 N_{i-1} Ni1表示第(i−1)个GOP中的总帧数,f 是预定义的帧率,由于信道带宽可能随时改变, B i B_{i} Bi的更新方式为
B i ( j ) = B i ( j − 1 ) − b i ( j − 1 ) + R i ( j ) − R i ( j − 1 ) f ( N i − j + 1 ) ; j = 2 , 3 , . . . , N i B_i(j)=B_{i}(j-1)-b_{i}(j-1)+\frac{R_{i}(j)-R_{i}(j-1)}{f}(N_{i}-j+1); j=2,3,...,N_{i} Bi(j)=Bi(j1)bi(j1)+fRi(j)Ri(j1)(Nij+1);j=2,3,...,Ni
其中, b i ( j − 1 ) b_i(j-1) bi(j1)表示第i个GOP当中的第j帧实际编码比特

PS:
(1) 针对于公式(2),假设i=1,即第一个GOP,有 B 1 ( 1 ) = R 1 ( 1 ) f N 1 B_{1}(1)=\frac{R_{1}(1)}{f}N_{1} B1(1)=fR1(1)N1,其中 R 1 ( 1 ) f \frac{R_{1}(1)}{f} fR1(1)表示单帧消耗比特数, N 1 N_{1} N1表示GOP一共有多少帧,二者的乘积表示GOP1的初始比特数。因此, B 1 ( 1 ) B_{1}(1) B1(1)表示GOP1总共需要分配多少比特
(2) 针对于 B i B_{i} Bi的更新方式,假设i=1,j=2,即GOP1的第二帧,有 B 1 ( 2 ) = B 1 ( 1 ) − b 1 ( 1 ) + R 1 ( 2 ) − R 1 ( 1 ) f ( N 1 − 1 ) B_{1}(2)=B_{1}(1)-b_{1}(1)+\frac{R_{1}(2)-R_{1}(1)}{f}(N_{1}-1) B1(2)=B1(1)b1(1)+fR1(2)R1(1)(N11),将前面计算的 B 1 ( 1 ) B_{1}(1) B1(1)带入展开,得到 B 1 ( 2 ) = R 1 ( 1 ) f − b 1 ( 1 ) + R 1 ( 2 ) f ( N 1 − 1 ) B_{1}(2)=\frac{R_{1}(1)}{f}-b_{1}(1)+\frac{R_{1}(2)}{f}(N_{1}-1) B1(2)=fR1(1)b1(1)+fR1(2)(N11),其中 R 1 ( 1 ) f − b 1 ( 1 ) \frac{R_{1}(1)}{f}-b_{1}(1) fR1(1)b1(1)表示预期第一帧编码比特数减去实际比特数, R 1 ( 2 ) f ( N 1 − 1 ) \frac{R_{1}(2)}{f}(N_{1}-1) fR1(2)(N11)表示按照当前带宽进行计算,当前GOP的后续所有帧的预期编码比特数。因此, B 1 ( 2 ) B_{1}(2) B1(2)表示的是GOP1,从第二帧开始计算一直到GOP1结束的所有帧比特数,后续的计算如 B 1 ( 3 ) B_{1}(3) B1(3) B 1 ( 4 ) B_{1}(4) B1(4)类似
(3) 针对于公式(2),假设i=2,即GOP2,有 B 2 ( 1 ) = R 2 ( 1 ) f N 2 + B 1 ( N 1 ) B_{2}(1)=\frac{R_{2}(1)}{f}N_{2}+B_{1}(N_{1}) B2(1)=fR2(1)N2+B1(N1)。根据前面的理解, B 1 ( N 1 ) B_{1}(N_{1}) B1(N1)表示的是GOP1,从第 N 1 N_{1} N1帧开始计算一直到第一个GOP结束的所有帧的比特数,而GOP1只有 N 1 N_{1} N1帧,所以这里的 B 1 ( N 1 ) B_{1}(N_{1}) B1(N1)可以理解为第一个GOP当中预期的编码比特数与实际编码比特数的差异。 R 2 ( 1 ) f N 2 \frac{R_{2}(1)}{f}N_{2} fR2(1)N2表示的是以当前带宽计算,帧数为 N 2 N_{2} N2总共需要消耗的比特数。因此, B 2 ( 1 ) B_{2}(1) B2(1)表示的是在GOP1实际编码情况的基础之上,GOP2预期使用多少比特
(4) B i − 1 ( N i − 1 ) B_{i-1}(N_{i-1}) Bi1(Ni1)也被称为虚拟缓冲区(Virtual Buffer),描述了预期编码比特与实际编码比特的差异程度,并以此来调整码率控制。虚拟缓冲区在x264中就是VBV,实现细节上有出入

3.2 初始化量化参数

在本文中,第i个GOP中的初始量化参数 Q P i ( 1 ) QP_{i}(1) QPi(1)是基于可用信道带宽和GOP长度预定义的。IDR图像和GOP的第一个存储图像由 Q P i ( 1 ) QP_{i}(1) QPi(1)编码,其他的 Q P i ( 1 ) QP_{i}(1) QPi(1)的计算方式为
Q P i ( 1 ) = m a x { m i n { S u m P Q P ( i − 1 ) N p ( i − 1 ) − m i n { 2 , N i − 1 15 } , Q P i − 1 ( 1 ) + 2 } , Q P i − 1 ( 1 ) − 2 } QP_{i}(1) =max\{min\{\frac{Sum_{PQP}(i-1)}{N_p(i-1)} - min\{2, \frac{N_{i-1}}{15}\}, QP_{i-1}(1)+2\}, QP_{i-1}(1)-2\} QPi(1)=max{min{Np(i1)SumPQP(i1)min{2,15Ni1},QPi1(1)+2},QPi1(1)2}
其中 N p ( i − 1 ) N_{p}(i-1) Np(i1)是第(i−1) 个GOP中存储图像的总数, S u m P Q P ( i − 1 ) Sum_{PQP}(i-1) SumPQP(i1)是第(i−1) 个GOP中所有存储图像的量化参数之和。 Q P i ( 1 ) QP_{i}(1) QPi(1)进一步通过以下方式进行调整:
Q P i ( 1 ) = Q P i ( 1 ) − 1 ; i f Q P i ( 1 ) > Q P i − 1 ( N i − 1 − L ) − 2 QP_{i}(1)=QP_{i}(1)-1; if QP_{i}(1) > QP_{i-1}(N_{i-1}-L)-2 QPi(1)=QPi(1)1;ifQPi(1)>QPi1(Ni1L)2
其中 Q P i − 1 ( N i − 1 − L ) QP_{i-1}(N_{i-1}-L) QPi1(Ni1L)是第(i−1) 个GOP中最后一个存储图像的量化参数。显然, Q P i ( 1 ) QP_{i}(1) QPi(1)既适应于GOP的长度,也适应于可用的信道带宽

PS:
(1) 对于IDR帧和第一个P帧,根据bpp的大小确定qp,bpp的计算方式为 b p p = ( b i t r a t e / f r a m e r a t e ) ∗ N p i x e l bpp=(bitrate / framerate) * N_{pixel} bpp=(bitrate/framerate)Npixel,即每个像素平均使用的比特数,依据bpp确定qp的方式如下
q p = { 35 b p p ≤ L 1 25 b p p ≤ L 2 20 b p p ≤ L 3 10 o t h e r w i s e qp=\left\{ \begin{array}{rcl} 35 & & {bpp \leq L1} \\ 25 & & {bpp \leq L2} \\ 20 & & {bpp \leq L3} \\ 10 & & {otherwise} \end{array} \right. qp= 35252010bppL1bppL2bppL3otherwise
其中,L1,L2和L3由视频的宽度计算。对于QCIF视频(176x144)推荐L1 = 0.1,L2 = 0.3,L3 = 0.6;对于CIF视频(352x288)推荐L1 = 0.2,L2 = 0.6,L3 = 1.2;其他情况L1 = 0.6,L2 = 1.4,L3 = 2.4 (参考H264编码-码率控制原理以及JM代码分析)
(2) 其他GOP中初始QP的计算方式可以拆分来理解。
  (a) m i n { 2 , N i − 1 15 } min\{2, \frac{N_{i-1}}{15}\} min{2,15Ni1}表示一个基于GOP内帧数量的调控因子,如果帧数量大于30,则调控因子为2,否则为 N i − 1 15 \frac{N_{i-1}}{15} 15Ni1
  (b) m i n { S u m P Q P ( i − 1 ) N p ( i − 1 ) − m i n { 2 , N i − 1 15 } , Q P i − 1 ( 1 ) + 2 } min\{\frac{Sum_{PQP}(i-1)}{N_{p}(i-1)}-min\{2, \frac{N_{i-1}}{15}\},QP_{i-1}(1)+2\} min{Np(i1)SumPQP(i1)min{2,15Ni1},QPi1(1)+2},其中的 S u m P Q P ( i − 1 ) N p ( i − 1 ) \frac{Sum_{PQP}(i-1)}{N_{p}(i-1)} Np(i1)SumPQP(i1)表示前一个GOP中PQP的平均值。除去调控因子的考虑,这里表示的含义是,前一个GOP中PQP的均值和前一个GOP的初始QP,取二者中小的一个
  (c) m a x { X , Q P i − 1 + 2 } max\{X, QP_{i-1}+2\} max{X,QPi1+2},其中X表示(b)中计算的结果
  (d) 综上所述,其他GOP中初始QP的计算方式将前一个GOP的初始QP和平均实际编码QP进行对比,增加一些调控因子,确定初始QP。这里更像是一个加上调控因子的clip3操作
(3) 如果经过上面的计算之后,当前GOP初始QP值大于前一个GOP最后一个P帧QP - 2,再将当前GOP初始QP减小1,进行微调

4.帧级码率控制

在这一层,计算了非存储图像的量化参数和存储图像的目标比特。

4.1 非存储图像的量化参数

非存储图片的量化参数通过线性插值方法得到,如下所示:

假设第j帧和(j+L+ 1)帧是存储的图片,相邻的两个存储图片 Q P i ( j ) QP_{i}(j) QPi(j) Q P i ( 1 ) QP_{i}(1) QPi(1)的量化参数分别为。根据以下两种情况给出第k (1<= k <= L)张非存储图片的量化参数:

(1) 情况1 当L=1 时,两个存储图像之间只有一幅非存储图像。量化参数由以下公式计算:
Q P i ( j + 1 ) = { Q P i ( j ) + Q P i ( j + 2 ) + 2 2 Q P i ( j ) ≠ Q P i ( j + 2 ) Q P i ( j ) + 2 O t h e r w i s e QP_{i}(j+1)=\left\{ \begin{array}{rcl} \frac{QP_{i}(j) + QP_{i}(j+2)+2}{2} & & {QP_{i}(j) \neq QP_{i}(j+2)}\\ QP_{i}(j)+2 & & {Otherwise}\\ \end{array} \right. QPi(j+1)={2QPi(j)+QPi(j+2)+2QPi(j)+2QPi(j)=QPi(j+2)Otherwise

(2) 情况2 当L>1 时,两个存储图像之间有多幅非存储图像。量化参数由以下公式计算:
Q P i ( j + k ) = Q P i ( j ) + α + m a x { − 2 ( k − 1 ) , m i n { ( Q P i ( j + L + 1 ) − Q P i ( j ) ) ( k − 1 ) L − 1 , 2 ( k − 1 ) } } QP_{i}(j+k)=QP_{i}(j)+\alpha+max\{-2(k-1), \\ min\{\frac{(QP_{i}(j+L+1)-QP_{i}(j))(k-1)}{L-1}, 2(k-1)\}\} QPi(j+k)=QPi(j)+α+max{2(k1),min{L1(QPi(j+L+1)QPi(j))(k1),2(k1)}}
其中 α \alpha α是第一个非存储图像的量化参数与 Q P i ( j ) QP_{i}(j) QPi(j) 之间的差值,计算公式为
α = { − 3 Q P i ( j + L + 1 ) − Q P i ( j ) ≤ − 2 L − 3 − 2 Q P i ( j + L + 1 ) − Q P i ( j ) = − 2 L − 2 − 1 Q P i ( j + L + 1 ) − Q P i ( j ) = − 2 L − 1 0 Q P i ( j + L + 1 ) − Q P i ( j ) = − 2 L 1 Q P i ( j + L + 1 ) − Q P i ( j ) = − 2 L + 1 2 O t h e r w i s e \alpha=\left\{ \begin{array}{rcl} -3 & & {QP_{i}(j+L+1) - QP_{i}(j) \leq -2L-3}\\ -2 & & {QP_{i}(j+L+1) - QP_{i}(j) = -2L-2}\\ -1 & & {QP_{i}(j+L+1) - QP_{i}(j) = -2L-1}\\ 0 & & {QP_{i}(j+L+1) - QP_{i}(j) = -2L}\\ 1 & & {QP_{i}(j+L+1) - QP_{i}(j) = -2L+1}\\ 2 & & {Otherwise}\\ \end{array} \right. α= 321012QPi(j+L+1)QPi(j)2L3QPi(j+L+1)QPi(j)=2L2QPi(j+L+1)QPi(j)=2L1QPi(j+L+1)QPi(j)=2LQPi(j+L+1)QPi(j)=2L+1Otherwise

PS:
(1) 非存储图像通常指的是B帧这种不会被其他帧用于参考的图像
(2) 如果L=1,假设当前编码序列为 … P B P …,直接取两个P帧QP的均值
(3) 如果L>1,假设当前编码序列为 … P B B B P …,将计算公式分解
 (a) m i n { ( Q P i ( j + L + 1 ) − Q P i ( j ) ) ( k − 1 ) L − 1 , 2 ( k − 1 ) } min\{\frac{(QP_{i}(j+L+1)-QP_{i}(j))(k-1)}{L-1}, 2(k-1)\} min{L1(QPi(j+L+1)QPi(j))(k1),2(k1)}中的 ( Q P i ( j + L + 1 ) − Q P i ( j ) ) ( k − 1 ) L − 1 \frac{(QP_{i}(j+L+1)-QP_{i}(j))(k-1)}{L-1} L1(QPi(j+L+1)QPi(j))(k1)表示第k个B帧,以第j个P帧和第j+L+1个P帧的距离为权重计算的QP调整因子
 (b) m a x { − 2 ( k − 1 ) , X } max\{-2(k-1), X\} max{2(k1),X},其中X表示(a)中计算的结果。(a)和(b)中的步骤是对QP调整权重因子进行clip操作
 (c) 第k个B帧的QP,以第j个P帧的QP作为基础( Q P i ( j ) QP_{i}(j) QPi(j)),加上偏移量( α \alpha α和QP调整因子)获得
 (d) α \alpha α的数值与两个P帧之间的距离有关,经验性参数

4.2 存储图像的目标比特

分配给当前存储图像的比特数应根据当前缓冲区占用情况和图像复杂度进行调整,如下所示:

(1) 步骤1 为当前GOP中的每个存储图像预定义一个目标缓冲区水平。
假设第j幅图像是一个存储图像。该图像的目标缓冲区水平由以下方式确定
S i ( j ) = S i ( j − L − 1 ) − S i ( 2 ) − V s 8 N p ( i ) − 1 + W ‾ p , i ( j − L − 1 ) ( L + 1 ) R i ( j ) f ( W ‾ p , i ( j − L − 1 ) + W ‾ b , i ( j − 1 ) L ) − R i ( j ) f S_{i}(j)=S_{i}(j-L-1)-\frac{S_i(2)-\frac{V_s}{8}}{N_{p}(i)-1}+\\ \frac{\overline{W}_{p,i}(j-L-1)(L+1)R_{i}(j)}{f(\overline{W}_{p,i}(j-L-1)+\overline{W}_{b,i}(j-1)L)}-\frac{R_{i}(j)}{f} Si(j)=Si(jL1)Np(i)1Si(2)8Vs+f(Wp,i(jL1)+Wb,i(j1)L)Wp,i(jL1)(L+1)Ri(j)fRi(j)

其中 S i ( 2 ) S_{i}(2) Si(2)在编码第i个GOP中的第一个存储图像 (即IDR帧) 后重置为 V i ( 2 ) V_{i}(2) Vi(2) V s V_s Vs 是缓冲区大小, W ‾ p , i ( j − L − 1 ) \overline{W}_{p,i}(j-L-1) Wp,i(jL1) 是已编码存储图像的平均复杂度权重 (涵盖过去所有可用于计算的帧集合而非单帧) W ‾ b , i ( j − 1 ) \overline{W}_{b,i}(j-1) Wb,i(j1)是已编码非存储图像的复杂度权重[4]。

PS:
(1) 存储图像通常指的是Intra帧以及P帧,因为它们会被用于参考,所以会存储在内存中
(2) 假设当前编码序列为 … P B B B P B B B P …,其中第一个P帧的索引为j-L-1,第二个P帧的索引为j,第三个P帧的索引为j+l+1。在步骤1中,为当前存储图像计算一个缓冲区,将计算的公式进行拆分
  (a) S i ( j ) S_{i}(j) Si(j)表示当前P帧在编码之后的目标缓冲区的比特数
  (b) S i ( j − L − 1 ) S_{i}(j-L-1) Si(jL1)表示前一个P帧在编码之后的目标缓冲区的比特数
  (c) S i ( 2 ) − V s 8 N p ( i ) − 1 \frac{S_i(2)-\frac{V_s}{8}}{N_{p}(i)-1} Np(i)1Si(2)8Vs中的 S i ( 2 ) S_{i}(2) Si(2)可以改写成 V i ( 2 ) V_{i}(2) Vi(2),表示第一个存储图像(通常是IDR帧)编码之后buffer中的实际比特数。分子部分表示编码当前帧之后,buffer中的比特数,buffer的实际比特数减去1/8,这是因为在计算复杂度时,当前帧的权重系数为1/8,前面一系列帧的平均复杂度的权重系数为7/8。随后,除以当前GOP中P帧数量减去1。因此,(c)表示了编码当前帧之后实际比特数的多少,或者理解为水池出水量。
  (d) W ‾ p , i ( j − L − 1 ) ( L + 1 ) R i ( j ) f ( W ‾ p , i ( j − L − 1 ) + W ‾ b , i ( j − 1 ) L ) − R i ( j ) f \frac{\overline{W}_{p,i}(j-L-1)(L+1)R_{i}(j)}{f(\overline{W}_{p,i}(j-L-1)+\overline{W}_{b,i}(j-1)L)}-\frac{R_{i}(j)}{f} f(Wp,i(jL1)+Wb,i(j1)L)Wp,i(jL1)(L+1)Ri(j)fRi(j)包含了B帧对当前P帧的影响,将公式中的 W ‾ p , i ( j − L − 1 ) \overline{W}_{p,i}(j-L-1) Wp,i(jL1) W ‾ b , i ( j − L − 1 ) \overline{W}_{b,i}(j-L-1) Wb,i(jL1)简记为 W p W_{p} Wp W b W_{b} Wb,公式变形为 ( W p ( L + 1 ) W p + W b L − 1 ) ∗ R i ( j ) f (\frac{W_{p}(L+1)}{W_{p}+W_{b}L}-1) * \frac{R_{i}(j)}{f} (Wp+WbLWp(L+1)1)fRi(j)。变形后的公式可以理解为,前一个P帧和前一个B帧分别赋予一个权重,对当前target buffer进行调控,这个权重由距离决定。在变式中,分子中的权重为L+1,表明仅考虑前一个P帧的复杂度对当前P帧的影响;在分母中,P帧复杂度权重为1,B帧复杂度为L,强调了更接近当前帧的B帧的复杂度。如果 W p W_p Wp大于 W b W_b Wb,则(d)这一项大于0,此时前一个P帧对应的平均复杂度偏高,表明target buffer应该增大一点,以适应较高的复杂度;反之,如果B帧对应的平均复杂度偏高,表明P帧的影响程度较小,target buffer应该减小一点。从这里来看,这个公式中主要的影响因子还是前一个P帧。另外,如果编码序列中没有B帧,则没有(d)这一项

(2) 步骤2 计算当前存储图像的目标比特数。
第i个GOP中的第j幅存储图像的目标比特数根据目标缓冲区水平、帧率、可用信道带宽和实际缓冲区占用情况确定,如下所示:
T ~ i ( j ) = R i ( j ) f + γ ( S i ( j ) − V i ( j ) ) \widetilde{T}_{i}(j)=\frac{R_{i}(j)}{f}+\gamma(S_{i}(j)-V_{i}(j)) T i(j)=fRi(j)+γ(Si(j)Vi(j))

其中 V i ( j ) V_{i}(j) Vi(j)是编码第i个GOP中的第(j−1) 幅存储图像后的实际缓冲区占用情况, γ \gamma γ是一个常数,当没有非存储图像时其典型值为0.5,否则为0.25。

同时,在计算目标比特时,还应考虑剩余比特的数量
T ^ i ( j ) = W p , i ( j − L − 1 ) B i ( j ) W p , i ( j − L − 1 ) N p , r + W b , i ( j − 1 ) N b , r \hat{T}_{i}(j)=\frac{W_{p,i}(j-L-1)B_{i}(j)}{W_{p,i}(j-L-1)N_{p,r}+W_{b,i}(j-1)N_{b,r}} T^i(j)=Wp,i(jL1)Np,r+Wb,i(j1)Nb,rWp,i(jL1)Bi(j)

其中 N p , r N_{p,r} Np,r N b , r N_{b,r} Nb,r分别是剩余存储图像和剩余非存储图像的数量。

目标比特是 T ~ i ( j ) \widetilde{T}_{i}(j) T i(j) T ~ i ( j ) \widetilde{T}_{i}(j) T i(j)的加权组合,如下所示
T i ( j ) = β T ^ i ( j ) + ( 1 − β ) T ~ i ( j ) ( 3 ) T_{i}(j)=\beta\hat{T}_{i}(j)+(1-\beta)\widetilde{T}_{i}(j) (3) Ti(j)=βT^i(j)+(1β)T i(j)(3)

其中p是一个常数,当没有非存储图像时其典型值为0.5,否则为0.9。从公式(3)可以看出,通过选择较小的p可以实现严格的缓冲区节。

为了保持编码帧的质量,目标比特数 T i ( j ) T_i(j) Ti(j) 被限制在:
T i ( j ) = m a x { T i ( j ) , m h r d , i ( j − L − 1 ) + R i ( j ) 4 f } ( 4 ) T_{i}(j)=max\{T_{i}(j), m_{hrd,i}(j-L-1)+\frac{R_{i}(j)}{4f}\}(4) Ti(j)=max{Ti(j),mhrd,i(jL1)+4fRi(j)}(4)
其中 m h r d , i ( j − L − 1 ) m_{hrd,i}(j-L-1) mhrd,i(jL1)是用于前一个存储图像的头部和运动矢量的位数。

为了符合HRD(假设参考解码器)的要求,目标比特数进一步被限制为:
T i ( j ) = m i n { m a x { Z i ( j ) , T i ( j ) , U i ( j ) } } ( 5 ) T_{i}(j)=min\{max\{Z_{i}(j), T_{i}(j), U_{i}(j)\}\}(5) Ti(j)=min{max{Zi(j),Ti(j),Ui(j)}}(5)
其中 Z i ( j ) Z_i(j) Zi(j) U i ( j ) U_i(j) Ui(j) 是根据HRD(假设参考解码器)的要求推导出来的[3]

PS:
(1) 在步骤2中,实现的功能是,基于前面的目标缓冲区大小来调整目标比特数
(2) 计算目标比特数时,分为两种情况考虑
  (a) 基于目标缓冲区水平、帧率、可用信道带宽和实际缓冲区占用情况确定,将公式拆解
   (a1) R i ( j ) f \frac{R_{i}(j)}{f} fRi(j)表示预期编码比特数,由信道带宽和帧率决定
   (a2) γ ( S i ( j ) − V i ( j ) ) \gamma(S_{i}(j)-V_{i}(j)) γ(Si(j)Vi(j))表示目标缓冲区大小和实际缓冲区大小的差异
   (a3) 结合(a1)和(a2),这种情况下的目标比特数为预期编码比特数加上缓冲区差异值的调整
  (b) 基于剩余比特的数量确定
   (b1) 将公式中的 W p , i ( j − L − 1 ) W_{p,i}(j-L-1) Wp,i(jL1) W b , i ( j − 1 ) W_{b,i}(j-1) Wb,i(j1) W p W_{p} Wp W b W_{b} Wb代替,可以改写为 T ^ i ( j ) = W p , i B i ( j ) W p , i N p , r + W b , i N b , r \hat{T}_{i}(j)=\frac{W_{p,i}B_{i}(j)}{W_{p,i}N_{p,r}+W_{b,i}N_{b,r}} T^i(j)=Wp,iNp,r+Wb,iNb,rWp,iBi(j),其中 B i ( j ) B_{i}(j) Bi(j)为剩余比特数,这里表示的含义是当前P帧的目标比特,等于P帧累积复杂度,除以帧类型及对应复杂度乘积。如果没有B帧,则当前公式变形为 B i ( j ) ∗ 1 N p , r B_{i}(j) * \frac{1}{N_{p,r}} Bi(j)Np,r1,即当前P帧的目标比特,等于剩余比特数除以剩余P帧的数量

5.基本单元级码率控制

基本单元级别的码率控制选择存储图像中所有基本单元的量化参数值,使得生成的比特数总和接近帧的目标比特数 T i ( j ) T_i(j) Ti(j)。以下是对该方法的逐步描述:

(1) 步骤1: 使用前一个存储图像中对应位置的基本单元的实际MAD值,通过模型(1)预测当前存储图像中剩余基本单元的MAD值

(2) 步骤2: 计算当前基本单元的纹理比特数 b ^ l , i ( j ) \hat{b}_{l,i}(j) b^l,i(j)。此步骤由以下三个子步骤组成:
 (2.1) 步骤2.1 计算当前基本单元的目标比特数。设 T r , i ( j ) T_{r,i}(j) Tr,i(j) 表示当前存储图像中剩余基本单元的剩余比特数,且 T r , i ( j ) T_{r,i}(j) Tr,i(j) 的初始值为 T i ( j ) T_i(j) Ti(j)。第l个基本单元的目标比特数由以下公式给出

b ~ l , i ( j ) = T r , i ( j ) σ ~ l , i 2 ( j ) Σ k = l N u n i t σ ~ k , i 2 ( j ) \widetilde{b}_{l,i}(j)=T_{r,i}(j)\frac{\widetilde{\sigma}^2_{l,i}(j)}{\Sigma^{N_{unit}}_{k=l}\widetilde{\sigma}^2_{k,i}(j)} b l,i(j)=Tr,i(j)Σk=lNunitσ k,i2(j)σ l,i2(j)

 (2.2) 步骤2.2 计算所有已编码基本单元生成的头部比特数的平均值:
m ~ h d r , l = m ~ h d r , l − 1 ( 1 − 1 l ) + m ^ h d r , i l \widetilde{m}_{hdr,l}=\widetilde{m}_{hdr,l-1}(1-\frac{1}{l})+\frac{\hat{m}_{hdr,i}}{l} m hdr,l=m hdr,l1(1l1)+lm^hdr,i

m h d r , l = m ~ h d r , l l N u n i t + m h d r , 1 ( 1 − l N u n i t ) m_{hdr,l}=\widetilde{m}_{hdr,l}\frac{l}{N_{unit}}+m_{hdr,1}(1-\frac{l}{N_{unit}}) mhdr,l=m hdr,lNunitl+mhdr,1(1Nunitl)

其中 m ^ h d r , i \hat{m}_{hdr,i} m^hdr,i是当前存储图像中第l 个基本单元实际生成的头部比特数, m h d r , 1 m_{hdr,1} mhdr,1是从前一帧的所有基本单元中估计的值。

 (2.3) 计算纹理比特数 b ^ l , i ( j ) \hat{b}_{l,i}(j) b^l,i(j)
b ^ l , i ( j ) = b ~ l , i ( j ) − m h d r , l \hat{b}_{l,i}(j)=\widetilde{b}_{l,i}(j)-m_{hdr,l} b^l,i(j)=b l,i(j)mhdr,l

为了保证每个基本单元的质量, b ^ l , i ( j ) \hat{b}_{l,i}(j) b^l,i(j)随后还会除以 R i ( j ) / ( 4 f N u n i t ) R_{i}(j)/(4fN_{unit}) Ri(j)/(4fNunit)

PS:
(1) 将总比特数分为纹理比特数和头部比特数,总比特数由步骤(2.1)给出,头部比特数由步骤(2.2)给出,纹理比特数由步骤(2.3)给出
(2) 在步骤(2.2)中, m h d r , l m_{hdr,l} mhdr,l表示第l个基本单元的头部比特数,其由两部分组成:
 (a) 基于帧内预测思想,利用当前帧前一个已编码单元,即第l-1个基本单元的头部比特数来获取。其中第l-1个基本单元的头部比特数权重为 ( 1 − 1 l ) (1-\frac{1}{l}) (1l1),第l个基本单元实际生成的头部比特数占比为\frac{1}{l}
 (b) 基于帧间预测思想,利用前一帧的所有已编码单元的平均值,即 m h d r , 1 m_{hdr,1} mhdr,1
 (c) 计算第l个基本单元的头部比特数时,基于帧内思想获得的比特数权重为 l N u n i t \frac{l}{N_{unit}} Nunitl,基于帧间思想获得的比特权重为 1 − l N u n i t 1-\frac{l}{N_{unit}} 1Nunitl。举例来说,假设 l = 50 l=50 l=50 N u n i t = 120 N_{unit}=120 Nunit=120,即处于整帧的前半部分编码,则帧间的权重较大,这是因为帧间部分使用的是前一帧中所有的已编码单元,这样可参考的信息更多;反之,假设 l = 100 l=100 l=100 N u n i t = 120 N_{unit}=120 Nunit=120,即处于整帧的后半部分编码,则帧内的权重较大,这是因为当前帧已经编码了很多单元,帧内可使用的信息足够多,其权重应该更大
(3) 最后,在获取了目标纹理比特数之后,还会做一个rounding

(3) 步骤3: 使用二次R-D模型[3]计算当前基本单元的量化参数。我们需要考虑以下三种情况:
情况1:当前存储图像中的第一个基本单元
Q P l , i ( j ) = Q P ^ i ( j − L − 1 ) QP_{l,i}(j)=\hat{QP}_{i}(j-L-1) QPl,i(j)=QP^i(jL1)
其中 Q P ^ i ( j − L − 1 ) \hat{QP}_{i}(j−L−1) QP^i(jL1) 是前一个存储图像中所有基本单元的量化参数的平均值。

情况2:当 T r , i < 0 T_{r,i}<0 Tr,i<0 时,当前基本单元的量化参数应大于前一个基本单元的量化参数,以使生成的比特数总和接近 T i ( j ) T_{i}(j) Ti(j)
Q P l , i = Q P l − 1 , i ( j ) + X b u QP_{l,i}=QP_{l-1,i}(j)+X_{bu} QPl,i=QPl1,i(j)+Xbu
其中, X b u X_{bu} Xbu是两个连续基本单元的量化参数的变化范围。

为了保持视觉质量的平滑性,量化参数进一步被限制为:
Q P l , i ( j ) = m a x { 0 , Q P ‾ i ( j − L − 1 ) − Y f r , m i n { 51 , Q P ‾ i ( j − L − 1 ) + Y f r , Q P l , i ( j ) } } QP_{l,i}(j)=max\{0,\overline{QP}_{i}(j-L-1)-Y_{fr}, min\{51, \overline{QP}_{i}(j-L-1)+Y_{fr}, QP_{l,i}(j)\}\} QPl,i(j)=max{0,QPi(jL1)Yfr,min{51,QPi(jL1)+Yfr,QPl,i(j)}}
其中, Y f r Y_{fr} Yfr是量化参数的进一步变化范围

情况3:否则,我们首先使用二次模型[3]和情况2中给出的限制来计算一个量化参数 Q P l , i ( j ) QP_{l,i}(j) QPl,i(j)

(4) 步骤4:对当前基本单元中的所有宏块(MBs)执行RDO。

(5) 步骤5:更新剩余比特数 T r , i ( j ) T_{r,i}(j) Tr,i(j),并更新头部MAD预测模型和二次R-D模型的参数。

(6) 步骤6:编码当前存储图像后,更新 Q P ^ i ( j ) \hat{QP}_{i}(j) QP^i(j)

PS:
(1) 在步骤3当中,使用的二次RD模型如下,这是一个提案中的公式(Z. G. Li, W. Gao, Feng Pan, S. W. Ma. K. P. Lim, G. N.Feng, X. Lm, R. Susanto, Y. Lu and H. Q. Lu. “Adaptive rate control with HRD consideration”. In 8th meeting:Geneva, 23-27, May, 2003, JVT-H014)
T a r g e t _ b i t s = c 1 ∗ M A D Q S t e p + c 2 ∗ M A D Q S t e p ∗ Q S t e p Target\_bits=c_1 * \frac{MAD}{QStep}+c_2 * \frac{MAD}{QStep * QStep} Target_bits=c1QStepMAD+c2QStepQStepMAD
公式描述了量化步长、目标比特和MAD之间的对应关系。延续前面的步骤,这里可以根据前面获得的目标比特来计算QStep,根据QStep和QP的对应表,获得QP
(2) 在确定QP时,分为三种情况
 (a) 如果是第一个基本单元,其QP由前一帧中所有基本编码单元的QP均值来确定
 (b) 如果剩余目标比特小于0,则会适当增大QP。调整的依据是前后量化参数变化的范围
 (c) 如果不属于前面两种情况,则先根据二次RD模型获得一个初始QP,随后添加情况2中的平滑性限制
(3) 在后续的步骤4,5,6中,根据前面计算出来的QP执行RDO过程,更新一系列参数

6.实验结果

我们将具有我们码率控制的H.264编码器的编码效率与具有固定量化参数的H.264编码器的编码效率进行了比较。这种类型的比较是由H.264的临时工作组根据[15]推荐的,实验设置在[4]中给出。表1列出了(Y, PSNR)的实验结果。结果显示,与使用固定量化参数的H.264编码器相比,我们的方案可以将平均PSNR提高多达0.78dB。在正常测试条件下,对于H.264测试序列,改进的平均PSNR为0.43dB。
在这里插入图片描述

7.结论

本文通过引入基本单元的概念和线性绝对差(MAD)预测模型,提出了一种自适应的H.264码率控制方案。对于H.264推荐的所有测试序列,整体平均PSNR改进为0.43dB。Remark 1中提供了一种替代方法,这将在我们未来的研究中进行探讨。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/445791.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OKHTTP 如何处理请求超时和重连机制

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

【ELKB】Kibana使用

搭建好ELKB后访问地址&#xff1a;http://localhost:5601 输入账号密码登录以后 左侧导航有home、Analysis、Enterprise search 、Observability、Security、Management home&#xff1a;首页Analysis&#xff1a;工具来分析及可视化数据Enterprise search&#xff1a;企业级搜…

解读《ARM Cortex-M3 与Cortex-M4 权威指南》——第1章 ARM Cortex-M处理器简介

1. 三级流水线设计 解释:三级流水线设计意味着处理器在执行指令时可以同时处理多个步骤。这些步骤通常包括取指(Fetch)、译码(Decode)和执行(Execute)。好处:这种设计提高了指令的执行效率,使得处理器能够在每个时钟周期内完成更多的工作,从而提升整体性能。2. 哈佛总…

分享一些常用的数据库性能监测工具

以下是一些常用的数据库性能监测工具&#xff1a; 一、MySQL MySQL Enterprise Monitor&#xff1a; 由 MySQL 官方推出&#xff0c;提供全面的数据库性能监控、诊断和优化功能。可以监控数据库的各种指标&#xff0c;如查询性能、连接数、缓存命中率等&#xff0c;并提供警报…

yolo参数调节

1-weight 不同版本的神经网络 可以在这下载复制 2 source图片路径或者文件夹路径 3 img size 尺寸&#xff08;尽量与神经网络模型匹配&#xff09; 4 4 -conf-thres 简单理解就是模型识别成功概率超过这一标准才会显示 5 iou多区域重合 &#xff08;重合比例&#xff09;…

数学科普读物《从毕达哥拉斯到怀尔斯》

毕达哥拉斯是古希腊数学家&#xff0c;怀尔斯是英国数学家&#xff0c;曾任美国普林斯顿大学教授。这本书是哈工大出版社刘培杰先生主编的。这是一本500多页的书&#xff0c;我不禁慨叹高级数学爱好者刘培杰的博学广识&#xff0c;因为书中纵论古今旁征博引&#xff0c;仅书后的…

数据结构-5.6.二叉树的先,中,后序遍历

一.遍历&#xff1a; 二.二叉树的遍历&#xff1a;利用了递归操作 1.简介&#xff1a; 二叉树的先序遍历&#xff0c;中序遍历&#xff0c;后序遍历都是以根结点遍历顺序为准的&#xff0c;如先序遍历就先遍历根结点 2.实例&#xff1a; 例一&#xff1a; 例二&#xff1a; …

C++中string函数用法总结

一&#xff0c;string的构造函数 string() //无参构造&#xff0c;初始化为空串 string(const string& str) //用str拷贝构造 string(size_t n,char c) //用n个字符c初始化 string(const char* s,size_t n) //用字符串s的前n个字符初始化 string(const string& str…

【最优化方法】最速下降法

给出点 x [1,4,5,8,12] y [7,9,15,14,27] 要找出温度和冰淇淋销量之间的关系&#xff0c;通过线性回归来拟合求出属性和结果之间的线性关系。 如果直接把这些点连起来&#xff0c;是吃力不讨好的&#xff0c;因为如果有新数据进来大概率不在这条线上&#xff0c;这个行为也…

Prometheus + Grafana 监控 MySQL 数据库

文章目录 1、前置介绍2、搭建流程2.1、安装 Docker2.2、安装 MySQL2.3、安装 MySQL Exporter2.4、安装 Prometheus2.5、安装 Grafana 1、前置介绍 本次监控平台搭建&#xff0c;我使用2台阿里云服务器来完成本次的搭建部署操作&#xff0c;配置如下&#xff1a; 阿里云ECS1&am…

【Kubernets】配置类型资源 Etcd, Secret, ConfigMap

文章目录 所有资源概览Etcd详细说明一、基本概念二、主要功能三、架构与组件四、数据模型与操作五、安全与认证六、集群部署与管理 Secret详细说明一、Secret 的类型二、Secret 的创建三、Secret 的使用四、Secret 的更新与删除五、Secret 的安全性 ConfigMap详细说明一、Confi…

2024年恩施职称评前公示

此次公示共有422人&#xff0c;初级职称、中级职称、馆员、畜牧师、助理馆员、三级演员、农艺师等均在一起进行评审前的公示。 根据恩施州职称改革工作领导小组办公室《关于报送2024年度恩施州中初级专业技术职务评审材料的通知》&#xff08;恩施州职改办〔2024〕14号&#xf…

jdk环境变量配置--小总结

1、jdk安装路径变量 2、在path下添加环境变量

【Python Django + Vue】酒店在线预订系统:用技术说话!

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

【JavaEE初阶】文件-IO之实现文件系统的操作如何进行实现

前言 &#x1f31f;&#x1f31f;本期讲解关于文件IO的操作&#xff0c;这里涉及到比较常用的文件操作哦~~~ &#x1f308;上期博客在这里&#xff1a;【JavaEE初阶】CAS的ABA问题&#xff0c;JUC多线程编程有用的相关类-CSDN博客 &#x1f308;感兴趣的小伙伴看一看小编主页&a…

支持向量机-笔记

支持向量机&#xff08;Support Vector Machine, SVM&#xff09; 是一种强大的监督学习算法&#xff0c;广泛应用于分类和回归任务&#xff0c;特别是在分类问题中表现优异。SVM 的核心思想是通过寻找一个最优超平面&#xff0c;将不同类别的数据点进行分割&#xff0c;并最大…

数据结构 ——— 顺序表oj题:有效的括号

目录 题目要求 代码实现 题目要求 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个…

[单master节点k8s部署]37.微服务(一)springCloud 微服务

微服务架构的一个重要特点是&#xff0c;它与开发中使用的具体编程语言或技术栈无关。每个微服务都可以使用最适合其功能需求的语言或技术来实现。例如&#xff0c;一个微服务可以用Java编写&#xff0c;另一个微服务可以用Python、Go、Node.js等编写。微服务架构允许这种灵活性…

Gin项目的初始化步骤和常见错误记录

相信很多人对Go的环境安装和Gin项目的初始化都已经手拿把攥很是熟练了&#xff0c;本节介绍一个自己新建Go项目时非常好用的设置以及记录一下Gin项目的初始化过程和常能遇到的错误。 一个容易忽略的Go ENV 在安装了Go的电脑中&#xff0c;我们可以在命令行执行 go env 命令&…

04 什么是线性表

什么是线性表 一、为什么需要线性表 例如&#xff1a; ​ 在程序中保存指定班级的所有的学生信息&#xff08;暂时只需要处理姓名、年龄&#xff09;&#xff0c;该班级最多可容纳30人&#xff0c;且可进行数量上的增减。 业务功能&#xff1a; ​ 1&#xff09;这个项目中…