天池大賽:街景字符編碼識別——Part2:數據讀取與數據擴增

街景字符編碼識別

更新流程↓
Task01:賽題理解
Task02:數據讀取與數據擴增
Task03:字符識別模型
Task04:模型訓練與驗證
Task05:模型集成
底到鏡一
比賽鏈接


Part2:數據讀取與數據擴增



1. 數據讀取

 1.1. 圖像讀取

  接下來將簡單介紹以下目前較爲主流的Python圖像庫的基本使用方法

matplotlib PIL(pillow) OpenCV skimage imageio

  1.1.1. matplotlib

  matplotlib是Python的繪圖庫,與numpy一起使用可以算是一種matlab開源替代方案,在科學繪圖領域被廣泛使用。當然,用來讀取圖像自然不在話下。
  使用plt.imread()讀取圖片將其儲存爲一個RGB像素值矩陣,再進行處理。故其可以與opencv或pillow結合使用,只需要傳入像素值矩陣,matplotlib便可以接手處理接下來想要完成的操作。

import matplotlib.pyplot as plt					# 導入matplotlib庫
import numpy as np								# 導入numpy庫

img = plt.imread('PicPath/PicName.jpg')			# 讀取圖片
print(img.shape)								# 輸出(高度h,寬度w,通道數c)
print(img.size)									# 輸出像素總數目
print(img.dtype)								# 輸出圖片類型,uint8爲[0-255]
print(img)										# 輸出所有像素的RGB值,一個像素RGB爲[0-255 0-255 0-255]
plt.imshow(img)									# 將圖片img插入畫布
plt.axis('off')									# 座標軸刻度不顯示
plt.show()										# 展示畫布

imgR = image[:,:,0] 							# R通道,熱量圖
plt.imshow(imgR)								# 將熱量圖插入畫布
plt.show()										# 展示畫布

plt.imshow(imgR,cmap='Greys_r')					# 將灰度圖插入畫布
plt.show()										# 展示畫布

figure = plt.figure(figsize=(80,40)) 			# 調整顯示畫布寬80,高40/英寸
img1 = plt.imread('PicPath/PicName1.jpg')		# 讀取圖片1
img2 = plt.imread('PicPath/PicName2.jpg')		# 讀取圖片2

plt.axis("off")									# 畫布座標軸刻度不顯示
ax = figure.add_subplot(121) 					# 畫布以1行2列的形式顯示,設置圖片定位爲序列1
plt.axis('off')									# 子圖1座標軸刻度不顯示
ax.imshow(img1) 								# 將圖片1插入子圖1
ax.set_title('title1')							# 給子圖1加標題
ax = figure.add_subplot(122) 					# 畫布以1行2列的形式顯示,設置圖片定位爲序列2
plt.axis('off')									# 子圖2座標軸刻度不顯示
ax.imshow(img2)  								# 將圖片2插入子圖2
ax.set_title('title2')							# 給子圖2加標題
plt.savefig('PicX.jpg')							# 保存畫布命名爲PicX.jpg
plt.show()										# 展示畫布

  1.1.2. PIL(pillow)

  PIL即Python Imaging Library,而pillow是PIL的一個分支。pillow提供了常見的圖像讀取和處理的操作,它比opencv更爲輕巧,且可以與ipython notebook無縫集成。
  使用Image.open()讀取圖片儲存爲一個對象,並非是numpy矩陣。

from PIL import Image							# 導入PIL庫
import numpy as np								# 導入numpy庫

img = Image.open('PicPath/PicName.jpg')			# 讀取圖片
imgL = Image.open('PicName.jpg').convert('L')	# 讀取圖片灰度圖
imgL.show()										# 展示灰度圖
img1 = img.copy() 								# 複製圖片
print(img.format) 								# 輸出圖片格式
print(img.size) 								# 輸出圖片(寬度w,高度h)
print(img.mode)  								# 輸出圖片類型,L爲灰度圖,RGB爲真彩色,RGBA爲RGB+Alpha透明度
im.show()										# 展示畫布

imgData = np.array(img)							# 將對象img轉化爲RGB像素值矩陣
print(imgData.shape)							# 輸出圖片(寬度w,高度h,通道c)
print(imgData.dtype)							# 輸出圖片類型,uint8爲[0-255]
print(imgData)									# 輸出所有像素的RGB值
imgN = Image.fromarray(imgData)					# 將RGB像素值矩陣轉化爲對象imgN
imgN.save('PicName.jpg')						# 儲存爲文件PicName.jpg

