Update before destroyal
parent
10d19c1243
commit
7015bd48f7
|
@ -25,15 +25,22 @@ def imread(path: str) -> npt.NDArray[np.float64]:
|
|||
return I
|
||||
|
||||
|
||||
def imread_gray(path: str) -> npt.NDArray[np.float64]:
|
||||
def imread_gray(path: str, type: str) -> npt.NDArray[np.float64] or npt.NDArray[np.uint8]:
|
||||
"""
|
||||
Reads an image in gray. Image type is transformed from uint8 to float, and
|
||||
range of values is reduced from [0, 255] to [0, 1].
|
||||
"""
|
||||
|
||||
I = Image.open(path).convert('L') # PIL image opening and converting to gray.
|
||||
I = np.asarray(I) # Converting to Numpy array.
|
||||
I = I.astype(np.float64) / 255
|
||||
return I
|
||||
|
||||
if type == "float64":
|
||||
I = I.astype(np.float64) / 255
|
||||
return I
|
||||
elif type == "uint8":
|
||||
return I
|
||||
|
||||
raise Exception("Wrong image format picked!")
|
||||
|
||||
|
||||
def imshow(img: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8],], title=None) -> None:
|
||||
|
|
227
assignment1/]
227
assignment1/]
|
@ -1,227 +0,0 @@
|
|||
import UZ_utils as uz
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from matplotlib import pyplot as plt
|
||||
import random
|
||||
|
||||
#######################################
|
||||
# EXCERCISE 1: Basic image processing #
|
||||
#######################################
|
||||
|
||||
def excercise_one() -> None:
|
||||
image = one_a()
|
||||
one_b(image)
|
||||
one_c(image)
|
||||
one_d(100, 200, 50, 200, image)
|
||||
one_e()
|
||||
|
||||
def one_a() -> npt.NDArray[np.float64]:
|
||||
"""
|
||||
Read the image from the file umbrellas.jpg and display it
|
||||
"""
|
||||
image = uz.imread('./images/umbrellas.jpg')
|
||||
uz.imshow(image, 'Umbrellas')
|
||||
return image
|
||||
|
||||
def one_b(image: npt.NDArray[np.float64]) -> None:
|
||||
"""
|
||||
Convert the loaded image to grayscale. A very simple way of doing this is summing
|
||||
up the color channels and dividing the result by 3, effectively averaging the values.
|
||||
The issue, however, is that the sum easily reaches beyond the np.uint8 range. We
|
||||
can avoid that by casting the data to a floating point type. You can access a specific
|
||||
image channel using the indexing syntax like red = I[:,:,0].
|
||||
"""
|
||||
grayscale_image = np.zeros(image.shape[:2])
|
||||
|
||||
for i in range(image.shape[0]):
|
||||
for j in range(image.shape[1]):
|
||||
grayscale_image[i, j] = (image[i, j, 0] + image[i,j, 1] + image[i, j, 2]) / 3
|
||||
|
||||
uz.imshow(grayscale_image, 'Umbrellas grayscale')
|
||||
|
||||
def one_c(image: npt.NDArray[np.float64]) -> None:
|
||||
"""
|
||||
Cut and display a specific part of the loaded image. Extract only one of the channels
|
||||
so you get a grayscale image. You can do this by indexing along the first two axes,
|
||||
for instance: cutout=I[130:260, 240:450, 1]. You can display multiple images in
|
||||
a single window using plt.subplot().
|
||||
Grayscale images can be displayed using different mappings (on a RGB monitor,
|
||||
every value needs to be mapped to a RGB triplet). Pyplot defaults to a color map
|
||||
named viridis, but often it is preferable to use a grayscale color map. This can be set
|
||||
with an additional argument to plt.imshow, like plt.imshow(I, cmap=’gray’).
|
||||
Question: Why would you use different color maps?
|
||||
Answer:
|
||||
"""
|
||||
uz.imshow(image[50:200, 100:400, 2], "Just one piece of umbrellas")
|
||||
|
||||
def one_d(startx: int, endx: int, starty: int, endy: int, image:npt.NDArray[np.float64]) -> None:
|
||||
"""
|
||||
(height, width, color)
|
||||
(x , y , color)
|
||||
y ->
|
||||
################# x
|
||||
# # |
|
||||
# # v
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
#################
|
||||
You can also replace only a part of the image using indexing. Write a script that
|
||||
inverts a rectangular part of the image. This can be done pixel by pixel in a loop or
|
||||
by using indexing.
|
||||
Question: How is inverting a grayscale value defined for uint8 ?
|
||||
Answer:
|
||||
"""
|
||||
inverted_image = image.copy()
|
||||
|
||||
for i in range(startx, endx):
|
||||
for j in range(starty, endy):
|
||||
inverted_image[i, j, 0] = 1 - image[i, j, 0]
|
||||
inverted_image[i, j, 1] = 1 - image[i, j, 1]
|
||||
inverted_image[i, j, 2] = 1 - image[i, j, 2]
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(1, 2)
|
||||
fig.suptitle("Lomberlini")
|
||||
|
||||
|
||||
ax0.imshow(image, cmap="gray")
|
||||
ax1.imshow(inverted_image, vmax=255, cmap="gray")
|
||||
|
||||
ax0.set(title="Original image")
|
||||
ax1.set(title="Inverted image")
|
||||
|
||||
plt.show()
|
||||
|
||||
def one_e() -> None:
|
||||
"""
|
||||
Perform a reduction of grayscale levels in the image. First read the image from
|
||||
umbrellas.jpg and convert it to grayscale. You can write your own function for
|
||||
grayscale conversion or use the function in UZ_utils.py.
|
||||
Convert the grayscale image to floating point type. Then, rescale the image values
|
||||
so that the largest possible value is 63. Convert the image back to uint8 and display
|
||||
both the original and the modified image. Notice that both look the same. Pyplot
|
||||
tries to maximize the contrast in displayed images by checking their values and
|
||||
scaling them to cover the entire uint8 interval. If you want to avoid this, you need
|
||||
to set the maximum expected value when using plt.imshow(), like plt.imshow(I,
|
||||
vmax=255. Use this to display the resulting image so the change is visible.
|
||||
"""
|
||||
grayscale_image = uz.imread_gray("./images/umbrellas.jpg")
|
||||
upscaled_grayscale_image = (grayscale_image.copy() * 63).astype(np.uint8)
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(1, 2)
|
||||
fig.suptitle("Lomberlini")
|
||||
|
||||
|
||||
ax0.imshow(grayscale_image, cmap="gray")
|
||||
ax1.imshow(upscaled_grayscale_image, vmax=255, cmap="gray")
|
||||
|
||||
ax0.set(title="Original grayscale image")
|
||||
ax1.set(title="Upscaled grayscale image")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
############################################
|
||||
# EXCERCISE 2: Thresholding and histograms #
|
||||
############################################
|
||||
|
||||
def excercise_two() -> None:
|
||||
"""
|
||||
Thresholding an image is an operation that produces a binary image (mask) of the same
|
||||
size where the value of pixels is determined by whether the value of the corresponding
|
||||
pixels in the source image is greater or lower than the given threshold.
|
||||
"""
|
||||
#two_a()
|
||||
two_b('./images/bird.jpg', 10, 20)
|
||||
|
||||
def two_a() -> tuple[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
|
||||
"""
|
||||
Create a binary mask from a grayscale image. The binary mask is a matrix the same
|
||||
size as the image which contains 1 where some condition holds and 0 everywhere
|
||||
else. In this case the condition is simply the original image intensity. Use the image
|
||||
bird.jpg. Display both the image and the mask.
|
||||
"""
|
||||
random_number = random.random()
|
||||
TRESHOLD = 0.4
|
||||
|
||||
image = uz.imread_gray("./images/bird.jpg")
|
||||
binary_mask = image.copy()
|
||||
|
||||
if random_number < 0.5:
|
||||
binary_mask[binary_mask < TRESHOLD] = 0
|
||||
binary_mask[binary_mask >= TRESHOLD] = 1
|
||||
else:
|
||||
binary_mask = np.where(binary_mask < TRESHOLD, 0, 1)
|
||||
|
||||
binary_mask = uz.convert_float64_array_to_uint8_array(binary_mask)
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(1, 2)
|
||||
fig.suptitle("Birdie and its mask")
|
||||
|
||||
|
||||
ax0.imshow(image, cmap="gray")
|
||||
ax1.imshow(binary_mask, cmap="gray")
|
||||
|
||||
ax0.set(title="Original image")
|
||||
ax1.set(title="Mask of birdie")
|
||||
|
||||
plt.show()
|
||||
return (image, binary_mask)
|
||||
|
||||
def my_hist(image: npt.NDArray[np.float64], number_of_bins: int): #-> npt.NDArray[np.uint64]:
|
||||
lspace = np.linspace(np.min(image), np.max(image), number_of_bins)
|
||||
print(lspace)
|
||||
bins = np.arange(0, 1, 1 / number_of_bins)
|
||||
print(bins)
|
||||
for pixel in image.reshape(-1):
|
||||
print(pixel)
|
||||
exit(0)
|
||||
#Put pixels into classes
|
||||
# ex. binsize = 10 then 0.4 would map into 4
|
||||
#binarray = np.digitize(image.reshape(-1), bins).astype(np.uint8)
|
||||
# Now count those values
|
||||
#binarray = np.unique(binarray, return_counts=True)
|
||||
#print(binarray)
|
||||
#binarray = binarray[1].astype(np.uint64)
|
||||
#return binarray
|
||||
|
||||
|
||||
def two_b(image_path: str, number_of_bins_first: int, number_of_bins_second: int) -> None:
|
||||
"""
|
||||
Write a function myhist that accepts a grayscale image and the number of bins that
|
||||
will be used in building a histogram. The function should return a 1D array that
|
||||
represents the image histogram (the size should be equal to the number of bins, of
|
||||
course).
|
||||
The histogram is simply a count of pixels with same (or similar) intensity for all
|
||||
bins. You can assume the values of the image are within the interval [0,255]. If you
|
||||
use fewer than 255 bins, intensities will have to be grouped together, e.g. if using 10
|
||||
bins, all values on the interval [0,25] will fall into bin 0.
|
||||
|
||||
Write a script that calculates and displays histograms for different numbers of bins
|
||||
using bird.jpg
|
||||
"""
|
||||
image = uz.imread_gray(image_path)
|
||||
|
||||
|
||||
H1 = my_hist(image, number_of_bins_first)
|
||||
H2 = my_hist(image, number_of_bins_second)
|
||||
|
||||
|
||||
fig, (ax0, ax1, ax2) = plt.subplots(1, 3)
|
||||
fig.suptitle("Birdie and histgrams")
|
||||
|
||||
|
||||
ax0.imshow(image, cmap="gray")
|
||||
ax1.hist(H1, bins=number_of_bins_first)
|
||||
ax2.hist(H2, bins=number_of_bins_second)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
#excercise_one()
|
||||
excercise_two()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -3,6 +3,7 @@ import numpy as np
|
|||
import numpy.typing as npt
|
||||
from matplotlib import pyplot as plt
|
||||
import random
|
||||
from PIL import Image
|
||||
|
||||
#######################################
|
||||
# EXCERCISE 1: Basic image processing #
|
||||
|
@ -10,9 +11,9 @@ import random
|
|||
|
||||
def excercise_one() -> None:
|
||||
image = one_a()
|
||||
one_b(image)
|
||||
one_c(image)
|
||||
one_d(100, 200, 50, 200, image)
|
||||
#one_b(image)
|
||||
#one_c(image)
|
||||
#one_d(100, 200, 50, 200, image)
|
||||
one_e()
|
||||
|
||||
def one_a() -> npt.NDArray[np.float64]:
|
||||
|
@ -105,7 +106,7 @@ def one_e() -> None:
|
|||
to set the maximum expected value when using plt.imshow(), like plt.imshow(I,
|
||||
vmax=255. Use this to display the resulting image so the change is visible.
|
||||
"""
|
||||
grayscale_image = uz.imread_gray("./images/umbrellas.jpg")
|
||||
grayscale_image = uz.imread_gray("./images/umbrellas.jpg", "float64")
|
||||
upscaled_grayscale_image = (grayscale_image.copy() * 63).astype(np.uint8)
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(1, 2)
|
||||
|
@ -132,8 +133,9 @@ def excercise_two() -> None:
|
|||
pixels in the source image is greater or lower than the given threshold.
|
||||
"""
|
||||
#two_a()
|
||||
#two_b('./images/bird.jpg', 100, 20)
|
||||
two_b('./images/bird.jpg', 100, 20)
|
||||
two_d()
|
||||
two_e(uz.imread_gray("./images/bird.jpg", "float64"))
|
||||
|
||||
|
||||
def two_a() -> tuple[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
|
||||
|
@ -146,7 +148,7 @@ def two_a() -> tuple[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
|
|||
random_number = random.random()
|
||||
TRESHOLD = 0.4
|
||||
|
||||
image = uz.imread_gray("./images/bird.jpg")
|
||||
image = uz.imread_gray("./images/bird.jpg", "float64")
|
||||
binary_mask = image.copy()
|
||||
|
||||
if random_number < 0.5:
|
||||
|
@ -222,7 +224,7 @@ def two_b(image_path: str, number_of_bins_first: int, number_of_bins_second: int
|
|||
Write a script that calculates and displays histograms for different numbers of bins
|
||||
using bird.jpg
|
||||
"""
|
||||
image = uz.imread_gray(image_path)
|
||||
image = uz.imread_gray(image_path, "float64")
|
||||
|
||||
|
||||
H1 = my_hist(image, number_of_bins_first)
|
||||
|
@ -240,41 +242,104 @@ def two_b(image_path: str, number_of_bins_first: int, number_of_bins_second: int
|
|||
|
||||
plt.show()
|
||||
|
||||
def two_c():
|
||||
assert("to be implemented", "to be implemented")
|
||||
def two_c() -> None:
|
||||
print("to be implemented", "to be implemented")
|
||||
|
||||
def two_d():
|
||||
def two_d() -> None:
|
||||
"""
|
||||
Test myhist function on images (three or more) of the same scene in
|
||||
different lighting conditions. One way to do this is to capture several images using
|
||||
your web camera and change the lighting of the room. Visualize the histograms for
|
||||
all images for different number of bins and interpret the results.
|
||||
"""
|
||||
light = uz.imread_gray("./images/ROOM_LIGHTS_ON.jpg")
|
||||
darker = uz.imread_gray("./images/ONE_ROOM_LIGH_ON.jpg")
|
||||
dark = uz.imread_gray("./images/DARK.jpg")
|
||||
light = uz.imread_gray("./images/ROOM_LIGHTS_ON.jpg", "float64")
|
||||
darker = uz.imread_gray("./images/ONE_ROOM_LIGH_ON.jpg", "float64")
|
||||
dark = uz.imread_gray("./images/DARK.jpg", "float64")
|
||||
|
||||
H1 = my_hist(light, 100)
|
||||
H2 = my_hist(darker, 100)
|
||||
H3 = my_hist(dark, 100)
|
||||
H10 = my_hist(light, 20)
|
||||
H11 = my_hist(light, 60)
|
||||
H12 = my_hist(light, 100)
|
||||
H20 = my_hist(darker, 20)
|
||||
H21 = my_hist(darker, 60)
|
||||
H22 = my_hist(darker, 100)
|
||||
H30 = my_hist(dark, 20)
|
||||
H31 = my_hist(dark, 60)
|
||||
H32 = my_hist(dark, 100)
|
||||
|
||||
fig, axs = plt.subplots(3, 2)
|
||||
fig, axs = plt.subplots(3, 4)
|
||||
fig.suptitle("spanskiduh and histgrams")
|
||||
|
||||
axs[0, 0].imshow(light, cmap="gray")
|
||||
axs[0, 0].set(title="Image in light conditions")
|
||||
axs[0, 1].bar(np.arange(100), H1)
|
||||
axs[0, 1].bar(np.arange(20), H10)
|
||||
axs[0, 1].set(title="Using 20 bins")
|
||||
axs[0, 2].bar(np.arange(60), H11)
|
||||
axs[0, 2].set(title="Using 60 bins")
|
||||
axs[0, 3].bar(np.arange(100), H12)
|
||||
axs[0, 3].set(title="Using 100 bins")
|
||||
|
||||
axs[1, 0].imshow(darker, cmap="gray")
|
||||
axs[1, 0].set(title="Image in darker conditions")
|
||||
axs[1, 1].bar(np.arange(100), H2)
|
||||
axs[1, 1].bar(np.arange(20), H20)
|
||||
axs[1, 1].set(title="Using 20 bins")
|
||||
axs[1, 2].bar(np.arange(60), H21)
|
||||
axs[1, 2].set(title="Using 60 bins")
|
||||
axs[1, 3].bar(np.arange(100), H22)
|
||||
axs[1, 3].set(title="Using 100 bins")
|
||||
|
||||
axs[2, 0].imshow(dark, cmap="gray")
|
||||
axs[2, 0].set(title="Image in dark conditions")
|
||||
axs[2, 1].bar(np.arange(100), H3)
|
||||
axs[2, 1].bar(np.arange(20), H30)
|
||||
axs[2, 1].set(title="Using 20 bins")
|
||||
axs[2, 2].bar(np.arange(60), H31)
|
||||
axs[2, 2].set(title="Using 60 bins")
|
||||
axs[2, 3].bar(np.arange(100), H32)
|
||||
axs[2, 3].set(title="Using 100 bins")
|
||||
|
||||
plt.show()
|
||||
|
||||
def two_e(image: npt.NDArray[np.float64]) -> None:
|
||||
"""
|
||||
Implement Otsu’s method for automatic threshold calculation. It
|
||||
should accept a grayscale image and return the optimal threshold. Using normalized
|
||||
histograms, the probabilities of both classes are easy to calculate. Write a script that
|
||||
shows the algorithm’s results on different images.
|
||||
References: https://en.wikipedia.org/wiki/Otsu%27s_method
|
||||
"""
|
||||
treshold_range = np.arange(np.max(image) + 1)
|
||||
criterias = []
|
||||
|
||||
for treshold in treshold_range:
|
||||
# create the thresholded image
|
||||
thresholded_im = np.zeros(image.shape)
|
||||
|
||||
thresholded_im[image >= treshold] = 1
|
||||
|
||||
# compute weights
|
||||
nb_pixels = image.size
|
||||
nb_pixels1 = np.count_nonzero(thresholded_im)
|
||||
weight1 = nb_pixels1 / nb_pixels
|
||||
weight0 = 1 - weight1
|
||||
|
||||
# if one the classes is empty, eg all pixels are below or above the threshold, that threshold will not be considered
|
||||
# in the search for the best threshold
|
||||
if weight1 == 0 or weight0 == 0:
|
||||
continue
|
||||
|
||||
# find all pixels belonging to each class
|
||||
val_pixels1 = image[thresholded_im == 1]
|
||||
val_pixels0 = image[thresholded_im == 0]
|
||||
|
||||
# compute variance of these classes
|
||||
var0 = np.var(val_pixels0) if len(val_pixels0) > 0 else 0
|
||||
var1 = np.var(val_pixels1) if len(val_pixels1) > 0 else 0
|
||||
|
||||
criterias.append( weight0 * var0 + weight1 * var1)
|
||||
|
||||
best_threshold = treshold_range[np.argmin(criterias)]
|
||||
print(best_threshold)
|
||||
print("h")
|
||||
|
||||
def main() -> None:
|
||||
#excercise_one()
|
||||
excercise_two()
|
||||
|
|
Loading…
Reference in New Issue