R语言exifr包照片标签排序脚本性能优化求助
嘿,我来帮你搞定这个脚本的性能问题!你的核心痛点在于逐个文件读取EXIF、嵌套循环处理标签,还有一些可以用向量操作替代的低效循环。下面是具体的优化思路和改进后的代码:
1. 批量读取EXIF数据(最大的性能提升点)
read_exif 支持一次性传入所有文件路径,不用在循环里逐个调用——这能大幅减少IO往返的开销,因为批量处理是exifr底层优化过的操作,比单文件多次调用效率高得多。
2. 简化标签提取逻辑
原来的tagcatcher函数嵌套了多层循环,完全可以用向量和列表操作简化,避免手动拼接和遍历。我们可以直接从批量读取的EXIF数据里提取所有关键词列,然后扁平化去重。
3. 预创建所有需要的文件夹
先收集所有照片里的标签,一次性检查并创建缺失的文件夹,不用在每个文件的循环里反复判断,减少文件系统的操作次数。
4. 优化文件复制逻辑
整理出所有需要复制的文件路径对,批量检查存在性,避免在循环里重复操作。
改进后的代码
#----- Imports ---- library(exifr) library(purrr) # 用于更简洁的列表操作(可选,但能简化代码) # ---------- Functions ---------- '%!in%' <- function(x,y)!('%in%'(x,y)) # 简化的标签提取函数:从单条EXIF数据中提取所有关键词 extract_tags <- function(dat_row) { # 只保留存在的关键词列 tag_cols <- intersect(keywords_names, names(dat_row)) if (length(tag_cols) == 0) return(character(0)) # 提取并扁平化所有标签(处理列表类型的列) tags <- unlist(dat_row[tag_cols], use.names = FALSE) # 去重并返回 unique(tags) } # ----------- Settings ---------- ss <- "/" haystacks <- c("H:/MyPhotos") # 补全路径斜杠,避免拼接出错 organizedMediaPhotos <- "V:/Photos" # 获取所有文件路径 all_files <- list.files(haystacks, recursive = TRUE, full.names = TRUE) keywords_names <- c("Category","XPKeywords","Keywords") # 预获取现有标签文件夹 current_tags <- basename(list.dirs(organizedMediaPhotos, recursive = FALSE)) # ----------- 批量处理核心逻辑 ----------- # 1. 批量读取所有文件的EXIF数据(这步比循环快N倍) all_exif <- read_exif(all_files, tags = keywords_names) # 2. 为每个文件提取标签 all_exif$tags <- map(all_exif, extract_tags) # 3. 收集所有需要的标签,创建缺失的文件夹 all_tags <- unique(unlist(all_exif$tags)) new_tags <- all_tags[all_tags %!in% current_tags] if (length(new_tags) > 0) { new_folders <- file.path(organizedMediaPhotos, new_tags) dir.create(new_folders, recursive = TRUE, showWarnings = FALSE) cat("Created folders for tags:", paste(new_tags, collapse = ", "), "\n") } # 4. 批量处理文件复制 # 先构建所有目标路径和源路径的对应关系 file_mappings <- map2(all_files, all_exif$tags, function(src, tags) { if (length(tags) == 0) return(NULL) dest_paths <- file.path(organizedMediaPhotos, tags, basename(src)) data.frame(src = rep(src, length(tags)), dest = dest_paths, stringsAsFactors = FALSE) }) file_mappings <- do.call(rbind, file_mappings) # 只复制不存在的文件 to_copy <- !file.exists(file_mappings$dest) if (any(to_copy)) { file.copy(file_mappings$src[to_copy], file_mappings$dest[to_copy], overwrite = FALSE) cat("Copied", sum(to_copy), "files to target folders\n") }
额外优化建议
- 如果你的照片数量特别多,可以考虑分批次处理(比如每次处理1000张),避免内存占用过高。
- 确保你的exiftool是最新版本,exifr的性能依赖于底层的exiftool,新版本通常有性能优化。
- 可以用
system.time()来测试各部分的耗时,定位具体的瓶颈。
内容的提问来源于stack exchange,提问作者Timo Kvamme
相关产品推荐
相关产品推荐

