예제 #1
0
        private void Enc(string inputPath, string outputPath, byte[] data)
        {
            var input = new jpeg_decompress_struct();

            using (var fileStream = new FileStream(inputPath, FileMode.Open))
            {
                input.jpeg_stdio_src(fileStream);
                input.jpeg_read_header(false);
                var coefficients = input.jpeg_read_coefficients();

                var channels = coefficients.Take(3).Select(JpegHelper.GetBuffer).ToArray();
                Encrypt(channels, data);

                var output = new jpeg_compress_struct();
                using (var outfile = new FileStream(outputPath, FileMode.Create))
                {
                    output.jpeg_stdio_dest(outfile);

                    input.jpeg_copy_critical_parameters(output);

                    output.jpeg_write_coefficients(coefficients);
                    output.jpeg_finish_compress();
                }
            }
        }
예제 #2
0
        public void TestMarkerList()
        {
            jpeg_decompress_struct cinfo = new jpeg_decompress_struct();

            using (FileStream input = new FileStream(Tester.MapOpenPath("PARROTS.JPG"), FileMode.Open))
            {
                /* Specify data source for decompression */
                cinfo.jpeg_stdio_src(input);

                const int markerDataLengthLimit = 1000;
                cinfo.jpeg_save_markers((int)JPEG_MARKER.COM, markerDataLengthLimit);
                cinfo.jpeg_save_markers((int)JPEG_MARKER.APP0, markerDataLengthLimit);

                /* Read file header, set default decompression parameters */
                cinfo.jpeg_read_header(true);

                Assert.AreEqual(cinfo.Marker_list.Count, 3);

                int[] expectedMarkerType           = { (int)JPEG_MARKER.APP0, (int)JPEG_MARKER.APP0, (int)JPEG_MARKER.COM };
                int[] expectedMarkerOriginalLength = { 14, 3072, 10 };
                for (int i = 0; i < cinfo.Marker_list.Count; ++i)
                {
                    jpeg_marker_struct marker = cinfo.Marker_list[i];
                    Assert.IsNotNull(marker);
                    Assert.AreEqual(marker.Marker, expectedMarkerType[i]);
                    Assert.AreEqual(marker.OriginalLength, expectedMarkerOriginalLength[i]);
                    Assert.LessOrEqual(marker.Data.Length, markerDataLengthLimit);
                }
            }
        }
예제 #3
0
        // the algorithm flow is based on:
        // https://bitmiracle.github.io/libjpeg.net/help/articles/KB/decompression-details.html
        private DecompressionResult DecompressJpgToRaw(byte[] image)
        {
            var cinfo = new jpeg_decompress_struct(new jpeg_error_mgr());

            using (var memoryStream = new MemoryStream(image))
            {
                cinfo.jpeg_stdio_src(memoryStream);
                cinfo.jpeg_read_header(true);

                cinfo.Out_color_space = J_COLOR_SPACE.JCS_RGB;

                cinfo.jpeg_start_decompress();

                // there are 3 components: R, G, B
                var rowStride    = 3 * cinfo.Output_width;
                var result       = new byte[rowStride * cinfo.Output_height];
                var resultOffset = 0;

                var buffer = new byte[1][];
                buffer[0] = new byte[rowStride];

                while (cinfo.Output_scanline < cinfo.Output_height)
                {
                    var ct = cinfo.jpeg_read_scanlines(buffer, 1);
                    if (ct > 0)
                    {
                        Array.Copy(buffer[0], 0, result, resultOffset, buffer[0].Length);
                        resultOffset += buffer[0].Length;
                    }
                }

                cinfo.jpeg_finish_decompress();
                return(new DecompressionResult(result, cinfo.Output_width, cinfo.Output_height));
            }
        }
