/// <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); }
// 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)); } }
/// <summary> /// Decompresses JPEG image to any image described as ICompressDestination /// </summary> /// <param name="jpeg">Stream with JPEG data</param> /// <param name="destination">Stream for output of compressed JPEG</param> public void Decompress(Stream jpeg, IDecompressDestination destination) { if (jpeg == null) { throw new ArgumentNullException("jpeg"); } if (destination == null) { throw new ArgumentNullException("destination"); } beforeDecompress(jpeg); // Start decompression m_decompressor.jpeg_start_decompress(); LoadedImageAttributes parameters = getImageParametersFromDecompressor(); destination.SetImageAttributes(parameters); destination.BeginWrite(); /* Process data */ while (m_decompressor.Output_scanline < m_decompressor.Output_height) { byte[][] row = jpeg_common_struct.AllocJpegSamples(m_decompressor.Output_width * m_decompressor.Output_components, 1); m_decompressor.jpeg_read_scanlines(row, 1); destination.ProcessPixelsRow(row[0]); } destination.EndWrite(); // Finish decompression and release memory. m_decompressor.jpeg_finish_decompress(); }
/// <summary> /// Get the Huffman tables using reflection for a specific component (AC, DC) /// </summary> /// <returns></returns> public static HuffmanTable[] GetHuffmanTables(string JpegFilePath) { FileStream objFileStreamMegaMap = File.Create(JpegFilePath); jpeg_decompress_struct jpds = new jpeg_decompress_struct(); jpeg_compress_struct jpcs = new jpeg_compress_struct(); jpcs.jpeg_stdio_dest(objFileStreamMegaMap); jpds.jpeg_copy_critical_parameters(jpcs); jpds.jpeg_finish_decompress(); objFileStreamMegaMap.Close(); // DC Huffman tables JHUFF_TBL[] jpeg_dc_huffman_tables = jpcs.Dc_huff_tbl_ptrs; var comp = HuffmanTable.EnumComponent.DC; var htdc = getHTables(jpeg_dc_huffman_tables, comp); // AC Huffman tables JHUFF_TBL[] jpeg_ac_huffman_tables = jpcs.Ac_huff_tbl_ptrs; comp = HuffmanTable.EnumComponent.AC; var htac = getHTables(jpeg_ac_huffman_tables, comp); HuffmanTable[] hts = new HuffmanTable[htdc.Length + htac.Length]; Array.Copy(htdc, hts, htdc.Length); Array.Copy(htac, 0, hts, htdc.Length, htac.Length); return(hts); }
/// <summary> /// Get the Quantization tables using reflection per component /// </summary> /// <returns></returns> public static short[][] GetQuantizationTables(string JpegFilePath) { FileStream objFileStreamMegaMap = File.Create(JpegFilePath); jpeg_decompress_struct jpds = new jpeg_decompress_struct(); jpeg_compress_struct jpcs = new jpeg_compress_struct(); jpcs.jpeg_stdio_dest(objFileStreamMegaMap); jpds.jpeg_copy_critical_parameters(jpcs); jpds.jpeg_finish_decompress(); objFileStreamMegaMap.Close(); JQUANT_TBL[] jpeg_quant_tables = jpcs.Quant_tbl_ptrs; int qCount = jpeg_quant_tables.Length; short[][] qt = new short[qCount][]; BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; int idx = 0; while (null != jpeg_quant_tables[idx]) { Type type = jpeg_quant_tables[idx].GetType(); object instance = jpeg_quant_tables[idx]; FieldInfo field = type.GetField("quantval", bindFlags); qt[idx] = field.GetValue(instance) as short[]; ++idx; } var qTables = qt.Where(tab => null != tab).ToArray(); return(qTables); }
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)); } }
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(); }
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"); } }
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"); } }
/// <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(); }