pytorch基礎學習(二)

  在神經網絡訓練時,還涉及到一些tricks,如網絡權重的初始化方法,優化器種類(權重更新),圖片預處理等,繼續填坑。html

1. 神經網絡初始化(Network Initialization )python

  1.1 初始化緣由算法

    咱們構建好網絡,開始訓練前,不能默認的將全部權重係數都初始化爲零,由於全部卷積核的係數都相等時,提取特徵就會同樣,反向傳播時的梯度也會存在對稱性,網絡會退化會線性模型。另外網絡層數較深時,初始化權重過大,會出現梯度爆炸,而太小又會出現梯度消失。通常權重初始化時須要考慮兩個問題:segmentfault

    (1)權重參數所有相同時,會有梯度更新對稱性問題(詳細見https://www.zhihu.com/question/36068411?sort=created)網絡

        以下圖中w,b係數都相同時,a1,a2,a3的值也會相同,反過來進行梯度更新時,w,b也是按相同的速率在更新。(不論是哪一個神經元,它的          前向傳播和反向傳播的算法都是同樣的,若是初始值也同樣的話,無論訓練多久,它們最終都同樣,都沒法打破對稱(fail to break the             symmetry),那每一層就至關於只有一個神經元,最終L層神經網絡就至關於一個線性的網絡,如Logistic regression)app

      

    (2)採用飽和激活函數時,進行隨機初始化時,權重分佈會使神經元輸出處於激活函數的梯度飽和區域。dom

      (詳細見:https://www.jianshu.com/p/03009cfdf733)ide

  1.2 初始化方法函數

    經常使用初始化方法有Gaussain initialization, Xavier initialization, Kaiming(MSRA) initialization 。pytorch的torch.nn.init模塊中包含了經常使用的初始化函數。學習

    Gaussian initialization:   採用高斯分佈初始化權重參數

      nn.init.normal_(tensor, mean=0, std=1) 能實現不一樣均值和標準差的高斯分佈

      nn.init.unoform_(tensor, a=0, b=1) 能實現(a, b)範圍內的均勻分佈   

import torch
import torch.nn as nn
w = torch.empty(3, 5)
nn.init.uniform_(w, a=0, b=1)      #初始化爲(0, 1)範圍內的均勻分佈
nn.init.normal_(w, mean=0, std=1)  #初始化爲均值爲0, 標準差爲1的正態分佈
nn.init.constant_(w, 0.3)          # 所有初始化爲常量值0.3
nn.init.eye_(w)                     # 初始化爲單位矩陣(對角線爲1)
print(w)

    Xavier Initialization: 均值爲0, 標準差根據輸入神經元和輸出神經元的參數個數決定,適合採用tanh和sigmoid等激活函數的模型,詳細見下面論文

        論文:Understanding the difficulty of training deep feedforward neural networks

        nn.init.xavier_uniform_(tensor, gain=1): 根據xavier,實現了(-a, a)範圍內的均勻分佈,其中a的計算公式以下:

                        gain:增益,能夠理解爲縮放倍數,

                        fan_in: in_channel*Kw*Kh   (輸入channel個數, kernel的寬和高)

                        fan_out: out_channel*Kw*Kh   (輸出channel個數, kernel的寬和高)

        nn.init.xavier_normal_(tensor, gain=1): 根據xavier,實現了mean=0, std=std 的高斯分佈,其中std的計算公式以下:

w = torch.empty(3, 3, 5, 5)          #fan_in=75, fan_out=75
nn.init.xavier_uniform_(w, gain=1)   #初始化爲(-sqrt(6/150), sqrt(6/150))範圍內的均勻分佈
nn.init.xavier_normal_(w, gain=1)    #初始化爲mean=0, std=sqrt(2/150)的正態分佈

         關於fan_in和fan_out的計算方式,pytorch的實現代碼以下:

def _calculate_fan_in_and_fan_out(tensor):
    dimensions = tensor.ndimension()
    if dimensions < 2:
        raise ValueError("Fan in and fan out can not be computed for tensor with fewer than 2 dimensions")

    if dimensions == 2:  # Linear
        fan_in = tensor.size(1)
        fan_out = tensor.size(0)
    else:
        num_input_fmaps = tensor.size(1)
        num_output_fmaps = tensor.size(0)
        receptive_field_size = 1
        if tensor.dim() > 2:
            receptive_field_size = tensor[0][0].numel()
        fan_in = num_input_fmaps * receptive_field_size
        fan_out = num_output_fmaps * receptive_field_size

    return fan_in, fan_out
計算fan_in和fan_out

      關於gain的值選取,pytorch推薦以下:

 

         也能夠經過函數計算: gain = nn.init.calculate_gain("leaky_relu", 0.2)        ( leaky_relu with negative_slope=0.2)

                 #第一個參數爲nn.functional中的函數名,第二個爲可選參樹。

 

    Kaiming/He Initialization: 均值爲0, 標準差根據輸入神經元的參數個數決定, 特別採用適合ReLU激活函數的模型,詳細見下面論文

          論文:《Delving Deep into Rectifiers:Surpassing Human-Level Performance on ImageNet Classification》

         nn.init.kaiming_uniform_(tensor, a=0, mode="fan_in", nonlinearity="leaky_relu"),  實現了(-bound, bound)範圍內的均勻分佈,計算公式以下:

         nn.init.kaiming_normal_(tensor, a=0, mode="fan_in", nonlinearity="leaky_relu"): 實現了mean=0, std=std的正態分佈,計算公式以下:

參數:
    tensor – n 維 torch.Tensor
    a – 該層後面一層的整流函數中負的斜率 (默認爲 0,此時爲 Relu)
    mode – ‘fan_in’ (default) 或者 ‘fan_out’。使用fan_in保持weights的方差在前向傳播中不變;使用fan_out保持weights的方差在反向傳播中不變。
    nonlinearity – 非線性函數 (nn.functional 中的名字),推薦只使用 ‘relu’ 或 ‘leaky_relu’ (default)。

w = torch.empty(3, 3, 5, 5)          #fan_in=75, fan_out=75
nn.init.kaiming_uniform_(w, mode='fan_in', nonlinearity='relu')   #初始化爲(-sqrt(6/75), sqrt(6/75))範圍內的均勻分佈
nn.init.kaiming_normal_(w, mode='fan_out', nonlinearity='relu')    #初始化爲mean=0, std=sqrt(2/75)的正態分佈

  1.3 初始化網絡

    在實際項目,要根據網絡中的卷積核,BN和Linear等進行分開初始化,代碼以下:

#coding:utf-8

import torch
import torch.nn as nn

class MyNet(nn.Module):
    
    def __init__(self):
        super(MyNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)  
        self.bn1 = nn.BatchNorm2d(64, eps=1e-05, momentum=0.1)
        self.relu1 = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(2, stride=2, padding=0)
        
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)   
        self.bn2 = nn.BatchNorm2d(128, eps=1e-05, momentum=0.1)
        self.relu2 = nn.ReLU(inplace=True)
        self.maxpool2 = nn.MaxPool2d(2, stride=2, padding=0)
        
        self.fc1 = nn.Linear(6 * 64 * 64, 150)
        self.relu3 = nn.ReLU(inplace=True)
        
        self.fc2 = nn.Linear(150, 3)
        self.softmax = nn.Softmax(dim=1)
        
    def froward(x):
        x = self.maxpool1(self.relu1(self.bn1(self.conv1(x))))
        x = self.maxpool2(self.relu2(self.bn2(self.conv2(x))))
        x = x.view(6 * 64 * 64, -1)
        x = self.relu3(self.fc1(x))
        x = self.softmax(self.fc1(x))
        return x

#初始化方法一        
def init_weights(net):
    for m in net.modules():
        if isinstance(m, nn.Conv2d):
            nn.init.kaiming_uniform_(m.weight, a=0, mode="fan_in", nonlinearity="relu")
            nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.BatchNorm2d):
            nn.init.constant_(m.weight, 1)
            nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.Linear):
            nn.init.normal_(m.weight, mean=0, std=1e-3)
            nn.init.constant_(m.bias, 0)