예제 #4
0
        /// <summary>
        /// The DCT coefficients are returned in a structure where the different components are separated in 2D arrays od shorts[][].
        /// The first dimension of the array contains the Blocks and the second dimension the 64 values for that block.
        /// </summary>
        /// <returns></returns>
        public static DCTCoefficients GetDctCoefficients(string JpegFilePath)
        {
            jpeg_decompress_struct jpds          = new jpeg_decompress_struct();
            FileStream             fileStreamImg = new FileStream(JpegFilePath, FileMode.Open, FileAccess.Read);

            jpds.jpeg_stdio_src(fileStreamImg);
            jpds.jpeg_read_header(true);
            // DCT coefficients
            var jBlock = jpds.jpeg_read_coefficients();
            // Initialize the list for all the dct
            var dctFull   = new List <short>();
            var dctFullDc = new List <short>();
            var dctFullAc = new List <short>();
            // Get coeffs and fill the list _dctFull
            List <short[]> dctCr = readDctCoeffs(EnumStegoChannel.Cr, jBlock, dctFull, dctFullDc, dctFullAc);
            List <short[]> dctCb = readDctCoeffs(EnumStegoChannel.Cb, jBlock, dctFull, dctFullDc, dctFullAc);
            List <short[]> dctY  = readDctCoeffs(EnumStegoChannel.Y, jBlock, dctFull, dctFullDc, dctFullAc);

            // Close decompress and filestream
            jpds.jpeg_finish_decompress();
            fileStreamImg.Close();
            // Return DCT coefficients
            DCTCoefficients dctCoeffs = new DCTCoefficients
            {
                DctY      = dctY.ToArray(),
                DctCb     = dctCb.ToArray(),
                DctCr     = dctCr.ToArray(),
                DctFull   = dctFull.ToArray(),
                DctFullDc = dctFullDc.ToArray(),
                DctFullAc = dctFullAc.ToArray()
            };

            return(dctCoeffs);
        }
예제 #5
0
        /// <summary>
        /// Tunes decompressor
        /// </summary>
        /// <param name="jpeg">Stream with input compressed JPEG data</param>
        private void beforeDecompress(Stream jpeg)
        {
            m_decompressor.jpeg_stdio_src(jpeg);
            /* Read file header, set default decompression parameters */
            m_decompressor.jpeg_read_header(true);

            applyParameters(m_decompressionParameters);
            m_decompressor.jpeg_calc_output_dimensions();
        }
예제 #6
0
        private byte[] Dec(string inputPath, int len)
        {
            var input = new jpeg_decompress_struct();

            input.jpeg_stdio_src(new FileStream(inputPath, FileMode.Open));
            input.jpeg_read_header(false);
            var coefficients = input.jpeg_read_coefficients();

            var channels = coefficients.Take(3).Select(JpegHelper.GetBuffer).ToArray();

            return(Decrypt(channels, len));
        }
        public byte[] DecodeJpeg(string inputPath, int len)
        {
            var input = new jpeg_decompress_struct();

            using (var fs = new FileStream(inputPath, FileMode.Open))
            {
                input.jpeg_stdio_src(fs);
                input.jpeg_read_header(false);
                var coefficients = input.jpeg_read_coefficients();

                var channels = coefficients.Take(3).Select(JpegHelper.GetBuffer).ToArray();
                input.jpeg_finish_decompress();
                return(DecodeBlocks(channels, len));
            }
        }
예제 #8
0
        public static void ApplyQuantizationTable(string JpegFilePath, EnumStegoChannel Component = EnumStegoChannel.Y, int ScaleFactor = 50, int[] QuantizationTable = null)
        {
            var        jpds          = new jpeg_decompress_struct();
            FileStream fileStreamImg = new FileStream(JpegFilePath, FileMode.Open, FileAccess.Read);

            jpds.jpeg_stdio_src(fileStreamImg);
            jpds.jpeg_read_header(true);
            // DCT coefficients
            var jBlock = jpds.jpeg_read_coefficients();

            jpds.jpeg_finish_decompress();
            fileStreamImg.Close();

            if (null == QuantizationTable)
            {
                switch (Component)
                {
                case EnumStegoChannel.Y:
                    QuantizationTable = _qLuminance;
                    break;

                case EnumStegoChannel.Cb:
                case EnumStegoChannel.Cr:
                    QuantizationTable = _qChromiance;
                    break;
                }
            }

            // Get fle info
            FileInfo fInfo            = new FileInfo(JpegFilePath);
            string   dir              = fInfo.DirectoryName;
            string   fNane            = fInfo.Name;
            string   stegFName        = $"steg_{DateTime.Now.ToString("yyyyMMddHHmm")}_{fNane}";
            string   stegJpegFilePath = Path.Combine(dir, stegFName);
            // Compress process
            FileStream           fileStream = File.Create(stegJpegFilePath);
            jpeg_compress_struct jpcs       = new jpeg_compress_struct();

            jpcs.jpeg_stdio_dest(fileStream);
            jpds.jpeg_copy_critical_parameters(jpcs);
            jpcs.jpeg_write_coefficients(jBlock);
            jpcs.jpeg_finish_compress();
            fileStream.Close();
            jpds.jpeg_abort_decompress();
        }
