Unit 2.2 Data Compression, Images

Lab will perform alterations on images, manipulate RGB values, and reduce the number of pixels. College Board requires you to learn about Lossy and Lossless compression.

  • toc:true- categories: [python]

Enumerate "Data" Big Idea from College Board

Some of the big ideas and vocab that you observe, talk about it with a partner ...

  • "Data compression is the reduction of the number of bits needed to represent data"
  • "Data compression is used to save transmission time and storage space."
  • "lossy data can reduce data but the original data is not recovered"
  • "lossless data lets you restore and recover"

The Image Lab Project contains a plethora of College Board Unit 2 data concepts. Working with Images provides many opportunities for compression and analyzing size.

Image Files and Size

Here are some Images Files. Download these files, load them into images directory under _notebooks in your Blog. - Clouds Impression

Describe some of the meta data and considerations when managing Image files. Describe how these relate to Data Compression ...

  • File Type, PNG and JPG are two types used in this lab
  • Size, height and width, number of pixels
  • Visual perception, lossy compression

Python Libraries and Concepts used for Jupyter and Files/Directories

Introduction to displaying images in Jupyter notebook

IPython

Support visualization of data in Jupyter notebooks. Visualization is specific to View, for the web visualization needs to be converted to HTML.

pathlib

File paths are different on Windows versus Mac and Linux. This can cause problems in a project as you work and deploy on different Operating Systems (OS's), pathlib is a solution to this problem.

  • What are commands you use in terminal to access files?

ls, sudo nano

  • What are the command you use in Windows terminal to access files?

cd, ls, dir, wsl

  • What are some of the major differences?

in windows command prompt, I have to type wsl in order to get to (base), whereas in Mac terminal, you can directly start typing commands.

Provide what you observed, struggled with, or leaned while playing with this code.

  • Why is path a big deal when working with images?

Originally, my photos weren't loading and they would say file not found. I realized this is because the images needed to be in the notebooks folder rather than the regular images folder in the directory. The route needs to be correct.

  • How does the meta data source and label relate to Unit 5 topics?

Metadata is data about data:i.e., its properties, history, origin, versions, and other information about a data asset. Metadata is data that describes other data, providing a structured reference that helps to sort and identify attributes of the information it describes, so it helps with the unit 5 topic of databases and data stuctures. - Look up IPython, describe why this is interesting in Jupyter Notebooks for both Pandas and Images?

IPython is basically the "recommended" Python shell, which provides extra features. IPython can manipulate image data, changing its display (size, color, scale...). You can use Pandas to perform tasks like filtering your data according to certain conditions, or segmenting and segregating the data according to preference, etc.

from IPython.display import Image, display
from pathlib import Path  # https://medium.com/@ageitgey/python-3-quick-tip-the-easy-way-to-deal-with-file-paths-on-windows-mac-and-linux-11a072b58d5f

# prepares a series of images
def image_data(path=Path("images/"), images=None):  # path of static images is defaulted
    if images is None:  # default image
        images = [
            # {'source': "Peter Carolin", 'label': "Clouds Impression", 'file': "clouds-impression.png"},
            # {'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "lassen-volcano.jpg"},
            {'source': "StampChamp", 'label': "Smiley Face", 'file': "smileyface.png"}
            
        ]
    for image in images:
        # File to open
        image['filename'] = path / image['file']  # file with path
    return images

def image_display(images):
    for image in images:  
        display(Image(filename=image['filename']))


# Run this as standalone tester to see sample data printed in Jupyter terminal
if __name__ == "__main__":
    # print parameter supplied image
    # smiley_face = image_data(images=[{'source': "Internet", 'label': "Smiley Face", 'file': "smileyface.png"}])
    # image_display(smiley_face)
    
    # display default images from image_data()
    default_images = image_data()
    image_display(default_images)
    

Reading and Encoding Images (2 implementations follow)

PIL (Python Image Library)

Pillow or PIL provides the ability to work with images in Python. Geeks for Geeks shows some ideas on working with images.

base64

Image formats (JPG, PNG) are often called *Binary File formats, it is difficult to pass these over HTTP. Thus, base64 converts binary encoded data (8-bit, ASCII/Unicode) into a text encoded scheme (24 bits, 6-bit Base64 digits). Thus base64 is used to transport and embed binary images into textual assets such as HTML and CSS.- How is Base64 similar or different to Binary and Hexadecimal? Binary is base2, base16 is hexadecimal. Base64 takes 4 characters for every three bytes so it's more efficient than base16

  • Translate first 3 letters of your name to Base64.

001010011010110010- kay

numpy

Numpy is described as "The fundamental package for scientific computing with Python". In the Image Lab, a Numpy array is created from the image data in order to simplify access and change to the RGB values of the pixels, converting pixels to grey scale.

io, BytesIO

Input and Output (I/O) is a fundamental of all Computer Programming. Input/output (I/O) buffering is a technique used to optimize I/O operations. In large quantities of data, how many frames of input the server currently has queued is the buffer. In this example, there is a very large picture that lags.

  • Where have you been a consumer of buffering?

When your watching a Youtube video, sometimes the video will buffer.

  • From your consumer experience, what effects have you experienced from buffering?

I feel annoyed and don't want to finish watching the video sometimes if it takes too long to load. Sometimes the data can get lost.

  • How do these effects apply to images?

Sometimes, if images are too large, they will take a long time to load, or the quality will go down.

Data Structures, Imperative Programming Style, and working with Images

Introduction to creating meta data and manipulating images. Look at each procedure and explain the the purpose and results of this program. Add any insights or challenges as you explored this program.

  • Does this code seem like a series of steps are being performed?

yes, you are defining functions and using if statements based on the defined functions from previous steps.

  • Describe Grey Scale algorithm in English or Pseudo code?

Get the red, green, and blue values of a pixel. Use fancy math to turn those numbers into a single gray value. Replace the original red, green, and blue values with the new gray value.

  • Describe scale image? What is before and after on pixels in three images?

the images are getting scaled down to a smaller size. Scale image is the resizing of a digital image. The green square became bigger, the clouds image stayed the same, and the mountains picture became smaller. Each of the 3 photos are scaled to (320, 320)

  • Is scale image a type of compression? If so, line it up with College Board terms described?

Yes, it is lossless compression, becaus the quality of the image remains the same but the image becomes smaller. There are trade-offs involved in choosing a compression technique for storing and transmitting data. While lossy compression reduces file size but lossless compression removes unecessary data. it resizes the image based on the pixel values/measurements you choose.

from IPython.display import HTML, display
from pathlib import Path  # https://medium.com/@ageitgey/python-3-quick-tip-the-easy-way-to-deal-with-file-paths-on-windows-mac-and-linux-11a072b58d5f
from PIL import Image as pilImage # as pilImage is used to avoid conflicts
from io import BytesIO
import base64
import numpy as np

# prepares a series of images
def image_data(path=Path("images/"), images=None):  # path of static images is defaulted
    if images is None:  # default image
        images = [
            {'source': "San Diego Humane Society", 'label': "Joe", 'file': "dogJoe.PNG"},
            {'source': "San Diego Humane Society", 'label': "Bean", 'file': "dogBean.PNG"},
            {'source': "San Diego Humane Society", 'label': "Harry", 'file': "dogHarry.PNG"}
        ]
    for image in images:
        # File to open
        image['filename'] = path / image['file']  # file with path
    return images

# Large image scaled to baseWidth of 320
def scale_image(img):
    baseWidth = 320
    scalePercent = (baseWidth/float(img.size[0]))
    scaleHeight = int((float(img.size[1])*float(scalePercent)))
    scale = (baseWidth, scaleHeight)
    return img.resize(scale)

# PIL image converted to base64
def image_to_base64(img, format):
    with BytesIO() as buffer:
        img.save(buffer, format)
        return base64.b64encode(buffer.getvalue()).decode()

# Set Properties of Image, Scale, and convert to Base64
def image_management(image):  # path of static images is defaulted        
    # Image open return PIL image object
    img = pilImage.open(image['filename'])
    
    # Python Image Library operations
    image['format'] = img.format
    image['mode'] = img.mode
    image['size'] = img.size
    # Scale the Image
    img = scale_image(img)
    image['pil'] = img
    image['scaled_size'] = img.size
    # Scaled HTML
    image['html'] = '<img src="data:image/png;base64,%s">' % image_to_base64(image['pil'], image['format'])
    
# Create Grey Scale Base64 representation of Image
def image_management_add_html_grey(image):
    # Image open return PIL image object
    img = image['pil']
    format = image['format']
    
    img_data = img.getdata()  # Reference https://www.geeksforgeeks.org/python-pil-image-getdata/
    image['data'] = np.array(img_data) # PIL image to numpy array
    image['gray_data'] = [] # key/value for data converted to gray scale

    # 'data' is a list of RGB data, the list is traversed and hex and binary lists are calculated and formatted
    for pixel in image['data']:
        # create gray scale of image, ref: https://www.geeksforgeeks.org/convert-a-numpy-array-to-an-image/
        average = (pixel[0] + pixel[1] + pixel[2]) // 3  # average pixel values and use // for integer division
        if len(pixel) > 3:
            image['gray_data'].append((average, average, average, pixel[3])) # PNG format
        else:
            image['gray_data'].append((average, average, average))
        # end for loop for pixels
        
    img.putdata(image['gray_data'])
    image['html_grey'] = '<img src="data:image/png;base64,%s">' % image_to_base64(img, format)


# Jupyter Notebook Visualization of Images
if __name__ == "__main__":
    # Use numpy to concatenate two arrays
    images = image_data()
    
    # Display meta data, scaled view, and grey scale for each image
    for image in images:
        image_management(image)
        print("---- meta data -----")
        print(image['label'])
        print(image['source'])
        print(image['format'])
        print(image['mode'])
        print("Original size: ", image['size'])
        print("Scaled size: ", image['scaled_size'])
        
        print("-- original image --")
        display(HTML(image['html'])) 
        
        print("--- grey image ----")
        image_management_add_html_grey(image)
        display(HTML(image['html_grey'])) 
    print()
---- meta data -----
Joe
San Diego Humane Society
PNG
RGBA
Original size:  (1126, 723)
Scaled size:  (320, 205)
-- original image --
--- grey image ----
---- meta data -----
Bean
San Diego Humane Society
PNG
RGBA
Original size:  (997, 732)
Scaled size:  (320, 234)
-- original image --
--- grey image ----
---- meta data -----
Harry
San Diego Humane Society
PNG
RGBA
Original size:  (994, 731)
Scaled size:  (320, 235)
-- original image --
--- grey image ----

Data Structures and OOP

Most data structures classes require Object Oriented Programming (OOP). Since this class is lined up with a College Course, OOP will be talked about often. Functionality in remainder of this Blog is the same as the prior implementation. Highlight some of the key difference you see between imperative and oop styles.

  • Read imperative and object-oriented programming on Wikipedia

imperative programming:imperative programming is a programming paradigm of software that uses statements that change a program's state.OOP: programming paradigm based on the concept of "objects", which can contain data and code.

  • Consider how data is organized in two examples, in relations to procedures
  1. ranking students by their score from highest to lowest on the most recent final.
  2. organizing who prefers soccer or football at school
  • Look at Parameters in Imperative and Self in OOP

The self variable is used to represent the instance of the class which is often used in object-oriented programming. It works as a reference to the object. Python uses the self parameter to refer to instance attributes and methods of the class.

Additionally, review all the imports in these three demos. Create a definition of their purpose, specifically these ...

  • PIL: PIL is an additional, free, open-source library for the Python programming language that provides support for opening, manipulating, and saving many different image file formats. It adds image processing to Python
  • numpy: tells Python to bring the NumPy library into your current environment. The as np portion of the code then tells Python to give NumPy the alias of np. This allows you to use NumPy functions by simply typing np. Numpy is an open-source library for the Python programming language. It is used for scientific computing and working with arrays. Apart from its multidimensional array object, it also provides high-level functioning tools for working with arrays.
  • base64: It's designed to carry data stored in binary format across the channels, and it takes any form of data and transforms it into a long string of plain text. This module provides functions for encoding binary data to printable ASCII characters and decoding such encodings back to binary data.
from IPython.display import HTML, display
from pathlib import Path  # https://medium.com/@ageitgey/python-3-quick-tip-the-easy-way-to-deal-with-file-paths-on-windows-mac-and-linux-11a072b58d5f
from PIL import Image as pilImage # as pilImage is used to avoid conflicts
from io import BytesIO
import base64
import numpy as np


class Image_Data:

    def __init__(self, source, label, file, path, baseWidth=320):
        self._source = source    # variables with self prefix become part of the object, 
        self._label = label
        self._file = file
        self._filename = path / file  # file with path
        self._baseWidth = baseWidth

        # Open image and scale to needs
        self._img = pilImage.open(self._filename)
        self._format = self._img.format
        self._mode = self._img.mode
        self._originalSize = self.img.size
        self.scale_image()
        self._html = self.image_to_html(self._img)
        self._html_grey = self.image_to_html_grey()


    @property
    def source(self):
        return self._source  
    
    @property
    def label(self):
        return self._label 
    
    @property
    def file(self):
        return self._file   
    
    @property
    def filename(self):
        return self._filename   
    
    @property
    def img(self):
        return self._img
             
    @property
    def format(self):
        return self._format
    
    @property
    def mode(self):
        return self._mode
    
    @property
    def originalSize(self):
        return self._originalSize
    
    @property
    def size(self):
        return self._img.size
    
    @property
    def html(self):
        return self._html
    
    @property
    def html_grey(self):
        return self._html_grey
        
    # Large image scaled to baseWidth of 320
    def scale_image(self):
        scalePercent = (self._baseWidth/float(self._img.size[0]))
        scaleHeight = int((float(self._img.size[1])*float(scalePercent)))
        scale = (self._baseWidth, scaleHeight)
        self._img = self._img.resize(scale)
    
    # PIL image converted to base64
    def image_to_html(self, img):
        with BytesIO() as buffer:
            img.save(buffer, self._format)
            return '<img src="data:image/png;base64,%s">' % base64.b64encode(buffer.getvalue()).decode()
            
    # Create Grey Scale Base64 representation of Image
    def image_to_html_grey(self):
        img_grey = self._img
        numpy = np.array(self._img.getdata()) # PIL image to numpy array
        
        grey_data = [] # key/value for data converted to gray scale
        # 'data' is a list of RGB data, the list is traversed and hex and binary lists are calculated and formatted
        for pixel in numpy:
            # create gray scale of image, ref: https://www.geeksforgeeks.org/convert-a-numpy-array-to-an-image/
            average = (pixel[0] + pixel[1] + pixel[2]) // 3  # average pixel values and use // for integer division
            if len(pixel) > 3:
                grey_data.append((average, average, average, pixel[3])) # PNG format
            else:
                grey_data.append((average, average, average))
            # end for loop for pixels
            
        img_grey.putdata(grey_data)
        return self.image_to_html(img_grey)

        
# prepares a series of images, provides expectation for required contents
def image_data(path=Path("images/"), images=None):  # path of static images is defaulted
    if images is None:  # default image
        images = [
            {'source': "San Diego Humane Society", 'label': "Joe", 'file': "dogJoe.PNG"},
            {'source': "San Diego Humane Society", 'label': "Bean", 'file': "dogBean.PNG"},
            {'source': "San Diego Humane Society", 'label': "Harry", 'file': "dogHarry.PNG"}
        ]
    return path, images

# turns data into objects
def image_objects():        
    id_Objects = []
    path, images = image_data()
    for image in images:
        id_Objects.append(Image_Data(source=image['source'], 
                                  label=image['label'],
                                  file=image['file'],
                                  path=path,
                                  ))
    return id_Objects

# Jupyter Notebook Visualization of Images
if __name__ == "__main__":
    for ido in image_objects(): # ido is an Imaged Data Object
        
        print("---- meta data -----")
        print(ido.label)
        print(ido.source)
        print(ido.file)
        print(ido.format)
        print(ido.mode)
        print("Original size: ", ido.originalSize)
        print("Scaled size: ", ido.size)
        
        print("-- scaled image --")
        display(HTML(ido.html))
        
        print("--- grey image ---")
        display(HTML(ido.html_grey))
        
    print()
---- meta data -----
Joe
San Diego Humane Society
dogJoe.PNG
PNG
RGBA
Original size:  (1126, 723)
Scaled size:  (320, 205)
-- scaled image --
--- grey image ---
---- meta data -----
Bean
San Diego Humane Society
dogBean.PNG
PNG
RGBA
Original size:  (997, 732)
Scaled size:  (320, 234)
-- scaled image --
--- grey image ---
---- meta data -----
Harry
San Diego Humane Society
dogHarry.PNG
PNG
RGBA
Original size:  (994, 731)
Scaled size:  (320, 235)
-- scaled image --
--- grey image ---

Hacks

Early Seed award

  • Add this Blog to you own Blogging site.
  • In the Blog add a Happy Face image.
  • Have Happy Face Image open when Tech Talk starts, running on localhost. Don't tell anyone. Show to Teacher.

AP Prep

  • In the Blog add notes and observations on each code cell that request an answer.
  • In blog add College Board practice problems for 2.3
  • Choose 2 images, one that will more likely result in lossy data compression and one that is more likely to result in lossless data compression. Explain.

Project Addition

  • If your project has images in it, try to implement an image change that has a purpose. (Ex. An item that has been sold out could become gray scale)

Pick a programming paradigm and solve some of the following ...

  • Numpy, manipulating pixels. As opposed to Grey Scale treatment, pick a couple of other types like red scale, green scale, or blue scale. We want you to be manipulating pixels in the image.
  • Binary and Hexadecimal reports. Convert and produce pixels in binary and Hexadecimal and display.
  • Compression and Sizing of images. Look for insights into compression Lossy and Lossless. Look at PIL library and see if there are other things that can be done.
  • There are many effects you can do as well with PIL. Blur the image or write Meta Data on screen, aka Title, Author and Image size.

AP Prep

JPEG images are generally compressed using lossy compression, PNG files benefit from lossless compression so no matter how much you save, open, resize, they will keep the same quality. TIFF files allow users a choice between lossy or lossless. Lossless (text, spreadsheets), loss (graphics, audio)

JPEG:

PNG:

CB Quiz Questions

I got full points on the other cb quizzes, but I missed two questions on the Using Programs with Data Quiz

  1. The owner of a clothing store records the following information for each transaction made at the store during a 7-day period.

The date of the transaction The method of payment used in the transaction The number of items purchased in the transaction The total amount of the transaction, in dollars Customers can pay for purchases using cash, check, a debit card, or a credit card.

Using only the data collected during the 7-day period, which of the following statements is true?

I said C (The most expensive item purchased on a given date can be determined by searching the data for all items purchased on the given date and then sorting the matching items by item price.), D (The total number of items purchased on a given date can be determined by searching the data for all transactions that occurred on the given date and then adding the number of items purchased for each matching transaction.) is the correct answer.

Corrections: C is wrong because that information cannot be determined using the data collected. The data does not include information about the costs of individual items, so it is impossible to find the most expensive item purchased on a given date. For each transaction, the data includes the date of the transaction and the number of items purchased in the transaction. By searching the data to find all transactions that occurred on the given date, and then adding the number of items purchased in each of those transactions, the total number of items purchased on a given date can be determined so D is correct.

  1. I understood this question I just accidentally clicked three answers, i was only supposed to click two :)

