Skip to content

Commit 21fae97

Browse files
committed
fix(reference): moved reference image to IGlobal
1 parent 1b70aca commit 21fae97

4 files changed

Lines changed: 23 additions & 47 deletions

File tree

nodes/src/nodes/visual_similarity_filter/IGlobal.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class IGlobal(IGlobalBase):
3737

3838
embedder = None
3939
config = None
40+
reference_embedding = None
4041

4142
def beginGlobal(self):
4243
self.device_lock = threading.Lock()
@@ -47,8 +48,10 @@ def beginGlobal(self):
4748
return
4849

4950
from .embedder import FrameEmbedder
51+
5052
self.embedder = FrameEmbedder(self.config)
5153

5254
def endGlobal(self):
5355
self.embedder = None
56+
self.reference_embedding = None
5457
self.device_lock = None

nodes/src/nodes/visual_similarity_filter/IInstance.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def beginInstance(self):
3838
self._match_rows = []
3939
self._image_buf = bytearray()
4040
self._image_mime = 'image/png'
41-
self._reference_embedding = None
4241

4342
cfg = self.IGlobal.config
4443
self._fps = float(cfg.get('fps', 1.0))
@@ -52,7 +51,6 @@ def open(self, obj: Entry):
5251
self._total = 0
5352
self._forwarded = 0
5453
self._match_rows = []
55-
self._reference_embedding = None
5654

5755
def close(self):
5856
if self.instance.hasListener('table') and self._match_rows:
@@ -92,10 +90,11 @@ def _on_frame_complete(self, image_bytes: bytes, mime: str):
9290
self._total += 1
9391
timestamp = idx / self._fps if self._fps > 0 else float(idx)
9492

95-
# First frame: capture as reference and forward unconditionally.
96-
if self._reference_embedding is None:
93+
# First frame ever: capture as reference and forward unconditionally.
94+
if self.IGlobal.reference_embedding is None:
9795
with self.IGlobal.device_lock:
98-
self._reference_embedding = self.IGlobal.embedder.embed(image_bytes)
96+
if self.IGlobal.reference_embedding is None: # double-checked under lock
97+
self.IGlobal.reference_embedding = self.IGlobal.embedder.embed(image_bytes)
9998
self._matched += 1
10099
self._match_rows.append([idx, self._fmt_time(timestamp), '1.000'])
101100
self._forward_frame(image_bytes, mime)
@@ -104,9 +103,10 @@ def _on_frame_complete(self, image_bytes: bytes, mime: str):
104103
# Subsequent frames: score against the reference.
105104
try:
106105
import numpy as np
106+
107107
with self.IGlobal.device_lock:
108108
frame_emb = self.IGlobal.embedder.embed(image_bytes)
109-
similarity = float(np.dot(self._reference_embedding, frame_emb))
109+
similarity = float(np.dot(self.IGlobal.reference_embedding, frame_emb))
110110
except Exception:
111111
similarity = 0.0
112112

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
transformers
2+
accelerate

nodes/src/nodes/visual_similarity_filter/services.json

Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,16 @@
11
{
22
"title": "Visual Similarity Filter",
33
"protocol": "visual_similarity_filter://",
4-
"classType": [
5-
"video"
6-
],
4+
"classType": ["image"],
75
"capabilities": [],
86
"register": "filter",
97
"node": "python",
108
"path": "nodes.visual_similarity_filter",
119
"prefix": "visual_similarity_filter",
12-
"description": [
13-
"Filters video frames by visual similarity using CLIP embeddings. ",
14-
"The first frame received becomes the reference; every subsequent frame ",
15-
"is scored against it and forwarded only if its similarity meets the threshold. ",
16-
"Includes pre-roll/post-roll context around matching segments."
17-
],
18-
"icon": "visual-similarity.svg",
10+
"description": ["Filters video frames by visual similarity using CLIP embeddings. ", "The first frame received becomes the reference; every subsequent frame ", "is scored against it and forwarded only if its similarity meets the threshold. ", "Includes pre-roll/post-roll context around matching segments."],
11+
"icon": "embedding-image.svg",
1912
"lanes": {
20-
"image": [
21-
"image", "table"
22-
]
13+
"image": ["image", "table"]
2314
},
2415
"input": [
2516
{
@@ -84,52 +75,34 @@
8475
},
8576
"vsf.clip-base": {
8677
"object": "clip-base",
87-
"properties": [
88-
"vsf.similarity_threshold",
89-
"vsf.fps"
90-
]
78+
"properties": ["vsf.similarity_threshold", "vsf.fps"]
9179
},
9280
"vsf.clip-large": {
9381
"object": "clip-large",
94-
"properties": [
95-
"vsf.similarity_threshold",
96-
"vsf.fps"
97-
]
82+
"properties": ["vsf.similarity_threshold", "vsf.fps"]
9883
},
9984
"vsf.custom": {
10085
"object": "custom",
101-
"properties": [
102-
"vsf.embedding_model",
103-
"vsf.similarity_threshold",
104-
"vsf.fps"
105-
]
86+
"properties": ["vsf.embedding_model", "vsf.similarity_threshold", "vsf.fps"]
10687
},
10788
"vsf.profile": {
10889
"title": "Embedding model",
10990
"description": "CLIP model and configuration",
11091
"type": "string",
11192
"default": "clip-base",
112-
"enum": [
113-
"*>preconfig.profiles.*.title"
114-
],
93+
"enum": ["*>preconfig.profiles.*.title"],
11594
"conditional": [
11695
{
11796
"value": "clip-base",
118-
"properties": [
119-
"vsf.clip-base"
120-
]
97+
"properties": ["vsf.clip-base"]
12198
},
12299
{
123100
"value": "clip-large",
124-
"properties": [
125-
"vsf.clip-large"
126-
]
101+
"properties": ["vsf.clip-large"]
127102
},
128103
{
129104
"value": "custom",
130-
"properties": [
131-
"vsf.custom"
132-
]
105+
"properties": ["vsf.custom"]
133106
}
134107
]
135108
}
@@ -138,9 +111,7 @@
138111
{
139112
"section": "Pipe",
140113
"title": "Visual Similarity Filter",
141-
"properties": [
142-
"vsf.profile"
143-
]
114+
"properties": ["vsf.profile"]
144115
}
145116
]
146117
}

0 commit comments

Comments
 (0)