예제 #9
0
        private void startDecompression(jpeg_decompress_struct jpeg)
        {
            m_input.Position = 0;

            jpeg.Src = new VirtualStreamSourceManager(jpeg, m_input);
            if (jpeg.jpeg_read_header(true) != ReadResult.JPEG_HEADER_OK)
            {
                throw new InvalidProgramException("Failed to decompress JPEG image data.");
            }

            var colorspace = jpeg.Out_color_space;

            if (colorspace != J_COLOR_SPACE.JCS_GRAYSCALE &&
                colorspace != J_COLOR_SPACE.JCS_RGB &&
                colorspace != J_COLOR_SPACE.JCS_CMYK)
            {
                jpeg.Out_color_space = J_COLOR_SPACE.JCS_RGB;
            }

            if (!jpeg.jpeg_start_decompress())
            {
                throw new InvalidProgramException("Failed to decompress JPEG image data.");
            }
        }
예제 #10
0
        private static void decompress(Stream input, DecompressOptions options, Stream output)
        {
            Debug.Assert(input != null);
            Debug.Assert(options != null);
            Debug.Assert(output != null);

            /* Initialize the JPEG decompression object with default error handling. */
            jpeg_decompress_struct cinfo = new jpeg_decompress_struct(new cd_jpeg_error_mgr());

            /* Insert custom marker processor for COM and APP12.
             * APP12 is used by some digital camera makers for textual info,
             * so we provide the ability to display it as text.
             * If you like, additional APPn marker types can be selected for display,
             * but don't try to override APP0 or APP14 this way (see libjpeg.doc).
             */
            cinfo.jpeg_set_marker_processor((int)JPEG_MARKER.COM, new jpeg_decompress_struct.jpeg_marker_parser_method(printTextMarker));
            cinfo.jpeg_set_marker_processor((int)JPEG_MARKER.APP0 + 12, printTextMarker);

            /* Specify data source for decompression */
            cinfo.jpeg_stdio_src(input);

            /* Read file header, set default decompression parameters */
            cinfo.jpeg_read_header(true);

            applyOptions(cinfo, options);

            /* Initialize the output module now to let it override any crucial
             * option settings (for instance, GIF wants to force color quantization).
             */
            djpeg_dest_struct dest_mgr = null;

            switch (options.OutputFormat)
            {
            case IMAGE_FORMATS.FMT_BMP:
                dest_mgr = new bmp_dest_struct(cinfo, false);
                break;

            case IMAGE_FORMATS.FMT_OS2:
                dest_mgr = new bmp_dest_struct(cinfo, true);
                break;

            default:
                cinfo.ERREXIT((int)ADDON_MESSAGE_CODE.JERR_UNSUPPORTED_FORMAT);
                break;
            }

            dest_mgr.output_file = output;

            /* Start decompressor */
            cinfo.jpeg_start_decompress();

            /* Write output file header */
            dest_mgr.start_output();

            /* Process data */
            while (cinfo.Output_scanline < cinfo.Output_height)
            {
                int num_scanlines = cinfo.jpeg_read_scanlines(dest_mgr.buffer, dest_mgr.buffer_height);
                dest_mgr.put_pixel_rows(num_scanlines);
            }

            /* Finish decompression and release memory.
             * I must do it in this order because output module has allocated memory
             * of lifespan JPOOL_IMAGE; it needs to finish before releasing memory.
             */
            dest_mgr.finish_output();
            cinfo.jpeg_finish_decompress();

            /* All done. */
            if (cinfo.Err.Num_warnings != 0)
            {
                Console.WriteLine("Corrupt-data warning count is not zero");
            }
        }
