about summary refs log tree commit diff
path: root/converter/ppm/ppmtompeg/psearch.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/ppm/ppmtompeg/psearch.c')
-rw-r--r--converter/ppm/ppmtompeg/psearch.c989
1 files changed, 989 insertions, 0 deletions
diff --git a/converter/ppm/ppmtompeg/psearch.c b/converter/ppm/ppmtompeg/psearch.c
new file mode 100644
index 00000000..aea1a29b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/psearch.c
@@ -0,0 +1,989 @@
+/*===========================================================================*
+ * psearch.c                                     
+ *                                       
+ *  Procedures concerned with the P-frame motion search          
+ *                                       
+ *===========================================================================*/
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "motion_search.h"
+#include "prototypes.h"
+#include "fsize.h"
+#include "param.h"
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+/* none */
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+int **pmvHistogram = NULL;  /* histogram of P-frame motion vectors */
+int **bbmvHistogram = NULL; /* histogram of B-frame bkwd motion vectors */
+int **bfmvHistogram = NULL; /* histogram of B-frame fwd motion vectors */
+int pixelFullSearch;
+int searchRangeP,searchRangeB;
+/* The range, in half pixels in each direction, that we are to search
+       when detecting motion.  Specified by RANGE statement in parameter file.
+    */
+int psearchAlg;
+/* specified by parameter file. */
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ *  Compute the best P-frame motion vector we can.  If it's better than
+ *  *motionP, update *motionP to it.
+ *
+ * PRECONDITIONS:   The relevant block in 'current' is valid (it has not
+ *          been dct'd).  Thus, the data in 'current' can be
+ *          accesed through y_blocks, cr_blocks, and cb_blocks.
+ *          This is not the case for the blocks in 'prev.'
+ *          Therefore, references into 'prev' should be done
+ *          through the struct items ref_y, ref_cr, ref_cb
+ *
+ * POSTCONDITIONS:  current, prev unchanged.
+ *          Some computation could be saved by requiring
+ *          the dct'd difference to be put into current's block
+ *          elements here, depending on the search technique.
+ *          However, it was decided that it mucks up the code
+ *          organization a little, and the saving in computation
+ *          would be relatively little (if any).
+ *
+ * NOTES:   the search procedure need not check the (0,0) motion vector
+ *      the calling procedure has a preference toward (0,0) and it
+ *      will check it itself
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+PMotionSearch(const LumBlock * const currentBlockP, 
+              MpegFrame *      const prev, 
+              int              const by,
+              int              const bx, 
+              vector *         const motionP) {
+
+    /* CALL SEARCH PROCEDURE */
+
+    switch(psearchAlg) {
+    case PSEARCH_SUBSAMPLE:
+        PSubSampleSearch(currentBlockP, prev, by, bx, motionP, searchRangeP);
+        break;
+    case PSEARCH_EXHAUSTIVE:
+        PLocalSearch(currentBlockP, prev, by, bx, 
+                     motionP, INT_MAX, searchRangeP);
+        break;
+    case PSEARCH_LOGARITHMIC:
+        PLogarithmicSearch(currentBlockP, prev, by, bx, motionP, searchRangeP);
+        break;
+    case PSEARCH_TWOLEVEL:
+        PTwoLevelSearch(currentBlockP, prev, by, bx, 
+                        motionP, INT_MAX, searchRangeP);
+        break;
+    default:
+        pm_error("IMPOSSIBLE PSEARCH ALG:  %d", psearchAlg);
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetPixelSearch
+ *
+ *  set the pixel search type (half or full)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    pixelFullSearch
+ *
+ *===========================================================================*/
+void
+SetPixelSearch(const char * const searchType) {
+    if ( (strcmp(searchType, "FULL") == 0 ) || 
+         ( strcmp(searchType, "WHOLE") == 0 )) {
+        pixelFullSearch = TRUE;
+    } else if ( strcmp(searchType, "HALF") == 0 ) {
+        pixelFullSearch = FALSE;
+    } else {
+        fprintf(stderr, "ERROR:  Invalid pixel search type:  %s\n",
+                searchType);
+        exit(1);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * SetPSearchAlg
+ *
+ *  set the P-search algorithm
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    psearchAlg
+ *
+ *===========================================================================*/
+void
+SetPSearchAlg(const char * const alg)
+{
+    if ( strcmp(alg, "EXHAUSTIVE") == 0 ) {
+        psearchAlg = PSEARCH_EXHAUSTIVE;
+    } else if (strcmp(alg, "SUBSAMPLE") == 0 ) {
+        psearchAlg = PSEARCH_SUBSAMPLE;
+    } else if ( strcmp(alg, "LOGARITHMIC") == 0 ) {
+        psearchAlg = PSEARCH_LOGARITHMIC;
+    } else if ( strcmp(alg, "TWOLEVEL") == 0 ) {
+        psearchAlg = PSEARCH_TWOLEVEL;
+    } else {
+        fprintf(stderr, "ERROR:  Invalid psearch algorithm:  %s\n", alg);
+        exit(1);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * PSearchName
+ *
+ *  returns a string containing the name of the search algorithm
+ *
+ * RETURNS: pointer to the string
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+const char *
+PSearchName(void)
+{
+    const char *retval;
+
+    switch(psearchAlg) {
+    case PSEARCH_EXHAUSTIVE:
+        retval = "EXHAUSTIVE";break;
+    case PSEARCH_SUBSAMPLE:
+        retval = "SUBSAMPLE";break;
+    case PSEARCH_LOGARITHMIC:
+        retval = "LOGARITHMIC";break;
+    case PSEARCH_TWOLEVEL:
+        retval = "TWOLEVEL";break;
+    default:
+        fprintf(stderr, "ERROR:  Illegal PSEARCH ALG:  %d\n", psearchAlg);
+        exit(1);
+        break;
+    }
+    return retval;
+}
+
+
+/*===========================================================================*
+ *
+ * SetSearchRange
+ *
+ *  sets the range of the search to the given number of pixels,
+ *  allocate histogram storage
+ *
+ *===========================================================================*/
+void
+SetSearchRange(int const pixelsP, int const pixelsB) {
+
+    searchRangeP = 2*pixelsP;   /* +/- 'pixels' pixels */
+    searchRangeB = 2*pixelsB;
+
+    if ( computeMVHist ) {
+        int const max_search = max(searchRangeP, searchRangeB);
+
+        int index;
+    
+        pmvHistogram = (int **) malloc((2*searchRangeP+3)*sizeof(int *));
+        bbmvHistogram = (int **) malloc((2*searchRangeB+3)*sizeof(int *));
+        bfmvHistogram = (int **) malloc((2*searchRangeB+3)*sizeof(int *));
+        for ( index = 0; index < 2*max_search+3; index++ ) {
+            pmvHistogram[index] = 
+                (int *) calloc(2*searchRangeP+3, sizeof(int));
+            bbmvHistogram[index] = 
+                (int *) calloc(2*searchRangeB+3, sizeof(int));
+            bfmvHistogram[index] = 
+                (int *) calloc(2*searchRangeB+3, sizeof(int));
+        }
+    }
+}
+
+
+/*===========================================================================*
+ *
+ *              USER-MODIFIABLE
+ *
+ * MotionSearchPreComputation
+ *
+ *  do whatever you want here; this is called once per frame, directly
+ *  after reading
+ *
+ * RETURNS: whatever
+ *
+ * SIDE EFFECTS:    whatever
+ *
+ *===========================================================================*/
+void
+MotionSearchPreComputation(MpegFrame * const frameP) {
+    /* do nothing */
+}
+
+
+/*===========================================================================*
+ *
+ * PSubSampleSearch
+ *
+ *  uses the subsampling algorithm to compute the P-frame vector
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ * REFERENCE:  Liu and Zaccarin:  New Fast Algorithms for the Estimation
+ *      of Block Motion Vectors, IEEE Transactions on Circuits
+ *      and Systems for Video Technology, Vol. 3, No. 2, 1993.
+ *
+ *===========================================================================*/
+int
+PSubSampleSearch(const LumBlock * const currentBlockP,
+                 MpegFrame *      const prev, 
+                 int              const by,
+                 int              const bx,
+                 vector *         const motionP,
+                 int              const searchRange) {
+    
+    int my, mx;
+    int bestBestDiff;
+    int stepSize;
+    int x;
+    int bestMY[4], bestMX[4], bestDiff[4];
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+
+    stepSize = (pixelFullSearch ? 2 : 1);
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX);
+
+    if ( searchRange < rightMY ) {
+        rightMY = searchRange;
+    }
+
+    if ( searchRange < rightMX ) {
+        rightMX = searchRange;
+    }
+
+    for ( x = 0; x < 4; x++ ) {
+        bestMY[x] = 0;
+        bestMX[x] = 0;
+        bestDiff[x] = INT_MAX;
+    }
+
+    /* do A pattern */
+    for (my = -searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for ( mx = -searchRange; mx < rightMX; mx += 2*stepSize ) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorA(currentBlockP, prev, by, bx, m,
+                                           bestDiff[0]);
+                    
+                    if (diff < bestDiff[0]) {
+                        bestMY[0] = my;
+                        bestMX[0] = mx;
+                        bestDiff[0] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* do B pattern */
+    for (my = stepSize-searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for (mx = -searchRange; mx < rightMX; mx += 2*stepSize) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorB(currentBlockP, prev, by, bx, m, 
+                                           bestDiff[1]);
+                    
+                    if (diff < bestDiff[1]) {
+                        bestMY[1] = my;
+                        bestMX[1] = mx;
+                        bestDiff[1] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* do C pattern */
+    for (my = stepSize-searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for ( mx = stepSize-searchRange; mx < rightMX; mx += 2*stepSize ) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorC(currentBlockP, prev, by, bx, m,
+                                           bestDiff[2]);
+                    
+                    if (diff < bestDiff[2]) {
+                        bestMY[2] = my;
+                        bestMX[2] = mx;
+                        bestDiff[2] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* do D pattern */
+    for (my = -searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for (mx = stepSize-searchRange; mx < rightMX; mx += 2*stepSize) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorD(currentBlockP, prev, by, bx, m,
+                                           bestDiff[3]);
+                    
+                    if (diff < bestDiff[3]) {
+                        bestMY[3] = my;
+                        bestMX[3] = mx;
+                        bestDiff[3] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* first check old motion */
+    if ((motionP->y >= leftMY) && (motionP->y < rightMY) &&
+        (motionP->x >= leftMX) && (motionP->x < rightMX)) {
+        bestBestDiff = LumMotionError(currentBlockP, prev, by, bx, 
+                                      *motionP, INT_MAX);
+    } else
+        bestBestDiff = INT_MAX;
+
+    /* look at Error of 4 different motion vectors */
+    for (x = 0; x < 4; ++x) {
+        vector m;
+        m.y = bestMY[x];
+        m.x = bestMX[x];
+        bestDiff[x] = LumMotionError(currentBlockP, prev, by, bx, m,
+                                     bestBestDiff);
+
+        if (bestDiff[x] < bestBestDiff) {
+            bestBestDiff = bestDiff[x];
+            *motionP     = m;
+        }
+    }
+    return bestBestDiff;
+}
+
+
+
+static void
+findBestSpaced(int              const minMY,
+               int              const minMX,
+               int              const maxMY,
+               int              const maxMX,
+               int              const spacing,
+               const LumBlock * const currentBlockP, 
+               MpegFrame *      const prev,
+               int              const by,
+               int              const bx,
+               int *            const bestDiffP, 
+               vector *         const centerP) {
+/*----------------------------------------------------------------------------
+   Examine every 'spacing'th half-pixel within the rectangle 
+   ('minBoundX', 'minBoundY', 'maxBoundX', 'maxBoundY'), 
+
+   If one of the half-pixels examined has a lower "LumMotionError" value
+   than *bestDiffP, update *bestDiffP to that value and update
+   *centerP to the location of that half-pixel.
+-----------------------------------------------------------------------------*/
+    int const minBoundY = MAX(minMY, centerP->y - spacing);
+    int const minBoundX = MAX(minMX, centerP->x - spacing);
+    int const maxBoundY = MIN(maxMY, centerP->y + spacing + 1);
+    int const maxBoundX = MIN(maxMX, centerP->x + spacing + 1);
+
+    int my;
+
+    for (my = minBoundY; my < maxBoundY; my += spacing) {
+        int mx;
+
+        for (mx = minBoundX; mx < maxBoundX; mx += spacing) {
+            int diff;
+            vector m;
+
+            m.y = my; m.x = mx;
+            
+            diff = LumMotionError(currentBlockP, prev, by, bx, m, *bestDiffP);
+            
+            if (diff < *bestDiffP) {
+                *centerP   = m;
+                *bestDiffP = diff;
+            }
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * PLogarithmicSearch
+ *
+ *  uses logarithmic search to compute the P-frame vector
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ * REFERENCE:  MPEG-I specification, pages 32-33
+ *
+ *===========================================================================*/
+int
+PLogarithmicSearch(const LumBlock * const currentBlockP,
+                   MpegFrame *      const prev, 
+                   int              const by,
+                   int              const bx, 
+                   vector *         const motionP,
+                   int              const searchRange) {
+
+    int const stepSize = (pixelFullSearch ? 2 : 1);
+
+    int minMY, minMX, maxMY, maxMX;
+    int spacing;  /* grid spacing */
+    vector motion;
+        /* Distance from (bx,by) (in half-pixels) of the block that is most
+           like the current block among those that we have examined so far.
+           (0,0) means we haven't examined any.
+        */
+    int bestDiff;
+        /* The difference between the current block and the block offset
+           'motion' from it.
+        */
+    
+    COMPUTE_MOTION_BOUNDARY(by, bx, stepSize, minMY, minMX, maxMY, maxMX);
+    minMX = max(minMX, - searchRange);
+    minMY = max(minMY, - searchRange);
+    maxMX = min(maxMX, + searchRange);
+    maxMY = min(maxMY, + searchRange);
+
+    /* Note: The clipping to 'searchRange' above may seem superfluous because
+       the basic algorithm would never want to look more than 'searchRange'
+       pixels away, but with rounding error, it can.
+    */
+
+    motion.x = motion.y = 0;
+    bestDiff = INT_MAX;
+
+    for (spacing = searchRange; spacing >= stepSize;) {
+        if (stepSize == 2) {  /* make sure spacing is even */
+            if (spacing == 2) 
+                spacing = 0;
+            else {
+                spacing = (spacing+1)/2;
+                if (spacing % 2 != 0) 
+                    --spacing;
+            }
+        } else {
+            if (spacing == 1) {
+                spacing = 0;
+            } else 
+                spacing = (spacing + 1) / 2;
+        }
+        if (spacing >= stepSize) 
+            findBestSpaced(minMY, minMX, maxMY, maxMX,
+                           spacing, currentBlockP, prev, by, bx,
+                           &bestDiff, &motion);
+    }
+
+    {
+        int diff;
+        /* check old motion -- see if it's better */
+        if ((motionP->y >= minMY) && (motionP->y < maxMY) &&
+            (motionP->x >= minMX) && (motionP->x < maxMX)) {
+            diff = LumMotionError(currentBlockP, prev, by, bx, 
+                                  *motionP, bestDiff);
+        } else 
+            diff = INT_MAX;
+        
+        if (bestDiff < diff)
+            *motionP = motion;
+        else 
+            bestDiff = diff;
+    }
+
+    return bestDiff;
+}
+
+
+
+/*===========================================================================*
+ *
+ * PLocalSearch
+ *
+ *  uses local exhaustive search to compute the P-frame vector
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+PLocalSearch(const LumBlock * const currentBlockP,
+             MpegFrame *      const prev, 
+             int              const by,
+             int              const bx,
+             vector *         const motionP,
+             int              const bestSoFar,
+             int              const searchRange) {
+
+    int mx, my;
+    int bestDiff;
+    int stepSize;
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+    int distance;
+    int tempRightMY, tempRightMX;
+
+    stepSize = (pixelFullSearch ? 2 : 1);
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX);
+
+    /* try old motion vector first */
+    if (VALID_MOTION(*motionP)) {
+        bestDiff = LumMotionError(currentBlockP, prev, by, bx, 
+                                  *motionP, bestSoFar);
+
+        if (bestSoFar < bestDiff)
+            bestDiff = bestSoFar;
+    } else {
+        motionP->y = motionP->x = 0;
+        bestDiff = bestSoFar;
+    }
+
+    /* try a spiral pattern */    
+    for (distance = stepSize; distance <= searchRange; distance += stepSize) {
+        tempRightMY = MIN(distance, rightMY);
+        tempRightMX = MIN(distance, rightMX);
+
+        /* do top, bottom */
+        for (my = -distance; my < tempRightMY;
+             my += max(tempRightMY+distance-stepSize, stepSize)) {
+            if (my >= leftMY) {
+                for ( mx = -distance; mx < tempRightMX; mx += stepSize ) {
+                    if (mx >= leftMX) {
+                        int diff;
+                        vector m;
+                        
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m, 
+                                              bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* do left, right */
+        for (mx = -distance; mx < tempRightMX;
+             mx += max(tempRightMX+distance-stepSize, stepSize)) {
+
+            if (mx >= leftMX) {
+                for (my = -distance+stepSize; my < tempRightMY-stepSize;
+                     my += stepSize) {
+                    if (my >= leftMY) {
+                        int diff;
+                        vector m;
+
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m,
+                                              bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return bestDiff;
+}
+
+
+/*===========================================================================*
+ *
+ * PTwoLevelSearch
+ *
+ *  uses two-level search to compute the P-frame vector
+ *  first does exhaustive full-pixel search, then looks at neighboring
+ *  half-pixel motion vectors
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+PTwoLevelSearch(const LumBlock * const currentBlockP,
+                MpegFrame *      const prev, 
+                int              const by,
+                int              const bx, 
+                vector *         const motionP,
+                int              const bestSoFar,
+                int              const searchRange) {
+    int mx, my;
+    int loopInc;
+    int diff, bestDiff;
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+    int distance;
+    int tempRightMY, tempRightMX;
+    int xOffset, yOffset;
+
+    /* exhaustive full-pixel search first */
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,2,leftMY,leftMX,rightMY,rightMX);
+
+    rightMY--;
+    rightMX--;
+
+    /* convert vector into full-pixel vector */
+    if (motionP->y > 0) {
+        if ((motionP->y % 2) == 1) {
+            --motionP->y;
+        }
+    } else if (((-motionP->y) % 2) == 1)
+        ++motionP->y;
+
+    if (motionP->x > 0) {
+        if ((motionP->x % 2) == 1)
+            --motionP->x;
+    } else if ((-motionP->x % 2) == 1)
+        ++motionP->x;
+
+    /* try old motion vector first */
+    if (VALID_MOTION(*motionP)) {
+        bestDiff = LumMotionError(currentBlockP, prev, by, bx, 
+                                  *motionP, bestSoFar);
+
+        if ( bestSoFar < bestDiff ) {
+            bestDiff = bestSoFar;
+        }
+    } else {
+        motionP->y = motionP->x = 0;
+        bestDiff = bestSoFar;
+    }
+
+    ++rightMY;
+    ++rightMX;
+
+    /* try a spiral pattern */    
+    for ( distance = 2; distance <= searchRange; distance += 2 ) {
+        tempRightMY = MIN(distance, rightMY);
+        tempRightMX = MIN(distance, rightMX);
+
+        /* do top, bottom */
+        loopInc = max(tempRightMY + distance - 2, 2);
+        for (my = -distance; my < tempRightMY; my += loopInc) {
+            if (my >= leftMY) {
+                for (mx = -distance; mx < tempRightMX; mx += 2) {
+                    if (mx >= leftMX) {
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m, 
+                                              bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* do left, right */
+        loopInc = max(tempRightMX+distance-2, 2);
+        for (mx = -distance; mx < tempRightMX; mx += loopInc) {
+            if (mx >= leftMX) {
+                for ( my = -distance+2; my < tempRightMY-2; my += 2 ) {
+                    if (my >= leftMY) {
+                        int diff;
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m, 
+                                              bestDiff);
+                        
+                        if ( diff < bestDiff ) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* now look at neighboring half-pixels */
+    my = motionP->y;
+    mx = motionP->x;
+
+    --rightMY;
+    --rightMX;
+
+    for (yOffset = -1; yOffset <= 1; ++yOffset) {
+        for (xOffset = -1; xOffset <= 1; ++xOffset) {
+            if ((yOffset != 0) || (xOffset != 0)) {
+                vector m;
+                m.y = my+yOffset; m.x = mx+xOffset;
+                if (VALID_MOTION(m)) {
+                    int diff;
+                    diff = LumMotionError(currentBlockP, prev, by, bx,
+                                          m, bestDiff);
+                    if (diff < bestDiff) {
+                        *motionP = m;
+                        bestDiff = diff;
+                    }
+                }
+            }
+        }
+    }
+    return bestDiff;
+}
+
+
+
+void
+ShowPMVHistogram(fpointer)
+    FILE *fpointer;
+{
+    register int x, y;
+    int *columnTotals;
+    int rowTotal;
+
+    columnTotals = (int *) calloc(2*searchRangeP+3, sizeof(int));
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "    ");
+    for ( y = 0; y < 2*searchRange+3; y++ ) {
+        fprintf(fpointer, "%3d ", y-searchRangeP-1);
+    }
+    fprintf(fpointer, "\n");
+#endif
+
+    for ( x = 0; x < 2*searchRangeP+3; x++ ) {
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%3d ", x-searchRangeP-1);
+#endif
+        rowTotal = 0;
+        for ( y = 0; y < 2*searchRangeP+3; y++ ) {
+            fprintf(fpointer, "%3d ", pmvHistogram[x][y]);
+            rowTotal += pmvHistogram[x][y];
+            columnTotals[y] += pmvHistogram[x][y];
+        }
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%4d\n", rowTotal);
+#else
+        fprintf(fpointer, "\n");
+#endif
+    }
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "Tot ");
+    for ( y = 0; y < 2*searchRangeP+3; y++ ) {
+        fprintf(fpointer, "%3d ", columnTotals[y]);
+    }
+#endif
+    fprintf(fpointer, "\n");
+}
+
+
+void
+ShowBBMVHistogram(fpointer)
+    FILE *fpointer;
+{
+    register int x, y;
+    int *columnTotals;
+    int rowTotal;
+
+    fprintf(fpointer, "B-frame Backwards:\n");
+
+    columnTotals = (int *) calloc(2*searchRangeB+3, sizeof(int));
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "    ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", y-searchRangeB-1);
+    }
+    fprintf(fpointer, "\n");
+#endif
+
+    for ( x = 0; x < 2*searchRangeB+3; x++ ) {
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%3d ", x-searchRangeB-1);
+#endif
+        rowTotal = 0;
+        for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+            fprintf(fpointer, "%3d ", bbmvHistogram[x][y]);
+            rowTotal += bbmvHistogram[x][y];
+            columnTotals[y] += bbmvHistogram[x][y];
+        }
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%4d\n", rowTotal);
+#else
+        fprintf(fpointer, "\n");
+#endif
+    }
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "Tot ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", columnTotals[y]);
+    }
+#endif
+    fprintf(fpointer, "\n");
+}
+
+
+void
+ShowBFMVHistogram(fpointer)
+    FILE *fpointer;
+{
+    register int x, y;
+    int *columnTotals;
+    int rowTotal;
+
+    fprintf(fpointer, "B-frame Forwards:\n");
+
+    columnTotals = (int *) calloc(2*searchRangeB+3, sizeof(int));
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "    ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", y-searchRangeB-1);
+    }
+    fprintf(fpointer, "\n");
+#endif
+
+    for ( x = 0; x < 2*searchRangeB+3; x++ ) {
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%3d ", x-searchRangeB-1);
+#endif
+        rowTotal = 0;
+        for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+            fprintf(fpointer, "%3d ", bfmvHistogram[x][y]);
+            rowTotal += bfmvHistogram[x][y];
+            columnTotals[y] += bfmvHistogram[x][y];
+        }
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%4d\n", rowTotal);
+#else
+        fprintf(fpointer, "\n");
+#endif
+    }
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "Tot ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", columnTotals[y]);
+    }
+#endif
+    fprintf(fpointer, "\n");
+}
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /u/smoot/md/mpeg_encode/RCS/psearch.c,v 1.9 1995/01/19 23:09:12 eyhung Exp $
+ *  $Log: psearch.c,v $
+ * Revision 1.9  1995/01/19  23:09:12  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.9  1995/01/19  23:09:12  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.8  1994/12/07  00:40:36  smoot
+ * Added seperate P and B search ranges
+ *
+ * Revision 1.7  1994/11/12  02:09:45  eyhung
+ * full pixel bug
+ * fixed on lines 512 and 563
+ *
+ * Revision 1.6  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.5  1993/12/22  19:19:01  keving
+ * nothing
+ *
+ * Revision 1.4  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ * Revision 1.3  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.2  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.1  1993/03/02  18:27:05  keving
+ * nothing
+ *
+ */
+
+