from IPython.display import HTML, display
from pathlib import Path  # https://medium.com/@ageitgey/python-3-quick-tip-the-easy-way-to-deal-with-file-paths-on-windows-mac-and-linux-11a072b58d5f
from PIL import Image as pilImage # as pilImage is used to avoid conflicts
from io import BytesIO
import base64
import numpy as np

# prepares a series of images
def image_data(path=Path("images/"), images=None):  # path of static images is defaulted
    if images is None:  # default image
        images = [
            {'source': "San Diego Humane Society", 'label': "Joe", 'file': "dogJoe.PNG"},
            # {'source': "San Diego Humane Society", 'label': "Bean", 'file': "dogBean.PNG"},
            # {'source': "San Diego Humane Society", 'label': "Harry", 'file': "dogHarry.PNG"}
        ]
    for image in images:
        # File to open
        image['filename'] = path / image['file']  # file with path
    return images

# Large image scaled to baseWidth of 320
def scale_image(img):
    baseWidth = 320
    scalePercent = (baseWidth/float(img.size[0]))
    scaleHeight = int((float(img.size[1])*float(scalePercent)))
    scale = (baseWidth, scaleHeight)
    return img.resize(scale)

# PIL image converted to base64
def image_to_base64(img, format):
    with BytesIO() as buffer:
        img.save(buffer, format)
        return base64.b64encode(buffer.getvalue()).decode()

