private unsafe bool SearchMotionVector(BitmapData inputData, BitmapData previousData, int pixelBytes, int yStart, int xStart, out MotionVector motionVector) { byte* inputPtr = (byte*)inputData.Scan0; byte* previousPtr = (byte*)previousData.Scan0; motionVector = MotionVector.ZERO; // Origin - no translation. This is most probable. if (TestMotionVector(inputData, previousData, pixelBytes, yStart, xStart, inputPtr, previousPtr, MotionVector.ZERO)) { return true; } foreach (MotionVector favouriteVector in favouriteMotionVectors) { if (TestMotionVector(inputData, previousData, pixelBytes, yStart, xStart, inputPtr, previousPtr, favouriteVector)) { motionVector = favouriteVector; return true; } } // check possible offsets to find an equal shifted // block in the previous frame for (int i = 0; i < mcPossibleOffsets.Length; i++) { motionVector = mcPossibleOffsets[i]; if (TestMotionVector(inputData, previousData, pixelBytes, yStart, xStart, inputPtr, previousPtr, motionVector)) { if (!favouriteMotionVectors.Contains(motionVector)) { favouriteMotionVectors.AddFirst(motionVector); if (favouriteMotionVectors.Count > MAX_FAVOURITE_MOTION_VECTORS_SIZE) { favouriteMotionVectors.RemoveLast(); } } return true; } } return false; }
private unsafe bool TestMotionVector(BitmapData inputData, BitmapData previousData, int pixelBytes, int yStart, int xStart, byte* inputPtr, byte* previousPtr, MotionVector motionVector) { for (int y = yStart; y < yStart + MCBlockSize; y++) { byte* inputRow = inputPtr + (y * inputData.Stride); byte* previousRow = previousPtr + (y * previousData.Stride); for (int x = xStart; x < xStart + MCBlockSize; x++) { int xSource = x + motionVector.x; int ySource = y + motionVector.y; if ((x < 0) || (x >= frameWidth) || (y < 0) || (y >= frameHeight) || (xSource < 0) || (xSource >= frameWidth) || (ySource < 0) || (ySource >= frameHeight)) { return false; } // assume BGRA pixel format for (int band = 0; band < 3; band++) { int inputIndex = x * pixelBytes + band; int previousIndex = motionVector.y * previousData.Stride + xSource * pixelBytes + band; if (inputRow[inputIndex] != previousRow[previousIndex]) { // means: inputFrame[x, y] != previousFrame[xSource, ySource]) return false; } } } } return true; }
private unsafe void DecodeTranslatedBlock(Stream inStream, BitmapData currentData, BitmapData previousData, int pixelBytes, int yStart, int xStart, MotionVector motionVector) { byte* currentPtr = (byte*)currentData.Scan0; byte* previousPtr = (byte*)previousData.Scan0; int debugPixelBytes = pixelBytes; if (VisualizeMCBlockTypes) { debugPixelBytes = GetBytesPerPixel(debugFrame.PixelFormat); } bool isIdenticalBlock = (motionVector.x == 0) && (motionVector.y == 0); for (int y = yStart; y < yStart + MCBlockSize; y++) { byte* currentRow = currentPtr + (y * currentData.Stride); byte* previousRow = previousPtr + ((y + motionVector.y) * previousData.Stride); byte* debugRow = (byte*)0; if (VisualizeMCBlockTypes) { debugRow = (byte*)debugFrameData.Scan0 + (y * debugFrameData.Stride); } for (int x = xStart; x < xStart + MCBlockSize; x++) { // assume BGRA input pixel format, store as RGB for (int band = 2; band >= 0; band--) { // temporal prediction int currentIndex = x * pixelBytes + band; int previousIndex = currentIndex + motionVector.x * pixelBytes; currentRow[currentIndex] = previousRow[previousIndex]; } if (pixelBytes == 4) { currentRow[x * pixelBytes + 3] = 255; // assume full alpha } if (VisualizeMCBlockTypes) { // identical - green // translated - blue int index = x * debugPixelBytes; debugRow[index] = (byte)((isIdenticalBlock) ? 0 : 255); debugRow[index + 1] = (byte)((isIdenticalBlock) ? 255 : 0); debugRow[index + 2] = 0; } } } }