Unit 2.2 Data Compression, Images
- Enumerate "Data" Big Idea from College Board
- Image Files and Size
- Python Libraries and Concepts used for Jupyter and Files/Directories
- Reading and Encoding Images (2 implementations follow)
- Data Structures, Imperative Programming Style, and working with Images
- Data Structures and OOP
- Additionally, review all the imports in these three demos. Create a definition of their purpose, specifically these ...
- Hacks
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"
My Takeaway:Data compression, especially for images, is a useful tool to minimize file size and loading times when viewing and testing projects, websites, and other finished CS work. 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
The type of image type used for each image has drastically different file sizes, which provide alternatives for users who need their images to take up less space.
- Size, height and width, number of pixels
The actual dimensions and # of pixels used in an image have a direct impact on the size of the file, less pixels and smaller dimensions equate to a smaller file size.
- Visual perception, lossy compression
Compression or other methods of image manipulation can reduce the size of image files as well, providing another method for changing image size for users who don't want to change other qualities.
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?
In default terminal, you would use a combination of 'ls' and 'cd' commands under the C:\Users\ workspace.
- What are the command you use in Windows terminal to access files?
In windows terminal (wsl), I would use both the 'ls' and 'cd' commands under (base) to find and select the correct file, then use a command such as 'code .' in order to view or access the file (in this case, opening up the file in VsCode.)
- What are some of the major differences?
I actually didn't know the differences between terminal and WSL, so I consulted the internet and learned that the main differences between the two include how the terminal is a program that provides a command-line interface for interacting with a computer's operating system, while Windows Terminal (WSL) is a modern, customizable, and feature-rich terminal application for Windows 10 that supports multiple command-line environments, including Windows PowerShell, Command Prompt, and Windows Subsystem for Linux.
Provide what you observed, struggled with, or leaned while playing with this code.
- Why is path a big deal when working with images?
Path is a big deal when working with images because its extremely important to define and locate image locations in order to fetch images in code programs. Without a specified, correct path, your images could break or not load.
- How does the meta data source and label relate to Unit 5 topics?
The relationship between metadata and labels in Unit 5 topics is that they are both used to organize and manage data. Metadata provides information about the data, while labels help to categorize and identify the data. Both metadata and labels can be used to improve the efficiency and effectiveness of algorithms, as well as to improve the organization and readability of code.
- Look up IPython, describe why this is interesting in Jupyter Notebooks for both Pandas and Images?
In Pandas, IPython provides a number of useful features that make data analysis more efficient and effective. For example, IPython provides tab completion, which allows users to quickly access and manipulate data within a Pandas DataFrame.
When working with images, IPython provides many powerful tools for visualization and manipulation. IPython provides a variety of image processing functions, such as resizing, cropping, and filtering. IPython also provides support for displaying images in specific lines within the notebook, which can be useful for visualizing the effects of image processing operations.
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': "Happy Face", 'file': "images.png"},
{'source': "Peter Carolin", 'label': "Lassen Volcano", 'file': "lassen-volcano.jpg"}
]
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.
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 used for low-level representation of data with only zeros and ones, hexadecimal is used for human-readable representation of binary data with base-16 digit notation, and Base64 is used for encoding binary data as text through its use of a set-64 characters.
- Translate first 3 letters of your name to Base64.
Convert 'Jef' to binary, combine the binary, divide the binary into groups of 6 bits, convert the groups into Base64 Notation, final product is 'S1c='
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 whenever I am consuming media, such as playing videogames, listening to music, or watching videos on the internet.
- From your consumer experience, what effects have you experienced from buffering?
When experiencing buffering as a consumer, I feel mildly agitated and annoyed at the issue, because in may situations the buffering is completely out of my control, and I cannot do anything else but wait for the buffering to end.
- How do these effects apply to images?
These effects apply to images through the time it takes to render and load images onto a platform or website. If there is a lengthy amount of buffering, the overall user experiences would be worsened.
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 seems like a series of steps are being performed. The code defines several functions that perform specific tasks such as loading images, scaling images, converting images to base64 format, setting image properties, and creating a grayscale representation of an image. The code follows a logical sequence of steps to achieve its goal of displaying multiple images in a Jupyter Notebook.
- Describe Grey Scale algorithm in English or Pseudo code?
The basic idea behind grayscale conversion is to remove the color information from the image and represent it as shades of gray that correspond to the intensity of each pixel. The algorithm works by calculating the average intensity of the three RGB values (red, green, and blue) for each pixel, and then setting all three values to the average value.
- Describe scale image? What is before and after on pixels in three images?
The scale_image function takes a PIL image object as input and scales it to a new width of 320 pixels, while preserving its aspect ratio. The function returns a new PIL image object that has been resized.
- Is scale image a type of compression? If so, line it up with College Board terms described?
Scaling an image is a type of lossy compression since it involves reducing the resolution of the original image, resulting in a loss of some details. Lossy compression techniques are commonly used to reduce the size of digital images while maintaining an acceptable level of visual quality.
In terms of College Board terms, scaling an image would fall under the category of image compression, which is a topic covered in the AP Computer Science Principles curriculum. The curriculum covers both lossless and lossy compression techniques, including the trade-offs between image quality and file size.
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': "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"}
]
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()
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
The Python Imaging Library (PIL) is a library for working with digital images in Python. It provides a range of functions for opening, manipulating, and saving images in various formats, including BMP, GIF, JPEG, PNG, and TIFF. PIL provides a range of image processing capabilities, including resizing, cropping, rotating, filtering, and enhancing images. PIL also provides a number of utilities for working with image data, including the ability to convert images to and from different data types and formats.
- numpy
NumPy is a Python library used for working with arrays. It provides tools for working with multidimensional arrays and matrices, along with a large collection of mathematical functions to operate on these arrays. NumPy is widely used in the scientific computing and data science communities, particularly for tasks such as numerical analysis, data manipulation, and machine learning.
- base64
Base64 is a method for encoding binary data as ASCII text. It works by converting each group of 3 bytes (a total of 24 bits) of binary data into 4 ASCII characters from a limited set of 64 characters. Base64 encoding is commonly used for encoding images, audio, and other binary data for transmission over the internet.
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()
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.
Image 1:Beautiful Mountain Sunset
This type of image is more likely to result in lossy data compression. This is because photographs of natural scenery tend to have a large number of colors and fine details, such as texture in the clouds, trees, or waves. These details are difficult to compress without losing some information. An algorithm may group similar colors together, resulting in a loss of detail and color accuracy. Similarly, if the algorithm tries to smooth out the image by removing some fine details, this may also result in a loss of quality.
Image 2: Simple Black Line Drawings
This type of image is more likely to result in lossless data compression. This is because simple graphics or line drawings tend to have a limited number of colors and very little texture or fine details. These images can be compressed without losing any information by using lossless compression algorithms such as run-length encoding or Huffman coding. These algorithms can identify and remove any redundant or repetitive information in the image while preserving the original data, resulting in a smaller file size without any loss of quality.
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.
from IPython.display import HTML, display
from pathlib import Path
from PIL import Image as pilImage, ImageOps
from io import BytesIO
import base64
import numpy as np
# prepares a series of images
def image_data(path=Path("images/"), images=None):
if images is None:
images = [
{'source': "Internet", 'label': "Train", 'file': "train.jpg"}
]
for image in images:
# File to open
image['filename'] = path / image['file']
return images
# scale image 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)
# rotate image to be right-side up
def rotate_image(img):
# use Exif data to determine orientation
try:
orientation = img._getexif().get(0x112, 1)
except:
orientation = 1
if orientation == 1:
# no rotation necessary
return img.rotate(-90)
elif orientation == 3:
# rotate 180 degrees
return img.rotate(180)
elif orientation == 6:
# rotate 270 degrees clockwise
return img.rotate(-90)
elif orientation == 8:
# rotate 90 degrees clockwise
return img.rotate(90)
# 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, Rotate, and convert to Base64
def image_management(image):
# 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_scaled'] = img
image['scaled_size'] = img.size
# Rotate the Image
img = rotate_image(img)
image['pil_rotated'] = img
# Set up HTML display
image['html'] = '<img src="data:image/png;base64,%s">' % image_to_base64(image['pil_rotated'], image['format'])
# Jupyter Notebook Visualization of Images
if __name__ == "__main__":
# Use numpy to concatenate two arrays
images = image_data()
# Display meta data, scaled view, and rotated view 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("-- rotated image --")
display(HTML(image['html']))
from PIL import Image, ImageFilter
# Load the image
image_path = "images/leaf.jpg"
image = Image.open(image_path)
# Apply a blur filter
blurred_image = image.filter(ImageFilter.BLUR)
# Show the blurred image
blurred_image.show()