{ "cells": [ { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "9edd6088-4fd1-4d02-84dc-86a8e1ad6630" } }, "source": [ "# [CptS 111 Introduction to Algorithmic Problem Solving](https://github.com/gsprint23/cpts111)\n", "[Washington State University](https://wsu.edu)\n", "\n", "[Gina Sprint](http://eecs.wsu.edu/~gsprint/)\n", "## PA6 Lists (125 pts)\n", "\n", "### Learner Objectives\n", "At the conclusion of this programming assignment, participants should be able to:\n", "* Initialize, manipulate, and display lists.\n", "* Initialize, manipulate, and display tuples.\n", "* Work with 2-dimensional lists.\n", "\n", "### Prerequisites\n", "Before starting this programming assignment, participants should be able to:\n", "* Apply iteration techniques\n", "* Perform file I/O\n", "* Apply basic string concepts and use string methods\n", "\n", "### Acknowledgments\n", "Content used in this assignment is based upon information in the following sources:\n", "* [Adam Carter's](http://www2.humboldt.edu/computerscience/carter.html) CptS 111 labs\n", "* [Stanford's Image Editor Assignment](http://nifty.stanford.edu/2012/guerin-image-editor/image_editor.html)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true, "nbpresent": { "id": "1db74d81-8706-4233-8440-7eed36f235e4" } }, "source": [ "### Overview and Requirements\n", "Write a program to modify images (image_modifier.py). The image format we are going to use is [PPM](http://netpbm.sourceforge.net/doc/ppm.html). Don't worry if you are not familiar with PPM images. This document will tell you everything you need to know!" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "ce9bbdc8-41a1-4177-a347-875b194ee4ad" } }, "source": [ "#### PPM Image Format\n", "The PPM image format is encoded in human-readable plain text. A PPM image has two main parts:\n", "1. Header\n", "1. Body\n", "\n", "##### PPM Header\n", "The header is always the top three uncommented lines in the file:\n", "```\n", "P3\n", "4 4\n", "255\n", "```\n", "The first line specifies the image encoding that is contained within the file. We will always use the \"P3\" specification. The second line specifies the number of pixel columns and rows present in the image. In this example, we have a 4 pixel by 4 pixel image. The final number indicates the maximum value for each red, green, and blue (RGB) element in the picture. We will always use a max value of 255. \n", "\n", "##### PPM Body\n", "Below the header is the body of the PPM file. Each pixel has a red, green, and blue value. For example, the body content of our 4x4 image might be:\n", "\n", "```\n", "255 0 0 0 255 0 0 0 255 255 255 255\n", "\n", "0 0 0 255 0 0 0 255 0 0 0 255\n", " \n", "255 0 255 0 0 0 255 0 0 0 255 0\n", "\n", "0 255 255 255 0 255 0 0 0 255 0 0\n", "```\n", "\n", "With these values, the pixel in the first column and first row has a RGB value of (255, 0, 0), which equates to red. The pixel in the last column and the first row has a RGB value of (255, 255, 255), which equates to white. A blown-up image containing these values would look like:\n", "\n", "Note: You can explore RGB color values at [this website](http://www.colorpicker.com/)." ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "ee2ac50a-b91c-4a4f-8d3f-cd28d15b28c6" } }, "source": [ "#### Example PPM Image\n", "Download this PPM image of the NY sky line: [ny.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny.ppm). You can open the image with software such as [Ifranview](http://www.irfanview.com/) to view it as an image. You can also open it as a text file (in Spyder IDE or a text editor) to view it as plain text (useful for debugging your code!).\n", "\n", "\n", "(Image from [https://pixabay.com/en/new-york-skyline-manhattan-hudson-540807/](https://pixabay.com/en/new-york-skyline-manhattan-hudson-540807/))" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "ea3bb5ab-3d19-4537-9722-925c0add58cf" } }, "source": [ "### Program Details\n", "Write a program to prompt the user for the name of an input PPM image. Produce an output PPM image file for each of the following modifications to the input PPM image:\n", "1. Flip the image vertically\n", "1. Flip the image horizontally\n", "1. Remove a primary color (red, green, blue)\n", "1. Negate the colors\n", "1. Apply a high contrast\n", "1. Add random noise\n", "1. Apply a gray scale\n", "1. (Bonus) Apply a horizontal blur\n", "\n", "Provide status updates to the console regarding your program's progress applying the above modifications.\n", "\n", "#### Required Data Structure\n", "In your program, the PPM image data must be stored in a 2-dimensional list (pixel rows and columns) of tuples. Each tuple represents an RGB triple, e.g. (red value, green value, blue value).\n", "\n", "#### Functions to Define\n", "At a minimum, your solution must contain the functions below. These functions will help you get started!\n", "\n", "Required *file I/O* functions:\n", "1. `process_header(infile, outfile)`: Accepts input and output file objects. Writes the first three lines of the input image to the output image (no need to modify these lines)\n", "1. `process_body(infile, outfile, modification)`: Accepts an input file object, output file object, and a string representing the modification to apply. Calls `load_image_data()` and calls the appropriate function to apply the specified modification according to `modification`. \n", "1. `load_image_data(infile)`: Accepts an input file object. Reads the data in from the input PPM image into a 2-dimensional list of RGB tuples. Returns the 2-dimensional list.\n", "\n", "Note: with the above functional decomposition we are going to open/process/close the input file for *every* modification. Is this necessary? **If you state in a comment block at the top of the code that you are going to implement this more efficient approach, you are free to change the above function headers.**\n", " \n", "Required *image modification* functions:\n", "1. `apply_vertical_flip(image_2dlist, outfile)`: Accepts a 2-dimensional list of tuples and an output file object. Flips the image data in `image_2dlist` vertically. \n", "1. `apply_horizontal_flip(image_2dlist, outfile)`: Accepts a 2-dimensional list of tuples and an output file object. Flips the image data in `image_2dlist` horizontally. \n", "1. (Bonus) `apply_horizontal_blur(image_2dlist, outfile)`: Accepts a 2-dimensional list of tuples and an output file object. Horizontally blurs the image data in `image_2dlist`. \n", " \n", "Required *RGB tuple/value* modification functions:\n", "1. `negative(num)`: Accepts a *single* integer RGB value. Subtracts 255 and takes the absolute value of the result. Returns the modified value.\n", "1. `high_contrast(num)`: Accepts a *single* integer RGB value. If the value is greater than 127, sets it to 255, otherwise sets it to 0. Returns the modified value.\n", "1. `random_noise(num)`: Accepts a *single* integer RGB value. Generates a random number in the range [-50, 50] and adds it to the value. Returns the modified value.\n", "1. `gray_scale(rgb)`: Accepts a RGB tuple. Computes the average of the RGB values in the tuple. Returns a new tuple with each RGB value is the average.\n", "1. `remove_color(rbg, color_to_remove)`: Accepts a RGB tuple and a string representing the color to remove (e.g. \"r\", \"g\", or \"b\"). Returns a new tuple with the appropriate red, green, or blue value set to 0.\n", "\n", "A `main()` function that drives your program.\n", "\n", "#### Mini Modification Examples (To Help Understand the Problem)\n", "Negative line example: `1 2 3 200 100 150` negated is `254 253 252 55 155 105`\n", "\n", "High contrast line example: `1 2 3 200 100 150` in high contrast is `0 0 0 255 0 255`\n", "\n", "Gray scale line example: `1 2 3 200 100 150` as gray scale is `2 2 2 150 150 150`.\n", "\n", "Remove green line example: `1 2 3 200 100 150` without green is `1 0 3 200 0 150`" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "0d7957c8-1887-4663-97f4-38c188a3dc34" } }, "source": [ "### Example Runs\n", "Here is an example run of the program (including the bonus):\n", "\n", "```\n", "Please enter the name of the input PPM image file: ny.ppm\n", "\n", "Opening ny.ppm for reading and ny_negative.ppm for writing...\n", "Image modification \"negative\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_high_contrast.ppm for writing...\n", "Image modification \"high contrast\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_remove_red.ppm for writing...\n", "Image modification \"remove red\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_remove_green.ppm for writing...\n", "Image modification \"remove green\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_remove_blue.ppm for writing...\n", "Image modification \"remove blue\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_gray_scale.ppm for writing...\n", "Image modification \"gray scale\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_vertical_flip.ppm for writing...\n", "Image modification \"vertical flip\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_horizontal_flip.ppm for writing...\n", "Image modification \"horizontal flip\" complete. Closing files...\n", "Opening ny.ppm for reading and ny_random_50.ppm for writing...\n", "Image modification \"random 50\" complete. Closing files...\n", "(Bonus) Opening ny.ppm for reading and ny_horizontal_blur.ppm for writing...\n", "(Bonus) Image modification \"horizontal blur\" complete. Closing files...\n", "\n", "Exiting program...\n", "```\n", "\n", "Here are the image files to compare with:\n", "\n", "#### Original\n", "[ny.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny.ppm):\n", "\n", "\n", "#### Vertical Flip\n", "[ny_vertical_flip.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_vertical_flip.ppm):\n", "\n", "\n", "#### Horizontal Flip\n", "([ny_horizontal_flip.ppm)](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_horizontal_flip.ppm):\n", "\n", "\n", "#### Remove Red\n", "[ny_remove_red.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_remove_red.ppm):\n", "\n", "\n", "#### Remove Green\n", "[ny_remove_green.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_remove_green.ppm):\n", "\n", "\n", "#### Remove Blue\n", "[ny_remove_blue.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_remove_blue.ppm):\n", "\n", "\n", "#### Negate\n", "[ny_negative.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_negative.ppm):\n", "\n", "\n", "#### High Contrast\n", "[ny_high_contrast.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_high_contrast.ppm):\n", "\n", "\n", "#### Random Noise\n", "[ny_random_50.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_random_50.ppm):\n", "\n", "\n", "#### Gray Scale\n", "[ny_gray_scale.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_gray_scale.ppm):\n", "" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "e4950e08-2252-49ef-9700-52e2ac825d2f" } }, "source": [ "### Bonus (10 pts)\n", "#### Horizontal Blur Modification (5 pts)\n", "Implement a \"horizontal blur\" which will replace every 5 RGB triples with the average of those 5 RGB triples. More specifically, a red value will be replaced with the average of itself and its four adjacent red values. Your program will do this for the green and blue values as well. Note that pixels on the edges will have to be handled specially.\n", "\n", "[ny_horizontal_blur.ppm](https://raw.githubusercontent.com/gsprint23/cpts111/master/progassignments/files/ny_horizontal_blur.ppm):\n", "\n", "\n", "#### Input File Validation Using the `os` Module (5 pts)\n", "Validate the input file specified by the user is valid, i.e. the input file adheres to the following requirements:\n", "1. The file exists in the current working directory\n", "1. The file has a .ppm extension\n", "\n", "If the user specifies an invalid input file, tell the user why the file is invalid, and *re-prompt until a valid file is specified by the user.*\n", "\n", "Hint: To check if a file exists, import the `os` module and call the predicate function [`os.path.isfile()`](https://docs.python.org/3/library/os.path.html#os.path.isfile):" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "nbpresent": { "id": "bfaf6a57-c9a7-452c-9f20-1fa6d63bd1a7" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n" ] } ], "source": [ "import os.path\n", "\n", "print(os.path.isfile(\"ny.ppm\")) \n", "print(os.path.isfile(\"file_that_doesnt_exist.ppm\")) " ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "e623cefb-a2bb-4b9f-8a34-fcf7d258ed28" } }, "source": [ "### Submitting Assignments\n", "1.\tUse the Blackboard tool https://learn.wsu.edu to submit your assignment to your TA. You will submit your code to the corresponding programming assignment under the \"Content\" tab. You must upload your solutions as `_pa6.zip` by the due date and time.\n", "2.\tYour .zip file should contain your .py file and **all .ppm files you used to test your program (originals and modifications)**.\n", "\n", "**Note: By submitting your code to be graded, you are stating that your submission does not violate the CptS 111 Academic Integrity Policy outlined in the [Syllabus](http://nbviewer.jupyter.org/github/gsprint23/cpts111/blob/master/Syllabus.ipynb)**." ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "9a27dd18-6be5-421e-953d-3e321318167c" } }, "source": [ "### Grading Guidelines\n", "This assignment is worth 125 points + 10 points bonus. Your assignment will be evaluated based on a successful compilation and adherence to the program requirements. We will grade according to the following criteria:\n", "\n", "A `main()` function that drives your program.\n", "\n", "* 3 pts for prompting and receiving input from the user\n", "* 5 pts for correct `process_header()`\n", "* 25 pts for correct `load_image_data()` and storage of PPM image data in a 2-dimensional list of RGB tuples\n", "* 15 pts for correct `process_body()` (and other helper functions you define)\n", "* 15 pts for correct `apply_vertical_flip()`\n", "* 15 pts for correct `apply_horizontal_flip()`\n", "* 5 pts for correct `negative()`\n", "* 5 pts for correct `high_contrast()`\n", "* 5 pts for correct `random_noise()`\n", "* 10 pts for correct `gray_scale()`\n", "* 10 pts for correct `remove_color()`\n", "* 5 pts for `main()`\n", "* 5 pts for adherence to proper programming style and comments established for the class\n", "* 2 pts for turning in original and modified ppm files" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" }, "nbpresent": { "slides": { "05646314-c8dc-4e7b-862a-584af25d89f1": { "id": "05646314-c8dc-4e7b-862a-584af25d89f1", "prev": "c15fbd62-6a38-4339-bd9c-58e0433136d8", "regions": { "67cf0eb3-da88-44a8-9676-4e260a489ae9": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "ee2ac50a-b91c-4a4f-8d3f-cd28d15b28c6", "part": "whole" }, "id": "67cf0eb3-da88-44a8-9676-4e260a489ae9" } } }, "0f89181b-7d4f-4f34-a6c5-214705460bbd": { "id": "0f89181b-7d4f-4f34-a6c5-214705460bbd", "prev": "90eb6cdf-cb41-4233-8285-197d0ab91342", "regions": { "4b8483ce-c0c0-4f6a-8852-a324d93110ec": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "e623cefb-a2bb-4b9f-8a34-fcf7d258ed28", "part": "whole" }, "id": "4b8483ce-c0c0-4f6a-8852-a324d93110ec" } } }, "1cc840a1-87bb-4772-a212-b0cb12d640ba": { "id": "1cc840a1-87bb-4772-a212-b0cb12d640ba", "prev": "05646314-c8dc-4e7b-862a-584af25d89f1", "regions": { "9f3a55dd-fbd3-4ad1-bc29-fde29a7ffbea": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "ea3bb5ab-3d19-4537-9722-925c0add58cf", "part": "whole" }, "id": "9f3a55dd-fbd3-4ad1-bc29-fde29a7ffbea" } } }, "226c71e9-5192-428b-8dd4-0af52338af0a": { "id": "226c71e9-5192-428b-8dd4-0af52338af0a", "prev": "d901de9f-aef2-412d-ab63-e94d26ce3d1a", "regions": { "0adc3210-7e47-4f3f-b1c4-79eb433645fc": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "1db74d81-8706-4233-8440-7eed36f235e4", "part": "whole" }, "id": "0adc3210-7e47-4f3f-b1c4-79eb433645fc" } } }, "65138b0a-d5e5-4749-aeb3-92a5ddb59e31": { "id": "65138b0a-d5e5-4749-aeb3-92a5ddb59e31", "prev": "66479557-cafd-48e1-afcb-cc2695277008", "regions": { "2a08988b-c3f8-46c2-a74e-8a2327c38a5c": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "e4950e08-2252-49ef-9700-52e2ac825d2f", "part": "whole" }, "id": "2a08988b-c3f8-46c2-a74e-8a2327c38a5c" } } }, "66479557-cafd-48e1-afcb-cc2695277008": { "id": "66479557-cafd-48e1-afcb-cc2695277008", "prev": "1cc840a1-87bb-4772-a212-b0cb12d640ba", "regions": { "5f8b826f-7a3d-4d7e-a781-b69057d04a63": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "0d7957c8-1887-4663-97f4-38c188a3dc34", "part": "whole" }, "id": "5f8b826f-7a3d-4d7e-a781-b69057d04a63" } } }, "90eb6cdf-cb41-4233-8285-197d0ab91342": { "id": "90eb6cdf-cb41-4233-8285-197d0ab91342", "prev": "65138b0a-d5e5-4749-aeb3-92a5ddb59e31", "regions": { "4b6f022c-94d3-4a42-ac54-6f3c2ff5419a": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "bfaf6a57-c9a7-452c-9f20-1fa6d63bd1a7", "part": "whole" }, "id": "4b6f022c-94d3-4a42-ac54-6f3c2ff5419a" } } }, "c15fbd62-6a38-4339-bd9c-58e0433136d8": { "id": "c15fbd62-6a38-4339-bd9c-58e0433136d8", "prev": "226c71e9-5192-428b-8dd4-0af52338af0a", "regions": { "65335029-91a3-4db5-badf-532bcb34f391": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "ce9bbdc8-41a1-4177-a347-875b194ee4ad", "part": "whole" }, "id": "65335029-91a3-4db5-badf-532bcb34f391" } } }, "d901de9f-aef2-412d-ab63-e94d26ce3d1a": { "id": "d901de9f-aef2-412d-ab63-e94d26ce3d1a", "prev": null, "regions": { "2b75c531-9f97-4d8f-bd16-40c1bb400bac": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "9edd6088-4fd1-4d02-84dc-86a8e1ad6630", "part": "whole" }, "id": "2b75c531-9f97-4d8f-bd16-40c1bb400bac" } } }, "f4eb4923-01cf-4b44-a64c-e2acd189fcd2": { "id": "f4eb4923-01cf-4b44-a64c-e2acd189fcd2", "prev": "0f89181b-7d4f-4f34-a6c5-214705460bbd", "regions": { "a3af9f33-ea3d-418b-b35b-d474cb4b7df1": { "attrs": { "height": 0.8, "width": 0.8, "x": 0.1, "y": 0.1 }, "content": { "cell": "9a27dd18-6be5-421e-953d-3e321318167c", "part": "whole" }, "id": "a3af9f33-ea3d-418b-b35b-d474cb4b7df1" } } } }, "themes": {} } }, "nbformat": 4, "nbformat_minor": 1 }