您好,欢迎来到易妖游戏网。
搜索
您的当前位置:首页项目实战|跳过报错文件继续运行,并输出未能正常处理的文件·25-01-04

项目实战|跳过报错文件继续运行,并输出未能正常处理的文件·25-01-04

来源:易妖游戏网

小罗碎碎念

这一期遇到的问题,是我在把svs格式的切片分割patch时,提示的与OpenSlideError相关的问题。

首先怀疑指定路径下有openslide不支持的文件格式,于是先查看所有文件的格式类型。

import os
from pathlib import Path

def check_file_types(folder_path):
    file_types = {}
    # 遍历文件夹中的所有文件
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            file_extension = Path(file).suffix
            if file_extension in file_types:
                file_types[file_extension] += 1
            else:
                file_types[file_extension] = 1
    return file_types


if __name__ == "__main__":
    folder_path = "/data2/data2_mailab015/24-11-25/sysucc/three_patch_2_ruofan"  # 替换为你要检查的文件夹路径
    result = check_file_types(folder_path)
    print(result)

结果显示都是svs格式,理论上不会报错,但是出问题了,所以我们需要继续排除。


知识星球

如需获取推文中提及的各种资料,欢迎加入我的知识星球!


一、拆分文件

1-1:思路引导

我们已经确认了文件格式没有问题,所以我产生以下两个怀疑:


为了解决上述两个怀疑,我分别做了两件事。

首先,将2400个文件拆分存储到8个的文件夹中,各文件夹分别执行patch分割任务。此外,原来的代码在遇到报错后会直接终止后续的进程,并且不会提示目前已经处理到哪个文件。

这会导致我在排除问题的时候,无法定位到具体的文件,所以我修改了代码,会显示目前处理哪个文件,并且在遇到报错后会跳过当前文件继续执行,并记录所有未能正确处理的切片,最后输出一个列表

增加这两个功能有以下这些好处:

  1. 兼容性更高,保证不同人上手就能用,即便有报错,也可以把能处理的先处理掉,再告诉你哪些有问题;
  2. 理论上有问题的会很少,例如我这里只出现了一例,所以出于时间成本考虑,我可以不再专门为了这一例去调整代码;


代码实现

要想实现上面提到的两个功能,我们需要修改主程序,修改前的代码如下。

def main(args):
    print(args, args.__dict__)
    slide_folder_path = '/path'
    print(os.listdir(slide_folder_path))
    for slide_name in tqdm(os.listdir(slide_folder_path)):
        #print(slide_name)
        slide_path = os.path.join(slide_folder_path, slide_name)
        args.slide_file = slide_path
        tile_factory = TileFactory(args.slide_file, args.tile_size, args.overlap, output_path=args.output_path,
                                num_workers=args.num_workers)
        tile_factory.make_overview()
        tile_factory.make_tiles()

修改后如下。

def main(args):
    print(args, args.__dict__)
    slide_folder_path = '/path'
    print(os.listdir(slide_folder_path))
    
    # 用于记录未能正确处理的文件
    failed_files = []
    
    for slide_name in tqdm(os.listdir(slide_folder_path)):
        slide_path = os.path.join(slide_folder_path, slide_name)
        args.slide_file = slide_path
        
        try:
            # 尝试处理文件
            tile_factory = TileFactory(args.slide_file, args.tile_size, args.overlap, output_path=args.output_path,
                                    num_workers=args.num_workers)
            tile_factory.make_overview()
            tile_factory.make_tiles()
        except OpenSlideError as e:
            # 捕获 OpenSlideError 异常并记录错误文件
            print(f"Error processing file {slide_path}: {e}")
            failed_files.append(slide_path)
        except Exception as e:
            # 捕获其他可能的异常并记录错误文件
            print(f"Unexpected error processing file {slide_path}: {e}")
            failed_files.append(slide_path)
    
    # 输出未能正确处理的文件列表
    if failed_files:
        print("\nThe following files failed to process:")
        for file in failed_files:
            print(file)
    else:
        print("\nAll files were processed successfully.")

1-2:文件拆分思路

