CMSC 150 Lab 8, Part II: Little PhotoShop of Horrors, Part Deux 10 Nov 2015

Similar documents
You Can Make a Difference! Due April 11/12 (Implementation plans due in class on 4/9)

To gain experience using GUI components and listeners.

Lab 7 Digital Image Processing Due: 23:00 hours, Mon. Nov. 2

Lab 5 Classy Chat. XMPP Client Implementation --- Part 2 Due Oct. 19 at 11PM

To gain experience using arrays, manipulating image data, and using inheritance.

To gain experience using arrays, manipulating image data, and using inheritance.

Lab 3. A Multi-Message Reader

2IS45 Programming

A set, collection, group, or configuration containing members regarded as having certain attributes or traits in common.

Lab 5 TOC to Me. Class Structure. IM Client Implementation --- Part 2 Due: 23:00 Monday 13 October. CS 150, Profs. Lawson and Szajda Fall 2008

Name: Checked: Learn about listeners, events, and simple animation for interactive graphical user interfaces.

Programming for Non-Programmers

Java Help Files. by Peter Lavin. May 22, 2004

CS 201 Advanced Object-Oriented Programming Lab 6 - Sudoku, Part 2 Due: March 10/11, 11:30 PM

Java Programming Lecture 6

CS Programming Exercise:

CS Problem Solving and Object-Oriented Programming

Outline. Topic 9: Swing. GUIs Up to now: line-by-line programs: computer displays text user types text AWT. A. Basics

Topic 9: Swing. Swing is a BIG library Goal: cover basics give you concepts & tools for learning more

Topic 9: Swing. Why are we studying Swing? GUIs Up to now: line-by-line programs: computer displays text user types text. Outline. 1. Useful & fun!

MCS 2514 Fall 2012 Programming Assignment 3 Image Processing Pointers, Class & Dynamic Data Due: Nov 25, 11:59 pm.

CS 134 Midterm Fall 2006

Module 10A Lecture - 20 What is a function? Why use functions Example: power (base, n)

Graphical User Interfaces. Comp 152

Lab 5 TOC to Me. IM Client Implementation --- Part 2

First Name: AITI 2004: Exam 2 July 19, 2004

PROGRAMMING LANGUAGE 2

Window Interfaces Using Swing Objects

CS 201 Advanced Object-Oriented Programming Lab 5 - Sudoku, Part 1 Due: March 3/4, 11:30 PM

AP CS Unit 11: Graphics and Events

Command-Line Applications. GUI Libraries GUI-related classes are defined primarily in the java.awt and the javax.swing packages.

Problem 1: Building and testing your own linked indexed list

+! Today. Lecture 3: ArrayList & Standard Java Graphics 1/26/14! n Reading. n Objectives. n Reminders. n Standard Java Graphics (on course webpage)

Introduction This assignment will ask that you write a simple graphical user interface (GUI).

Inheritance and Interfaces

Java Object Oriented Design. CSC207 Fall 2014

Before We Begin. Introduction to Computer Use II. Overview (1): Winter 2006 (Section M) CSE 1530 Winter Bill Kapralos.

Java classes cannot extend multiple superclasses (unlike Python) but classes can implement multiple interfaces.

+ Inheritance. Sometimes we need to create new more specialized types that are similar to types we have already created.

12/22/11. Java How to Program, 9/e. public must be stored in a file that has the same name as the class and ends with the.java file-name extension.

Review sheet for Final Exam (List of objectives for this course)

Comp Assignment 4: Commands and Graphics

Control Flow: Overview CSE3461. An Example of Sequential Control. Control Flow: Revisited. Control Flow Paradigms: Reacting to the User

Chapter 4 Defining Classes I

CS 209 Spring, 2006 Lab 8: GUI Development Instructor: J.G. Neal

CS 170 Java Programming 1. Week 9: Learning about Loops

Programs as Models. Procedural Paradigm. Class Methods. CS256 Computer Science I Kevin Sahr, PhD. Lecture 11: Objects

