投資、科技、生活


Stanford Code in Place線上Python入門課程 - Week 3

Stanford Python程式課 - 第三週

上週 (第二週) 的課程主要為Python入門,介紹Expressions運算式、Control flow流程控制等,從While/If、Booleans、For Loops等,建立完整的 Python 核心基礎。


課程第三週學習目標

  • Lesson 7: Functions Revisited 複習Functions
  • Lesson 8: Core Complete 完整核心
  • Lesson 9: Images 影像處理
Code in Place第三週課程 Lesson 7: Functions


Lesson 7: Functions Revisited

學習重點

  • 如何定義function
  • 透過參數輸入資料
  • 回傳資料

Functions are Like Toasters!

使用「烤麵包機」,不需要知道「烤麵包機」是怎麼製造的 = 使用「別人寫的Function」,不需要知道「別人寫的Function」是怎麼完成的

使用「烤麵包機」,需要知道「烤麵包機」只接受什麼樣的麵包 = 使用「別人寫的Function」,需要知道「別人寫的Function」只接受什麼樣的參數

使用「烤麵包機」,知道「烤麵包機」會回傳烤好的麵包 = 使用「別人寫的Function」,知道「別人寫的Function」會回傳什麼樣的資料

Function的運用是最容易被忽略的重要觀念,因為它影響程式的簡潔程度 (教授用語是elegent)、也決定能夠寫出複雜程式的能力!


YouTube課程影片

   Lecture 7-1: Recap and Show

   Lecture 7-2: Toaster are Fns

   Lecture 7-3: Anatomy of a Fn

   Lecture 7-4: Many Examples

   Lecture 7-5: Decomp Show

點我下載課程講義

延伸閱讀Python Reader

   Python Functions




Lesson 8: Core Complete完整核心

本節課程是最完整的Python核心基礎,包含參數、流程控制、Function間資訊傳遞,以及很重要的Doctest!

下面Lecture example程式碼中的Factorial、Doctest都有Doctest使用的範例可以參考。

學習重點

  • Trace control flow 追蹤控制流程
  • Doctest 除錯驗證
  • Practice practice practice 不斷練習
Code in Place Lesson 8: Core Complete學習目標


YouTube課程影片

   Lecture 8-1: Intro

   Lecture 8-2: Sentiment

   Lecture 8-3: Factorial

   Lecture 8-4: Add_five

   Lecture 8-5: Doctests

   Lecture 8-6: Rock Paper Scissors

點我下載課程講義

延伸閱讀Python Reader

   Functions

   Doctests

Lecture Examples

Sentiment Analysis程式

import nltk.sentiment
analyzer = nltk.sentiment.SentimentIntensityAnalyzer()

def main():
    while True:
        user_text = input('? ')
        score = get_sentiment(user_text)
        reaction = get_reaction(score)
        print(reaction)
        print(score)
        print('')

def get_reaction(score):
    """
    Parameter score: a float between -1 and +1
    Return: An emoji as a string!
    """
    if score > 0.5:  
        return "🥰"
    if score > 0:
        return "🙂"
    if score == 0:
        return "😶"
    if score < -0.5:
        return "😢"
    if score < 0:
        return "😟"

def get_sentiment(user_text):
    """
    Parameter user_text: any text (string)
    Return: a sentiment score between -1 and +1 (float)
    """
    # 1. pass the text into the analyzer.polarity_scores function, part of the nltk package
    scores = analyzer.polarity_scores(user_text)
    # 2. extract the sentiment score. Scores is a "dictionary" (covered on May 17th)
    sentiment_score = scores['compound']

    return sentiment_score

if __name__ == '__main__':
    main()

Factorial程式

# Constant – visible to all functions
MAX_NUM = 9

def main():
    # repeat for several values!
    for i in range(MAX_NUM+1):
        print(i, factorial(i))

