Calculation of Motion Using Motion Vectors Extracted From An MPEG Stream
Calculation of Motion Using Motion Vectors Extracted From An MPEG Stream
JOSEPH GILVARRY
School of Computer Applications
Contents
Abstract 1 Description of a Motion Vector 2 Operation of the program
2.1 Reordering the bit stream order to the display order 2.2 Storing the motion vectors 2.3 Explanation of the Algorithm 2.4Alterations made to the decoder
4 Implementing the rules 5 Using the Program 6 Using MatLab 7 Results 8 ToDo Appendix
Abstract
This document describes what a motion vector is, and how they can be used to show motion in an MPEG video. It describes the operation of the program used to find the motion. The results of some of the testing are also given.
Search area
x y
Macroblock x is the macroblock we wish to encode, macroblock y is its counterpart in the reference frame. A search is done around y to find the best match for x. This search is limited to a finite area, and even if there is a perfectly matching macroblock outside the search area, it will not be used. The displacement between the two macroblocks gives the motion vector associated with x.
Display Order
future 1I
5P 5P 5P 5P 10P 10P 10P 10P 10P 11I 15P 15P 15P 15P
arrays need to be created. The names of all the arrays used in this project are given below:
Array name
futureRight futureDown presentForwardRight presentForwardDown presentBackwardRight presentBackwardDown pastForwardRight pastForwardDown pastBackwardRight pastBackwardDown
Function of array
Store the motion vectors in a P frame until it is the P frames turn in the display order. Stores the motion vectors of the present frame in the display order Stores the motion vectors of the previous frame in the display order
Frame comes in
Is it an I, P or B frame?
Reset future
backward
Figure 2.2 Diagram of where the motion vectors for the different frames are stored
Frame comes in
Reset present
Is frame I or P Type?
Yes Take Vectors out of future and put in present Reset future
No
Is frame I or P type ?
frame to frame. Only vectors with the same reference point can be subtracted from each other. To illustrate, lets take the simple example of x moving across a portion of the screen as shown in Figure 3.1 1 x x I B B B Figure 3.1 Motion vectors associated with a moving picture. The arrow represents a forward vector and P 2 x 3 4 x 5 x
forward vector doesnt have to be pointing forward, and a backward vector pointing backward. It is just the nameing convention for whither the reference frame in the past (forward) or future (backward).] The values for the vectors are given below: In the first frame there are no motion vectors In frame 2: forwardRight = 2; forwardDown = -3; (2, -3) backwardRight = -7; backwardDown = 5; (-7, 4) Frame 3: forward = (4, -6) backward = (-5, 1) Frame 4: forward = (7, -7) backward = (-2, 0) Frame 5: forward = (9, -7) Transition 1 To find the motion in the transition from frame one to frame two, we can only use the forward vector. The backward vector has no reference in the I frame. The motion is just (2, -3) Transition 2 Here, the forward and backward vectors can be used as both forward vectors have the same reference point and both backward vectors have the same reference point. presentForward - pastForward = forward motion (4, -6) - (2, -3) = (2, -3)
(-5, -1)
(-7, 4)
= (2, -3)
To find the total motion avarage the two results motionRight = (2+2)/2 = 2 motionDown = (-3+-3)/2 = -3 Total motion = (2, -3) Note in this example the forward motion will always equal the backward motion but this is not usually the case in video.
Transition 3 forward backward (7, -7) - (4, -6) = (3, -1) (-2, 0) - (-5, 1) = (3, -1)
Transition 4 Both the forward and backward vectors can be used here. Both forward vectors are referenced to the same point and, as the B frames backward vector is referenced to the P frame. The P frame is said to have a zero backward vector. forward backward (9 ,-7) - (7, -7) = (2, 0) (0, 0) - (-2, 0) = (2, 0)
The motion for the sequence is: (2,-3), (2, -3), (3, -1), (2, 0)
Table 3.1 Vector types that can be used in the transition from frame to frame past present Vector types that can be subtracted I B or P forward only I I None P B or P forward only P I None B B or P forward and backward B I backward only
I Frame to B or P Frame: When going from an I frame to a B or P frame only the forward motion vectors can be used. The P frame will only have forward vectors, the B frames backward vectors cant be used as they have no reference in the I frame. I Frame to I frame: There are no vectors present in either frame. P Frame to P or B frame: None of the backward vectors in the B frame have a reference in the P frame. Therefore only forward vectors can be used. P Frame to I Frame: The forward vectors in the P frame do not have a reference in the I frame. No motion can be found. B Frame to B or P Frame: Both forward and backward vectors can be used as both have the same reference point from frame to frame. B Frame to I Frame: Only the backward vectors are referenced in the I frame.
In this example the transition from frame 1 to frame 2 can be calculated as before. If the second transition is calulated as before we get: forward motion: (4, -6) - (2, -3) = (2, -3) backward motion: (-5, 1) - (0, 0) = (-5, 1) Total motion = (-1.5, -1) This result is incorrect. To get the correct result, only the forward motion can be used. Similarly only the backward motion is used for the third transition. The motion for the final transition can not be found because there is only a backward vector in frame 4 and only a forward vector in frame 5. Only similar types of vector can be subtracted from each other.
Below are further rules to complement the rules that were established in table 3.1 Only if there is a similar type of vector (forward, backward or both) present in both frames can the motion be found. A reference frame is said to have all vectors equal to (0, 0) If there is a skipped macroblock in the present P frame, there is zero motion for that transition. If there is a skipped macroblock in the previous P frame, the motion for that transition cant be calulated. An exception to this is if there is also a skipped macroblock in the present P frame in which case the motion will be zero. If there is an Intra macroblock in either the present or previous frame, the motion for that transition cant be calculated.
backward). The arrays are the same dimensions as all the other arrays, ie the height and the width of the screen in macroblocks. The arrays are initialised with to zero. When the vector values are being entered into the appropriate array eg
presentForwardRight
corresponding status array (sPresent or sFutureB). When it time to subtract the vectors only if there is a 1 in both sPast and sPresent (or sPastB and
sPresentB) is the subtraction allowed.
NB If Motion.txt already exists the results are just appended on to the end of the file. The results are outputted in a way that will allow them to be easily plotted. The array u contains the vectors components for the horizontal motion in the transition, v contains the components for the vertical motion.
6 Using MatLab
To make sure the correct vectors are being given out they had to be plotted, this was done in MatLab. There is a function in MatLab called quiver that allows vectors to be plotted. To use the function first a grid of the screen has to be set up this is done with the command: >>[x y] = meshgrid(1:1:22,1:1:18); The 22 is the number of columns and 18 is the number of rows. The u and v values from the output file are then entered. The command: >>quiver(x,y,u,v); plots the vectors. If the vectors are all very small you can use: >>quiver(x,y,u,v,s); s scales the vectors to the size you want.
7 Results
The program was run on BL01_Vid.mpg, below are some of the results that were obtained. Figure 7.1 shows a zoom in, in a zoom all the motion is away from the centre as shown by the arrows. Figure 7.2 shows a zoom out while the camera moves to the right.
Fig.7.1
Fig.7.2
ToDo
After implementing the rules the correct results were not always being outputted the first problem was that MPEG uses the convention down for vertical motion while MatLab uses up (positive direction). On correcting this a lot of the results were incorrect. I suggest that you should start on line 260 of Pictures.cc in /local6/joe/Motion/mp and test each of the if statements separately and just change the sign if the motion is in the wrong direction. Note I found that some times the arrows were in the correct direction but some times they were in the opposite direction, I cant see any reason for this. Maybe you should separate any if statements that allow a transition between two types of frame (a P or B frame).
Appendix
Pictures.cc #include #include #include #include #include #include #include "Berkeley.h" <string.h> <stdlib.h> "Pictures.h" "MPEGDecoder.h" "Slice.h" <fstream.h>
extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern
int int int int int int int int int int int int int int int int int int int
sFuture[18][22]; futureRight[18][22]; futureDown[18][22]; presentForwardRight[18][22]; presentForwardDown[18][22]; sPresent[18][22]; pastForwardRight[18][22]; pastForwardDown[18][22]; sPast[18][22]; presentBackwardRight[18][22]; presentBackwardDown[18][22]; sPresentB[18][22]; pastBackwardRight[18][22]; pastBackwardDown[18][22]; sPastB[18][22]; jFrameNo; pastIpb; presentIpb; futureIpb;
void Picture::HandlePictureData(BitStream *bs) { ParsePicHeader(bs); if ((fPictureType == eB_TYPE) && (!(fMPEG->HavePastReference() && fMPEG->HaveFutureReference()))) throw MPEG_Exception(3); if ((fPictureType == eP_TYPE) && !fMPEG->HaveFutureReference()) throw MPEG_Exception(3); /*********************************************************************/ ofstream fout("Motion.txt", ios::app); //Creat output file Motion.txt cout<<jFrameNo<<endl; //Print frame number (optional) fout<<"Frame "<<jFrameNo-1<<" to frame "<<jFrameNo<<endl; jFrameNo++; /*All present information is put in Past, Present is then reset*/ pastIpb = presentIpb; for (int i = 0; i<18; i++) { for (int j = 0; j<22; j++)
{ pastForwardRight[i][j] = presentForwardRight[i][j]; pastForwardDown[i][j] = presentForwardDown[i][j]; sPast[i][j] = sPresent[i][j]; presentForwardRight[i][j] = 0; presentForwardDown[i][j] = 0; sPresent[i][j] = 0; pastBackwardRight[i][j] = presentBackwardRight[i][j]; pastBackwardDown[i][j] = presentBackwardDown[i][j]; sPastB[i][j] = sPresentB[i][j]; presentBackwardRight[i][j] = 0; presentBackwardDown[i][j] = 0; sPresentB[i][j] = 0; motionRight[i][j] = 0; motionDown[i][j] = 0; } } /*If the frame is I or P take all the information out of future and put it in present, reset future. Future is now clear and can recive the information in the present frame (bitstream order)*/ if ((fPictureType == eP_TYPE)||(fPictureType == eI_TYPE)) { for(int i = 0; i<=18; i++) { for (int j = 0; j<= 22; j++) { presentForwardRight[i][j] = futureRight[i][j]; presentForwardDown[i][j] = futureDown[i][j]; sPresent[i][j] = sFuture[i][j]; futureRight[i][j] = 0; futureDown[i][j] = 0; sFuture[i][j] = 0; } } } /*******************************************************************/ ProcessSlices(bs); /*******************************************************************/ /*A record of the frame type has to be kept so the correct subtraction can be preformed*/ if (fPictureType == eI_TYPE) { presentIpb = futureIpb; futureIpb = 1; } if (fPictureType == eP_TYPE) { presentIpb = futureIpb; futureIpb = 2; } if (fPictureType == eB_TYPE) { presentIpb = 3; } /*Skipped macroblocks in B frames were not accounted for in the status arrays, the following code accounts for them (was causing problems to the P fames - don't know why*/ if (presentIpb !=2) { for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++)
{ if ((presentForwardRight[i][j] != 0)||(presentForwardDown[i][j] != 0)) sPresent[i][j] = 1; if ((presentBackwardRight[i][j] != 0)||(presentBackwardDown[i][j] != 0)) sPresentB[i][j] = 1; } } } /*If the transition is from an I frame to a P or B frame only subtract the forward vectors*/ if((pastIpb == 1)&&(presentIpb !=1)) { for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { motionRight[i][j] = -presentForwardRight[i][j]; motionUp[i][j] = presentForwardDown[i][j]; } } } */ /*If the transition is from a B frame to an I frame only subtract the backward vectors*/ if((pastIpb == 3)&&(presentIpb == 1)) { for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { motionRight[i][j] = pastBackwardRight[i][j]; motionUp[i][j] = -pastBackwardDown[i][j]; } } } /*If the transition is from a P frame to a P or B frame only subtract the forward vectors (if the macroblocks in both frames has a forward vector associated with it)*/ /* if((pastIpb == 2)&&(presentIpb != 1)) { for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { if((sPast[i][j] == 1)&&(sPresent[i][j] == 1)) { motionRight[i][j] = -presentForwardRight[i][j]; motionUp[i][j] = presentForwardDown[i][j]; } } } } */ /*If the transition is from a B frame to a B frame the motion is either the interpolated forward and backward motion, the forward motion or the backward motion depending on whither both frames have backward and forward vectors associated with them, forward only or backward only. If eithe frame doesn't
have a vector the motion is said to be zero. (Interpolatin is not working)*/ if((pastIpb == 3)&&(presentIpb == 3)) { for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { if((sPast[i][j] == 1)||(sPresent[i][j] == 1)) { motionRight[i][j] = pastForwardRight[i][j] - presentForwardRight[i][j]; motionUp[i][j] = -(pastForwardDown[i][j] - presentForwardDown[i][j]); } else if((sPastB[i][j] == 1)||(sPresentB[i][j] == 1)) { motionRight[i][j] = pastBackwardRight[i][j] presentBackwardRight[i][j]; motionUp[i][j] = -(pastBackwardDown[i][j] - presentBackwardDown[i][j]); } } } } /*If the transition is from a B frame to a P frame Only the backward vectors can be used*/ if((pastIpb == 3)&&(presentIpb == 2)) { for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { motionRight[i][j] = pastBackwardRight[i][j]; motionUp[i][j] = -pastBackwardDown[i][j]; } } } /*Write the values to the file Motion.txt*/ if(jFrameNo>2) { fout<<"u = ["; for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { fout<<motionRight[i][j]<<" "; } fout<<endl; } fout<<"];"<<endl; fout<<"v = ["; for(int i = 0; i<18; i++) { for (int j = 0; j<22; j++) { fout<<motionDown[i][j]<<" "; } fout<<endl; } fout<<"];"<<endl; } }
extern extern extern extern extern extern extern extern extern extern extern extern }
int int int int int int int int int int int int
sFuture[18][22]; futureRight[18][22]; futureDown[18][22]; presentForwardRight[18][22]; presentForwardDown[18][22]; sPresent[18][22]; presentBackwardRight[18][22]; presentBackwardDown[18][22]; sPresentB[18][22]; pastIpb; presentIpb; futureIpb;
void Macroblock::PBlockMotionVectors(vecComponents& v, Picture *pict) /*jrow and jcol give the macroblocks position in the frame*/ { jrow = fCurrentAddress / 22; jcol = fCurrentAddress % 22; // cout<< "P Block!"; if (!fHasForwardVector) { v.right_forward = 0; v.down_forward = 0; sFuture[jrow][jcol] = 0; // cout<< "P Block!\n"; fPrev.ReInit(); } else ComputeForwardVectorElements(v, pict); futureRight[jrow][jcol] = v.right_forward; futureDown[jrow][jcol] = v.down_forward; sFuture[jrow][jcol] = 1; } void Macroblock::BBlockMotionVectors(vecComponents& v, Picture *pict) { jrow = fCurrentAddress / 22; jcol = fCurrentAddress % 22; if (fIsIntraCoded) { fPrev.ReInit(); sPresent[jrow][jcol] = 0; } else { v = fPrev; if(fHasForwardVector) { ComputeForwardVectorElements(v, pict); presentForwardRight[jrow][jcol] = v.right_forward; presentForwardDown[jrow][jcol] = v.down_forward; sPresent[jrow][jcol] = 1; }
if(fHasBackwardVector) { ComputeBackwardVectorElements(v, pict); presentBackwardRight[jrow][jcol] = v.right_backward; presentBackwardDown[jrow][jcol] = v.down_backward; sPresentB[jrow][jcol] = 1; } fHadForwardVector = fHasForwardVector; fHadBackwardVector = fHasBackwardVector;
} }