要将 2401 个文件拆分为 8 个文件夹,且每个文件夹的内容不能重复,可以使用 Python 的 osshutil 模块来实现。

  1. 获取文件列表

    • 使用 os.listdir() 获取文件夹中的所有文件。
    • 过滤出 .svs 文件。
  2. 计算每个文件夹的文件数量

    • 将 2401 个文件均匀分配到 8 个文件夹中,每个文件夹的文件数量为 2401 // 8 = 300,剩余的 1 个文件可以分配到任意一个文件夹。
  3. 创建目标文件夹

    • 使用 os.makedirs() 创建 8 个目标文件夹。
  4. 移动文件

    • 使用 shutil.move() 将文件移动到对应的目标文件夹中。

1-3:文件拆分代码

import os
import shutil

def split_files_into_folders(source_folder, num_folders=8):
    # 获取所有 .svs 文件
    files = [f for f in os.listdir(source_folder) if f.endswith(".svs")]
    total_files = len(files)
    print(f"Total .svs files found: {total_files}")

    # 计算每个文件夹的文件数量
    files_per_folder = total_files // num_folders
    remainder = total_files % num_folders
    print(f"Files per folder: {files_per_folder}, Remainder: {remainder}")

    # 创建目标文件夹
    target_folders = [os.path.join(source_folder, f"folder_{i+1}") for i in range(num_folders)]
    for folder in target_folders:
        os.makedirs(folder, exist_ok=True)

    # 分配并移动文件
    start_index = 0
    for i, folder in enumerate(target_folders):
        # 计算当前文件夹的文件数量
        end_index = start_index + files_per_folder + (1 if i < remainder else 0)
        # 获取当前文件夹的文件列表
        files_to_move = files[start_index:end_index]
        # 移动文件
        for file in files_to_move:
            shutil.move(os.path.join(source_folder, file), os.path.join(folder, file))
        print(f"Moved {len(files_to_move)} files to {folder}")
        # 更新起始索引
        start_index = end_index

if __name__ == "__main__":
    # 指定源文件夹路径
    source_folder = "/path/to/your/svs/files"  # 替换为你的文件夹路径
    split_files_into_folders(source_folder)

1-4:运行示例

假设源文件夹 /path/to/your/svs/files 中有 2401 个 .svs 文件,运行代码后,输出如下:

Total .svs files found: 2401
Files per folder: 300, Remainder: 1
Moved 301 files to /path/to/your/svs/files/folder_1
Moved 300 files to /path/to/your/svs/files/folder_2
Moved 300 files to /path/to/your/svs/files/folder_3
Moved 300 files to /path/to/your/svs/files/folder_4
Moved 300 files to /path/to/your/svs/files/folder_5
Moved 300 files to /path/to/your/svs/files/folder_6
Moved 300 files to /path/to/your/svs/files/folder_7
Moved 300 files to /path/to/your/svs/files/folder_8

1-5:注意事项

  1. 备份数据

    在运行代码之前,建议先备份源文件夹中的数据,以防意外。

  2. 路径问题

    确保 source_folder 是正确的路径。如果路径中包含空格或特殊字符,建议使用原始字符串(如 r"/path/to/your/svs/files")。

  3. 权限问题

    确保程序有权限读取源文件夹和写入目标文件夹。

通过这段代码,你可以轻松将 2401 个文件均匀分配到 8 个文件夹中,且每个文件夹的内容不会重复。


二、不中断运行

如果我们想关掉终端后,代码仍然能够持续运行,则需要使用nohup函数。

nohup python -u main2.py &

如果你想将日志输出到指定的文件(例如 scraw.log),可以使用重定向符号 >2>&1 来指定日志文件。例如:

nohup python -u main2.py > scraw.log 2>&1 &

注意,这个指令是把日志文件存在当前路径。

nohup python -u main2.py > /path/to/logs/my_log.log 2>&1 &

追加日志

上面是默认覆盖日志文件的,如果希望每次运行脚本时将日志追加到现有文件而不是覆盖,可以使用 >> 代替 >

例如:

nohup python -u main2.py >> my_log.log 2>&1 &

停止后台任务

如果需要停止后台运行的脚本,可以使用 ps 查找进程 ID,然后使用 kill 终止进程。例如:

