/************************************************************************ * * mot_est.c, part of tmn (TMN encoder) * * Copyright (C) 1997 University of BC, Canada * * Contacts: * Michael Gallant * Guy Cote * Berna Erol * * UBC Image Processing Laboratory http://www.ee.ubc.ca/image * 2356 Main Mall tel.: +1 604 822 4051 * Vancouver BC Canada V6T1Z4 fax.: +1 604 822 5949 * * Copyright (C) 1995, 1996 Telenor R&D, Norway * * Contacts: * Robert Danielsen * * Telenor Research and Development http://www.nta.no/brukere/DVC/ * P.O.Box 83 tel.: +47 63 84 84 00 * N-2007 Kjeller, Norway fax.: +47 63 81 00 76 * ************************************************************************/ /* Disclaimer of Warranty * * These software programs are available to the user without any license fee * or royalty on an "as is" basis. The University of British Columbia * disclaims any and all warranties, whether express, implied, or * statuary, including any implied warranties or merchantability or of * fitness for a particular purpose. In no event shall the * copyright-holder be liable for any incidental, punitive, or * consequential damages of any kind whatsoever arising from the use of * these programs. * * This disclaimer of warranty extends to the user of these programs and * user's customers, employees, agents, transferees, successors, and * assigns. * * The University of British Columbia does not represent or warrant that the * programs furnished hereunder are free of infringement of any * third-party patents. * * Commercial implementations of H.263, including shareware, are subject to * royalty fees to patent holders. Many of these patents are general * enough such that they are unavoidable regardless of implementation * design. * */ #include "sim.h" /********************************************************************** * * Name: MotionEstimation * Description: Estimate all motion vectors for one MB * * Input: pointers to current and reference (previous) * image, pointers to current slice and * current MB * Returns: * Side effects: motion vector information 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 * 951001 Mod for extended motion vector range * 970526 Fast motion estimation by mikeg@ee.ubc.ca, * modified by guyc@ee.ubc.ca. * 970831 Added support for true-B pictures, * modified by mikeg@ee.ubc.ca * 971025 Added new motion vector range for RVLC * modified by guyc@ee.ubc.ca. * ***********************************************************************/ void MotionEstimation (unsigned char *curr, unsigned char *reference, int x_curr, int y_curr, int xoff, int yoff, int seek_dist, MotionVector * MV[7][MBR + 1][MBC + 2], int *SAD_0, int estimation_type) { int Min_FRAME[7]; MotionVector MV_FRAME[7]; unsigned char *act_block, *aa, *ii; unsigned char *search_area, *adv_search_area = NULL, *zero_area = NULL; int sxy, i, k, j, l, m; int ihigh, ilow, jhigh, jlow, h_length, v_length; int adv_ihigh, adv_ilow, adv_jhigh, adv_jlow, adv_h_length, adv_v_length; int xmax, ymax, block, sad, lx; int adv_x_curr, adv_y_curr, xvec, yvec; #ifndef FULLSEARCH static htp[4] = {-1, 0, 1, 0}; static vtp[4] = {0, 1, 0, -1}; int j_min_now, i_min_now, i_min_next, j_min_next, sad_layr; int distortion_0, distortion_1, distortion_2; #endif xmax = pels; ymax = lines; sxy = seek_dist; if (!long_vectors || (estimation_type == PB_PICTURE_ESTIMATION)) { /* Maximum normal search range centered around _zero-vector_ */ sxy = mmin (15, sxy); } else { if(EPTYPE) { /* if RVLC codes of H.263+ are used, new motion vector range * * is used. */ sxy = (pels < 353) ? 31 : (pels < 705) ? 63 : (pels < 1409) ? 127 : 255; } else { /* Maximum extended search range centered around _predictor_ */ sxy = mmin (15 - (2 * DEF_8X8_WIN), sxy); /* NB! */ /* It is only possible to transmit motion vectors within a 15x15 * window around the motion vector predictor for any 8x8 or 16x16 * block */ /* The reason for the search window's reduction above with * 2*DEF_8X8_WIN+1 is that the 8x8 search may change the MV predictor * for some of the blocks within the macroblock. When we impose the * limitation above, we are sure that any 8x8 vector we might find is * possible to transmit */ /* We have found that with OBMC, DEF_8X8_WIN should be quite small for * two reasons: (i) a good filtering effect, and (ii) not too many * bits used for transferring the vectors. As can be seen above this * is also useful to avoid a large limitation on the MV search range */ /* It is possible to make sure the motion vectors found are legal in * other less limiting ways than above, but this would be more * complicated as well as time-consuming. Any good suggestions for * improvement is welcome, though */ } xoff = mmin (16, mmax (-16, xoff)); yoff = mmin (16, mmax (-16, yoff)); /* There is no need to check if (xoff + x_curr) points outside the * picture, since the Extended Motion Vector Range is always used * together with the Unrestricted MV mode */ } lx = (mv_outside_frame ? pels + (long_vectors ? 64 : 32) : pels); ilow = x_curr + xoff - sxy; ihigh = x_curr + xoff + sxy; jlow = y_curr + yoff - sxy; jhigh = y_curr + yoff + 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 (reference, ilow, jlow, h_length, v_length, lx); for (k = 0; k < 7; k++) { Min_FRAME[k] = INT_MAX; MV_FRAME[k].x = 0; MV_FRAME[k].y = 0; MV_FRAME[k].x_half = 0; MV_FRAME[k].y_half = 0; } /* Zero vector search */ if (x_curr - ilow < 0 || y_curr - jlow < 0 || x_curr - ilow + MB_SIZE > h_length || y_curr - jlow + MB_SIZE > v_length) { /* in case the zero vector is outside the loaded area in search_area */ zero_area = LoadArea (reference, x_curr, y_curr, 16, 16, lx); *SAD_0 = SAD_Macroblock (zero_area, act_block, 16, Min_FRAME[0]) - PREF_NULL_VEC; free (zero_area); } else { /* the zero vector is within search_area */ ii = search_area + (x_curr - ilow) + (y_curr - jlow) * h_length; *SAD_0 = SAD_Macroblock (ii, act_block, h_length, Min_FRAME[0]) - PREF_NULL_VEC; } if (xoff == 0 && yoff == 0) { Min_FRAME[0] = *SAD_0; MV_FRAME[0].x = 0; MV_FRAME[0].y = 0; } else { ii = search_area + (x_curr + xoff - ilow) + (y_curr + yoff - jlow) * h_length; Min_FRAME[0] = SAD_Macroblock (ii, act_block, h_length, Min_FRAME[0]); MV_FRAME[0].x = xoff; MV_FRAME[0].y = yoff; } /* NB: if xoff or yoff != 0, the Extended MV Range is used. If we allow * the zero vector to be chosen prior to the half pel search in this * case, the half pel search might lead to a non-transmittable vector * (on the wrong side of zero). If SAD_0 turns out to be the best SAD, * the zero-vector will be chosen after half pel search instead. The * zero-vector can be transmitted in all modes, no matter what the MV * predictor is */ /* Spiral search for full search motion estimation */ #ifdef FULLSEARCH for (l = 1; l <= sxy; l++) { i = x_curr + xoff - l; j = y_curr + yoff - 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--; } } #else /* Fast search motion estimation */ i_min_now = x_curr + xoff; j_min_now = y_curr + yoff; distortion_0 = distortion_1 = distortion_2 = 65536; for (l = 1; l <= 2 * sxy; l++) { sad_layr = 65536; for (m = 0; m < 4; m++) { i = i_min_now + htp[m]; j = j_min_now + vtp[m]; 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, sad_layr); /* set search map to 1 for this location */ if (sad < Min_FRAME[0]) { MV_FRAME[0].x = i - x_curr; MV_FRAME[0].y = j - y_curr; Min_FRAME[0] = sad; } if (sad < sad_layr) { sad_layr = sad; i_min_next = i; j_min_next = j; } } } i_min_now = i_min_next; j_min_now = j_min_next; distortion_2 = distortion_1; distortion_1 = distortion_0; distortion_0 = sad_layr; if (distortion_1 <= distortion_0 && distortion_2 <= distortion_0) { break; } } #endif if (use_4mv) { /* 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 for this is: (i) it is faster, and (ii) it generally gives * better results because of a better OBMC filtering effect and less * bits spent for vectors, and (iii) if the Extended MV Range is used, * the search range around the motion vector predictor will be less * limited */ xvec = MV_FRAME[0].x; yvec = MV_FRAME[0].y; if (!long_vectors || estimation_type == PB_PICTURE_ESTIMATION) { 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; } } adv_x_curr = x_curr + xvec; adv_y_curr = y_curr + yvec; sxy = DEF_8X8_WIN; adv_ilow = adv_x_curr - sxy; adv_ihigh = adv_x_curr + sxy; adv_jlow = adv_y_curr - sxy; adv_jhigh = adv_y_curr + sxy; adv_h_length = adv_ihigh - adv_ilow + 16; adv_v_length = adv_jhigh - adv_jlow + 16; adv_search_area = LoadArea (reference, adv_ilow, adv_jlow, adv_h_length, adv_v_length, lx); for (block = 0; block < 4; block++) { ii = adv_search_area + (adv_x_curr - adv_ilow) + ((block & 1) << 3) + (adv_y_curr - adv_jlow + ((block & 2) << 2)) * adv_h_length; aa = act_block + ((block & 1) << 3) + ((block & 2) << 2) * 16; Min_FRAME[block + 1] = SAD_Block (ii, aa, adv_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 full search motion estimation */ #ifdef FULLSEARCH for (l = 1; l <= sxy; l++) { i = adv_x_curr - l; j = adv_y_curr - l; for (k = 0; k < 8*l; k++) { if (i>=adv_ilow && i<=adv_ihigh && j>=adv_jlow && j<=adv_jhigh) { /* 8x8 integer pel MVs */ for (block = 0; block < 4; block++) { ii = adv_search_area + (i-adv_ilow) + ((block&1)<<3) + (j-adv_jlow + ((block&2)<<2) )*adv_h_length; aa = act_block + ((block&1)<<3) + ((block&2)<<2)*16; sad = SAD_Block(ii, aa, adv_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--; } } #else /* Fast search motion estimation */ for (block = 0; block < 4; block++) { i_min_now = adv_x_curr; j_min_now = adv_y_curr; distortion_0 = distortion_1 = distortion_2 = 65536; for (l = 1; l <= 2 * sxy; l++) { sad_layr = 65536; for (m = 0; m < 4; m++) { i = i_min_now + htp[m]; j = j_min_now + vtp[m]; if (i >= adv_ilow && i <= adv_ihigh && j >= adv_jlow && j <= adv_jhigh) { /* 8x8 integer pel MVs */ ii = adv_search_area + (i - adv_ilow) + ((block & 1) << 3) + (j - adv_jlow + ((block & 2) << 2)) * adv_h_length; aa = act_block + ((block & 1) << 3) + ((block & 2) << 2) * 16; sad = SAD_Block (ii, aa, adv_h_length, sad_layr); 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 (sad < sad_layr) { sad_layr = sad; i_min_next = i; j_min_next = j; } } } } i_min_now = i_min_next; j_min_now = j_min_next; distortion_2 = distortion_1; distortion_1 = distortion_0; distortion_0 = sad_layr; if (distortion_1 <= distortion_0 && distortion_2 <= distortion_0) { break; } } #endif } i = x_curr / MB_SIZE + 1; j = y_curr / MB_SIZE + 1; if (!use_4mv) { 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); if (use_4mv) free (adv_search_area); return; } /********************************************************************** * * Name: MotionEstimationBackward * Description: Estimate backward motion vector for one MB * * Input: pointers to current and reference (future) * image, pointers to current slice and * current MB * Returns: * Side effects: motion vector information in MB changed * * Date: 970728 Author: Michael Gallant --- mikeg@ee.ubc.ca * Essentially the same as MotionEstimation. * Written as a separate routine for clarity and * readability. * *************************************************************************/ void MotionEstimationBackward (unsigned char *curr, unsigned char *reference, int x_curr, int y_curr, int xoff, int yoff, int seek_dist, MotionVector * MV[7][MBR + 1][MBC + 2], int *SAD_0) { int Min_FRAME; MotionVector MV_FRAME; unsigned char *act_block, *ii; unsigned char *search_area, *zero_area = NULL; int sxy, i, j, k, l; int ihigh, ilow, jhigh, jlow, h_length, v_length; int sad, lx; int xmax, ymax; #ifndef FULLSEARCH static htp[4] = {-1, 0, 1, 0}; static vtp[4] = {0, 1, 0, -1}; int j_min_now, i_min_now, i_min_next, j_min_next, sad_layr; int distortion_0, distortion_1, distortion_2; #endif xmax = pels; ymax = lines; sxy = seek_dist; if (!long_vectors) { /* Maximum normal search range centered around _zero-vector_ */ sxy = mmin (15, sxy); } else { if(EPTYPE) { /* if RVLC codes of H.263+ are used, new motion vector range * * is used. */ sxy = (pels < 353) ? 31 : (pels < 705) ? 63 : (pels < 1409) ? 127 : 255; } else { /* Maximum extended search range centered around _predictor_ */ sxy = mmin (15 - (2 * DEF_8X8_WIN), sxy); } xoff = mmin (16, mmax (-16, xoff)); yoff = mmin (16, mmax (-16, yoff)); /* There is no need to check if (xoff + x_curr) points outside the * picture, since the Extended Motion Vector Range is always used * together with the Unrestricted MV mode */ } lx = (mv_outside_frame ? pels + (long_vectors ? 64 : 32) : pels); ilow = x_curr + xoff - sxy; ihigh = x_curr + xoff + sxy; jlow = y_curr + yoff - sxy; jhigh = y_curr + yoff + 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 (reference, ilow, jlow, h_length, v_length, lx); Min_FRAME = INT_MAX; MV_FRAME.x = 0; MV_FRAME.y = 0; MV_FRAME.x_half = 0; MV_FRAME.y_half = 0; /* Zero vector search */ if (x_curr - ilow < 0 || y_curr - jlow < 0 || x_curr - ilow + MB_SIZE > h_length || y_curr - jlow + MB_SIZE > v_length) { /* in case the zero vector is outside the loaded area in search_area */ zero_area = LoadArea (reference, x_curr, y_curr, 16, 16, lx); *SAD_0 = SAD_Macroblock (zero_area, act_block, 16, Min_FRAME) - PREF_NULL_VEC; free (zero_area); } else { /* the zero vector is within search_area */ ii = search_area + (x_curr - ilow) + (y_curr - jlow) * h_length; *SAD_0 = SAD_Macroblock (ii, act_block, h_length, Min_FRAME) - PREF_NULL_VEC; } if (xoff == 0 && yoff == 0) { Min_FRAME = *SAD_0; MV_FRAME.x = 0; MV_FRAME.y = 0; } else { ii = search_area + (x_curr + xoff - ilow) + (y_curr + yoff - jlow) * h_length; Min_FRAME = SAD_Macroblock (ii, act_block, h_length, Min_FRAME); MV_FRAME.x = xoff; MV_FRAME.y = yoff; } /* NB: if xoff or yoff != 0, the Extended MV Range is used. If we allow * the zero vector to be chosen prior to the half pel search in this * case, the half pel search might lead to a non-transmittable vector * (on the wrong side of zero). If SAD_0 turns out to be the best SAD, * the zero-vector will be chosen after half pel search instead. The * zero-vector can be transmitted in all modes, no matter what the MV * predictor is */ #ifdef FULLSEARCH /* Spiral search for full search motion estimation */ for (l = 1; l <= sxy; l++) { i = x_curr + xoff - l; j = y_curr + yoff - 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); if (sad < Min_FRAME) { MV_FRAME.x = i - x_curr; MV_FRAME.y = j - y_curr; Min_FRAME = sad; } } if (k<2*l) i++; else if (k<4*l) j++; else if (k<6*l) i--; else j--; } } #else /* Fast search motion estimation */ i_min_now = x_curr + xoff; j_min_now = y_curr + yoff; distortion_0 = distortion_1 = distortion_2 = 65536; for (l = 1; l <= 2 * sxy; l++) { sad_layr = 65536; for (k = 0; k < 4; k++) { i = i_min_now + htp[k]; j = j_min_now + vtp[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, sad_layr); /* set search map to 1 for this location */ if (sad < Min_FRAME) { MV_FRAME.x = i - x_curr; MV_FRAME.y = j - y_curr; Min_FRAME = sad; } if (sad < sad_layr) { sad_layr = sad; i_min_next = i; j_min_next = j; } } } i_min_now = i_min_next; j_min_now = j_min_next; distortion_2 = distortion_1; distortion_1 = distortion_0; distortion_0 = sad_layr; if (distortion_1 <= distortion_0 && distortion_2 <= distortion_0) { break; } } #endif i = x_curr / MB_SIZE + 1; j = y_curr / MB_SIZE + 1; MV[5][j][i]->x = MV_FRAME.x; MV[5][j][i]->y = MV_FRAME.y; MV[5][j][i]->min_error = Min_FRAME; 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_Bidir (unsigned char *ii, unsigned char *aa, unsigned char *bb, int width, int min_sofar) { int i, sad = 0; unsigned char *ll, *kk; kk = aa; ll = bb; i = 16; while (i--) { sad += (abs (*ii - ((*kk + *ll) >> 1)) + abs (*(ii + 1) - ((*(kk + 1) + *(ll + 1)) >> 1)) + abs (*(ii + 2) - ((*(kk + 2) + *(ll + 2)) >> 1)) + abs (*(ii + 3) - ((*(kk + 3) + *(ll + 3)) >> 1)) + abs (*(ii + 4) - ((*(kk + 4) + *(ll + 4)) >> 1)) + abs (*(ii + 5) - ((*(kk + 5) + *(ll + 5)) >> 1)) + abs (*(ii + 6) - ((*(kk + 6) + *(ll + 6)) >> 1)) + abs (*(ii + 7) - ((*(kk + 7) + *(ll + 7)) >> 1)) + abs (*(ii + 8) - ((*(kk + 8) + *(ll + 8)) >> 1)) + abs (*(ii + 9) - ((*(kk + 9) + *(ll + 9)) >> 1)) + abs (*(ii + 10) - ((*(kk + 10) + *(ll + 10)) >> 1)) + abs (*(ii + 11) - ((*(kk + 11) + *(ll + 11)) >> 1)) + abs (*(ii + 12) - ((*(kk + 12) + *(ll + 12)) >> 1)) + abs (*(ii + 13) - ((*(kk + 13) + *(ll + 13)) >> 1)) + abs (*(ii + 14) - ((*(kk + 14) + *(ll + 14)) >> 1)) + abs (*(ii + 15) - ((*(kk + 15) + *(ll + 15)) >> 1))); ii += width; kk += width; ll += width; 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); }