Design Patterns: State, Bridge, Visitor

Initial Coding Guidelines

Expressions and Casting

Introduction to the JAVA UI classes Advanced HCI IAT351

CSC 160 LAB 8-1 DIGITAL PICTURE FRAME. 1. Introduction

First Name: AITI 2004: Exam 2 July 19, 2004

Due: 9 February 2017 at 1159pm (2359, Pacific Standard Time)

CS 051 Homework Laboratory #2

Survey #2. Programming Assignment 3. University of British Columbia CPSC 111, Intro to Computation Alan J. Hu. Readings

Project #1 Seam Carving

PROGRAMMING DESIGN USING JAVA (ITT 303) Unit 7

Graphics User Defined Forms, Part I

Educational Fusion. Implementing a Production Quality User Interface With JFC

CSE 142 Su 04 Computer Programming 1 - Java. Objects

Software Design and Analysis for Engineers

Assoc. Prof. Dr. Marenglen Biba. (C) 2010 Pearson Education, Inc. All rights reserved.

Queen s University Faculty of Arts and Science School of Computing CISC 124 Final Examination December 2004 Instructor: M. Lamb

Slide 1 CS 170 Java Programming 1 Multidimensional Arrays Duration: 00:00:39 Advance mode: Auto

Lab & Assignment 1. Lecture 3: ArrayList & Standard Java Graphics. Random Number Generator. Read Lab & Assignment Before Lab Wednesday!

CSCI 136 Programming Exam #2 Fundamentals of Computer Science II Spring 2012

Page 1 of 7. public class EmployeeAryAppletEx extends JApplet

Window Interfaces Using Swing. Chapter 12

Parts of a Contract. Contract Example. Interface as a Contract. Wednesday, January 30, 13. Postcondition. Preconditions.

Stego my Ego: In which our hero hides information in waffles...

Topics. Java arrays. Definition. Data Structures and Information Systems Part 1: Data Structures. Lecture 3: Arrays (1)

Guessing Game with Objects

3.Constructors and Destructors. Develop cpp program to implement constructor and destructor.

Equality for Abstract Data Types

Window Interfaces Using Swing Objects

Chapter 2, Part I Introduction to C Programming

CS 315 Software Design Homework 1 First Sip of Java Due: Sept. 10, 11:30 PM

Prototyping a Swing Interface with the Netbeans IDE GUI Editor

OBJECT ORİENTATİON ENCAPSULATİON

(Refer Slide Time: 01.26)

CS Fall Homework 5 p. 1. CS Homework 5

Lesson 6A Loops. By John B. Owen All rights reserved 2011, revised 2014

Java Swing. Recitation 11/(20,21)/2008. CS 180 Department of Computer Science, Purdue University

To gain experience using recursion and recursive data structures.

EECS 1001 and EECS 1030M, lab 01 conflict

CE221 Programming in C++ Part 1 Introduction

Part I: Learn Common Graphics Components

Starting Out with Java: From Control Structures Through Objects Sixth Edition

Class 9: Static Methods and Data Members

CPS221 Lecture: Threads

CSC207 Week 4. Larry Zhang

Frames, GUI and events. Introduction to Swing Structure of Frame based applications Graphical User Interface (GUI) Events and event handling

CS415 Human Computer Interaction

APCS Semester #1 Final Exam Practice Problems

CS 11 java track: lecture 3

Graphical User Interface (GUI)

CS18000: Problem Solving And Object-Oriented Programming

Intensity Transformations and Spatial Filtering

CS-140 Fall 2017 Test 1 Version Practice Practice for Nov. 20, Name:

Transcription:

