Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
P
PhoneManager
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Yang
PhoneManager
Commits
5712ddc8
Commit
5712ddc8
authored
Apr 23, 2025
by
shenyong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
优化相似度判断
parent
0f7aa68e
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
122 additions
and
36 deletions
+122
-36
PhotoAndVideoMananger.swift
...l/Class/PhotoAndVideoMananger/PhotoAndVideoMananger.swift
+122
-36
No files found.
PhoneManager/Class/Tool/Class/PhotoAndVideoMananger/PhotoAndVideoMananger.swift
View file @
5712ddc8
...
...
@@ -55,6 +55,9 @@ class PhotoAndVideoMananger {
var
ids
:[
String
]
=
[]
// 定义
private
let
hashDistance
=
100
func
setAssets
()
{
let
fetchOptions
=
PHFetchOptions
()
...
...
@@ -1009,39 +1012,54 @@ class PhotoAndVideoMananger {
// 计算两个哈希值的汉明距离
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
// 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
guard
hash1
.
count
==
hash2
.
count
else
{
return
Int
.
max
}
return
zip
(
hash1
,
hash2
)
.
filter
{
$0
!=
$1
}
.
count
}
func
groupSimilarImages
(
assets
:
[
PHAsset
],
progressCompletion
:
@escaping
([[
AssetModel
]])
->
Void
,
completion
:
@escaping
([[
AssetModel
]])
->
Void
)
{
print
(
"开始处理相似图片"
)
DispatchQueue
.
global
()
.
async
{
print
(
"进入异步任务处理相似图片"
)
var
assetModels
:
[
AssetModel
]
=
[]
var
hashes
:
[
String
:
AssetModel
]
=
[:]
var
groupedImages
:
[[
AssetModel
]]
=
[]
let
dispatchGroup
=
DispatchGroup
()
for
asset
in
assets
{
_
=
asset
.
pixelWidth
*
asset
.
pixelHeight
// 创建 AssetModel
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
// 请求图像
dispatchGroup
.
enter
()
PHImageManager
.
default
()
.
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
!=
""
{
let
hash
=
self
.
calculateImageHashUsingCoreImage
(
image
)
if
!
hash
.
isEmpty
{
hashes
[
hash
]
=
model
}
}
dispatchGroup
.
leave
()
}
}
// 等待所有请求完成后进行比较
dispatchGroup
.
notify
(
queue
:
.
global
())
{
print
(
"获取到全部hash值"
)
var
usedHashes
:
Set
<
String
>
=
[]
for
(
hash1
,
model1
)
in
hashes
{
if
usedHashes
.
contains
(
hash1
)
{
continue
}
...
...
@@ -1051,7 +1069,7 @@ class PhotoAndVideoMananger {
for
(
hash2
,
model2
)
in
hashes
{
if
usedHashes
.
contains
(
hash2
)
{
continue
}
let
distance
=
self
.
hammingDistance
(
hash1
,
hash2
)
if
distance
<
4
{
// 汉明距离小于 5 认为相似
if
distance
<
self
.
hashDistance
{
// 可以根据需求调整阈值
similarModels
.
append
(
model2
)
usedHashes
.
insert
(
hash2
)
}
...
...
@@ -1060,6 +1078,7 @@ class PhotoAndVideoMananger {
if
similarModels
.
count
>=
2
{
groupedImages
.
append
(
similarModels
)
// 每次找到新的相似组,通过 progressCompletion 回调返回当前已处理好的分组数据
print
(
"判断相似"
,
similarModels
)
DispatchQueue
.
main
.
async
{
progressCompletion
(
groupedImages
)
}
...
...
@@ -1071,6 +1090,7 @@ class PhotoAndVideoMananger {
}
}
}
}
}
...
...
@@ -1148,3 +1168,69 @@ extension Array {
}
}
}
extension
PhotoAndVideoMananger
{
// 计算图片的感知哈希值
func
calculateImageHashUsingCoreImage
(
_
image
:
UIImage
)
->
String
{
guard
let
cgImage
=
image
.
cgImage
else
{
return
""
}
// 生成CIImage
let
ciImage
=
CIImage
(
cgImage
:
cgImage
)
// 创建滤镜:灰度化图像
let
filter
=
CIFilter
(
name
:
"CIPhotoEffectNoir"
)
!
filter
.
setValue
(
ciImage
,
forKey
:
kCIInputImageKey
)
guard
let
outputImage
=
filter
.
outputImage
else
{
return
""
}
// 将输出图像缩放到32x32
let
context
=
CIContext
()
let
targetSize
=
CGSize
(
width
:
32
,
height
:
32
)
let
scaledImage
=
context
.
createCGImage
(
outputImage
,
from
:
outputImage
.
extent
)
!
let
resizedImage
=
UIImage
(
cgImage
:
scaledImage
,
scale
:
1.0
,
orientation
:
image
.
imageOrientation
)
// 获取像素数据
guard
let
pixelData
=
resizedImage
.
cgImage
?
.
dataProvider
?
.
data
,
let
data
=
CFDataGetBytePtr
(
pixelData
)
else
{
return
""
}
// 计算灰度像素平均值
var
pixels
:
[
UInt8
]
=
Array
(
repeating
:
0
,
count
:
1024
)
// 32 * 32
for
i
in
0
..<
32
{
for
j
in
0
..<
32
{
let
pixelIndex
=
(
i
*
32
+
j
)
*
4
// RGBA
let
r
=
data
[
pixelIndex
]
let
g
=
data
[
pixelIndex
+
1
]
let
b
=
data
[
pixelIndex
+
2
]
// 使用灰度公式转化
let
gray
=
UInt8
(
0.299
*
Double
(
r
)
+
0.587
*
Double
(
g
)
+
0.114
*
Double
(
b
))
pixels
[
i
*
32
+
j
]
=
gray
}
}
// 计算平均值
let
sum
=
pixels
.
reduce
(
0
)
{
UInt32
(
$0
)
+
UInt32
(
$1
)
}
// 使用 UInt32 来避免溢出
let
average
=
UInt8
(
sum
/
UInt32
(
pixels
.
count
))
// 确保将 sum 转换为 UInt32
// 生成哈希值
var
hash
=
""
for
pixel
in
pixels
{
hash
+=
pixel
>
average
?
"1"
:
"0"
}
return
hash
}
// 计算汉明距离
func
calculateHammingDistance
(
_
hash1
:
String
,
_
hash2
:
String
)
->
Int
{
guard
hash1
.
count
==
hash2
.
count
else
{
return
Int
.
max
}
return
zip
(
hash1
,
hash2
)
.
filter
{
$0
!=
$1
}
.
count
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment