0% found this document useful (0 votes)
53 views2 pages

Homework 6 Pennstagram - CIS 1200

Homework description

Uploaded by

gianna summer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
53 views2 pages

Homework 6 Pennstagram - CIS 1200

Homework description

Uploaded by

gianna summer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 2

CIS 1200

Homework 6:
Pennstagram

In this homework, you’ll be completing a basic

photo manipulation application in Java!

Task 0: Set up your


project and browse the
files
Setup (Codio Users)

You do not have to download the files for the

homework assignment – the Codio box has been

configured with them for you.

Setup (IntelliJ Users)

Download IntelliJ, following the instructions in our

IntelliJ set-up guide.

Once it’s downloaded, set up your Java project,

following the instructions in the “Setting up

homework in IntelliJ” section of the guide linked

above. Make sure not to put any special

characters (other than hyphens and underscores)

or spaces in the full absolute path of the project.

You can download all the code files here, and

import them into a Java project. If you’re not sure

how to run files, tests, the style checker, etc.,

please check the setup guide linked above.

Project Overview
We’ve given you the GUI for this program and

have defined several super-fancy photo effects,

controlled by buttons on the right side of the

application (“1890s”, “Pin Hole”, etc.). However,

these effects don’t quite work yet, because they

depend on basic photo manipulation algorithms

that you must implement. (These basic

manipulations are controlled by the buttons on

the left side, provided for your testing

convenience.)

We’ve given you a few completed files to get

started:

PixelPicture.java , which manages the

reading and writing of image data.

GUI.java , the simple GUI for the program

(which you can run to help you test Parts 3

and 4).

ColorMap.java , a Map data structure

that helps build histograms.

ManipulateTest.java , a JUnit test file

for the image manipulations.

PointQueue.java , a data structure for

managing queues of ints (needed only for

the Kudos flood fill problem).

And you’ll have to finish off a few more:

Pixel.java , which is a point of color in

an image. You’ll have to finish a few

constructors ( Pixel() ), getRed() ,

getGreen() , getBlue() ,

getComponents() , distance() ,

toString() and sameRGB() .

MyPixelTest.java , which contains an

example test for Pixel . You will need to

add your own tests to ensure your code

works the way you want it to.

SimpleManipulations.java , a

collection of simpler image manipulations.

You’ll have to finish rotateCCW() ,

border() , invertColors() ,

grayScaleAverage() ,

scaleColors() , and alphaBlend() .

AdvancedManipulations.java , a

collection of image manipulations requiring

pre-processing of the image or

consideration of multiple pixels at once in

order to paint a single pixel. You’ll have to

finish adjustContrast() ,

reducePalette() , and blur() (and

flood() for Kudos!).

Effects.java , which implements super-

fancy photo effects, based on the basic

image manipulations (modified only for

Kudos).

ImageTest.java , a place to put your own

JUnit tests.

The JavaDocs for the classes you are working

with can be found here, and the FAQ for this

assignment can be found here.

Tip: Do not edit any of the completed

files (i.e. PixelPicture , GUI ,

PointQueue , ColorMap or

PictureTest ), and do not add any new

files. You’ll be submitting a ZIP archive

containing just the files (listed above)

that we are asking you to modify.

Task 1: Pixels
Your first task is to complete the Pixel class. A

pixel represents a color and is composed of three

ints, indicating amounts of red, green, and blue,

with values ranging from 0 to 255. Lower values

mean less color; higher values mean more.

To start off, you will need to think of how to store

the red, green, and blue values associated with

each Pixel object. Keep in mind that every new

Pixel created will need to store its own red, green,

and blue values. In addition, we want to make

Pixels immutable. That is, once we create a new

Pixel , there should be no way to modify its RGB

values.

Once you decide how to store the different


values, complete the two different constructors.

In order to create a new Pixel, one of its

constructors must be called: new

Pixel(255,255,255) represents white, new

Pixel(0,0,0) is black, and new

Pixel(0,255,0) represents green. If a is an

array containing the values {0, 0, 255} , then

new Pixel(a) constructs a blue pixel.

Note: Your Pixel class should maintain

the invariant that the three color

components are in the range 0 to 255. If

a Pixel constructor is passed values

outside of this range, they should be

clipped: negative numbers get clipped to

0; numbers greater than 255 should be

clipped to 255.

