Beispiel #1
0
        /// <summary>
        /// This API usage is optimized for multiple runs of the same image size, by reusing the byte arrays instead of allocating new ones for every operation.
        /// </summary>
        private static void benchTJOptimized()
        {
            byte[] data = File.ReadAllBytes(inputFilePath);
            byte[] recompressed;
            int    recompressedSize = 0;

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            using (TJDecompressor decomp = new TJDecompressor(data))
            {
                // Optimize by pre-allocating the buffers and re-using them.
                byte[] rawImg = new byte[decomp.getWidth() * decomp.getHeight() * TJ.getPixelSize(PixelFormat.RGB)];
                recompressed = new byte[TJ.bufSize(decomp.getWidth(), decomp.getHeight(), decomp.getSubsamp())];
                using (TJCompressor comp = new TJCompressor(rawImg, decomp.getWidth(), decomp.getHeight()))
                {
                    comp.setJPEGQuality(jpegQuality);
                    for (int i = 0; i < numIterations; i++)
                    {
                        decomp.decompress(rawImg);
                        comp.compress(ref recompressed, Flag.NONE);
                    }
                    recompressedSize = comp.getCompressedSize();
                }
            }
            sw.Stop();
            PrintBenchmarkResult("turbojpegCLI optimized", sw.ElapsedMilliseconds);
            using (FileStream fs = new FileStream("out-libjpeg-turbo-optimized.jpg", FileMode.Create))
            {
                fs.Write(recompressed, 0, recompressedSize);
            }
        }
