private Bitmap DecompressPFrame(byte[] bs) { int index = 0; // sbytes index sbyte[] decoded = RunLengthDecode(bs); // data including paddings // read in vectors var vectors = new Vector[(int)Math.Ceiling((double)FrameHeight / N) * (int)Math.Ceiling((double)FrameWidth / N)]; for (int i = 0; i < vectors.Length; ++i) { vectors[i].x = decoded[index++]; vectors[i].y = decoded[index++]; } // ycbcr differences var data = new sbyte[decoded.Length - index]; Array.Copy(decoded, index, data, 0, data.Length); YCbCrSubSample diffs = SeparateYCbCrFromSbytes(FrameHeight, FrameWidth, data); // pframe diffs AddDifferencesToMemoryFrame(diffs, vectors); YCbCr[,] yCbCrs = UpSample(frameMemory); return(ColorChannel.CreateBitmapFromYCbCr(yCbCrs)); }
private byte[] CompressIframe(Bitmap bitmap) { YCbCr[,] yCbCrs = ColorChannel.ReadYCbCrs(bitmap); YCbCrSubSample yCbCrSubSamples = JPEG.SubSample(yCbCrs); //DCT subquantized // for each channel, run a sperate thread for DCT, quantized, zigzag sampling, and RLE var tasks = new Task[3]; sbyte[,] yQuantized = null, cbQuantized = null, crQuantized = null; tasks[0] = Task.Factory.StartNew(() => { yQuantized = DCTSubQuantized(yCbCrSubSamples.Y, LuminanceQuantizationTable); }); tasks[1] = Task.Factory.StartNew(() => { cbQuantized = DCTSubQuantized(yCbCrSubSamples.Cb, ChrominanceQuantizationTable); }); tasks[2] = Task.Factory.StartNew(() => { crQuantized = DCTSubQuantized(yCbCrSubSamples.Cr, ChrominanceQuantizationTable); }); Task.WaitAll(tasks); // upquantized IDCT, store as frame memory int yHeight = yCbCrSubSamples.Y.GetLength(0), yWidth = yCbCrSubSamples.Y.GetLength(1), cHeight = yCbCrSubSamples.Cb.GetLength(0), cWidth = yCbCrSubSamples.Cb.GetLength(1); tasks[0] = Task.Factory.StartNew(() => { frameMemory.Y = UpQuantizedIDCT(yHeight, yWidth, yQuantized, LuminanceQuantizationTable); }); tasks[1] = Task.Factory.StartNew(() => { frameMemory.Cb = UpQuantizedIDCT(cHeight, cWidth, cbQuantized, ChrominanceQuantizationTable); }); tasks[2] = Task.Factory.StartNew(() => { frameMemory.Cr = UpQuantizedIDCT(cHeight, cWidth, crQuantized, ChrominanceQuantizationTable); }); // zigzag RLE sbyte[] yStream = ZigZagTransform(yQuantized); sbyte[] cbStream = ZigZagTransform(cbQuantized); sbyte[] crStream = ZigZagTransform(crQuantized); // concatennate byte stream var data = new sbyte[yStream.Length + cbStream.Length + crStream.Length]; yStream.CopyTo(data, 0); cbStream.CopyTo(data, yStream.Length); crStream.CopyTo(data, yStream.Length + cbStream.Length); Task.WaitAll(tasks); return(RunLengthEncode(data)); }
// duplicate cb cr channels to reproduce ycbcr pixels public static YCbCr[,] UpSample(YCbCrSubSample chans) { int h = chans.Y.GetLength(0), w = chans.Y.GetLength(1); YCbCr[,] yCbCrs = new YCbCr[h, w]; for (int row = 0; row < h; ++row) { for (int col = 0; col < w; ++col) { yCbCrs[row, col].Y = chans.Y[row, col]; // fill up the square with top left number int subR = row >> 1, subC = col >> 1; yCbCrs[row, col].Cb = chans.Cb[subR, subC]; yCbCrs[row, col].Cr = chans.Cr[subR, subC]; } } return(yCbCrs); }
public static Bitmap Decompress(int height, int width, byte[] compressedData) { YCbCrSubSample yCbCrSubSamples = DecodeCompressedBytes(height, width, compressedData); // up sample YCbCr[,] yCbCrs = UpSample(yCbCrSubSamples); // change to RGB and write to bitmap int h = yCbCrs.GetLength(0), w = yCbCrs.GetLength(1); Bitmap img = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb); for (int row = 0; row < h; ++row) { for (int col = 0; col < w; ++col) { img.SetPixel(col, row, ColorChannel.YcbcrToRgb(yCbCrs[row, col])); } } return(img); }
public static byte[] Compress(Bitmap image) { // 1. get YCbCr channels of the image YCbCr[,] yCbCrs = ColorChannel.ReadYCbCrs(image); // 2. sub sample YCbCrSubSample yCbCrSubSamples = SubSample(yCbCrs); // for each channel, run a sperate thread for DCT, quantized, zigzag sampling var tasks = new Task[3]; sbyte[] yStream = null, cbStream = null, crStream = null; tasks[0] = Task.Factory.StartNew(() => { //3,4. DCT sub quantized sbyte[,] s = DCTSubQuantized(yCbCrSubSamples.Y, LuminanceQuantizationTable); yStream = ZigZagTransform(s); }); tasks[1] = Task.Factory.StartNew(() => { sbyte[,] s = DCTSubQuantized(yCbCrSubSamples.Cb, ChrominanceQuantizationTable); cbStream = ZigZagTransform(s); }); tasks[2] = Task.Factory.StartNew(() => { sbyte[,] s = DCTSubQuantized(yCbCrSubSamples.Cr, ChrominanceQuantizationTable); crStream = ZigZagTransform(s); }); Task.WaitAll(tasks); // concatennate byte stream var data = new sbyte[yStream.Length + cbStream.Length + crStream.Length]; yStream.CopyTo(data, 0); cbStream.CopyTo(data, yStream.Length); crStream.CopyTo(data, yStream.Length + cbStream.Length); return(RunLengthEncode(data)); }
private void AddDifferencesToMemoryFrame(YCbCrSubSample diffs, Vector[] vectors) { int vectorIndex = 0; int yHeight = FrameHeight - 1; int yWidth = FrameWidth - 1; int cHeight = yHeight >> 1, cWidth = yWidth >> 1; // cb cr for (int row = 0; row <= yHeight; row += N) { for (int col = 0; col <= yWidth; col += N) { Vector v = vectors[vectorIndex++]; for (int Y = row, yBound = row + N; Y < yBound && Y <= yHeight; ++Y) { for (int X = col, xBound = col + N; X < xBound && X <= yWidth; ++X) { int ry = Math.Max(0, Math.Min(Y - v.y, yHeight)); int rx = Math.Max(0, Math.Min(X - v.x, yWidth)); diffs.Y[Y, X] += frameMemory.Y[ry, rx]; } } for (int Y = (row >> 1), yBound = Y + (N >> 1); Y < yBound && Y <= cHeight; ++Y) { for (int X = (col >> 1), xBound = X + (N >> 1); X < xBound && X <= cWidth; ++X) { int ry = Math.Max(0, Math.Min(Y - v.y, cHeight)); int rx = Math.Max(0, Math.Min(X - v.x, cWidth)); diffs.Cb[Y, X] += frameMemory.Cb[ry, rx]; diffs.Cr[Y, X] += frameMemory.Cr[ry, rx]; } } } } frameMemory = diffs; }
private Bitmap DecompressIFrame(byte[] bs) { frameMemory = DecodeCompressedBytes(FrameHeight, FrameWidth, bs); YCbCr[,] yCbCrs = UpSample(frameMemory); return(ColorChannel.CreateBitmapFromYCbCr(yCbCrs)); }
private byte[] CompressPframe(Bitmap bitmap) { YCbCr[,] yCbCrs = ColorChannel.ReadYCbCrs(bitmap); YCbCrSubSample yCbCrSubSamples = JPEG.SubSample(yCbCrs); // motion vecotr estimate var mv = new MotionVector(15); EstimateResult vecotrDiffs = mv.Estimate(frameMemory, yCbCrSubSamples); // dct subquantized differences var tasks = new Task[3]; sbyte[,] yQuantized = null, cbQuantized = null, crQuantized = null; tasks[0] = Task.Factory.StartNew(() => { yQuantized = DCTSubQuantized(vecotrDiffs.YDiffs, LuminanceQuantizationTable); }); tasks[1] = Task.Factory.StartNew(() => { cbQuantized = DCTSubQuantized(vecotrDiffs.CbDiffs, ChrominanceQuantizationTable); }); tasks[2] = Task.Factory.StartNew(() => { crQuantized = DCTSubQuantized(vecotrDiffs.CrDiffs, ChrominanceQuantizationTable); }); Task.WaitAll(tasks); //upquantized IDCT, add to frame memory int yHeight = yCbCrSubSamples.Y.GetLength(0), yWidth = yCbCrSubSamples.Y.GetLength(1), cHeight = yCbCrSubSamples.Cb.GetLength(0), cWidth = yCbCrSubSamples.Cb.GetLength(1); tasks[0] = Task.Factory.StartNew(() => { yCbCrSubSamples.Y = UpQuantizedIDCT(yHeight, yWidth, yQuantized, LuminanceQuantizationTable); }); tasks[1] = Task.Factory.StartNew(() => { yCbCrSubSamples.Cb = UpQuantizedIDCT(cHeight, cWidth, cbQuantized, ChrominanceQuantizationTable); }); tasks[2] = Task.Factory.StartNew(() => { yCbCrSubSamples.Cr = UpQuantizedIDCT(cHeight, cWidth, crQuantized, ChrominanceQuantizationTable); }); // zigzag RLE differences sbyte[] yStream = ZigZagTransform(yQuantized); sbyte[] cbStream = ZigZagTransform(cbQuantized); sbyte[] crStream = ZigZagTransform(crQuantized); // concatennate byte stream var data = new sbyte[vecotrDiffs.Vectors.Length * 2 + yStream.Length + cbStream.Length + crStream.Length]; for (int i = 0, j = 0; i < vecotrDiffs.Vectors.Length; ++i) { data[j++] = (sbyte)(vecotrDiffs.Vectors[i].x); data[j++] = (sbyte)(vecotrDiffs.Vectors[i].y); } yStream.CopyTo(data, vecotrDiffs.Vectors.Length * 2); cbStream.CopyTo(data, yStream.Length + vecotrDiffs.Vectors.Length * 2); crStream.CopyTo(data, yStream.Length + cbStream.Length + vecotrDiffs.Vectors.Length * 2); Task.WaitAll(tasks); AddDifferencesToMemoryFrame(yCbCrSubSamples, vecotrDiffs.Vectors); // update frame memory return(RunLengthEncode(data)); }
// estimate best match using 2d logorithmic search public EstimateResult Estimate(YCbCrSubSample reference, YCbCrSubSample target) { // image dimension int height = reference.Y.GetLength(0); int width = reference.Y.GetLength(1); // number of 8*8 blocks to fill the y channel int h = (int)Math.Ceiling((double)height / N); int w = (int)Math.Ceiling((double)width / N); var result = new EstimateResult { }; int vectorIndex = 0; result.Vectors = new Vector[h * w]; // y channel with paddings if origin image is not multiples of 8 h <<= 3; w <<= 3; result.YDiffs = new double[h, w]; // cb cr h = (int)Math.Ceiling((double)reference.Cb.GetLength(0) / N) << 3; w = (int)Math.Ceiling((double)reference.Cb.GetLength(1) / N) << 3; result.CbDiffs = new double[h, w]; result.CrDiffs = new double[h, w]; // dec to prevent -1 calculation inside the loop --width; --height; // cb cr height and width int cHeight = height >> 1, cWidth = width >> 1; for (int row = 0; row <= height; row += N) { for (int col = 0; col <= width; col += N) { // calculate the difference at the same pos first var r8 = new double[N, N]; var t8 = new double[N, N]; // 8*8 block filling for (int i = 0, Y = row; i < N; ++i) { for (int j = 0, X = col; j < N; ++j) { // use y channel to get vector r8[i, j] = reference.Y[Y, X]; t8[i, j] = target.Y[Y, X]; if (X < width) // padding number use the boundary number { ++X; } } if (Y < height) { ++Y; } } Differences min = CalDifferences(r8, t8); Vector mv = new Vector { x = 0, y = 0 }; // 2d logorithmic search for new reference block (p92) int centerY = row, centerX = col; int offset = (int)Math.Ceiling(P / 2.0); bool last = false; while (!last) { // find one of the nine specified macroblocks that yields minimum MAD, then update centerX, centerY, min Difference. vecotr. for (int offY = centerY - offset; offY <= centerY + offset; offY += offset) { for (int offX = centerX - offset; offX <= centerX + offset; offX += offset) { if ((offX == centerX && offY == centerY) || offX < 0 || offY < 0 || offX > width || offY > height) { continue; } // 8*8 block filling references block for (int i = 0, Y = offY; i < N; ++i) { for (int j = 0, X = offX; j < N; ++j) { r8[i, j] = reference.Y[Y, X]; if (X < width) // padding number use the boundary number { ++X; } } if (Y < height) { ++Y; } } Differences d = CalDifferences(r8, t8); // compare with min if (d.AbsoluteDifference < min.AbsoluteDifference) { min = d; mv.x = col - offX; mv.y = row - offY; centerX = offX; centerY = offY; } } } if (offset == 1) { last = true; } offset = (int)Math.Ceiling(offset / 2.0); } // cb cr for (int Y = row >> 1, YB = Y + (N >> 1); Y < YB && Y < cHeight; ++Y) { for (int X = col >> 1, XB = X + (N >> 1); X < XB && X < cWidth; ++X) { int ry = Math.Max(0, Math.Min(Y - mv.y, cHeight)); int rx = Math.Max(0, Math.Min(X - mv.x, cWidth)); result.CbDiffs[Y, X] = target.Cb[Y, X] - reference.Cb[ry, rx]; result.CrDiffs[Y, X] = target.Cr[Y, X] - reference.Cr[ry, rx]; } } // y for (int _y = 0, Y = row; _y < N; ++_y, ++Y) { for (int _x = 0, X = col; _x < N; ++_x, ++X) { result.YDiffs[Y, X] = min.DifferenceBlock[_y, _x]; } } result.Vectors[vectorIndex++] = mv; } } return(result); }