Algorithm overview
The programming language used in this program is C#.
C #'s Aforge.imaging library was initially used. After many debugging and algorithm function analysis, it is found that there are great problems in the implementation of FFT in the library. It can only transform the picture Fourier, but not the inverse transform.
And in the StackOverflow forum, someone raised a similar problem, which could not be solved
Later, a relatively good function was found in Exocortex.DSP library, but there were still problems in the implementation of the algorithm.
The image can be changed back on the spot after the first transformation, but the image cannot be output when the transformed image is used as input for inverse transformation.
After many times of debugging and analysis of the Fourier coefficient value in the algorithm, it is found that the coefficients when transforming the image matrix after Fourier transform into the coefficients of Fourier complex number will be different from those after debugging, analysis and data access
The problem is found in the algorithm Π (Pi), Log (N) and other numerical values requiring high accuracy, as well as the problem of Fourier coefficient conversion in the picture matrix of the transformed picture.
For most FFT algorithms, the "inaccuracy" increases roughly by O(NlogN) with the size of the input element; And KCS/IEEE754 floating point numbers carry about 24 bit precision. Therefore, for non ultra long FFT, the noise in the data, imperfect anti aliasing and input quantization are usually greater than the arithmetic error.
After analysis, I thought of retaining the transformed Fourier coefficients directly.
My idea is to save the coefficients of the original image after the first fast Fourier transform, so that the original image can be restored directly according to these data.
This program realizes the data retention of BMP format. By modifying the BMP file header, the coefficient is retained in the specified position in the file format, and will not affect the pixel data of the image file. Before embedding the secret information into the file header, it will be encrypted by AES encryption algorithm. Then the data in the BMP file header is embedded into the file in the form of ciphertext, which improves certain security.
If the LSB algorithm is selected to embed the information, the image pixel data will be directly destroyed, which will lead to the inaccurate matrix coefficient when the original calculation accuracy is not ideal. Therefore, the file format without affecting the file pixels is adopted for embedding.
BMP introduction
BMP is the abbreviation of English Bitmap. It is the standard image file format in Windows operating system and can be supported by a variety of Windows applications. With the popularity of Windows operating system and the development of rich Windows applications, BMP Bitmap format is naturally widely used. This format is characterized by rich image information and almost no compression, but it leads to its inherent disadvantage - too much disk space. Therefore, BMP is popular on single machine at present.
Bit analysis of BMP file format
[0,14) is the header of the bitmap file.
[0,2): "BM", i.e. file type
[2,6): file size (in bytes)
[6,10) reserved bits (all 0)
[10,14) offset (file header + information header + palette)
[14, 54) are bitmap information headers,
[14,18) header length
[18,22) width
[22,26) height
[26,28) number of planes (constant 1)
[28,30) the number of bits per pixel (8 is a grayscale image and 24 is a true color)
[30,34) compression description
[34,38) bitmap data size (multiple of 4)
[38,42) horizontal resolution [42,46) vertical resolution
[46,50) number of colors used
[50,54) specify the number of important colors
[54, offset address) is the palette portion of the image
[offset address, last) is the bitmap data part of the file, which stores the pixel information of the picture
Encryption idea
The information to be embedded is mainly divided into two parts. The first is the information file we need to hide, and the second is the coefficients of the original image after fast Fourier transform
[0,64] name of the watermark file,
[64, 68] size of watermark file,
[68, 68+size] Bookstore stream for storing documents,
[length-68-size] storing Fourier coefficients
Modify the reserved position of part [6, 10] in the source file format to the length of the overall embedded data
flow chart
Encryption flow chart
Decryption flowchart
Information embedding flow chart
Information extraction process
The extraction process is the inverse of embedding
Program demonstration
FFT transform
encryption
decrypt
Comparison of similarity between carrier before transformation and after inverse transformation (gray image)
Comparison of carrier similarity before and after embedding
Comparison of watermark file similarity embedding and extraction
Because the image information is embedded into the file format rather than pixels, it certainly will not have any impact on the similarity of the embedded image
analysis
1. In terms of the complexity analysis of FFT and DFT, the algorithm complexity of FFT is O (NlogN), and the algorithm complexity of DFT is O (N^2). The optimization effect is very good
2. The image after Fourier transform is opened again, and there is no way to restore it. By embedding the coefficient information of the initial transform and the secret file information to be embedded into the binary file of the carrier, it is ensured that the transformed image can realize the inverse transform, and the embedded watermark file is between the file bitmap data and the information header, which will not affect the pixel information of the carrier Rest.
3. The information embedded in the binary bit stream has been encrypted by AES. Even if the attacker has mastered the algorithm, the information cannot be restored without the key, realizing the confidentiality of the information
4. Before transformation, the carrier image is transformed into gray image for the purpose of calculating Fourier coefficient, and the information hiding and transformation of color image can not be realized.
5. The accuracy of Fourier transform has not been substantially solved. The problem is that the algorithm used has some defects. Secondly, the method of embedding coefficients into pictures is not necessarily scientific, but can meet the operation of changing back after transformation
6. After analyzing and comparing the similarity of Fourier images before and after transformation, carrier images before and after transformation, and PSNR and MSE of embedded watermark images, it is found that modifying the file header to embed data will not affect the file pixels, and the difference between the extracted Fourier coefficient and the restored image and the gray image of the original carrier image is also within a reasonable range.
Partial code display
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Windows.Forms; using Information Steganography Based on picture format.Helper; using Information Steganography Based on picture format.Exceptions; using Information Steganography Based on picture format.Encryption; namespace Information Steganography Based on picture format { public partial class Form1 : Form { private byte [] fileflow; private byte[] bmpflow; private uint file_size; private string filename_; private uint encrypted_length; private List<Byte> File_list=new List<byte> { }; public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { using (OpenFileDialog S = new OpenFileDialog()) { S.DefaultExt = ""; S.Filter = "file|*.*"; S.InitialDirectory = ""; if (S.ShowDialog() == System.Windows.Forms.DialogResult.OK) { this.label2.Text = S.FileName; FileInfo file_ = new FileInfo(S.FileName); filename_ = S.FileName.Substring(S.FileName.LastIndexOf("\\") + 1); string fileNameBinary = Converter.StringToBinary(filename_, 64); fileflow = File.ReadAllBytes(S.FileName); byte[] temp = Converter.BinaryToByteArray(fileNameBinary); for (int i = 0; i < temp.Length; i++) { File_list.Add(temp[i]); } for (int i = 64; i < fileflow.Length+64; i++) { File_list.Add(fileflow[i-64]); } try { pictureBox2.Image = Image.FromFile(S.FileName); } catch (Exception) { } } } } private void button2_Click(object sender, EventArgs e) { using (OpenFileDialog S = new OpenFileDialog()) { S.DefaultExt = ""; S.Filter = "BMP image|*.bmp;*.BMP"; S.InitialDirectory = ""; if (S.ShowDialog() == System.Windows.Forms.DialogResult.OK) { try { pictureBox1.Image = Image.FromFile(S.FileName); this.label1.Text = S.FileName; bmpflow = File.ReadAllBytes(S.FileName); } catch (Exception) { MessageBox.Show("Invalid picture file"); } } } } private void button3_Click(object sender, EventArgs e) { byte[] fileflow_encrypted = new byte[]{ }; fileflow_encrypted = AES.Encrypt(File_list.ToArray(), this.textBox1.Text); encrypted_length = (uint)fileflow_encrypted.Length; byte[] encrypted_length_array = BitConverter.GetBytes(encrypted_length); file_size = encrypted_length; uint filesize = file_size; for (int i = 6; i < 10; i++) { bmpflow[i] = encrypted_length_array[i - 6]; } byte[] size = new byte[4]; //file size Array.Copy(bmpflow, 2, size, 0, 4); var size_int = BitConverter.ToUInt32(size, 0); size_int += filesize; byte[] temp = BitConverter.GetBytes(size_int); for (int i = 2; i < 6; i++) { bmpflow[i] = temp[i-2]; //111 } byte[] offset_array = new byte[4]; Array.Copy(bmpflow, 10, offset_array, 0, 4); //Offset var offset_int = BitConverter.ToUInt32(offset_array, 0); uint offset_origin = offset_int; offset_int += encrypted_length; byte[] temp2 = BitConverter.GetBytes(offset_int); for (int i = 10; i < 14; i++) { bmpflow[i] = temp2[i-10];//1111 } List<byte> arr1 = new List<byte> { }; for (int i = 0; i < bmpflow.Length; i++) { arr1.Add(bmpflow[i]); } List<byte> arr2 = new List<byte> { }; for (int i = 0; i < fileflow_encrypted.Length; i++) { arr2.Add(fileflow_encrypted[i]); } arr1.InsertRange((int)offset_origin, arr2);//insert byte[] compound_flow = arr1.ToArray(); string stegoPath=""; FolderBrowserDialog dlg = new FolderBrowserDialog(); if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { stegoPath = dlg.SelectedPath; } string tempname = this.label1.Text.Substring(this.label1.Text.LastIndexOf("\\") + 1); tempname = tempname.Insert(tempname.LastIndexOf('.'), "_compound"); stegoPath += "\\" + tempname; File.WriteAllBytes(stegoPath, compound_flow ); } private void button4_Click(object sender, EventArgs e) { byte[] compound = new byte[] { }; using (OpenFileDialog S = new OpenFileDialog()) { S.DefaultExt = ""; S.Filter = "BMP image|*.bmp;*.BMP"; S.InitialDirectory = ""; if (S.ShowDialog() == System.Windows.Forms.DialogResult.OK) { try { pictureBox3.Image = Image.FromFile(S.FileName); this.label6.Text = S.FileName; compound = File.ReadAllBytes(S.FileName); } catch (Exception) { MessageBox.Show("Invalid picture file"); } } string filename; byte[] offset2 = new byte[4]; //Offset Array.Copy(compound,10 , offset2, 0, 4); var offset_int = BitConverter.ToUInt32(offset2, 0); byte[] length_array = new byte[4]; //Offset Array.Copy(compound, 6, length_array, 0, 4); var length_ = BitConverter.ToUInt32(length_array, 0); byte[] encrypted_flow = new byte[length_]; Array.Copy(compound, offset_int - length_, encrypted_flow, 0, length_); try { byte[] extracted_flow = AES.Decrypt(encrypted_flow, this.textBox1.Text); int size = extracted_flow.Length; byte[] name = new byte[64]; byte[] extracted_fileflow = new byte[size - 64]; Array.Copy(extracted_flow, 0, name, 0, 64); Array.Copy(extracted_flow, 64, extracted_flow, 0, size - 64); filename = Converter.ByteArrayToBinary(name); filename = Converter.BinaryToString(filename).Replace("\0", ""); FolderBrowserDialog s = new FolderBrowserDialog(); string result_path = ""; if (s.ShowDialog() == System.Windows.Forms.DialogResult.OK) { result_path = s.SelectedPath + "\\" + filename.Insert(filename.LastIndexOf('.'), "_extracted"); } File.WriteAllBytes(result_path, extracted_flow ); this.label8.Text = result_path; try { pictureBox4.Image = Image.FromFile(result_path); } catch (Exception) { } } catch(Exception) { MessageBox.Show("Invalid secret key"); } } } } }