玩转肺癌目标检测数据集Lung-PET-CT-Dx ——③整理、验证数据,建立Dataset对象

news2025/1/17 5:55:46

文章目录

    • 数据整理
      • 整理出所有的dcm文件
      • 整理出所有的xml标注文件
      • 整理数据①——舍弃错误的标注文件
      • 整理数据②——两个标注文件指向同一个目标图片的情况
        • 封装函数,传入xml文件,显示标注效果
      • 整理数据③——将PETCT的三通道图像转成平扫CT的单通道图像格式
        • 关于PETCT三通道转单通道灰度图的一些思考
      • 建立Dataset

本文进一步对数据集进行探索,对数据进行清洗、整理、验证。
最后建立Dataset对象,以便进行后续的训练工作。
本文所用代码: 我的Github

数据整理

我们最终的目的是建立Dataset对象,所以我们的目标应该是整理出 数据+标签 的组合。
在目标检测任务中,数据即我们的CT图片(读取自dcm文件);标签即我们的xml文件,内含 Bounding Box框 + 类别标签。
所以我们的目标是对所有可配对的 dcm文件xml文件 进行一一对应,剩下读取则是一件简单的工作。

整理出所有的dcm文件

dcm文件和xml文件对应的方法在上一节中已经阐释。
现在需要将数据集中所有的 dcm文件 和 对应的SOP Instance UID(后通称UID)整理出来,一共有25万条记录。


import pydicom
import matplotlib.pyplot as plt
import os
from tqdm import tqdm
import pandas as pd
import numpy as np

# 查看整个数据集下所有的dcm文件名
dcm_file=[]
for root, dirs, files in os.walk('manifest-1608669183333/Lung-PET-CT-Dx'):
    for file in files:
        file_path = os.path.join(root, file)
        if 'dcm' in file_path:
            dcm_file.append(file_path)
print(dcm_file[0])
len(dcm_file)

在这里插入图片描述


导入DataFrame后,获取对应的UID。
由于有25w张图片,我在固态硬盘上跑这段代码耗时约50min,跑完后保存为csv,下次就不用再跑一遍了。

df_dcm = pd.DataFrame(dcm_file)
# 建立新列uid,获取dcm文件对应的 SOPInstanceUID 号,这步耗时50min左右
df_dcm['uid'] = df_dcm[0].apply(lambda x: pydicom.dcmread(x).SOPInstanceUID)
df_dcm['uid_str'] = df_dcm['uid'].apply(lambda x: str(x))
df_dcm.to_csv('dcm_file_uid.csv')

# 上面一步耗时较久,如果已经读取过一遍并已保存,这里直接读取
df_dcm = pd.read_csv('dcm_file_uid.csv', index_col=0)
df_dcm

在这里插入图片描述


简单检查一下dcm文件是否独一无二。
在这里插入图片描述

整理出所有的xml标注文件

我们再把所有的xml文件整理出来,附上对应的UID。
在这里插入图片描述


在这里插入图片描述

整理数据①——舍弃错误的标注文件

我们先检查一下3万多个xml文件对应的UID是否都对应在df_dcm中。

# 检查Annotation目录下所有的uid文件名是否都在df_dcm中
xml_file_df = xml_file_df.loc[xml_file_df['uid_str'].isin(df_dcm['uid_str'])]
xml_file_df

# 我们发现 30884 < 31562

在这里插入图片描述
结果我们发现并不是所有的xml都有对应原图片,这应该属于这个数据集的标注错误问题,有可能标注文件的对应UID写错了。
总之,我们不能将这些xml文件纳入训练,所以将这些找不到对应图片的标注文件舍弃掉。

整理数据②——两个标注文件指向同一个目标图片的情况

我们查看一下XML标注文件是否有重复的文件名。

# 查看Annotation目录下的XML文件是否有重复的文件名
len(xml_file_df[0].unique()) == len(xml_file_df['uid_str'].unique())
# 如有False,说明Annotation下有重复的文件

在这里插入图片描述
这里我们发现该数据集第二个坑,Annotation目录下存在重复的标注文件指向同一个目标图片的情况(否则上面就显示True)。
我们继续进一步探究是哪些标注文件重复了。


我们查看一下原本的XML文件数量 和 去重后的XML文件数量。

# 显示Annotation目录下去重后的XML文件数量、原本的XML文件数量
print(len(xml_file_df['uid_str'].unique()), len(xml_file_df[0].unique()) )

