CryoEM粒子(Particle)类型预测的数据集构建

Posted SpikeKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CryoEM粒子(Particle)类型预测的数据集构建相关的知识,希望对你有一定的参考价值。

通过cyroSPARC的2D Class模块,将冷冻电镜粒子图像,聚类为16个类型,构建模型,验证这16个类型的视觉区分度。

冷冻电镜图像:噪声(Noise )图像 和 低通滤波(Lowpass)图像

SampleSample 1Sample 2Sample 3Sample 4Sample 5
Lowpass
Noise

聚类平均图像(噪声强度0.9):清晰(Average)图像、噪声(Noise )图像 和 低通滤波(Lowpass)图像

SampleSample 1Sample 2Sample 3Sample 4Sample 5
Lowpass
Noise
Average

冷冻电镜图像

解析star和mrc文件,star是标签,mrc是原始图像。

star文件格式:

000001@extract/000000855321499642015_stack_1293_cor2_DW_particles.mrc J352/imported/000000855321499642015_stack_1293_cor2_DW.mrc 423 1246 103.775505 -0.450000 1.050000 300.000000 10000.000000 1.082500 13015.895508 12950.861328 9.064259 2.700000 0.000000 0.100000 0.000000 1 15
000002@extract/000000855321499642015_stack_1293_cor2_DW_particles.mrc J352/imported/000000855321499642015_stack_1293_cor2_DW.mrc 1821 3159 46.836731 -3.150000 1.350000 300.000000 10000.000000 1.082500 12611.307617 12546.273438 9.064259 2.700000 0.000000 0.100000 0.000000 1 16

解析单行star,将mrc图像与标签相对应:

  • mrc_name000000855321499642015_stack_1293_cor2_DW_particles.mrc,mrc文件中的文件名
  • idx000001,mrc是3维矩阵,第1维表示多张图像,1表示编号第1个图像
  • label:标签,2D Class的聚类标签
def parse_star_line(line, mrc_dir, max_items=19):
    """
    解析star文件的行
    """
    # 解析star的字段
    items = line.split(" ")
    if len(items) != max_items:
        return
    mrc_name = items[0].split("@")[1].split("/")[1]  # mrc
    mrc_idx = int(items[0].split("@")[0])  # idx
    label = int(items[max_items - 1])  # 标签是最后一位

    # 解析图像
    mrc_path = os.path.join(mrc_dir, mrc_name)
    mrc_arr = read_mrc(mrc_path)  # mrc图像
    img_r, img_n = get_particle_img(mrc_arr, mrc_idx)  # 获取索引图像

    return img_r, img_n, label

读取mrc文件:

def read_mrc(mrc_path):
    """
    加载MRC文件
    """
    mrc = mrcfile.open(mrc_path, permissive=True)
    data = mrc.data
    arr = np.zeros(shape=data.shape, dtype=data.dtype)
    arr[:] = data[:]
    return arr

获取特定索引的图像:索引从0开始,再正则化,支持设置最小值和最大值。

def norm_img(img, v_min=None, v_max=None):
    """
    图像正则化
    """
    if not v_min and not v_max:
        v_min, v_max = np.min(img), np.max(img)
    img = (img - v_min) / (v_max-v_min)
    img = np.clip(img * 255, 0, 255)
    return img


def get_particle_img(mrc_arr, mrc_idx, v_min=None, v_max=None):
    """
    根据particle的索引提取图像,索引是从1开始
    :param mrc_arr: mrc文件
    :param mrc_idx: particle索引
    :param v_min: 最小值
    :param v_max: 最大值
    :return: 图像
    """
    img_r = np.squeeze(mrc_arr[mrc_idx - 1, :, :])
    img_n = norm_img(img_r, v_min=v_min, v_max=v_max)
    return img_r, img_n

解析star文件和mrc_dir文件夹:解析star的标注文件、确定数据起始行,前面是标签说明、逐行处理样本