ps aux | grep main2.py  # 查找进程 ID
kill <PID>              # 终止进程
pkill -f main2.py       #直接终止与脚本相关的所有进程

三、删除文件夹内指定时间之前的所有文件

要删除文件夹内指定时间之前的所有文件,可以使用 Python 的 ostime 模块来实现。

以下是一个完整的代码示例,它会遍历文件夹中的所有文件,检查文件的修改时间,并删除早于指定时间的文件。

3-1:实现思路

获取文件列表

使用 os.listdir() 获取文件夹中的所有文件。

检查文件修改时间

  1. 使用 os.path.getmtime() 获取文件的最后修改时间。
  2. 将修改时间与指定的时间进行比较。

删除文件

如果文件的修改时间早于指定时间,使用 os.remove() 删除文件。


3-2:完整代码

import os
import time

def delete_files_older_than(folder_path, days):
    # 获取当前时间
    current_time = time.time()
    # 计算指定时间之前的时间戳
    time_threshold = current_time - (days * 800)  # 800 秒 = 1 天

    # 遍历文件夹中的所有文件
    for filename in os.listdir(folder_path):
        file_path = os.path.join(folder_path, filename)
        # 检查是否为文件(排除文件夹)
        if os.path.isfile(file_path):
            # 获取文件的最后修改时间
            file_mtime = os.path.getmtime(file_path)
            # 如果文件的修改时间早于指定时间,删除文件
            if file_mtime < time_threshold:
                print(f"Deleting file: {filename} (Last modified: {time.ctime(file_mtime)})")
                os.remove(file_path)

if __name__ == "__main__":
    # 指定文件夹路径
    folder_path = "//data2/data2_mailab015/get_patch/sysucc/three_patch_2_ruofan"  # 替换为你的文件夹路径
    # 指定天数(删除早于多少天前的文件)
    days = 30  # 例如,删除 30 天前的文件
    delete_files_older_than(folder_path, days)

3-3:代码说明

  1. 计算时间阈值

    • time.time() 获取当前时间的时间戳。
    • time_threshold = current_time - (days * 800) 计算指定天数之前的时间戳。
  2. 遍历文件夹

    • 使用 os.listdir(folder_path) 获取文件夹中的所有文件。
    • 使用 os.path.isfile(file_path) 检查是否为文件(排除文件夹)。
  3. 检查文件修改时间

    • 使用 os.path.getmtime(file_path) 获取文件的最后修改时间。
    • 如果文件的修改时间早于 time_threshold,则删除文件。
  4. 删除文件

    • 使用 os.remove(file_path) 删除文件。

3-4:运行示例

假设文件夹 /path/to/your/folder 中有以下文件:

file1.txt (最后修改时间: 2023-09-01)
file2.txt (最后修改时间: 2023-10-01)
file3.txt (最后修改时间: 2023-10-15)

运行代码后,输出如下:

Deleting file: file1.txt (Last modified: Fri Sep  1 12:00:00 2023)

3-5:递归删除子文件夹中的文件

如果需要递归删除子文件夹中的文件,可以使用 os.walk() 遍历文件夹。

import os
import time

def delete_files_older_than(folder_path, days):
    current_time = time.time()
    time_threshold = current_time - (days * 800)

    for root, dirs, files in os.walk(folder_path):
        for filename in files:
            file_path = os.path.join(root, filename)
            file_mtime = os.path.getmtime(file_path)
            if file_mtime < time_threshold:
                print(f"Deleting file: {file_path} (Last modified: {time.ctime(file_mtime)})")
                os.remove(file_path)

if __name__ == "__main__":
    folder_path = "/data2/data2_mailab015/get_patch/sysucc/three_patch_2_ruofan"
    days = 30
    delete_files_older_than(folder_path, days)

通过这段代码,你可以轻松删除文件夹内指定时间之前的所有文件。


结束语

本期推文的内容就到这里啦,如果需要获取医学AI领域的最新发展动态,请关注小罗的推送!如需进一步深入研究,获取相关资料,欢迎加入我的知识星球!

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- vipyiyao.com 版权所有 湘ICP备2023022495号-8

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务