HM编码器代码阅读(44)——样点自适应补偿SAO(三)选取最优的SAO模式并确定补偿值

选取最优的SAO模式并确定补偿值



    对像素值进行统计之后,需要确定应该SAO的哪一种模式,具体的方法是利用率失真优化的方式来确定最优的模式。同时还要得到该模式下的补偿值,后面就可以利用这个补偿值对像素块进行补偿(所谓补偿就是对像素值进行修正)。


选取最优模式和确定补偿值的主函数


    选取最优模式和确定补偿值的主函数是decideBlkParams,流程如下:
    1、对图像的每一个CTU进行处理
    2、CTU尝试每一种SAO模式,选出最优的SAO模式
    3、利用选取出来的SAO模式对重建块进行补偿

/*
** 选取最优模式
** 对图像的每一个CTU进行处理,对CTU尝试每一种SAO模式,选出最优的SAO模式
** 用选出来的最优SAO模式,对重建块进行处理
*/
Void TEncSampleAdaptiveOffset::decideBlkParams(TComPic* pic, Bool* sliceEnabled, SAOStatData*** blkStats, TComPicYuv* srcYuv, TComPicYuv* resYuv, SAOBlkParam* reconParams, SAOBlkParam* codedParams)
{
	Bool isAllBlksDisabled = false;
	if(!sliceEnabled[SAO_Y] && !sliceEnabled[SAO_Cb] && !sliceEnabled[SAO_Cr])
	{
		isAllBlksDisabled = true;
	}

	m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[ SAO_CABACSTATE_PIC_INIT ]);

	SAOBlkParam modeParam;
	Double minCost, modeCost;

	// 遍历图像的所有CTU,对每一个CTU进行SAO处理
	for(Int ctu=0; ctu< m_numCTUsPic; ctu++)
	{
		if(isAllBlksDisabled)
		{
			codedParams[ctu].reset();
			continue;
		}

		m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[ SAO_CABACSTATE_BLK_CUR ]);

		//get merge list
		std::vector<SAOBlkParam*> mergeList;

		// 获取merge列表
		getMergeList(pic, ctu, reconParams, mergeList);

		minCost = MAX_DOUBLE;
		// 遍历所有的SAO模式
		for(Int mode=0; mode < NUM_SAO_MODES; mode++)
		{
			switch(mode)
			{
			case SAO_MODE_OFF:
				{
					continue; //not necessary, since all-off case will be tested in SAO_MODE_NEW case.
				}
				break;
			case SAO_MODE_NEW: // EO或者BO
				{
					deriveModeNewRDO(ctu, mergeList, sliceEnabled, blkStats, modeParam, modeCost, m_pppcRDSbacCoder, SAO_CABACSTATE_BLK_CUR);

				}
				break;
			case SAO_MODE_MERGE: // merge模式
				{
					deriveModeMergeRDO(ctu, mergeList, sliceEnabled, blkStats , modeParam, modeCost, m_pppcRDSbacCoder, SAO_CABACSTATE_BLK_CUR);
				}
				break;
			default:
				{
					printf("Not a supported SAO mode\n");
					assert(0);
					exit(-1);
				}
			}

			// 选取最优的模式
			if(modeCost < minCost)
			{
				minCost = modeCost;
				codedParams[ctu] = modeParam;
				m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[ SAO_CABACSTATE_BLK_NEXT ]);

			}
		} //mode
		m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[ SAO_CABACSTATE_BLK_NEXT ]);

		//apply reconstructed offsets
		reconParams[ctu] = codedParams[ctu];
		// 对重建像素块进行像素值补偿
		reconstructBlkSAOParam(reconParams[ctu], mergeList);
		offsetCTU(ctu, srcYuv, resYuv, reconParams[ctu], pic);
	} //ctu

#if SAO_ENCODING_CHOICE 
	Int picTempLayer = pic->getSlice(0)->getDepth();
	Int numLcusForSAOOff[NUM_SAO_COMPONENTS];
	numLcusForSAOOff[SAO_Y ] = numLcusForSAOOff[SAO_Cb]= numLcusForSAOOff[SAO_Cr]= 0;

	for (Int compIdx=0; compIdx<NUM_SAO_COMPONENTS; compIdx++)
	{
		for(Int ctu=0; ctu< m_numCTUsPic; ctu++)
		{
			if( reconParams[ctu][compIdx].modeIdc == SAO_MODE_OFF)
			{
				numLcusForSAOOff[compIdx]++;
			}
		}
	}
