public Frame Process(Frame[] input) { Frame result = new Frame(input[0].Size); AnnotatedFrame frameWithLogs = (AnnotatedFrame)input[0]; /* Create Bitmap to draw the vectors on */ using (Bitmap drawableFrame = new Bitmap(input[0].Size.Width, input[0].Size.Width)) { for (int x = 0; x < input[0].Size.Width; x++) { for (int y = 0; y < input[0].Size.Height; y++) { Rgb pixel = input[0][x, y]; drawableFrame.SetPixel(x, y, Color.FromArgb(pixel.R, pixel.G, pixel.B)); } } /* Draw the movementvector of each macroblock */ for (int x = 0; x < (input[0].Size.Width / 16); x++) { for (int y = 0; y < (input[0].Size.Height / 16); y++) { DrawVector(drawableFrame, x * 16, y * 16, GetScaledVector(14.0, frameWithLogs.Decisions[x, y].Movement)); } } /* Print the frame with vectors into the resultframe */ for (int x = 0; x < input[0].Size.Width; x++) { for (int y = 0; y < input[0].Size.Height; y++) { Rgb pixel = new Rgb(drawableFrame.GetPixel(x, y).R, drawableFrame.GetPixel(x, y).G, drawableFrame.GetPixel(x, y).B); result[x, y] = pixel; } } } return result; }
public void GetsData() { // Get the parent PipelineVM and add DiagramNode var vm = MainViewModelTest.GetInstance().PipelineViewModel; var node = new NodeViewModel(new HistogramNode(), vm); vm.Nodes.Add(node); vm.Parent.Model.Graph.AddNode(node.Model); var hvm = new HistogramViewModel((HistogramNode)vm.Nodes.Single().Model); // Generates an array of 1 Frame with randomly filled Data var testSize = new Size(5, 5); Frame[] inputs = { new Frame(testSize) }; for (var x = 0; x < testSize.Width; x++) { for (var y = 0; y < testSize.Height; y++) { inputs[0][x, y] = new Rgb((byte)(x + y), (byte)(x + y), (byte)(x + y)); } } hvm.NodeModel.Type = HistogramType.R; // porcess one frame with chosen type hvm.NodeModel.Process(inputs, 0); hvm.Handle(null); Assert.NotEmpty(hvm.Data.Data); }
public override YuvKA.VideoModel.Frame[] Process(YuvKA.VideoModel.Frame[] inputs, int tick) { Size maxSize = Frame.MaxBoundaries(inputs); Frame outputFrame = new Frame(maxSize); byte resultR; byte resultG; byte resultB; for (int x = 0; x < maxSize.Width; x++) { for (int y = 0; y < maxSize.Height; y++) { resultR = 0; resultG = 0; resultB = 0; resultR += inputs[0].GetPixelOrBlack(x, y).R; resultR += inputs[0].GetPixelOrBlack(x, y).G; resultR += inputs[0].GetPixelOrBlack(x, y).B; resultG += inputs[1].GetPixelOrBlack(x, y).R; resultG += inputs[1].GetPixelOrBlack(x, y).G; resultG += inputs[1].GetPixelOrBlack(x, y).B; resultB += inputs[2].GetPixelOrBlack(x, y).R; resultB += inputs[2].GetPixelOrBlack(x, y).G; resultB += inputs[2].GetPixelOrBlack(x, y).B; outputFrame[x, y] = new Rgb(resultR, resultG, resultB); } } return new[] { outputFrame }; }
/// <summary> /// Alternate constructor for saving the trouble of using the indexer for setting up a frame with already well formed data /// </summary> /// <param name="size">The supposed frame size</param> /// <param name="data">The frame data</param> public Frame(Size size, Rgb[] data) { if (data.Length != size.Height * size.Width) { throw new System.ArgumentException(); } this.data = data; Size = size; }
/// <summary> /// Generates an AnnotatedFrame with randomly filled Data and the given Macroblock decisions. /// </summary> /// <returns> An annotated Frame with random Data and the given Macroblock decisions.</returns> public static AnnotatedFrame GenerateAnnFrame(MacroblockDecision[,] decisions) { var testSize = new YuvKA.VideoModel.Size(8, 8); var output = new AnnotatedFrame(testSize, decisions); for (int x = 0; x < testSize.Width; x++) { for (int y = 0; y < testSize.Height; y++) { output[x, y] = new Rgb((byte)(x + y), (byte)(x + y), (byte)(x + y)); } } return output; }
/// <summary> /// Splits the first entry of the input into its RGB components /// </summary> /// <param name="inputs">An array of Frames, with only the first entry regarded.</param> /// <param name="tick">The index of the Frame which is processes now.</param> /// <returns>An array of Frames with the red components of the input in the first, /// the green component in the second and the blue component in the third entry. </returns> public override Frame[] Process(Frame[] inputs, int tick) { Size size = inputs[0].Size; Frame[] outputs = { new Frame(size), new Frame(size), new Frame(size) }; for (int x = 0; x < size.Width; x++) { for (int y = 0; y < size.Height; y++) { outputs[0][x, y] = new Rgb(inputs[0][x, y].R, 0, 0); outputs[1][x, y] = new Rgb(0, inputs[0][x, y].G, 0); outputs[2][x, y] = new Rgb(0, 0, inputs[0][x, y].B); } } return outputs; }
/// <summary> /// A Process method to be used by AnonymousNodes. /// Generates an array of 1 Frame and 2 AnnotatedFrame with randomly filled Data. /// </summary> /// <param name="inputs">The inputs used for processing. This parameter is not used here.</param> /// <param name="tick"> The current index of the frame. This parameter is not used here.</param> /// <returns> An array of generated Frames.</returns> public static Frame[] SourceNode(Frame[] inputs, int tick) { var testSize = new YuvKA.VideoModel.Size(8, 8); Frame[] outputs = { GenerateAnnFrame(new MacroblockDecision[,] { { new MacroblockDecision { Movement = new Vector(0.0, 0.0), PartitioningDecision = MacroblockPartitioning.Inter4x4 }, new MacroblockDecision { Movement = new Vector(0.0, 0.0), PartitioningDecision = MacroblockPartitioning.Inter4x4 }, new MacroblockDecision { Movement = new Vector(0.0, 0.0), PartitioningDecision = MacroblockPartitioning.Inter8x4 } } }), new Frame(testSize), GenerateAnnFrame(new MacroblockDecision[,] { { new MacroblockDecision { Movement = new Vector(0.0, 0.0), PartitioningDecision = MacroblockPartitioning.Intra4x4 }, new MacroblockDecision { Movement = new Vector(0.0, 0.0), PartitioningDecision = MacroblockPartitioning.Intra4x4 }, new MacroblockDecision { Movement = new Vector(0.0, 0.0), PartitioningDecision = MacroblockPartitioning.Inter8x4 } } }), }; for (int x = 0; x < testSize.Width; x++) { for (int y = 0; y < testSize.Height; y++) { outputs[1][x, y] = new Rgb((byte)(x * y), (byte)(x * y), (byte)(x * y)); } } return outputs; }
/// <summary> /// Adds up the weighted frames and finally averages them according to the following formula: /// If there are n frames to be merged and w_1,..., w_n are their weights, the resulting frame will be computed by using this formula: /// newPixel_xy = (sum(w_i * oldValue_xy_i))/sum(w_i) /// (xy represents the position of the pixel in the frame.) /// </summary> public override Frame[] Process(Frame[] inputs, int tick) { Size maxSize = Frame.MaxBoundaries(inputs); double sumOfWeights = Weights.Sum(); Frame[] output = { new Frame(new Size(maxSize.Width, maxSize.Height)) }; for (int x = 0; x < maxSize.Width; x++) { for (int y = 0; y < maxSize.Height; y++) { // sums up the weighted values double newR = 0, newG = 0, newB = 0; for (int i = 0; i < inputs.Length; i++) { newR += Weights[i] * inputs[i].GetPixelOrBlack(x, y).R; newG += Weights[i] * inputs[i].GetPixelOrBlack(x, y).G; newB += Weights[i] * inputs[i].GetPixelOrBlack(x, y).B; } // averages the values newR = newR / sumOfWeights; newG = newG / sumOfWeights; newB = newB / sumOfWeights; output[0][x, y] = new Rgb((byte)newR, (byte)newG, (byte)newB); } } return output; }
public void TestHistogramNodeValue() { // Generates an array of 1 Frame with randomly filled Data YuvKA.VideoModel.Size testSize = new YuvKA.VideoModel.Size(5, 5); Frame[] inputs = { new Frame(testSize) }; for (int x = 0; x < testSize.Width; x++) { for (int y = 0; y < testSize.Height; y++) { inputs[0][x, y] = new Rgb((byte)(x + y), (byte)(x + y), (byte)(x + y)); } } // Generate Value HistogramNode once HistogramNode histNodeValue = new HistogramNode(); histNodeValue.Type = HistogramType.Value; histNodeValue.Process(inputs, 0); // Calculate expected results independently from Histogram method. Color rgbValue; int value; int[] intData = new int[256]; for (int x = 0; x < inputs[0].Size.Width; x++) { for (int y = 0; y < inputs[0].Size.Height; y++) { rgbValue = Color.FromArgb(inputs[0][x, y].R, inputs[0][x, y].G, inputs[0][x, y].B); value = (int)(rgbValue.GetBrightness() * 255); intData[value]++; } } int numberOfPixels = inputs[0].Size.Height * inputs[0].Size.Width; for (int i = 0; i < 256; i++) { Assert.Equal(histNodeValue.Data[i], (double)intData[i] / numberOfPixels); } }
/// <summary> /// Helper method for converting a bunch of data from YUV to a single RGB frame /// The method supports the IYUV / YUV420 format, and assumes that data array /// size, width and height make sense. /// </summary> /// <param name="data"> /// The raw Yuv data that constitutes the frame /// </param> private static Frame YuvToRgb(byte[] data, int width, int height) { int pixelNum = width * height; int quartSize = width * height / 4; Rgb[] frameData = new Rgb[height * width]; int ypixel, upixel, vpixel; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int coordOffset = y * width + x; // Our data format it IYUV / YUV420: // first all Y values, then all U value, and then all V values // the Y 'frame' is twice as big as the U and V 'frames', as // the human eye is better at recognizing luminance than it is at // distinguishing between different chromacities. // Get YUV data from given dataset ypixel = data[coordOffset]; upixel = data[pixelNum + ((width / 2) * (y / 2) + x / 2)]; vpixel = data[(pixelNum + quartSize) + ((width / 2) * (y / 2) + x / 2)]; // Convert data to RGB values // YCrCb conversion as described by ITU-R 601, with tweaked coefficients byte r = ClampToByte(1.167 * (ypixel - 16) + 1.596 * (vpixel - 128)); byte g = ClampToByte(1.169 * (ypixel - 16) - 0.393 * (upixel - 128) - 0.816 * (vpixel - 128)); byte b = ClampToByte(1.167 * (ypixel - 16) + 2.018 * (upixel - 128)); frameData[coordOffset] = new Rgb(r, g, b); } } return new Frame(new Size(width, height), frameData); }
public void TestNoOverlay() { Frame testFrame = new Frame(new YuvKA.VideoModel.Size(80, 80)); for (int x = 0; x < testFrame.Size.Width; x++) { for (int y = 0; y < testFrame.Size.Height; y++) { testFrame[x, y] = new Rgb(111, 111, 111); } } Frame[] input = { testFrame }; OverlayNode node = new OverlayNode { Type = new NoOverlay() }; node.ProcessCore(input, 0); List<Frame> output = new List<Frame>(); for (int x = 0; x < testFrame.Size.Width; x++) { for (int y = 0; y < testFrame.Size.Height; y++) { Assert.Equal(testFrame[x, y], node.Data[x, y]); } } }
public void ArtifactOverlay() { Frame testFrame = new Frame(new YuvKA.VideoModel.Size(80, 80)); for (int x = 0; x < testFrame.Size.Width; x++) { for (int y = 0; y < testFrame.Size.Height; y++) { testFrame[x, y] = new Rgb(111, 111, 111); } } Frame alteredTestFrame = new Frame(testFrame.Size); for (int x = 0; x < testFrame.Size.Width; x++) { for (int y = 0; y < testFrame.Size.Height; y++) { alteredTestFrame[x, y] = new Rgb((byte)(x + y), (byte)(x + y), (byte)(x + y)); } } Frame[] input = { alteredTestFrame, testFrame }; OverlayNode node = new OverlayNode { Type = new ArtifactsOverlay() }; node.ProcessCore(input, 0); List<Frame> output = new List<Frame>(); output.Add(node.Data); YuvEncoder.Encode(@"..\..\..\..\output\ArtifactOverlayTest_80x80.yuv", output); }
public void TestVecorOverlay() { Frame testFrame = new Frame(new YuvKA.VideoModel.Size(64, 48)); for (int x = 0; x < testFrame.Size.Width; x++) { for (int y = 0; y < testFrame.Size.Height; y++) { testFrame[x, y] = new Rgb(111, 111, 111); } } MacroblockDecision[] decisions = new MacroblockDecision[12]; decisions[0] = new MacroblockDecision { Movement = new Vector(0.0, 12.0) }; decisions[1] = new MacroblockDecision { Movement = new Vector(12.0, 12.0) }; decisions[2] = new MacroblockDecision { Movement = new Vector(12.0, 0.0) }; decisions[3] = new MacroblockDecision { Movement = new Vector(12.0, -12.0) }; decisions[4] = new MacroblockDecision { Movement = new Vector(3.0, -12.0) }; decisions[5] = new MacroblockDecision { Movement = new Vector(-38.0, -15.0) }; decisions[6] = new MacroblockDecision { Movement = new Vector(-120.0, 0.0) }; decisions[7] = new MacroblockDecision { Movement = new Vector(-20.0, 20.0) }; decisions[8] = new MacroblockDecision { Movement = new Vector(4.0, 0.0) }; decisions[9] = new MacroblockDecision { Movement = new Vector(0.0, 4.0) }; decisions[10] = new MacroblockDecision { Movement = new Vector(4.0, 4.0) }; decisions[11] = new MacroblockDecision { Movement = new Vector(-4.0, 0.0) }; Frame[] input = { new AnnotatedFrame(testFrame, decisions) }; OverlayNode node = new OverlayNode { Type = new MoveVectorsOverlay() }; node.ProcessCore(input, 0); List<Frame> output = new List<Frame>(); output.Add(node.Data); YuvEncoder.Encode(@"..\..\..\..\output\VectorOverlayTest_64x48.yuv", output); }
public void TestMacroBlockOverlay() { Frame testFrame = new Frame(new YuvKA.VideoModel.Size(64, 64)); for (int x = 0; x < testFrame.Size.Width; x++) { for (int y = 0; y < testFrame.Size.Height; y++) { testFrame[x, y] = new Rgb(111, 111, 111); } } MacroblockDecision[] decisions = new MacroblockDecision[16]; decisions[0] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.InterSkip }; decisions[1] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter16x16 }; decisions[2] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter16x8 }; decisions[3] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter8x16 }; decisions[4] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter8x8 }; decisions[5] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter4x8 }; decisions[6] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter8x4 }; decisions[7] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter4x4 }; decisions[8] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Intra16x16 }; decisions[9] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Intra8x8 }; decisions[10] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Intra4x4 }; decisions[11] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Unknown }; decisions[12] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Inter8x8OrBelow }; decisions[13] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.IntraPCM }; decisions[14] = new MacroblockDecision { PartitioningDecision = null }; decisions[15] = new MacroblockDecision { PartitioningDecision = MacroblockPartitioning.Unknown }; Frame[] input = { new AnnotatedFrame(testFrame, decisions) }; OverlayNode node = new OverlayNode { Type = new BlocksOverlay() }; node.ProcessCore(input, 0); List<Frame> output = new List<Frame>(); output.Add(node.Data); YuvEncoder.Encode(@"..\..\..\..\output\BlockOverlayTest_64x64.yuv", output); }
public void TestHistogramNodeRGB() { // Generates an array of 1 Frame with randomly filled Data YuvKA.VideoModel.Size testSize = new YuvKA.VideoModel.Size(5, 5); Frame[] inputs = { new Frame(testSize) }; for (int x = 0; x < testSize.Width; x++) { for (int y = 0; y < testSize.Height; y++) { inputs[0][x, y] = new Rgb((byte)(x + y), (byte)(x + y), (byte)(x + y)); } } // Generate all RGB HistogramNode once HistogramNode histNodeR = new HistogramNode(); histNodeR.Type = HistogramType.R; HistogramNode histNodeG = new HistogramNode(); histNodeG.Type = HistogramType.G; HistogramNode histNodeB = new HistogramNode(); histNodeB.Type = HistogramType.B; histNodeR.Process(inputs, 0); histNodeG.Process(inputs, 0); histNodeB.Process(inputs, 0); // Calculate expected results independently from Histogram methods. int[] value = new int[3]; int[,] intData = new int[256, 3]; for (int x = 0; x < inputs[0].Size.Width; x++) { for (int y = 0; y < inputs[0].Size.Height; y++) { value[0] = inputs[0][x, y].R; value[1] = inputs[0][x, y].G; value[2] = inputs[0][x, y].B; intData[value[0], 0]++; intData[value[1], 1]++; intData[value[2], 2]++; } } int numberOfPixels = inputs[0].Size.Height * inputs[0].Size.Width; for (int i = 0; i < 256; i++) { Assert.Equal(histNodeR.Data[i], (double)intData[i, 0] / numberOfPixels); Assert.Equal(histNodeG.Data[i], (double)intData[i, 1] / numberOfPixels); Assert.Equal(histNodeB.Data[i], (double)intData[i, 2] / numberOfPixels); } }
public Frame Process(Frame[] input) { AnnotatedFrame frameWithLogs = (AnnotatedFrame)input[0]; Frame result = new Frame(input[0].Size); for (int x = 0; x < input[0].Size.Width; x++) { for (int y = 0; y < input[0].Size.Height; y++) { /* Make result black and white first to emphasize color highlighting */ byte average = (byte)((input[0][x, y].R + input[0][x, y].G + input[0][x, y].B) / 3); result[x, y] = new Rgb(average, average, average); result[x, y] = GetMaskedPixel(result[x, y], x + 1, y + 1, frameWithLogs.Decisions[x / 16, y / 16].PartitioningDecision); } } return result; }
private Rgb GetMaskedPixel(Rgb pixel, int x, int y, MacroblockPartitioning? decision) { switch (decision) { case MacroblockPartitioning.InterSkip: if ((x % 16 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter16x16: if ((x % 16 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter16x8: if ((x % 16 == 0) || (y % 8 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter4x4: if ((x % 4 == 0) || (y % 4 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter4x8: if ((x % 4 == 0) || (y % 8 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter8x16: if ((x % 8 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter8x4: if ((x % 8 == 0) || (y % 4 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter8x8: if ((x % 8 == 0) || (y % 8 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Inter8x8OrBelow: if ((x % 8 == 0) || (y % 8 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, (byte)Math.Min(255, pixel.G + 100), pixel.B); case MacroblockPartitioning.Intra16x16: if ((x % 16 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb((byte)Math.Min(255, pixel.R + 80), pixel.G, (byte)Math.Min(255, pixel.B + 80)); case MacroblockPartitioning.Intra4x4: if ((x % 4 == 0) || (y % 4 == 0)) return new Rgb(0, 0, 0); else return new Rgb((byte)Math.Min(255, pixel.R + 80), pixel.G, (byte)Math.Min(255, pixel.B + 80)); case MacroblockPartitioning.Intra8x8: if ((x % 8 == 0) || (y % 8 == 0)) return new Rgb(0, 0, 0); else return new Rgb((byte)Math.Min(255, pixel.R + 80), pixel.G, (byte)Math.Min(255, pixel.B + 80)); case MacroblockPartitioning.IntraPCM: if ((x % 16 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb((byte)Math.Min(255, pixel.R + 80), pixel.G, (byte)Math.Min(255, pixel.B + 80)); case MacroblockPartitioning.Unknown: if ((x % 16 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb((byte)Math.Min(255, pixel.R + 100), pixel.G, pixel.B); default: if ((x % 16 == 0) || (y % 16 == 0)) return new Rgb(0, 0, 0); else return new Rgb(pixel.R, pixel.G, (byte)Math.Max(255, pixel.B + 40)); } }