예제 #11
0
        static string outfilename; /* for -outfile switch */

        public static void Main(string[] args)
        {
            progname = Path.GetFileName(Environment.GetCommandLineArgs()[0]);

            /* Initialize the JPEG decompression object with default error handling. */
            cd_jpeg_error_mgr      err   = new cd_jpeg_error_mgr();
            jpeg_decompress_struct cinfo = new jpeg_decompress_struct(err);

            /* Insert custom marker processor for COM and APP12.
             * APP12 is used by some digital camera makers for textual info,
             * so we provide the ability to display it as text.
             * If you like, additional APPn marker types can be selected for display,
             * but don't try to override APP0 or APP14 this way (see libjpeg.doc).
             */
            cinfo.jpeg_set_marker_processor((int)JPEG_MARKER.COM, new jpeg_decompress_struct.jpeg_marker_parser_method(print_text_marker));
            cinfo.jpeg_set_marker_processor((int)JPEG_MARKER.APP0 + 12, print_text_marker);

            /* Scan command line to find file names. */

            /* It is convenient to use just one switch-parsing routine, but the switch
             * values read here are ignored; we will rescan the switches after opening
             * the input file.
             * (Exception: tracing level set here controls verbosity for COM markers
             * found during jpeg_read_header...)
             */
            int file_index;

            if (!parse_switches(cinfo, args, false, out file_index))
            {
                usage();
                return;
            }

            /* Must have either -outfile switch or explicit output file name */
            if (outfilename == null)
            {
                // file_index should point to input file
                if (file_index != args.Length - 2)
                {
                    Console.WriteLine(string.Format("{0}: must name one input and one output file.", progname));
                    usage();
                    return;
                }

                // output file comes right after input one
                outfilename = args[file_index + 1];
            }
            else
            {
                // file_index should point to input file
                if (file_index != args.Length - 1)
                {
                    Console.WriteLine(string.Format("{0}: must name one input and one output file.", progname));
                    usage();
                    return;
                }
            }

            /* Open the input file. */
            FileStream input_file = null;

            if (file_index < args.Length)
            {
                try
                {
                    input_file = new FileStream(args[file_index], FileMode.Open);
                }
                catch (Exception e)
                {
                    Console.WriteLine(string.Format("{0}: can't open {1}", progname, args[file_index]));
                    Console.WriteLine(e.Message);
                    return;
                }
            }
            else
            {
                Console.WriteLine(string.Format("{0}: sorry, can't read file from console"));
                return;
            }

            /* Open the output file. */
            FileStream output_file = null;

            if (outfilename != null)
            {
                try
                {
                    output_file = new FileStream(outfilename, FileMode.Create);
                }
                catch (Exception e)
                {
                    Console.WriteLine(string.Format("{0}: can't open {1}", progname, args[file_index]));
                    Console.WriteLine(e.Message);
                    return;
                }
            }
            else
            {
                Console.WriteLine(string.Format("{0}: sorry, can't write file to console"));
                return;
            }

            /* Specify data source for decompression */
            cinfo.jpeg_stdio_src(input_file);

            /* Read file header, set default decompression parameters */
            cinfo.jpeg_read_header(true);

            /* Adjust default decompression parameters by re-parsing the options */
            parse_switches(cinfo, args, true, out file_index);

            /* Initialize the output module now to let it override any crucial
             * option settings (for instance, GIF wants to force color quantization).
             */
            djpeg_dest_struct dest_mgr = null;

            switch (requested_fmt)
            {
            case IMAGE_FORMATS.FMT_BMP:
                dest_mgr = new bmp_dest_struct(cinfo, false);
                break;

            case IMAGE_FORMATS.FMT_OS2:
                dest_mgr = new bmp_dest_struct(cinfo, true);
                break;

            default:
                cinfo.ERREXIT((int)ADDON_MESSAGE_CODE.JERR_UNSUPPORTED_FORMAT);
                break;
            }

            dest_mgr.output_file = output_file;

            /* Start decompressor */
            cinfo.jpeg_start_decompress();

            /* Write output file header */
            dest_mgr.start_output();

            /* Process data */
            while (cinfo.Output_scanline < cinfo.Output_height)
            {
                int num_scanlines = cinfo.jpeg_read_scanlines(dest_mgr.buffer, dest_mgr.buffer_height);
                dest_mgr.put_pixel_rows(num_scanlines);
            }

            /* Finish decompression and release memory.
             * I must do it in this order because output module has allocated memory
             * of lifespan JPOOL_IMAGE; it needs to finish before releasing memory.
             */
            dest_mgr.finish_output();
            cinfo.jpeg_finish_decompress();

            /* Close files, if we opened them */
            input_file.Close();
            input_file.Dispose();

            output_file.Close();
            output_file.Dispose();

            /* All done. */
            if (cinfo.Err.Num_warnings != 0)
            {
                Console.WriteLine("Corrupt-data warning count is not zero");
            }
        }
