Camelyon16数据集切块批量预处理

news2024/11/13 10:07:32

参考自: Camelyon16数据集切块预处理
区别是这里做了批量处理

数据集目录格式:
在这里插入图片描述

** main.py**

# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2024/9/4 20:21
# @Author  : 猫娜Lisa
# @File    : camelyon16_get_patch.py
# @Software: PyCharm


import os
import json
import numpy as np
import xml.etree.ElementTree as ET
import openslide  # 这个的下载有点子麻烦的哦
import cv2
import shutil
from PIL import Image
from skimage.color import rgb2hsv
from skimage.filters import threshold_otsu
from multiprocessing import Pool
from pathos.multiprocessing import ProcessingPool as paPool

from config import train_root, test_root
from config import root, wsi_dir
from config import xml_dir, json_dir
from config import tumor_npy_dir, tissue_npy_dir, no_tumor_npy_dir
from config import tumor_txt_dir, no_tumor_txt_dir, normal_txt_dir
from config import tumor_patch_dir, no_tumor_patch_dir, normal_patch_dir
from config import level, RGB_min, patch_number, patch_size, level_patch, num_process
from config import if_trans_xml_to_json, if_get_tumor_mask_npy, if_get_tissue_mask_npy, if_get_no_tumor_mask_npy, if_get_sample_txt
from config import (if_get_train_tumor_patches, if_get_train_no_tumor_patches, if_get_train_normal_patches,
                    if_get_test_tumor_patches, if_get_test_no_tumor_patches, if_get_test_normal_patches)


# 将xml标注转换为json格式
# 每个注释都是多边形列表,其中每个多边形都由其顶点表示。阳性多边形表示肿瘤区域,阴性多边形表示正常区域。在本阶段,将标注格式转换成更简单的 .json 格式。
# xml_dir, json_dir, wsi_names
def camelyon16xml2json(xml_dir, json_dir, wsi_names):
    """
    Convert an annotation of camelyon16 xml format into a json format.
    Arguments:
        inxml: string, path to the input camelyon16 xml format
        outjson: string, path to the output json format
    """

    for wsi_name in wsi_names:
        inxml = xml_dir + wsi_name + '.xml'
        outjson = json_dir + wsi_name + '.json'

        root = ET.parse(inxml).getroot()
        annotations_tumor = \
            root.findall('./Annotations/Annotation[@PartOfGroup="Tumor"]')
        annotations_0 = \
            root.findall('./Annotations/Annotation[@PartOfGroup="_0"]')
        annotations_1 = \
            root.findall('./Annotations/Annotation[@PartOfGroup="_1"]')
        annotations_2 = \
            root.findall('./Annotations/Annotation[@PartOfGroup="_2"]')
        annotations_positive = \
            annotations_tumor + annotations_0 + annotations_1
        annotations_negative = annotations_2

        json_dict = {}
        json_dict['positive'] = []
        json_dict['negative'] = []

        for annotation in annotations_positive:
            X = list(map(lambda x: float(x.get('X')),
                         annotation.findall('./Coordinates/Coordinate')))
            Y = list(map(lambda x: float(x.get('Y')),
                         annotation.findall('./Coordinates/Coordinate')))
            vertices = np.round([X, Y]).astype(int).transpose().tolist()
            name = annotation.attrib['Name']
            json_dict['positive'].append({'name': name, 'vertices': vertices})

        for annotation in annotations_negative:
            X = list(map(lambda x: float(x.get('X')),
                         annotation.findall('./Coordinates/Coordinate')))
            Y = list(map(lambda x: float(x.get('Y')),
                         annotation.findall('./Coordinates/Coordinate')))
            vertices = np.round([X, Y]).astype(int).transpose().tolist()
            name = annotation.attrib['Name']
            json_dict['negative'].append({'name': name, 'vertices': vertices})

        with open(outjson, 'w') as f:
            json.dump(json_dict, f, indent=1)


