PhotosImageClassifier+Batchs.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. //
  2. // PhotosImageClassifier+Batchs.swift
  3. // Demo
  4. //
  5. // Created by Groot on 2025/4/27.
  6. //
  7. import Foundation
  8. // MARK: - 批处理方法
  9. extension PhotosImageClassifier {
  10. /// 按批次处理图片项目
  11. /// - Parameters:
  12. /// - types: 分类类型
  13. /// - items: 图片项目列表
  14. /// - progressHandler: 进度和批次结果回调处理器
  15. func processBatches(
  16. with types: [PhotoImageClassifyType],
  17. items: [ImageItem],
  18. batchCompletionHandler: @escaping BatchProgressCompletion
  19. ) async {
  20. guard !items.isEmpty else {
  21. print("Unprocess ImageItems count is Zero --- Finished batch")
  22. return
  23. }
  24. // 计算需要处理的批次总数
  25. let totalItems = items.count
  26. let batchCount = Int(ceil(Double(totalItems) / Double(configuration.batchSize)))
  27. let startTime = Date()
  28. for batchIndex in 0..<batchCount {
  29. let batchStartTime = Date()
  30. // 处理当前批次
  31. let startIndex = batchIndex * configuration.batchSize
  32. let endIndex = min((batchIndex + 1) * configuration.batchSize, totalItems)
  33. guard startIndex <= endIndex else { return }
  34. let currentBatch = Array(items[startIndex..<endIndex])
  35. // 处理当前批次
  36. let processedItems = await processBatch(with: types, items: currentBatch)
  37. // 是否为最后一个批次
  38. let isLastBatch = batchIndex == batchCount - 1
  39. let batchDuration = Date().timeIntervalSince(batchStartTime)
  40. let totalDuration = Date().timeIntervalSince(startTime)
  41. let progress = Double(batchIndex + 1) / Double(batchCount)
  42. // 创建进度信息对象
  43. let progressInfo = ClassificationProgress(
  44. currentBatch: batchIndex + 1,
  45. totalBatches: batchCount,
  46. rate: progress,
  47. batchDuration: batchDuration,
  48. totalDuration: totalDuration,
  49. isLastBatch: isLastBatch
  50. )
  51. // 回调进度更新和批次处理结果
  52. await batchCompletionHandler(progressInfo, processedItems)
  53. }
  54. }
  55. /// 处理单批次图片项目
  56. /// - Parameters:
  57. /// - types: 分类类型
  58. /// - items: 图片项目列表
  59. /// - Returns: 处理后的图片项目列表
  60. func processBatch(with types: [PhotoImageClassifyType], items: [ImageItem]) async -> [ImageItem] {
  61. return await limitConcurrentProcessing(items: items, maxConcurrent: configuration.maxConcurrentProcessing) { item in
  62. // 请求图片
  63. var mutableItem = await self.requestAssetsImageFor(item, options: self.imageRequestOptions)
  64. // 处理相似图片、人物图片、模糊图片
  65. self.processImage(with: types, item: &mutableItem)
  66. // 提取图片信息
  67. mutableItem.imageInfo = mutableItem.asset.extractAssetInfo()
  68. return mutableItem
  69. }
  70. }
  71. // MARK: - 辅助方法
  72. /// 限制并发任务处理
  73. /// - Parameters:
  74. /// - items: 待处理项目
  75. /// - maxConcurrent: 最大并发数
  76. /// - processItem: 单项处理函数
  77. /// - Returns: 处理结果数组
  78. func limitConcurrentProcessing<T>(
  79. items: [ImageItem],
  80. maxConcurrent: Int,
  81. processItem: @escaping (ImageItem) async -> T?
  82. ) async -> [T] {
  83. guard !items.isEmpty else { return [] }
  84. var results: [T] = []
  85. // 使用TaskGroup并发处理
  86. await withTaskGroup(of: T?.self) { group in
  87. // 控制进行中的任务数量
  88. var pendingItems = items[...]
  89. var runningTasks = 0
  90. // 添加任务的辅助函数
  91. func addTask(for item: ImageItem) {
  92. group.addTask {
  93. return await processItem(item)
  94. }
  95. pendingItems = pendingItems.dropFirst()
  96. runningTasks += 1
  97. }
  98. // 初始添加任务,不超过最大并发数
  99. while runningTasks < maxConcurrent, let item = pendingItems.first {
  100. addTask(for: item)
  101. }
  102. // 处理完成的任务,并添加新任务
  103. for await result in group {
  104. runningTasks -= 1
  105. // 添加有效结果
  106. if let result = result {
  107. results.append(result)
  108. }
  109. // 如果还有待处理项,继续添加任务
  110. if let nextItem = pendingItems.first, runningTasks < maxConcurrent {
  111. addTask(for: nextItem)
  112. }
  113. }
  114. }
  115. return results
  116. }
  117. }