在这里插入图片描述


我们获取一下那些重名(同一UID)的xml文件。

# 获取那些重复的xml文件的索引
idx = xml_file_df['uid_str'][xml_file_df['uid_str'].duplicated(keep=False)].index
# 对xml_file_df文件应用idx索引,获取完整文件名
xml_file_name_duplicated = pd.DataFrame(xml_file_df.loc[idx,:])

# 导出到csv文件(记录一下)
xml_file_name_duplicated.to_csv('duplicated.csv')
xml_file_name_duplicated[:6]

在这里插入图片描述

xml_file_name_duplicated = pd.read_csv('duplicated.csv', index_col=0)
print(len(xml_file_name_duplicated))  # 显示重复条目数量
xml_file_name_duplicated['0'][:12].values  # 显示前12行

# 可以看到A0003和A0004中存在相同名称的文件。
# 我们知道A0003和A0004是两个不同的患者(Clinical Data的“statistics-clinical-20201221.xlsx”文件),理应不可能存在相同的CT影像,所以应该是xml标注文件本身出错了

在这里插入图片描述
以上我们可以看到A0003和A0004中存在相同名称的文件。
我们知道A0003和A0004是两个不同的患者(从Clinical Data的“statistics-clinical-20201221.xlsx”文件看出),理应不可能存在相同的CT影像,所以应该是xml标注文件本身出错了。
那么具体是怎么出错了呢?


# 查看重复条目有多少种,如果为重复条目总数的1/2,那么均为两两重复
print('重复条目有', len(xml_file_name_duplicated['uid_str'].unique()), '种,总条目为', len(xml_file_name_duplicated), '个')

# 下面我们看看这些重复的标注文件的标注情况

在这里插入图片描述
上面我们得知重复标注的情况是两两重复。
我们再进一步查看一下重复标注的情况。


封装函数,传入xml文件,显示标注效果

我们设计一个函数,传入xml文件路径,显示图片和锚框。

# 设计一个函数,传入xml文件路径,显示图片和锚框
##################

# 从XML文件获取bounding box信息
import xml.etree.ElementTree as ET
def get_labelFromXml(xml_file):     
    label=[]
    bbox_list=[]

    an_file = open(xml_file, encoding='utf-8')
    tree=ET.parse(an_file)
    root = tree.getroot()
    for object in root.findall('object'):    
        cancer_type=object.find('name').text.upper()
        xmin=object.find('bndbox').find('xmin').text
        xmax=object.find('bndbox').find('xmax').text
        ymin=object.find('bndbox').find('ymin').text
        ymax=object.find('bndbox').find('ymax').text

        if int(xmin)==0 or int(xmax)==0 or int(ymin)==0 or int(ymax)==0:
            pass
        elif int(xmin)==int(xmax) or int(ymin)==int(ymax):
            pass
        else:
            bbox=[int(xmin),int(ymin),int(xmax),int(ymax)]
            bbox_list.append(bbox)
            label.append(cancer_type)
    return bbox_list,label

# 画布上添加锚框
def bbox_to_rect(bbox, color):
    # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式:
    # ((左上x,左上y),宽,高)
    return plt.Rectangle(
        xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
        fill=False, edgecolor=color, linewidth=2)

def show_img_and_anchors(url):  # 传入xml文件路径,展示标注效果,一张图片可显示多个bbox框
    # 获取
    uid = xml_file_df.loc[xml_file_df[0]==url, 'uid_str'].values[0]
    im = df_dcm.loc[df_dcm['uid_str']==uid].values[0][0]
    im = pydicom.read_file(im)

    # 获取像素矩阵
    img_arr = im.pixel_array

    fig = plt.imshow(img_arr,cmap=plt.cm.bone)
    plt.title("UID:{}".format(uid))

    bbox, label = get_labelFromXml(url)

    for i, j in zip(bbox, label):
        fig.axes.add_patch(bbox_to_rect(i, 'red'))
        fig.axes.text(i[0]+12, i[1]+12, j,
                            va='center', ha='center', fontsize=12, color='red')
    