net = MyNet()
init_weights(net)
print(list(net.parameters())
    
#初始化方式二
def init_weights(m):
    if isinstance(m, nn.Conv2d):
        nn.init.xavier_uniform_(m.weight.data)
        nn.init.constant_(m.bias.data, 0.1)
    elif isinstance(m, nn.BatchNorm2d):
        m.weight.data.fill_(1)
        m.bias.data.zero_()
    elif isinstance(m, nn.Linear):
        m.weight.data.normal_(0, 0.01)
        m.bias.data.zero_()
net = MyNet()
net.apply(init_weights)
print(list(net.parameters()))
神經網絡初始化

 

2. 優化器(optimizer)

  神經網絡訓練時,採用梯度降低,更新權重參數,逐漸逼近最小loss的方式。pytorch中優化梯度降低的算法有多種,包括基於動量的SGD, SGD+Momentum, Nesterov, 和自適應的Adagrad, Adam.。

  2.1 動量法

  動量更新: 動量法旨在經過每一個參數在以前的迭代中的梯度,來改變當前位置參數的梯度,在梯度穩定的地方可以加速更新的速度,在梯度不穩定的地方可以穩定梯度。

   2.1.1 SGD(stachastic gradient desent)

   隨機梯度降低:最簡單的梯度降低優化算法, 每個mini-batch 更新一次權重參數, 公式以下, w爲權重參數,\frac{\partial L}{\partial W} 表示損失函數關於 W 的梯度,\eta 表示學習率。

 

    SGD存在兩個問題:

    (1) SGD方法中的高方差振盪使得網絡很那穩定收斂,即會之字形搖擺,收斂速度慢。

    (2)可能會陷入局部最小值而沒法跳出。

   2.1.2 SGD+Momentum: 加速訓練,跳出局部最小值

     帶動量的SGD優化器:在SGD的基礎上引入動量,以下面公式中, v保存上一次的梯度, \alpha爲衰減係數。能夠發現每次梯度更新時,都會引入上一次的梯度值,若是本次梯度和上一次梯度方向相同,會加速梯度降低,而方向不一致時,能減少梯度降低,從而保證:梯度方向不變的維度上速度變快,梯度方向有所改變的維度上的更新速度變慢,這樣就能夠加快收斂並減少震盪。

 

  2.1.3 Nesetrov: 也是一種動量更新的方式,不是很理解,更新公式以下, 詳細解釋參見https://zhuanlan.zhihu.com/p/79981927

     pytorch的optim.SGD()實現了上述三種優化算法:

      optim.SGD(params, lr, momentum=0, dampening=0, weight_decay=0, nesterov=False)
        params: 須要更新的權重參數,生成器對象
        lr: 學習速率,經常使用0.001
        momentum: 動量的權重係數,對應上面式子中的\alpha,默認爲0,表示不採用動量更新。經常使用0.8和0.9
        nesterov: 是否採用Nesterov動量更新方式
        weight_decay: L2正則懲罰項的係數, 默認爲0表示不進行正則化懲罰。經常使用1e-5

from torch import optim
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)  #model.parameters(), 神經網絡的權重參數

  2.2 自適應法 

    自適應更新:它經過每一個參數的歷史梯度,動態更新每個參數的學習率,使得每一個參數的更新率都可以逐漸減少。前期梯度加大的,學習率減少得更快,梯度小的,學習率減少得更慢些。(能解決 SGD 遇到鞍點或者極小值點後學習變慢的問題)

   2.2.1  Adagrad:自適應的調節學習率,更新算法以下,h中儲存了梯度的平方值,梯度越大時,h越大,而學習率會越小。

 

     optim.Adagrad(params, lr=0.01,  lr_decay=0,  weight_decay=0,  initial_accumulator_value=0)

      lr_decay:

      init_accumulator_value

