VideoCodec/Modules/Prediction/MotionEstimation.cpp
#include "MotionEstimation.h"
namespace VideoCodec
{
MotionEstimation::MotionEstimation() : Module(MOTION_ESTIMATION_MODULE_ID)
{
}
MotionEstimation::~MotionEstimation()
{
}
ConfigurationStatus MotionEstimation::Configure(ConfigurationElement* configuration)
{
// SAE limit stopping condition.
if (configuration != NULL && configuration->GetValue("saeLimit") != NULL && configuration->GetValue("saeLimit")->GetType() == CONFIGURATION_INTEGER)
this->saeLimit = *(configuration->GetValue("saeLimit")->GetInteger());
else
this->saeLimit = 500;
// Distance of search.
if (configuration != NULL && configuration->GetValue("distance") != NULL && configuration->GetValue("distance")->GetType() == CONFIGURATION_INTEGER)
this->distance = *(configuration->GetValue("distance")->GetInteger());
else
this->distance = 8;
// Whether to search sub-pixel.
if (configuration != NULL && configuration->GetValue("subPixel") != NULL && configuration->GetValue("subPixel")->GetType() == CONFIGURATION_STRING)
this->subPixel = (*configuration->GetValue("subPixel")->GetString() == std::string("true"));
else
this->subPixel = true;
// This will be enabled by the pipeline if required.
this->mv4 = false;
return MODULE_CONFIGURATION_OK;
}
void MotionEstimation::ApplyMotionVectors(PictureInfo* subject, SignedByte* xComponents, SignedByte* yComponents)
{
// Use the specified vectors and pixels on the source instance to set pixel data on the destination.
if (subject->GetPixelLayout() != PIXEL_LAYOUT_SCAN)
{
Logger::LogMessage("Warning: Motion vector application requires scan order pixel data.");
return;
}
UnsignedByte* sourceData = subject->GetUnsignedByteData();
UnsignedByte* destinationData = new UnsignedByte[subject->GetPixelsX() * subject->GetPixelsY() * 3];
UnsignedByte blockRows = subject->GetBlockRows();
UnsignedByte blockColumns = subject->GetBlockColumns();
PixelCount stride = subject->GetPixelsX();
for (UnsignedByte blockY = 0; blockY < blockRows; blockY++)
{
for (UnsignedByte blockX = 0; blockX < blockColumns; blockX++)
{
for (unsigned char mvI = 0; mvI < ((this->mv4) ? 4 : 1); mvI++)
{
// The vector tells us where the pixel data for each block should come from.
SignedByte xDelta = xComponents[this->mv4 ? (blockX + blockY * blockColumns) * 4 + mvI : (blockX + blockY * blockColumns)];
SignedByte yDelta = yComponents[this->mv4 ? (blockX + blockY * blockColumns) * 4 + mvI : (blockX + blockY * blockColumns)];
// This will only ever apply at the encoder. The decoder has its values scaled by 2 at decoding if half pixel is off.
if (!this->subPixel)
{
xDelta *= 2;
yDelta *= 2;
}
SignedByte sx = 0; SignedByte sy = 0;
bool xNegative = xDelta < 0; bool yNegative = yDelta < 0;
UnsignedByte xMagnitude = xNegative ? -1 * xDelta : xDelta; UnsignedByte yMagnitude = yNegative ? -1 * yDelta : yDelta;
if ((xMagnitude & 1) != 0)
sx = (xNegative) ? -1 : 1;
if ((yMagnitude & 1) != 0)
sy = (yNegative) ? -1 : 1;
xDelta = xDelta / 2;
yDelta = yDelta / 2;
UnsignedByte xO = ((mvI == 1 || mvI == 3) ? 8 : 0);
UnsignedByte yO = ((mvI == 2 || mvI == 3) ? 8 : 0);
// Translates a block. Use an appropriate expression as curExpr to apply sub-pixel translation.
#define translate(curExpr,bSize,bxExpr,byExpr) \ for (UnsignedByte by = 0; by < bSize; by++)\ {\ for (UnsignedByte bx = 0; bx < bSize; bx++)\ {\ for (UnsignedByte componentIndex = 0; componentIndex < 3; componentIndex++)\ {\ destinationData[(blockX * 16 + bxExpr + stride * (blockY * 16 + byExpr)) * 3 + componentIndex] = (curExpr);\ }\ }\ };
// Integral pixel number.
if (sx == 0 && sy == 0)
{
if (!this->mv4)
{
translate((sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex]), 16, bx, by);
}
else
{
translate((sourceData[(blockX * 16 + bx + xO + xDelta + stride * (blockY * 16 + by + yO - yDelta)) * 3 + componentIndex]), 8, bx + xO, by + yO);
}
}
// E
if (sx == 1 && sy == 0)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + 1 + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex]
) >> 1, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + bx + xO + xDelta + stride * (blockY * 16 + by + yO - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xO + 1 + xDelta + stride * (blockY * 16 + by + yO - yDelta)) * 3 + componentIndex]
) >> 1, 8, bx + xO, by + yO);
}
}
// SE
else if (sx == 1 && sy == 1)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + 1 + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + 1 + xDelta + stride * (blockY * 16 + by - yDelta - 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta - 1)) * 3 + componentIndex]
) >> 2, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta - 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta - 1)) * 3 + componentIndex]
) >> 2, 8, bx + xO, by + yO);
}
}
// S
else if (sx == 0 && sy == 1)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta - 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex]
) >> 1, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta - 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex]
) >> 1, 8, bx + xO, by + yO);
}
}
// SW
else if (sx == -1 && sy == 1)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx - 1 + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx - 1 + xDelta + stride * (blockY * 16 + by - yDelta - 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta - 1)) * 3 + componentIndex]
) >> 2, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx - 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx - 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta - 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta - 1)) * 3 + componentIndex]
) >> 2, 8, bx + xO, by + yO);
}
}
// W
else if (sx == -1 && sy == 0)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx - 1 + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex]
) >> 1, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx - 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex]
) >> 1, 8, bx + xO, by + yO);
}
}
// NW
else if (sx == -1 && sy == -1)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx - 1 + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx - 1 + xDelta + stride * (blockY * 16 + by - yDelta + 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta + 1)) * 3 + componentIndex]
) >> 2, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx - 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx - 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta + 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta + 1)) * 3 + componentIndex]
) >> 2, 8, bx + xO, by + yO);
}
}
// N
else if (sx == 0 && sy == -1)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta + 1)) * 3 + componentIndex]
) >> 1, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta + 1)) * 3 + componentIndex]
) >> 1, 8, bx + xO, by + yO);
}
}
// NE
else if (sx == 1 && sy == -1)
{
if (!this->mv4)
{
translate((
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + 1 + xDelta + stride * (blockY * 16 + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + 1 + xDelta + stride * (blockY * 16 + by - yDelta + 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + bx + xDelta + stride * (blockY * 16 + by - yDelta + 1)) * 3 + componentIndex]
) >> 2, 16, bx, by);
}
else
{
translate((
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + 1 + xDelta + stride * (blockY * 16 + yO + by - yDelta + 1)) * 3 + componentIndex] +
sourceData[(blockX * 16 + xO + bx + xDelta + stride * (blockY * 16 + yO + by - yDelta + 1)) * 3 + componentIndex]
) >> 2, 8, bx + xO, by + yO);
}
}
#undef translate
}
}
}
subject->SetUnsignedByteData(destinationData, subject->GetPixelsX() * subject->GetPixelsY() * 3);
}
void MotionEstimation::CalculateMotionVectors(PictureInfo* reference, PictureInfo* current)
{
// We are trying to derive one vector for each block in current, positioned at the relevant block's
// centre, which gives an offset from the same block in the reference picture to a pixel block
// which has a lowest difference value based on the chosen metric.
// Here we use the sum of absolute differences metric, based on values in the luma channel.
// Also, we assume 16 * 16 macroblocks in the luma channel.
// 20th April 2007: Added half-pixel estimation.
// Make sure the input is in the correct format.
if (reference->GetPixelLayout() != PIXEL_LAYOUT_SCAN)
{
Logger::LogMessage("Warning: Motion vector calculation requires scan order pixel data.");
return;
}
if (reference->GetPixelsX() != current->GetPixelsX() || reference->GetPixelsY() != current->GetPixelsY())
{
Logger::LogMessage("Warning: Mismatched reference picture dimensions in motion vector calculation.");
return;
}
BlockCount blockColumns = reference->GetPixelsX() >> 4;
BlockCount blockRows = reference->GetPixelsY() >> 4;
UnsignedShort imageWidth = reference->GetPixelsX();
UnsignedByte* referenceData = reference->GetUnsignedByteData();
UnsignedByte* currentData = current->GetUnsignedByteData();
// Allocate memory to store the displacements
SignedByte* xDisplacements = new SignedByte[(this->mv4 ? 4 : 1) * blockColumns * blockRows];
SignedByte* yDisplacements = new SignedByte[(this->mv4 ? 4 : 1) * blockColumns * blockRows];
// Currently we use the sum of absolute errors (SAE) measure here.
for (BlockCount by = 0; by < blockRows; by++)
{
for (BlockCount bx = 0; bx < blockColumns; bx++)
{
for (unsigned char mvI = 0; mvI < (this->mv4 ? 4 : 1); mvI++)
{
SignedByte away, yDelta, xDelta;
unsigned char bs = (this->mv4 ? 8 : 16);
unsigned char xO = ((mvI == 0 || mvI == 2) ? 0 : 8);
unsigned char yO = ((mvI == 0 || mvI == 1) ? 0 : 8);
// Calculate for (0, 0) displacement.
UnsignedShort thisSAE = this->sumAbsoluteError(
referenceData + ((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3),
currentData + ((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3),
imageWidth, bs);
// Store the best current vector and its SAE value.
SignedByte bestXDisplacement = 0;
SignedByte bestYDisplacement = 0;
UnsignedShort bestSAE = thisSAE;
// Spiral scan pattern. Break if there is a perfect match.
for (away = 1; away < this->distance && bestSAE > this->saeLimit; away++)
{
// Check we are within bounds.
yDelta = -away;
if (by > 0)
for (xDelta = -away; xDelta < away; xDelta++)
{
if (bx == 0 && xDelta < 0)
xDelta = 0;
if (bx == blockColumns - 1 && xDelta > 0)
break;
// Calculate the SAE and add it on.
thisSAE = this->sumAbsoluteError(
referenceData + ((by * 16 + yDelta + yO) * 3 * imageWidth) + ((bx * 16 + xDelta + xO) * 3),
currentData + ((by * 16 + yO) * 3 * imageWidth) + (((bx + xO) * 16) * 3),
imageWidth, bs);
if (thisSAE < bestSAE)
{
bestSAE = thisSAE;
bestXDisplacement = xDelta;
bestYDisplacement = yDelta;
}
}
else
xDelta = away;
if (bx < blockColumns - 1)
for (yDelta = -away; yDelta < away; yDelta++)
{
if (by == 0 && yDelta < 0)
yDelta = 0;
if (by == blockRows - 1 && yDelta > 0)
break;
thisSAE = this->sumAbsoluteError(
referenceData + ((by * 16 + yDelta + yO) * 3 * imageWidth) + ((bx * 16 + xDelta + xO) * 3),
currentData + ((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3),
imageWidth, bs);
if (thisSAE < bestSAE)
{
bestSAE = thisSAE;
bestXDisplacement = xDelta;
bestYDisplacement = yDelta;
}
}
else
yDelta = away;
if (by < blockRows - 1)
for (xDelta = away; xDelta > -away; xDelta--)
{
if (bx == blockColumns - 1 && xDelta > 0)
xDelta = 0;
if (bx == 0 && xDelta < 0)
break;
thisSAE = this->sumAbsoluteError(
referenceData + ((by * 16 + yDelta + yO) * 3 * imageWidth) + ((bx * 16 + xDelta + xO) * 3),
currentData + ((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3),
imageWidth, bs);
if (thisSAE < bestSAE)
{
bestSAE = thisSAE;
bestXDisplacement = xDelta;
bestYDisplacement = yDelta;
}
}
else
xDelta = -away;
if (bx > 0)
for (yDelta = away; yDelta > -away; yDelta--)
{
if (by == blockRows - 1 && yDelta > 0)
yDelta = 0;
if (by == 0 && yDelta < 0)
break;
thisSAE = this->sumAbsoluteError(
referenceData + ((by * 16 + yDelta + yO) * 3 * imageWidth) + ((bx * 16 + xDelta + xO) * 3),
currentData + ((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3),
imageWidth, bs);
if (thisSAE < bestSAE)
{
bestSAE = thisSAE;
bestXDisplacement = xDelta;
bestYDisplacement = yDelta;
}
}
else
yDelta = -away;
}
SignedByte dx = bestXDisplacement;
SignedByte dy = bestYDisplacement;
SignedByte subXDelta = 0;
SignedByte subYDelta = 0;
if (this->subPixel)
{
// A macro to make the sum for a block SAE more concise.
// refExpr should be a function x and y which are the coordinates in the block.
#define sae(refExpr,curExpr) \ for (unsigned char y = 0; y < bs; y++)\ {\ for (unsigned char x = 0; x < bs; x++)\ {\ SignedShort delta = (refExpr) - (curExpr);\ saeValue += (delta < 0 ? -delta : delta);\ }\ };
// We now refine these values using half pixel motion estimation.
// sae(referenceData[((by * 16 + y) * 3 * imageWidth) + ((bx * 16 + x) * 3)],currentData[((by * 16) * 3 * imageWidth) + ((bx * 16) * 3)]);
UnsignedShort saeValue = 0;
// E
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + 1 + xO) * 3)]) >> 1,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = 1; subYDelta = 0; }
saeValue = 0;
// SE
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + 1 + xO) * 3)] + referenceData[((by * 16 + dy + y + 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + 1 + xO) * 3)] + referenceData[((by * 16 + dy + y + 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)]) >> 2,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = 1; subYDelta = 1; }
saeValue = 0;
// S
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)]) >> 1,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = 0; subYDelta = 1; }
saeValue = 0;
// SW
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x - 1 + xO) * 3)] + referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x - 1 + xO) * 3)]) >> 2,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = -1; subYDelta = 1; }
saeValue = 0;
// W
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x - 1 + xO) * 3)]) >> 1,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = -1; subYDelta = 0; }
saeValue = 0;
// NW
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y - 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y - 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x - 1 + xO) * 3)] + referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x - 1 + xO) * 3)]) >> 2,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = -1; subYDelta = -1; }
saeValue = 0;
// N
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y - 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)]) >> 1,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = 0; subYDelta = -1; }
saeValue = 0;
// NE
sae((referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)] + referenceData[((by * 16 + dy + y + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + 1 + xO) * 3)] + referenceData[((by * 16 + dy + y - 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + 1 + xO) * 3)] + referenceData[((by * 16 + dy + y - 1 + yO) * 3 * imageWidth) + ((bx * 16 + dx + x + xO) * 3)]) >> 2,currentData[((by * 16 + yO) * 3 * imageWidth) + ((bx * 16 + xO) * 3)]);
if (saeValue < bestSAE) { bestSAE = saeValue; subXDelta = 1; subYDelta = -1; }
#undef sae
}
bool negative = (bestXDisplacement < 0);
UnsignedByte magnitude = negative ? (-bestXDisplacement) : (bestXDisplacement);
xDisplacements[this->mv4 ? (bx + by * blockColumns) * 4 + mvI : bx + by * blockColumns] = ((this->subPixel ? (magnitude << 1) : magnitude) + (subXDelta < 0 ? -subXDelta : subXDelta)) * (negative ? -1 : 1);
// The Y displacement vector is reflected vertically for convenience of display.
negative = (bestYDisplacement < 0);
magnitude = (negative) ? (-bestYDisplacement) : (bestYDisplacement);
yDisplacements[this->mv4 ? (bx + by * blockColumns) * 4 + mvI : bx + by * blockColumns] = -(((this->subPixel ? (magnitude << 1) : magnitude) + (subYDelta < 0 ? -subYDelta : subYDelta)) * (negative ? -1 : 1));
}
}
}
current->SetMVDisplacementsX(xDisplacements, blockColumns * blockRows);
current->SetMVDisplacementsY(yDisplacements, blockColumns * blockRows);
}
inline UnsignedShort MotionEstimation::sumAbsoluteError(UnsignedByte* referenceDataStart, UnsignedByte* currentStart, UnsignedShort imageWidth, unsigned char blockSize)
{
UnsignedShort sae = 0;
for (unsigned char y = 0; y < blockSize; y++)
{
for (unsigned char x = 0; x < blockSize; x++)
{
SignedShort delta = referenceDataStart[x * 3] - currentStart[x * 3];
sae += (delta < 0 ? -delta : delta);
}
// Stride to the next row.
referenceDataStart += imageWidth * 3;
currentStart += imageWidth * 3;
}
return sae;
}
void MotionEstimation::EnableMV4()
{
this->mv4 = true;
}
bool MotionEstimation::UseSubPixel()
{
return this->subPixel;
}
}