2013-04-16 08:23:03 +00:00
/**
* HEVC Encoder
* - Marko Viitanen ( fador at iki . fi ) , Tampere University of Technology , Department of Pervasive Computing .
*/
/*! \file search.c
\ brief searching
\ author Marko Viitanen
\ date 2013 - 04
Search related functions
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "global.h"
# include "config.h"
# include "bitstream.h"
# include "picture.h"
2013-09-16 13:37:24 +00:00
# include "encoder.h"
2013-04-16 12:10:43 +00:00
# include "intra.h"
2013-09-05 12:02:53 +00:00
# include "inter.h"
2013-04-16 08:23:03 +00:00
# include "filter.h"
2013-04-16 12:10:43 +00:00
# include "search.h"
2013-09-18 08:07:48 +00:00
# include "debug.h"
2013-04-16 12:10:43 +00:00
2013-09-16 14:34:20 +00:00
2013-09-16 16:18:24 +00:00
// Temporarily for debugging.
# define USE_INTRA_IN_P 0
2013-09-18 08:07:48 +00:00
# define RENDER_CU 1
2013-09-16 16:18:24 +00:00
2013-09-16 14:34:20 +00:00
/**
*
*
* pic :
* pic_data : picture color data starting from the block MV is being searched for .
* ref_data : picture color data starting from the beginning of reference pic .
* cur_cu :
*/
2013-09-16 15:58:28 +00:00
void search_motion_vector ( picture * pic , uint8_t * pic_data , uint8_t * ref_data , CU_info * cur_cu , unsigned step , int orig_x , int orig_y , int x , int y , unsigned depth )
2013-09-16 14:34:20 +00:00
{
// TODO: Inter: Handle non-square blocks.
2013-09-16 15:58:28 +00:00
unsigned block_width = CU_WIDTH_FROM_DEPTH ( depth ) ;
2013-09-16 14:34:20 +00:00
unsigned block_height = block_width ;
2013-09-16 15:58:28 +00:00
unsigned cost ;
2013-09-16 14:34:20 +00:00
// TODO: Inter: Calculating error outside picture borders.
// This prevents choosing vectors that need interpolating of borders to work.
2013-09-16 15:58:28 +00:00
if ( orig_x + x < 0 | | orig_y + y < 0 | | orig_x + x > pic - > width - block_width | | orig_y + y > pic - > height - block_height ) return ;
2013-09-16 14:34:20 +00:00
2013-09-16 15:58:28 +00:00
cost = SAD ( pic_data , & ref_data [ ( orig_y + y ) * pic - > width + ( orig_x + x ) ] , block_width , block_height , pic - > width ) + 1 ;
if ( cost < cur_cu - > inter . cost ) {
cur_cu - > inter . cost = cost ;
cur_cu - > inter . mv [ 0 ] = x < < 2 ;
cur_cu - > inter . mv [ 1 ] = y < < 2 ;
}
2013-09-16 14:34:20 +00:00
step / = 2 ;
if ( step > 0 ) {
2013-09-16 15:58:28 +00:00
search_motion_vector ( pic , pic_data , ref_data , cur_cu , step , orig_x , orig_y , x , y - step , depth ) ;
search_motion_vector ( pic , pic_data , ref_data , cur_cu , step , orig_x , orig_y , x - step , y , depth ) ;
search_motion_vector ( pic , pic_data , ref_data , cur_cu , step , orig_x , orig_y , x + step , y , depth ) ;
search_motion_vector ( pic , pic_data , ref_data , cur_cu , step , orig_x , orig_y , x , y + step , depth ) ;
2013-09-16 14:34:20 +00:00
}
}
2013-04-18 11:04:15 +00:00
void search_buildReferenceBorder ( picture * pic , int32_t xCtb , int32_t yCtb , int16_t outwidth , int16_t * dst , int32_t dststride , int8_t chroma )
{
int32_t leftColumn ; /*!< left column iterator */
int16_t val ; /*!< variable to store extrapolated value */
int32_t i ; /*!< index iterator */
int16_t dcVal = 1 < < ( g_bitDepth - 1 ) ; /*!< default predictor value */
int32_t topRow ; /*!< top row iterator */
int32_t srcWidth = ( pic - > width > > ( chroma ? 1 : 0 ) ) ; /*!< source picture width */
int32_t srcHeight = ( pic - > height > > ( chroma ? 1 : 0 ) ) ; /*!< source picture height */
uint8_t * srcPic = ( ! chroma ) ? pic - > yData : ( ( chroma = = 1 ) ? pic - > uData : pic - > vData ) ; /*!< input picture pointer */
int16_t SCU_width = LCU_WIDTH > > ( MAX_DEPTH + ( chroma ? 1 : 0 ) ) ; /*!< Smallest Coding Unit width */
uint8_t * srcShifted = & srcPic [ xCtb * SCU_width + ( yCtb * SCU_width ) * srcWidth ] ; /*!< input picture pointer shifted to start from the left-top corner of the current block */
2013-06-12 12:41:57 +00:00
int32_t width_in_SCU = pic - > width_in_LCU < < MAX_DEPTH ; /*!< picture width in SCU */
2013-04-18 11:04:15 +00:00
/* Fill left column */
if ( xCtb )
{
/* Loop SCU's */
for ( leftColumn = 1 ; leftColumn < outwidth / SCU_width ; leftColumn + + )
{
2013-04-19 07:56:40 +00:00
/* If over the picture height or block not yet searched, stop */
2013-04-18 11:04:15 +00:00
if ( ( yCtb + leftColumn ) * SCU_width > = srcHeight | | pic - > CU [ 0 ] [ xCtb - 1 + ( yCtb + leftColumn ) * width_in_SCU ] . type = = CU_NOTSET )
{
break ;
}
}
/* Copy the pixels to output */
for ( i = 0 ; i < leftColumn * SCU_width - 1 ; i + + )
{
dst [ ( i + 1 ) * dststride ] = srcShifted [ i * srcWidth - 1 ] ;
}
/* if the loop was not completed, extrapolate the last pixel pushed to output */
if ( leftColumn ! = outwidth / SCU_width )
{
val = srcShifted [ ( leftColumn * SCU_width - 1 ) * srcWidth - 1 ] ;
for ( i = ( leftColumn * SCU_width ) ; i < outwidth ; i + + )
{
dst [ i * dststride ] = val ;
}
}
}
/* If left column not available, copy from toprow or use the default predictor */
else
{
val = yCtb ? srcShifted [ - srcWidth ] : dcVal ;
for ( i = 0 ; i < outwidth ; i + + )
{
dst [ i * dststride ] = val ;
}
}
if ( yCtb )
{
/* Loop top SCU's */
for ( topRow = 1 ; topRow < outwidth / SCU_width ; topRow + + )
{
if ( ( xCtb + topRow ) * SCU_width > = srcWidth | | pic - > CU [ 0 ] [ xCtb + topRow + ( yCtb - 1 ) * width_in_SCU ] . type = = CU_NOTSET )
{
break ;
}
}
for ( i = 0 ; i < topRow * SCU_width - 1 ; i + + )
{
dst [ i + 1 ] = srcShifted [ i - srcWidth ] ;
}
if ( topRow ! = outwidth / SCU_width )
{
val = srcShifted [ ( topRow * SCU_width ) - srcWidth - 1 ] ;
for ( i = ( topRow * SCU_width ) ; i < outwidth ; i + + )
{
dst [ i ] = val ;
}
}
}
else
{
val = xCtb ? srcShifted [ - 1 ] : dcVal ;
for ( i = 1 ; i < outwidth ; i + + )
{
dst [ i ] = val ;
}
}
/* Topleft corner */
dst [ 0 ] = ( xCtb & & yCtb ) ? srcShifted [ - srcWidth - 1 ] : dst [ dststride ] ;
}
2013-04-16 12:10:43 +00:00
void search_tree ( encoder_control * encoder , uint16_t xCtb , uint16_t yCtb , uint8_t depth )
{
uint8_t border_x = ( ( encoder - > in . width ) < ( xCtb * ( LCU_WIDTH > > MAX_DEPTH ) + ( LCU_WIDTH > > depth ) ) ) ? 1 : 0 ;
uint8_t border_y = ( ( encoder - > in . height ) < ( yCtb * ( LCU_WIDTH > > MAX_DEPTH ) + ( LCU_WIDTH > > depth ) ) ) ? 1 : 0 ;
2013-06-03 11:22:50 +00:00
uint8_t border_split_x = ( ( encoder - > in . width ) < ( ( xCtb + 1 ) * ( LCU_WIDTH > > MAX_DEPTH ) + ( LCU_WIDTH > > ( depth + 1 ) ) ) ) ? 0 : 1 ;
uint8_t border_split_y = ( ( encoder - > in . height ) < ( ( yCtb + 1 ) * ( LCU_WIDTH > > MAX_DEPTH ) + ( LCU_WIDTH > > ( depth + 1 ) ) ) ) ? 0 : 1 ;
2013-04-16 12:10:43 +00:00
uint8_t border = border_x | border_y ; /*!< are we in any border CU */
2013-09-12 13:28:40 +00:00
CU_info * cur_CU = & encoder - > in . cur_pic - > CU [ depth ] [ xCtb + yCtb * ( encoder - > in . width_in_LCU < < MAX_DEPTH ) ] ;
2013-04-16 12:10:43 +00:00
2013-04-17 14:08:52 +00:00
cur_CU - > intra . cost = 0xffffffff ;
2013-04-18 11:37:56 +00:00
cur_CU - > inter . cost = 0xffffffff ;
2013-04-16 12:10:43 +00:00
/* Force split on border */
if ( depth ! = MAX_DEPTH )
{
if ( border )
{
/* Split blocks and remember to change x and y block positions */
uint8_t change = 1 < < ( MAX_DEPTH - 1 - depth ) ;
SET_SPLITDATA ( cur_CU , 1 ) ;
search_tree ( encoder , xCtb , yCtb , depth + 1 ) ;
2013-06-03 11:22:50 +00:00
if ( ! border_x | | border_split_x )
2013-04-16 12:10:43 +00:00
{
search_tree ( encoder , xCtb + change , yCtb , depth + 1 ) ;
}
2013-06-03 11:22:50 +00:00
if ( ! border_y | | border_split_y )
2013-04-16 12:10:43 +00:00
{
search_tree ( encoder , xCtb , yCtb + change , depth + 1 ) ;
}
2013-06-03 11:22:50 +00:00
if ( ! border | | ( border_split_x & & border_split_y ) )
2013-04-16 12:10:43 +00:00
{
search_tree ( encoder , xCtb + change , yCtb + change , depth + 1 ) ;
}
/* We don't need to do anything else here */
return ;
}
}
2013-04-19 07:56:40 +00:00
/* INTER SEARCH */
2013-09-12 13:28:40 +00:00
if ( encoder - > in . cur_pic - > slicetype ! = SLICE_I ) // && (xCtb == 0) && yCtb == 0)
2013-04-16 12:10:43 +00:00
{
2013-09-11 11:32:20 +00:00
//if(depth >= MIN_SEARCH_DEPTH)
2013-04-19 07:56:40 +00:00
{
2013-09-05 12:02:53 +00:00
/* Motion estimation on P-frame */
2013-09-12 13:28:40 +00:00
if ( encoder - > in . cur_pic - > slicetype ! = SLICE_B )
2013-09-05 12:02:53 +00:00
{
2013-04-16 12:10:43 +00:00
2013-09-05 12:02:53 +00:00
}
2013-04-24 13:49:47 +00:00
2013-09-11 11:32:20 +00:00
{
2013-09-16 14:34:20 +00:00
unsigned mv [ 2 ] = { 0 , 0 } ; // TODO: Take initial MV from adjacent blocks.
picture * cur_pic = encoder - > in . cur_pic ;
picture * ref_pic = encoder - > ref - > pics [ 0 ] ;
2013-09-16 15:58:28 +00:00
int x = xCtb * CU_MIN_SIZE_PIXELS ;
int y = yCtb * CU_MIN_SIZE_PIXELS ;
uint8_t * cur_data = & cur_pic - > yData [ ( y * cur_pic - > width ) + x ] ;
search_motion_vector ( cur_pic , cur_data , ref_pic - > yData , cur_CU , 8 , x , y , 0 , 0 , depth ) ;
2013-09-11 11:32:20 +00:00
}
2013-09-16 14:34:20 +00:00
cur_CU - > type = CU_INTER ;
2013-09-05 12:02:53 +00:00
cur_CU - > inter . mv_dir = 1 ;
2013-09-12 13:28:40 +00:00
inter_setBlockMode ( encoder - > in . cur_pic , xCtb , yCtb , depth , cur_CU ) ;
2013-09-05 12:02:53 +00:00
}
2013-04-16 12:10:43 +00:00
}
/* INTRA SEARCH */
2013-09-16 16:18:24 +00:00
if ( depth > = MIN_SEARCH_DEPTH & & ( encoder - > in . cur_pic - > slicetype = = SLICE_I | | USE_INTRA_IN_P ) )
2013-04-16 12:10:43 +00:00
{
2013-04-18 11:04:15 +00:00
int x = 0 , y = 0 ;
2013-09-12 13:28:40 +00:00
uint8_t * base = & encoder - > in . cur_pic - > yData [ xCtb * ( LCU_WIDTH > > ( MAX_DEPTH ) ) + ( yCtb * ( LCU_WIDTH > > ( MAX_DEPTH ) ) ) * encoder - > in . width ] ;
2013-04-16 12:10:43 +00:00
uint32_t width = LCU_WIDTH > > depth ;
/* INTRAPREDICTION */
2013-05-22 14:27:15 +00:00
int16_t pred [ LCU_WIDTH * LCU_WIDTH + 1 ] ;
2013-04-16 12:10:43 +00:00
int16_t rec [ ( LCU_WIDTH * 2 + 8 ) * ( LCU_WIDTH * 2 + 8 ) ] ;
2013-08-02 13:35:30 +00:00
int16_t * recShift = & rec [ ( LCU_WIDTH > > ( depth ) ) * 2 + 8 + 1 ] ;
2013-05-22 14:27:15 +00:00
//int16_t *pred = (int16_t*)malloc(LCU_WIDTH*LCU_WIDTH*sizeof(int16_t));
//int16_t *rec = (int16_t*)malloc((LCU_WIDTH*2+8)*(LCU_WIDTH*2+8)*sizeof(int16_t));
2013-04-17 14:08:52 +00:00
2013-04-19 07:56:40 +00:00
/* Build reconstructed block to use in prediction with extrapolated borders */
2013-09-12 13:28:40 +00:00
search_buildReferenceBorder ( encoder - > in . cur_pic , xCtb , yCtb , ( LCU_WIDTH > > ( depth ) ) * 2 + 8 , rec , ( LCU_WIDTH > > ( depth ) ) * 2 + 8 , 0 ) ;
cur_CU - > intra . mode = ( uint8_t ) intra_prediction ( encoder - > in . cur_pic - > yData , encoder - > in . width , recShift , ( LCU_WIDTH > > ( depth ) ) * 2 + 8 , xCtb * ( LCU_WIDTH > > ( MAX_DEPTH ) ) , yCtb * ( LCU_WIDTH > > ( MAX_DEPTH ) ) , width , pred , width , & cur_CU - > intra . cost ) ;
2013-05-22 14:27:15 +00:00
//free(pred);
//free(rec);
2013-04-16 12:10:43 +00:00
}
/* Split and search to max_depth */
2013-04-17 14:08:52 +00:00
if ( depth ! = MAX_SEARCH_DEPTH )
2013-04-16 12:10:43 +00:00
{
/* Split blocks and remember to change x and y block positions */
uint8_t change = 1 < < ( MAX_DEPTH - 1 - depth ) ;
search_tree ( encoder , xCtb , yCtb , depth + 1 ) ;
search_tree ( encoder , xCtb + change , yCtb , depth + 1 ) ;
search_tree ( encoder , xCtb , yCtb + change , depth + 1 ) ;
search_tree ( encoder , xCtb + change , yCtb + change , depth + 1 ) ;
}
}
uint32_t search_best_mode ( encoder_control * encoder , uint16_t xCtb , uint16_t yCtb , uint8_t depth )
{
2013-09-12 13:28:40 +00:00
CU_info * cur_CU = & encoder - > in . cur_pic - > CU [ depth ] [ xCtb + yCtb * ( encoder - > in . width_in_LCU < < MAX_DEPTH ) ] ;
2013-09-05 12:02:53 +00:00
uint32_t bestIntraCost = cur_CU - > intra . cost ;
uint32_t bestInterCost = cur_CU - > inter . cost ;
uint32_t bestCost = 0 ;
2013-04-16 12:10:43 +00:00
uint32_t cost = 0 ;
2013-09-09 11:37:58 +00:00
uint32_t lambdaCost = ( 4 * g_lambda_cost [ encoder - > QP ] ) < < 4 ; //<<5; //TODO: Correct cost calculation
2013-09-05 12:02:53 +00:00
2013-04-16 12:10:43 +00:00
/* Split and search to max_depth */
2013-09-09 11:37:58 +00:00
if ( depth ! = MAX_SEARCH_DEPTH ) {
2013-04-16 12:10:43 +00:00
/* Split blocks and remember to change x and y block positions */
2013-04-17 14:08:52 +00:00
uint8_t change = 1 < < ( MAX_DEPTH - 1 - depth ) ;
cost = search_best_mode ( encoder , xCtb , yCtb , depth + 1 ) ;
2013-04-16 12:10:43 +00:00
cost + = search_best_mode ( encoder , xCtb + change , yCtb , depth + 1 ) ;
2013-04-17 14:08:52 +00:00
cost + = search_best_mode ( encoder , xCtb , yCtb + change , depth + 1 ) ;
2013-04-16 12:10:43 +00:00
cost + = search_best_mode ( encoder , xCtb + change , yCtb + change , depth + 1 ) ;
2013-04-19 07:56:40 +00:00
/* We split if the cost is better (0 cost -> not checked) */
2013-09-11 11:32:20 +00:00
if ( cost ! = 0 & & ( bestIntraCost ! = 0 & & cost + lambdaCost < bestIntraCost ) & &
2013-09-12 13:28:40 +00:00
( bestInterCost ! = 0 & & cost + lambdaCost < bestInterCost & & encoder - > in . cur_pic - > slicetype ! = SLICE_I ) )
2013-04-16 12:10:43 +00:00
{
2013-04-19 07:56:40 +00:00
/* Set split to 1 */
2013-09-12 13:28:40 +00:00
picture_setBlockSplit ( encoder - > in . cur_pic , xCtb , yCtb , depth , 1 ) ;
2013-04-17 14:08:52 +00:00
bestCost = cost + lambdaCost ;
2013-04-16 12:10:43 +00:00
}
2013-09-05 12:02:53 +00:00
/* Else, check if inter cost is smaller or the same as intra */
2013-09-12 13:28:40 +00:00
else if ( bestInterCost ! = 0 & & ( bestInterCost < = bestIntraCost | | bestIntraCost = = 0 ) & & encoder - > in . cur_pic - > slicetype ! = SLICE_I )
2013-09-05 12:02:53 +00:00
{
/* Set split to 0 and mode to inter.mode */
2013-09-12 13:28:40 +00:00
picture_setBlockSplit ( encoder - > in . cur_pic , xCtb , yCtb , depth , 0 ) ;
inter_setBlockMode ( encoder - > in . cur_pic , xCtb , yCtb , depth , cur_CU ) ;
2013-09-05 12:02:53 +00:00
bestCost = bestInterCost ;
}
2013-04-17 14:08:52 +00:00
/* Else, dont split and recursively set block mode */
2013-04-16 12:10:43 +00:00
else
{
2013-04-19 07:56:40 +00:00
/* Set split to 0 and mode to intra.mode */
2013-09-12 13:28:40 +00:00
picture_setBlockSplit ( encoder - > in . cur_pic , xCtb , yCtb , depth , 0 ) ;
intra_setBlockMode ( encoder - > in . cur_pic , xCtb , yCtb , depth , cur_CU - > intra . mode ) ;
2013-09-05 12:02:53 +00:00
bestCost = bestIntraCost ;
2013-04-16 12:10:43 +00:00
}
}
2013-09-12 13:28:40 +00:00
else if ( bestInterCost ! = 0 & & ( bestInterCost < = bestIntraCost | | bestIntraCost = = 0 ) & & encoder - > in . cur_pic - > slicetype ! = SLICE_I )
2013-09-05 12:02:53 +00:00
{
/* Set split to 0 and mode to inter.mode */
2013-09-12 13:28:40 +00:00
picture_setBlockSplit ( encoder - > in . cur_pic , xCtb , yCtb , depth , 0 ) ;
inter_setBlockMode ( encoder - > in . cur_pic , xCtb , yCtb , depth , cur_CU ) ;
2013-09-05 12:02:53 +00:00
bestCost = bestInterCost ;
}
2013-04-17 14:08:52 +00:00
else
{
2013-04-19 07:56:40 +00:00
/* Set split to 0 and mode to intra.mode */
2013-09-12 13:28:40 +00:00
picture_setBlockSplit ( encoder - > in . cur_pic , xCtb , yCtb , depth , 0 ) ;
intra_setBlockMode ( encoder - > in . cur_pic , xCtb , yCtb , depth , cur_CU - > intra . mode ) ;
2013-09-05 12:02:53 +00:00
bestCost = bestIntraCost ;
2013-04-17 14:08:52 +00:00
}
2013-04-16 12:10:43 +00:00
return bestCost ;
}
void search_slice_data ( encoder_control * encoder )
{
2013-04-19 07:56:40 +00:00
int16_t xCtb , yCtb ;
2013-09-18 08:07:48 +00:00
FILE * fp = 0 , * fp2 = 0 ;
if ( RENDER_CU & & encoder - > frame = = 1 ) {
fp = open_cu_file ( " cu_search.html " ) ;
fp2 = open_cu_file ( " cu_best.html " ) ;
}
2013-04-16 12:10:43 +00:00
/* Loop through every LCU in the slice */
for ( yCtb = 0 ; yCtb < encoder - > in . height_in_LCU ; yCtb + + )
{
for ( xCtb = 0 ; xCtb < encoder - > in . width_in_LCU ; xCtb + + )
{
uint8_t depth = 0 ;
/* Recursive function for looping through all the sub-blocks */
search_tree ( encoder , xCtb < < MAX_DEPTH , yCtb < < MAX_DEPTH , depth ) ;
2013-09-18 08:07:48 +00:00
if ( RENDER_CU & & encoder - > frame = = 1 ) render_cu_file ( encoder , depth , xCtb < < MAX_DEPTH , yCtb < < MAX_DEPTH , fp ) ;
2013-04-16 12:10:43 +00:00
/* Decide actual coding modes */
search_best_mode ( encoder , xCtb < < MAX_DEPTH , yCtb < < MAX_DEPTH , depth ) ;
2013-09-18 08:07:48 +00:00
if ( RENDER_CU & & encoder - > frame = = 1 ) render_cu_file ( encoder , depth , xCtb < < MAX_DEPTH , yCtb < < MAX_DEPTH , fp2 ) ;
2013-04-16 12:10:43 +00:00
}
}
2013-09-18 08:07:48 +00:00
if ( RENDER_CU & & fp ) {
close_cu_file ( fp ) ;
fp = 0 ;
}
if ( RENDER_CU & & fp2 ) {
close_cu_file ( fp2 ) ;
fp2 = 0 ;
}
2013-09-16 14:34:20 +00:00
}