圖像加載問題
使用pytorch製作圖像數據集時,需要將存儲在磁盤、硬盤的圖像讀取到內存中,涉及到圖像I/O問題。
在python中,圖像處理主要採用的庫:skimage, opencv-python, Pillow (PIL)。 這三個庫均提供了圖像讀取的方法。
三種主流圖像處理庫的比較:
庫 | 函數/方法 | 返回值 | 圖像像素格式 | 像素值範圍 | 圖像矩陣表示 |
---|---|---|---|---|---|
skimage | io.imread(xxx) | numpy.ndarray | RGB | [0, 255] | (H X W X C) |
cv2 | cv2.imread(xxx) | numpy.ndarray | BGR | [0, 255] | (H X W X C) |
Pillow(PIL) | Image.open(xxx) | PIL.Image.Image對象 | 根據圖像格式,一般爲RGB | [0, 255] | — |
讀取圖像
一張測試圖像,彩色,test_liuwen.jpg。(斷網了,沒安裝skimage)
首先先從小數列形象地看一下array、tensor、bgr、rgb的分佈。
'''關於列表使用的知識:
'''OpenCV 的cv2.imread()導入圖片時是BGR通道順序,這與Matplotlib的顯示,或者讀取圖片的通道不同,
如果需要可以轉換爲RGB模式,以下代碼顯示不同之處,但BGR在許多地方使用,caffe倒入數據是以BGR方式
i,j爲正就是序號爲j-1,i,j爲負就是倒數第-j個
b = a[i:j:s]這,以步進s,從序號爲i的到序號爲j-1的,缺省爲1. 所以a[i:j:1]相當於a[i:j] [1:3].
當s<0時,i缺省時,默認爲-1。j缺省時,默認爲-len(a)-1
所以a[::-1]相當於 a[-1:-len(a)-1:-1],也就是從最後一個元素到第一個元素複製一遍,即倒序,依次可以達到BGR通道轉換爲RGB通道。
'''
if __name__ == "__main__":
# 先從小數列形象的看一下
a = [0,1,2,3,4,5,6,7,8]
print(a[1:6:2],a[:2],a[-1:3:-2],a[:3:],a[:3:-1],a[:-3:-1],a[2::1],a[::-1])#
array_t1_bgr = np.array([
[['b1','g1', 'r1'], ["b4", 'g4', 'r4'], ['b7', 'g7', 'r7']],
[['b2','g2', 'r2'], ['b5', 'g4', 'r5'], ['b8', 'g8', 'r8']],
[['b3','g3', 'r3'], ['b6', 'g6', 'r6'], ['b9', 'g9', 'r9']],])
# 轉化爲 rgb
array_t1_rgb = array_t1_bgr[:,:,::-1]
# 1,2,3各自代表一個通道 看到通道是和輸入的array通道是一致的
tensor_t1 = torch.from_numpy(np.array([
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],]))
array_t1_tensor = np.array([[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]])
# print(t[:,:-1],'ok\n',t[:,::-1],'ok')#
# print(t1[:,:-1],'ok\n',t1[:,::-1],'ok\n',t1[:,:,::-1])#
# test_wen.jpg width = 690, height=1034, channel=3 <class 'tuple'>: (1034, 690, 3)
# 使用opencv讀取圖像 # cv2.imread()------np.array, (H x W xC), [0, 255], BGR
img_cv = cv2.imread( 'test_wen.jpg',)
img_cv_2 = cv2.imread( 'test_wen.jpg',2)# 不同
# 使用PIL讀取
img_pil = Image.open( 'test_wen.jpg',) # PIL.Image.Image對象
img_pil = np.asarray(img_pil) # (H x W x C), [0, 255], RGB
# 使用skimage讀取圖像 # skimage.io imread()-----np.ndarray, (H x W x C), [0, 255],RGB
img_skimage = io.imread('test_wen.jpg',)
# 放大下尺寸再顯示
plt.figure(figsize=(10,10))
plt.subplot(151),plt.title('img_cv'), plt.imshow(img_cv)
plt.subplot(152),plt.title('img_cv_2'),plt.imshow(img_cv_2)
plt.subplot(153),plt.title('img_pil'),plt.imshow(img_pil)
plt.subplot(154),plt.title('img_skimage'),plt.imshow(img_skimage )
plt.subplot(155),plt.title('img_cv_BGRtoRGB'),plt.imshow(img_cv[:,:,::-1])
顯示圖像:
PIL讀取的圖像爲PIL.Image.Image對象,無法用matplotlib直接顯示,需要先轉爲numpy.ndarray對象。
圖3,圖4顯示正常,圖像12顯示不正常,因爲opencv讀取的圖像爲BGR格式,matplotllib使用RGB方式顯示,圖像通道順序不一致。至於numpy形式的BGR通道的img轉化爲RGB,只需要img[:,:,::-1]
圖像轉爲torch.Tensor對象
在深度學習中,原始圖像需要轉換爲深度學習框架自定義的數據格式,在pytorch中,需要轉爲torch.Tensor。
pytorch提供了torch.Tensor
與numpy.ndarray
轉換爲接口:
方法名 | 作用 |
---|---|
torch.from_numpy(xxx) |
numpy.ndarray 轉爲torch.Tensor |
tensor1.numpy() |
獲取tensor1對象的numpy格式數據 |
torch.Tensor
高維矩陣的表示: (batch)x C x H x W。numpy.ndarray
高維矩陣的表示: H x W x C
因此在兩者轉換的時候需要使用numpy.transpose( )
方法 。
至於numpy形式的BGR通道的img轉化爲RGB,只需要img[:,:,::-1]
# ------------np.ndarray轉爲torch.Tensor------------------------------------
# numpy image: H x W x C
# torch image: C x H x W
# np.transpose( xxx, (2, 0, 1)) # 將 H x W x C 轉化爲 C x H x W
tensor_skimage = torch.from_numpy(np.transpose(img_skimage, (2, 0, 1)))
tensor_cv = torch.from_numpy(np.transpose(img_cv, (2, 0, 1)))
tensor_pil = torch.from_numpy(np.transpose(img_pil_1, (2, 0, 1)))
torch.Tensor轉numpy.ndarray
# np.transpose( xxx, (2, 0, 1)) # 將 C x H x W 轉化爲 H x W x C
img_skimage_2 = np.transpose(tensor_skimage.numpy(), (1, 2, 0))
img_cv_2 = np.transpose(tensor_cv.numpy(), (1, 2, 0))
img_pil_2 = np.transpose(tensor_pil.numpy(), (1, 2, 0))
plt.figure()
for i, im in enumerate([img_skimage_2, img_cv_2, img_pil_2]):
ax = plt.subplot(1, 3, i + 1)
ax.imshow(im)
plt.pause(0.01)
opencv圖像BGR->RGB操作
opencv默認讀取的圖像爲BGR形式,可以使用opencv提供的方法:cv2.cvtColor( )
進行圖像顏色空間轉換。也可以直接轉。耗時一致
# BRG 轉 RGB
# opencv 讀取的圖像爲BGR
# 第一種 轉爲RGB
start = time.time()
img_cv_rgb_1 = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
t_cvtColor = time.time_ns()-start
# 第二種 轉爲RGB
start = time.time()
img_cv_rgb_2 = img_cv[:,:,::-1]
t_list = time.time_ns()-start
plt.figure()
plt.subplot(131), plt.title('img_cv'),plt.imshow(img_cv)
plt.subplot(132), plt.title('img_cv_rgb_1'),plt.imshow(img_cv_rgb_1)
plt.subplot(133), plt.title('img_cv_rgb_2'),plt.imshow(img_cv_rgb_2)
plt.show()
print(t_cvtColor == t_list)
OpenCV 彩色轉灰度
有兩種方法:
# 將彩色轉爲灰度圖 方法一
img_gray_1 = cv2.cvtColor(img_cv, cv2.COLOR_RGB2GRAY)
print("img_cv shape:{}".format(np.shape(img_cv)))
print("img_gray shape:{}".format(np.shape(img_gray_1)))
# 將彩色轉爲灰度圖 方法二 直接讀取爲灰度圖
img_gray_2 = cv2.imread(image_file, cv2.IMREAD_GRAYSCALE)
print("img_gray shape:{}".format(np.shape(img_gray_2)))
plt.figure()
plt.subplot(121),plt.title("cv2.COLOR_RGB2GRAY"),plt.imshow(img_gray_1)
plt.subplot(122),plt.title("cv2.IMREAD_GRAYSCALE"),plt.imshow(img_gray_2)
plt.show()
OpenCV 通道拆分(cv2.split)
cv2.split函數分離得到各個通道的灰度值(單通道圖像)。
(不是B、G、R三種顏色哦)cv2.merge函數是合併單通道成多通道(不能合併多個多通道圖像)。
# 通道分離,注意順序BGR不是RGB
(B, G, R) = cv2.split(img_cv)
# 顯示各個分離出的通道
plt.figure()
plt.subplot(131), plt.title('B'),plt.imshow(B)
plt.subplot(132), plt.title('G'),plt.imshow(G)
plt.subplot(133), plt.title('R'),plt.imshow(R)
plt.show()
OpenCV 通道合併(cv2.merge)
# 生成一個值爲0的單通道數組
zeros = np.zeros(img_cv.shape[:2], dtype="uint8")
# 分別擴展B、G、R成爲三通道。另外兩個通道用上面的值爲0的數組填充
plt.figure()
plt.subplot(131), plt.title('B'),plt.imshow(cv2.merge([B, zeros, zeros]))
plt.subplot(132), plt.title('G'),plt.imshow(cv2.merge([zeros, G, zeros]))
plt.subplot(133), plt.title('R'),plt.imshow(cv2.merge([zeros, zeros, R]))
plt.show()
ps:可以用skimage.util.random_noise(B, mode='gaussian', seed=None, clip=True)在指定通道加不同類型的噪聲,但在 skimage.util.random_noise中, 圖像將會被轉換爲float64類型的。在合併時,G和R通道的圖像應轉換爲float64類型。
#G = skimage.util.img_as_float(G)
#R = skimage.util.img_as_float(R)
ps: 圖片縮小
img_gray_2 = cv2.resize(img_gray,
(int(img_cv.shape[0] * 0.5), int(img_cv.shape[1] * 0.5)),
interpolation=cv2.INTER_CUBIC)
生成熱圖
import math
import time
import cv2
import torch
import numpy as np
import matplotlib.pyplot as plt
# import skimage.io as io
from PIL import Image
# 方法一
def get_heatmap(annos, height, width):
"""
Parameters
- annos: 關鍵點列表 [
[[12,10],[10,30],....19個],#某一個人的
[[xx,xx],[aa,aa],....19個],#另外一個人的
]
- heigth:圖像的高
- width: 圖像的寬
Returns
- heatmap: 熱圖
"""
# 19 for coco, 15 for MPII
num_joints = 19
# the heatmap for every joints takes the maximum over all people
joints_heatmap = np.zeros((num_joints, height, width), dtype=np.float32)
# among all people
for joint in annos:
# generate heatmap for every keypoints
# loop through all people and keep the maximum
for i, points in enumerate(joint):
if points[0] < 0 or points[1] < 0:
continue
joints_heatmap = put_heatmap(joints_heatmap, i, points, 8.0)
# 0: joint index, 1:y, 2:x
joints_heatmap = joints_heatmap.transpose((1, 2, 0))
# background
joints_heatmap[:, :, -1] = np.clip(1 - np.amax(joints_heatmap, axis=2), 0.0, 1.0)
mapholder = []
for i in range(0, 19):
a = cv2.resize(np.array(joints_heatmap[:, :, i]), (height, width))
mapholder.append(a)
mapholder = np.array(mapholder)
joints_heatmap = mapholder.transpose(1, 2, 0)
return joints_heatmap.astype(np.float16)
def put_heatmap(heatmap, plane_idx, center, sigma):
"""
Parameters
-heatmap: 熱圖(heatmap)
- plane_idx:關鍵點列表中第幾個關鍵點(決定了在熱圖中通道)
- center: 關鍵點的位置
- sigma: 生成高斯分佈概率時的一個參數
Returns
- heatmap: 熱圖
"""
center_x, center_y = center # mou發
_, height, width = heatmap.shape[:3]
th = 4.6052
delta = math.sqrt(th * 2)
x0 = int(max(0, center_x - delta * sigma + 0.5))
y0 = int(max(0, center_y - delta * sigma + 0.5))
x1 = int(min(width - 1, center_x + delta * sigma + 0.5))
y1 = int(min(height - 1, center_y + delta * sigma + 0.5))
exp_factor = 1 / 2.0 / sigma / sigma
## fast - vectorize
arr_heatmap = heatmap[plane_idx, y0:y1 + 1, x0:x1 + 1]
y_vec = (np.arange(y0, y1 + 1) - center_y) ** 2 # y1 included
x_vec = (np.arange(x0, x1 + 1) - center_x) ** 2
xv, yv = np.meshgrid(x_vec, y_vec)
arr_sum = exp_factor * (xv + yv)
arr_exp = np.exp(-arr_sum)
arr_exp[arr_sum > th] = 0
heatmap[plane_idx, y0:y1 + 1, x0:x1 + 1] = np.maximum(arr_heatmap, arr_exp)
return heatmap
# 方法二
def CenterLabelHeatMap(img_width, img_height, c_x, c_y, sigma):
X1 = np.linspace(1, img_width, img_width)
Y1 = np.linspace(1, img_height, img_height)
[X, Y] = np.meshgrid(X1, Y1)
X = X - c_x
Y = Y - c_y
D2 = X * X + Y * Y
E2 = 2.0 * sigma * sigma
Exponent = D2 / E2
heatmap = np.exp(-Exponent)
return heatmap
# Compute gaussian kernel
def CenterGaussianHeatMap(img_height, img_width, c_x, c_y, variance):
gaussian_map = np.zeros((img_height, img_width))
for x_p in range(img_width):
for y_p in range(img_height):
dist_sq = (x_p - c_x) * (x_p - c_x) + \
(y_p - c_y) * (y_p - c_y)
exponent = dist_sq / 2.0 / variance / variance
gaussian_map[y_p, x_p] = np.exp(-exponent)
return gaussian_map
if __name__ == "__main__":
image_file = 'test_wen.jpg'
# BGR
img = cv2.imread(image_file)
# RGB
img = img[:, :, ::-1]
# heatmaps
annos = [
[[16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12], [7, 13], [6, 7], [6, 8], [7, 9], [8, 10], [9, 11], [2, 3], [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7]],
[[16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12], [7, 13], [6, 7], [6, 8], [7, 9], [8, 10], [9, 11],
[2, 3], [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7]]
]
height, width, _ = np.shape(img)
cy, cx = height / 2.0, width / 2.0
start = time.time()
heatmap1 = CenterLabelHeatMap(width, height, cx, cy, 21)
t1 = time.time() - start
start = time.time()
heatmap2 = CenterGaussianHeatMap(height, width, cx, cy, 21)
t2 = time.time() - start
print(t1, t2)
plt.figure(figsize=(10,10))
plt.subplot(1, 2, 1)
plt.imshow(heatmap1)
plt.subplot(1, 2, 2)
plt.imshow(heatmap2)
plt.show()
print('End.')
OVER