def parse_star_lines(star_path, mrc_dir, p_start=23, n_sample=-1):
    """
    解析star文件,输出样本数组(img, label)
    """
    # 解析star的标注文件
    with open(star_path, 'r', errors='ignore') as f:
        output = f.readlines()
        star_lines = [o.strip() for o in output]

    # 确定数据起始行,前面是标签说明
    star_lines = star_lines[p_start:]

    if n_sample > 0:  # 部分采样
        n_sample = min(n_sample, len(star_lines))
        star_lines = star_lines[:n_sample]

    print(f'[Info] star: len(star_lines)')
    # 逐行处理样本
    samples = []
    for line in tqdm(star_lines, desc="star"):
        try:
            img_r, img_n, label = parse_star_line(line, mrc_dir)
        except Exception as e:
            print(f'[Error] err line: line, exception: e')
            continue
        samples.append((img_r, img_n, label))
    return samples

划分数据集,train、val、test:

def split_dataset(data_lines, gap=10):
    """
    分离数据集为训练和验证
    """
    print(f'[Info] samples: len(data_lines), gap: gap')
    random.seed(42)
    random.shuffle(data_lines)
    n_gap = len(data_lines) // gap
    val = data_lines[:n_gap]
    test = data_lines[n_gap:n_gap*2]
    train = data_lines[n_gap*2:]
    print(f'[Info] train: len(train), val: len(val), test: len(test)')
    return train, val, test

处理原始图像:低通滤波图像,输入是原始图像(非正则化)

def process_line(idx, sample, out_dir, flag):
    """
    原始图像和低通滤波图像,写入图像至相应文件夹
    :param idx: 索引
    :param sample: 样本,即图像和标签
    :param out_dir: 输出文件夹
    :param flag: train\\val\\test
    :return: None
    """
    label = sample[-1]  # 标签
    img_r = sample[0]   # 噪声样本,用于低通滤波的输入
    img_n = sample[1]   # 正则化的噪声样本

    from low_pass.lowpass_filter import lowpass_filter_for_cryoem
    img_lowpass = lowpass_filter_for_cryoem(img_r)  # 低通滤波图像,输入是原始图像(非正则化)

    # 存储raw图像
    label_dir = os.path.join(out_dir, "raw", flag, str(label))
    mkdir_if_not_exist(label_dir)
    cv2.imwrite(os.path.join(label_dir, f"flag_idx.png"), img_n)

    # 存储lowpass图像
    label_dir = os.path.join(out_dir, "lowpass", flag, str(label))
    mkdir_if_not_exist(label_dir)
    cv2.imwrite(os.path.join(label_dir, f"flag_idx.png"), img_lowpass)

    if len(sample) == 4:  # 增加1维,聚类中心图像
        img_avg = sample[2]
        label_dir = os.path.join(out_dir, "average", flag, str(label))
        mkdir_if_not_exist(label_dir)
        cv2.imwrite(os.path.join(label_dir, f"flag_idx.png"), img_avg)

    if idx % 100 == 0:
        print('[Info] 已处理完成: '.format(idx))

数据集创建的函数:输入样本和数据集文件夹

def process_base(samples, out_dir):
    train, val, test = split_dataset(samples)  # 划分数据集

    # 多进程处理样本
    pool = Pool(processes=40)
    for idx, sample in tqdm(enumerate(train), "train"):
        # DatasetProcessor.process_line(idx, sample, out_dir, "train")
        pool.apply_async(DatasetProcessor.process_line, (idx, sample, out_dir, "train"))
    for idx, sample in tqdm(enumerate(val), "val"):
        pool.apply_async(DatasetProcessor.process_line, (idx, sample, out_dir, "val"))
    for idx, sample in tqdm(enumerate(test), "test"):
        pool.apply_async(DatasetProcessor.process_line, (idx, sample, out_dir, "test"))
    pool.close()
    pool.join()

    print(f'[Info] 全部处理完成: out_dir')

构建冷冻电镜粒子图像数据集:

  • 构建cryoEM数据集,参数是:star文件,mrc粒子图像文件夹,out_dir输出文件夹
  • 解析Particle样本 -> 划分数据集 -> 多进程处理样本