d = (torch.randn(5, 3) for i in range(3))
optimizer = optim.Adagrad(d, lr=0.001)
print(optimizer)

  2.2.2 RMSprop (Root mean square):   Adagrad有個問題,其學習率會不斷地衰退,會使得不少任務在達到最優解以前學習率就已通過量減少,因此RMSprop採用了使用指數衰減平均來慢慢丟棄先前得梯度歷史,防止學習率過早地減少。其更新公式以下:

     optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)

      alpha:  對應上式子中的ρ, 默認爲0.99

      eps:  爲數學穩定項,對應上式子中的δ, 默認爲1e-08

params = (torch.randn(5, 3) for i in range(3))
optimizer = optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
print(optimizer)

  2.2.3 Adam: 結合了上述的動量(Momentum)自適應(Adaptive),同時對梯度和學習率進行動態調整。若是說動量至關於給優化過程增長了慣性,那麼自適應過程就像是給優化過程加入了阻力。速度越快,阻力也會越大。更新公式以下:

  optim.Adam(params, lr=0.001, beats=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

    betas: 對應上述式子中的β1,β2, 默認β1=0.9, β2=0.999

params = (torch.randn(5, 3) for i in range(3))
optimizer = optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=Fals)
print(optimizer)

  2.3 優化器使用

    上 述幾種優化器,對params中的參數所有使用相同的學習速率,在實際訓練模型時,也能夠爲模型不一樣層設置不一樣的學習速率。代碼以下:

    使用相同的學習速率:

#coding:utf-8

import torch
import torch.nn as nn
from torch import optim

class MyNet(nn.Module):
    
    def __init__(self):
        super(MyNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)  
        self.bn1 = nn.BatchNorm2d(64, eps=1e-05, momentum=0.1)
        self.relu1 = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(2, stride=2, padding=0)
        
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)   
        self.bn2 = nn.BatchNorm2d(128, eps=1e-05, momentum=0.1)
        self.relu2 = nn.ReLU(inplace=True)
        self.maxpool2 = nn.MaxPool2d(2, stride=2, padding=0)
        
        self.fc1 = nn.Linear(6 * 64 * 64, 150)
        self.relu3 = nn.ReLU(inplace=True)
        
        self.fc2 = nn.Linear(150, 3)
        self.softmax = nn.Softmax(dim=1)
        
    def froward(x):
        x = self.maxpool1(self.relu1(self.bn1(self.conv1(x))))
        x = self.maxpool2(self.relu2(self.bn2(self.conv2(x))))
        x = x.view(6 * 64 * 64, -1)
        x = self.relu3(self.fc1(x))
        x = self.softmax(self.fc1(x))
        return x
        
model = MyNet()
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-05)
print(optimizer)

#dataset得本身實現
for input, target in dataset:
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    optimizer.step()
模型中optimizer

    使用不一樣的學習速率:

#coding:utf-8

import torch
import torch.nn as nn
from torch import optim

class MyNet(nn.Module):
    
    def __init__(self):
        super(MyNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)  
        self.bn1 = nn.BatchNorm2d(64, eps=1e-05, momentum=0.1)
        self.relu1 = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(2, stride=2, padding=0)
        
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)   
        self.bn2 = nn.BatchNorm2d(128, eps=1e-05, momentum=0.1)
        self.relu2 = nn.ReLU(inplace=True)
        self.maxpool2 = nn.MaxPool2d(2, stride=2, padding=0)
        
        self.fc1 = nn.Linear(6 * 64 * 64, 150)
        self.relu3 = nn.ReLU(inplace=True)
        
        self.fc2 = nn.Linear(150, 3)
        self.softmax = nn.Softmax(dim=1)
        
    def froward(x):
        x = self.maxpool1(self.relu1(self.bn1(self.conv1(x))))
        x = self.maxpool2(self.relu2(self.bn2(self.conv2(x))))
        x = x.view(6 * 64 * 64, -1)
        x = self.relu3(self.fc1(x))
        x = self.softmax(self.fc1(x))
        return x
        
model = MyNet()

conv1_params = list(map(id, model.conv1.parameters()))
conv2_params = list(map(id, model.conv2.parameters()))
base_params = filter(lambda p: id(p) not in conv1_params+conv2_params, model.parameters())
params = [{"params": base_params},
          {"params": model.conv1.parameters(), "lr": 0.001, "weight_decay":1e-05},
          {"params": model.conv2.parameters(), "lr": 0.001, "weight_decay":1e-05}
]
optimizer = optim.Adam(params, lr=0.01, weight_decay=1e-04)  #base_params採用lr=0.01, weight_decay=1e-04; conv1和conv2層採用單獨的學習速率
print(optimizer)


#dataset得本身實現
for input, target in dataset:
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    optimizer.step()
模型中optimizer

 