CMSC 150 Lab 8, Part II: Little PhotoShop of Horrors, Part Deux 10 Nov 2015 By now you should have completed the Open/Save/Quit portion of the menu options. Today we are going to finish implementing the three image processing algorithms discussed during lecture over the last few days: invert: essentially make a photographic-negative version of an image; box blur: make a blurred version of the image by averaging pixel values in a box neighborhood; and contrast stretch: stretch the distribution of gray (or color) levels to add more contrast to an image (this part is for extra credit). During this implementation, the only new concept will be a gentle introduction to inheritance in Java. 1) Invert: (Though this was discussed in the Lab 8, Part I handout, I have included it here for completeness.) First we want to tackle the Invert option under the Image menu. We want this to be an in-place operation, meaning as soon as Invert is selected, the photographic negative of the image will be displayed in the main window as depicted in the example below. As discussed recently, to accomplish the invert operation, you simply walk through the image s array pixelby-pixel (use doubly-nested for loops refer to last week s lab if you need a refresher) changing the pixel value to 255 minus its current value. Do this for each of the three color bands red, green, and blue. (This will cause the invert to work for both color and grayscale images. For a grayscale image, at a given pixel the amounts of red, green, and blue are the same, corresponding to a gray level between black and white based on the [identical] intensities of red, green, and blue at that pixel.) Set the SImage instance variable to reference the new inverted image. (This way, if you choose Invert again, the image displayed will be just like the original.) 1

Test your Invert on both grayscale and color images (samples provided on the course Web page). 2) Box Blur: Now we want to tackle the Box Blur option. This will not be an in-place operation; rather, we want to open a separate preview window in which we can experiment with the blurring before accepting the result to be displayed in the main window. Before diving in, let s think ahead. Not only will we want a separate preview window for the box blur, we will also want one for the contrast stretch. Now is a good time to think about what functionality the two separate previews will share, and make good design decisions to facilitate the sharing. Shown below on the left is an example of what the box blur preview window might look like; an example of the contrast stretch preview window is shown below right. The similarities between the two windows are: both will contain two JButton objects for accepting or canceling the blurred result; both will contain a JLabel to display an SImage; both will contain a JSlider for experimenting with an input parameter to the algorithm, and an associated JTextField for displaying and/or inputting the JSlider value; The differences between the two windows are: different labels ( Blur Radius: or Contrast: ) at the lower left; different min, max, and initial values for the sliders; (obviously) different algorithms to execute when the slider values change. It would be nice if we had a general preview window class that implemented only the similarities of the two previews. We could then write two separate classes (one for the box blur preview and one for the contrast stretch preview) each would inherit all the properties and functionality from the general class, so that the only thing to implement in each class would be the functionality unique to that specific preview. This basic idea is known as inheritance. One mechanism that Java provides for allowing inheritance is to define a class as abstract, similar to the following: public abstract class PreviewWindow extends GUIManager 2

Notice the keyword abstract in the class declaration. An abstract class is a class for which you cannot directly create instances. Instead, you must write a separate (non-abstract) class that extends the abstract class, and then you can create instances of the new class. Furthermore, in the abstract class, Java allows you to declare abstract methods similar to the following: public abstract void sliderchanged(); Notice the keyword abstract in the method declaration. Also notice that the declaration ends immediately with a semicolon there is no body defined in the abstract class for this method. Java will then force any class that extends this abstract class to provide a definition (i.e., a body) for the abstract method. On the course Web page, we have provided an abstract class named PreviewWindow. Download the file PreviewWindow.java and add it as a new class from the downloaded file. Open the class in the editor window, and you will see the abstract class declaration as well as two abstract method declarations (no bodies). This abstract class implements the commonalities of the two different preview windows, namely it creates a new window with a BorderLayout; stores as an instance variable the SImage provided as input to the constructor; creates a new JLabel with the SImage as its icon and places the label in the center of the content pane; creates two JButton objects for accepting or canceling the blurring process and places the buttons in the top of the content pane and implements the appropriate buttonclicked() method. (Note that the details for what occurs when the accept button is clicked are omitted. You will add these details later.) This class does not construct the JSlider portion because the values will be different in each preview window these differences will be handled when we create new (extending) classes for each preview. Now in the BlueJ main window, right-click on the PreviewWindow class and try to create a new instance of the class. BlueJ will not let you you get no new PreviewWindow() option even though the class contains a constructor. This is because the class is abstract, and (as stated above) instances of an abstract class cannot be created. Let s start by creating a new class named BoxBlurPreviewWindow that extends PreviewWindow. Modify the default class code provided by BlueJ until the definition for you class looks like the following: import javax.swing.*; import squint.*; public class BoxBlurPreviewWindow extends PreviewWindow public BoxBlurPreviewWindow( SImage inputimage ) Now try to compile the BoxBlurPreviewWindow class. You will receive an error (similar to one you saw at the bottom of page 2 yesterday) saying something about does not override abstract method.... Recall above we said that Java will force any class extending an abstract class to provide definitions for all abstract methods. Look again at the bottom of the PreviewWindow class the two methods sliderchanged() and textentered() are declared as abstract, and so we must implement these two methods in our new class. For now, don t worry about the details of these two methods (we will address them below), but just put skeleton definitions in your new class, leaving out the abstract keyword: 3