After finishing the constructors, complete the

following methods:

public int getRed()

public int getGreen()

public int getBlue()

public int[] getComponents()

public int distance(Pixel px)

public String toString()

public boolean sameRGB(Pixel

other)

Make sure that your implementation is fully

encapsulated, in the sense that it is not possible

to modify the internal representation of an object

except by calling methods from its class. For

example, if a client modifies an array obtained

from getComponents , the Pixel value should

not change. There are multiple ways to achieve

encapsulation, and you do not need to use

Arrays.copyOf() .

Pixel Testing

In MyPixelTest.java , write unit test cases for

the methods of the Pixel class.

Note: You must complete and test this

class before moving on. You will not be

able to complete the rest of the

assignment until Pixel is finished.

Pictures

This homework will require you to work with

bitmaps — two-dimensional arrays of color values

— which are a standard representation for

images.

Java offers a variety of classes for working with a

wide variety of different image formats. To

simplify your life, we’ve wrapped up the tricky

bits of this code in a class called

PixelPicture . The PixelPicture (and

Pixel ) classes provide all of the basic image

management you’ll need. Instead of working with

Java’s image processing libraries directly, you’ll

process the bitmaps provided by this class.

The PixelPicture class makes image data

available to you via the getBitmap method,

which returns a bitmap of Pixels corresponding to

the PixelPicture ’s contents. Note that, in this

application, bitmaps are indexed from top to

bottom and from left to right.

0 0 1 2

1 0 1 2

2 0 1 2

3 0 1 2

Left-to-right, top-to-bottom

pixel layout for a 4 x 3 bitmap

Each dotted box is an array;

each solid-bordered box is a

pixel

The numbers are the index in

the array

In the figure above, each dashed box represents

an array. The top-level array holds each of the

rows of the image, in top-to-bottom order. Each

row array holds the pixels of that row, in left-to-

right order.

This layout is convenient because it puts the

origin in the top-left corner and lets us visualize

the 2-d array as (x,y) coordinates.

Note that since we index first by row, we access

the array first by its y coordinate and secondarily

by its x coordinate. This means that a coordinate

at position (x,y) will appear in the array at

index bmp[y][x] . This is called row-major

layout.

Most tasks in this assignment involve taking a

PixelPicture p , getting its bitmap via

p.getBitmap() , manipulating the Pixels in that

bitmap in some way, and finally constructing a

new PixelPicture from the manipulated

bitmap using the appropriate PixelPicture

constructor.

Tip: Accessing the bitmap of a picture

with p.getBitmap() gives you a copy

of the entire 2-d array. You want to make

sure that you do this only once for each

image transformation. If you call

p.getBitmap() more frequently, such

as for every pixel in an image during

some pixel-by-pixel transformation, your

program will run very, very slowly.

Implementing the Photo


Manipulations

For the rest of the assignment, you will use

bitmaps and Pixels to implement some photo

manipulation algorithms. These algorithms are

described in detail in the sections below.

Make sure to test your functions in

ImageTest.java . If you are using Codio, follow

the instructions found there to run ImageTest

and the provided GUI through Codio. If you are

using IntelliJ, you can run the Gui or the tests by

right-clicking on the file and using the “Run As…”

menu item. Either way, the only buttons that will

work initially are “Load new image”, “Save image”,

“Undo”, “Quit”, and “RotateCW”. Note that the GUI

downloads its initial image from the internet, so

make sure that you are connected to the internet

when you run it.

Tip: Some of the algorithms you are

asked to implement during this

assignment, especially those found in

AdvancedManipulations.java , can

be implemented in a very inefficient

manner. We have timeouts in place that

will fail individual tests if they take too

long. If many of your tests are taking too

long, we will not be able to accept your

submission (because we can’t test it).

None of the algorithms we ask you to

implement should take more than a

second or two to run if they are

implemented properly.

Tip: Read over the whole assignment (on

this webpage) before starting to code.

You’ll also want to read over a few of the

source files first. In particular, the file

Effects.java demonstrates how the

basic manipulations can be used and put

together to form composite effects.

Reading this file will help you understand

how to use the static methods in

SimpleManipulations.java .

NOTE: For a lot of the assignment, you will be

updating individual pixel values. Many (but not all)

of the functions require you to divide or multiply

by doubles. Since the red, green, and blue

