Research/Video Coding

[구현] Block-based Translational, Affine, Perspective Motion Estimation을 직접 구현해보기 (3)

영스퀘어 2021. 4. 9. 16:18
  • 소스 코드

<Perspective ME>

using System;
using System.IO;

namespace MEModule_Perspective
{
    class Program
    {
        static void Main(string[] args)
        {
            ...

        }

        private static byte[,,] full_search(byte[] luma, int frame, int width, int height)
        {
            byte[,,] result = new byte[frame - 1, width, height];

            ulong[] allResi = new ulong[frame - 1];

            int MB = 32; // 매크로블록 사이즈
            int SubMB = 16; // Sub 블록 사이즈
            int SU = 16; // 매크로블록에서 위아래 얼마나 더 가져올지
            int SR = SU * 2 + SubMB; // 서치레인지 사이즈(32x32)
            int REST = MB + SU;
            int REST_X = REST + ((width - (REST * 2)) 
            	- ((int)((width - (REST * 2)) / MB) * MB));
            int REST_Y = REST + ((height - (REST * 2)) 
            	- ((int)((height - (REST * 2)) / MB) * MB));
            int SubPoint = (MB - 1) - SubMB;

            int numParamAffine = 6;

            int numControlPoint = 4;
            int numParam = 8;

            double[,] ControlPoint = new double[numControlPoint, 4]; // 4개의 control point

            for (int f = 0; f < frame - 1; f++)
            {
                byte[,] curr = get_frame(luma, f + 1, width, height);
                byte[,] prev = get_frame(luma, f, width, height);

                // padding
                // i : width, j : height
                for (int j = 0; j < height; j++)
                {
                    for (int i = 0; i < REST; i++) //좌
                    {
                        result[f, i, j] = curr[i, j];
                    }
                    for (int i = (width - REST_X); i < width; i++) //우
                    {
                        result[f, i, j] = curr[i, j];
                    }
                }

                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < REST; j++) //위
                    {
                        result[f, i, j] = curr[i, j];
                    }

                    for (int j = (height - REST_Y); j < height; j++) //아래
                    {
                        result[f, i, j] = curr[i, j];
                    }
                }

                //매크로 블록, 4개의 sub 블록, 서치 레인지 가져오기
                byte[,] mb = new byte[MB, MB]; // 32X32
                byte[,] sr = new byte[SR + MB, SR + MB];

                byte[,] submbLeftTop = new byte[SubMB, SubMB]; // 16X16
                byte[,] submbRightTop = new byte[SubMB, SubMB]; // 16X16
                byte[,] submbLeftBottom = new byte[SubMB, SubMB]; // 16X16
                byte[,] submbRightBottom = new byte[SubMB, SubMB];

                byte[,] srLeftTop = new byte[SR + SubMB, SR + SubMB];
                byte[,] srRightTop = new byte[SR + SubMB, SR + SubMB];
                byte[,] srLeftBottom = new byte[SR + SubMB, SR + SubMB];
                byte[,] srRightBottom = new byte[SR + SubMB, SR + SubMB];

                double[,] affineMat = new double[numParamAffine, numParamAffine];
                double[,] affineMatInverse = new double[numParamAffine, numParamAffine];
                double[,] affinePrime = new double[numParamAffine, 1];
                double[,] affineParam = new double[numParamAffine, 1];

                double[,] perspectiveMat = new double[numParam, numParam];
                double[,] perspectiveMatInverse = new double[numParam, numParam];
                double[,] perspectivePrime = new double[numParam, 1];
                double[,] perspectiveParam = new double[numParam, 1];