# Set Properties of Image, Scale, and convert to Base64
def image_management(image):  # path of static images is defaulted        
    # Image open return PIL image object
    img = pilImage.open(image['filename'])
    
    # Python Image Library operations
    image['format'] = img.format
    image['mode'] = img.mode
    image['size'] = img.size
    # Scale the Image
    img = scale_image(img)
    image['pil'] = img
    image['scaled_size'] = img.size
    # Scaled HTML
    image['html'] = '<img src="data:image/png;base64,%s">' % image_to_base64(image['pil'], image['format'])
    
# Create Grey Scale Base64 representation of Image
def image_management_add_html_red(image):
    # Image open return PIL image object
    img = image['pil']
    format = image['format']
    
    img_data = img.getdata()  # Reference https://www.geeksforgeeks.org/python-pil-image-getdata/
    image['data'] = np.array(img_data) # PIL image to numpy array
    image['red_data'] = [] # key/value for data converted to red scale

    # 'data' is a list of RGB data, the list is traversed and hex and binary lists are calculated and formatted
    for pixel in image['data']: #loops through pixels in the image 
        # create red scale of image
        if len(pixel) > 3:
            image['red_data'].append((pixel[0], 0, 0, pixel[3])) # PNG format
            #sets blue and green channels to 0
        # end for loop for pixels, creates new channel with only red channel present
        else:
            image['red_data'].append((pixel[0], 0, 0)) 
        
    img.putdata(image['red_data'])
    image['html_red'] = '<img src="data:image/png;base64,%s">' % image_to_base64(img, format)