def show_imgs_and_anchors(urls, num=5):  # 传入xml文件路径组成的列表,展示多张图片发标注效果
    num_toshow = min(num, len(urls))
    print('载入', len(urls), '张图片,显示前', num, '张。')

    # 获取
    temp_df = xml_file_df.loc[xml_file_df[0].isin(urls)]
    urls = temp_df[0].values  # 这里urls顺序被重排,需要重新赋值以此,以和uids建立一一对应
    uids = temp_df['uid_str'].values
    ims = [df_dcm.loc[df_dcm['uid_str']==x].values[0][0] for x in uids]
    ims_dcm = [pydicom.read_file(x).pixel_array for x in ims]  # 获取像素矩阵List

    # 取标注框和类别
    bbox_label = [get_labelFromXml(i) for i in urls]

    # _表示忽略不适用的变量,返回的fig用不上
    _, axes = plt.subplots(nrows=1, ncols=num_toshow, figsize=(48,48))
    # 使用zip方法,在for循环中设置axes中的各个子区域的参数并绘图
    for ax, uid, img, lbl in zip(axes, uids, ims_dcm, bbox_label):
        ax.imshow(img, cmap=plt.cm.bone)
        ax.set_title(uid)

        bbox, label = lbl
        for i, j in zip(bbox, label):
            ax.add_patch(bbox_to_rect(i, 'red'))
            ax.text(i[0]+12, i[1]+12, j,
                            va='center', ha='center', fontsize=12, color='red')
            ax.axes.get_xaxis().set_visible(False)
            ax.axes.get_yaxis().set_visible(False)

展示标注效果(一个xml文件):
在这里插入图片描述


展示标注效果(多个xml文件):
在这里插入图片描述

在这里插入图片描述

我们看到上方第1行第1张图片和第2行第1张图片是标注了同一张CT图片的情况。
我们可以看到标注的质量差不多,若标注得差不多,则可以假设不同的标注框可以作为一种数据增强的形式,因此可以无需特意舍弃重复的对应同UID的xml文件。
下面抽样查看一下所有的的重复标记情况。


我们可以再随机抽20张看看

# 随机抽20张看看
slice = np.random.choice(len(xml_file_name_duplicated), size=(4,5), replace=False)  # 生成4×5整数矩阵
for i in slice:
    urls = xml_file_name_duplicated['0'].values[i]
    show_imgs_and_anchors(urls, num=5)

效果见源码文件。

整理数据③——将PETCT的三通道图像转成平扫CT的单通道图像格式

普通CT图像是单通道(512×512),但PETCT图像是三通道(512×512×3),需转成单通道进行训练。
因为日后我们希望使用平扫的CT片子进行推理预测。

我们看看读取PETCT和普通CT片子的图片shape:

# 各取一张ct,一张petct图像的xml文件
url_ct = 'Lung-PET-CT-Dx-Annotations-XML-Files-rev12222020/Annotation\\A0213\\1.3.6.1.4.1.14519.5.2.1.6655.2359.211226049141098860991228194230.xml'  # 普通CT图像
url_petct = 'Lung-PET-CT-Dx-Annotations-XML-Files-rev12222020/Annotation\\A0213\\1.3.6.1.4.1.14519.5.2.1.6655.2359.284461122128650673200074494931.xml'  # PETCT图像
# 从xml文件获取对应ct图像文件信息
def get_img_pixel_array(url):
    uid = xml_file_df.loc[xml_file_df[0]==url, 'uid_str'].values[0]
    im = df_dcm.loc[df_dcm['uid_str']==uid].values[0][0]
    im = pydicom.read_file(im)
    img_arr = im.pixel_array  # 获取像素矩阵
    return img_arr

print('ct图像矩阵的形状为,', get_img_pixel_array(url_ct).shape)
print('petct图像矩阵的形状为,', get_img_pixel_array(url_petct).shape)

在这里插入图片描述


关于PETCT三通道转单通道灰度图的一些思考

我这里使用了opencv库的方法将PETCT的三通道转成单通道灰度图。
在opencv库中,三通道转单通道主要是两种转换方法:COLOR_BGR2GRAY or COLOR_RGB2GRAY

我们知道,直接使用cv2读取后图片的通道顺序是BGR,而pydicom读取的pixel_array的通道顺序应该是RGB(调用PhotometricInterpretation属性可以看到)。
所以对pixel_array直接使用cv2的转灰度方法时,可以使用COLOR_RGB2GRAY模式。