def factorial(n):
    """
    Calculates n factorial.
    5 factorial is 5 * 4 * 3 * 2 * 1
    >>> factorial(6)
    720
    >>> factorial(5)
    120
    >>> factorial(4)
    24
    >>> factorial(3)
    6
    >>> factorial(1)
    1
    >>> factorial(0)
    1
    """
    answer = 1
    for i in range(1,n+1):
        answer *= i
    return answer
    
if __name__ == '__main__':
    main()

Doctest程式

"""
Example of Doctest
"""

def main():
    year = int(input("Enter a year to check leap year: "))
    if is_leap_year(year):
        print("Year "+str(year)+" is a leap year")
    else:
        print("Year "+str(year)+" is NOT a leap year")        

def is_divisible(a, b):
    """
    >>> is_divisible(20, 4)
    True
    >>> is_divisible(12, 7)
    False
    >>> is_divisible(10, 10)
    True
    """
    return a % b == 0

def is_leap_year(year):
    """
    Returns Boolean indicating if given year is a leap year.
    It is a leap year if the year is:
    * divisible by 4, but not divisible by 100
     OR
    * divisible by 400
    Doctests:
    >>> is_leap_year(2001)
    False
    >>> is_leap_year(2020)
    True
    >>> is_leap_year(2000)
    True
    >>> is_leap_year(1900)
    False
    """

    # if the year is divisible by 400, it is a leap year!
    if is_divisible(year, 400):
        return True

    # other wise its a leap year if its divisible by 4 and not 100
    return is_divisible(year, 4) and not is_divisible(year, 100)
            
if __name__ == '__main__':
    main()



Lesson 9: Images


"It is no surprise then that it was this same class, that inspired Kevin Systrom and Mike Krieger, co-founders of Instagram , to develop image filters that are at the heart of every popular social media platform today."

Instagram其實是創辦人在CS106課程中學習了程式處理影像,才激發了創辦Instagram的動機!


學習重點

  • 了解影像如何在程式中呈現
  • 學習SimpleImage Library
  • 撰寫操控影像的程式

YouTube課程影片

   Lecture 9-1: Intro

   Lecture 9-2: SimpleImage

   Lecture 9-3: Image Examples

   Lecture 9-4: Greenscreen

   Lecture 9-5: Mirror

   Lecture 9-6: Wrap

   Lecture 9-7: Responsibility

點我下載課程講義

延伸閱讀Python Reader

   Images

安裝Pillow、下載 simpleimage.py