# Jupyter Notebook Visualization of Images
if __name__ == "__main__":
    # Use numpy to concatenate two arrays
    images = image_data()
    
    # Display meta data, scaled view, and grey scale for each image
    for image in images:
        image_management(image)
        print("---- meta data -----")
        print(image['label'])
        print(image['source'])
        print(image['format'])
        print(image['mode'])
        print("Original size: ", image['size'])
        print("Scaled size: ", image['scaled_size'])
        
        print("-- original image --")
        display(HTML(image['html'])) 
        
        print("--- red image ----")
        image_management_add_html_red(image)
        display(HTML(image['html_red'])) 
    print()
---- meta data -----
Joe
San Diego Humane Society
PNG
RGBA
Original size:  (1126, 723)
Scaled size:  (320, 205)
-- original image --
--- red image ----

for pixel in image['data']: #loops through pixels in the image 
    # create green scale of image
    if len(pixel) > 3:
        image['green_data'].append((0, pixel[1], 0, pixel[3])) # PNG format
        #sets red and blue channels to 0, keeps green channel value as is
    # end for loop for pixels, creates new channel with only green channel present
    else:
        image['green_data'].append((0, pixel[1], 0))
for pixel in image['data']: #loops through pixels in the image 
    # create green scale of image
    if len(pixel) > 3:
        image['green_data'].append((0, 0, pixel[2], pixel[3])) # PNG format
        #sets red and blue channels to 0, keeps green channel value as is
    # end for loop for pixels, creates new channel with only green channel present
    else:
        image['green_data'].append((0, 0, pixel[2]))
