Perception 0.8.3__tar.gz → 0.8.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {perception-0.8.3 → perception-0.8.4}/PKG-INFO +4 -4
- {perception-0.8.3 → perception-0.8.4}/build.py +0 -1
- {perception-0.8.3 → perception-0.8.4}/perception/approximate_deduplication/__init__.py +22 -47
- perception-0.8.4/perception/approximate_deduplication/_graph_backend.py +138 -0
- {perception-0.8.3 → perception-0.8.4}/perception/approximate_deduplication/debug.py +2 -4
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/image_transforms.py +2 -2
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/__init__.py +0 -1
- {perception-0.8.3 → perception-0.8.4}/pyproject.toml +5 -3
- {perception-0.8.3 → perception-0.8.4}/setup.py +5 -3
- {perception-0.8.3 → perception-0.8.4}/LICENSE +0 -0
- {perception-0.8.3 → perception-0.8.4}/README.md +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/__init__.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/approximate_deduplication/index.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/approximate_deduplication/serve.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/__init__.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/common.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/extensions.pyx +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/image.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/video.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/benchmarking/video_transforms.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/extensions.pyx +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/hasher.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/__init__.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/average.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/dhash.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/opencv.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/pdq.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/phash.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/image/wavelet.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/tools.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/video/__init__.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/video/framewise.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/hashers/video/tmk.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/local_descriptor_deduplication.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/py.typed +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/__init__.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/README.md +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image1.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image10.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image2.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image3.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image4.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image5.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image6.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image7.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image8.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/images/image9.jpg +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/logos/README.md +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/logos/logoipsum.png +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/README.md +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/expected_tmk.json.gz +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/extra_channel_attached_pic.mp4 +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/extra_channel_attached_pic_audio.mp4 +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/rgb.m4v +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/v1.m4v +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/v2.m4v +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/testing/videos/v2s.mov +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/tools.py +0 -0
- {perception-0.8.3 → perception-0.8.4}/perception/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Perception
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Perception provides flexible, well-documented, and comprehensively tested tooling for perceptual hashing research, development, and production use.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -14,17 +14,17 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
16
16
|
Provides-Extra: benchmarking
|
|
17
|
-
Provides-Extra: experimental
|
|
18
17
|
Provides-Extra: matching
|
|
19
18
|
Provides-Extra: pdq
|
|
20
19
|
Requires-Dist: Cython (>=3.0.0,<4.0.0)
|
|
21
20
|
Requires-Dist: Pillow
|
|
22
21
|
Requires-Dist: aiohttp ; extra == "matching"
|
|
23
22
|
Requires-Dist: albumentations (>=2.0.8,<3.0.0) ; extra == "benchmarking"
|
|
24
|
-
Requires-Dist: faiss-cpu (>=1.8.0,<2.0.0)
|
|
23
|
+
Requires-Dist: faiss-cpu (>=1.8.0,<2.0.0)
|
|
25
24
|
Requires-Dist: ffmpeg-python ; extra == "benchmarking"
|
|
26
25
|
Requires-Dist: matplotlib ; extra == "benchmarking"
|
|
27
|
-
Requires-Dist: networkit (>=11.1,<12.0.0) ;
|
|
26
|
+
Requires-Dist: networkit (>=11.1,<12.0.0) ; sys_platform != "darwin"
|
|
27
|
+
Requires-Dist: networkx (>=3.0,<4.0) ; sys_platform == "darwin"
|
|
28
28
|
Requires-Dist: numpy (>=1.26.4,<3.0.0)
|
|
29
29
|
Requires-Dist: opencv-contrib-python-headless (>=4.10.0,<5.0.0)
|
|
30
30
|
Requires-Dist: pandas
|
|
@@ -4,11 +4,12 @@ import os.path as op
|
|
|
4
4
|
import typing
|
|
5
5
|
|
|
6
6
|
import faiss
|
|
7
|
-
import networkit as nk
|
|
8
7
|
import numpy as np
|
|
9
8
|
import tqdm
|
|
10
9
|
import typing_extensions
|
|
11
10
|
|
|
11
|
+
from ._graph_backend import get_graph_backend
|
|
12
|
+
|
|
12
13
|
LOGGER = logging.getLogger(__name__)
|
|
13
14
|
DEFAULT_PCT_PROBE = 0
|
|
14
15
|
|
|
@@ -227,16 +228,13 @@ def pairs_to_clusters(
|
|
|
227
228
|
node_to_id_map = {v: k for k, v in id_to_node_map.items()}
|
|
228
229
|
|
|
229
230
|
LOGGER.debug("Building graph.")
|
|
230
|
-
graph = nk.Graph(len(list_ids))
|
|
231
231
|
node_pairs = {(id_to_node_map[pair[0]], id_to_node_map[pair[1]]) for pair in pairs}
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
backend = get_graph_backend()
|
|
233
|
+
graph = backend.build_graph(len(list_ids), node_pairs)
|
|
234
234
|
|
|
235
235
|
assignments: list[ClusterAssignment] = []
|
|
236
236
|
cluster_index = 0
|
|
237
|
-
|
|
238
|
-
cc_query.run()
|
|
239
|
-
components = cc_query.getComponents()
|
|
237
|
+
components = backend.connected_components(graph)
|
|
240
238
|
|
|
241
239
|
for component in components:
|
|
242
240
|
LOGGER.debug("Got component with size: %s", len(component))
|
|
@@ -246,19 +244,9 @@ def pairs_to_clusters(
|
|
|
246
244
|
)
|
|
247
245
|
cluster_index += 1
|
|
248
246
|
continue
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
algo = nk.community.PLP(cc_sub_graph)
|
|
253
|
-
algo.run()
|
|
254
|
-
communities = algo.getPartition()
|
|
255
|
-
community_map = communities.subsetSizeMap()
|
|
256
|
-
for community, size in community_map.items():
|
|
257
|
-
LOGGER.debug("Got community with size: %s", size)
|
|
258
|
-
community_members = list(
|
|
259
|
-
communities.getMembers(community)
|
|
260
|
-
) # Need to do this to do batching.
|
|
261
|
-
community_members = [component_node_map[i] for i in community_members]
|
|
247
|
+
communities = backend.communities(graph, component)
|
|
248
|
+
for community_members in communities:
|
|
249
|
+
LOGGER.debug("Got community with size: %s", len(community_members))
|
|
262
250
|
if strictness == "community":
|
|
263
251
|
assignments.extend(
|
|
264
252
|
[
|
|
@@ -269,33 +257,20 @@ def pairs_to_clusters(
|
|
|
269
257
|
cluster_index += 1
|
|
270
258
|
continue
|
|
271
259
|
|
|
272
|
-
for
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
260
|
+
for clique_members in backend.maximal_cliques(
|
|
261
|
+
graph,
|
|
262
|
+
community_members,
|
|
263
|
+
max_clique_batch_size=max_clique_batch_size,
|
|
264
|
+
):
|
|
265
|
+
assignments.extend(
|
|
266
|
+
[
|
|
267
|
+
{
|
|
268
|
+
"id": node_to_id_map[n],
|
|
269
|
+
"cluster": cluster_index,
|
|
270
|
+
}
|
|
271
|
+
for n in clique_members
|
|
272
|
+
]
|
|
281
273
|
)
|
|
282
|
-
|
|
283
|
-
while subgraph.numberOfNodes() > 0:
|
|
284
|
-
LOGGER.debug("Subgraph size: %s", subgraph.numberOfNodes())
|
|
285
|
-
clique = nk.clique.MaximalCliques(subgraph, maximumOnly=True)
|
|
286
|
-
clique.run()
|
|
287
|
-
clique_members = clique.getCliques()[0]
|
|
288
|
-
assignments.extend(
|
|
289
|
-
[
|
|
290
|
-
{
|
|
291
|
-
"id": node_to_id_map[community_node_map[n]],
|
|
292
|
-
"cluster": cluster_index,
|
|
293
|
-
}
|
|
294
|
-
for n in clique_members
|
|
295
|
-
]
|
|
296
|
-
)
|
|
297
|
-
cluster_index += 1
|
|
298
|
-
for n in clique_members:
|
|
299
|
-
subgraph.removeNode(n)
|
|
274
|
+
cluster_index += 1
|
|
300
275
|
|
|
301
276
|
return assignments
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import typing
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GraphBackend(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def build_graph(
|
|
9
|
+
self, node_count: int, edges: typing.Iterable[tuple[int, int]]
|
|
10
|
+
) -> typing.Any: ...
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def connected_components(self, graph: typing.Any) -> list[list[int]]: ...
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def communities(
|
|
17
|
+
self, graph: typing.Any, component: list[int]
|
|
18
|
+
) -> list[list[int]]: ...
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def maximal_cliques(
|
|
22
|
+
self,
|
|
23
|
+
graph: typing.Any,
|
|
24
|
+
community_nodes: list[int],
|
|
25
|
+
max_clique_batch_size: int,
|
|
26
|
+
) -> list[list[int]]: ...
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NetworkitGraphBackend(GraphBackend):
|
|
30
|
+
def __init__(self):
|
|
31
|
+
import networkit as nk
|
|
32
|
+
|
|
33
|
+
self.nk = nk
|
|
34
|
+
|
|
35
|
+
def build_graph(
|
|
36
|
+
self, node_count: int, edges: typing.Iterable[tuple[int, int]]
|
|
37
|
+
) -> typing.Any:
|
|
38
|
+
graph = self.nk.Graph(node_count)
|
|
39
|
+
for start, end in edges:
|
|
40
|
+
graph.addEdge(start, end)
|
|
41
|
+
return graph
|
|
42
|
+
|
|
43
|
+
def connected_components(self, graph: typing.Any) -> list[list[int]]:
|
|
44
|
+
cc_query = self.nk.components.ConnectedComponents(graph)
|
|
45
|
+
cc_query.run()
|
|
46
|
+
return cc_query.getComponents()
|
|
47
|
+
|
|
48
|
+
def communities(self, graph: typing.Any, component: list[int]) -> list[list[int]]:
|
|
49
|
+
component_node_map = dict(enumerate(component))
|
|
50
|
+
subgraph = self.nk.graphtools.subgraphFromNodes(graph, component, compact=True)
|
|
51
|
+
algo = self.nk.community.PLP(subgraph, maxIterations=32)
|
|
52
|
+
algo.run()
|
|
53
|
+
communities = algo.getPartition()
|
|
54
|
+
return [
|
|
55
|
+
[component_node_map[node] for node in communities.getMembers(community)]
|
|
56
|
+
for community in communities.subsetSizeMap().keys()
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
def maximal_cliques(
|
|
60
|
+
self,
|
|
61
|
+
graph: typing.Any,
|
|
62
|
+
community_nodes: list[int],
|
|
63
|
+
max_clique_batch_size: int,
|
|
64
|
+
) -> list[list[int]]:
|
|
65
|
+
cliques: list[list[int]] = []
|
|
66
|
+
for start in range(0, len(community_nodes), max_clique_batch_size):
|
|
67
|
+
batch_nodes = community_nodes[start : start + max_clique_batch_size]
|
|
68
|
+
community_node_map = dict(enumerate(batch_nodes))
|
|
69
|
+
subgraph = self.nk.graphtools.subgraphFromNodes(
|
|
70
|
+
graph, batch_nodes, compact=True
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
while subgraph.numberOfNodes() > 0:
|
|
74
|
+
clique = self.nk.clique.MaximalCliques(subgraph, maximumOnly=True)
|
|
75
|
+
clique.run()
|
|
76
|
+
clique_members = clique.getCliques()[0]
|
|
77
|
+
cliques.append([community_node_map[node] for node in clique_members])
|
|
78
|
+
for node in clique_members:
|
|
79
|
+
subgraph.removeNode(node)
|
|
80
|
+
|
|
81
|
+
return cliques
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class NetworkxGraphBackend(GraphBackend):
|
|
85
|
+
def __init__(self):
|
|
86
|
+
import networkx as nx
|
|
87
|
+
|
|
88
|
+
self.nx = nx
|
|
89
|
+
|
|
90
|
+
def build_graph(
|
|
91
|
+
self, node_count: int, edges: typing.Iterable[tuple[int, int]]
|
|
92
|
+
) -> typing.Any:
|
|
93
|
+
graph = self.nx.Graph()
|
|
94
|
+
graph.add_nodes_from(range(node_count))
|
|
95
|
+
graph.add_edges_from(edges)
|
|
96
|
+
return graph
|
|
97
|
+
|
|
98
|
+
def connected_components(self, graph: typing.Any) -> list[list[int]]:
|
|
99
|
+
return [list(component) for component in self.nx.connected_components(graph)]
|
|
100
|
+
|
|
101
|
+
def communities(self, graph: typing.Any, component: list[int]) -> list[list[int]]:
|
|
102
|
+
subgraph = graph.subgraph(component)
|
|
103
|
+
return [
|
|
104
|
+
list(community)
|
|
105
|
+
for community in self.nx.algorithms.community.asyn_lpa_communities(
|
|
106
|
+
subgraph, seed=0
|
|
107
|
+
)
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
def maximal_cliques(
|
|
111
|
+
self,
|
|
112
|
+
graph: typing.Any,
|
|
113
|
+
community_nodes: list[int],
|
|
114
|
+
max_clique_batch_size: int,
|
|
115
|
+
) -> list[list[int]]:
|
|
116
|
+
cliques: list[list[int]] = []
|
|
117
|
+
for start in range(0, len(community_nodes), max_clique_batch_size):
|
|
118
|
+
batch_nodes = community_nodes[start : start + max_clique_batch_size]
|
|
119
|
+
subgraph = graph.subgraph(batch_nodes).copy()
|
|
120
|
+
|
|
121
|
+
while subgraph.number_of_nodes() > 0:
|
|
122
|
+
clique_members = max(
|
|
123
|
+
self.nx.find_cliques(subgraph),
|
|
124
|
+
key=lambda clique: (
|
|
125
|
+
len(clique),
|
|
126
|
+
tuple(sorted(clique)),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
cliques.append(list(clique_members))
|
|
130
|
+
subgraph.remove_nodes_from(clique_members)
|
|
131
|
+
|
|
132
|
+
return cliques
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def get_graph_backend() -> GraphBackend:
|
|
136
|
+
if sys.platform == "darwin":
|
|
137
|
+
return NetworkxGraphBackend()
|
|
138
|
+
return NetworkitGraphBackend()
|
|
@@ -79,10 +79,8 @@ def vizualize_pair(
|
|
|
79
79
|
circle_size=circle_size,
|
|
80
80
|
)
|
|
81
81
|
else:
|
|
82
|
-
LOGGER.warning(
|
|
83
|
-
|
|
84
|
-
won't match perception match points."""
|
|
85
|
-
)
|
|
82
|
+
LOGGER.warning("""No match_metadata provided, recalculating match points,
|
|
83
|
+
won't match perception match points.""")
|
|
86
84
|
img_matched = viz_brute_force(features_1, features_2, img1, img2, ratio=ratio)
|
|
87
85
|
|
|
88
86
|
return img_matched
|
|
@@ -17,7 +17,7 @@ def apply_watermark(watermark, alpha: float = 1.0, size: float = 1.0):
|
|
|
17
17
|
|
|
18
18
|
# Why do we have to do this? It's not clear. But the process doesn't work
|
|
19
19
|
# without it.
|
|
20
|
-
|
|
20
|
+
B, G, R, A = cv2.split(watermark)
|
|
21
21
|
B = cv2.bitwise_and(B, B, mask=A)
|
|
22
22
|
G = cv2.bitwise_and(G, G, mask=A)
|
|
23
23
|
R = cv2.bitwise_and(R, R, mask=A)
|
|
@@ -25,7 +25,7 @@ def apply_watermark(watermark, alpha: float = 1.0, size: float = 1.0):
|
|
|
25
25
|
|
|
26
26
|
def transform(image):
|
|
27
27
|
# Add alpha channel
|
|
28
|
-
|
|
28
|
+
h, w = image.shape[:2]
|
|
29
29
|
wh, ww = watermark.shape[:2]
|
|
30
30
|
scale = size * min(h / wh, w / ww)
|
|
31
31
|
image = np.dstack([image, np.ones((h, w), dtype="uint8") * 255])
|
|
@@ -10,6 +10,9 @@ dependencies = [
|
|
|
10
10
|
"Cython>=3.0.0,<4.0.0",
|
|
11
11
|
"numpy>=1.26.4,<3.0.0",
|
|
12
12
|
"opencv-contrib-python-headless>=4.10.0,<5.0.0",
|
|
13
|
+
"faiss-cpu>=1.8.0,<2.0.0",
|
|
14
|
+
"networkit>=11.1,<12.0.0; sys_platform != 'darwin'",
|
|
15
|
+
"networkx>=3.0,<4.0; sys_platform == 'darwin'",
|
|
13
16
|
"pandas",
|
|
14
17
|
"Pillow",
|
|
15
18
|
"pywavelets>=1.5.0,<2.0.0",
|
|
@@ -18,7 +21,7 @@ dependencies = [
|
|
|
18
21
|
"scipy",
|
|
19
22
|
"tqdm>=4.67.1,<5.0.0",
|
|
20
23
|
]
|
|
21
|
-
version = "0.8.
|
|
24
|
+
version = "0.8.4"
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
[project.optional-dependencies]
|
|
@@ -30,7 +33,6 @@ benchmarking = [
|
|
|
30
33
|
"ffmpeg-python",
|
|
31
34
|
]
|
|
32
35
|
matching = ["aiohttp", "python-json-logger"]
|
|
33
|
-
experimental = ["networkit>=11.1,<12.0.0", "faiss-cpu>=1.8.0,<2.0.0"]
|
|
34
36
|
pdq = ["pdqhash>=0.2.7,<0.3.0"]
|
|
35
37
|
|
|
36
38
|
|
|
@@ -38,7 +40,7 @@ pdq = ["pdqhash>=0.2.7,<0.3.0"]
|
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
[tool.poetry.group.dev.dependencies]
|
|
41
|
-
black = "^
|
|
43
|
+
black = "^26"
|
|
42
44
|
coverage = "*"
|
|
43
45
|
ipython = "*"
|
|
44
46
|
mypy = "*"
|
|
@@ -16,6 +16,7 @@ package_data = \
|
|
|
16
16
|
install_requires = \
|
|
17
17
|
['Cython>=3.0.0,<4.0.0',
|
|
18
18
|
'Pillow',
|
|
19
|
+
'faiss-cpu>=1.8.0,<2.0.0',
|
|
19
20
|
'numpy>=1.26.4,<3.0.0',
|
|
20
21
|
'opencv-contrib-python-headless>=4.10.0,<5.0.0',
|
|
21
22
|
'pandas',
|
|
@@ -26,18 +27,19 @@ install_requires = \
|
|
|
26
27
|
'validators>=0.22.0,<1.0.0']
|
|
27
28
|
|
|
28
29
|
extras_require = \
|
|
29
|
-
{'
|
|
30
|
+
{':sys_platform != "darwin"': ['networkit>=11.1,<12.0.0'],
|
|
31
|
+
':sys_platform == "darwin"': ['networkx>=3.0,<4.0'],
|
|
32
|
+
'benchmarking': ['matplotlib',
|
|
30
33
|
'albumentations>=2.0.8,<3.0.0',
|
|
31
34
|
'tabulate',
|
|
32
35
|
'scikit-learn',
|
|
33
36
|
'ffmpeg-python'],
|
|
34
|
-
'experimental': ['networkit>=11.1,<12.0.0', 'faiss-cpu>=1.8.0,<2.0.0'],
|
|
35
37
|
'matching': ['aiohttp', 'python-json-logger'],
|
|
36
38
|
'pdq': ['pdqhash>=0.2.7,<0.3.0']}
|
|
37
39
|
|
|
38
40
|
setup_kwargs = {
|
|
39
41
|
'name': 'Perception',
|
|
40
|
-
'version': '0.8.
|
|
42
|
+
'version': '0.8.4',
|
|
41
43
|
'description': 'Perception provides flexible, well-documented, and comprehensively tested tooling for perceptual hashing research, development, and production use.',
|
|
42
44
|
'long_description': "# perception \n\n`perception` provides flexible, well-documented, and comprehensively tested tooling for perceptual hashing research, development, and production use. See [the documentation](https://perception.thorn.engineering/en/latest/) for details.\n\n## Background\n\n`perception` was initially developed at [Thorn](https://www.thorn.org) as part of our work to eliminate child sexual abuse material from the internet. For more information on the issue, check out [our CEO's TED talk](https://www.thorn.org/blog/time-is-now-eliminate-csam/).\n\n## Getting Started\n\n### Installation\n\n`pip install perception`\n\n### Hashing\n\nHashing with different functions is simple with `perception`.\n\n```python\nfrom perception import hashers\n\nfile1, file2 = 'test1.jpg', 'test2.jpg'\nhasher = hashers.PHash()\nhash1, hash2 = hasher.compute(file1), hasher.compute(file2)\ndistance = hasher.compute_distance(hash1, hash2)\n```\n\n### Examples\n\nSee below for end-to-end examples for common use cases for perceptual hashes.\n\n- [Detecting child sexual abuse material](https://perception.thorn.engineering/en/latest/examples/detecting_csam.html)\n- [Deduplicating media](https://perception.thorn.engineering/en/latest/examples/deduplication.html)\n- [Benchmarking perceptual hashes](https://perception.thorn.engineering/en/latest/examples/benchmarking.html)\n\n## Supported Hashing Algorithms\n\n`perception` currently ships with:\n\n- pHash (DCT hash) (`perception.hashers.PHash`)\n- Facebook's PDQ Hash (`perception.hashers.PDQ`)\n- dHash (difference hash) (`perception.hashers.DHash`)\n- aHash (average hash) (`perception.hashers.AverageHash`)\n- Marr-Hildreth (`perception.hashers.MarrHildreth`)\n- Color Moment (`perception.hashers.ColorMoment`)\n- Block Mean (`perception.hashers.BlockMean`)\n- wHash (wavelet hash) (`perception.hashers.WaveletHash`)\n\n## Contributing\n\nTo work on the project, start by doing the following.\n\n```bash\n# Install local dependencies for\n# code completion, etc.\nmake init\n\n- To do a (close to) comprehensive check before committing code, you can use `make precommit`.\n\nTo implement new features, please first file an issue proposing your change for discussion.\n\nTo report problems, please file an issue with sample code, expected results, actual results, and a complete traceback.\n\n## Alternatives\n\nThere are other packages worth checking out to see if they meet your needs for perceptual hashing. Here are some\nexamples.\n\n- [dedupe](https://github.com/dedupeio/dedupe)\n- [imagededup](https://idealo.github.io/imagededup/)\n- [ImageHash](https://github.com/JohannesBuchner/imagehash)\n- [PhotoHash](https://github.com/bunchesofdonald/photohash)\n```\n",
|
|
43
45
|
'author': 'Thorn',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{perception-0.8.3 → perception-0.8.4}/perception/testing/videos/extra_channel_attached_pic.mp4
RENAMED
|
File without changes
|
{perception-0.8.3 → perception-0.8.4}/perception/testing/videos/extra_channel_attached_pic_audio.mp4
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|