目錄
1. 介紹
2. 關(guān)于分割中的 resize 問題
3. 分割的 transform
3.1 隨機(jī)縮放?RandomResize
3.2 隨機(jī)水平翻轉(zhuǎn)?RandomHorizontalFlip
3.3?隨機(jī)豎直翻轉(zhuǎn)?RandomVerticalFlip
3.4 隨機(jī)裁剪?RandomCrop
3.5 ToTensor
3.6 normalization
3.7?Compose
3.8 中心裁剪
3.9 Resize 縮放
4. 預(yù)處理結(jié)果可視化
1. 介紹
圖像分割的預(yù)處理不像分類那樣好操作,因?yàn)榉诸惖膌abel就是一個(gè)類別,圖像增強(qiáng)的操作都是對(duì)原始圖像操作的。
圖像分割的label和img是嚴(yán)格對(duì)應(yīng)的,或者說兩者的空間分辨率(h*w)一樣且像素點(diǎn)的對(duì)應(yīng)位置都不能改變。否則,監(jiān)督學(xué)習(xí)就失去了作用。而深度學(xué)習(xí)中,數(shù)據(jù)增強(qiáng)是不必可少的,對(duì)于數(shù)據(jù)更少的醫(yī)學(xué)圖像來說,數(shù)據(jù)增強(qiáng)更加必不可少。
所以,本章主要聊聊 圖像分割中的數(shù)據(jù)增強(qiáng)
下面是DRIVE數(shù)據(jù)集的預(yù)處理可視化:
2. 關(guān)于分割中的 resize 問題
圖像分割的resize問題,本人一直沒有弄明白....
分割最后的目的應(yīng)該是將前景從圖像中口出來,那么兩者的尺寸肯定需要保證一樣的。然而,例如unet一類的網(wǎng)絡(luò)輸入都是固定大小480*480,那么最后分割的分辨率也就是480*480,顯然已經(jīng)和最原始的圖像不一樣了
現(xiàn)在不管是分類,還是分割網(wǎng)絡(luò)的輸入都不需要和原論文一致了,網(wǎng)絡(luò)里面都做了優(yōu)化,例如最大池化層等等....
不管網(wǎng)絡(luò)如何優(yōu)化,大部分預(yù)處理中都增加了resize的操作。那么能保證輸入圖像和輸出的分辨率一樣,但是最原始的圖像還是不能一致。例如,原始512*512,resize 480*480 輸入給網(wǎng)絡(luò)產(chǎn)生 480*480 的分割圖像,480和512已經(jīng)不一樣了
雖然最后分割出來的圖像也可以通過resize還原成最原始的尺寸。但是插值又成了問題,更好的線性插值會(huì)導(dǎo)致分割圖像的灰度值改變。例如分割的圖是二值圖像,背景為0前景為255,通過插值會(huì)導(dǎo)致出現(xiàn)0-255的任何一個(gè)數(shù)字,變成了灰度圖像。當(dāng)然最近鄰插值可以避免這一問題,但最近鄰插值顯然在圖像處理中不是一個(gè)好的選擇。
之前想過,用雙線性插值resize分割圖像,然后利用閾值處理產(chǎn)生二值圖。但是,這樣的方法不僅僅麻煩,還有很多的問題,且違背了end to end的思想
下面純個(gè)人瞎想...僅供參考...
所以說,解決的辦法就是訓(xùn)練的圖像隨機(jī)的resize,例如需要給網(wǎng)絡(luò)的輸入是480*480,那么隨機(jī)將訓(xùn)練圖像變成縮放成例如300-500之之間任意的大小,再裁剪成480*480輸入給分割網(wǎng)絡(luò)
這樣的好處就是,網(wǎng)絡(luò)就不會(huì)對(duì)單純的圖像縮放敏感
那么,再隨機(jī)分割的時(shí)候,就不需要resize,直接輸入原圖就行了
3. 分割的 transform
如下,分割任務(wù)中圖像預(yù)處理的測(cè)試代碼
其中就只要保證img和label是同時(shí)變換即可
3.1 隨機(jī)縮放?RandomResize
如下,在給定的min和max直接隨機(jī)生成一個(gè)整數(shù),然后resize即可。
分割的label圖像要采用最近鄰算法,否則resize之后的label就不是二值圖像
class RandomResize(object):
def __init__(self, min_size, max_size=None):
self.min_size = min_size
if max_size is None:
max_size = min_size
self.max_size = max_size
def __call__(self, image, target):
size = random.randint(self.min_size, self.max_size)
# 這里size傳入的是int類型,所以是將圖像的最小邊長(zhǎng)縮放到size大小
image = F.resize(image, size)
target = F.resize(target, size, interpolation=T.InterpolationMode.NEAREST)
return image, target
3.2 隨機(jī)水平翻轉(zhuǎn)?RandomHorizontalFlip
flip_prob 就是翻轉(zhuǎn)的概率
class RandomHorizontalFlip(object):
def __init__(self, flip_prob):
self.flip_prob = flip_prob
def __call__(self, image, target):
if random.random() < self.flip_prob:
image = F.hflip(image)
target = F.hflip(target)
return image, target
3.3?隨機(jī)豎直翻轉(zhuǎn)?RandomVerticalFlip
和水平翻轉(zhuǎn)的一樣
class RandomVerticalFlip(object):
def __init__(self, flip_prob):
self.flip_prob = flip_prob
def __call__(self, image, target):
if random.random() < self.flip_prob:
image = F.vflip(image)
target = F.vflip(target)
return image, target
3.4 隨機(jī)裁剪?RandomCrop
隨機(jī)裁剪的代碼如下,需要注意的是,因?yàn)閳D像很可能不足裁剪的大小,所以需要填充
class RandomCrop(object):
def __init__(self, size):
self.size = size
def __call__(self, image, target):
image = pad_if_smaller(image, self.size)
target = pad_if_smaller(target, self.size, fill=255)
crop_params = T.RandomCrop.get_params(image, (self.size, self.size))
image = F.crop(image, *crop_params)
target = F.crop(target, *crop_params)
return image, target
填充的代碼,這里填充255代表不敢興趣的區(qū)域
def pad_if_smaller(img, size, fill=0):
# 如果圖像最小邊長(zhǎng)小于給定size,則用數(shù)值fill進(jìn)行padding
min_size = min(img.size)
if min_size < size:
ow, oh = img.size
padh = size - oh if oh < size else 0
padw = size - ow if ow < size else 0
img = F.pad(img, (0, 0, padw, padh), fill=fill)
return img
3.5 ToTensor
這里label不能進(jìn)行官方實(shí)現(xiàn)的totensor方法,因?yàn)闅w一化,前景像素的灰度值就會(huì)被改變
dtype 是因?yàn)橐褂媒徊骒負(fù)p失,需要為整型,且label的維度中不能有channel
class ToTensor(object):
def __call__(self, image, target):
image = F.to_tensor(image)
target = torch.as_tensor(np.array(target), dtype=torch.int64)
return image, target
3.6 normalization
normalization 的實(shí)現(xiàn)也很簡(jiǎn)單
class Normalize(object):
def __init__(self, mean, std):
self.mean = mean
self.std = std
def __call__(self, image, target):
image = F.normalize(image, mean=self.mean, std=self.std)
return image, target
3.7?Compose
將transform 逐個(gè)實(shí)現(xiàn)就行了
class Compose(object):
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, image, target):
for t in self.transforms:
image, target = t(image, target)
return image, target
3.8 中心裁剪
class CenterCrop(object):
def __init__(self, size):
self.size = size
def __call__(self, image, target):
image = F.center_crop(image, self.size)
target = F.center_crop(target, self.size)
return image, target
3.9 Resize 縮放
? ? ? ? image = F.resize(image,(self.size,self.size)) 可以縮放成width = height
# 將圖像和標(biāo)簽縮放
class Resize(object):
def __init__(self, size:list):
self.size = size
def __call__(self, image, target):
image = F.resize(image, self.size)
target = F.resize(target, self.size, interpolation=T.InterpolationMode.NEAREST)
return image, target
4. 預(yù)處理結(jié)果可視化
dataset里面改成這樣就行了
加載完數(shù)據(jù)這樣調(diào)用即可
測(cè)試代碼:
label 中灰度值只有0 1 255
label 中沒有 channel
# 可視化數(shù)據(jù)
def plot(data_loader):
plt.figure(figsize=(12,8))
imgs,labels = data_loader
for i,(x,y) in enumerate(zip(imgs,labels)):
x = np.transpose(x.numpy(),(1,2,0))
x[:,:,0] = x[:,:,0]*0.127 + 0.709 # 去 normalization
x[:,:,1] = x[:,:,1]*0.079 + 0.381
x[:,:,2] = x[:,:,2]*0.043 + 0.224
y = y.numpy()
# print(np.unique(y)) # 0 1 255
# print(x.shape) # 480*480*3
# print(y.shape) # 480*480
plt.subplot(2,4,i+1)
plt.imshow(x)
plt.subplot(2,4,i+5)
plt.imshow(y)
plt.show()
顯示結(jié)果:
在dataset 里面,將前景像素改成120,就可以看到label的細(xì)節(jié)
文章來源:http://www.zghlxwxcb.cn/news/detail-440689.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-440689.html
到了這里,關(guān)于關(guān)于圖像分割的預(yù)處理 transform的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!