1. Read, show, and save imgs

import cv2

import numpy as np
from PIL import Image

from tensorflow.keras.preprocessing.image import load_img, img_to_array, array_to_img

import matplotlib.pyplot as plt

1.1. read & show

1.1.1. 彩色圖片

  • 常見的讀檔套件比較

package

read function

show function

save function

type

channel

dimension (color img)

dimension (gray img)

value range

value type

plt

import matplotlib.pyplot as plt

img_array = plt.imread(“xx.jpg”)

plt.show(img_array)

plt.imsave(‘xx.png’, img_array)

ndarray

R, G, B

(height, width, channel)

(height, width)

[0-255]

uint8

PIL

import np
from PIL import Image

img_obj = Image.open(“xx.jpg”)
img_array = np.asarray(img_obj)

img

plt.imshow(img_array)

img_obj.save(“xx.jpeg”)

img_obj = Image.fromarray(img_array)
img_obj.save(“xx.jpeg”)




PIL
ndarray

R, G, B

(height, width, channel)

(height, width, channel)
# channel = 3
# same value

[0-255]

uint8

tf.keras

from tensorflow.keras.preprocessing.image import
load_img, img_to_array, array_to_img

img_obj = load_img(“xx.jpg”)
img_array = img_to_array(img_obj)

img
plt.imshow(img_array)

img_obj.save(“xx.jpeg”)

img_obj = array_to_img(img_array)
img_obj.save(“xx.jpeg”)



PIL
ndarray

R, G, B

(height, width, channel)

(height, width, channel)

[0.-255.]

float32

cv2

import cv2
img_array = cv2.imread(“xx.jpg”)

plt.imshow(img_array)

cv2.imshow(“window_name”, img_array)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.imwrite(‘xx.png’, img_array)

ndarray

B, G, R

(height, width, channel)

(height, width, channel)

[0-255]

uint8

  • 可以看到 3 個重點:

    • channel 的順序,只有 CV2 是 BGR, 其他全都是 RGB

    • value range: 只有 pytorch 讀進來會轉換成多少,其他全都是 0~255

    • value type 只有 tf.keras 轉完是 float32, 其他都是 uint8

1.1.1.1. plt.imread

import matplotlib.pyplot as plt
plt_img = plt.imread("imgs/fruits-2.jpg")
print(f"read image type: {type(plt_img)}")
print(f"read image data type: {plt_img.dtype}")
print(f"read image data shape: {plt_img.shape}")
read image type: <class 'numpy.ndarray'>
read image data type: uint8
read image data shape: (417, 626, 3)
  • 來看一下圖片長怎樣

plt.imshow(plt_img);
_images/read_show_save_imgs_12_0.png

1.1.1.2. PIL, tf.keras

import numpy as np
from PIL import Image
pil_img = Image.open("imgs/fruits-2.jpg")
print(f"read image type: {type(pil_img)}")
read image type: <class 'PIL.JpegImagePlugin.JpegImageFile'>
pil_img_array = np.asarray(pil_img)
print(f"read image type: {type(pil_img_array)}")
print(f"read image data type: {pil_img_array.dtype}")
print(f"read image data shape: {pil_img_array.shape}")
read image type: <class 'numpy.ndarray'>
read image data type: uint8
read image data shape: (417, 626, 3)
  • keras 是直接沿用 PIL,所以結果都和剛剛一樣:

keras_img = load_img("imgs/fruits-2.jpg")
print(f"read image type: {type(keras_img)}")
read image type: <class 'PIL.JpegImagePlugin.JpegImageFile'>
keras_img_array = img_to_array(keras_img)
print(f"read image type: {type(keras_img_array)}")
print(f"read image data type: {keras_img_array.dtype}")
print(f"read image data shape: {keras_img_array.shape}")
read image type: <class 'numpy.ndarray'>
read image data type: float32
read image data shape: (417, 626, 3)
  • 可以看到,keras 的 data type 是 float32

  • 驗證一下,兩個套件讀出來的 numpy array 完全一樣 (注意: integer 255, 和 float 255.0,比較時會視為一樣):