但是我们知道PETCT的图像信息是 正常的CT影像 和 代谢摄取增高影(高亮部分)两者重合。
我们发现使用COLOR_BGR2GRAY模式转灰度后,代谢摄取增广的高亮信息较COLOR_RGB2GRAY模式不明显。(下面的代码块可见效果)
这样似乎更适合用于训练,因为后面我们需要常用普通平扫的CT片子进行预测。

因此,转单通道我采用了cv2的COLOR_BGR2GRAY模式。

关于三通道转灰度图的一些其他参考资料:

  1. https://note.nkmk.me/python-opencv-numpy-color-to-gray/
  2. https://stackoverflow.com/questions/62855718/why-would-cv2-color-rgb2gray-and-cv2-color-bgr2gray-give-different-results

两种转单通道模式的对比:

import cv2 as cv

img_array = get_img_pixel_array(url_petct)
img_array_gray = cv.cvtColor(img_array, cv.COLOR_BGR2GRAY)
img_array_gray2 = cv.cvtColor(img_array, cv.COLOR_RGB2GRAY)

# # img_array = np.array(img_array, dtype=np.uint16)
_, axes = plt.subplots(1,3,figsize=(24,24))
axes[0].imshow(img_array, cmap=plt.cm.bone)
axes[0].set_title('PETCT')
axes[1].imshow(img_array_gray, cmap=plt.cm.bone)
axes[1].set_title('COLOR_BGR2GRAY')
axes[2].imshow(img_array_gray2, cmap=plt.cm.bone)
axes[2].set_title('COLOR_RGB2GRAY')
plt.show()
plt.close()

在这里插入图片描述

建立Dataset

有了前面的数据整理,我们可以建立Dataset数据集对象了。
目标是传入 [dcm图片集] 和 对应的 [xml标注集] ,即生成一个Dataset对象。

import torch
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from PIL import Image

transform=transforms.Compose([
    transforms.ToTensor(),    
])

但是,这里我们要注意,pydicom从CT读取的pixel_array数据类型(dtype)为uint16,从PETCT读取的pixel_array则为uint8
这会导致Image.open(PIL库)读取并使用transforms.ToTensor()转成Tensor后数据类型不一样。前者变为torch.int16,后者变为torch.float32。(如下演示)

因此,我们需要将pixel_array统一转成float32

在这里插入图片描述


创建Dataset数据集对象(Pytorch)。
注意一下Dataset输出的数据格式,img为图片Tensor,label则是一个dict,内含bbox的坐标和类别。

class_to_id={'A':1, 'B':2, 'E':3, 'G':4}
# 重写get_labelFromXml,返回label的id
def get_labelidFromXml(xml_file):     
    label=[]
    bbox_list=[]

    an_file = open(xml_file, encoding='utf-8')
    tree=ET.parse(an_file)
    root = tree.getroot()
    for object in root.findall('object'):    
        cancer_type=object.find('name').text.upper()
        xmin=object.find('bndbox').find('xmin').text
        xmax=object.find('bndbox').find('xmax').text
        ymin=object.find('bndbox').find('ymin').text
        ymax=object.find('bndbox').find('ymax').text

        if int(xmin)==0 or int(xmax)==0 or int(ymin)==0 or int(ymax)==0:
            pass
        elif int(xmin)==int(xmax) or int(ymin)==int(ymax):
            pass
        else:
            bbox=[int(xmin),int(ymin),int(xmax),int(ymax)]
            bbox_list.append(bbox)
            label.append(class_to_id[cancer_type])
    return bbox_list,label

# 计划传入img和label,以此对应dcm文件路径集和xml文件路径集
class LungDetection(Dataset):
    def __init__(self, img, label, transform=transform,):
        self.img = img
        self.label = label
        self.transform = transform

    def __getitem__(self, index):
        img = self.img[index]
        label = self.label[index]
        
        img_open=pydicom.read_file(img)
        img_array=img_open.pixel_array

        if len(img_array.shape) == 3:
            img_array = cv.cvtColor(img_array, cv.COLOR_BGR2GRAY)

        img_array = np.array(img_array, dtype=np.float32)
        
        img_pic = Image.fromarray(img_array)
        img_tensor=self.transform(img_pic)

        bbox, label = get_labelidFromXml(label)
        
        bbox_tensor = torch.as_tensor(bbox,dtype=torch.float32)
        label_tensor = torch.as_tensor(label,dtype=torch.int64)
        target={}
        target['boxes']=bbox_tensor
        target['labels']=label_tensor

        return img_tensor, target

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