public void sliderchanged() public void textentered() Now compile the class again. You no longer get an error about does not override defining our new non-abstract methods fixed that problem. However, now you get an error that Java cannot find constructor PreviewWindow(). From this, it should be clear that Java knows our new class extends PreviewWindow and so in our own constructor is trying to automatically call the default constructor for PreviewWindow (i.e., the constructor that accepts no arguments). Look at PreviewWindow in the editor there is no default constructor, but rather a constructor that expects an SImage as an argument. Rather than have Java try to automatically call a constructor for PreviewWindow, we need to explicitly tell Java to invoke the constructor expecting an SImage. We do this by including the following as the first line of our constructor: super( inputimage ); In the constructor of a class that extends another (parent) class, the method call super() invokes the constructor in that parent class. Because the constructor in our parent class PreviewWindow expects an SImage (which it stores as an instance variable), we pass in the SImage parameter passed to our own constructor. Now compile the BoxBlurPreviewWindow class again it should compile successfully. In the main BlueJ window, right-click on the class. Notice that, unlike for the PreviewWindow, you get an option for new BoxBlurPreviewWindow(). This new class is not abstract, but rather extends the abstract class, and so we can create instances of it. (Don t create an instance at this point we just wanted you to see that you can.) Let s now implement the details for our BoxBlurPreviewWindow class. Start by declaring four private final instance variables: one each for the minimum, maximum, and initial slider values (0, 10, and 3, respectively), and one for the text field width (here, 1). After the super call in your constructor, try constructing the JSlider inherited from PreviewWindow. The name of the instance variable in PreviewWindow is slider, so we should be able to reference it directly in our own constructor (without declaring a JSlider in this class!), similar to the following: slider = new JSlider( SLIDER_MIN, SLIDER_MAX, SLIDER_INITIAL); Add this code and try compiling the class. You will receive an error that slider has private access in PreviewWindow. Look back at the PreviewWindow class slider is indeed declared as private. You are beginning to see the implications of declaring things as public or private. We can t declare the variable as private because an inheriting class cannot access the variable. We shouldn t declare the variable as public because any class would be able to access (and modify!) the variable. The keyword we want to use here is protected this will allow inheriting classes direct access to the variable while prohibiting direct access by all other classes. So in PreviewWindow, change the instance variables from private to protected. BoxBlurPreviewWindow again no errors this time. Now compile Finish the appropriate code in your constructor to make your box blur preview window look like that on page 2. Create a new local JPanel, adding the inherited slider, new (local) JLabel s, and 4