from IPython.display import HTML, display
from pathlib import Path  # https://medium.com/@ageitgey/python-3-quick-tip-the-easy-way-to-deal-with-file-paths-on-windows-mac-and-linux-11a072b58d5f
from PIL import Image as pilImage # as pilImage is used to avoid conflicts
from PIL import ImageFilter
from io import BytesIO
import base64
import numpy as np

# prepares a series of images
def image_data(path=Path("images/"), images=None):  # specifies path to images folder # path of static images is defaulted
    if images is None:  # default image
        images = [
            {'source': "San Diego Humane Society", 'label': "Joe", 'file': "dogJoe.PNG"},
            # {'source': "San Diego Humane Society", 'label': "Bean", 'file': "dogBean.PNG"},
            # {'source': "San Diego Humane Society", 'label': "Harry", 'file': "dogHarry.PNG"}
        ]
    for image in images:
        # File to open
        image['filename'] = path / image['file']  # file with path
    return images

# Large image scaled to baseWidth of 320
def scale_image(img):
    baseWidth = 320
    scalePercent = (baseWidth/float(img.size[0]))
    scaleHeight = int((float(img.size[1])*float(scalePercent)))
    scale = (baseWidth, scaleHeight)
    return img.resize(scale)

# PIL image converted to base64
def image_to_base64(img, format):
    with BytesIO() as buffer:
        img.save(buffer, format)
        return base64.b64encode(buffer.getvalue()).decode()