# 获得tumor区域的mask
# 本阶段利用json标注得到tumor区域的mask文件,格式为 .npy 。
def get_tumor_mask(wsi_dir, level, json_dir, tumor_npy_dir, wsi_names):
    for wsi_name in wsi_names:
        wsi_path = wsi_dir + wsi_name + '.tif'
        json_path = json_dir + wsi_name + '.json'
        tumor_npy_path = tumor_npy_dir + wsi_name + '.npy'
        slide = openslide.OpenSlide(wsi_path)
        w, h = slide.level_dimensions[level]
        mask_tumor = np.zeros((h, w))  # the init mask, and all the value is 0

        factor = slide.level_downsamples[level]  # get the factor of level * e.g. level 6 is 2^6

        with open(json_path) as f:
            dicts = json.load(f)
        tumor_polygons = dicts['positive']
        for tumor_polygon in tumor_polygons:
            # plot a polygon
            vertices = np.array(tumor_polygon["vertices"]) / factor
            vertices = vertices.astype(np.int32)

            cv2.fillPoly(mask_tumor, [vertices], (255))

        mask_tumor = mask_tumor[:] > 127
        mask_tumor = np.transpose(mask_tumor)
        np.save(tumor_npy_path, mask_tumor)  # 获得Tumor_001.tif在level_6下的tumor区域掩码


# 获得tissue区域的mask
# 使用大津算法进行图像分割即可获得组织区域。RGB_min可以手动调整,确定最低阈值。可以将tissue_mask转化为二值图像保存下来看看效果.
def get_tissue_mask(wsi_dir, level, tissue_npy_dir, RGB_min, wsi_names):
    for wsi_name in wsi_names:
        wsi_path = wsi_dir + wsi_name + '.tif'
        tissue_npy_path = tissue_npy_dir + wsi_name + '.npy'

        slide = openslide.OpenSlide(wsi_path)
        img_RGB = np.transpose(np.array(slide.read_region((0, 0),
                                                          level,
                                                          slide.level_dimensions[level]).convert('RGB')),
                               axes=[1, 0, 2])
        img_HSV = rgb2hsv(img_RGB)

        background_R = img_RGB[:, :, 0] > threshold_otsu(img_RGB[:, :, 0])
        background_G = img_RGB[:, :, 1] > threshold_otsu(img_RGB[:, :, 1])
        background_B = img_RGB[:, :, 2] > threshold_otsu(img_RGB[:, :, 2])
        tissue_RGB = np.logical_not(background_R & background_G & background_B)
        tissue_S = img_HSV[:, :, 1] > threshold_otsu(img_HSV[:, :, 1])
        min_R = img_RGB[:, :, 0] > RGB_min
        min_G = img_RGB[:, :, 1] > RGB_min
        min_B = img_RGB[:, :, 2] > RGB_min

        tissue_mask = tissue_S & tissue_RGB & min_R & min_G & min_B

        np.save(tissue_npy_path, tissue_mask)  # 获得Tumor_001.tif在level_6下的组织掩码
        # img = Image.fromarray(tissue_mask)
        # img.save('tumor_001_tissue.png') # 可以保存二值图像看看效果如何


# 获得no_tumor区域的mask
# tissue区域包含了tumor和no_tumor,所以只需要通过tissue_mask和tumor_mask做一下逻辑运算即可得到no_tumor区域的mask。
def get_no_tumor_mask(tumor_npy_dir, tissue_npy_dir, no_tumor_npy_dir, wsi_names):
    for wsi_name in wsi_names:
        tumor_npy_path = tumor_npy_dir + wsi_name + '.npy'
        tissue_npy_path = tissue_npy_dir + wsi_name + '.npy'
        no_tumor_npy_path = no_tumor_npy_dir + wsi_name + '.npy'

        tumor_mask = np.load(tumor_npy_path)
        tissue_mask = np.load(tissue_npy_path)
        no_tumor_mask = tissue_mask & (~ tumor_mask)

        np.save(no_tumor_npy_path, no_tumor_mask)