the inherited text field (you still need to construct it) to the panel, and then adding the panel to the BorderLayout.SOUTH part of the content pane. (You will need to import java.awt.* for the BorderLayout.) Finally, invoke this.sliderchanged() at the end of your constructor. Test your class directly in the main BlueJ window, passing null as the argument to your constructor. No image will be displayed, but the buttons, labels, slider, and text field should look similar to the figures shown on page 2. Test the cancel button to make sure the window goes away. (The accept button will do nothing at this point.) Construct a skeleton method for the box blur algorithm: public void boxblur() Finish the code for the sliderchanged() and textentered() methods. When the slider is changed, you should set the text in your text field to be the current value of the slider, and then call your boxblur() method (which for now does nothing). When text is entered in the text field, you should set the value of the slider to the value entered in the text field (refer to the Java API), have the text field request focus and select all, and then call your boxblur() method. To set the value of the slider, you will need to convert the text field entry from String to an int. To do so, use Integer.parseInt( textfield.gettext() ). Test your BoxBlurPreviewWindow directly in the BlueJ main window (passing null to the constructor). If you move the slider, the text field should update; if you enter a value into the text field, the slider should update. Now let s take care of the actual box blur algorithm. Again, think ahead we want this blur algorithm to work on either grayscale or color images. We know from above that if we apply an algorithm to each of the red, green, and blue arrays, the algorithm will do the right thing regardless if the image is grayscale or color. So, within our boxblur() method, we want to call a separate method, passing in exactly one of the red/green/blue arrays, that will do the grunt work of blurring the given array. To do this, first create a new private method named boxblurarray(). The method will accept three arguments: a 2D integer array to be blurred, the width of that array, and the height of that array. The method will return a 2D integer array. As discussed in lecture yesterday, to accomplish a box blur you will need two copies of the image s array: the original and (what will be) the blurred copy. The original array will be provided as a parameter. You will need to construct the blurred copy as a new (local) 2D integer array of the same width and height as the provided original array. Then, you need to walk through the image s original array pixel-by-pixel (again, use double-nested for loops). For each pixel, you want to compute the average of that pixel and its eight nearest neighbors (i.e., all the pixels in a 3 3 box centered on that pixel). This average will then become the corresponding value in the blurred copy array. Recall from class that the code necessary to compute the average for a pixel at (row, col) is similar to the following: int sumofpixels = 0; int pixelsinbox = 0; for ( int boxrow = row - 1; boxrow <= row + 1; boxrow++ ) for ( int boxcol = col - 1; boxcol <= col + 1; boxcol++ ) 5