# Set Properties of Image, Scale, and convert to Base64
def image_management(image):  # path of static images is defaulted        
    # Image open return PIL image object
    img = pilImage.open(image['filename'])
    
    # Python Image Library operations
    image['format'] = img.format
    image['mode'] = img.mode
    image['size'] = img.size
    # Scale the Image
    img = scale_image(img)
    image['pil'] = img
    image['scaled_size'] = img.size
    # Scaled HTML
    image['html'] = '<img src="data:image/png;base64,%s">' % image_to_base64(image['pil'], image['format'])
    
# Create Grey Scale Base64 representation of Image
def image_management_add_html_blur(image): #takes an image dictionary and adds a blurred version of the image to the dictionary.
    # Image open return PIL image object
    img = image['pil'].filter(ImageFilter.BLUR) #applying the blur filter using the PIL ImageFilter module

# Update image dictionary with blurred image
    image['pil_blur'] = img
    image['html_blur'] = '<img src="data:image/png;base64,%s">' % image_to_base64(image['pil_blur'], image['format'])
#html_blur' which stores a base64-encoded string representation of the blurred image in HTML format. 

# Jupyter Notebook Visualization of Images
if __name__ == "__main__":
    # Use numpy to concatenate two arrays
    images = image_data()
    
    # Display meta data, scaled view, and grey scale for each image
    for image in images:
        image_management(image)
        print("---- meta data -----")
        print(image['label'])
        
        print("-- original image --")
        display(HTML(image['html'])) 
        
        print("--- blurred image ----")
        image_management_add_html_blur(image)
        display(HTML(image['html_blur'])) 
    print()
---- meta data -----
Joe
-- original image --
--- blurred image ----