#if SAO_ENCODING_CHOICE_CHROMA
	for (Int compIdx=0; compIdx<NUM_SAO_COMPONENTS; compIdx++)
	{
		m_saoDisabledRate[compIdx][picTempLayer] = (Double)numLcusForSAOOff[compIdx]/(Double)m_numCTUsPic;
	}
#else
	if (picTempLayer == 0)
	{
		m_saoDisabledRate[SAO_Y][0] = (Double)(numLcusForSAOOff[SAO_Y]+numLcusForSAOOff[SAO_Cb]+numLcusForSAOOff[SAO_Cr])/(Double)(m_numCTUsPic*3);
	}
#endif                                              
#endif
}

选取EO和BO类别的最优模式


    首先要明确,色度分量的SAO模式可以和亮度分量的SAO模式不同,但是两个色度分量的SAO模式是一样的
选取EO、BO类别的最优模式的流程如下:
    1、对亮度分量进行操作
        (1)尝试SAO_OFF模式,并计算代价
        (2)尝试其余的SAO模式(EO、BO),选取出最优的模式
    2、对色度分量
        (1)尝试SAO_OFF模式,并计算代价
        (2)对两个色度分量尝试其他的SAO模式(EO、BO),选取出最优的模式
    3、通过步骤1和2得到了亮度和色度分量的最优SAO模式


选取EO和BO类别最优模式的函数

    流程如下:

    1、先确定亮度分量的最优模式

    2、再确定色度分量的最优模式