Beispiel #2
0
 private static void benchTJSimpleAPI()
 {
     byte[] data                     = File.ReadAllBytes(inputFilePath);
     byte[] rawImg                   = null;
     byte[] recompressed             = null;
     System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
     sw.Start();
     using (TJDecompressor decomp = new TJDecompressor(data))
     {
         using (TJCompressor comp = new TJCompressor())
         {
             comp.setJPEGQuality(jpegQuality);
             for (int i = 0; i < numIterations; i++)
             {
                 rawImg = decomp.decompress();
                 comp.setSourceImage(rawImg, decomp.getWidth(), decomp.getHeight());
                 recompressed = comp.compressToExactSize();
             }
         }
     }
     sw.Stop();
     PrintBenchmarkResult("turbojpegCLI simple API", sw.ElapsedMilliseconds);
     File.WriteAllBytes("out-libjpeg-turbo.jpg", recompressed);
 }
        /// <summary>
        /// Accepts a jpeg frame and encodes the corresponding JpegDiff video frame.  This instance reserves ownership of the returned byte array, and may return the same byte array again later after changing its content.  You can use the byte array for any purpose until the next call to EncodeFrame on this instance.
        /// </summary>
        /// <param name="jpegData">A byte array containing a jpeg image.</param>
        /// <param name="inputSizeBytes">The length of the input data (probably jpegData.Length).</param>
        /// <param name="outputSizeBytes">This variable will be set to the length of the jpeg data in the returned array. Assume the returned array is actually longer than this value.</param>
        /// <returns></returns>
        public byte[] EncodeFrame(byte[] jpegData, int inputSizeBytes, out int outputSizeBytes, int version)
        {
            if (!Versions.Contains(version))
            {
                outputSizeBytes = 0;
                return(new byte[0]);
            }
            lock (this)
            {
                if (isDisposed)
                {
                    outputSizeBytes = 0;
                    return(new byte[0]);
                }
                if (clientVisibleFrame == null)
                {
                    // Special case: First frame of video.
                    clientVisibleFrame = decoder.DecodeFrame(jpegData, inputSizeBytes, version);
                    outputSizeBytes    = jpegData.Length;
                    return(jpegData);
                }
                decomp.setSourceImage(jpegData, inputSizeBytes);
                if (decoder.Width != decomp.getWidth() || decoder.Height != decomp.getHeight())
                {
                    throw new JpegDiffVideoException("New frame dimensions (" + decomp.getWidth() + "x" + decomp.getHeight() + ") do not match the first frame (" + decoder.Width + "x" + decoder.Height + ")");
                }

                if (diffFrame == null)
                {
                    diffFrame = new byte[clientVisibleFrame.Length];
                }

                decomp.decompress(diffFrame);

                try
                {
                    waitBeforeStartingEncode.WaitOne();
                    // We will now iterate through the pixels, one color channel at a time (each byte is one color channel for one pixel)
                    // When we are done iterating, the [bitmap] object will contain an image representing the difference between the
                    // live (previous) frame and the upcoming (new) frame.
                    //
                    // We are shooting for a low contrast image here, as this will compress the best.
                    byte[] encoderArray;
                    if (version == 1)
                    {
                        encoderArray = encoderArrayV1;                         // V1 clamps extreme values to -128 or 128.  Color quality is maintained, but sharp color transitions suffer.
                    }
                    else if (version == 2)
                    {
                        encoderArray = encoderArrayV2;                         // V2 simply cuts color depth in half.
                    }
                    else if (version == 3)
                    {
                        encoderArray = encoderArrayV3;                         // V3 compresses the full color range into half the space, giving priority to small changes.
                    }
                    else if (version == 4)
                    {
                        encoderArray = encoderArrayV4;                         // V4 compresses the full color range into the full space, giving priority to small changes.
                    }
                    else
                    {
                        throw new Exception("Invalid version number specified");
                    }

                    for (int i = 0; i < diffFrame.Length; i++)
                    {
                        diffFrame[i] = encoderArray[255 + ((int)diffFrame[i] - (int)clientVisibleFrame[i])];
                    }

                    lastArguedVersion = version;

                    // The diff frame has been calculated.  Now compress it.
                    comp.setSourceImage(diffFrame, decoder.Width, decoder.Height);
                    comp.setJPEGQuality(compressionQuality);
                    if (returnDataBuffer == null)
                    {
                        returnDataBuffer = comp.compress();
                    }
                    else
                    {
                        comp.compress(ref returnDataBuffer, Flag.NONE);
                    }
                    outputSizeBytes = comp.getCompressedSize();

                    //comp.setSourceImage(previousFrameData, decoder.Width, decoder.Height);
                    //returnDataBuffer = comp.compress();
                    //outputSizeBytes = comp.getCompressedSize();
                    return(returnDataBuffer);
                }
                finally
                {
                    waitBeforeStartingEncode.Reset();
                    waitBeforeStartingDecode.Set();
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Accepts a JpegDiff frame and decodes it to raw RGB data.  This instance reserves ownership of the returned byte array, and may return the same byte array again later after changing its content.  You can use the byte array for any purpose until the next call to DecodeFrame on this instance.
        /// </summary>
        /// <param name="jpegDiffFrame">A buffer containing a jpegDiff frame.</param>
        /// <param name="inputSizeBytes">The length of the data in the input buffer, which may be shorter than the buffer itself.</param>
        /// <returns></returns>
        public byte[] DecodeFrame(byte[] jpegDiffFrame, int inputSizeBytes, int version)
        {
            if (!Versions.Contains(version))
            {
                return(new byte[0]);
            }
            lock (this)
            {
                if (isDisposed)
                {
                    return(new byte[0]);
                }
                decomp.setSourceImage(jpegDiffFrame, inputSizeBytes);
                if (previousFrameData == null)
                {
                    // Special case: First frame of video is a complete frame.  All following frames will be difference (diff) frames.
                    frameWidth        = decomp.getWidth();
                    frameHeight       = decomp.getHeight();
                    previousFrameData = decomp.decompress();
                    return(previousFrameData);
                }
                if (decomp.getWidth() != frameWidth || decomp.getHeight() != frameHeight)
                {
                    throw new JpegDiffVideoException("Diff frame dimensions (" + decomp.getWidth() + "x" + decomp.getHeight() + ") do not match the first frame (" + frameWidth + "x" + frameHeight + ")");
                }

                if (currentFrameData == null)
                {
                    currentFrameData = new byte[previousFrameData.Length];
                }

                decomp.decompress(currentFrameData);

                // Combine this diff frame with the previous frame.
                int[] decoderArray;
                if (version == 1)
                {
                    decoderArray = decoderArrayV1;
                }
                else if (version == 2)
                {
                    decoderArray = decoderArrayV2;
                }
                else if (version == 3)
                {
                    decoderArray = decoderArrayV3;
                }
                else if (version == 4)
                {
                    decoderArray = decoderArrayV4;
                }
                else
                {
                    throw new Exception("Invalid version number specified");
                }

                for (int i = 0; i < currentFrameData.Length; i++)
                {
                    int newVal = previousFrameData[i] + decoderArray[currentFrameData[i]];
                    if (newVal < 0)
                    {
                        previousFrameData[i] = 0;
                    }
                    else if (newVal > 255)
                    {
                        previousFrameData[i] = 255;
                    }
                    else
                    {
                        previousFrameData[i] = (byte)newVal;
                    }
                }
                return(previousFrameData);
            }
        }