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?

Some commands that we use in the terminal include cd, nano, or even cat. The cd command brings us to the directory with the desired file. The nano command allows us to read and edit the desired file within that directory. The cat file allows us to only read the file.

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

Some commands in the Windows terminals that we used to access files include

  • What are some of the major differences?

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

  • Why is path a big deal when working with images?
  • How does the meta data source and label relate to Unit 5 topics?
  • Look up IPython, describe why this is interesting in Jupyter Notebooks for both Pandas and Images?
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': "Peter Carolin", '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
    green_square = image_data(images=[{'source': "Internet", 'label': "Green Square", 'file': "green-square-16.png"}])
    image_display(green_square)
    
    # 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.

  • Allows you to have text in your 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?

  • Translate first 3 letters of your name to Base64.

In Base 64, there are 64 possible characters, while in binary and hexadecimal, there are only 8 and 16 characters, respectively. My name: Emaad First three letters: Ema 45 6D 61 E M A

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?

I have been a consumer of buffering when it comes to transferring pictures from my camera and uploading them on to my Google Photos account. Buffering involves temporarily storiing data as one moves it from one place to another, so in that sense, the images that I want added to my Google Photos serve as the data being stored.

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

With buffering, I have been able to retain the original resolution of the desired images while still moving the image data from one place to another.

Effects

  • How do these effects apply to images?

In my experience, I have used buffers to change some attributes of the image, such as the size and scale, the format, the file type (png, jpg, heic, etc.) and many more. While I do end up getting the desired product, whether it was changing the size or file type, I have found that it can sometimes reduce the quality of the image slightly and make the image look a little "off" from the original. On the surface, it may not look like anything has changed, but when you look closely, one can begin to clearly notice the differences.

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, this code definitely acts as a series of steps being performed, as there are several functions and procedures tat go into making the image tinted grey.

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

The scale for each image changes depending on what it was initially, For example, originally, the green square image was smaller, but using the scale function, it was made much larger. With the scale for the second image, there was no visible change (may have been made slightly larger), however, for the third image (largest image), the image was shrunk down a lot.

  • Describe Grey Scale algorithm in English or Pseudo code?

(1) Import the necessary libraries, such as PIL, base64, and numpy.

(2) Create a list of images that you would like to make grey (or whatever the desired color is)

(3) Create functions that will resize/rescale the image however you want it (shrink it, enlarge it, keep it the same, etc.)

(4) Convert the images into base64

(5) Create the function that will play the main role of tinting the images grey

(6) For every pixel in the image, if the length of the pixel list is greater than 3, then the grey data will be appended and will be updated accordingly

(7) Print both the original image and the grey image with the different size, scale, etc. so that the user can see what changed between the two

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