                for (int h = REST; h < height - REST_Y; h += MB)
                {
                    for (int w = REST; w < width - REST_X; w += MB)
                    {
                        //매크로 블록
                        for (int j = 0; j < MB; j++)
                        {
                            for (int i = 0; i < MB; i++)
                            {
                                mb[i, j] = curr[w + i, h + j];
                            }
                        }

                        //Sub 블록
                        for (int j = 0; j < SubMB; j++)
                        {
                            for (int i = 0; i < SubMB; i++)
                            {
                                submbLeftTop[i, j] = mb[i, j];
                                submbRightTop[i, j] = mb[i + SubPoint, j];
                                submbLeftBottom[i, j] = mb[i, j + SubPoint];
                                submbRightBottom[i, j] = mb[i + SubPoint, j + SubPoint];
                            }
                        }

                        //일반적인 Translational ME 수행
                        int x = w - SU;
                        int y = h - SU;

                        if (width - x < SR + MB && height - y >= SR + MB)
                        {
                            for (int j = 0; j < SR + MB; j++)
                            {
                                for (int i = 0; i < width - x; i++)
                                {
                                    sr[i, j] = prev[x + i, y + j];
                                }
                            }

                        }
                        else if (width - x >= SR + MB && height - y < SR + MB)
                        {
                            for (int j = 0; j < height - y; j++)
                            {
                                for (int i = 0; i < SR + MB; i++)
                                {
                                    sr[i, j] = prev[x + i, y + j];
                                }
                            }

                        }
                        else if (width - x < SR + MB && height - y < SR + MB)
                        {
                            for (int j = 0; j < height - y; j++)
                            {
                                for (int i = 0; i < width - x; i++)
                                {
                                    sr[i, j] = prev[x + i, y + j];
                                }
                            }

                        }
                        else
                        {
                            for (int j = 0; j < SR + MB; j++)
                            {
                                for (int i = 0; i < SR + MB; i++)
                                {
                                    sr[i, j] = prev[x + i, y + j];
                                }
                            }
                        }

                        // SAD (Sum of Absolute Difference)
                        int[,] sad = new int[SR, SR];
                        int sum = 0;

                        // sad 전체를 임의의 큰값인 999로 초기화
                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                sad[i, j] = 999;
                            }
                        }

                        int sr_width_length = sr.GetLength(0);
                        int sr_height_length = sr.GetLength(1);
                        int VALID_SR_WIDTH = SR - ((SR + MB) - sr_width_length);
                        int VALID_SR_HEIGHT = SR - ((SR + MB) - sr_height_length);

                        //서치레인지
                        for (int j = 0; j < VALID_SR_HEIGHT; j++)
                        {
                            for (int i = 0; i < VALID_SR_WIDTH; i++)
                            {
                                //매크로 블록
                                for (int l = 0; l < MB; l++)
                                {
                                    for (int k = 0; k < MB; k++)
                                    {
                                        sum += Math.Abs(sr[k + i, l + j] - mb[k, l]);
                                    }
                                }

                                sad[i, j] = sum;
                                sum = 0;
                            }
                        }