xml_file_df 是我们之前整理出的xml标签文件,我们现在将xml文件和dcm文件对应起来。

# xml_file_df 是我们之前整理出的xml标签文件,我们现在将xml文件和dcm文件对应起来
xml_file_dataset = xml_file_df.reset_index(drop=True)
xml_file_dataset.columns = ['xml', 'uid_str']
xml_file_dataset

在这里插入图片描述

根据xml的UID对应上dcm文件:
下面这段代码跑起来需要几分钟的时间,所以跑完后可以保存一下csv文件

xml_file_dataset['dcm'] = xml_file_dataset['uid_str'].apply(lambda x : df_dcm.loc[df_dcm['uid_str']==x].values[0][0])
xml_file_dataset.to_csv('xml_file_dataset.csv')
# 以上代码需要跑8分钟,所以保存起来
xml_file_dataset = pd.read_csv('xml_file_dataset.csv', index_col=0)
xml_file_dataset

在这里插入图片描述


传入Dataset。

img = xml_file_dataset['dcm'].values
label = xml_file_dataset['xml'].values
lungdetection_dataset = LungDetection(img=img, label=label, transform=transform)

在这里插入图片描述

顺利完成!

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

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

相关文章

企业费控,驶向「一体化」

在数字化于企业内部各个环节实现平权、成为标配的当下,财务,这个被称为“控制企业生命力”的核心环节,是否应该拥有新的价值? 作者| 皮爷 出品|产业家 2022年年中,施伟和分贝通的团队接到一项“特殊需求”。 这个…

在职读研专业——劳动经济学

研究方向:劳动经济学主要研究劳动力市场现象及其所引起的劳动力资源配置等相关问题,包括劳动力供给、劳动力需求、就业、工资、人力资本投资、收入分配等。例如:劳动力市场失衡背后各种因素的变化,如何通过资源的配置使劳动资源配…

MySQL innobackupex 备份工具使用总结

前言 在MySQL备份以及搭建从库的过程中,我们针对比较小的库大多采用的是mysqldump,简单,方便。但是在大库进行搭建从库的时候,且数据库主机没有一个很好的存储来满足备份,这个时候就需要使用innobackupex来协助你来做一…

Pocsuite3框架POC/EXP编写练习:Flask(Jinja2) 服务端模板注入漏洞

Pocsuite3 是由知道创宇 404 实验室打造的一款基于 GPLv2 许可证开源的远程漏洞测试框架。可以用来编写POC/EXP,今天借助Flask框架下的SSTI漏洞练习记录一下Pocsuite3框架的配置和编写过程。 官方文档:Pocsuite3 是什么? | Pocsuite3 安装 …

小黑继续跟着沛奇老师学携程:携程基础3

3.异步编程 3.1事件循环 理解成一个死循环,去检测并执行某些代码 # 伪代码任务列表 [任务1,任务2,任务3,...] while True:可执行的任务列表,已完成的任务列表 去任务列表中检查所有任务,将可执行和已完…

安装、启动和登陆doccano

一、安装 1、使用的Pycharm安装的doccano 2、初始化数据库 doccano init 3、创建用户名和密码 # admin是用户名,pass是密码,都可以自定义 doccano createuser --username admin --password pass 二、启动 1、在一个Terminal终端启动webserver服务 …

自适应控制专栏目录及介绍

