用我雯示意,圖像的加載、讀取、轉換、熱圖。 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

 

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