r, g, b = img.split()							# 分離通道
img = Image.merge("RGB", (b, g, r))				# 合併通道

# ROI(region of interest),只對ROI區域操作
roi = img.crop((0,0,300,300)) 					# (左上x,左上y,右下x,右下y)座標
roi.show()										# 展示ROI區域

#捕捉異IOError,爲讀取圖片失敗
try:
    img = Image.open('PicName.jpg')
except IOError:
    print('image failed to load')

  1.1.3. OpenCV

  OpenCV是一個跨平臺的計算機視覺庫。其發展非常早,擁有衆多的計算機視覺、數字圖像處理和機器視覺等功能,OpenCV是今天介紹得所有圖像庫中最全面也最強大的庫,學習成本也相對要高很多。
  使用cv2.imread讀取圖片將其儲存爲一個BGR像素值矩陣,故若要結合使用matplotlib則要先進行轉化。

import cv2										# 導入OpenCV庫
import numpy as np								# 導入numpy庫
 
img = cv2.imread('PicName.jpg',0)				# 讀取圖片:灰度模式
img = cv2.imread('PicName.jpg',-1)				# 讀取圖片:BRGA模式(BRG+Alpha通道)
img = cv2.imread('PicName.jpg',1)				# 讀取圖片:BRG模式
img = cv2.imread('PicName.jpg')					# 讀取圖片:第二參數默認爲1,BRG模式
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)		# 將顏色通道從BRG轉爲RGB
if img == None:									# 讀取圖片失敗
	print('image failed to load')
cv2.imshow('src',img)							# 圖片源src爲img
print(img.shape) 								# 輸出圖片(高度h,寬度w,通道c)
print(img.size) 								# 像素總數目
print(img.dtype)								# 輸出圖片類型,uint8爲[0-255]
print(img)										# 輸出所有像素的RGB值
cv2.waitKey()									# 按鍵關閉窗口
# waitKey(delay)函數的功能是不斷刷新圖像,頻率時間爲delay,單位爲ms,返回值爲當前鍵盤按鍵值
# waitKey() 是在一個給定的時間內(單位ms)等待用戶按鍵觸發; 如果用戶沒有按下鍵,則接續等待(循環)

imgL = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)		# 讀取img灰度圖
cv2.imshow('gray',imgL)							# 圖片源gray爲imgL
cv2.imwrite('imgL.jpg',imgL)  					# 將imgL儲存名爲imgL.jpg的圖片
print(imgL.shape)								# 輸出圖片(高度h,寬度w)
print(imgL.size)								# 像素總數目
print(imgL)										# 輸出所有像素的灰度值
cv2.waitKey()									# 按鍵關閉窗口

img = img.transpose(2,0,1)						# 圖片矩陣變換爲(通道c,高度h,寬度w)
img = np.expand_dims(img, axis=0)				# 圖片矩陣擴展維度添加在第一維
print(img.shape)								# (1,通道c,高度h,寬度w)

# 圖片歸一化處理
img = cv2.imread('PicName.jpg')						
img = img.astype("float") / 255.0  				# 轉化數據類型爲float後進行歸一化
print(img.dtype)								# 輸出爲:float64
print(img)               						# 輸出爲[0-1 0-1 0-1]


print(img[10,10])  								# 訪問圖片img像素[10,10],輸出 [0-255 0-255 0-255]
print(imgL[10,10]) 								# 訪問灰色圖片img像素[10,10],輸出 0-255
img[10,10] = [255,255,255]						# 修改圖片img像素點[10,10]爲[255,255,255]
imgL[10,10] = 255								# 修改灰色圖片img像素點[10,10]爲255
img[:,:,2] = 0  								# 將R通道全部修改爲0


roi = img[200:550,100:450,:]					# ROI操作,座標(高度範圍,寬度範圍,通道範圍)
cv2.imshow('roi',roi)							# 圖片源roi爲roi
cv2.waitKey()									# 按鍵關閉窗口

  1.1.4. skimage

  skimage包的全稱是scikit-image SciKit (toolkit for SciPy) ,它對scipy.ndimage進行了擴展,提供了更多的圖片處理功能。它是由python語言編寫的,由scipy 社區開發和維護。skimage包由許多的子模塊組成,各個子模塊提供不同的功能。
  使用io.imread()讀取圖片將其儲存爲一個RGB像素值矩陣。