                        //모션 벡터 구하기
                        int trans_min_sum = int.MaxValue;
                        int trans_x_prime = 0;
                        int trans_y_prime = 0;

                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                if (sad[i, j] < trans_min_sum)
                                {
                                    trans_min_sum = sad[i, j];
                                    trans_x_prime = x + i;
                                    trans_y_prime = y + j;
                                }
                            }
                        }

                        //서치 레인지
                        int xLeftTop = w - SU;
                        int yLeftTop = h - SU;
                        int xRightTop = w - SU + SubPoint;
                        int yRightTop = h - SU;
                        int xLeftBottom = w - SU;
                        int yLeftBottom = h - SU + SubPoint;
                        int xRightBottom = w - SU + SubPoint;
                        int yRightBottom = h - SU + SubPoint;

                        // 4개 sub 블록에 대한 각각의 ME를 수행한다.

                        //------------------------------------------
                        // srLeftTop
                        if (width - xLeftTop < SR + SubMB && height - yLeftTop 
                        	>= SR + SubMB)
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < width - xLeftTop; i++)
                                {
                                    srLeftTop[i, j] = prev[xLeftTop + i, yLeftTop + j];
                                }
                            }

                        }
                        else if (width - xLeftTop >= SR + SubMB && height - yLeftTop 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yLeftTop; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srLeftTop[i, j] = prev[xLeftTop + i, yLeftTop + j];
                                }
                            }

                        }
                        else if (width - xLeftTop < SR + SubMB && height - yLeftTop 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yLeftTop; j++)
                            {
                                for (int i = 0; i < width - xLeftTop; i++)
                                {
                                    srLeftTop[i, j] = prev[xLeftTop + i, yLeftTop + j];
                                }
                            }

                        }
                        else
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srLeftTop[i, j] = prev[xLeftTop + i, yLeftTop + j];
                                }
                            }
                        }

                        // SAD (Sum of Absolute Difference)
                        sum = 0;

                        // sad 전체를 임의의 큰값인 999로 초기화
                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                sad[i, j] = 999;
                            }
                        }

                        sr_width_length = srLeftTop.GetLength(0);
                        sr_height_length = srLeftTop.GetLength(1);
                        VALID_SR_WIDTH = SR - ((SR + SubMB) - sr_width_length); 
                        VALID_SR_HEIGHT = SR - ((SR + SubMB) - sr_height_length); 

                        for (int j = 0; j < VALID_SR_HEIGHT; j++)
                        {
                            for (int i = 0; i < VALID_SR_WIDTH; i++)
                            {
                                //Left Top (0,0)
                                for (int l = 0; l < SubMB; l++)
                                {
                                    for (int k = 0; k < SubMB; k++)
                                    {

                                        sum += Math.Abs(srLeftTop[k + i, l + j] 
                                        	- submbLeftTop[k, l]);
                                    }
                                }

                                sad[i, j] = sum;
                                sum = 0;
                            }
                        }

                        //모션 벡터 구하기
                        int min = int.MaxValue;
                        int mv_x = 0;
                        int mv_y = 0;

                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                if (sad[i, j] < min)
                                {
                                    min = sad[i, j];
                                    mv_x = xLeftTop + i;
                                    mv_y = yLeftTop + j;
                                }
                            }
                        }

                        ControlPoint[0, 0] = 0;
                        ControlPoint[0, 1] = 0;
                        ControlPoint[0, 2] = mv_x - w;
                        ControlPoint[0, 3] = mv_y - h;

                        //-------------------------------------
                        // srRightTop
                        if (width - xRightTop < SR + SubMB && height - yRightTop 
                        	>= SR + SubMB)
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < width - xRightTop; i++)
                                {
                                    srRightTop[i, j] = prev[xRightTop + i, yRightTop + j];
                                }
                            }

                        }
                        else if (width - xRightTop >= SR + SubMB && height - yRightTop 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yRightTop; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srRightTop[i, j] = prev[xRightTop + i, yRightTop + j];
                                }
                            }

                        }
                        else if (width - xRightTop < SR + SubMB && height - yRightTop 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yRightTop; j++)
                            {
                                for (int i = 0; i < width - xRightTop; i++)
                                {
                                    srRightTop[i, j] = prev[xRightTop + i, yRightTop + j];
                                }
                            }

                        }
                        else
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srRightTop[i, j] = prev[xRightTop + i, yRightTop + j];
                                }
                            }
                        }

                        // SAD (Sum of Absolute Difference)
                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                sad[i, j] = 999;
                            }
                        }

                        sr_width_length = srRightTop.GetLength(0);
                        sr_height_length = srRightTop.GetLength(1);
                        VALID_SR_WIDTH = SR - ((SR + SubMB) - sr_width_length); 
                        VALID_SR_HEIGHT = SR - ((SR + SubMB) - sr_height_length); 

                        for (int j = 0; j < VALID_SR_HEIGHT; j++)
                        {
                            for (int i = 0; i < VALID_SR_WIDTH; i++)
                            {
                                //Right Top (13,0)
                                for (int l = 0; l < SubMB; l++)
                                {
                                    for (int k = 0; k < SubMB; k++)
                                    {

                                        sum += Math.Abs(srRightTop[k + i, l + j] 
                                        	- submbRightTop[k, l]);
                                    }
                                }

                                sad[i, j] = sum;
                                sum = 0;
                            }
                        }

                        //모션 벡터 구하기
                        min = int.MaxValue;
                        mv_x = 0;
                        mv_y = 0;

                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                if (sad[i, j] < min)
                                {
                                    min = sad[i, j];
                                    mv_x = xRightTop + i;
                                    mv_y = yRightTop + j;
                                }
                            }
                        }

                        ControlPoint[1, 0] = MB;
                        ControlPoint[1, 1] = 0;
                        ControlPoint[1, 2] = mv_x - (w + SubPoint) + MB;
                        ControlPoint[1, 3] = mv_y - h;

                        //---------------------------------------------
                        // srLeftBottom
                        if (width - xLeftBottom < SR + SubMB && height - yLeftBottom 
                        	>= SR + SubMB)
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < width - xLeftBottom; i++)
                                {
                                    srLeftBottom[i, j] = prev[xLeftBottom + i, yLeftBottom + j];
                                }
                            }

                        }
                        else if (width - xLeftBottom >= SR + SubMB && height - yLeftBottom 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yLeftBottom; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srLeftBottom[i, j] = prev[xLeftBottom + i, yLeftBottom + j];
                                }
                            }

                        }
                        else if (width - xLeftBottom < SR + SubMB && height - yLeftBottom 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yLeftBottom; j++)
                            {
                                for (int i = 0; i < width - xLeftBottom; i++)
                                {
                                    srLeftBottom[i, j] = prev[xLeftBottom + i, yLeftBottom + j];
                                }
                            }

                        }
                        else
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srLeftBottom[i, j] = prev[xLeftBottom + i, yLeftBottom + j];
                                }
                            }
                        }

                        // SAD (Sum of Absolute Difference)
                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                sad[i, j] = 999;
                            }
                        }

                        sr_width_length = srLeftBottom.GetLength(0);
                        sr_height_length = srLeftBottom.GetLength(1); 
                        VALID_SR_WIDTH = SR - ((SR + SubMB) - sr_width_length); 
                        VALID_SR_HEIGHT = SR - ((SR + SubMB) - sr_height_length); 

                        for (int j = 0; j < VALID_SR_HEIGHT; j++)
                        {
                            for (int i = 0; i < VALID_SR_WIDTH; i++)
                            {
                                //Left Bottom (13,0)
                                for (int l = 0; l < SubMB; l++)
                                {
                                    for (int k = 0; k < SubMB; k++)
                                    {

                                        sum += Math.Abs(srLeftBottom[k + i, l + j] 
                                        	- submbLeftBottom[k, l]);
                                    }
                                }

                                sad[i, j] = sum;
                                sum = 0;
                            }
                        }

                        //모션 벡터 구하기
                        min = int.MaxValue;
                        mv_x = 0;
                        mv_y = 0;

                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                if (sad[i, j] < min)
                                {
                                    min = sad[i, j];
                                    mv_x = xLeftBottom + i;
                                    mv_y = yLeftBottom + j;
                                }
                            }
                        }

                        //ControlPoint[2, 0] = w;
                        //ControlPoint[2, 1] = h + SubPoint;
                        //ControlPoint[2, 2] = mv_x;
                        //ControlPoint[2, 3] = mv_y;

                        ControlPoint[2, 0] = 0;
                        ControlPoint[2, 1] = MB;
                        ControlPoint[2, 2] = mv_x - w;
                        ControlPoint[2, 3] = mv_y - (h + SubPoint) + MB;

                        //----------------------------------------
                        // srRightBottom
                        if (width - xRightBottom < SR + SubMB && height - yRightBottom 
                        	>= SR + SubMB)
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < width - xRightBottom; i++)
                                {
                                    srRightBottom[i, j] = prev[xRightBottom + i, 
                                    	yRightBottom + j];
                                }
                            }

                        }
                        else if (width - xRightBottom >= SR + SubMB && height - yRightBottom 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yRightBottom; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srRightBottom[i, j] = prev[xRightBottom + i, 
                                    	yRightBottom + j];
                                }
                            }

                        }
                        else if (width - xRightBottom < SR + SubMB && height - yRightBottom 
                        	< SR + SubMB)
                        {
                            for (int j = 0; j < height - yRightBottom; j++)
                            {
                                for (int i = 0; i < width - xRightBottom; i++)
                                {
                                    srRightBottom[i, j] = prev[xRightBottom + i, 
                                    	yRightBottom + j];
                                }
                            }

                        }
                        else
                        {
                            for (int j = 0; j < SR + SubMB; j++)
                            {
                                for (int i = 0; i < SR + SubMB; i++)
                                {
                                    srRightBottom[i, j] = prev[xRightBottom + i, 
                                    	yRightBottom + j];
                                }
                            }
                        }

                        // SAD (Sum of Absolute Difference)
                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                sad[i, j] = 999;
                            }
                        }

                        sr_width_length = srRightBottom.GetLength(0);
                        sr_height_length = srRightBottom.GetLength(1);
                        VALID_SR_WIDTH = SR - ((SR + SubMB) - sr_width_length);
                        VALID_SR_HEIGHT = SR - ((SR + SubMB) - sr_height_length);

                        for (int j = 0; j < VALID_SR_HEIGHT; j++)
                        {
                            for (int i = 0; i < VALID_SR_WIDTH; i++)
                            {
                                //Right Bottom
                                for (int l = 0; l < SubMB; l++)
                                {
                                    for (int k = 0; k < SubMB; k++)
                                    {

                                        sum += Math.Abs(srRightBottom[k + i, l + j] 
                                        	- submbRightBottom[k, l]);
                                    }
                                }

                                sad[i, j] = sum;
                                sum = 0;
                            }
                        }

                        //모션 벡터 구하기
                        min = int.MaxValue;
                        mv_x = 0;
                        mv_y = 0;

                        for (int j = 0; j < SR; j++)
                        {
                            for (int i = 0; i < SR; i++)
                            {
                                if (sad[i, j] < min)
                                {
                                    min = sad[i, j];
                                    mv_x = xRightBottom + i;
                                    mv_y = yRightBottom + j;
                                }
                            }
                        }

                        ControlPoint[3, 0] = MB;
                        ControlPoint[3, 1] = MB;
                        ControlPoint[3, 2] = mv_x - (w + SubPoint) + MB;
                        ControlPoint[3, 3] = mv_y - (h + SubPoint) + MB;

                        // create matrix (Affine Mat)
                        for (int m = 0; m < numParamAffine / 2; m++)
                        {
                            //짝수 행 (0, 2, 4)
                            affineMat[2 * m, 0] = ControlPoint[m, 0];
                            affineMat[2 * m, 1] = ControlPoint[m, 1];
                            affineMat[2 * m, 2] = 0;
                            affineMat[2 * m, 3] = 0;
                            affineMat[2 * m, 4] = 1;
                            affineMat[2 * m, 5] = 0;

                            //홀수 행 (1, 3, 5)
                            affineMat[2 * m + 1, 0] = 0;
                            affineMat[2 * m + 1, 1] = 0;
                            affineMat[2 * m + 1, 2] = ControlPoint[m, 0];
                            affineMat[2 * m + 1, 3] = ControlPoint[m, 1];
                            affineMat[2 * m + 1, 4] = 0;
                            affineMat[2 * m + 1, 5] = 1;

                            affinePrime[2 * m, 0] = ControlPoint[m, 2];
                            affinePrime[2 * m + 1, 0] = ControlPoint[m, 3];
                        }
                        // create matrix (Perspective Mat)
                        for (int m = 0; m < numParam / 2; m++)
                        {
                            //짝수 행 (0, 2, 4, 6)
                            perspectiveMat[2 * m, 0] = ControlPoint[m, 0];
                            perspectiveMat[2 * m, 1] = ControlPoint[m, 1];
                            perspectiveMat[2 * m, 2] = 1;
                            perspectiveMat[2 * m, 3] = 0;
                            perspectiveMat[2 * m, 4] = 0;
                            perspectiveMat[2 * m, 5] = 0;
                            perspectiveMat[2 * m, 6] = -1 * ControlPoint[m, 0] 
                            	* ControlPoint[m, 2];
                            perspectiveMat[2 * m, 7] = -1 * ControlPoint[m, 2] 
                            	* ControlPoint[m, 1];

                            //홀수 행 (1, 3, 5, 7)
                            perspectiveMat[2 * m + 1, 0] = 0;
                            perspectiveMat[2 * m + 1, 1] = 0;
                            perspectiveMat[2 * m + 1, 2] = 0;
                            perspectiveMat[2 * m + 1, 3] = ControlPoint[m, 0];
                            perspectiveMat[2 * m + 1, 4] = ControlPoint[m, 1];
                            perspectiveMat[2 * m + 1, 5] = 1;
                            perspectiveMat[2 * m + 1, 6] = -1 * ControlPoint[m, 0] 
                            	* ControlPoint[m, 3];
                            perspectiveMat[2 * m + 1, 7] = -1 * ControlPoint[m, 1] 
                            	* ControlPoint[m, 3];

                            perspectivePrime[2 * m, 0] = ControlPoint[m, 2];
                            perspectivePrime[2 * m + 1, 0] = ControlPoint[m, 3];
                        }

                        // generate inverse matrix
                        affineMatInverse = GetInverseMatrix(affineMat, numParamAffine);

                        // product matrix x vector 
                        // and get perspective parameter for current MB
                        affineParam = MMult(affineMatInverse, affinePrime);

                        // generate inverse matrix
                        perspectiveMatInverse = GetInverseMatrix(perspectiveMat, numParam);

                        // product matrix x vector 
                        // and get perspective parameter for current MB
                        perspectiveParam = MMult(perspectiveMatInverse, perspectivePrime);

                        uint sum_affine_block = 0;
                        uint sum_persp_block = 0;

                        for (int a = 0; a < MB; a += 4)
                        {
                            for (int b = 0; b < MB; b += 4)
                            {
                                int eachSubMB_x = w + b;
                                int eachSubMB_y = h + a;
                                int eachSubMBcenter_x = b + (4 / 2);
                                int eachSubMBcenter_y = a + (4 / 2);

                                double eachSubMBcenterPrimeAffine_x = ((affineParam[0, 0] 
                                	* eachSubMBcenter_x) 
                                    + (affineParam[1, 0] * eachSubMBcenter_y) 
                                    + (affineParam[4, 0]));
                                double eachSubMBcenterPrimeAffine_y = ((affineParam[2, 0] 
                                	* eachSubMBcenter_x) 
                                    + (affineParam[3, 0] * eachSubMBcenter_y) 
                                    + (affineParam[5, 0]));

                                double eachSubMBcenterPrime_x = ((perspectiveParam[0, 0] 
                                	* eachSubMBcenter_x) 
                                    + (perspectiveParam[1, 0] * eachSubMBcenter_y) 
                                    + (perspectiveParam[2, 0])) 
                                    / (double)((perspectiveParam[6, 0] * eachSubMBcenter_x) 
                                    + (perspectiveParam[7, 0] * eachSubMBcenter_y) + 1);
                                double eachSubMBcenterPrime_y = ((perspectiveParam[3, 0] 
                                	* eachSubMBcenter_x) 
                                    + (perspectiveParam[4, 0] * eachSubMBcenter_y) 
                                    + (perspectiveParam[5, 0])) 
                                    / (double)((perspectiveParam[6, 0] * eachSubMBcenter_x) 
                                    + (perspectiveParam[7, 0] * eachSubMBcenter_y) + 1);

                                int eachSubMBMVAffine_x = (int)(eachSubMBcenterPrimeAffine_x 
                                	- (double)eachSubMBcenter_x);
                                int eachSubMBMVAffine_y = (int)(eachSubMBcenterPrimeAffine_y 
                                	- (double)eachSubMBcenter_y);

                                int eachSubMBMV_x = (int)(eachSubMBcenterPrime_x 
                                	- (double)eachSubMBcenter_x);
                                int eachSubMBMV_y = (int)(eachSubMBcenterPrime_y 
                                	- (double)eachSubMBcenter_y);

                                for (int c = 0; c < 4; c++)
                                {
                                    for (int d = 0; d < 4; d++)
                                    {
                                        if (eachSubMB_x + eachSubMBMVAffine_x + 4 < width 
                                        	&& eachSubMB_y + eachSubMBMVAffine_y + 4 
                                            < height)
                                        {
                                            if (eachSubMB_x + eachSubMBMVAffine_x < 0 
                                            	&& eachSubMB_y + eachSubMBMVAffine_y < 0)
                                            {
                                                sum_affine_block += (byte)Math.Abs(
                                                	prev[0 + d, 0 + c] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                            else if (eachSubMB_x + eachSubMBMVAffine_x < 0 
                                            	&& eachSubMB_y + eachSubMBMVAffine_y >= 0)
                                            {
                                                sum_affine_block += (byte)Math.Abs(
                                                	prev[0 + d, eachSubMB_y 
                                                    + c + eachSubMBMVAffine_y] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                            else if (eachSubMB_x + eachSubMBMVAffine_x >= 0 
                                            	&& eachSubMB_y + eachSubMBMVAffine_y < 0)
                                            {
                                                sum_affine_block += (byte)Math.Abs(
                                                	prev[eachSubMB_x + d 
                                                    + eachSubMBMVAffine_x, 0 + c] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                            else
                                            {
                                                sum_affine_block += (byte)Math.Abs(
                                                	prev[eachSubMB_x + d 
                                                    + eachSubMBMVAffine_x, eachSubMB_y 
                                                    + c + eachSubMBMVAffine_y] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                        }

                                        if (eachSubMB_x + eachSubMBMV_x + 4 < width 
                                        	&& eachSubMB_y + eachSubMBMV_y + 4 < height)
                                        {
                                            if (eachSubMB_x + eachSubMBMV_x < 0 
                                            	&& eachSubMB_y + eachSubMBMV_y < 0)
                                            {
                                                sum_persp_block += (byte)Math.Abs(
                                                	prev[0 + d, 0 + c] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                            else if (eachSubMB_x + eachSubMBMV_x < 0 
                                            	&& eachSubMB_y + eachSubMBMV_y >= 0)
                                            {
                                                sum_persp_block += (byte)Math.Abs(
                                                	prev[0 + d, eachSubMB_y 
                                                    + c + eachSubMBMV_y] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                            else if (eachSubMB_x + eachSubMBMV_x >= 0 
                                            	&& eachSubMB_y + eachSubMBMV_y < 0)
                                            {
                                                sum_persp_block += (byte)Math.Abs(
                                                	prev[eachSubMB_x + d + eachSubMBMV_x, 
                                                    0 + c] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                            else
                                            {
                                                sum_persp_block += (byte)Math.Abs(
                                                	prev[eachSubMB_x + d + eachSubMBMV_x, 
                                                    eachSubMB_y + c + eachSubMBMV_y] 
                                                    - curr[eachSubMB_x + d, eachSubMB_y + c]);
                                            }
                                        }                                   
                                    }
                                }
                            }
                        }

                        if (Math.Min(trans_min_sum, 
                        	Math.Min(sum_affine_block, sum_persp_block)) 
                            == trans_min_sum)
                        {
                            for (int c = 0; c < MB; c++)
                            {
                                for (int d = 0; d < MB; d++)
                                {
                                    result[f, w + d, h + c] = (byte)Math.Abs(
                                    	prev[trans_x_prime + d, trans_y_prime + c] 
                                        - curr[w + d, h + c]);
                                    allResi[f] += result[f, w + d, h + c];
                                }
                            }
                        }
                        else
                        {
                            for (int a = 0; a < MB; a += 4)
                            {
                                for (int b = 0; b < MB; b += 4)
                                {
                                    int eachSubMB_x = w + b;
                                    int eachSubMB_y = h + a;
                                    int eachSubMBcenter_x = b + (4 / 2);
                                    int eachSubMBcenter_y = a + (4 / 2);

                                    if (sum_affine_block <= sum_persp_block)
                                    {
                                        double eachSubMBcenterPrimeAffine_x 
                                        	= ((affineParam[0, 0] * eachSubMBcenter_x) 
                                            + (affineParam[1, 0] * eachSubMBcenter_y) 
                                            + (affineParam[4, 0]));
                                        double eachSubMBcenterPrimeAffine_y 
                                        	= ((affineParam[2, 0] * eachSubMBcenter_x) 
                                            + (affineParam[3, 0] * eachSubMBcenter_y) 
                                            + (affineParam[5, 0]));

                                        int eachSubMBMVAffine_x 
                                        	= (int)(eachSubMBcenterPrimeAffine_x 
                                            - (double)eachSubMBcenter_x);
                                        int eachSubMBMVAffine_y 
                                        	= (int)(eachSubMBcenterPrimeAffine_y 
                                            - (double)eachSubMBcenter_y);

                                        for (int c = 0; c < 4; c++)
                                        {
                                            for (int d = 0; d < 4; d++)
                                            {
                                                if (eachSubMB_x + eachSubMBMVAffine_x + 4 
                                                    < width 
                                                    && eachSubMB_y + eachSubMBMVAffine_y + 4 
                                                    < height)
                                                {
                                                    if (eachSubMB_x + eachSubMBMVAffine_x < 0 
                                                    	&& eachSubMB_y + eachSubMBMVAffine_y 
                                                        < 0)
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                            eachSubMB_y + c] 
                                                            = (byte)Math.Abs(prev[0 + d, 
                                                            0 + c] 
                                                            - curr[eachSubMB_x + d, 
                                                            eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                    else if (eachSubMB_x + eachSubMBMVAffine_x 
                                                    	< 0 
                                                        && eachSubMB_y + eachSubMBMVAffine_y 
                                                        >= 0)
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(prev[0 + d, 
                                                            eachSubMB_y 
                                                            + c + eachSubMBMVAffine_y] 
                                                            - curr[eachSubMB_x + d, 
                                                            eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                    else if (eachSubMB_x + eachSubMBMVAffine_x 
                                                    	>= 0 
                                                        && eachSubMB_y + eachSubMBMVAffine_y 
                                                        < 0)
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(
                                                                prev[eachSubMB_x + d 
                                                                + eachSubMBMVAffine_x, 0 + c] 
                                                                - curr[eachSubMB_x + d, 
                                                                eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                    else
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(
                                                                prev[eachSubMB_x + d 
                                                                + eachSubMBMVAffine_x, 
                                                                eachSubMB_y 
                                                                + c + eachSubMBMVAffine_y] 
                                                                - curr[eachSubMB_x + d, 
                                                                eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        double eachSubMBcenterPrime_x 
                                        	= (((perspectiveParam[0, 0] * eachSubMBcenter_x) 
                                            + (perspectiveParam[1, 0] * eachSubMBcenter_y) 
                                            + (perspectiveParam[2, 0])) 
                                            / (double)((perspectiveParam[6, 0] 
                                            * eachSubMBcenter_x) 
                                            + (perspectiveParam[7, 0] * eachSubMBcenter_y) 
                                            + 1));
                                        double eachSubMBcenterPrime_y 
                                        	= (((perspectiveParam[3, 0] * eachSubMBcenter_x) 
                                            + (perspectiveParam[4, 0] * eachSubMBcenter_y) 
                                            + (perspectiveParam[5, 0])) 
                                            / (double)((perspectiveParam[6, 0] 
                                            * eachSubMBcenter_x) 
                                            + (perspectiveParam[7, 0] * eachSubMBcenter_y) 
                                            + 1));

                                        int eachSubMBMV_x = (int)(eachSubMBcenterPrime_x 
                                        	- (double)eachSubMBcenter_x);
                                        int eachSubMBMV_y = (int)(eachSubMBcenterPrime_y 
                                        	- (double)eachSubMBcenter_y);

                                        for (int c = 0; c < 4; c++)
                                        {
                                            for (int d = 0; d < 4; d++)
                                            {
                                                if (eachSubMB_x + eachSubMBMV_x + 4 < width 
                                                	&& eachSubMB_y + eachSubMBMV_y + 4 
                                                    < height)
                                                {
                                                    if (eachSubMB_x + eachSubMBMV_x < 0 
                                                    	&& eachSubMB_y + eachSubMBMV_y < 0)
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                            eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(prev[0 + d
                                                            , 0 + c] 
                                                            - curr[eachSubMB_x + d, eachSubMB_y 
                                                            + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                    else if (eachSubMB_x + eachSubMBMV_x < 0 
                                                    	&& eachSubMB_y + eachSubMBMV_y >= 0)
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                            eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(
                                                            prev[0 + d, eachSubMB_y 
                                                            + c 
                                                            + eachSubMBMV_y] 
                                                            - curr[eachSubMB_x + d, 
                                                            eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                    else if (eachSubMB_x + eachSubMBMV_x >= 0 
                                                    	&& eachSubMB_y + eachSubMBMV_y < 0)
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                            eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(
                                                            prev[eachSubMB_x + d 
                                                            + eachSubMBMV_x, 0 + c] 
                                                            - curr[eachSubMB_x + d, 
                                                            eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                    else
                                                    {
                                                        result[f, eachSubMB_x + d, 
                                                            eachSubMB_y + c] 
                                                        	= (byte)Math.Abs(
                                                            prev[eachSubMB_x + d 
                                                            + eachSubMBMV_x, eachSubMB_y 
                                                            + c + eachSubMBMV_y] 
                                                            - curr[eachSubMB_x + d, 
                                                            eachSubMB_y + c]);
                                                        allResi[f] += result[f, eachSubMB_x + d, 
                                                        	eachSubMB_y + c];
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } // current MB
                }
            }
            return result;
        }

        private static byte[] make_stream(byte[,,] residual, int frame, int width, int height)
        {
            ...
        }

        private static byte[,] get_frame(byte[] luma, int f, int width, int height)
        {
            ...
        }

        public static double[,] GetInverseMatrix(double[,] input, int dim)
        {
            ...
        }

        public static double[,] MMult(double[,] m1, double[,] m2)
        {
            ...
        }
    }
}