Question: Image process question. Need help on this java assignment. First: Work with reading from and writing to files The first thing you should do is
Image process question. Need help on this java assignment.
First: Work with reading from and writing to files
The first thing you should do is work with the example code from earlier in the assignment for reading and writing text files. Make sure that you can get that code working. A working knowledge of this example code to open files for reading and writing is necessary for the next step.
Second: Get the PPM image files to work with
In order to grab a copy of the image files you'll be using, issue the following command when logged into Loki:
This should download a ZIP archive of images to your directory. You then need to expand the contents of the ZIP archive with the following command:
If everything works successfully, you should have three files in your account that are PPM images: lamb.ppm , nebfarm.ppm and flowers.ppm . You can transfer those files to your local machine using FileZilla and
open them with GIMP to see the original images.
If you ever accidentally mess up the original copies of these files, you can simply reissue the complete unzip command and you'll unpack a fresh copy of each of the original image files over the messed up copies.
You should open up at least one of these image files in vim , such as vim lamb.ppm , to take a look at how it is structured. Take notice that the first three lines of each file is the header for the PPM file and that the rest of the file is nothing but integer values, with each successive group of three integers describing a single pixel of the image.
Third: Write code to make a copy of a PPM image file
The next thing you need to be able to do is to read in a PPM file and then write out an exact copy of the file under a new filename.
Starting Off
So, first, write a program that will prompt the user for a source filename and a destination filename from the keyboard. When you enter the filenames, they should both end in .ppm such as flowers.ppm and copyflowers.ppm . Then, using the code already presented in the assignment, open the source file and
copy it into the destination file.
You might start by just opening the file and reading the header and printing it out to the screen so you know you're reading the file correctly.
Once you have that working, you could add in code to read all of the remaining integers of the file and just blast them out to the screen so you can see that you're reading the rest of the file correctly.
Finally, you should add in appropriate code that will write the data to the output file that the user specified earlier.
But image files are huge!
Because a picture of normal size (thousands of pixels wide by thousands of pixels tall) might contain hundreds of thousands of numbers, you probably wouldn't be able to store it in memory all at once.
Therefore, you will implement your program so that it has a temporary "copy buffer", which will be implemented with a one-dimensional array of 3000 integers. This array would represent one row of an image up to a 1000pixel-wide image (3 integers per pixel).
Your program will need to read one row of the image at a time from the file into the "copy buffer" array. If the image was 500 pixels wide, it would need to read 1500 numbers from the file (500 pixels * 3 integers per pixel). If the image was 782 pixels wide, it would need to read 2346 numbers from the file (782 pixels * 3 integers per pixel).
As you can see, you may not use all 3000 integer values in your buffer for each row. The header information in the PPM file gives you the number of columns in the image, and if you do the math correctly, you should know how many values to read for each row of the image.
After you've read and written one row of the image, you will end up reusing the "copy buffer" again, starting to fill it from element 0 again. In this way, your copy buffer is used over and over and over again to read a single row's worth of data from the file.
Things to keep in mind
When reading the input file:
Don't forget to read the header lines from the file and process that information correctly. The first line of the file should be processed as String input (the "magic number"). The next two lines should be processed with int data being read from the file, according to the format described earlier in this document. The rest of the data in the file will be int data. There is no need to read any of the pixel data as anything other than int data.
When writing out the image, here are some things to think about:
Make sure to output the image header first in the correct format (i.e., a "magic number" line, a line with the columns and rows, separated by a space and a third line with the color depth.
After that, the format of the numerical data doesn't matter much. It's just as acceptable to write out the numerical/pixel data with spaces between each value as it is to write it out with newlines, or to write out each pixel (the red, green and blue values) each on one one line.
Example program run:
Checking your progress so far
Viewing PPM images
You'll need to be be able to view the PPM images. PPM images aren't natively supported for viewing in many ways, so you'll end up running a program that converts the PPM image to a more commonly supported JPG image format. The conversion program will then copy the JPG images to a directory on loki.ist.unomaha.edu that you can access by a web browser with a given URL.
The program that you'll be running is ~rfulkerson/pjc , which stands for "PPM JPEG Convert".
Whenever you have a new .ppm file you would like to look at, you can run the ~rfulkerson/pjc program to create the JPG version of the image and move it to the appropriate place on Loki.
You'll run the program like this:
After the program is run, you can load the images individually in a web browser using the URLs or load them all at once using the given URL ( http://loki.ist.unomaha.edu/~jsmith/ppm.html in this example; it will be personalized for your particular account).
The ~rfulkerson/pjc conversion program can take a single argument to convert one .ppm file or an unlimited number of arguments to convert as many .ppm files as you want to convert. The ~rfulkerson/pjc conversion program can only convert .ppm files.
These are some examples of various runs of the conversion program:
~rfulkerson/pjc input.ppm
~rfulkerson/pjc input.ppm foo.ppm
~rfulkerson/pjc foo.ppm input.ppm what.ppm another.ppm a.ppm
TIP: You can quickly convert all PPM files in your directory using the following command:
Fourth: Start Transforming the Image
Don't start on this part of the program until you have completed the previous part, which is to make a basic copy of the PPM file!
Now that you are certain you are reading and writing the image correctly, you will start writing methods to transform the image in different ways.
Now you're going to start writing methods to manipulate the image.
Changing Red Values
The first method you'll write will be called negateRed . This method will change each pixel (remember that a pixel is represented by three integer values!) in the buffer so that the red color values are their "negative" value.
The "negative" value is based on the maximum color depth, which was loaded in the header.
Since 255 is the color depth we're working with, an initial red value for a pixel of 0 would become 255 . An intial red value for a pixel of 255 would become 0 . An initial red value for a pixel of 100 would become
155 , whereas an initial red value for a pixel of 201 would become 54 . Doing this calculation is simple subtraction.
For example, if the original array looked like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 50 | 100 | 150 | 25 | 50 | 75 | 100 | 50 | 10 | 1 | 2 | 3 |
then the modified array would look like this:
| Position | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 205 | 100 | 150 | 230 | 50 | 75 | 155 | 50 | 10 | 254 | 2 | 3 |
Notice how the elements in positions 0 , 3 , 6 and 9 were complemented.
When this method is written, make a call to the method after you have filled your copy buffer but before you write out the buffer so that the image row in the buffer is processed by negating all of the red in that row.
Compile and run the program and see if the output image looks the way you would expect an image to look if all of the red in the image was negated.
The method and process sounds complicated, but ultimately it can be written in just a few lines of code.
Changing Green Values
Once you have that method working, write a method called negateGreen that works on the green value in each pixel by negating it's value in the same way that you negated the red value previously.
For example, if the original array looked like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 50 | 100 | 150 | 25 | 50 | 75 | 100 | 50 | 10 | 1 | 2 | 3 |
then the modified array would look like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 50 | 155 | 150 | 25 | 205 | 75 | 100 | 205 | 10 | 1 | 253 | 3 |
Notice how the elements in positions 1 , 4 , 7 and 10 were complemented.
When this method is written, replace your call to negateRed() with a call to negateGreen() and see how the image is processed.
Compile and run the program and see if the output image looks the way you would expect an image to look if all of the green in the image was negated.
Changing Blue Values
Once you have that method working, you should write a method called negateBlue that works on the blue value in each pixel by negating it's value in the same way that you negated the red and green values previously.
For example, if the original array looked like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 50 | 100 | 150 | 25 | 50 | 75 | 100 | 50 | 10 | 1 | 2 | 3 |
then the modified array would look like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 50 | 100 | 105 | 25 | 50 | 180 | 100 | 50 | 245 | 1 | 2 | 252 |
Notice how the elements in positions 2 , 5 , 8 and 11 were complemented.
When this method is written, replace your call to negateGreen() with a call to negateBlue() and see how the image is processed.
Compile and run the program and see if the output image looks the way you would expect an image to look if all of the blue in the image was negated.
Checkpoint
If you have each of the negation methods written correctly, you should be able to call all three of them, one after the other, and receive a perfect negative image of the original PPM file.
Creating a Greyscale Version of the Image
Write a method called greyscale which will modify the image so that it no longer has any color and is nothing but shades of grey.
Converting an image to greyscale is easier that you might think. You simply average the red, green and blue values for each pixel and then replace all three values with that average.
For example, if the pixel's RGB values were 56, 100 and 237, then the average is 131 and you should replace all three RGB values with 131. Make sure you do integer division for the average and that you replace all three values for each pixel with the average. So a pixel with values (56,100,237) would become a pixel with values (131,131,131).
For example, if the original array looked like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 50 | 100 | 150 | 25 | 50 | 75 | 100 | 50 | 10 | 1 | 2 | 3 |
then the modified array would look like this:
| Pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Value | 100 | 100 | 100 | 50 | 50 | 50 | 53 | 53 | 53 | 2 | 2 | 2 |
For example, notice how the values for positions (0,1,2) have been averaged to 100 and the values for positions (6,7,8) have been averaged to 53 .
When this method is written, comment out any calls to negateRed , negateGreen and negateBlue and insert a call to greyscale instead.
Compile and run the program and see if the output image looks the way you would expect an image to look if it was converted to a greyscale image.
The method sounds complicated, but just like the negation methods, it can be written in just a few lines of code.
It takes a few more lines of code here for this method than in the negation methods.
Flipping the image horizontally
The trickiest transition to write is one that will flip the image horizontally. Write a method called flipHorizontal which will implement a horizontal image flip.
This means that the pixel that is on the far right end of a row will end up on the far left end of the row instead. Remember while you're doing this to maintain the RGB triplet ordering of the data. Don't just flip the array cellby-cell, but rather pixel-by-pixel, in groups of three values.
Swapping pixels is very similar to swapping values in an array for sorting. There will need to be temporary values when swapping, and when you swap a pixel, you'll actually need to do three separate swaps -- one for the red values, one for the green values and one for the blue values.
For example, if the original array looked like this:
| Position | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| Value | 50 | 100 | 150 | 25 | 50 | 75 | 100 | 50 | 10 | 1 | 2 | 3 | 15 | 30 | 17 |
Then after the horizontal flip it would look like this:
| Position | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| Value | 15 | 30 | 17 | 1 | 2 | 3 | 100 | 50 | 10 | 25 | 50 | 75 | 50 | 100 | 150 |
Notice how elements 0,1,2 have been swapped with elements 12,13,14 and how elements 3,4,5 have swapped with elements 9,10,11. Because there are an odd number of pixels in this array (five groups of three), the middle pixel remains the same (elements 6,7,8).
When this method is written, comment out any calls to negateRed , negateGreen , negateBlue and greyscale and insert a call to flipHorizontal instead.
Compile and run the program and see if the output image looks the way you would expect an image to look if it was a horizontal mirror image of itself.
Fifth: Make Each Transformation Optional
Lastly, once you have each of the image transformations working correctly, you need to add code to ask the user if they would like to negate the red color, negate the green color, negate the blue color, greyscale the image and/or flip the image horizontally. Here is an example of this functionality:
The questions must be asked after the input and output file names have been obtained from the user, and the answer to each question must be a simple y or n (lowercase y or n ).
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
