/***************************************************************** * tmn (TMN encoder) * Copyright (C) 1995 Telenor R&D * Karl Olav Lillevold * * *****************************************************************/ #include"sim.h" /********************************************************************** * * Name: MotionEstimation * Description: Estimate all motionvectors for one MB * * Input: pointers to current an previous image, * pointers to current slice and current MB * Returns: * Side effects: motion vector imformation in MB changed * * Date: 930118 Author: Karl.Lillevold@nta.no * 940203 Mod. to use PGB's faster search * 941208 Mod to use spiral search from mpeg2encode * ***********************************************************************/ void MotionEstimation(unsigned char *curr, unsigned char *prev, int x_curr, int y_curr, int seek_dist, MotionVector *MV[6][MBR+1][MBC+2]) { int Min_FRAME[5],h_length, v_length; MotionVector MV_FRAME[5]; unsigned char *act_block,*aa,*ii,*search_area; int sxy,i,k,j,l,ihigh,ilow,jhigh,jlow; int xmax,ymax,block,sad,lx; int x_adv_curr, y_adv_curr,xvec,yvec;; sxy = seek_dist; xmax = pels; ymax = lines; lx = (mv_outside_frame ? pels + 32 : pels); ilow = x_curr - sxy; ihigh = x_curr + sxy; jlow = y_curr - sxy; jhigh = y_curr + sxy; if (!mv_outside_frame) { if (ilow<0) ilow = 0; if (ihigh>xmax-16) ihigh = xmax-16; if (jlow<0) jlow = 0; if (jhigh>ymax-16) jhigh = ymax-16; } h_length = ihigh - ilow + 16; v_length = jhigh - jlow + 16; act_block = LoadArea(curr, x_curr, y_curr, 16, 16, pels); search_area = LoadArea(prev, ilow, jlow, h_length, v_length, lx); for (k = 0; k < 5; k++) Min_FRAME[k] = INT_MAX; ii = search_area + (x_curr-ilow) + (y_curr-jlow)*h_length; Min_FRAME[0] = SAD_Macroblock(ii, act_block, h_length, Min_FRAME[0]) - PREF_NULL_VEC; MV_FRAME[0].x = 0; MV_FRAME[0].y = 0; /* Spiral search */ for (l = 1; l <= sxy; l++) { i = x_curr - l; j = y_curr - l; for (k = 0; k < 8*l; k++) { if (i>=ilow && i<=ihigh && j>=jlow && j<=jhigh) { /* 16x16 integer pel MV */ ii = search_area + (i-ilow) + (j-jlow)*h_length; sad = SAD_Macroblock(ii, act_block, h_length, Min_FRAME[0]); if (sad < Min_FRAME[0]) { MV_FRAME[0].x = i - x_curr; MV_FRAME[0].y = j - y_curr; Min_FRAME[0] = sad; } } if (k<2*l) i++; else if (k<4*l) j++; else if (k<6*l) i--; else j--; } } if (advanced) { /* Center the 8x8 search around the 16x16 vector. This is different than in TMN5 where the 8x8 search is also a full search. The reasons behind this is (i) it is faster, and (ii) it generally gives better results */ xvec = MV_FRAME[0].x; yvec = MV_FRAME[0].y; if (xvec > 15 - DEF_8X8_WIN) { xvec = 15 - DEF_8X8_WIN ;} if (yvec > 15 - DEF_8X8_WIN) { yvec = 15 - DEF_8X8_WIN ;} if (xvec < -15 + DEF_8X8_WIN) { xvec = -15 + DEF_8X8_WIN ;} if (yvec < -15 + DEF_8X8_WIN) { yvec = -15 + DEF_8X8_WIN ;} x_adv_curr = x_curr + xvec; y_adv_curr = y_curr + yvec; sxy = DEF_8X8_WIN; ilow = x_adv_curr - sxy; ihigh = x_adv_curr + sxy; jlow = y_adv_curr - sxy; jhigh = y_adv_curr + sxy; h_length = ihigh - ilow + 16; v_length = jhigh - jlow + 16; free(search_area); search_area = LoadArea(prev, ilow, jlow, h_length, v_length, lx); for (block = 0; block < 4; block++) { ii = search_area + (x_adv_curr-ilow) + ((block&1)<<3) + (y_adv_curr-jlow + ((block&2)<<2) )*h_length; aa = act_block + ((block&1)<<3) + ((block&2)<<2)*16; Min_FRAME[block+1] = SAD_Block(ii, aa, h_length,Min_FRAME[block+1]); MV_FRAME[block+1].x = MV_FRAME[0].x; MV_FRAME[block+1].y = MV_FRAME[0].y; } /* Spiral search */ for (l = 1; l <= sxy; l++) { i = x_adv_curr - l; j = y_adv_curr - l; for (k = 0; k < 8*l; k++) { if (i>=ilow && i<=ihigh && j>=jlow && j<=jhigh) { /* 8x8 integer pel MVs */ for (block = 0; block < 4; block++) { ii = search_area + (i-ilow) + ((block&1)<<3) + (j-jlow + ((block&2)<<2) )*h_length; aa = act_block + ((block&1)<<3) + ((block&2)<<2)*16; sad = SAD_Block(ii, aa, h_length, Min_FRAME[block+1]); if (sad < Min_FRAME[block+1]) { MV_FRAME[block+1].x = i - x_curr; MV_FRAME[block+1].y = j - y_curr; Min_FRAME[block+1] = sad; } } } if (k<2*l) i++; else if (k<4*l) j++; else if (k<6*l) i--; else j--; } } } i = x_curr/MB_SIZE+1; j = y_curr/MB_SIZE+1; if (!advanced) { MV[0][j][i]->x = MV_FRAME[0].x; MV[0][j][i]->y = MV_FRAME[0].y; MV[0][j][i]->min_error = Min_FRAME[0]; } else { for (k = 0; k < 5; k++) { MV[k][j][i]->x = MV_FRAME[k].x; MV[k][j][i]->y = MV_FRAME[k].y; MV[k][j][i]->min_error = Min_FRAME[k]; } } free(act_block); free(search_area); return; } /********************************************************************** * * Name: LoadArea * Description: fills array with a square of image-data * * Input: pointer to image and position, x and y size * Returns: pointer to area * Side effects: memory allocated to array * * Date: 940203 Author: PGB * Mod: KOL * ***********************************************************************/ unsigned char *LoadArea(unsigned char *im, int x, int y, int x_size, int y_size, int lx) { unsigned char *res = (unsigned char *)malloc(sizeof(char)*x_size*y_size); unsigned char *in; unsigned char *out; int i = x_size; int j = y_size; in = im + (y*lx) + x; out = res; while (j--) { while (i--) *out++ = *in++; i = x_size; in += lx - x_size; }; return res; } /********************************************************************** * * Name: SAD_Macroblock * Description: fast way to find the SAD of one vector * * Input: pointers to search_area and current block, * Min_F1/F2/FR * Returns: sad_f1/f2 * Side effects: * * Date: 940203 Author: PGB * Mod: KOL * ***********************************************************************/ int SAD_Macroblock(unsigned char *ii, unsigned char *act_block, int h_length, int Min_FRAME) { int i; int sad = 0; unsigned char *kk; kk = act_block; i = 16; while (i--) { sad += (abs(*ii - *kk ) +abs(*(ii+1 ) - *(kk+1) ) +abs(*(ii+2) - *(kk+2) ) +abs(*(ii+3 ) - *(kk+3) ) +abs(*(ii+4) - *(kk+4) ) +abs(*(ii+5 ) - *(kk+5) ) +abs(*(ii+6) - *(kk+6) ) +abs(*(ii+7 ) - *(kk+7) ) +abs(*(ii+8) - *(kk+8) ) +abs(*(ii+9 ) - *(kk+9) ) +abs(*(ii+10)- *(kk+10)) +abs(*(ii+11) - *(kk+11)) +abs(*(ii+12)- *(kk+12)) +abs(*(ii+13) - *(kk+13)) +abs(*(ii+14)- *(kk+14)) +abs(*(ii+15) - *(kk+15)) ); ii += h_length; kk += 16; if (sad > Min_FRAME) return INT_MAX; } return sad; } int SAD_Block(unsigned char *ii, unsigned char *act_block, int h_length, int min_sofar) { int i; int sad = 0; unsigned char *kk; kk = act_block; i = 8; while (i--) { sad += (abs(*ii - *kk ) +abs(*(ii+1 ) - *(kk+1) ) +abs(*(ii+2) - *(kk+2) ) +abs(*(ii+3 ) - *(kk+3) ) +abs(*(ii+4) - *(kk+4) ) +abs(*(ii+5 ) - *(kk+5) ) +abs(*(ii+6) - *(kk+6) ) +abs(*(ii+7 ) - *(kk+7) )); ii += h_length; kk += 16; if (sad > min_sofar) return INT_MAX; } return sad; } int SAD_MB_integer(int *ii, int *act_block, int h_length, int min_sofar) { int i, sad = 0, *kk; kk = act_block; i = 16; while (i--) { sad += (abs(*ii - *kk ) +abs(*(ii+1 ) - *(kk+1) ) +abs(*(ii+2) - *(kk+2) ) +abs(*(ii+3 ) - *(kk+3) ) +abs(*(ii+4) - *(kk+4) ) +abs(*(ii+5 ) - *(kk+5) ) +abs(*(ii+6) - *(kk+6) ) +abs(*(ii+7 ) - *(kk+7) ) +abs(*(ii+8) - *(kk+8) ) +abs(*(ii+9 ) - *(kk+9) ) +abs(*(ii+10)- *(kk+10)) +abs(*(ii+11) - *(kk+11)) +abs(*(ii+12)- *(kk+12)) +abs(*(ii+13) - *(kk+13)) +abs(*(ii+14)- *(kk+14)) +abs(*(ii+15) - *(kk+15)) ); ii += h_length; kk += 16; if (sad > min_sofar) return INT_MAX; } return sad; } /********************************************************************** * * Name: FindMB * Description: Picks out one MB from picture * * Input: position of MB to pick out, * pointer to frame data, empty 16x16 array * Returns: * Side effects: fills array with MB data * * Date: 930119 Author: Karl Olav Lillevold * ***********************************************************************/ void FindMB(int x, int y, unsigned char *image, int MB[16][16]) { int n; register int m; for (n = 0; n < MB_SIZE; n++) for (m = 0; m < MB_SIZE; m++) MB[n][m] = *(image + x+m + (y+n)*pels); }