Background You are to write programs to find and hide the steganographic files (
ID: 3696262 • Letter: B
Question
Background
You are to write programs to find and hide the steganographic files
(http://en.wikipedia.org/wiki/Steganography) in the included set of images. For example, a file is hidden this image:
Here’s how we did it. We have two files:
the mask image (left, color/grayscale)
the hidden file (any format, but I suggest using a text file)
The method for hiding data in an existing image is rather simple. A standard grayscale image is made up of a 2-dimensional array of pixels. Each pixel is just an 8-bit unsigned char (a number between 0 and 255). 8 bits is usually enough to encode most information for a grayscale image. However, while your eye can probably tell the difference between a pixel value of 5 and a pixel value of 200, you probably will not notice a small difference (say the difference between 200 and 201). We can take advantage of this to hide information that a computer can detect but a person could not.
So, if we start with an 8-bit pixel, we can throw out the least significant bit, the one that distinguishes the value of 200 from 201. The binary value of 200 is: 11100010 and 201 is: 11100011.
If we just toss out whatever value is in that last bit, we can hide a single bit from the file we want to hide. (like 1 if the hidden bit should be a 1, and 0 if the hidden bit should be a 0).
More specifically, you need to:
Write a program to hide a file inside of another image
allocate memory for a properly-sized multi-dimensional array
for each pixel in the hidden image, encode that information along with the mask image
write the new image to a file
Write a program to unmask the hidden file from an encoded image and save it as a decoded file
for each pixel in the encoded image, pull out the least significant bit and use those bits to build bytes
write the bytes to the decoded file
Your goal (for the encode portion): to write a program that will:
read three command-line arguments, the first is the image we wish to hide a file in, the second is the filename of the file we wish to hide, the third is the filename that we will write the hidden image to.
copy the original image to an array
create new pgm file to hold changed image
allocate memory for the new image data
start at the mask image’s 0,0 pixel
for each bit in the hidden file
drop the least significant bit from the mask image current pixel value
add a 1 to that pixel value if the corresponding hidden file bit is a 1
add a 0 (or don’t) to that pixel value if the corresponding hidden file bit should be a 0
move to the next pixel in the image (the next column in the image, or the next row’s 0th column if at the max column has been reached)
write an EOF character in the next 8 image pixels
save the array to a file using the pgm image format we’ve been using for the prior projects
Your goal (for the decode portion): to write a program that will:
read two command-line arguments; take the first command-line argument which is the filename of an image (we will provide two pgm images to test with)
read that image from the provided filename to an array
open the decoded file with the second command-line argument as the filename
for each pixel in the imageif the last bit is zero
make the next corresponding bit in the hidden file a 0
if the last bit is one
make the next corresponding bit in the hidden file a 1
write each decoded byte to the decoded file
If you recall from before, to do this you need to:
Name your files encode.c and decode.c
Example images can be found here: (http://www.cse.unr.edu/~dave/cs135/)
Challenge 1:
For encode.c, test the files beforehand to make sure that every bit of the code file can fit into the mask image. Save this challenge as encode_challenge.c
Challenge 2:
Create functions:
One that takes a filename as an argument, and reads a PGM image from a file and return a dynamically allocated 2D array that stores the image
One that takes a filename, a dynamically allocated 2D array, and image dimensions as arguments and writes the image to a file
Save those functions in a separate filename, image.c. Put prototypes for those functions in a header file image.h, and use those files in a revised version of your program, encode_challenge2.c
and decode_challenge2.c
Explanation / Answer
entity tb_pgm is
end entity tb_pgm;
use work.libv.all;
use work.pgm.all;
architecture test of tb_pgm is
begin -- architecture test
test1 : process is
variable i : pixel_array_ptr;
constant testdata : pixel_array(0 to 7, 0 to 3) := transpose(pixel_array'(
(000, 027, 062, 095, 130, 163, 198, 232),
(000, 000, 000, 000, 000, 000, 000, 000),
(255, 255, 255, 255, 255, 255, 255, 255),
(100, 100, 100, 100, 100, 255, 255, 255))
);
variable blacksquare : pixel_array(0 to 7, 0 to 7) := (others => (others => 0));
begin -- process test1
-- test on a proper image
i := pgm_read("testimage_ascii.pgm");
assert i /= null report "pixels are null" severity error;
assert_equal("ASCII PGM Width", i.all'length(1), 8);
assert_equal("ASCII PGM Height", i.all'length(2), 4);
assert_equal("ASCII PGM data", i.all, testdata);
-- make sure we return a non-image for the binary-style PGM file
i := pgm_read("testimage.pgm");
assert i = null report "Binary pixels should be null" severity error;
-- Now create an image from scratch - a letter M
blacksquare(1,1) := 255; blacksquare(5,1) := 255;
blacksquare(1,2) := 255; blacksquare(2,2) := 255; blacksquare(4,2) := 255; blacksquare(5,2) := 255;
blacksquare(1,3) := 255; blacksquare(3,3) := 255; blacksquare(5,3) := 255;
blacksquare(1,4) := 255; blacksquare(5,4) := 255;
blacksquare(1,5) := 255; blacksquare(5,5) := 255;
blacksquare(1,6) := 255; blacksquare(5,6) := 255;
pgm_write("test_write.pgm", blacksquare);
report "End of tests" severity note;
wait;
end process test1;
end architecture test;
int writeImage()
{
// Create a FITS primary array containing a 2-D image
// declare axis arrays.
long naxis = 2;
long naxes[2] = { 300, 200 };
// declare auto-pointer to FITS at function scope. Ensures no resources
// leaked if something fails in dynamic allocation.
std::auto_ptr<FITS> pFits(0);
try
{
// overwrite existing file if the file already exists.
const std::string fileName("!atestfil.fit");
// Create a new FITS object, specifying the data type and axes for the primary
// image. Simultaneously create the corresponding file.
// this image is unsigned short data, demonstrating the cfitsio extension
// to the FITS standard.
pFits.reset( new FITS(fileName , USHORT_IMG , naxis , naxes ) );
}
catch (FITS::CantCreate)
{
// ... or not, as the case may be.
return -1;
}
// references for clarity.
long& vectorLength = naxes[0];
long& numberOfRows = naxes[1];
long nelements(1);
// Find the total size of the array.
// this is a little fancier than necessary ( It's only
// calculating naxes[0]*naxes[1]) but it demonstrates use of the
// C++ standard library accumulate algorithm.
nelements = std::accumulate(&naxes[0],&naxes[naxis],1,std::multiplies<long>());
// create a new image extension with a 300x300 array containing float data.
std::vector<long> extAx(2,300);
string newName ("NEW-EXTENSION");
ExtHDU* imageExt = pFits->addImage(newName,FLOAT_IMG,extAx);
// create a dummy row with a ramp. Create an array and copy the row to
// row-sized slices. [also demonstrates the use of valarray slices].
// also demonstrate implicit type conversion when writing to the image:
// input array will be of type float.
std::valarray<int> row(vectorLength);
for (long j = 0; j < vectorLength; ++j) row[j] = j;
std::valarray<int> array(nelements);
for (int i = 0; i < numberOfRows; ++i)
{
array[std::slice(vectorLength*static_cast<int>(i),vectorLength,1)] = row + i;
}
// create some data for the image extension.
long extElements = std::accumulate(extAx.begin(),extAx.end(),1,std::multiplies<long>());
std::valarray<float> ranData(extElements);
const float PIBY (M_PI/150.);
for ( int jj = 0 ; jj < extElements ; ++jj)
{
float arg = PIBY*jj;
ranData[jj] = std::cos(arg);
}
long fpixel(1);
// write the image extension data: also demonstrates switching between
// HDUs.
imageExt->write(fpixel,extElements,ranData);
//add two keys to the primary header, one long, one complex.
long exposure(1500);
std::complex<float> omega(std::cos(2*M_PI/3.),std::sin(2*M_PI/3));
pFits->pHDU().addKey("EXPOSURE", exposure,"Total Exposure Time");
pFits->pHDU().addKey("OMEGA",omega," Complex cube root of 1 ");
// The function PHDU& FITS::pHDU() returns a reference to the object representing
// the primary HDU; PHDU::write( <args> ) is then used to write the data.
pFits->pHDU().write(fpixel,nelements,array);
// PHDU's friend ostream operator. Doesn't print the entire array, just the
// required & user keywords, and is provided largely for testing purposes [see
// readImage() for an example of how to output the image array to a stream].
std::cout << pFits->pHDU() << std::endl;
return 0;
}
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.