/*
** 利用率失真优化,确定SAO的EO/BO类别的最优模式
*/
Void TEncSampleAdaptiveOffset::deriveModeNewRDO(Int ctu, std::vector<SAOBlkParam*>& mergeList, Bool* sliceEnabled, SAOStatData*** blkStats, SAOBlkParam& modeParam, Double& modeNormCost, TEncSbac** cabacCoderRDO, Int inCabacLabel)
{
	Double minCost, cost;
	Int rate;
	UInt previousWrittenBits;
	
	Int64 dist[NUM_SAO_COMPONENTS], modeDist[NUM_SAO_COMPONENTS];
	SAOOffset testOffset[NUM_SAO_COMPONENTS];
	
	Int compIdx;
	Int invQuantOffset[MAX_NUM_SAO_CLASSES];

	// 初始化模式的失真
	modeDist[SAO_Y]= modeDist[SAO_Cb] = modeDist[SAO_Cr] = 0;

	//pre-encode merge flags
	// 预处理merge模式
	modeParam[SAO_Y ].modeIdc = SAO_MODE_OFF;
	m_pcRDGoOnSbacCoder->load(cabacCoderRDO[inCabacLabel]);
	m_pcRDGoOnSbacCoder->codeSAOBlkParam(modeParam, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true);
	m_pcRDGoOnSbacCoder->store(cabacCoderRDO[SAO_CABACSTATE_BLK_MID]);

	// 【=========对亮度分量进行操作,目的是选出亮度分量的最优SAO模式-begin============】
	//------ luma --------//
	compIdx = SAO_Y;
	//"off" case as initial cost
	// 先尝试SAO-OFF(即关闭SAO)模式
	modeParam[compIdx].modeIdc = SAO_MODE_OFF;
	m_pcRDGoOnSbacCoder->resetBits();
	m_pcRDGoOnSbacCoder->codeSAOOffsetParam(compIdx, modeParam[compIdx], sliceEnabled[compIdx]);
	modeDist[compIdx] = 0;
	// 计算代价
	minCost= m_lambda[compIdx]*((Double)m_pcRDGoOnSbacCoder->getNumberOfWrittenBits());
	m_pcRDGoOnSbacCoder->store(cabacCoderRDO[SAO_CABACSTATE_BLK_TEMP]);
	
	// 对SAO的EO、BO模式进行操作,选出最优的模式
	if(sliceEnabled[compIdx])
	{
		for(Int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++)
		{
			testOffset[compIdx].modeIdc = SAO_MODE_NEW;
			testOffset[compIdx].typeIdc = typeIdc;

			//derive coded offset
			deriveOffsets(ctu, compIdx, typeIdc, blkStats[ctu][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo);

			//inversed quantized offsets
			invertQuantOffsets(compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset);

			//get distortion
			dist[compIdx] = getDistortion(ctu, compIdx, testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctu][compIdx][typeIdc]);

			//get rate
			m_pcRDGoOnSbacCoder->load(cabacCoderRDO[SAO_CABACSTATE_BLK_MID]);
			m_pcRDGoOnSbacCoder->resetBits();
			m_pcRDGoOnSbacCoder->codeSAOOffsetParam(compIdx, testOffset[compIdx], sliceEnabled[compIdx]);
			rate = m_pcRDGoOnSbacCoder->getNumberOfWrittenBits();
			cost = (Double)dist[compIdx] + m_lambda[compIdx]*((Double)rate);
			if(cost < minCost)
			{
				minCost = cost;
				modeDist[compIdx] = dist[compIdx];
				modeParam[compIdx]= testOffset[compIdx];
				m_pcRDGoOnSbacCoder->store(cabacCoderRDO[SAO_CABACSTATE_BLK_TEMP]);
			}
		}
	}
	// 【=========对亮度分量进行操作,目的是选出亮度分量的最优SAO模式-end============】
	
	m_pcRDGoOnSbacCoder->load(cabacCoderRDO[SAO_CABACSTATE_BLK_TEMP]);
	m_pcRDGoOnSbacCoder->store(cabacCoderRDO[SAO_CABACSTATE_BLK_MID]);

	
	// 【=========对色度分量进行操作,目的是选出色度分量的最优模式-begin============】
	/*
	** 注意:色度分量的SAO模式可以和亮度分量的SAO模式不同
	** 但是两个色度分量的SAO模式是一样的
	*/
	//------ chroma --------//
	//"off" case as initial cost
	cost = 0;
	previousWrittenBits = 0;
	m_pcRDGoOnSbacCoder->resetBits();
	// 对两个色度分量,尝试SAO-OFF模式
	for (Int component = SAO_Cb; component < NUM_SAO_COMPONENTS; component++)
	{
		modeParam[component].modeIdc = SAO_MODE_OFF; 
		modeDist [component] = 0;

		m_pcRDGoOnSbacCoder->codeSAOOffsetParam(component, modeParam[component], sliceEnabled[component]);

		const UInt currentWrittenBits = m_pcRDGoOnSbacCoder->getNumberOfWrittenBits();
		cost += m_lambda[component] * (currentWrittenBits - previousWrittenBits);
		previousWrittenBits = currentWrittenBits;
	}

	minCost = cost;

	//doesn't need to store cabac status here since the whole CTU parameters will be re-encoded at the end of this function

	// 对两个色度分量尝试其他的SAO模式(EO、BO)
	for(Int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++)
	{
		m_pcRDGoOnSbacCoder->load(cabacCoderRDO[SAO_CABACSTATE_BLK_MID]);
		m_pcRDGoOnSbacCoder->resetBits();
		previousWrittenBits = 0;
		cost = 0;

		for(compIdx= SAO_Cb; compIdx< NUM_SAO_COMPONENTS; compIdx++)
		{
			if(!sliceEnabled[compIdx])
			{
				testOffset[compIdx].modeIdc = SAO_MODE_OFF;
				dist[compIdx]= 0;
				continue;
			}
			testOffset[compIdx].modeIdc = SAO_MODE_NEW;
			testOffset[compIdx].typeIdc = typeIdc;

			//derive offset & get distortion
			deriveOffsets(ctu, compIdx, typeIdc, blkStats[ctu][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo);
			invertQuantOffsets(compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset);
			dist[compIdx]= getDistortion(ctu, compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctu][compIdx][typeIdc]);

			m_pcRDGoOnSbacCoder->codeSAOOffsetParam(compIdx, testOffset[compIdx], sliceEnabled[compIdx]);

			const UInt currentWrittenBits = m_pcRDGoOnSbacCoder->getNumberOfWrittenBits();
			cost += dist[compIdx] + (m_lambda[compIdx] * (currentWrittenBits - previousWrittenBits));
			previousWrittenBits = currentWrittenBits;
		}

		// 选出最优的模式
		if(cost < minCost)
		{
			minCost = cost;
			for(compIdx= SAO_Cb; compIdx< NUM_SAO_COMPONENTS; compIdx++)
			{
				modeDist [compIdx] = dist      [compIdx];
				modeParam[compIdx] = testOffset[compIdx];
			}
		}
	}
	// 【=========对色度分量进行操作,目的是选出色度分量的最优模式-end============】

	// 到了这里已经选出了亮度和色度分量的最优SAO模式了
	
	//----- re-gen rate & normalized cost----//
	
	modeNormCost = 0;
	for(UInt component = SAO_Y; component < NUM_SAO_COMPONENTS; component++)
	{
		modeNormCost += (Double)modeDist[component] / m_lambda[component];
	}
	m_pcRDGoOnSbacCoder->load(cabacCoderRDO[inCabacLabel]);
	m_pcRDGoOnSbacCoder->resetBits();
	m_pcRDGoOnSbacCoder->codeSAOBlkParam(modeParam, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false);
	modeNormCost += (Double)m_pcRDGoOnSbacCoder->getNumberOfWrittenBits();

}