3. 圖片預處理

  在進行訓練模型前,有時候會對圖片進行預處理,主要包括兩方面: PCA降維和圖片加強

  3.1 PCA降維

     提取圖片的主要特徵,同時能對一些較大的圖片進行壓縮。原理推導過程以下:(參考1參考2

    

PCA算法步驟

  (1)將數據組織成d =m*n(特徵爲n維, 每一個維度上有每一個數據)的矩陣,減去每個維度上的均值(若不一樣維度間的數據尺度相差大時,還需除以標準差)

  (2)計算矩陣d的協方差矩陣(dT *d)

  (3)對協方差矩陣進行特徵分解(或奇異值分解)

  (4) 選取較大特徵值對應的特徵向量做爲基向量,將矩陣的映射到基向量空間,即完成降維

使用代碼以下:

#coding:utf-8


import numpy as np


#對x進行PCA將維
x = np.random.randn(1000, 500)
x -= np.mean(x, axis=0)
cov = np.dot(x.T, x)/(x.shape[0] -1)  #x的協方差矩陣
u, s, v = np.linalg.svd(cov)  #奇異值分解
x_reduced = np.dot(x, u[:, :100])  #取前100個特徵向量(根據特徵值大小從到小排列)
PCA降維
#coding:utf-8


import numpy as np
from sklearn.decomposition import PCA 
import cv2


def my_pca(x, n=100):
    #對x進行PCA將維
    x = x-np.mean(x, axis=0)
    cov = np.dot(x.T, x)/(x.shape[0] -1)  #x的協方差矩陣, (n-1:除以n-1表示是樣本方差; 如果全部樣本的方差則除以n;參見https://www.cnblogs.com/datamining-bio/p/9267759.html)
    # cov = np.cov(x, rowvar=0)  #計算協方差矩陣,注意此處rowvar=0表示,一列數據表示一個特徵/一個維度
    u, s, v = np.linalg.svd(cov)  #奇異值分解
    x_reduced = np.dot(x, u[:, :n])  #取前100個特徵向量(根據特徵值大小從到小排列)
    return x_reduced
    
def my_pca2(x, n=100):
    #對x進行PCA將維
    x = x-np.mean(x, axis=0)
    cov = np.dot(x.T, x)/(x.shape[0] -1)  #x的協方差矩陣, (n-1:除以n-1表示是樣本方差; 如果全部樣本的方差則除以n;參見https://www.cnblogs.com/datamining-bio/p/9267759.html)
    #cov = np.cov(x, rowvar=0)  #計算協方差矩陣,注意此處rowvar=0表示,一列數據表示一個特徵/一個維度
    eig_values, eig_vector = np.linalg.eig(cov)   #特徵值分解
    index = np.argsort(-eig_values)[:n]     #按特徵值排序,挑選出特徵值最大的100個特徵值index,隨後取出其對應的100個特徵向量
    x_reduced = np.dot(x, eig_vector[:, :n])  #取前100個特徵向量(根據特徵值大小從到小排列)
    return x_reduced

if __name__ == "__main__":
    data = np.random.randn(1000, 500)   #表示1000條數據,一條數據有500個特徵
    r1 = my_pca(data)
    r2= my_pca2(data)
    

    pca=PCA(n_components=100)     #加載PCA算法,設置降維後主成分數目爲2
    reduced_x=pca.fit_transform(data)#對樣本進行降維
    
    #三種方法計算出來的降維數據並不近似相等,不是很理解??
    print(np.allclose(r1, reduced_x, equal_nan=True))   #False
    print(np.allclose(r2, reduced_x,  equal_nan=True))  #False
    print(np.allclose(r2, r1, equal_nan=True))          #False
    
 
    
降維方法對比

   3.2 圖片加強

    對圖片進行旋轉,顏色轉換,投影變換等來加強圖片的多樣性,減小模型訓練過擬合,使模型更加魯棒,簡單代碼以下:

#!/usr/bin/env python
#coding:utf-8



import cv2 as cv
import numpy as np
import random
import argparse
import os

def crop(img,roi):
    """
    img: 圖像矩陣
    roi: 待截取的區域,格式爲:(top,bottom,left,right)
    """
    top,bottom,left,right=roi
    bottom = img.shape[0] if bottom>img.shape[0] else bottom
    right = img.shape[1] if right>img.shape[1] else right
    return img[top:bottom,left:right]
    
def color_shift(img,shift=50):
    """
    img: 圖像矩陣
    shift: 像素值偏移值
    """
    r_offset = random.randint(-abs(shift),abs(shift))
    g_offset = random.randint(-abs(shift),abs(shift))
    b_offset = random.randint(-abs(shift),abs(shift))
    img = img+np.array([b_offset,g_offset,r_offset])
    img[img<0]=0
    img[img>255]=255
    return img
    
def rotate(img,angle=30,scale=1):
    """
    img: 圖像矩陣
    angle: 選轉角度
    scale:縮放因子
    """
    rows,cols = img.shape[:2]
    M = cv.getRotationMatrix2D((int(cols/2),int(rows/2)),angle,scale)
    img_rotate = cv.warpAffine(img,M,(cols,rows))
    return img_rotate
def perspective(img,random_margin=60):
    """
    img: 圖像矩陣
    random_margin: 隨機值,用來生成座標
    """
    height,width = img.shape[:2]
    x1 = random.randint(-random_margin, random_margin)
    y1 = random.randint(-random_margin, random_margin)
    x2 = random.randint(width - random_margin - 1, width - 1)
    y2 = random.randint(-random_margin, random_margin)
    x3 = random.randint(width - random_margin - 1, width - 1)
    y3 = random.randint(height - random_margin - 1, height - 1)
    x4 = random.randint(-random_margin, random_margin)
    y4 = random.randint(height - random_margin - 1, height - 1)

    dx1 = random.randint(-random_margin, random_margin)
    dy1 = random.randint(-random_margin, random_margin)
    dx2 = random.randint(width - random_margin - 1, width - 1)
    dy2 = random.randint(-random_margin, random_margin)
    dx3 = random.randint(width - random_margin - 1, width - 1)
    dy3 = random.randint(height - random_margin - 1, height - 1)
    dx4 = random.randint(-random_margin, random_margin)
    dy4 = random.randint(height - random_margin - 1, height - 1)

    pts1 = np.float32([[x1, y1], [x2, y2], [x3, y3], [x4, y4]])
    pts2 = np.float32([[dx1, dy1], [dx2, dy2], [dx3, dy3], [dx4, dy4]])
    M = cv.getPerspectiveTransform(pts1,pts2)
    img_pers = cv.warpPerspective(img,M,(width,height))
    return img_pers

# def perspective(img,pos1,pos2):
    # """
    # img: 圖像矩陣
    # pos1: 原圖像中四組座標值, 格式爲((x1,y1),(x2,y2),(x3,y3),(x4,y4))
    # pos2 投影變換後圖像中四組座標值,格式同from
    # """
    # pos1 = np.float32(pos1)
    # pos2 = np.float32(pos2)
    # M = cv.getPerspectiveTransform(pos1,pos2)
    # img_pers = cv.warpPerspective(img,M,(img.shape[1],img.shape[0]))
    # return img_pers

def argparser():
    parser = argparse.ArgumentParser()
    parser.add_argument("--img_input", type=str,help="輸入圖片路徑")
    parser.add_argument("--img_output", type=str,help="輸出圖片路徑")
    parser.add_argument("--roi", nargs='*',type=int,help="待截取的區域,格式爲:(top,bottom,left,right)")
    parser.add_argument("--shift",type=int,help="像素值偏移值")
    parser.add_argument("--angle",type=float,help="旋轉角度")
    parser.add_argument("--scale",type=float,help="縮放因子")
    parser.add_argument("--margin",type=int,help="隨機值,用來生成座標")
    
    return parser.parse_args()

if __name__=="__main__":
    parse = argparser()
    img_dir = parse.img_input
    out_dir = parse.img_output
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)
    if os.path.exists(img_dir):
        for file in os.listdir(img_dir):
            if file.endswith(("jpg","png")):
                img = cv.imread(os.path.join(img_dir,file))
                if parse.roi and len(parse.roi)==4:
                    img = crop(img,parse.roi)
                if parse.shift:
                    img = color_shift(img,parse.shift)
                if parse.angle and parse.scale:
                    img = rotate(img,parse.angle,parse.scale)
                if parse.margin:
                    img = perspective(img,parse.margin)
                cv.imwrite(os.path.join(out_dir,file),img)
    
圖像加強

 

 

參考:https://www.jianshu.com/p/03009cfdf733

    https://zhuanlan.zhihu.com/p/39076763

         https://zhuanlan.zhihu.com/p/79981927

    https://segmentfault.com/a/1190000012668819?utm_source=tag-newest

相關文章
相關標籤/搜索