PhotoClassifierPlugin.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import Flutter
  2. import UIKit
  3. import Photos
  4. public class PhotoClassifierPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
  5. private var eventSink: FlutterEventSink?
  6. public static func register(with registrar: FlutterPluginRegistrar) {
  7. // 注册方法通道
  8. let methodChannel = FlutterMethodChannel(name: "com.atmob.photo_classifier", binaryMessenger: registrar.messenger())
  9. // 注册事件通道
  10. let eventChannel = FlutterEventChannel(name: "com.atmob.photo_classifier_events", binaryMessenger: registrar.messenger())
  11. let instance = PhotoClassifierPlugin()
  12. registrar.addMethodCallDelegate(instance, channel: methodChannel)
  13. eventChannel.setStreamHandler(instance)
  14. }
  15. public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
  16. // 确保我们在主线程上处理UI相关操作
  17. DispatchQueue.main.async {
  18. switch call.method {
  19. case "startClassification":
  20. self.handleStartClassification(call: call, result: result)
  21. case "configureClassifier":
  22. self.handleConfigureClassifier(call: call, result: result)
  23. case "resetClassifier":
  24. PhotosImageClassifier.shared.reset()
  25. result(nil)
  26. case "checkAssetsLoaded":
  27. result(PhotosImageClassifier.shared.isAssetsLoaded)
  28. default:
  29. result(FlutterMethodNotImplemented)
  30. }
  31. }
  32. }
  33. }
  34. // MARK: -- Platform Methods
  35. extension PhotoClassifierPlugin {
  36. // 处理开始分类请求
  37. private func handleStartClassification(call: FlutterMethodCall, result: @escaping FlutterResult) {
  38. guard let args = call.arguments as? [String: Any], let types = self.convertClassifyTypes(args["types"] as? [String]), !types.isEmpty else {
  39. result(FlutterError(code: "INVALID_ARGUMENTS",
  40. message: "Missing or invalid types argument",
  41. details: nil))
  42. result(nil)
  43. return
  44. }
  45. Task {
  46. // 开始分类过程
  47. await PhotosImageClassifier.shared.startClassification(with: types) { [weak self] progress, classifiedResult in
  48. guard let self = self, let eventSink = self.eventSink else { return }
  49. // 转换进度信息
  50. let progressDict = progress.toDictionary()
  51. // 转换分类结果
  52. let resultDict = self.convertClassifiedResultToDictionary(classifiedResult)
  53. // 发送进度更新
  54. let updateDict: [String: Any] = [
  55. "progress": progressDict,
  56. "result": resultDict
  57. ]
  58. await MainActor.run {
  59. eventSink(updateDict)
  60. if progress.isCompleted {
  61. eventSink(FlutterEndOfEventStream)
  62. }
  63. }
  64. }
  65. result(nil)
  66. }
  67. }
  68. // 处理配置分类器
  69. private func handleConfigureClassifier(call: FlutterMethodCall, result: @escaping FlutterResult) {
  70. defer { result(nil) }
  71. guard let args = call.arguments as? [String: Any] else {
  72. result(FlutterError(code: "INVALID_ARGUMENTS",
  73. message: "Missing arguments",
  74. details: nil))
  75. return
  76. }
  77. // 解码参数
  78. guard let configData = try? JSONSerialization.data(withJSONObject: args),
  79. let config = try? JSONDecoder().decode(PhotosImageClassifier.Configuration.self, from: configData) else {
  80. result(FlutterError(code: "INVALID_CONFIG",
  81. message: "无法解析配置参数",
  82. details: nil))
  83. return
  84. }
  85. // 应用配置
  86. PhotosImageClassifier.shared.configure(with: config)
  87. }
  88. // 将字符串数组转换为分类类型
  89. private func convertClassifyTypes(_ types: [String]?) -> [PhotoImageClassifyType]? {
  90. guard let types else { return nil }
  91. var classifyTypes: Set<PhotoImageClassifyType> = []
  92. for type in types {
  93. guard let classifyType = PhotoImageClassifyType(type) else { continue }
  94. classifyTypes.insert(classifyType)
  95. }
  96. return Array(classifyTypes)
  97. }
  98. // 将分类结果转换为字典
  99. private func convertClassifiedResultToDictionary(_ result: PhotoImageClassifiedResult) -> [String: Any] {
  100. var resultDict: [String: Any] = [:]
  101. // 处理相似组
  102. if let similarGroups = result.similarGroups {
  103. resultDict["similarGroups"] = [
  104. "groups": similarGroups.map { group in
  105. group.images.map { $0.toDictionary() }
  106. }
  107. ]
  108. }
  109. // 处理人物图片
  110. if let peopleImages = result.peopleImages {
  111. resultDict["peopleImages"] = peopleImages.map { $0.toDictionary() }
  112. }
  113. // 处理截图
  114. if let screenshotImages = result.screenshotImages {
  115. resultDict["screenshotImages"] = screenshotImages.map { $0.toDictionary() }
  116. }
  117. // 处理模糊图片
  118. if let blurryImages = result.blurryImages {
  119. resultDict["blurryImages"] = blurryImages.map { $0.toDictionary() }
  120. }
  121. return resultDict
  122. }
  123. }
  124. extension PhotoClassifierPlugin {
  125. public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
  126. self.eventSink = events
  127. return nil
  128. }
  129. public func onCancel(withArguments arguments: Any?) -> FlutterError? {
  130. self.eventSink = nil
  131. return nil
  132. }
  133. }