from skimage import io							# 導入skimage庫
from skimage import color
import numpy as np								# 導入numpy庫
 
img = io.imread('PicName.jpg')					# 讀取圖片
imgL = io.imread('PicName.jpg',as_grey=True)  	# 讀取圖片:灰度模式
print(img.shape) 								# 輸出圖片img(高度h,寬度w,通道c)
print(imgL.shape) 								# 輸出圖片imgL(高度h,寬度w)
print(img.size)									# img像素總數目
print(img.dtype)								# 輸出img圖片類型,uint8爲[0-255]
print(imgL.dtype)								# 輸出imgL圖片類型,float64爲[0-1],已經被歸一化
print(img)										# 輸出img所有像素的RGB值
print(imgL)										# 輸出imgL所有灰度值,長度爲imgL.size的numpy數組
io.imsave('img.png',img)						# 將img儲存名爲img.png的圖片
io.imshow(img)									# 圖片img插入畫板
io.show()										# 展示畫板

imgl = io.imread('PicName.jpg')					# 讀取圖片
imgl = color.rgb2grey(imgl)						# 轉換爲灰度模式
print(imgl.dtype)								# 以下數據同imgL
print(imgl.size)
print(imgl.shape)
io.imshow(imgl)
io.show()

'''
skimage.color.rgb2grey(rgb)
skimage.color.rgb2hsv(rgb)
skimage.color.rgb2lab(rgb)
skimage.color.gray2rgb(image)
skimage.color.hsv2rgb(hsv)
skimage.color.lab2rgb(lab)
'''

  1.1.5. imageio

Imageio是一個Python庫,提供了一個簡單的接口用於讀取和寫入各種圖像數據,包括動畫圖像,視頻,體積數據和科學格式。
  使用io.imread()讀取圖片將其儲存爲一個RGB像素值矩陣。

import imageio									# 導入imageio庫
img = imageio.imread('PicName.jpg')				# 讀取圖片
imageio.imsave('img.png',img)					# 將img儲存名爲img.png的文件
print(img.shape) 								# 輸出圖片img(高度h,寬度w,通道c)
print(img.size)									# img像素總數目
print(img.dtype)								# 輸出img圖片類型,uint8爲[0-255]
print(img)										# 輸出img所有像素的RGB值
plt.imshow(img)									# 圖片img插入畫板
plt.show()										# 展示畫板

 1.2. 總結

  • 其他圖像庫讀取彩色圖片都以RGB形式儲存,而OpenCV則是以BGR形式存儲。
  • 其他圖像庫讀取圖片都以numpy十六進制彩色值形式儲存,而PIL讀取圖片是以對象形式儲存。

2. 數據擴增

  爲了增加數據量、豐富數據多樣性、提高模型的泛化能力,同時也可以有效緩解模型過擬合的情況,給模型帶來的更強的泛化能力。我們可以不實際增加原始數據,只是對原始數據做一些變換,從而創造出更多的數據。我們只需要對現有數據集進行微小改動,例如裁剪或灰度變換或翻轉(數字6與9翻轉會發生交換) 。無論如何,我們的神經網絡會認爲這些是不同的圖像。從而完成數據擴增(Data Augmentation)操作。

 2.1. 數據擴增方法

數據擴增方法有很多:從顏色空間、尺度空間到樣本空間,同時根據不同任務數據擴增都有相應的區別。
對於圖像分類,數據擴增一般不會改變標籤;對於物體檢測,數據擴增會改變物體座標位置;對於圖像分割,數據擴增會改變像素標籤。

  • 幾何變換:彈性變換(Elastic Transform)、透視變換(Perspective Transform)、分段仿射變換(Piecewise Affine transforms)、枕形畸變(Pincushion Distortion);隨機改變大小(resize),隨機縮放、水平翻轉(flip),豎直翻轉(mirror);
  • 加噪聲:對主成分做一個(0, 0.1)的高斯擾動。
  • PCA Jittering(顏色改變):首先按照RGB三個顏色通道計算均值和標準差,對網絡的輸入數據進行規範化,隨後我們在整個訓練集上計算了協方差矩陣,進行特徵分解,得到特徵向量和特徵值,用來做PCA Jittering。對RGB空間做PCA,然後對主成分做一個(0, 0.1)的高斯擾動。結果讓錯誤率又下降了1%。
  • 對比度和亮度:給圖像增加一些隨機的光照,對比度受限自適應直方圖均衡化算法(Clahe),銳化(Sharpen),凸點(Emboss)
  • 隨機色相、飽和度、明度(HSV)變換
  • 彩圖到灰度轉換(Color to Gray)
  • 將灰度圖重新映射到隨機顏色的圖像中
  • 模糊(Blur)、一般模糊(Median Blur)、非常模糊(Motion Blur)