# 随机采样各组织(tumor、no_tumor)区域。
# 一张WSI就可以切出来成千上万块patch,但并不需要全部的,只需要在每张WSI中采样出一定数量就可以了。
# 采样原理比较简单,由于前面拿到的都是WSI 在level 6 下的mask,大概1k * 2k的分辨率,直接在低分辨率的mask中采样一些点,
# 得到采样点在level 6下的坐标,再乘以缩放倍数就能算出他们在level 0 下的坐标(patch的中心点坐标)。得到采样坐标txt文件。
def sample_from_mask(npy_dir, patch_number, level, txt_dir, wsi_names):
    for wsi_name in wsi_names:
        npy_path = npy_dir + wsi_name + '.npy'
        txt_path = txt_dir + wsi_name + '.txt'
        mask_tissue = np.load(npy_path)
        X_idcs, Y_idcs = np.where(mask_tissue)
        centre_points = np.stack(np.vstack((X_idcs.T, Y_idcs.T)), axis=1)
        if centre_points.shape[0] > patch_number:
            sampled_points = centre_points[np.random.randint(centre_points.shape[0],
                                                             size=patch_number), :]
        else:
            sampled_points = centre_points  # 点数不够就全要

        sampled_points = (sampled_points * 2 ** level).astype(np.int32)  # make sure the factor
        mask_only_name = os.path.split(npy_path)[-1].split(".")[0]
        name = np.full((sampled_points.shape[0], 1), mask_only_name)
        center_points = np.hstack((name, sampled_points))

        with open(txt_path, "a") as f:
            np.savetxt(f, center_points, fmt="%s", delimiter=",")


# 得到patch数据集
# 根据采样点的坐标,在level 0 下切割WSI即可得到patch。需要对tumor和no_tumor分别操作,
# 得到两类patch。还需要对测试集切块,都是一样的流程。仅以训练集的tumor切块举例。
def process(opts):  # , patch_size, wsi_path, level_patch, patch_dir
    j, pid, x_center, y_center, wsi_path, patch_size, level_patch, patch_dir = opts
    x = int(int(x_center) - patch_size / 2)
    y = int(int(y_center) - patch_size / 2)
    slide = openslide.OpenSlide(wsi_path)
    img = slide.read_region((x,y),level_patch,(patch_size,patch_size)).convert('RGB')
    img.save(os.path.join(patch_dir,pid+'_'+str(100000+j)+'.png'))


# 得到patch数据集
def get_patches(txt_dir, wsi_dir, num_process, patch_size, level_patch, patch_dir, wsi_names):
    for wsi_name in wsi_names:
        txt_path = txt_dir + wsi_name + '.txt'
        wsi_path = wsi_dir + wsi_name + '.tif'
        opt_list = []
        with open(txt_path) as f:
            for j, line in enumerate(f):
                pid, x_center, y_center = line.strip('\n').split(',')
                # pid为不带后缀的文件名字,如tumor_001
                opt_list.append((j, pid, x_center, y_center, wsi_path, patch_size, level_patch, patch_dir))
                # print(j)
        pool = Pool(processes=num_process)
        # print(opt_list)
        pool.map(process, opt_list)

        # pool.close()
        # pool.join()