예제 #12
0
        /// <summary>
        /// For bits 0-63 only the LSB of DCT coefficients is written (it takes the full length of the bits to read). From bit 64 every DCT coefficient is written with the last 2 LSBs.
        /// </summary>
        /// <param name="jpegFilePath"></param>
        /// <param name="bits"></param>
        /// <param name="securityLevel"></param>
        private static void changeDctCoefficients(string jpegFilePath, BitArray bits, int securityLevel)
        {
            var        jpds          = new jpeg_decompress_struct();
            FileStream fileStreamImg = new FileStream(jpegFilePath, FileMode.Open, FileAccess.Read);

            jpds.jpeg_stdio_src(fileStreamImg);
            jpds.jpeg_read_header(true);
            // DCT coefficients
            var  jBlock            = jpds.jpeg_read_coefficients();
            var  bitsLen           = bits.Length;
            int  currentBit        = 0;
            bool canIterate        = true;
            int  cntAcCoeffNotZero = 0;
            // Write on position 0 then 1
            // Start with Cr component
            int component = (int)EnumStegoChannel.Cr;

            while ((component >= 0) && canIterate)
            {
                int z = 0;
                while (canIterate)
                {
                    int countCrRows;
                    try
                    {
                        countCrRows = jBlock[component].Access(z, 1)[0].Length;
                    }
                    catch
                    {
                        break;
                    }
                    int i = 0;
                    while ((i < countCrRows) && canIterate)
                    {
                        // Skip DC coefficients j = 0
                        int j = 1;
                        while ((j < 64) && canIterate)
                        {
                            short currentVal = jBlock[component].Access(z, 1)[0][i][j];

                            // Skip bits 0 and dct according to the security level (steps)
                            if (0 != currentVal) // Set only 1 bit for the first 64 AC coefficients
                            {
                                // Define the step of bits to write after 64 bits
                                if ((0 == cntAcCoeffNotZero % securityLevel) || (currentBit < 64))
                                {
                                    // Get the current bit
                                    bool bit = bits[currentBit];
                                    // Write the new value in position 0
                                    short newVal = getNewValForComponent(currentVal, bit, 0);
                                    jBlock[component].Access(z, 1)[0][i][j] = newVal;

                                    currentBit++;
                                    canIterate = currentBit < bitsLen;
                                    // Start writing on bit 1 after 64 bits to encode the content length
                                    if (canIterate && currentBit > 64)
                                    {
                                        // Get the current bit
                                        bit = bits[currentBit];
                                        // Write the new value in position 1
                                        newVal = getNewValForComponent(newVal, bit, 1);
                                        jBlock[component].Access(z, 1)[0][i][j] = newVal;
                                        currentBit++;
                                        canIterate = currentBit < bitsLen;
                                    }
                                }
                                cntAcCoeffNotZero++;
                            }
                            j++;
                        }
                        i++;
                    }
                    z++;
                }
                component--;
            }
            jpds.jpeg_finish_decompress();
            fileStreamImg.Close();
            // Get file info
            FileInfo fInfo            = new FileInfo(jpegFilePath);
            string   dir              = fInfo.DirectoryName;
            string   fName            = fInfo.Name;
            var      docPath          = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var      stegJpegFilePath = $@"{docPath}\StegImageUI\steg_{DateTime.Now.ToString("yyyyMMddHHmmss")}_{fName}";
            // Compress process
            FileStream           fileStream = File.Create(stegJpegFilePath);
            jpeg_compress_struct jpcs       = new jpeg_compress_struct();

            jpcs.jpeg_stdio_dest(fileStream);
            jpds.jpeg_copy_critical_parameters(jpcs);
            jpcs.jpeg_write_coefficients(jBlock);
            jpcs.jpeg_finish_compress();
            fileStream.Close();
            jpds.jpeg_abort_decompress();
        }