(keras_img_array == pil_img_array).all()
True
  • 看一下圖

plt.imshow(keras_img_array.astype("uint8"))
<matplotlib.image.AxesImage at 0x159f9ff10>
_images/read_show_save_imgs_27_1.png

1.1.1.3. cv2

cv2_img = cv2.imread("imgs/fruits-2.jpg")
print(f"read image type: {type(cv2_img)}")
print(f"read image data type: {cv2_img.dtype}")
print(f"read image data shape: {cv2_img.shape}")
read image type: <class 'numpy.ndarray'>
read image data type: uint8
read image data shape: (417, 626, 3)
  • 可以看到,shape 和剛剛一樣

  • 但他的第三個軸,是 GBR,不是 RGB,所以一樣用 plt.imshow() 來畫圖時,可以看出差異

plt.imshow(cv2_img);
_images/read_show_save_imgs_32_0.png
  • 原本的紅色,都變藍色了,就是因為 R 和 B 的 channel 調換了,而 plt.imshow() 是以為你輸入的影像是 RGB

  • 如果要用 plt.imshow() 來顯示正確顏色的話,就要把原本的 array 的通道做調整,例如這樣

plt.imshow(cv2_img[:,:,::-1]); # 對通道那個軸,取最後到最前 (::-1),就把 BGR 變成 RGB 了
_images/read_show_save_imgs_34_0.png
  • 或是,你要用 cv2 裡的 function 也可以

img_rgb = cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb);
_images/read_show_save_imgs_36_0.png
  • 那,最後一種做法,是直接用 cv2 的 function 來 show 圖,他就會知道你的影像是 BGR,就會顯是正確