def camelyon16_process(wsi_names):

    # 将xml标注转换为json格式
    if if_trans_xml_to_json:
        shutil.rmtree(json_dir)
        os.mkdir(json_dir)
        camelyon16xml2json(xml_dir, json_dir, wsi_names)

    # 获得tumor区域的mask
    if if_get_tumor_mask_npy:
        shutil.rmtree(tumor_npy_dir)
        os.mkdir(tumor_npy_dir)
        get_tumor_mask(wsi_dir, level, json_dir, tumor_npy_dir, wsi_names)

    # 获得tissue区域的mask
    if if_get_tissue_mask_npy:
        shutil.rmtree(tissue_npy_dir)
        os.mkdir(tissue_npy_dir)
        get_tissue_mask(wsi_dir, level, tissue_npy_dir, RGB_min, wsi_names)

    # 获得no_tumor区域的mask
    if if_get_no_tumor_mask_npy:
        shutil.rmtree(no_tumor_npy_dir)
        os.mkdir(no_tumor_npy_dir)
        get_no_tumor_mask(tumor_npy_dir, tissue_npy_dir, no_tumor_npy_dir, wsi_names)

    if if_get_train_tumor_patches or if_get_test_tumor_patches:
        npy_dir = tumor_npy_dir
        patch_dir = tumor_patch_dir
        txt_dir = tumor_txt_dir
    elif if_get_train_no_tumor_patches or if_get_test_no_tumor_patches:
        npy_dir = no_tumor_npy_dir
        patch_dir = no_tumor_patch_dir
        txt_dir = no_tumor_txt_dir
    elif if_get_train_normal_patches or if_get_test_normal_patches:
        npy_dir = tissue_npy_dir
        patch_dir = normal_patch_dir
        txt_dir = normal_txt_dir
    else:
        assert False

    #  随机采样各组织(tumor、no_tumor)区域。
    if if_get_sample_txt:
        shutil.rmtree(txt_dir)
        os.mkdir(txt_dir)
        sample_from_mask(npy_dir, patch_number, level, txt_dir, wsi_names)

    # 得到patch数据集
    shutil.rmtree(patch_dir)
    os.mkdir(patch_dir)
    get_patches(txt_dir, wsi_dir, num_process, patch_size, level_patch, patch_dir, wsi_names)


if __name__=='__main__':
    # 获取wsi文件夹下所有的tif文件名
    wsi_names = []
    if if_get_train_tumor_patches or if_get_train_no_tumor_patches or if_get_train_normal_patches:
        for wsi_name in os.listdir(wsi_dir):
            if os.path.splitext(wsi_name)[1] == '.tif':
                wsi_names.append(wsi_name.split('.')[0])
    elif if_get_test_tumor_patches or if_get_test_no_tumor_patches:
        wsi_all_names = []
        for wsi_name in os.listdir(wsi_dir):
            if os.path.splitext(wsi_name)[1] == '.tif':
                wsi_all_names.append(wsi_name.split('.')[0])
        xml_names = []
        for xml_name in os.listdir(xml_dir):
            if os.path.splitext(xml_name)[1] == '.xml':
                xml_names.append(xml_name.split('.')[0])
        wsi_names = list(set(wsi_all_names)&set(xml_names))
    else:
        wsi_all_names = []
        for wsi_name in os.listdir(wsi_dir):
            if os.path.splitext(wsi_name)[1] == '.tif':
                wsi_all_names.append(wsi_name.split('.')[0])
        xml_names = []
        for xml_name in os.listdir(xml_dir):
            if os.path.splitext(xml_name)[1] == '.xml':
                xml_names.append(xml_name.split('.')[0])
        wsi_names = list(set(wsi_all_names).difference(set(wsi_all_names) & set(xml_names)))

    print(wsi_names)
    camelyon16_process(wsi_names)


config.py

# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2024/9/5 20:18
# @Author  : 猫娜Lisa
# @File    : config.py
# @Software: PyCharm

train_root = 'xxx\\CAMELYON16\\train\\'
test_root = 'xxx\\CAMELYON16\\test\\'

# train
root = train_root
# tumor
wsi_dir = train_root + 'tumor\\'
# normal
# wsi_dir = train_root + 'normal\\'

# test
# root = test_root
# wsi_dir = test_root + 'image\\'

xml_dir = root + 'util_annotations\\lesion_annotations\\'
json_dir = root + 'util_annotations\\json_annotations\\'

tumor_npy_dir = root + 'util_tumor\\tumor_npy\\'
no_tumor_npy_dir = root + 'util_no_tumor\\no_tumor_npy\\'
tissue_npy_dir = root + 'util_tissue_npy\\'  # mask文件输出路径

