Lab 9
Get started now
## Objectives * Employ binary file input and output to read and write image files. * Construct a non-trivial interactive graphical user interface with at least two windows. * Handle exceptions in a robust and meaningful fashion. ## Resources * [Video Introduction](https://use.vg/7jYLVN). * [Sample `.bmsoe` image](specs.bmsoe). ## Overview In this assignment you will build on the solution created for [Lab 8](Lab8). In addition to updating your solution based on feedback from your instructor, you will add support for a new custom binary image format, additional image transformations, and additional user interface elements to support the new functionality. ## Assignment This week you must implement the following functionality: * Load and save images in the custom binary `.bmsoe` file format. * Convert an image to a red-channel version * Convert an image to a red/grayscale version * Refactor the code for grayscale and photo negative transformations using a functional programming approach * Create a second window (an additional stage) for entering filter parameters. * The appropriate graphical user interface to support the operations listed above. * Correct any issues identified in your previous submission, if your instructor has provided feedback to you in a timely manner. All of your classes should be placed in a package whose name matches your MSOE username. ## Details ### Handler Methods * **`void red(ActionEvent event)`** &mdash; Handler for converting an image to red-channel only. * **`void redGray(ActionEvent event)`** &mdash; Handler for converting an image so that every pixel on an odd numbered line contains only the red color data and every pixel on an even numbered line contains the grayscale representation of the color. * **`void blur(ActionEvent event)`** &mdash; Handler for updating filter kernel values for blurring an image.<sup><a href="#fn__1" name="fnt__1" id="fnt__1" class="fn_top">1)</a></sup> * **`void sharpen(ActionEvent event)`** &mdash; Handler for updating filter kernel values for sharpening an image.<sup><a href="#fn__1" id="fnt__1" class="fn_top">1)</a></sup> * **`void apply(ActionEvent event)`** &mdash; Handler for applying a filter operation to an image.<sup><a href="#fn__1" id="fnt__1" class="fn_top">1)</a></sup> ### Refactoring for Functional Programming You must refactor your implementations of the `grayscale()` and `negative()` so that they make use of a functional programming approach. This same approach must be used in the implementations of `red()` and `redGray()`. You must implement the following method in the class that handle the action events for above transformations: ``` private static Image transformImage(Image image, Transformable transform) { // ... } ``` This method applies the specified transformation to each pixel in the image to produce a transformed image. The second argument specifies the behavior of the desired transformation. The `Transformable` interface is a *functional interface*. A functional interface has a single method, and can therefore be implemented with a lambda expression. The method, named `apply()`, must accept two arguments: the y location of the pixel and its color. The method must return the color for the pixel after the applying the transformation. You must specify implementations of the `Transformable` interface for the **grayscale**, **red**, **redGray**, and **negative** image transformations as lambda expressions. ### Red Only This transformation acts as a red filter. The green and blue components of each pixel are set to zero. <figure>[![Red Only](lab9red.png)](lab9red.png)<figcaption>Red Only Example</figcaption></figure> ### Red-Gray This transformation is based on an experiment demonstrated by Edwin Land, founder of Polaroid, in 1959 [more info](http://t-a-y-l-o-r.com/land/). The experiment was based on the Retinex Theory of Color Vision. [This page](http://neuronresearch.net/vision/files/retinex.htm) has a nice explanation of the experiment and the Retinex theory. The transform is quite simple: perform a Grayscale transformation on the pixels in alternating rows and perform a Red Only transformation on the pixels in the other rows<sup><a href="#fn__2" name="fnt__2" id="fnt__2" class="fn_top">2)</a></sup>. <figure>[![Red-Gray](lab9redGray.png)](lab9redGray.png)<figcaption>Red-Gray Example</figcaption></figure> ### Filter Kernel Window When clicked, the **Show Filter** button reveals a second window and changes the text on the button to **Hide Filter**. When **Hide Filter** is clicked, the second window is hidden, and the text on the button changes back to **Show Filter**. Note: you may chose an alternate user experience for this but must be able to justify why an alternate approach is superior to this. <figure>[![User Interface](lab9kernel.png)](lab9kernel.png)<figcaption>Filter Kernel Window</figcaption></figure> The Filter Kernel window contains a 3 x 3 grid<sup><a href="#fn__3" name="fnt__3" id="fnt__3" class="fn_top">3)</a></sup> of filter weights. The user must be able to modify these weights manually or by clicking the Blur or Sharpen buttons. When clicked, the Blur or Sharpen button on the right change the filter weights as follows: <pre> Blur Sharpen 0 1 0 0 -1 0 1 5 1 -1 5 -1 0 1 0 0 -1 0 </pre> The **Apply** button applies the filter kernel to the image. At a minimum, the button need only apply the filter kernel if the sum of the filter kernel weights is a positive value. For example, if the user enters 0 for all the filter weights, applying the filter can produce unpredictable results. The weights in the array specifying the filter kernel should sum to 1. Since the blur filter kernel has values that sum to 9 (0+1+0+1+5+1+0+1+0), each value must be divided by 9. The following code applies the **Blur** filter kernel: ``` double[] kernel = { 0.0, 1.0/9, 0.0, 1.0/9, 5.0/9, 1.0/9, 0.0, 1.0/9, 0.0}; Image blurredImage = ImageUtil.convolve(originalImage, kernel); ``` The values for filter kernel for **Sharpen** sum to 1 (5-1-1-1-1) so the weights in the array here would be: ``` double[] kernel = { 0.0, -1.0, 0.0, -1.0, 5.0, -1.0, 0.0, -1.0, 0.0}; ``` <figure>[![Blur](lab9blur.png)](lab9blur.png)<figcaption>Blurred (applied 10 times) Example</figcaption></figure> <figure>[![Sharpen](lab9sharpen.png)](lab9sharpen.png)<figcaption>Sharpened (applied 1 time) Example</figcaption></figure> If the **Apply** button is pressed when an unsupported set of weights are entered in the window, an exception should be thrown (and be handled in a way consistent with the exception handling requirements listed below). ### Loading and Saving Images Your program must now support loading and saving `.bmsoe` files. Add the following two methods to your `ImageIO` class and modify your **`read()`** and `write()` methods to make use of these new methods when appropriate: * **`readBMSOE(Path path)`** &mdash; Reads an image file in `.bmsoe` format. * **`writeBMSOE(Image image, Path path)`** &mdash; Writes an image file in `.bmsoe` format. ### `.bmsoe` Binary Image Format The custom `.bmsoe` file format is a binary based file format for storing images. It is designed to be easy to load and save as well as produce smaller file sizes than the `.msoe` format. The file consists of a stream of binary data with the following header information: * The characters `B`, `M`, `S`, `O`, `E` each written as a `byte`. * The image width written as an `int` * The image height written as an `int` The remainder of the file contains the pixel data. Each pixel is stored as a single integer value with bits 24-31 representing the alpha channel, bits 16-23 representing the red channel, bits 8-15 representing the green channel, and 0-7 representing the blue channel. You may wish to add these two methods to your `ImageIO` class to facilitate converstion between `Color` and `int`: ``` private static Color intToColor(int color) { double red = ((color >> 16) & 0x000000FF)/255.0; double green = ((color >> 8) & 0x000000FF)/255.0; double blue = (color & 0x000000FF)/255.0; double alpha = ((color >> 24) & 0x000000FF)/255.0; return new Color(red, green, blue, alpha); } private static int colorToInt(Color color) { int red = ((int)(color.getRed()*255)) & 0x000000FF; int green = ((int)(color.getGreen()*255)) & 0x000000FF; int blue = ((int)(color.getBlue()*255)) & 0x000000FF; int alpha = ((int)(color.getOpacity()*255)) & 0x000000FF; return (alpha << 24) + (red << 16) + (green << 8) + blue; } ``` Your program should be able to load [this `.bmsoe` image](specs.bmsoe). #### Alpha Channel Complexities You may find that some images appear all black when saved in the `.bmsoe` format. This occurs when the original image does not have alpha channel information. In that case, the saved images will have values of 0 for all of the alpha channel values. You do not need to support saving such images. ### Exception Handling There are a number of situations that could cause your program to throw an exception. For example, if the file is not found, cannot be opened, or contains incorrectly formatted data, it is likely that an exception will be thrown. In these cases, the program should display an useful message and recover gracefully. ## Just For Fun There are many additional enhancements that could build on the required functionality. You are encouraged to enhance this application using your creativity. A number of enhancements are included below; however, you should not feel limited to these suggestions. * Add a menu to the main window to replace (or in addition to) the buttons * Apply a transform to only a selected region of the image * Support saving `.bmsoe` images when the input image does not have alpha channel values. * Display the original and transformed images side-by-side * Toggle between original and transformed images when mouse button is pressed on the image * Create a meme generator by adding styled text on the image * Tonal adjustment (e.g., adding a red hue to the image underneath the mouse) * Provide undo functionality * Implement additional transformations, e.g. * Brighten * Darken * Decrease color saturation * Increase color saturation * Apply additional filters like the **Edge** filter [see here](http://lodev.org/cgtutor/filtering.html) ## Lab Deliverables > See your professor's instructions for details on submission guidelines and due dates. > * Dr. Taylor's students: See below > * All other students should refer to Blackboard > >If you have any questions, consult your instructor. ## Acknowledgment This laboratory assignment was developed by [Dr. Chris Taylor](/taylor/). <div class="footnote"> <sup><a href="#fnt__1" id="fn__1" name="fn__1" class="fn_bot">1)</a></sup> These methods will likely be part of a separate controller class associated with the second stage. <br /> <sup><a href="#fnt__2" id="fn__2" name="fn__2" class="fn_bot">2)</a></sup> This is a slight simplification from the actual transformation. You may chose to implement the actual transformation, if you choose to (you'll need to look it up). Be sure to indicate with a comment if you choose to do this. <br /> <sup><a href="#fnt__3" id="fn__3" name="fn__3" class="fn_bot">3)</a></sup> You may choose to make a 5 x 5, 7 x 7, or 9 x 9 grid instead. <br /> </div>

Tuesday, 11-Feb-2020 04:16:41 CST