cv2.imshow("window_name", cv2_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

1.1.2. 灰階圖片

  • 原始的灰階圖片,不會有 channel,所以讀進來應該是 (1024, 1024) 這種 shape

  • 但是!!!!!

  • 只有 plt.imread(),讀進來會是 (1024, 1024)

  • 其他像 cv2.imread(), 或是 PIL 的 Image.open(), 或是 keras 的 load_img(),讀進來都會是 (1024, 1024, 3) 這種 shape,然後 3 個 channel 值都一樣

plt_img = plt.imread("imgs/chest_xray_image.png")
print(f"Image shape is: {plt_img.shape}")
Image shape is: (1024, 1024)
plt.imshow(plt_img, cmap = "gray");
_images/read_show_save_imgs_42_0.png
cv2_img = cv2.imread("imgs/chest_xray_image.png")
print(f"Image shape is: {cv2_img.shape}")
Image shape is: (1024, 1024, 3)
  • 然後,三個通道的值都一樣

print((cv2_img[:,:,0] == cv2_img[:,:,1]).all())
print((cv2_img[:,:,0] == cv2_img[:,:,2]).all())
print((cv2_img[:,:,1] == cv2_img[:,:,2]).all())
True
True
True
  • 畫圖時,三個通道的值都相同,混出來就變灰階的顏色

plt.imshow(cv2_img);
_images/read_show_save_imgs_47_0.png
  • 不相信的話,你三個通道都寫 0 ,會幫你混出全黑的圖

plt.imshow(np.zeros((1024,1024,3)));
_images/read_show_save_imgs_49_0.png
  • PIL 和 keras,也是和 cv2 一樣,都是同樣的值,複製三次給三個通道

keras_img = img_to_array(load_img("imgs/chest_xray_image.png"))
print(f"Image shape is {keras_img.shape}")
Image shape is (1024, 1024, 3)
plt.imshow(keras_img.astype("uint8"));
_images/read_show_save_imgs_52_0.png

1.2. save

1.2.1. 存彩色圖片

  • 將 np.array 存成 圖片:

    • 如果你的 np.array 的 channel 是 RGB,請用 plt.imsave() 來存,因為 plt當初在讀的時候,就預設讀進來後轉成 RGB 的 array

    • 如果你的 np.array 的 channel 是 BGR,請用 cv2.imwrite() 來存。因為 cv2 當初在讀檔時,就預設讀進來後轉成 BGR 的 array

  • 將 np.array 先轉成 PIL 物件,再存成圖片

    • PIL 或 keras,他們本身就有 PIL 物件可用,那就用該物件的 .save() method 來存。所以,就要把 np.array 先轉回 PIL 物件,再存

1.2.1.1. np.array 存檔,用 cv2.imread(),但要注意 channel

  • 如果我用 plt.imread() 來讀檔,那 array 的 channel 會是 RGB

plt_img = plt.imread("imgs/fruits-2.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_58_0.png
  • 此時,如果我用 cv2.imwrite() 來存檔,他會把你的 array 當 BGR 來存

cv2.imwrite("imgs/temp.jpg", plt_img)
True
  • 所以,讀檔後,就會發現他剛剛存出去存錯了

plt_img = plt.imread("imgs/temp.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_62_0.png
  • 那,一種做法,是把這個影像轉成 BGR,再存

plt_img = plt.imread("imgs/fruits-2.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_64_0.png
cv2.imwrite("imgs/temp.jpg", plt_img[:,:,::-1])
True
plt_img = plt.imread("imgs/temp.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_66_0.png
  • 另一種作法,是用 matplotlib.image.imsave 來存,那就會預設你是給他 RGB

plt_img = plt.imread("imgs/fruits-2.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_68_0.png
import matplotlib
matplotlib.image.imsave('imgs/temp.jpg', plt_img)
plt_img = plt.imread("imgs/temp.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_70_0.png

1.2.1.2. 轉回 PIL 物件,再存檔

  • 那對於 np.array 的資料,我們都可以轉回 PIL 物件

plt_img = plt.imread("imgs/temp.jpg")

import PIL
img_obj = PIL.Image.fromarray(plt_img)
type(img_obj)
PIL.Image.Image
  • 接著,用 .save() method 來存檔

img_obj.save("imgs/temp.jpg")
  • 讀檔後就可發現是對的了

plt_img = plt.imread("imgs/temp.jpg")
plt.imshow(plt_img)
<matplotlib.image.AxesImage at 0x15a23ed30>
_images/read_show_save_imgs_77_1.png
  • 那因為 keras 的底層就是 PIL,所以做法也是一樣的

img_obj = array_to_img(plt_img)
img_obj.save("imgs/temp.jpg")
  • 一樣讀檔後,就可載入

plt_img = plt.imread("imgs/temp.jpg")
plt.imshow(plt_img);
_images/read_show_save_imgs_81_0.png

1.2.2. 存灰階圖片

  • 如果原本的圖片,是只有 (height, width),而沒有 channel 的 numpy array,那用

plt_img = plt.imread("imgs/chest_xray_image.png")
print(f"Image shape is: {plt_img.shape}")
Image shape is: (1024, 1024)
plt.imshow(plt_img, cmap = "gray");
_images/read_show_save_imgs_85_0.png
cv2.imwrite("imgs/temp.jpg", plt_img)
True
plt_img = plt.imread("imgs/temp.jpg")
print(f"Image shape is: {plt_img.shape}")
Image shape is: (1024, 1024)
plt.imshow(plt_img, cmap = "gray")
<matplotlib.image.AxesImage at 0x14d669700>
_images/read_show_save_imgs_88_1.png
set(plt_img.flatten())
{0, 1, 2}
import matplotlib
matplotlib.image.imsave("imgs/temp.jpg", plt_img)
plt_img = plt.imread("imgs/temp.jpg")
print(f"Image shape is: {plt_img.shape}")
Image shape is: (1024, 1024, 3)
plt.imshow(plt_img)
<matplotlib.image.AxesImage at 0x14f389760>
_images/read_show_save_imgs_92_1.png
(plt_img[:,:,0] == plt_img[:,:,1]).all()
True
plt.imsave('imgs/temp.jpg', plt_img)
plt.imsave('imgs/temp.jpg', plt_img, cmap='gray')