def process_from_star(star_path, mrc_dir, out_dir, is_mini=False):
    """
    构建cryoEM的star数据集,参数是:star文件,mrc粒子图像文件夹,out_dir输出文件夹
    """
    print(f'[Info] star_path: star_path')
    print(f'[Info] mrc_dir: mrc_dir')
    print(f'[Info] out_dir: out_dir')
    mkdir_if_not_exist(out_dir)

    if is_mini:
        n_sample = 200  # mini样本数量
    else:
        n_sample = -1

    # 解析Particle样本
    samples = parse_star_lines(star_path, mrc_dir, p_start=23, n_sample=n_sample)

    DatasetProcessor.process_base(samples, out_dir)

聚类平均图像

解析MRC图像函数:获取Particle图像的最小、最大值,归一化,存储于输出文件夹

def mrc_parser(mrc_path):
    """
    读取MRC文件,获取Particle图像的最小、最大值,归一化,存储于输出文件夹
    :param mrc_path: MRC文件
    :return: None
    """
    arr = read_mrc(mrc_path)
    i_min, i_max = np.min(arr), np.max(arr)
    imgs_r, imgs_n = [], []
    for i in tqdm(range(arr.shape[0]), desc="particles"):
        img_r, img_n = get_particle_img(arr, i+1, i_min, i_max)
        imgs_r.append(img_r)
        imgs_n.append(img_n)
    return imgs_r, imgs_n

旋转粒子函数:

def rotate_particle(img, angle, out_wh=None):
    """
    旋转粒子粒子图像,填充背景像素
    :param img: 待旋转的图像
    :param angle: 旋转角度,来源于_rlnAnglePsi,顺时针旋转
    :param out_wh: 输出尺寸
    :return: 旋转之后的图像
    """
    bkg_val = int(np.argmax(np.bincount(img.flatten())))  # 背景像素,用于填充
    # 旋转图像
    img = ndimage.rotate(img, 360-angle, reshape=False, cval=bkg_val)
    # 截取图像中心
    if out_wh:
        img = cv2.resize(img, out_wh)
    return img

添加噪声函数:

def add_noise_to_particle(img, noise=0.90, out_size=(128, 128)):
    """
    添加随机噪声至粒子图像
    """
    img = cv2.resize(img, out_size)
    h, w = img.shape[:2]
    img_noise = (np.random.rand(h, w) * 255).astype(np.uint8)
    img = cv2.addWeighted(img, 1 - noise, img_noise, noise, gamma=0)
    return img

构建数据集:

  • 噪声图像、低通滤波图像、聚类平均图像
  • 加载聚类平均图像 -> 旋转图像 -> 添加噪声 -> 构建数据集
    def process_from_average(avg_path, out_dir, is_mini=False):
        """
        根据聚类中心构建数据集
        """
        mkdir_if_not_exist(out_dir)
        imgs_r, imgs_n = mrc_parser(avg_path)  # 加载图像

        # 不同角度的样本数据
        samples = []
        for label, (img_r, img_n) in tqdm(enumerate(zip(imgs_r, imgs_n))):
            for i in range(360):
                img_n_out = rotate_particle(img_n, i, out_wh=(128, 128))
                img_n_noise = add_noise_to_particle(img_n_out, noise=0.9)
                # 增加avg图像
                samples.append((img_n_noise, img_n_noise, img_n_out, label))

        if is_mini:  # 是否mini数据集
            random.seed(42)
            random.shuffle(samples)
            samples = samples[:200]  # mini采样数量

        DatasetProcessor.process_base(samples, out_dir)

以上是关于CryoEM粒子(Particle)类型预测的数据集构建的主要内容,如果未能解决你的问题,请参考以下文章

CryoEM粒子(Particle)类型预测的数据集构建

粒子滤波(Particle Filter)的通俗解释

unity5.1 particle systems怎么控制粒子速度

Unity学习——粒子系统(Particle System)

粒子系统模块(Particle System Modules40)

粒子滤波 particle filter—从贝叶斯滤波到粒子滤波—Part-V(粒子滤波 PF)