attributes of Pixels are ints, you’ll need to do a

little work to make sure they are updated with the

correct values to pass our tests.

Whenever you need to use doubles in

calculations: at the end of your calculations,

you must round using Math.round() then

cast to int. Like this:

double d = ... /* compute a

double */

int val = (int) Math.round(d);

/* convert it to an int */

Writing Tests

You can use the files MyPixelTest.java ,

ManipulateTest.java , and

ImageTest.java to test your code (only

MyPixelTest.java will count towards your

grade). ManipulateTest.java contains a

number of simple tests for the basic

manipulations, and ImageTest.java uses all of

the sample images from this page as the basis of

its tests. Neither of these files is sufficient to fully

exercise the functionality we’re asking you to

build, so you should also create additional test

cases to help understand, debug, and evaluate

the code you write.

For help on how to write JUnit tests for this

assignment, look at the provided tests in these

test files. The tests verify that your methods

return the correct PixelPicture objects by

comparing them to simple images constructed

from small two-dimensional arrays. The diff

method in the PixelPicture class is useful for

checking that two PixelPicture objects have

the same bitmaps.

The tests in ImageTest.java are based on the

picture files included in the images folder in

Codio. Note that these tests compare your

solution to ours exactly. Because of floating point

imprecision, your code may fail these tests but

still be visually correct. These tests will allow you

to see how close your solution is to ours.

Finally, beware of image compression effects

when comparing two images. Due to image

compression, if you save as a jpg or gif , the

pixels in the saved image on your hard drive will

be slightly altered compared to the

PixelPicture object (in memory) that is

returned from your manipulation methods. (In the

case of gif images, this is due to palette reduction

of the same kind as the one you will implement

yourself!) Therefore, you always want to compare

only uncompressed saved images to our sample

images.

Task 2: Rotation
Here, your task is to change the orientation of an

image.

In SimpleManipulations.java , there are two

rotation functions: rotateCW() and

rotateCCW() , which rotate an image clockwise

and counter-clockwise, respectively. Each

function rotates the image 90° in the given

direction.

We have implemented rotateCW for you; you

will implement rotateCCW . Implementing this

command will require you to process bitmaps. To

understand the two rotations, consider the

following bitmap, where we’ve numbered each

pixel with its coordinates:

(0, 0) (0, 1) (0, 2) (0, 3) ...

(1, 0) (1, 1) (1, 2) (1, 3) ...

(2, 0) (2, 1) (2, 2) (2, 3) ...

(3, 0) (3, 1) (3, 2) (3, 3) ...

.... .... .... .... ...

Original array, pixels numbered

with coordinates

(1, 0) (0, 0) .... .... .... .... ...

(1,1) (0, 1) (0, 2) (1, 2) (2, 2) (3, 2) ...

(1, 2) (0, 2) (0, 1) (1, 1) (2, 1) (3, 1) ...

.... .... (0, 0) (1, 0) (2, 0) (3, 0) ...

e rotation Counter-clockwise rotation

Your job is to implement this “renumbering”,

copying pixels from their old coordinates to their

new coordinates.

For this implementation, you should fill in the

definition of the static method rotateCCW in

SimpleManipulations.java . Do not merely

call rotateCW three times.

Task 3: Border
In this task, you will create a new image that adds

a border to an existing image.

The first step is to implement the static method

border in SimpleManipulations.java . As

with rotation, this operation is performed by

copying pixels from their old locations to new

coordinates. However, this time the new image

will be larger than the supplied picture because of

the added border.

The default image with a black 10-pixel border

Task 4: Simple Pixel


Transformations
In this task, you will perform some image

manipulations that require manipulation of Pixel

RGB values.

For this task, you will need to implement several

basic pixel transformations from

SimpleManipulations.java . These

manipulations are simple in that they only require

you to consider each pixel independently; you


don’t have to pre-process the image or consider

neighboring pixels. As an example of this kind of

transformation, we have given you an

implementation of grayScaleLuminosity . You

will implement the following transformations:

Color Inversion: invertColors()

Grayscaling Via Averaging:

grayScaleAverage()

Color Scaling: scaleColors() with (1.0,

0.5, 0.5)

Color inversion takes each pixel and chooses the

“opposite” color of the current one — that is, the

one directly across the color wheel.

Grayscaling algorithms transform images from