sumofpixels = sumofpixels + originalarray[boxcol][boxrow]; pixelsinbox++; blurredarray[col][row] = (int) (sumofpixels / (double) pixelsinbox); Finally, return the blurred array. There is one problem with the code above. The line that computes the sum of pixels will cause an index-out-of-bounds exception whenever the box is not completely inside the image. (Think about what happens when row and col are both 0. Then boxrow and boxcol both start at -1, which are not valid indices into the array.) This can be fixed by wrapping pixels outside the array back to the other side e.g., the pixel at row 0 column -1 should wrap back to be the pixel at row 0 column (width - 1). This is accomplished using some simple modular arithmetic. Just add the image width (height) to boxcol (boxrow) to ensure a positive result and then take the remainder when dividing by the width (height). In other words, instead of the above you should add to sumpixels the following: originalarray[(boxcol + width) % width][(boxrow + height) % height] Now back in the boxblur() method, create local copies of each of the red/green/blue pixel arrays (using the inherited originalimage instance variable), and overwrite each of those arrays in turn by the blurred array returned from a call to boxblurarray (passing in the corresponding color array. Then overwrite the inherited instance variable currentimage by constructing a new SImage using the three blurred color arrays and set the icon of the (inherited) JLabel to be this current image. In order to test your algorithm, add to the actionperformed method in your PhotoShop class the appropriate code to construct a new BoxBlurPreviewWindow instance. Then compile, create a new instance of PhotoShop, and test your program using various images. (At this point, the slider will not affect the blurring for now you should see only slight blurring of the images you load.) There are still two issues we need to address with box blurring: 1. making the slider affect the blurring; 2. making the accept button change the image in the main window. To address the first issue, change the for loops that compute the walk through the box. We initially assumed that the radius of the box was 1, i.e., we computed the average by considering pixels only within 1 of the center pixel. Instead, we want the radius of the box to be determined by the current slider value. Change the for loops so that the average considers all pixels within radius of the center pixel, where radius is the current value of the slider. To address the second issue, we need a way to update the SImage back in the main PhotoShop window. One way to do this would be to include in our PreviewWindow class a reference back to the PhotoShop class, construct a new method in PreviewWindow by which we can update its displayed image, and then invoke that method if the user presses the accept button. To implement this approach, do the following: in the PreviewWindow class, add a new private instance variable of type PhotoShop; change the constructor for PreviewWindow to accept a parameter of type PhotoShop, and in the constructor set the new instance variable to this parameter; in the BoxBlurPreviewWindow class, change the constructor to also accept a PhotoShop parameter (matching the constructor for PreviewWindow), and in the constructor change the call to super() to pass this parameter to the PreviewWindow constructor; 6

in the PhotoShop class, create a new public method named updateimage which accepts an SImage as a parameter, updates the SImage instance variable in that class using the parameter, and then sets the icon of the JLabel instance variable; in the buttonclicked() method in the PreviewWindow class, within the body of the if statement corresponding to the accept button being pressed, invoke the updateimage() method you created above (you will use the PhotoShop instance variable you created in the first step) passing in the currentimage instance variable (i.e., the currently held blurred version of the image); in the actionperformed() method in the PhotoShop class, update the statement that created a new instance of BoxBlurPreviewWindow to pass to its constructor a reference to the current PhotoShop object use the keyword this. Carefully test your program to make sure you can use the slider to change the blurred image in the preview window, and test to ensure that the accept and cancel buttons do the right thing. Contrast Stretch: (Extra Credit) Creating the contrast stretch preview window will be much easier. Create a new class named ContrastStretchPreviewWindow, delete the default BlueJ code, copy and paste the code from BoxBlurPreviewWindow, and change all occurrences of BoxBlur and boxblur to ContrastStretch and contraststretch respectively. Change the text for the slider label to match that shown on page 2, and set the min, max, and initial value constants to 0, 255, and 128 respectively. Now all we need to do is change the code in the method contraststretcharray() to do the right thing. Recall from lecture yesterday that the approach here is to construct a histogram of the levels currently in the image, and then stretch those levels appropriately. We will stretch the levels based on the amount of contrast selected by the user 0 being no contrast (all gray); 255 being the most contrast possible under our approach. 1. First, create a histogram of the pixel values. There are 256 possible values, so create a 1D array of 256 possible values: int[] histogram = new int[256]; 2. Now walk through the original image s pixel array (use double-nested for loops) and update the histogram by adding one to the location in the histogram associated with the level of the current pixel, using a statement like the following: histogram[ pixels[col][row] ]++; 3. Then construct a cumulative histogram. This involves creating a new 1D array of 256 possible values and setting each element in the new array to be the sum of all the histogram values up to that point, e.g.,: int[] cumulative = new int[256]; cumulative[0] = histogram[0]; for ( int i = 1; i < 256; i++ ) // cumulative value at point i is previous cumulative // plus the current histogram value cumulative[i] = cumulative[i - 1] + histogram[i]; 7

4. Now construct a stretched cumulative histogram. This involves creating a new 1D array of 256 possible values. Then walk through the entire array and set each location using an equation similar to the following: stretched[i] = (int) Math.round( (cumulative[i] / (double)(width*height) * slidervalue) + ((255 - slidervalue) / 2.0) ); 5. Finally, now step through the 2D pixel array (use doubly-nested for loops), setting each pixel s level according to the new stretched level stored in the stretched array, e.g., pixels[col][row] = stretched[ pixels[col][row] ]; and then return the updated pixel array. Test your implementation carefully using various images. Submission: Submit your lab in the usual process: send an email, with your zipped Lab 8 file as an attachment, to the appropriate email address for Lab 8. 8