tumor_txt_dir = root + 'util_tumor\\tumor_txt\\'
no_tumor_txt_dir = root + 'util_no_tumor\\no_tumor_txt\\'
normal_txt_dir = root + 'util_normal\\normal_txt\\'

tumor_patch_dir = root + 'train_patch\\tumor'  # patch输出文件夹路径
no_tumor_patch_dir = root + 'train_patch\\no_tumor'  # patch输出文件夹路径
normal_patch_dir = root + 'train_patch\\normal'  # patch输出文件夹路径

level = 6  # at which WSI level to obtain the mask
RGB_min = 50  # min value for RGB channel
patch_number = 10  # 采样点数 1000
patch_size = 224  # patch 的尺寸 默认256*256大小
level_patch = 0  # 默认在level 0 切割WSI
num_process = 2  # 进程数,使用多进程切块要快得多 16

# 
if_trans_xml_to_json = True
if_get_tumor_mask_npy = True
if_get_tissue_mask_npy = True
if_get_no_tumor_mask_npy = True
if_get_sample_txt = True

#
if_get_train_tumor_patches = True
if_get_train_no_tumor_patches = False
if_get_train_normal_patches = False
if_get_test_tumor_patches = False
if_get_test_no_tumor_patches = False
if_get_test_normal_patches = False

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2107800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux从0到1——基础IO(下)【磁盘/文件系统/软硬链接/动静态库】

Linux从0到1——基础IO(下) 0. 前言1. 磁盘1.1 物理存储结构1.2 磁盘的逻辑存储结构 2. 文件系统2.1 文件系统的管理思想2.2 详解块组2.3 文件名和inode2.4 挂载 3. 软硬链接3.1 引入3.2 软链接3.3 硬链接 4. 动静态库4.1 静态库4.2 动态库 0. 前言 前面…

强化学习之PPO算法

PPO算法 什么是PPO算法PPO算法的原理PPO-Clip的目标函数 什么是PPO算法 PPO算法,全称为Proximal Policy Optimization(近端策略优化),是一种强化学习算法。它由OpenAI在2017年提出,旨在解决策略梯度方法中的一些问题&…

带你0到1之QT编程:六、打地基QList的高效用法

此为QT编程的第六谈!关注我,带你快速学习QT编程的学习路线! 每一篇的技术点都是很很重要!很重要!很重要!但不冗余! 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点! 码…

【系统架构设计师-2012年】综合知识-答案及详解

更多内容请见: 备考系统架构设计师-核心总结索引 文章目录 【第1~2题】【第3~4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10~11题】【第12~13题】【第14~19题】【第20~21题】【第22~24题】【第25~26题】【第27~31题】【第32~33题】【第34~36题】【第37…

南京大学机试试题合集

🍰🍰🍰hello宝子们,今天我们来练习南京大学的机试题目,这些题目的缺点就是太老了,都是18或19年的题,大家就练练手。加油!fighting!( •̀ ω •́ )✧ 🍩1161…

geodatatool(地图资源工具)3.8

geodatatool(地图资源工具)3.8(新)修复更新,修复更新包括: 1.优化在线地图多线程下载及用户体验。 注意:在线地图下载与背景地图显示用的是同样的源,所以在线地图是否能下载&#x…

通过Jflash合并程序以 BOOT + APP 合并为例

打开【jflash】新建一个JFash工程 建好后界面如下 打开【File】下面的【Open data file…】 找到Boot程序所在位置 打开后界面如下,可以看到hex中的数据 点击【File】下面的【Merge data file…】 打开应用程序 查看APP地址区域是否有数据&#xff0c…

备战秋招60天算法挑战,Day32

题目链接: https://leetcode.cn/problems/house-robber-ii/ 视频题解: https://www.bilibili.com/video/BV1WRYKeKEQE/ LeetCode 213. 打家劫舍 II 题目描述 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。…

京东的AIGC革新之旅:通过JoyCoder实现研发提效 | 新程序员