提供 Stanford 的 simpleimage.py 程式,請自行選取複製後儲存成 simpleimage.py ,在Pycharm ((IDE) 中執行課程的範例需要

  1. 安裝Pillow library
  2. 將 simpleimage.py 檔案放在PyCharm (IDE) 的project目錄內

延伸閱讀:Stanford Code in Place線上Python入門課程 - Week 2

延伸閱讀:Stanford Code in Place線上Python入門課程 - Week 1

延伸閱讀:從Stanford學習免費、但值一萬美元的程式設計課





Pillow安裝方式,資料來源:Code in Place Lesson 9 Lecture Slides


simpleimage.py:

#!/usr/bin/env python3

"""
Stanford CS106AP SimpleImage

Written by Nick Parlante, Sonja Johnson-Yu, and Nick Bowman.
 -7/2019  version, has file reading, pix, foreach, hidden get/setpix

SimpleImage Features:
Create image:
  image = SimpleImage.blank(400, 200)   # create new image of size
  image = SimpleImage('foo.jpg')        # create from file

Access size
  image.width, image.height

Get pixel at x,y
  pix = image.get_pixel(x, y)
  # pix is RGB tuple like (100, 200, 0)

Set pixel at x,y
  image.set_pixel(x, y, pix)   # set data by tuple also

Get Pixel object at x,y
  pixel = image.get_pixel(x, y)
  pixel.red = 0
  pixel.blue = 255

Show image on screen
  image.show()

The main() function below demonstrates the above functions as a test.
"""

import sys
# If the following line fails, "Pillow" needs to be installed
from PIL import Image


def clamp(num):
    """
    Return a "clamped" version of the given num,
    converted to be an int limited to the range 0..255 for 1 byte.
    """
    num = int(num)
    if num < 0:
        return 0
    if num >= 256:
        return 255
    return num


class Pixel(object):
    """
    A pixel at an x,y in a SimpleImage.
    Supports set/get .red .green .blue
    and get .x .y
    """
    def __init__(self, image, x, y):
        self.image = image
        self._x = x
        self._y = y

    def __str__(self):
        return 'r:' + str(self.red) + ' g:' + str(self.green) + ' b:' + str(self.blue)

    # Pillow image stores each pixel color as a (red, green, blue) tuple.
    # So the functions below have to unpack/repack the tuple to change anything.

    @property
    def red(self):
        return self.image.px[self._x, self._y][0]

    @red.setter
    def red(self, value):
        rgb = self.image.px[self._x, self._y]
        self.image.px[self._x, self._y] = (clamp(value), rgb[1], rgb[2])

    @property
    def green(self):
        return self.image.px[self._x, self._y][1]

    @green.setter
    def green(self, value):
        rgb = self.image.px[self._x, self._y]
        self.image.px[self._x, self._y] = (rgb[0], clamp(value), rgb[2])

    @property
    def blue(self):
        return self.image.px[self._x, self._y][2]

    @blue.setter
    def blue(self, value):
        rgb = self.image.px[self._x, self._y]
        self.image.px[self._x, self._y] = (rgb[0], rgb[1], clamp(value))

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y


# color tuples for background color names 'red' 'white' etc.
BACK_COLORS = {
    'white': (255, 255, 255),
    'black': (0, 0, 0),
    'red': (255, 0, 0),
    'green': (0, 255, 0),
    'blue': (0, 0, 255),
}


class SimpleImage(object):
    def __init__(self, filename, width=0, height=0, back_color=None):
        """
        Create a new image. This case works: SimpleImage('foo.jpg')
        To create a blank image use SimpleImage.blank(500, 300)
        The other parameters here are for internal/experimental use.
        """
        # Create pil_image either from file, or making blank
        if filename:
            self.pil_image = Image.open(filename).convert("RGB")
            if self.pil_image.mode != 'RGB':
                raise Exception('Image file is not RGB')
            self._filename = filename  # hold onto
        else:
            if not back_color:
                back_color = 'white'
            color_tuple = BACK_COLORS[back_color]
            if width == 0 or height == 0:
                raise Exception('Creating blank image requires width/height but got {} {}'
                                .format(width, height))
            self.pil_image = Image.new('RGB', (width, height), color_tuple)
        self.px = self.pil_image.load()
        size = self.pil_image.size
        self._width = size[0]
        self._height = size[1]
        self.curr_x = 0
        self.curr_y = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.curr_x < self.width and self.curr_y < self.height:
            x = self.curr_x
            y = self.curr_y
            self.increment_curr_counters()
            return Pixel(self, x, y)
        else:
            self.curr_x = 0
            self.curr_y = 0
            raise StopIteration()

    def increment_curr_counters(self):
        self.curr_x += 1
        if self.curr_x == self.width:
            self.curr_x = 0
            self.curr_y += 1

    @classmethod
    def blank(cls, width, height, back_color=None):
        """Create a new blank image of the given width and height, optional back_color."""
        return SimpleImage('', width, height, back_color=back_color)

    @classmethod
    def file(cls, filename):
        """Create a new image based on a file, alternative to raw constructor."""
        return SimpleImage(filename)

    @property
    def width(self):
        """Width of image in pixels."""
        return self._width

    @property
    def height(self):
        """Height of image in pixels."""
        return self._height

    def get_pixel(self, x, y):
        """
        Returns a Pixel at the given x,y, suitable for getting/setting
        .red .green .blue values.
        """
        if x < 0 or x >= self._width or y < 0 or y >= self.height:
            e = Exception('get_pixel bad coordinate x %d y %d (vs. image width %d height %d)' %
                          (x, y, self._width, self.height))
            raise e
        return Pixel(self, x, y)

    def set_pixel(self, x, y, pixel):
        if x < 0 or x >= self._width or y < 0 or y >= self.height:
            e = Exception('set_pixel bad coordinate x %d y %d (vs. image width %d height %d)' %
                          (x, y, self._width, self.height))
            raise e
        self.px[x, y] = (pixel.red, pixel.green, pixel.blue)

    def set_rgb(self, x, y, red, green, blue):
        """
        Set the pixel at the given x,y to have
        the given red/green/blue values without
        requiring a separate pixel object.
        """
        self.px[x, y] = (red, green, blue)

    def _get_pix_(self, x, y):
        """Get pix RGB tuple (200, 100, 50) for the given x,y."""
        return self.px[x, y]

    def _set_pix_(self, x, y, pix):
        """Set the given pix RGB tuple into the image at the given x,y."""
        self.px[x, y] = pix

    def show(self):
        """Displays the image using an external utility."""
        self.pil_image.show()

    def make_as_big_as(self, image):
        """Resizes image to the shape of the given image"""
        self.pil_image = self.pil_image.resize((image.width, image.height))
        self.px = self.pil_image.load()
        size = self.pil_image.size
        self._width = size[0]
        self._height = size[1]


def main():
    """
    main() exercises the features as a test.
    1. With 1 arg like flowers.jpg - opens it
    2. With 0 args, creates a yellow square with
    a green stripe at the right edge.
    """
    args = sys.argv[1:]
    if len(args) == 1:
        image = SimpleImage.file(args[0])
        image.show()
        return

    # Create yellow rectangle, using foreach iterator
    image = SimpleImage.blank(400, 200)
    for pixel in image:
        pixel.red = 255
        pixel.green = 255
        pixel.blue = 0

    # for pixel in image:
    #     print(pixel)

    # Set green stripe using pix access.
    pix = image._get_pix_(0, 0)
    green = (0, pix[1], 0)
    for x in range(image.width - 10, image.width):
        for y in range(image.height):
            image._set_pix_(x, y, green)
    image.show()


if __name__ == '__main__':
    main()

Lecture Examples

Imageexampless.py

"""
This program contains several examples of functions that
manipulate an image to show how the SimpleImage library works.
"""

from simpleimage import SimpleImage

def darker(image):
    """
    Makes image passed in darker by halving red, green, blue values.
    Note: changes in image persist after function ends.
    """
    # Demonstrate looping over all the pixels of an image,
    # changing each pixel to be half its original intensity.
    for pixel in image:
        pixel.red = pixel.red // 2
        pixel.green = pixel.green // 2
        pixel.blue = pixel.blue // 2

def red_channel(filename):
    """
    Reads image from file specified by filename.
    Changes the image as follows:
    For every pixel, set green and blue values to 0
    yielding the red channel.
    Return the changed image.
    """
    image = SimpleImage(filename)
    for pixel in image:
        pixel.green = 0
        pixel.blue = 0
    return image

def compute_luminosity(red, green, blue):
    """
    Calculates the luminosity of a pixel using NTSC formula
    to weight red, green, and blue values appropriately.
    """
    return (0.299 * red) + (0.587 * green) + (0.114 * blue)

def grayscale(filename):
    """
    Reads image from file specified by filename.
    Change the image to be grayscale using the NTSC
    luminosity formula and return it.
    """
    image = SimpleImage(filename)
    for pixel in image:
        luminosity = compute_luminosity(pixel.red, pixel.green, pixel.blue)
        pixel.red = luminosity
        pixel.green = luminosity
        pixel.blue = luminosity
    return image

def main():
    """
    Run your desired image manipulation functions here.
    You should store the return value (image) and then
    call .show() to visualize the output of your program.
    """
    flower = SimpleImage('flower.png')
    flower.show()

    darker(flower)
    flower.show()

    red_flower = red_channel('flower.png')
    red_flower.show()

    grayscale_flower = grayscale('flower.png')
    grayscale_flower.show()

if __name__ == '__main__':
    main()

greenscreen.py

"""
This program shows an example of "greenscreening" (actually
"redscreening" in this case).  This is where we replace the
pixels of a certain color intensity in a particular channel
(here, we use red) with the pixels from another image.
"""

from simpleimage import SimpleImage

INTENSITY_THRESHOLD = 1.6

def redscreen(main_filename, back_filename):
    """
    Implements the notion of "redscreening".  That is,
    the image in the main_filename has its "sufficiently red"
    pixels replaced with pixel from the corresponding x,y
    location in the image in the file back_filename.
    Returns the resulting "redscreened" image.
    """
    image = SimpleImage(main_filename)
    back = SimpleImage(back_filename)
    for pixel in image:
        average = (pixel.red + pixel.green + pixel.blue) // 3
        # See if this pixel is "sufficiently" red
        if pixel.red >= average * INTENSITY_THRESHOLD:
            # If so, we get the corresponding pixel from the
            # back image and overwrite the pixel in
            # the main image with that from the back image.
            x = pixel.x
            y = pixel.y
            image.set_pixel(x, y, back.get_pixel(x, y))
    return image

def main():
    """
    Run your desired image manipulation functions here.
    You should store the return value (image) and then
    call .show() to visualize the output of your program.
    """
    original_stop = SimpleImage('stop.png')
    original_stop.show()

    original_leaves = SimpleImage('leaves.png')
    original_leaves.show()

    stop_leaves_replaced = redscreen('stop.png', 'leaves.png')
#    stop_leaves_replaced = redscreen(original_stop, original_leaves)
    stop_leaves_replaced.show()

if __name__ == '__main__':
    main()

mirror.py

"""
File: mirror.py
---------------
This program shows an example of creating an image
that shows an original image and its mirror reflection
in a new image.
"""

from simpleimage import SimpleImage

def mirror_image(filename):
    """
    Read an image from the file specified by filename.
    Returns a new images that includes the original image
    and its mirror reflection.
    Returns the resulting "redscreened" image.
    """
    image = SimpleImage(filename)
    width = image.width
    height = image.height

    # Create new image to contain mirror reflection
    mirror = SimpleImage.blank(width * 2, height)

    for y in range(height):
        for x in range(width):
            pixel = image.get_pixel(x, y)
            mirror.set_pixel(x, y, pixel)
            mirror.set_pixel((width * 2) - (x + 1), y, pixel)
    return mirror

def main():
    """
    Run your desired image manipulation functions here.
    You should store the return value (image) and then
    call .show() to visualize the output of your program.
    """
    original = SimpleImage('burrito.jpg')
    original.show()

    mirrored = mirror_image('burrito.jpg')
    mirrored.show()

if __name__ == '__main__':
    main()

未完待續 ...






2 comments

  1. 您好,看了您的文章加上code in place的課程收穫良多,但現在課程已經結束,無法再報名取得Ed的帳號,前兩週您的的文章裡有看到作業可以練習,week3就沒有看到assignments的題目,不知是否方便提供呢?謝謝!

    回覆刪除
    回覆
    1. 抱歉先前沒看到留言,Assignment並不是每週課程都有,剛剛回去看了Code in Place網頁,一共只有三個Assignment,加最後的Final Project,Assignment 3是在 Week 5的課程文章內喔
      Final Project沒有限定題目,是學生自由發揮,最後的連結在此 https://codeinplace.stanford.edu/2021/showcase,有興趣可以參考大家的創作

      刪除