方法 中文說明
transforms.CenterCrop 對圖片中心進行裁剪
transforms.ColorJitter 對圖像顏色的對比度、飽和度和零度進行變換
transforms.FiveCrop 對圖像四個角和中心進行裁剪得到五分圖像
transforms.Grayscale 對圖像進行灰度變換
transforms.Pad 使用固定值進行像素填充
transforms.RandomAffine 隨機仿射變換
transforms.RandomCrop 隨機區域裁剪
transforms.RandomHorizontalFlip 隨機水平翻轉
transforms.RandomRotation 隨機旋轉
transforms.RandomVerticalFlip 隨機垂直翻轉

 2.2. 常用的數據擴增庫

  2.2.1.torchvision

  pytorch官方提供的數據擴增庫,提供了基本的數據數據擴增方法,可以無縫與torch進行集成;但數據擴增方法種類較少,且速度中等;

  2.2.2. imgaug

imgaug是常用的第三方數據擴增庫,提供了多樣的數據擴增方法,且組合起來非常方便,速度較快;

  2.2.3. albumentations

是常用的第三方數據擴增庫,提供了多樣的數據擴增方法,對圖像分類、語義分割、物體檢測和關鍵點檢測都支持,速度較快。

3. Pytorch讀取數據

  本次賽題我們使用Pytorch讀取賽題數據。
  在Pytorch中數據是通過Dataset進行封裝,並通過DataLoder進行並行讀取。所以我們只需要重載一下數據讀取的邏輯,之後我們將在定義好的Dataset基礎上構建DataLoder,DataLoder是爲了實現不同於Dataset的功能。

  • Dataset:對數據集的封裝,提供索引方式的對數據樣本進行讀取
  • DataLoder:對Dataset進行封裝,提供批量讀取的迭代讀取
import os, sys, glob, shutil, json
import cv2

from PIL import Image
import numpy as np

import torch
from torch.utils.data.dataset import Dataset
import torchvision.transforms as transforms

class SVHNDataset(Dataset):
    def __init__(self, img_path, img_label, transform=None):
        self.img_path = img_path
        self.img_label = img_label 
        if transform is not None:
            self.transform = transform
        else:
            self.transform = None

    def __getitem__(self, index):
        img = Image.open(self.img_path[index]).convert('RGB')

        if self.transform is not None:
            img = self.transform(img)
        
        # 原始SVHN中類別10爲數字0
        lbl = np.array(self.img_label[index], dtype=np.int)
        lbl = list(lbl)  + (5 - len(lbl)) * [10]
        
        return img, torch.from_numpy(np.array(lbl[:5]))

    def __len__(self):
        return len(self.img_path)

train_path = glob.glob('../input/train/*.png')
train_path.sort()
train_json = json.load(open('../input/train.json'))
train_label = [train_json[x]['label'] for x in train_json]

train_loader = torch.utils.data.DataLoader(
        SVHNDataset(train_path, train_label,
                   transforms.Compose([
                       transforms.Resize((64, 128)),				# 縮放到固定尺寸
                       transforms.ColorJitter(0.3, 0.3, 0.2),		# 隨機顏色變換
                       transforms.RandomRotation(5),				# 加入隨機旋轉
                       transforms.ToTensor(),						# 將圖片轉換爲pytorch 的tesntor
                       transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])		# 對圖像像素進行歸一化
            ])), 
    batch_size=10, # 每批樣本個數
    shuffle=False, # 是否打亂順序
    num_workers=10, # 讀取的線程個數
)

for data in train_loader:
    break

  在加入DataLoder後,數據按照批次獲取,每批次調用Dataset讀取單個樣本進行拼接。
    此時data的格式爲:torch.Size([10, 3, 64, 128]), torch.Size([10, 6])
    前者爲圖像文件,爲batchsize * chanel * height * width次序,
    後者爲字符標籤。

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