【导读】从需求分析、设计编码到测试运维,AI已经逐步渗透到软件开发的各个环节,如何切实针对研发场景进行提效,是业内每个企业都在思考的问题。本文作者详细分析了AI在研发中的实际应用,并分享了JoyCoder与京东内部工具结合的实际…

一款可以替代Notepad++的免费高级文本编辑器

Kate 文本编辑器是一款跨平台的免费高级文本编辑器,具有丰富的功能和特性。它支持标签页、代码高亮、多文件查找、垂直/水平视图、侧边栏、颜色主题等特性,类似于Notepad。它以其多功能性和易用性广受好评。Kate 支持多文档界面(MDI&#xff…

加密技术.

基本保密通信模型 密码学发展 古典密码学 主要特点:数据的安全基于算法的保密 经典的加密⽅法包括凯撒密码(Caesar Cipher)(替代密码)、维吉尼亚密码(Vigenre Cipher) 主要分类 替代密码&…

OCR技术视角:智能文档管理中的票据自动化识别与处理

在数字化转型的浪潮中,企业对于高效、自动化的文档管理需求日益增长。票据作为企业运营中不可或缺的部分,其识别与管理的智能化成为了提升工作效率的关键。本文将深入探讨智能文档系统中票据识别功能的原理、技术优势以及在不同行业中的应用实践&#xf…

【报错已解决】`Solving environment: failed`

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 引言:一、问题描述:1.1 报错示例:1.2 报错分析:1.3 解决思路&#xff…

智能指针,QT,C++语言的关键字总结

八、C中关键字总结 1> C中一共有63个关键字,如上图所示,其中标红的为c语言中的关键字,有32个 2> 数据类型相关的关键字 bool、true、false:对于bool类型数据的相关处理,值为true和false char、wchar_t:char是…

《Exemplar Free Class Agnostic Counting》CVPR2022

概述 摘要: 这篇论文提出了一种新颖的“无范例类别无关计数”(Exemplar Free Class Agnostic Counting)方法,旨在测试时对新类别的对象进行计数,而无需访问该类别的标记训练数据。以往的类别无关计数方法无法在完全自…

【刷题笔记】打家劫舍问题

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 题目一 题目链接:打家劫舍I 思路 小偷每到一初,都可以选择对这个位置偷还是不偷,所以,这次我们需要定义两个表 小Tips:针对这种情况,一般…

OpenGL/GLUT实践:水面模拟——从单振源到 Gerstner Wave(电子科技大学信软图形与动画Ⅱ实验)

源码见GitHub:A-UESTCer-s-Code 文章目录 1 实现效果1 简单水面模拟——单振源1.1 水面高度函数1.2 水面建模1.3 openGL 渲染(1) renderSense(2) 其他 1.4 实现效果 2 添加鼠标控制3 添加纹理4 多个振源组合5 Gerstner Wave 模型5.1 原理5.2 具体实现5.2.1 全局变量…

pytest 常用的辅助函数和工具函数

pytest 常用的辅助函数和工具函数示例 # File: my_module.pydef fetch_data():return process datadef process_data():data fetch_data()return data.upper() import logging import sys import pytest#01-------------------------------pytest.fixture,sample_data 在测试…

Android 下的 XML 文件(概念理解、存储位置理解)

一、XML 1、XML 概述 XML(Extensible Markup Language,可扩展标记语言)是一种用于存储和传输数据的标记语言 类似于 HTML,但旨在传输和存储数据,而不是显示数据,且基本语法都是标签 2、XML 的特点 &am…

【2024数模国赛赛题思路公开】国赛B题思路丨附可运行代码丨无偿自提

2024年国赛B题解题思路 问题 1: 抽样检测方案设计 【题目分析】 分析: 目标是设计一个高效的抽样检测方案,在尽量少的样本数量下,确保在高信度水平下做出正确的接受或拒收决策。需要处理两个不同的信度要求,这对样本量的计算提…