确定补偿值


    确定补偿值的流程如下:
    1、确定初始的补偿值
    2、调整补偿值,得到最优的补偿值

/*
** 得到最优的补偿值
** 该函数EO和BO共用
*/
Void TEncSampleAdaptiveOffset::deriveOffsets(Int ctu, Int compIdx, Int typeIdc, SAOStatData& statData, Int* quantOffsets, Int& typeAuxInfo)
{
	Int bitDepth = (compIdx== SAO_Y) ? g_bitDepthY : g_bitDepthC;
	Int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth-8);
	Int offsetTh = g_saoMaxOffsetQVal[compIdx];  //inclusive

	::memset(quantOffsets, 0, sizeof(Int)*MAX_NUM_SAO_CLASSES);

	//derive initial offsets 
	Int numClasses = (typeIdc == SAO_TYPE_BO)?((Int)NUM_SAO_BO_CLASSES):((Int)NUM_SAO_EO_CLASSES);
	// 遍历EO或者BO下的所有类型,确定初始的补偿值
	for(Int classIdx=0; classIdx< numClasses; classIdx++)
	{
		if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN)  ) 
		{
			continue; //offset will be zero
		}

		if(statData.count[classIdx] == 0)
		{
			continue; //offset will be zero
		}

		quantOffsets[classIdx] = (Int) xRoundIbdi(bitDepth, (Double)( statData.diff[classIdx]<<(bitDepth-8)) 
			/ 
			(Double)( statData.count[classIdx]<< m_offsetStepLog2[compIdx])
			);
		quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]);
	}

	// adjust offsets
	// 调整补偿值
	switch(typeIdc)
	{
	case SAO_TYPE_EO_0:
	case SAO_TYPE_EO_90:
	case SAO_TYPE_EO_135:
	case SAO_TYPE_EO_45:
		{
			Int64 classDist;
			Double classCost;
			for(Int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++)  
			{         
				if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0) quantOffsets[classIdx] =0;
				if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0) quantOffsets[classIdx] =0;
				if(classIdx==SAO_CLASS_EO_HALF_PEAK   && quantOffsets[classIdx] > 0) quantOffsets[classIdx] =0;
				if(classIdx==SAO_CLASS_EO_FULL_PEAK   && quantOffsets[classIdx] > 0) quantOffsets[classIdx] =0;

				// 如果补偿值不为0,那么进行调整,以得到最优的补偿值
				if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
				{
					quantOffsets[classIdx] = estIterOffset( typeIdc, classIdx, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh );
				}
			}

			typeAuxInfo =0;
		}
		break;
	case SAO_TYPE_BO:
		{
			Int64  distBOClasses[NUM_SAO_BO_CLASSES];
			Double costBOClasses[NUM_SAO_BO_CLASSES];
			::memset(distBOClasses, 0, sizeof(Int64)*NUM_SAO_BO_CLASSES);
			for(Int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++)
			{         
				costBOClasses[classIdx]= m_lambda[compIdx];
				// 如果补偿值不为0,那么进行调整,以得到最优的补偿值
				if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
				{
					quantOffsets[classIdx] = estIterOffset( typeIdc, classIdx, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], distBOClasses[classIdx], costBOClasses[classIdx], offsetTh );
				}
			}

			//decide the starting band index
			Double minCost = MAX_DOUBLE, cost;
			for(Int band=0; band< NUM_SAO_BO_CLASSES- 4+ 1; band++) 
			{
				cost  = costBOClasses[band  ];
				cost += costBOClasses[band+1];
				cost += costBOClasses[band+2];
				cost += costBOClasses[band+3];

				if(cost < minCost)
				{
					minCost = cost;
					typeAuxInfo = band;
				}
			}
			//clear those unused classes
			Int clearQuantOffset[NUM_SAO_BO_CLASSES];
			::memset(clearQuantOffset, 0, sizeof(Int)*NUM_SAO_BO_CLASSES);
			for(Int i=0; i< 4; i++) 
			{
				Int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES;
				clearQuantOffset[band] = quantOffsets[band];
			}
			::memcpy(quantOffsets, clearQuantOffset, sizeof(Int)*NUM_SAO_BO_CLASSES);        
		}
		break;
	default:
		{
			printf("Not a supported type");
			assert(0);
			exit(-1);
		}

	}
}



