📄 mot_est.c
字号:
#include"sim.h"/********************************************************************** * * 函数名:MotionEstimation * 函数功能:估计编码块的所有运动向量 * 函数输入:curr:当前帧图像数据的指针
* prev:上一帧图像数据的指针
* x_curr:当前帧图像分片的位置(水平方向)
* y_curr:当前帧图像分片的位置(竖直方向)
* xoff:编码宏块的水平位置
* yoff:编码宏块的竖直位置
* seek_dist:运动估计的搜索区域
* MV:运动向量
* SAD_0:宏块的SAD值 * 函数返回值:编码宏块中运动向量信息 ***********************************************************************/void MotionEstimation(unsigned char *curr, unsigned char *prev, int x_curr, int y_curr, int xoff, int yoff, int seek_dist, MotionVector *MV[6][MBR+1][MBC+2], int *SAD_0){ int Min_FRAME[5]; MotionVector MV_FRAME[5]; unsigned char *act_block,*aa,*ii; unsigned char *search_area, *adv_search_area = NULL, *zero_area = NULL; int sxy,i,k,j,l; 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; xmax = pels; ymax = lines; sxy = seek_dist; if (!long_vectors) { /* 以零向量为中心的最大搜索区域 */ sxy = mmin(15, sxy); } else { /* 以预测为中心的最大扩展搜索范围*/ sxy = mmin(15 - (2*DEF_8X8_WIN+1), sxy); /* 对任意8x8和16x16的编码块,只有那些处在运动向量预测
周围15x15的窗口区域的运动向量才能被传送出来。 */ xoff = mmin(16,mmax(-16,xoff)); yoff = mmin(16,mmax(-16,yoff)); } /* 通常不需要检查(xoff + x_curr)的点是否在图像帧之外,这是因为
扩展运动区域总是同非限制性的MV模式同时使用 */ 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(prev, ilow, jlow, h_length, v_length, lx); for (k = 0; k < 5; 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; } /* 零向量搜索 */ if (x_curr-ilow < 0 || y_curr-jlow < 0 || x_curr-ilow+MB_SIZE > h_length || y_curr-jlow+MB_SIZE > v_length) { /* 防止零向量跳出搜索区域的有效面积 */ zero_area = LoadArea(prev, 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 { /* 零向量位于搜索区域内 */ 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; } /* 如果xoff和yoff等于0,即SAD_0成为最佳的编码块SAD值,那么零向量
在所有模式下都可传送,而不需要考虑MV预测值是怎么样的。*/ if (xoff == 0 && yoff == 0) { Min_FRAME[0] = *SAD_0; MV_FRAME[0].x = 0; MV_FRAME[0].y = 0; }
/* 如果xoff或yoff不等于0,那么系统使用扩展的MV搜索范围。*/ 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; } /* 盘旋搜索 */ 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整数个像素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) { /* 以8x8的区域为中心寻找16x16的运动向量,这里仍然采用全局搜索的方法。
这样做的原因:1)这种方法更快;2)这种方法能得到更好的结果;3)如
果运用了扩展的MV搜索范围,那么在运动向量预测周围的搜索范围将会降低
限制。*/ xvec = MV_FRAME[0].x; yvec = MV_FRAME[0].y; if (!long_vectors) { 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(prev, 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; } /* 盘旋搜索 */ 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整数个像素MV */ 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--; } } } 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); if (advanced) free(adv_search_area); return;}/********************************************************************** * * 函数名:LoadArea * 函数功能:利用一个方块区域的图像数据填充矩阵 * 函数输入:im:图像块数据指针
* x:图像块的水平位置
* y:图像块的竖直位置
* x_size:矩阵的水平尺寸
* y_size:矩阵的竖直尺寸
* lx:辅助变量,将矩阵和图像的访问形式转换为一维数组形式 * 函数返回值:图像区域数据指针 ***********************************************************************/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;}/********************************************************************** * * 函数名:SAD_Macroblock * 函数功能:计算一个运动向量的SAD值 * 函数输入值:ii:当前搜索区域的数据指针
* act_block:当前的编码块
* h_length:编码块高度值
* Min_FRAME:当前帧最小的SAD值
* 函数返回值:宏块运动向量的SAD值 ***********************************************************************/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;}/********************************************************************** * * 函数名:FindMB
* 函数功能:从当前视频帧中找出一个编码宏块
* 函数输入:x:所要寻找的宏块的水平位置
* y:所要寻找的宏块的竖直位置
* image:视频帧的数据指针
* MB:一个空的16x16大小的二维数组,存放宏块的数据 ***********************************************************************/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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -