Commit 17db8452 authored by CZ1004's avatar CZ1004

优化

parent 96dd0303
......@@ -67,16 +67,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func getHomeCacheData(){
// 首页有7个数据源
PhotoAndVideoMananger.mananger.setAssets()
}
var dataArrayDu : [[AssetModel]] = []
var dataArraySi : [[AssetModel]] = []
......@@ -175,6 +165,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
Print("更新截图数据结束")
})
}
Print("更新相似截图数据")
PhotoAndVideoMananger.mananger.groupSimilarImages(assets: PhotoAndVideoMananger.mananger.screenShotAssets) { similarGroups in
model.otherModelArray[1].assets = similarGroups
model.otherModelArray[1].allFileSize = 0
PhotoDataManager.manager.saveToFileSystem(model: model)
let dataUpdated = Notification.Name("HomeSimilarScreenshotResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
} completion: { similarGroups in
Print("更新相似截图数据结束")
}
}
......@@ -200,106 +203,46 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
// 更新重复图片和截图
// 查看更新到了哪一个时间点了【这里以时间为基准】
var allImageUpdateLocation = UserDefaults.standard.object(forKey: "allImageUpdateLocationDate")
if allImageUpdateLocation == nil {
allImageUpdateLocation = []
}
// 找到位置
var newArray : [PHAsset] = []
for item in PhotoAndVideoMananger.mananger.allAssets {
if !(allImageUpdateLocation as! [String]).contains(item.localIdentifier) {
newArray.append(item)
}
}
if newArray.count > 0 {
// 继续比较
PhotoAndVideoMananger.mananger.dealSimilarPhotos(assets: newArray, type: 0, threshold: 0.99) { data1,data2 in
if screeshotCount != PhotoAndVideoMananger.mananger.screenShotAssets.count || imageCount != PhotoAndVideoMananger.mananger.otherAssets.count{
Print("更新重复图片数据")
PhotoAndVideoMananger.mananger.groupDuplicateImages(assets: PhotoAndVideoMananger.mananger.allAssets) { similarGroups in
var dataArray1 : [AssetModel] = []
for item in data1 {
dataArray1 = dataArray1 + item
}
var dataArray2 : [AssetModel] = []
for item in data2 {
dataArray2 = dataArray2 + item
var dataArray : [AssetModel] = []
for item in similarGroups {
dataArray = dataArray + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray1)!,progress: { fileSiez, index in
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray)!,progress: { fileSiez, index in
// 不做处理
}, completion: {[dataArray1 = dataArray1] fileSize,index in
if dataArray1.count > 0 {
self.dataArrayDu.append(dataArray1)
model.titleModelArray[0].assets = self.dataArrayDu
self.dataArrayDu.remove(at: self.dataArrayDu.count - 1)
model.titleModelArray[0].allFileSize = self.dataDuFileSize + Double(fileSize)
}, completion: { fileSize,index in
model.titleModelArray[0].allFileSize = Double(fileSize)
model.titleModelArray[0].assets = similarGroups
PhotoDataManager.manager.saveToFileSystem(model: model)
let dataUpdated = Notification.Name("HomeDupImageResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
}
})
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray2)!,progress: { fileSize, index in
// 不做处理
}, completion: {[dataArray2 = dataArray2] fileSize,index in
if dataArray2.count > 0 {
self.dataArraySi.append(dataArray2)
model.titleModelArray[1].assets = self.dataArraySi
self.dataArraySi.remove(at: self.dataArraySi.count - 1)
model.titleModelArray[1].allFileSize = self.dataSiFileSize + Double(fileSize)
PhotoDataManager.manager.saveToFileSystem(model: model)
let dataUpdated = Notification.Name("HomeSimilarImageResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
} completion: { similarGroups in
Print("更新重复图片数据结束")
}
})
} completionHandler: { data1, data2 in
}
}
Print("更新相似图片数据")
PhotoAndVideoMananger.mananger.groupSimilarImages(assets: PhotoAndVideoMananger.mananger.allAssets) { similarGroups in
// 处理相似截图
var screenshotUpdateLocation = UserDefaults.standard.object(forKey: "screenshotUpdateLocationDate")
if screenshotUpdateLocation == nil {
screenshotUpdateLocation = []
}
// 找到位置
var screenshotNewArray : [PHAsset] = []
for item in PhotoAndVideoMananger.mananger.screenShotAssets {
if !(screenshotUpdateLocation as! [String]).contains(item.localIdentifier) {
screenshotNewArray.append(item)
}
}
if screenshotNewArray.count > 0 {
PhotoAndVideoMananger.mananger.dealSimilarPhotos(assets: screenshotNewArray, type: 1, threshold: 0.99) { data1,data2 in
var dataArray2 : [AssetModel] = []
for item in data2 {
dataArray2 = dataArray2 + item
var dataArray : [AssetModel] = []
for item in similarGroups {
dataArray = dataArray + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray2)!,progress: { fileSiez, index in
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray)!,progress: { fileSiez, index in
// 不做处理
}, completion: {[dataArray2 = dataArray2] fileSize,index in
if dataArray2.count > 0 {
self.dataArraySc.append(dataArray2)
model.titleModelArray[0].assets = self.dataArraySc
self.dataArraySc.remove(at: self.dataArraySc.count - 1)
model.otherModelArray[1].allFileSize = self.dataScFileSize + Double(fileSize)
}, completion: { fileSize,index in
model.titleModelArray[1].assets = similarGroups
model.titleModelArray[1].allFileSize = Double(fileSize)
PhotoDataManager.manager.saveToFileSystem(model: model)
let dataUpdated = Notification.Name("HomeSimilarScreenshotResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
}
})
} completionHandler: { data1, data2 in
} completion: { similarGroups in
Print("更新相似图片数据结束")
}
}
......
......@@ -14,8 +14,8 @@ class ChargeViewController:BaseViewController {
let sview:UIButton = UIButton()
sview.setImage(UIImage(named: "ic_details_charging"), for: .normal)
sview.width = 20
sview.height = 20
sview.width = 28
sview.height = 28
sview.x = view.width - sview.width - 15
sview.centerY = navCenterY
sview.addTarget(self, action: #selector(guideClick), for: .touchUpInside)
......
......@@ -305,7 +305,7 @@ class CompressCompletedViewController : BaseViewController{
}){ success, error in
if(success){
self.updateCompressData(flag: data.ident)
var deleteModel = AssetModel(localIdentifier: data.ident, assetSize: data.orgSize, createDate: data.createDate)
let deleteModel = AssetModel(localIdentifier: data.ident, assetSize: data.orgSize, createDate: data.createDate)
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: [deleteModel])
print("删除文件成功")
}else {
......
......@@ -58,9 +58,14 @@ class CompressNavView : UIView {
}
@objc private func proBtnClick() {
HomePayViewController.show {
if HomePayModel.share.isNoAd == false {
HomePayViewController.show {}
}else {
let vc : PayCompletedViewController = PayCompletedViewController()
vc.modalPresentationStyle = .fullScreen
self.responderViewController()?.present(vc, animated: true)
}
}
......
......@@ -225,6 +225,10 @@ class HomeViewController:BaseViewController {
}else {
if HomePayModel.share.isNoAd == false {
HomePayViewController.show {}
}else{
let vc : PayCompletedViewController = PayCompletedViewController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
}
}
......
......@@ -88,8 +88,12 @@ class HomeNavView:UIView {
}
@objc private func proBtnClick() {
HomePayViewController.show {
if HomePayModel.share.isNoAd == false {
HomePayViewController.show {}
}else {
let vc : PayCompletedViewController = PayCompletedViewController()
vc.modalPresentationStyle = .fullScreen
self.responderViewController()?.present(vc, animated: true)
}
}
......
......@@ -111,26 +111,6 @@ class PhotoDataManager {
self.saveToFileSystem(model: model)
}
})
// 将磁盘中存的内容也删除
let allImageUpdateLocation : [String] = UserDefaults.standard.object(forKey: "allImageUpdateLocationDate") as! [String]
var tempArray = allImageUpdateLocation
for item in data{
tempArray.removeAll{$0 == item.localIdentifier}
}
UserDefaults.standard.set(tempArray, forKey: "allImageUpdateLocationDate")
var screenshotUpdateLocation : [String] = UserDefaults.standard.object(forKey: "screenshotUpdateLocationDate") as! [String]
var tempArraySC = screenshotUpdateLocation
for item in data{
tempArraySC.removeAll{$0 == item.localIdentifier}
}
UserDefaults.standard.set(tempArraySC, forKey: "screenshotUpdateLocationDate")
// 删除更新中的数据
for item in data{
screenshotUpdateLocation.removeAll{$0 == item.localIdentifier}
}
// 将单利中的数据也删除
for item in data {
Singleton.shared.resourceModel.removeAll{ $0.ident == item.localIdentifier }
......@@ -176,71 +156,48 @@ class PhotoDataManager {
PhotoDataManager.manager.saveToFileSystem(model: allModel)
Print("正在处理重复图片")
PhotoAndVideoMananger.mananger.groupDuplicateImages(assets: PhotoAndVideoMananger.mananger.allAssets) { similarGroups in
let group = DispatchGroup()
Print("正在处理重复和相似图片")
group.enter()
PhotoAndVideoMananger.mananger.dealSimilarPhotos(assets: PhotoAndVideoMananger.mananger.allAssets, type: 0, threshold: 0.99) { data1,data2 in
var dataArray1 : [AssetModel] = []
var dataArray2 : [AssetModel] = []
for item in data1 {
dataArray1 = dataArray1 + item
}
for item in data2 {
dataArray2 = dataArray2 + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray1)!,progress: { fileSiez, index in
// 不做处理
}, completion: { fileSize,index in
// model1.assets = data1
// model1.allFileSize = Double(fileSize)
// PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeDupImageResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
})
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray2)!,progress: { fileSize, index in
// 不做处理
}, completion: { fileSize,index in
model2.assets = data2
model2.allFileSize = Double(fileSize)
PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeSimilarImageResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
})
} completionHandler: { data1, data2 in
var dataArray1 : [AssetModel] = []
var dataArray2 : [AssetModel] = []
for item in data1 {
dataArray1 = dataArray1 + item
}
for item in data2 {
dataArray2 = dataArray2 + item
var dataArray : [AssetModel] = []
for item in similarGroups {
dataArray = dataArray + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray1)!,progress: { fileSiez, index in
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray)!,progress: { fileSiez, index in
// 不做处理
}, completion: { fileSize,index in
model1.assets = data1
model1.assets = similarGroups
model1.allFileSize = Double(fileSize)
PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeDupImageResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
})
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray1)!,progress: { fileSize, index in
} completion: { model in
Print("处理重复图片结束")
}
Print("正在处理相似图片")
PhotoAndVideoMananger.mananger.groupSimilarImages(assets: PhotoAndVideoMananger.mananger.allAssets) { (similarGroups) in
var dataArray : [AssetModel] = []
for item in similarGroups {
dataArray = dataArray + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray)!,progress: { fileSiez, index in
// 不做处理
}, completion: { fileSize,index in
model2.assets = data2
model2.assets = similarGroups
model2.allFileSize = Double(fileSize)
PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeSimilarImageResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
})
group.leave()
Print("处理重复和相似图片结束")
} completion: { similarGroups in
Print("处理相似图片结束")
}
// 视频数据
......@@ -262,43 +219,19 @@ class PhotoDataManager {
})
}
// 相似截图
Print("正在处理相似截图")
PhotoAndVideoMananger.mananger.dealSimilarPhotos(assets: PhotoAndVideoMananger.mananger.screenShotAssets, type: 1, threshold: 0.99) { data1,data2 in
var dataArray1 : [AssetModel] = []
for item in data1 {
dataArray1 = dataArray1 + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray1)!,progress: { fileSiez, index in
// 不做处理
}, completion: { fileSize,index in
model4.assets = data2
model4.allFileSize = Double(fileSize)
PhotoAndVideoMananger.mananger.groupSimilarImages(assets: PhotoAndVideoMananger.mananger.screenShotAssets) { (similarGroups) in
model4.assets = similarGroups
model4.allFileSize = 0
PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeSimilarScreenshotResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
})
} completionHandler: { data1, data2 in
var dataArray1 : [AssetModel] = []
for item in data1 {
dataArray1 = dataArray1 + item
}
PhotoAndVideoMananger.mananger.calculateTotalSize(of: PhotoAndVideoMananger.mananger.getPHAsssetwithIDs(ids: dataArray1)!,progress: { fileSiez, index in
// 不做处理
}, completion: { fileSize,index in
model4.assets = data2
model4.allFileSize = Double(fileSize)
PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeSimilarScreenshotResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
})
} completion: { similarGroups in
Print("处理相似截图结束")
}
// 截图数据
Print("正在处理截图")
group.enter()
PhotoAndVideoMananger.mananger.dealScreenShotData { data in
var dataArray : [AssetModel] = []
for item in data {
......@@ -312,7 +245,6 @@ class PhotoDataManager {
PhotoDataManager.manager.saveToFileSystem(model: allModel)
let dataUpdated = Notification.Name("HomeScreenShotResourceUpdate")
NotificationCenter.default.post(name: dataUpdated, object: nil)
group.leave()
Print("处理截图结束")
})
}
......
//
// PayCompletedViewController.swift
// PhoneManager
//
// Created by edy on 2025/4/16.
//
import Foundation
class PayCompletedViewController : BaseViewController{
lazy var logoImageView : UIImageView = {
let view = UIImageView()
view.layer.cornerRadius = 22.5
view.clipsToBounds = true
view.image = UIImage(named: "logo_phone_manager")
return view
}()
lazy var titleLabel : UILabel = {
let view = UILabel()
view.text = "Phone Manager Pro Edition"
view.textColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
view.font = UIFont.systemFont(ofSize: 18, weight: .bold)
view.textAlignment = .center
return view
}()
lazy var detailTitleLabel : UILabel = {
let view = UILabel()
view.text = "You can now access all Phone Manager features without restrictions"
view.textColor = UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
view.font = UIFont.systemFont(ofSize: 14, weight: .regular)
view.textAlignment = .center
view.numberOfLines = 0
return view
}()
lazy var closeButton : UIButton = {
let view = UIButton()
view.backgroundColor = .clear
view.setImage(UIImage(named: "ic_close_charging"), for: .normal)
view.addTarget(self, action: #selector(closeCurrentPage), for: .touchUpInside)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setUI()
}
func setUI(){
self.view.addSubview(self.closeButton)
self.view.addSubview(self.logoImageView)
self.view.addSubview(self.titleLabel)
self.view.addSubview(self.detailTitleLabel)
self.closeButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-15)
make.top.equalToSuperview().offset(statusBarHeight + 15)
make.width.height.equalTo(28)
}
self.logoImageView.snp.makeConstraints { make in
make.width.height.equalTo(150)
make.top.equalToSuperview().offset(210)
make.centerX.equalToSuperview()
}
self.titleLabel.snp.makeConstraints { make in
make.top.equalTo(self.logoImageView.snp.bottom).offset(40)
make.left.equalToSuperview().offset(30)
make.right.equalToSuperview().offset(-30)
make.height.equalTo(25)
}
self.detailTitleLabel.snp.makeConstraints { make in
make.top.equalTo(self.titleLabel.snp.bottom).offset(4)
make.left.equalToSuperview().offset(30)
make.right.equalToSuperview().offset(-30)
make.height.equalTo(40)
}
}
}
extension PayCompletedViewController {
@objc func closeCurrentPage(){
self.dismiss(animated: true)
}
}
......@@ -21,7 +21,12 @@ class SettingViewHeaderCell : UITableViewCell {
lazy var fLabel : UILabel = {
let label = UILabel();
if HomePayModel.share.isNoAd == false {
label.text = "Unlock Unlimited Access"
}else {
label.text = "Phone Manager Pro Edition"
}
label.font = UIFont(name: "PingFang SC-Bold", size: 18)
label.textColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
label.textAlignment = .center
......@@ -29,11 +34,17 @@ class SettingViewHeaderCell : UITableViewCell {
}()
lazy var sLabel : UILabel = {
let label = UILabel();
let label = UILabel()
label.numberOfLines = 0
if HomePayModel.share.isNoAd == false {
label.text = "Enjoy unlimited access with all Cleanup features"
}else{
label.text = "You can now access all Phone Manager features without restrictions"
}
label.font = UIFont(name: "PingFang SC-Regular", size: 14)
label.textColor = UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
label.textAlignment = .center
return label
}()
......@@ -90,7 +101,11 @@ class SettingViewHeaderCell : UITableViewCell {
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(fLabel.snp.bottom).offset(4)
if HomePayModel.share.isNoAd == false {
make.height.equalTo(20)
}else{
make.height.equalTo(42)
}
}
self.preButtonImageView.snp.makeConstraints { make in
......@@ -101,7 +116,11 @@ class SettingViewHeaderCell : UITableViewCell {
self.moreButton.snp.makeConstraints { make in
make.width.equalTo(245)
if HomePayModel.share.isNoAd == false {
make.height.equalTo(46)
}else{
make.height.equalTo(0)
}
make.centerX.equalToSuperview()
make.top.equalTo(self.sLabel.snp.bottom).offset(20)
}
......
......@@ -138,7 +138,11 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
if HomePayModel.share.isNoAd == false {
return 385
}else{
return 339
}
}
return 60
}
......
......@@ -12,6 +12,8 @@ import Vision
import CoreML
import HXPhotoPicker
import CryptoKit
import Photos
import CoreImage
enum SoureceType {
case Photo
......@@ -431,7 +433,7 @@ class PhotoAndVideoMananger {
// 1. 判断创建时间(误差 < 1s)
let timeC = abs((asset1.creationDate?.timeIntervalSince1970 ?? 0) - (asset2.creationDate?.timeIntervalSince1970 ?? 0))
//
//
if timeC > 1000 {
return false
......@@ -450,7 +452,7 @@ class PhotoAndVideoMananger {
return false
}
// // 4. 判断文件大小
// // 4. 判断文件大小
let resource1 = PHAssetResource.assetResources(for: asset1).first
let resource2 = PHAssetResource.assetResources(for: asset2).first
......@@ -896,6 +898,179 @@ class PhotoAndVideoMananger {
}
}
// 计算图片的哈希值
func calculateHash(for asset: PHAsset) -> String? {
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.isSynchronous = false // 异步请求
options.deliveryMode = .fastFormat // 快速格式
let semaphore = DispatchSemaphore(value: 0)
var hashString: String?
manager.requestImageData(for: asset, options: options) { (imageData, _, _, _) in
defer {
semaphore.signal()
}
if let data = imageData {
let hash = Insecure.MD5.hash(data: data)
hashString = hash.compactMap { String(format: "%02x", $0) }.joined()
}
}
semaphore.wait()
return hashString
}
// 分组重复图片
func groupDuplicateImages(assets: [PHAsset], progressCompletion: @escaping ([[AssetModel]]) -> Void, completion: @escaping ([[AssetModel]]) -> Void) {
DispatchQueue.global().async {
var hashToAssets: [String: [PHAsset]] = [:]
var allDuplicateGroups: [[AssetModel]] = []
let chunkSize = 10 // 每次处理 10 张图片
func isGroupDuplicate(_ newGroup: [AssetModel]) -> Bool {
for existingGroup in allDuplicateGroups {
if Set(newGroup.map { $0.localIdentifier }) == Set(existingGroup.map { $0.localIdentifier }) {
return true
}
}
return false
}
for chunk in assets.chunked(into: chunkSize) {
for asset in chunk {
if let hash = self.calculateHash(for: asset) {
hashToAssets[hash, default: []].append(asset)
if hashToAssets[hash]!.count > 1 && hashToAssets[hash]!.count == 2 {
var currentGroup: [AssetModel] = []
for asset in hashToAssets[hash]! {
currentGroup.append(AssetModel(localIdentifier: asset.localIdentifier, assetSize: self.findAssetSize(asset: asset), createDate: asset.creationDate!))
}
if !isGroupDuplicate(currentGroup) {
allDuplicateGroups.append(currentGroup)
}
// 持续返回已处理好的分组数据
progressCompletion(allDuplicateGroups)
}
}
}
}
// 过滤出重复的分组
let duplicateGroups = hashToAssets.filter { $0.value.count > 1 }.map { $0.value }
for (_, group) in duplicateGroups.enumerated() {
var currentGroup: [AssetModel] = []
for asset in group {
currentGroup.append(AssetModel(localIdentifier: asset.localIdentifier, assetSize: self.findAssetSize(asset: asset), createDate: asset.creationDate!))
}
if !isGroupDuplicate(currentGroup) {
allDuplicateGroups.append(currentGroup)
}
// 持续返回已处理好的分组数据
progressCompletion(allDuplicateGroups)
}
// 全部找完之后返回所有重复组
completion(allDuplicateGroups)
}
}
// 计算图片的 PHash 值
func pHash(for image: UIImage) -> String {
let size = CGSize(width: 32, height: 32)
UIGraphicsBeginImageContextWithOptions(size, false, 1.0)
image.draw(in: CGRect(origin: .zero, size: size))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let ciImage = CIImage(image: resizedImage!) else { return "" }
let context = CIContext()
let filter = CIFilter(name: "CILanczosScaleTransform")
filter?.setValue(ciImage, forKey: kCIInputImageKey)
filter?.setValue(32 / ciImage.extent.width, forKey: kCIInputScaleKey)
filter?.setValue(1.0, forKey: kCIInputAspectRatioKey)
guard let outputImage = filter?.outputImage else {
return ""
}
let cgImage = context.createCGImage(outputImage, from: outputImage.extent)!
let data = cgImage.dataProvider!.data!
if let pointer = CFDataGetBytePtr(data) {
var hash = ""
for i in 0..<8 {
hash += String(format: "%02x", pointer[i])
}
return hash
}
return ""
}
// 计算两个哈希值的汉明距离
func hammingDistance(_ hash1: String, _ hash2: String) -> Int {
var distance = 0
for (char1, char2) in zip(hash1, hash2) {
let int1 = Int(String(char1), radix: 16)!
let int2 = Int(String(char2), radix: 16)!
let xor = int1 ^ int2
distance += String(xor, radix: 2).filter { $0 == "1" }.count
}
return distance
}
func groupSimilarImages(assets: [PHAsset], progressCompletion: @escaping ([[AssetModel]]) -> Void, completion: @escaping ([[AssetModel]]) -> Void) {
DispatchQueue.global().async {
var assetModels: [AssetModel] = []
var hashes: [String: AssetModel] = [:]
var groupedImages: [[AssetModel]] = []
for asset in assets {
_ = asset.pixelWidth * asset.pixelHeight
let createDate = asset.creationDate ?? Date()
let model = AssetModel(localIdentifier: asset.localIdentifier, assetSize: self.findAssetSize(asset: asset), createDate: createDate)
assetModels.append(model)
let manager = PHImageManager.default()
manager.requestImage(for: asset, targetSize: CGSize(width: 32, height: 32), contentMode: .aspectFit, options: nil) { (image, _) in
if let image = image {
let hash = self.pHash(for: image)
if hash != "" {
hashes[hash] = model
}
}
}
}
var usedHashes: Set<String> = []
for (hash1, model1) in hashes {
if usedHashes.contains(hash1) { continue }
var similarModels: [AssetModel] = [model1]
usedHashes.insert(hash1)
for (hash2, model2) in hashes {
if usedHashes.contains(hash2) { continue }
let distance = self.hammingDistance(hash1, hash2)
if distance < 4 { // 汉明距离小于 5 认为相似
similarModels.append(model2)
usedHashes.insert(hash2)
}
}
if similarModels.count >= 2 {
groupedImages.append(similarModels)
// 每次找到新的相似组,通过 progressCompletion 回调返回当前已处理好的分组数据
DispatchQueue.main.async {
progressCompletion(groupedImages)
}
}
}
DispatchQueue.main.async {
completion(groupedImages)
}
}
}
}
......@@ -965,3 +1140,11 @@ class SecretPhotoManager: NSObject, PhotoPickerControllerDelegate , CameraContro
}
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0..<Swift.min($0 + size, count)])
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment