关于图像分割的预处理 transform

目录

1. 介绍

2. 关于分割中的 resize 问题

3. 分割的 transform

3.1 随机缩放 RandomResize

3.2 随机水平翻转 RandomHorizontalFlip

3.3 随机竖直翻转 RandomVerticalFlip

3.4 随机裁剪 RandomCrop

3.5 ToTensor

3.6 normalization

3.7 Compose

3.8 中心裁剪

3.9 Resize 缩放

4. 预处理结果可视化


1. 介绍

图像分割的预处理不像分类那样好操作,因为分类的label就是一个类别,图像增强的操作都是对原始图像操作的。

图像分割的label和img是严格对应的,或者说两者的空间分辨率(h*w)一样且像素点的对应位置都不能改变。否则,监督学习就失去了作用。而深度学习中,数据增强是不必可少的,对于数据更少的医学图像来说,数据增强更加必不可少。

所以,本章主要聊聊 图像分割中的数据增强

下面是DRIVE数据集的预处理可视化:

2. 关于分割中的 resize 问题

图像分割的resize问题,本人一直没有弄明白....

分割最后的目的应该是将前景从图像中口出来,那么两者的尺寸肯定需要保证一样的。然而,例如unet一类的网络输入都是固定大小480*480,那么最后分割的分辨率也就是480*480,显然已经和最原始的图像不一样了

现在不管是分类,还是分割网络的输入都不需要和原论文一致了,网络里面都做了优化,例如最大池化层等等....

不管网络如何优化,大部分预处理中都增加了resize的操作。那么能保证输入图像和输出的分辨率一样,但是最原始的图像还是不能一致。例如,原始512*512,resize 480*480 输入给网络产生 480*480 的分割图像,480和512已经不一样了

虽然最后分割出来的图像也可以通过resize还原成最原始的尺寸。但是插值又成了问题,更好的线性插值会导致分割图像的灰度值改变。例如分割的图是二值图像,背景为0前景为255,通过插值会导致出现0-255的任何一个数字,变成了灰度图像。当然最近邻插值可以避免这一问题,但最近邻插值显然在图像处理中不是一个好的选择。

之前想过,用双线性插值resize分割图像,然后利用阈值处理产生二值图。但是,这样的方法不仅仅麻烦,还有很多的问题,且违背了end to end的思想

下面纯个人瞎想...仅供参考...

所以说,解决的办法就是训练的图像随机的resize,例如需要给网络的输入是480*480,那么随机将训练图像变成缩放成例如300-500之之间任意的大小,再裁剪成480*480输入给分割网络

这样的好处就是,网络就不会对单纯的图像缩放敏感

那么,再随机分割的时候,就不需要resize,直接输入原图就行了

3. 分割的 transform

如下,分割任务中图像预处理的测试代码

其中就只要保证img和label是同时变换即可

3.1 随机缩放 RandomResize

如下,在给定的min和max直接随机生成一个整数,然后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类型,所以是将图像的最小边长缩放到size大小
        image = F.resize(image, size)
        target = F.resize(target, size, interpolation=T.InterpolationMode.NEAREST)
        return image, target

3.2 随机水平翻转 RandomHorizontalFlip

flip_prob 就是翻转的概率

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 随机竖直翻转 RandomVerticalFlip

和水平翻转的一样

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 随机裁剪 RandomCrop

随机裁剪的代码如下,需要注意的是,因为图像很可能不足裁剪的大小,所以需要填充

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代表不敢兴趣的区域

def pad_if_smaller(img, size, fill=0):
    # 如果图像最小边长小于给定size,则用数值fill进行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不能进行官方实现的totensor方法,因为归一化,前景像素的灰度值就会被改变

dtype 是因为要使用交叉熵损失,需要为整型,且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 的实现也很简单

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 逐个实现就行了

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

# 将图像和标签缩放
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. 预处理结果可视化

dataset里面改成这样就行了

加载完数据这样调用即可

测试代码:

label 中灰度值只有0 1 255

label 中没有 channel

# 可视化数据
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()

显示结果:

在dataset 里面,将前景像素改成120,就可以看到label的细节

 

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
图像分割是将一幅图像分成若干个互不重叠的区域,每个区域内的像素具有相似的特征,这些特征包括颜色、纹理、亮度等等。图像分割可以作为分类的预处理,通过分割得到的区域可以提取出更加准确的特征,从而提高分类的准确率。 在Python中,可以使用OpenCV库进行图像分割。以下是一个简单的图像分割重组的示例代码: ```python import cv2 import numpy as np # 读取图像 img = cv2.imread('test.jpg') # 对图像进行分割 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) kernel = np.ones((3,3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) sure_bg = cv2.dilate(opening, kernel, iterations=3) dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, sure_fg = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0) sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg, sure_fg) ret, markers = cv2.connectedComponents(sure_fg) markers = markers + 1 markers[unknown==255] = 0 markers = cv2.watershed(img, markers) img[markers == -1] = [255,0,0] # 显示分割结果 cv2.imshow('image',img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在上述代码中,通过对图像进行二值化、形态学操作、距离变换等处理,得到了图像的分割结果。然后使用Watershed算法进行重组,最终得到了分割后的图像。可以按照自己的需求进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听风吹等浪起

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值