对补偿值进行调整


    对补偿值调整的目的是得到最优的补偿值

inline Int TEncSampleAdaptiveOffset::estIterOffset(Int typeIdx, Int classIdx, Double lambda, Int offsetInput, Int64 count, Int64 diffSum, Int shift, Int bitIncrease, Int64& bestDist, Double& bestCost, Int offsetTh )
{
	Int iterOffset, tempOffset;
	Int64 tempDist, tempRate;
	Double tempCost, tempMinCost;
	Int offsetOutput = 0;
	iterOffset = offsetInput;
	// Assuming sending quantized value 0 results in zero offset and sending the value zero needs 1 bit. entropy coder can be used to measure the exact rate here. 
	tempMinCost = lambda; 
	while (iterOffset != 0)
	{
		// Calculate the bits required for signaling the offset
		tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((Int)iterOffset)+2) : (abs((Int)iterOffset)+1); 
		if (abs((Int)iterOffset)==offsetTh) //inclusive 
		{  
			tempRate --;
		}
		// Do the dequantization before distortion calculation
		tempOffset  = iterOffset << bitIncrease;
		tempDist    = estSaoDist( count, tempOffset, diffSum, shift);
		tempCost    = ((Double)tempDist + lambda * (Double) tempRate);
		if(tempCost < tempMinCost)
		{
			tempMinCost = tempCost;
			offsetOutput = iterOffset;
			bestDist = tempDist;
			bestCost = tempCost;
		}
		iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1);
	}
	return offsetOutput;
}




选取merge类别的最优模式


    在SAO的merge模式中,merge是一个大类,它包含两种方式:
    1、与左边的CTU的SAO参数进行合并
    2、与上面的CTU的SAO参数进行合并
    因此,首先应该获取候选的merge模式列表,然后遍历候选列表,选取最优的合并方式。另外,因为merge模式是和相邻CTU贡献SAO参数,因此不需要计算补偿值。


获取merge模式的候选列表