Yes, scale image is a type of compression in that it is a lossless data compression. Lossless data compression involves resizing the images into a smaller version, which aligns with how you can scale the image in order to make it smaller while still retaining its data and resolution.

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': "Internet", 'label': "Green Square", 'file': "green-square-16.png"},
            {'source': "Peter Carolin", 'label': "Clouds Impression", 'file': "clouds-impression.png"},
            {'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "lassen-volcano.jpg"},
            {'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "smileyface.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 -----
Green Square
Internet
green-square-16.png
PNG
RGBA
Original size:  (16, 16)
Scaled size:  (320, 320)
-- scaled image --
--- grey image ---
---- meta data -----
Clouds Impression
Peter Carolin
clouds-impression.png
PNG
RGBA
Original size:  (320, 234)
Scaled size:  (320, 234)
-- scaled image --
--- grey image ---
---- meta data -----
Lassen Volcano
Peter Carolin
lassen-volcano.jpg
JPEG
RGB
Original size:  (2792, 2094)
Scaled size:  (320, 240)
-- scaled image --
--- grey image ---
---- meta data -----
Lassen Volcano
Peter Carolin
smileyface.png
PNG
RGBA
Original size:  (639, 517)
Scaled size:  (320, 258)
-- scaled 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
  • Consider how data is organized in two examples, in relations to procedures
  • Look at Parameters in Imperative and Self in OOP

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

  • PIL
  • numpy
  • base64
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': "Internet", 'label': "Green Square", 'file': "green-square-16.png"},
            {'source': "Peter Carolin", 'label': "Clouds Impression", 'file': "clouds-impression.png"},
            {'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "lassen-volcano.jpg"}
        ]
    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 -----
Green Square
Internet
green-square-16.png
PNG
RGBA
Original size:  (16, 16)
Scaled size:  (320, 320)
-- scaled image --
--- grey image ---
---- meta data -----
Clouds Impression
Peter Carolin
clouds-impression.png
PNG
RGBA
Original size:  (320, 234)
Scaled size:  (320, 234)
-- scaled image --
--- grey image ---
---- meta data -----
Lassen Volcano
Peter Carolin
lassen-volcano.jpg
JPEG
RGB
Original size:  (2792, 2094)
Scaled size:  (320, 240)
-- 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.

Seed Award

from PIL import Image
smile = Image.open('../images/smileyface.png')

display(smile)

AP Prep

College Board Practice Problems:

(1) Which of the following is an advantage of a lossless compression algorithm over a lossy compression algorithm?

Possible Answers:

(A) A lossless compression algorithm can guarantee that compressed information is kept secure, while a lossy compression algorithm cannot.

(B) A lossless compression algorithm can guarantee reconstruction of original data, while a lossy compression algorithm cannot.

(C) A lossless compression algorithm typically allows for faster transmission speeds than does a lossy compression algorithm.

(D) A lossless compression algorithm typically provides a greater reduction in the number of bits stored or transmitted than does a lossy compression algorithm.

(2) A user wants to save a data file on an online storage site. The user wants to reduce the size of the file, if possible, and wants to be able to completely restore the file to its original version. Which of the following actions best supports the user’s needs?

Possible Answers:

(A) Compressing the file using a lossless compression algorithm before uploading it

(B) Compressing the file using a lossy compression algorithm before uploading it

(C) Compressing the file using both lossy and lossless compression algorithms before uploading it

(D) Uploading the original file without using any compression algorithm

(3) A programmer is developing software for a social media platform. The programmer is planning to use compression when users send attachments to other users. Which of the following is a true statement about the use of compression?

Possible Answers:

(A) Lossless compression of video files will generally save more space than lossy compression of video files.

(B) Lossless compression of an image file will generally result in a file that is equal in size to the original file.

(C) Lossy compression of an image file generally provides a greater reduction in transmission time than lossless compression does.

(D) Sound clips compressed with lossy compression for storage on the platform can be restored to their original quality when they are played.

Project Addition

Red/Blue/Green Scaling

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)
    
    def image_to_html_red(self):
        img_red = self._img.copy()  # create a copy of the image
        numpy = np.array(self._img.getdata())

        # set green and blue channels to 0
        red_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 values and use // for integer division

            if len(pixel) > 3:
                red_data.append((pixel[0], 0, 0, pixel[3])) # PNG format
            else:
                red_data.append((pixel[0],0,0))
        
        # create a new image with the modified numpy array
        img_red.putdata(red_data)
        return self.image_to_html(img_red)

    def image_to_html_green(self):
        img_green = self._img.copy()
        numpy = np.array(self._img.getdata())

        # set red and blue channels to 0
        numpy[:, [0, 2]] = 0
        
        img_green.putdata(numpy.tolist())
        return self.image_to_html_green(img_green)

    def image_to_html_blue(self):
        img_blue = self._img.copy()
        numpy = np.array(self._img.getdata())

        # set red and green channels to 0
        numpy[:, :-1] = 0
        
        img_blue.putdata(numpy.tolist())
        return self.image_to_html_blue(img_blue)


    
    



        
# 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': "Internet", 'label': "Green Square", 'file': "green-square-16.png"},
            {'source': "Peter Carolin", 'label': "Clouds Impression", 'file': "clouds-impression.png"},
            {'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "lassen-volcano.jpg"},
            {'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "smileyface.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("--- red image ---")
        display(HTML(ido.image_to_html_red()))

        # print("--- green image ---")
        # display(HTML(ido.image_to_html_green()))

        # print("--- blue image ---")
        # display(HTML(ido.image_to_html_blue()))

        
    print()
---- meta data -----
Green Square
Internet
green-square-16.png
PNG
RGBA
Original size:  (16, 16)
Scaled size:  (320, 320)
-- scaled image --
--- grey image ---
--- red image ---
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb Cell 17 in <cell line: 175>()
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=190'>191</a>     display(HTML(ido.html_grey))
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=192'>193</a>     print("--- red image ---")
--> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=193'>194</a>     display(HTML(ido.image_to_html_red()))
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=195'>196</a>     # print("--- green image ---")
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=196'>197</a>     # display(HTML(ido.image_to_html_green()))
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=197'>198</a> 
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=198'>199</a>     # print("--- blue image ---")
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=199'>200</a>     # display(HTML(ido.image_to_html_blue()))
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=202'>203</a> print()

/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb Cell 17 in Image_Data.image_to_html_red(self)
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=119'>120</a> # create a new image with the modified numpy array
    <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=120'>121</a> img_red.putdata(red_data)
--> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/emaad/vscode/emaad-fastpages/_notebooks/2023-03-07-AP-unit2-2.ipynb#X46sdnNjb2RlLXJlbW90ZQ%3D%3D?line=121'>122</a> return self.image_to_html_red(img_red)

TypeError: image_to_html_red() takes 1 positional argument but 2 were given