"""CSC110 Fall 2021 Assignment 1, Part 4 Instructions (READ THIS FIRST!) =============================== Please follow the instructions in the assignment handout to complete this file. Copyright and Usage Information =============================== This file is provided solely for the personal and private use of students taking CSC110 at the University of Toronto St. George campus. All forms of distribution of this code, whether as given or with any changes, are expressly prohibited. For more information on copyright for CSC110 materials, please consult our Course Syllabus. This file is Copyright (c) 2021 Mario Badr and Tom Fairgrieve. """ import random import a1_image def maximize_channels(old_pixel: tuple, value: int) -> tuple: """Return a new pixel that has colour channels set to the larger of value or the corresponding colour channel in old_pixel. * Assumed that value is positive >>> example_pixel = (100, 12, 155) >>> maximize_channels(example_pixel, 128) (128, 128, 155) """ return tuple([max(chan, value) for chan in old_pixel]) def divide_channels(old_pixel: tuple, denominator: int) -> tuple: """Return a new pixel that has colour channels set to the quotient from dividing the corresponding colour channel in old_pixel by denominator. * Assumed that denominators are positive integers. >>> example_pixel = (100, 12, 155) >>> divide_channels(example_pixel, 2) (50, 6, 77) """ return tuple([chan // denominator for chan in old_pixel]) def add_pepper(pixel_data: list, k: int) -> list: """Return a new list of pixels formed from the corresponding pixels in pixel_data that has some randomly chosen black pixels. The chance that a new pixel will be black is based on k. The probability that a pixel is black is to be : 1 / (k + 1) Assume that k >= 0. You must use the divide_channels function (above). Hint: use the function random.choice to choose from a list of denominators. What denominator, passed to divide_channels, causes a pixel to not change its colour? What denominator causes a pixel to become black? Because of the randomness, we can't specify an exact doctest. """ return [divide_channels(pixel, random.choice([1] * k + [256])) for pixel in pixel_data] def add_salt(pixel_data: list, k: int) -> list: """Return a new list of pixels formed from the corresponding pixels in pixel_data that has some randomly chosen white pixels. The chance that a new pixel will be white is based on k. The probability that a pixel is white is to be : 1 / (k + 1) Assume that k >= 0. You must use the maximize_channels function (above). Hint: use the function random.choice to choose from a list of values. What value, passed to maximize_channels, causes a pixel to not change its colour? What value causes a pixel to become white? Because of the randomness, we can't specify an exact doctest. """ return [maximize_channels(pixel, random.choice([0] * k + [255])) for pixel in pixel_data] def add_salt_and_pepper(pixel_data: list, k: int) -> list: """Return a new list of pixels formed from the corresponding pixels in pixel_data that has some randomly chosen white pixels and some randomly chosen black pixels. You must use the add_pepper then add_salt functions, in that order, with both using noise probabilities determined by k. Because of the randomness, we can't specify an exact doctest. """ pixel_data = add_pepper(pixel_data, k) pixel_data = add_salt(pixel_data, k) return pixel_data def run_salt_and_pepper_example(source: str, destination: str, k: int) -> None: """Add salt and pepper noise to an example image file at source and save the result in destination, where noisiness is a function of k. You can run this function with an image of your choice. Make sure the image is not too large or this will take a long time. """ original_pixel_data, width, height = a1_image.load_image(source) noisy_pixel_data = add_salt_and_pepper(original_pixel_data, k) a1_image.save_image(destination, noisy_pixel_data, width, height) if __name__ == '__main__': import doctest doctest.testmod() run_salt_and_pepper_example('images/horses.png', 'images/noisy-horses.png', 16) # When you are ready to check your work with python_ta, uncomment the following lines. # (Delete the "#" and space before each line.) import python_ta python_ta.check_all(config={ 'extra-imports': ['random', 'a1_image'], 'max-line-length': 100 })