用我雯示意,图像的加载、读取、转换、热图。 OpenCV彩色转为灰度图、通道拆分(cv2.split)及合并(cv2.merge)

图像加载问题

使用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.Tensornumpy.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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章