colorful ones to shades of gray; there are several

methods of doing this, each of which works best

in different situations. We have given you one

algorithm and you will be implementing another.

An explanation of the specific algorithms can be

found in the relevant files.

Color scaling multiplies the color components of

each pixel by given scaling values. For example,

with the parameters (1.0, 0.5, 0.5) , the red

components will be unchanged, but the blue and

green parameters will be converted to half their

value. This has the effect of giving the picture a

strong red tint and decreasing the overall

brightness.

The transformations you will need to implement

all require decomposing each pixel into its three

color components: red, green, and blue. Take a

look at the included Pixel class for help with this.

Tip: Again, if you have a double value d ,

you can convert it to an int by rounding

and then casting, using the code (int)

Math.round(d) .

Task 5: Alpha-Blend
In this task you will blend the pixels of another

image into the current image.

The next picture manipulation, alphaBlend()

in SimpleManipulations.java , actually

takes two pictures and combines them pixel-by-

pixel to produce a new image. Both pictures must

be of the same dimensions (if they are not, just

return the picture provided as the first argument).

The algorithm goes through the two pictures

computing the weighted average of each of the

corresponding pixels in the two images.

This shows the default image blended (alpha =

0.3) with a grayscale (average) version of itself.

This blend reduces the color saturation of the

image by incorporating some gray into each pixel.

Task 6: Advanced Pixel


Transformations
For the next operations, you’ll work in

AdvancedManipulations.java . Each of

these transformations requires you to compute

additional information about the image before it

can be executed.

Contrast

First, you’ll change the contrast of a picture by

implementing the method adjustContrast()

in AdvancedManipulations.java .

Your job is to change the intensity of the colors in

the picture, following this simple method of

changing contrast:

1. Find the average color intensity of the

picture.

1. Sum the values of all the color

components in all of the pixels (for

each pixel add each of its color

components to the total sum).

2. Divide the total sum by the number of

pixels times 3 (the number of

components). This is the average

color intensity.

2. Subtract the average color intensity from

each color component of each pixel,

resulting in a “normalized” color

component. (This will make the average

color intensity for the entire image zero.)

3. Scale each normalized color component by

multiplying them by the contrast “multiplier”

parameter. Note that the multiplier is a

double (a decimal value like 1.2 or 0.6) and

normalized color values are integers.

These scaled and normalized color

components may be negative or larger than

255, but that is OK! (See below.)

4. Add the original average color intensity

back to the scaled, normalized components

to create a new pixel. Note that the Pixel

class will handle clipping of the resulting

components to the range 0-255, as desired.

The default image with contrast multiplier of 2.0.

Tip: There are a few steps where

truncation of decimals can cause

rounding errors. Any time you compute a

floating-point quantity, you should use

Math.round() and type casting to

properly round it to an int.

Reduced color palette

Next, you’ll reduce a picture to its most common

colors by implementing the method

reducePalette() .

You will need to make use of the ColorMap

class to generate a map from Pixels of a certain

color to the frequency with which identically-

colored pixels appear in the image. Once you

have generated your ColorMap , select your

palette by retrieving the pixels whose color

appears in the picture with the highest frequency.

Then, change each pixel in the picture to one with

the closest matching color from your palette. Use

the distance method in the Pixel class to figure

out the difference between two pixels.

Algorithms like this are widely used in image

compression. GIFs in particular compress the

palette to no more than 255 colors. The variant

we have implemented here is a weak one since it

only counts color frequency by exact match.

Advanced palette reduction algorithms (known as

“indexing” algorithms) calculate color regions and

distribute the palette over the regions. For

example, if our picture had a lot of shades of blue

and a little bit of red, our algorithm would likely

choose a palette of all blue colors. An advanced

algorithm would recognize that blues look similar

and distribute the palette so that it would be

possible to display red as well.

This shows the default image with a palette

reduced to 512 colors

Task 7: Blur
Finally, you will write code to make an image

appear blurry.

The blur() method in

AdvancedManipulations.java takes one

argument, a radius. There are different blurring

algorithms; we’ll implement the simplest, called a

box blur. Box blurring works by averaging a box-

shaped neighborhood around a pixel. Be sure to

include this pixel in each average. The size of the

box is configurable by setting the radius, half the

length of a side of the box.

In the simplest case — a radius of 1 — blurring

