使用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 : (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]
在深度学习中,原始图像需要转换为深度学习框架自定义的数据格式,在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)))
# 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形式,可以使用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)
有两种方法:
# 将彩色转为灰度图 方法一
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()
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()
# 生成一个值为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