Second ex refactored

main
Gasper Spagnolo 2022-10-22 17:35:47 +02:00
parent 37c80f45cf
commit 6ced34f623
2 changed files with 111 additions and 151 deletions

View File

@ -12,9 +12,9 @@ import UZ_utils as uz
def excercise_one() -> None: def excercise_one() -> None:
image = one_a() image = one_a()
#one_b(image) one_b(image)
#one_c(image) one_c(image)
#one_d(100, 200, 200, 400, image) one_d(100, 200, 200, 400, image)
one_e() one_e()
def one_a() -> npt.NDArray[np.float64]: def one_a() -> npt.NDArray[np.float64]:
@ -143,10 +143,10 @@ def excercise_two() -> None:
pixels in the source image is greater or lower than the given threshold. pixels in the source image is greater or lower than the given threshold.
""" """
two_a() two_a()
#two_b('./images/bird.jpg', 100, 20) two_b('./images/bird.jpg', 100, 20)
#two_c('./images/bird.jpg', 20, 100) two_c('./images/bird.jpg', 20, 100)
#two_d() two_d()
#two_e(uz.imread_gray('./images/bird.jpg', uz.ImageType.uint8).astype(np.uint8)) two_e(uz.imread_gray('./images/bird.jpg', uz.ImageType.uint8).astype(np.uint8))
def two_a() -> tuple[npt.NDArray[np.float64], npt.NDArray[np.uint8]]: def two_a() -> tuple[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
@ -180,48 +180,6 @@ def two_a() -> tuple[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
plt.show() plt.show()
return (image, binary_mask) return (image, binary_mask)
def my_hist_for_loop(image: npt.NDArray[np.float64], number_of_bins: int) -> npt.NDArray[np.float64]:
bin_restrictions = np.arange(0, 1, 1 / number_of_bins)
bins = np.zeros(number_of_bins).astype(np.float64)
for pixel in image.reshape(-1):
# https://stackoverflow.com/a/16244044
bins[np.argmax(bin_restrictions > pixel)] += 1
return bins / np.sum(bins)
# Much faster implementation than for loop
def my_hist(image: npt.NDArray[np.float64], number_of_bins: int, img_typ: uz.ImageType) -> npt.NDArray[np.float64]:
if img_typ == uz.ImageType.float64:
bins = np.arange(0, 1, 1 / number_of_bins)
elif img_typ == uz.ImageType.uint8:
bins = np.arange(0, 255, 255/number_of_bins)
# 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)
counts = binarray[1].astype(np.float64) # Get the counts out of tuple
# Check if there is any empty bin
empty_bins = []
bins = binarray[0]
for i in range(1, number_of_bins + 1):
if i not in bins:
empty_bins.append(i)
# Add empty bins with zeros
if empty_bins != []:
for i in empty_bins:
counts = np.insert(counts, i - 1, 0)
return counts / np.sum(counts)
def two_b(image_path: str, number_of_bins_first: int, number_of_bins_second: int) -> None: 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 Write a function myhist that accepts a grayscale image and the number of bins that
@ -239,20 +197,20 @@ def two_b(image_path: str, number_of_bins_first: int, number_of_bins_second: int
sum of all cells. Why is that? sum of all cells. Why is that?
Answer: Answer:
""" """
image = uz.imread_gray(image_path, uz.ImageType.uint8) image = uz_image.imread_gray(image_path, uz_image.ImageType.uint8)
H1 = my_hist(image, number_of_bins_first, uz.ImageType.uint8) H1 = uz_image.get_image_bins(image, number_of_bins_first)
H2 = my_hist(image, number_of_bins_second, uz.ImageType.uint8) H2 = uz_image.get_image_bins(image, number_of_bins_second)
fig, (ax0, ax1, ax2) = plt.subplots(1, 3) fig, axs = plt.subplots(1, 3)
fig.suptitle("Birdie and histgrams") fig.suptitle("Birdie and histograms")
ax0.imshow(image, cmap="gray") axs[0].imshow(image, cmap="gray")
ax0.set(title="Birdie image") axs[0].set(title="Birdie image")
ax1.bar(np.arange(number_of_bins_first), H1) axs[1].bar(np.arange(number_of_bins_first), H1)
ax1.set(title="100 bins") axs[1].set(title="100 bins")
ax2.bar(np.arange(number_of_bins_second), H2) axs[2].bar(np.arange(number_of_bins_second), H2)
ax2.set(title="20 bins") axs[2].set(title="20 bins")
plt.show() plt.show()
@ -264,14 +222,14 @@ def two_c(image_path: str, number_of_bins_first: int, number_of_bins_second: int
difference between both versions of the function. difference between both versions of the function.
""" """
image_uint8 = uz.imread_gray(image_path, uz.ImageType.uint8) image_uint8 = uz_image.imread_gray(image_path, uz_image.ImageType.uint8)
image_float64 = uz.imread_gray(image_path, uz.ImageType.float64) image_float64 = uz_image.imread_gray(image_path, uz_image.ImageType.float64)
H01 = my_hist(image_uint8, number_of_bins_first, uz.ImageType.uint8) H01 = uz_image.get_image_bins(image_uint8, number_of_bins_first)
H02 = my_hist(image_uint8, number_of_bins_second, uz.ImageType.uint8) H02 = uz_image.get_image_bins(image_uint8, number_of_bins_second)
H11 = my_hist(image_float64, number_of_bins_first, uz.ImageType.float64) H11 = uz_image.get_image_bins(image_float64, number_of_bins_first)
H12 = my_hist(image_float64, number_of_bins_second, uz.ImageType.float64) H12 = uz_image.get_image_bins(image_float64, number_of_bins_second)
fig, axs = plt.subplots(2, 3) fig, axs = plt.subplots(2, 3)
fig.suptitle("Comparison between two histograms") fig.suptitle("Comparison between two histograms")
@ -283,7 +241,7 @@ def two_c(image_path: str, number_of_bins_first: int, number_of_bins_second: int
axs[0, 2].bar(np.arange(number_of_bins_second), H02) axs[0, 2].bar(np.arange(number_of_bins_second), H02)
axs[0, 2].set(title=f'{number_of_bins_second} bins used') axs[0, 2].set(title=f'{number_of_bins_second} bins used')
axs[1, 0].imshow(image_float64, cmap="gray") axs[1, 0].imshow(image_uint8, cmap="gray")
axs[1, 0].set(title="Grayscale image in uint8 representation") axs[1, 0].set(title="Grayscale image in uint8 representation")
axs[1, 1].bar(np.arange(number_of_bins_first), H11) axs[1, 1].bar(np.arange(number_of_bins_first), H11)
axs[1, 1].set(title=f'{number_of_bins_first} bins used') axs[1, 1].set(title=f'{number_of_bins_first} bins used')
@ -298,49 +256,26 @@ def two_d() -> None:
your web camera and change the lighting of the room. Visualize the histograms for 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. all images for different number of bins and interpret the results.
""" """
light = uz.imread_gray("./images/ROOM_LIGHTS_ON.jpg", uz.ImageType.float64) imgs = []
darker = uz.imread_gray("./images/ONE_ROOM_LIGH_ON.jpg", uz.ImageType.float64) bins = [20, 60, 100]
dark = uz.imread_gray("./images/DARK.jpg", uz.ImageType.float64) imgs.append(uz_image.imread_gray("./images/ROOM_LIGHTS_ON.jpg", uz_image.ImageType.float64)) # light
imgs.append(uz_image.imread_gray("./images/ONE_ROOM_LIGH_ON.jpg", uz_image.ImageType.float64)) # darker
H10 = my_hist(light, 20, uz.ImageType.float64) imgs.append(uz_image.imread_gray("./images/DARK.jpg", uz_image.ImageType.float64)) # dark
H11 = my_hist(light, 60, uz.ImageType.float64)
H12 = my_hist(light, 100, uz.ImageType.float64)
H20 = my_hist(darker, 20, uz.ImageType.float64)
H21 = my_hist(darker, 60, uz.ImageType.float64)
H22 = my_hist(darker, 100, uz.ImageType.float64)
H30 = my_hist(dark, 20, uz.ImageType.float64)
H31 = my_hist(dark, 60, uz.ImageType.float64)
H32 = my_hist(dark, 100, uz.ImageType.float64)
fig, axs = plt.subplots(3, 4) fig, axs = plt.subplots(3, 4)
fig.suptitle("spanskiduh and histgrams") fig.suptitle("Me and my histograms")
axs[0, 0].imshow(light, cmap="gray") for i in range(3):
for j in range(3):
axs[i, j+1].bar(np.arange(bins[j]), uz_image.get_image_bins(imgs[i], bins[j]))
axs[i, j+1].set(title=f"Using {bins[j]} bins")
axs[0, 0].imshow(imgs[0], cmap="gray")
axs[0, 0].set(title="Image in light conditions") axs[0, 0].set(title="Image in light conditions")
axs[0, 1].bar(np.arange(20), H10) axs[1, 0].imshow(imgs[1], cmap="gray")
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, 0].set(title="Image in darker conditions")
axs[1, 1].bar(np.arange(20), H20) axs[2, 0].imshow(imgs[2], cmap="gray")
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, 0].set(title="Image in dark conditions")
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() plt.show()
@ -352,39 +287,7 @@ def two_e(image: npt.NDArray[np.uint8]):
shows the algorithms results on different images. shows the algorithms results on different images.
References: https://en.wikipedia.org/wiki/Otsu%27s_method References: https://en.wikipedia.org/wiki/Otsu%27s_method
""" """
treshold_range = np.arange(np.max(image) + 1) return uz_image.calculate_best_treshold_using_otsu_method(image)
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(f'best treshold is: {best_threshold}')
return best_threshold
###################################################### ######################################################
@ -392,7 +295,7 @@ def two_e(image: npt.NDArray[np.uint8]):
###################################################### ######################################################
def excercise_three() -> None: def excercise_three() -> None:
#three_a() three_a()
#mask1, _ = three_b() #mask1, _ = three_b()
#three_c(uz.imread('./images/bird.jpg', uz.ImageType.float64), mask1) #three_c(uz.imread('./images/bird.jpg', uz.ImageType.float64), mask1)
#three_d() #three_d()
@ -527,6 +430,9 @@ def three_d():
binary_mask = np.where(binary_mask < TRESHOLD, 0, 1) binary_mask = np.where(binary_mask < TRESHOLD, 0, 1)
binary_mask = uz.convert_float64_array_to_uint8_array(binary_mask) binary_mask = uz.convert_float64_array_to_uint8_array(binary_mask)
plt.imshow(binary_mask, cmap='gray')
plt.show()
# If I would invert image here, then we would get crap # If I would invert image here, then we would get crap
# So workaround: # So workaround:
SE_CROSS = cv2.getStructuringElement(cv2.MORPH_CROSS, (2, 2)) SE_CROSS = cv2.getStructuringElement(cv2.MORPH_CROSS, (2, 2))
@ -582,7 +488,7 @@ def three_e():
sizes = stats[:, -1] sizes = stats[:, -1]
for blob in range(n_blobs): for blob in range(n_blobs):
if sizes[blob] > 700: if sizes[blob] > COIN_SIZE:
binary_mask[im_with_separated_blobs == blob] = 0 binary_mask[im_with_separated_blobs == blob] = 0
three_c(coin_img_color, binary_mask) three_c(coin_img_color, binary_mask)

View File

@ -11,7 +11,7 @@ class ImageType(enum.Enum):
uint8 = 0 uint8 = 0
float64 = 1 float64 = 1
def imread(path: str, type: ImageType) -> npt.NDArray[np.float64] or npt.NDArray[np.uint8]: def imread(path: str, type: ImageType) -> Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
""" """
Reads an image in RGB order. Image type is transformed from uint8 to float, and Reads an image in RGB order. Image type is transformed from uint8 to float, and
range of values is reduced from [0, 255] to [0, 1]. range of values is reduced from [0, 255] to [0, 1].
@ -24,10 +24,10 @@ def imread(path: str, type: ImageType) -> npt.NDArray[np.float64] or npt.NDArray
elif type == ImageType.uint8: elif type == ImageType.uint8:
return I return I
raise Exception("Unrecognized image format!") raise Exception(f"Unrecognized image format! {type}")
def imread_gray(path: str, type: ImageType) -> npt.NDArray[np.float64] or npt.NDArray[np.uint8]: def imread_gray(path: str, type: ImageType) -> Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
""" """
Reads an image in gray. Image type is transformed from uint8 to float, and 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]. range of values is reduced from [0, 255] to [0, 1].
@ -85,7 +85,7 @@ def transform_coloured_image_to_grayscale(image: npt.NDArray[np.float64]) -> npt
return grayscale_image return grayscale_image
def invert_coloured_image_part(image: npt.NDArray[np.float64] or npt.NDArray[np.uint8], startx: int, endx: int, starty: int, endy: int) -> npt.NDArray[np.float64] or npt.NDArray[np.uint8]: def invert_coloured_image_part(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], startx: int, endx: int, starty: int, endy: int) -> Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
""" """
Accepts image, starting position end end position for axes x & y. Returns whole image with inverted part. Accepts image, starting position end end position for axes x & y. Returns whole image with inverted part.
""" """
@ -108,13 +108,13 @@ def invert_coloured_image_part(image: npt.NDArray[np.float64] or npt.NDArray[np.
raise Exception("Unrecognized image format!") raise Exception("Unrecognized image format!")
def invert_coloured_image(image: npt.NDArray[np.float64] or npt.NDArray[np.uint8]) -> npt.NDArray[np.float64] or npt.NDArray[np.uint8]: def invert_coloured_image(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]) -> Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]:
""" """
Accepts image and inverts it Accepts image and inverts it
""" """
return invert_coloured_image_part(image, 0, image.shape[0], 0, image.shape[1]) return invert_coloured_image_part(image, 0, image.shape[0], 0, image.shape[1])
def calculate_best_treshold_using_otsu_method(image: npt.NDArray[np.float64] or npt.NDArray[np.uint8]) -> int: def calculate_best_treshold_using_otsu_method(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]]) -> int:
""" """
Accepts image and returns best treshold using otsu method Accepts image and returns best treshold using otsu method
""" """
@ -159,3 +159,57 @@ def calculate_best_treshold_using_otsu_method(image: npt.NDArray[np.float64] or
best_threshold = treshold_range[np.argmin(criterias)] best_threshold = treshold_range[np.argmin(criterias)]
return best_threshold return best_threshold
def get_image_bins_for_loop(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], number_of_bins: int) -> npt.NDArray[np.float64]:
"""
Accepts image in the float64 format or uint8, returns normailzed
image bins, histogram
"""
if image.dtype.type == np.float64 or image.dtype.type == np.uint8:
bin_restrictions = np.linspace(np.min(image), np.max(image), num=number_of_bins)
else:
raise Exception("Unrecognized image format!")
bins = np.zeros(number_of_bins).astype(np.float64)
for pixel in image.reshape(-1):
# https://stackoverflow.com/a/16244044
bins[np.argmax(bin_restrictions > pixel)] += 1
return bins / np.sum(bins)
# Much faster implementation than for loop
def get_image_bins(image: Union[npt.NDArray[np.float64], npt.NDArray[np.uint8]], number_of_bins: int) -> npt.NDArray[np.float64]:
"""
Accepts image in the float64 format or uint8, returns normailzed
image bins, histogram
"""
if image.dtype.type == np.float64 or image.dtype.type == np.uint8:
bins = np.linspace(np.min(image), np.max(image), num=number_of_bins)
else:
raise Exception("Unrecognized image format!")
# 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)
counts = binarray[1].astype(np.float64) # Get the counts out of tuple
# Check if there is any empty bin
empty_bins = []
bins = binarray[0]
for i in range(1, number_of_bins + 1):
if i not in bins:
empty_bins.append(i)
# Add empty bins with zeros
if empty_bins != []:
for i in empty_bins:
counts = np.insert(counts, i - 1, 0)
return counts / np.sum(counts)