just takes the average around a pixel. Here, to

blur around the pixel at (1,1) with radius 1, we take

the average value of the pixels of its

neighborhood: (0,0) through (2,2), including

(1,1).

(0,0) (1,0) (2,0) (3,0) ...

(0,1) (1,1) (2,1) (3,1) ...

(0,2) (1,2) (2,2) (3,2) ...

(0,3) (1,3) (2,3) (3,3) ...

.... .... .... .... ...

Box blur neighborhood around

(1,1), radius 1

Each component should be

averaged separately.

This algorithm must be careful of corner cases.

When blurring (0,0) with radius 1, we only need to

consider the top-left corner of the images, pixels

(0,0) through (1,1), and so we need to divide by 4

at the end, not 9. You’ll have to be careful to only

access bitmaps inside of their bounds. You can

assume that you will not be given a radius less

than 1.

This shows the default image blurred with radius

2.

Warning: There are very inefficient ways

to implement this algorithm. If your

solution takes more than 3 seconds to

run when you test it yourself, then it is

likely incorrect and will time out upon

submission!

A Custom Effect (Kudos


Problem I)
At this point, you have implemented all of the

basic transformations. The effects on the right

side of the GUI should all work, except for the last

one. For this effect, you have the opportunity to

design your own filter. Take a look at how the

effects in Effects.java are implemented and

do something cool in the method custom . This

part of the assignment is worth no points, but we

want to see what you come up with. If you create

a particularly nice effect and want to share with

all of us, post the output image (and the source

code if you wish) in a private Ed post.

Flood fill (Kudos


Problem II)
The last problem is a challenge. It is here for

additional practice but again worth no points on

the assignment.

The flood command is short for “flood fill,”

which is the familiar “paint bucket” operation in

graphics programs. In a paint program, the user

clicks on a point in the image. Every neighboring,

similarly colored point is then “flooded” with the

color the user selected.

Suppose we want to flood color at (x,y) . The

simplest way to do flood fill is as follows.

1. Let target be the color at (x,y) .

2. Create a set of points Q containing just the

point (x,y) .

3. Take the first point p out of Q .

4. Set the color at p to color .

5. For each of p ’s non-diagonal neighbors—

up, down, left, and right — check to see if

they have the color target . If they do, add

them to Q .

6. If Q is empty, stop. Otherwise, go to step 3.

(Some questions you should ask yourself (but not

the TAs!): What happens when target and

color are the same? How can you speed up this

naïve algorithm?)

For Q , you should use the provided

PointQueue class. It works very much like the

queues we implemented in OCaml.

This shows the default image after applying the

operations blur(16) , contrast(16) , and

flood .

Submission
If you are using Codio

Submit hw06-submit.zip containing only:

Pixel.java

MyPixelTest.java

SimpleManipulations.java

AdvancedManipulations.java

ImageTest.java

This zip file will be automatically created with the

correct files if you use the “Zip for Submission”

command in Codio.

If you are using IntelliJ

Gradescope allows you to easily drag-and-drop

files into it, or you can click “Browse” in

Gradescope to open up a file browser on your

computer to select files. Upload only the files

listed above.

Grading
Here’s the grade breakdown:

1. Pixel: 13 points

2. Simple image manipulations ( rotateCCW ,

border ): 10 points total

3. Simple pixel transformations (color

inversion, grayscale average, color scaling):

12 points total

4. Alpha-Blend: 8 points total

5. Contrast: 12 points total

6. ReducePalette: 20 points total

7. Blur: 20 points total

8. Flood fill: 0 (kudos only)

9. Style: 5 auto-graded points total

You have five free submissions, after which there

will be a five-point penalty for each extra

submission.

Task 0: Set up your project and browse the files

Setup (Codio Users)

Setup (IntelliJ Users)

Project Overview

Task 1: Pixels

Pixel Testing

Pictures

Implementing the Photo Manipulations

Writing Tests

Task 2: Rotation

Task 3: Border

Task 4: Simple Pixel Transformations

Task 5: Alpha-Blend

Task 6: Advanced Pixel Transformations

Contrast

Reduced color palette

Task 7: Blur

A Custom Effect (Kudos Problem I)

Flood fill (Kudos Problem II)

Submission

If you are using Codio

If you are using IntelliJ

Grading

You might also like