/*
** 获取SAO的merge模式的候选列表
*/
Int TComSampleAdaptiveOffset::getMergeList(TComPic* pic, Int ctu, SAOBlkParam* blkParams, std::vector<SAOBlkParam*>& mergeList)
{
	Int ctuX = ctu % m_numCTUInWidth;
	Int ctuY = ctu / m_numCTUInWidth;
	Int mergedCTUPos;
	Int numValidMergeCandidates = 0;

	// 遍历所有的merge模式
	for(Int mergeType=0; mergeType< NUM_SAO_MERGE_TYPES; mergeType++)
	{
		SAOBlkParam* mergeCandidate = NULL;

		switch(mergeType)
		{
		case SAO_MERGE_ABOVE: // 和上面的CTU合并
			{
				if(ctuY > 0)
				{
					mergedCTUPos = ctu- m_numCTUInWidth;
					if( pic->getSAOMergeAvailability(ctu, mergedCTUPos) )
					{
						mergeCandidate = &(blkParams[mergedCTUPos]);
					}
				}
			}
			break;
		case SAO_MERGE_LEFT: // 和左边的CTU合并
			{
				if(ctuX > 0)
				{
					mergedCTUPos = ctu- 1;
					if( pic->getSAOMergeAvailability(ctu, mergedCTUPos) )
					{
						mergeCandidate = &(blkParams[mergedCTUPos]);
					}
				}
			}
			break;
		default:
			{
				printf("not a supported merge type");
				assert(0);
				exit(-1);
			}
		}

		// 把候选模式压入列表中
		mergeList.push_back(mergeCandidate);
		if (mergeCandidate != NULL)
		{
			numValidMergeCandidates++;
		}
	}

	return numValidMergeCandidates;
}



计算merge模式的最优合并方式

    遍历候选列表,通过率失真优化的方式选取最优的模式

/*
** 利用率失真优化,确定SAO的merge模式的最优合并方式
*/
Void TEncSampleAdaptiveOffset::deriveModeMergeRDO(Int ctu, std::vector<SAOBlkParam*>& mergeList, Bool* sliceEnabled, SAOStatData*** blkStats, SAOBlkParam& modeParam, Double& modeNormCost, TEncSbac** cabacCoderRDO, Int inCabacLabel)
{
	Int mergeListSize = (Int)mergeList.size();
	modeNormCost = MAX_DOUBLE;

	Double cost;
	SAOBlkParam testBlkParam;

	// 遍历merge模式的候选列表
	for(Int mergeType=0; mergeType< mergeListSize; mergeType++)
	{
		if(mergeList[mergeType] == NULL)
		{
			continue;
		}

		testBlkParam = *(mergeList[mergeType]);
		//normalized distortion
		Double normDist=0;
		
		// 依次处理三个分量
		for(Int compIdx=0; compIdx< NUM_SAO_COMPONENTS; compIdx++)
		{
			testBlkParam[compIdx].modeIdc = SAO_MODE_MERGE;
			testBlkParam[compIdx].typeIdc = mergeType;

			SAOOffset& mergedOffsetParam = (*(mergeList[mergeType]))[compIdx];

			if( mergedOffsetParam.modeIdc != SAO_MODE_OFF)
			{
				//offsets have been reconstructed. Don't call inversed quantization function.
				// 计算失真
				normDist += (((Double)getDistortion(ctu, compIdx, mergedOffsetParam.typeIdc, mergedOffsetParam.typeAuxInfo, mergedOffsetParam.offset, blkStats[ctu][compIdx][mergedOffsetParam.typeIdc]))
					/m_lambda[compIdx]
				);
			}

		}

		//rate
		m_pcRDGoOnSbacCoder->load(cabacCoderRDO[inCabacLabel]);
		m_pcRDGoOnSbacCoder->resetBits();
		m_pcRDGoOnSbacCoder->codeSAOBlkParam(testBlkParam, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false);
		Int rate = m_pcRDGoOnSbacCoder->getNumberOfWrittenBits();

		cost = normDist+(Double)rate;

		// 选取最优模式
		if(cost < modeNormCost)
		{
			modeNormCost = cost;
			modeParam    = testBlkParam;
			m_pcRDGoOnSbacCoder->store(cabacCoderRDO[SAO_CABACSTATE_BLK_TEMP]);
		}
	}

	m_pcRDGoOnSbacCoder->load(cabacCoderRDO[SAO_CABACSTATE_BLK_TEMP]);

}

通过比较得到所有模式中最优的模式


    在decideBlkParams函数中,得到了EO、BO的最优模式以及merge的最优模式之后,要对他们的率失真代价进行比较,来得到它们之中最优的一个。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值