目录 自适应控制专栏目录及介绍第一篇:[具有不确定参数系统的自适应跟踪控制设计_ADi_hhh的博客-CSDN博客](https://blog.csdn.net/qq_45830323/article/details/129713051)第二篇:[(SISO)单输入单输出系统的模型参考自适应控制_A…

cdh Hue集成sentry的权限管理详细步骤

hue登录hue的第一个用户要用hue用户创建,默认hue为超级用户,hue用户页面的权限可以管理很多用户操作,比如查看hdfs目录,使用其他组件,授权功能等等 一.hive的配置 1.关闭模拟,开启sentry 2.添加配置(注意配置的位置) <property> <name>sentry.hive.tes…

【C++11】lambda表达式

目录 lambda表达式 1.1 lambda表达式出现的原因 1.2 lambda表达式语法 1.3 函数对象与lambda表达式 lambda表达式 1.1 lambda表达式出现的原因 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用 std::sort 方法 测试代码 #include <…

苹果撤离中国市场?中国市场太重要,印度制造是备胎

苹果在中国之行后&#xff0c;却计划进一步扩大印度制造iPhone的比例&#xff0c;甚至将iPhone15全数交给印度制造&#xff0c;业界因此认为苹果正在离开中国市场&#xff0c;然而这完全是臆想&#xff0c;中国市场对苹果来说仍然非常重要&#xff0c;它不会轻易舍弃这个市场。…

【Unity VR开发】结合VRTK4.0:添加碰撞忽略器

语录&#xff1a; 最远的旅行&#xff0c;是从自己的身体到自己的心&#xff0c;是从一个人的心到另一个人的心。坚强不是面对悲伤不流一滴泪&#xff0c;而是擦干眼泪后微笑面对以后的生活。 前言&#xff1a; 模块化提供了一种允许两个或者多个对象忽略彼此碰撞的方法&#x…

Logstash:多管道配置

我们大多数人已经熟悉 Elastic 产品。 在此博客中&#xff0c;我们将逐步介绍配置 Logstash 以使用多个管道摄取和处理数据的步骤。 Logstash 是一种开源数据处理管道&#xff0c;可让你摄取、转换和输出数据。 它是从各种来源收集和处理大量数据的强大工具&#xff0c;可用于管…

IP协议头

IP 4位版本号&#xff08;version&#xff09;4位头部长度&#xff08;header length&#xff09;8位服务类型&#xff08;Type Of Service&#xff09;16位总长度&#xff08;total length&#xff09;16位标识&#xff08;id&#xff09;3位标志字段13位分片偏移&#xff08;…

Spring的循环依赖

什么是循环依赖&#xff1f; 循环依赖其实就是循环引用&#xff0c;也就是两个或者两个以上的 bean 互相持有对方&#xff0c;最终形成闭环。比如 A 依赖于 B&#xff0c;B 依赖于 C&#xff0c;C 又依赖于 A。如下图&#xff1a; 注意&#xff0c;这里不是函数的循环调用&…

金陵科技学院五年一贯制专转本计算机应用基础考试大纲

金陵科技学院五年一贯制专转本计算机应用基础考试大纲 一、参考书目&#xff1a; 《大学计算机信息技术教程》&#xff0c;张福炎&#xff0c;孙志挥&#xff0c;南京大学出版社&#xff0c;《全国计算机等级考试一级教程计算机基础及MS Office应用》&#xff0c;教育部考试中…

书写我的人生回忆录-这应该是给子女和父母最好的礼物

作为一个业余的软件开发爱好者&#xff0c;我又捣鼓了一个有意思的小东西 &#xff0c;使用完全免费哈 您可以是以子女的身份帮助父母来完成这份回忆录&#xff1a; 《书写我的人生回忆录》是一款软件&#xff0c;其中包含70个问题&#xff0c;涵盖了父母的个人喜好、家庭、工…

关于图床使用

安装windows终端 Windows 终端安装 | Microsoft Learn 安装oh-my-posh 安装scoop &#xff08;后续都使用PowerShell 7.2.10&#xff09;&#xff1a; > Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first ti…

代谢组学-南京医科大学胡志斌团队绘制心脏发育全周期多组学图谱

文章标题&#xff1a;Multi-omics profifiling visualizes dynamics of cardiac development and functions 发表期刊&#xff1a;Cell Reports 影响因子&#xff1a;9.995 作者单位&#xff1a;南京医科大学 百趣提供服务&#xff1a;发现代谢组学HD-MIX版 百趣代谢组学分…

教你用Python破解WiFi,流量自由不再是梦!(附完整源码)

目录 一、准备工作二、编写代码三、展示测试结果四、生成密码本&#xff08;建议自己找一个密码本&#xff09; 一、准备工作 工具&#xff1a;python 二、编写代码 在桌面新建一个文件 如果你新建的文件没有后缀名.txt&#xff0c;请进行如下设置 打开刚刚新建的文件&#…

pg数据库安装

pg数据库安装 下载对应的安装包 安装服务 使用安装包直接安装 rpm -ivh postgresql13-libs-13.1-3PGDG.rhel7.x86_64.rpm rpm -ivh postgresql13-13.1-3PGDG.rhel7.x86_64.rpm rpm -ivh postgresql13-server-13.1-3PGDG.rhel7.x86_64.rpm rpm -ivh postgresql13-contrib-…