Skip to content

Commit

Permalink
a through q
Browse files Browse the repository at this point in the history
  • Loading branch information
Amorano committed May 29, 2024
1 parent 4c0c1b3 commit 85fc906
Show file tree
Hide file tree
Showing 15 changed files with 168 additions and 138 deletions.
2 changes: 0 additions & 2 deletions core/adjust.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,6 @@ def run(self, **kw) -> Tuple[Any, ...]:
else:
h = (start + fuzz * 128).clamp(max=255).view(1, 1, 1, 3)
img = torch.zeros((MIN_IMAGE_SIZE, MIN_IMAGE_SIZE, 3), dtype=torch.uint8, device="cpu") if pA is None else pA
print(len(img.shape))
if img.shape[2] == 4:
img = img[:, :, :3]
mask = (torch.clamp(img, 0, 1.0) * 255.0).round().to(torch.int)
Expand All @@ -415,6 +414,5 @@ def run(self, **kw) -> Tuple[Any, ...]:
img = image_mask_add(img, tensor2cv(mask))
matte = image_matte(img, matte)[:,:,:3]
images.append([cv2tensor(img), cv2tensor(matte), mask])
print(img.shape, matte.shape, mask.shape)
pbar.update_absolute(idx)
return [torch.stack(i, dim=0).squeeze(1) for i in list(zip(*images))]
2 changes: 1 addition & 1 deletion core/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def run(self, **kw) -> Tuple[torch.Tensor, torch.Tensor]:
logger.error(str(e))
waves.append(data)
pbar.update_absolute(idx)
return waves
return (waves,)

class WaveGraphNode(JOVBaseNode):
NAME = "WAVE GRAPH (JOV) ▶ ılıılı"
Expand Down
6 changes: 3 additions & 3 deletions core/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def run(self, **kw) -> Tuple[bool]:
case EnumBinaryOperation.MODULUS:
val = [a % b if b != 0 else 0 for a, b in zip(val_a, val_b)]
case EnumBinaryOperation.POWER:
val = [a ** b for a, b in zip(val_a, val_b)]
val = [a ** b if b >= 0 else 0 for a, b in zip(val_a, val_b)]
case EnumBinaryOperation.MAXIMUM:
val = max(val_a, val_b)
case EnumBinaryOperation.MINIMUM:
Expand All @@ -361,9 +361,9 @@ def run(self, **kw) -> Tuple[bool]:
case EnumBinaryOperation.BIT_XNOR:
val = [not(int(a) ^ int(b)) for a, b in zip(val_a, val_b)]
case EnumBinaryOperation.BIT_LSHIFT:
val = [int(a) << int(b) for a, b in zip(val_a, val_b)]
val = [int(a) << int(b) if b >= 0 else 0 for a, b in zip(val_a, val_b)]
case EnumBinaryOperation.BIT_RSHIFT:
val = [int(a) >> int(b) for a, b in zip(val_a, val_b)]
val = [int(a) >> int(b) if b >= 0 else 0 for a, b in zip(val_a, val_b)]

# GROUP
case EnumBinaryOperation.UNION:
Expand Down
33 changes: 19 additions & 14 deletions core/compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ def INPUT_TYPES(cls) -> dict:
Lexicon.G: (WILDCARD, {}),
Lexicon.B: (WILDCARD, {}),
Lexicon.A: (WILDCARD, {}),
Lexicon.MODE: (EnumScaleMode._member_names_, {"default": EnumScaleMode.NONE.name}),
Lexicon.WH: ("VEC2", {"default": (MIN_IMAGE_SIZE, MIN_IMAGE_SIZE), "step": 1, "label": [Lexicon.W, Lexicon.H]}),
Lexicon.SAMPLE: (EnumInterpolation._member_names_, {"default": EnumInterpolation.LANCZOS4.name}),
Lexicon.MATTE: ("VEC4", {"default": (0, 0, 0, 255), "step": 1, "label": [Lexicon.R, Lexicon.G, Lexicon.B, Lexicon.A], "rgb": True})
}}
return Lexicon._parse(d, cls)
Expand All @@ -277,23 +280,27 @@ def run(self, **kw) -> Tuple[torch.Tensor, torch.Tensor]:
G = parse_param(kw, Lexicon.G, EnumConvertType.IMAGE, None)
B = parse_param(kw, Lexicon.B, EnumConvertType.IMAGE, None)
A = parse_param(kw, Lexicon.A, EnumConvertType.IMAGE, None)
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255)
mode = parse_param(kw, Lexicon.MODE, EnumConvertType.STRING, EnumScaleMode.NONE.name)
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (MIN_IMAGE_SIZE, MIN_IMAGE_SIZE), MIN_IMAGE_SIZE)
sample = parse_param(kw, Lexicon.SAMPLE, EnumConvertType.STRING, EnumInterpolation.LANCZOS4.name)
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC3INT, (0, 0, 0), 0, 255)
if len(R)+len(B)+len(G)+len(A) == 0:
img = channel_solid(MIN_IMAGE_SIZE, MIN_IMAGE_SIZE, 0, EnumImageType.BGRA)
return list(cv2tensor_full(img, matte))
params = list(zip_longest_fill(R, G, B, A, matte))
params = list(zip_longest_fill(R, G, B, A, mode, wihi, sample, matte))
images = []
pbar = ProgressBar(len(params))
for idx, (r, g, b, a, matte) in enumerate(params):
r = tensor2cv(r) if r is not None else channel_solid()
r = image_grayscale(r)
g = tensor2cv(g) if g is not None else channel_solid()
g = image_grayscale(g)
b = tensor2cv(b) if b is not None else channel_solid()
b = image_grayscale(b)
mask = tensor2cv(a) if a is not None else channel_solid()
mask = image_grayscale(mask)
img = channel_merge([b, g, r, mask])
for idx, (r, g, b, a, mode, wihi, sample, matte) in enumerate(params):
w, h = wihi
ret = [channel_solid(w, h, chan=EnumImageType.GRAYSCALE) if x is None else image_grayscale(tensor2cv(x)) for x in (r, g, b, a)]
h, w = ret[0].shape[:2]
ret = [cv2.resize(r, (w, h)) for r in ret]
img = channel_merge(ret)
mode = EnumScaleMode[mode]
if mode != EnumScaleMode.NONE:
w, h = wihi
sample = EnumInterpolation[sample]
img = image_scalefit(img, w, h, mode, sample)
images.append(cv2tensor_full(img, matte))
pbar.update_absolute(idx)
return [torch.stack(i, dim=0).squeeze(1) for i in list(zip(*images))]
Expand Down Expand Up @@ -542,9 +549,7 @@ def INPUT_TYPES(cls) -> dict:

def run(self, **kw) -> torch.Tensor:
pA = parse_dynamic(kw, Lexicon.PIXEL, EnumConvertType.IMAGE, None)
print(len(pA))
pA = [item for sublist in pA for item in sublist]
print(len(pA))
if len(pA) == 0:
logger.error("no images to flatten")
return ()
Expand Down
185 changes: 102 additions & 83 deletions core/device_midi.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from Jovimetrix import JOVBaseNode
from Jovimetrix.sup.lexicon import Lexicon
from Jovimetrix.sup.util import EnumConvertType, parse_param
from Jovimetrix.sup.util import EnumConvertType, parse_param, zip_longest_fill
from Jovimetrix.sup.midi import midi_device_names, \
MIDIMessage, MIDINoteOnFilter, MIDIServerThread

Expand Down Expand Up @@ -48,15 +48,14 @@ def INPUT_TYPES(cls) -> dict:
return Lexicon._parse(d, cls)

def run(self, **kw) -> Tuple[object, bool, int, int, int, float, float]:
message = parse_param(kw, Lexicon.MIDI, EnumConvertType.ANY, None)
message: MIDIMessage = parse_param(kw, Lexicon.MIDI, EnumConvertType.ANY, None)
results = []
pbar = ProgressBar(len(message))
for idx, (message,) in enumerate(message):
data = [message]
if message is None:
data.extend([False, -1, -1, -1, -1, -1])
data = [False, -1, -1, -1, -1, -1]
else:
data.extend(*message.flat)
data = [message]
results.append(data)
pbar.update_absolute(idx)
return (results,)
Expand Down Expand Up @@ -152,31 +151,44 @@ def INPUT_TYPES(cls) -> dict:
return Lexicon._parse(d, cls)

def run(self, **kw) -> Tuple[MIDIMessage, bool]:
message = parse_param(kw, Lexicon.MIDI, EnumConvertType.ANY, None)[0]
if message is None:
logger.warning('no midi message. connected?')
return (None, False, )

# empty values mean pass-thru (no filter)
val = parse_param(kw, Lexicon.MODE, EnumConvertType.STRING, MIDINoteOnFilter.IGNORE.name)[0]
val = MIDINoteOnFilter[val]
if val != MIDINoteOnFilter.IGNORE:
if val == MIDINoteOnFilter.NOTE_ON and message.note_on != True:
return (message, False, )
if val == MIDINoteOnFilter.NOTE_OFF and message.note_on != False:
return (message, False, )

if (val := parse_param(kw, Lexicon.CHANNEL, EnumConvertType.INT, -1)[0]) != -1 and val != message.channel:
return (message, False, )
if (val := parse_param(kw, Lexicon.CONTROL, EnumConvertType.INT, -1)[0]) != -1 and val != message.control:
return (message, False, )
if (val := parse_param(kw, Lexicon.NOTE, EnumConvertType.INT, -1)[0]) != -1 and val != message.note:
return (message, False, )
if (val := parse_param(kw, Lexicon.VALUE, EnumConvertType.INT, -1)[0]) != -1 and val != message.value:
return (message, False, )
if (val := parse_param(kw, Lexicon.NORMALIZE, EnumConvertType.INT, -1)[0]) != -1 and isclose(message.normal):
return (message, False, )
return (message, True, )
message: MIDIMessage = parse_param(kw, Lexicon.MIDI, EnumConvertType.ANY, None)
mode = parse_param(kw, Lexicon.MODE, EnumConvertType.STRING, MIDINoteOnFilter.IGNORE.name)
chan = parse_param(kw, Lexicon.CHANNEL, EnumConvertType.INT, -1)
ctrl = parse_param(kw, Lexicon.CONTROL, EnumConvertType.INT, -1)
note = parse_param(kw, Lexicon.NOTE, EnumConvertType.INT, -1)
value = parse_param(kw, Lexicon.VALUE, EnumConvertType.INT, -1)
normal = parse_param(kw, Lexicon.NORMALIZE, EnumConvertType.FLOAT, -1)
params = list(zip_longest_fill(message, mode, chan, ctrl, note, value, normal))
ret = []
pbar = ProgressBar(len(params))
for idx, (message, mode, chan, ctrl, note, value, normal) in enumerate(params):
mode = MIDINoteOnFilter[mode]
if mode != MIDINoteOnFilter.IGNORE:
if mode == MIDINoteOnFilter.NOTE_ON and message.note_on == False:
ret.append((message, False, ))
continue
elif mode == MIDINoteOnFilter.NOTE_OFF and message.note_on == False:
ret.append((message, False, ))
continue
if chan != -1 and chan != message.channel:
ret.append((message, False, ))
continue
if ctrl != -1 and ctrl != message.control:
ret.append((message, False, ))
continue
if note != -1 and note != message.note:
ret.append((message, False, ))
continue
if value != -1 and value != message.value:
ret.append((message, False, ))
continue
if normal > 0 and not isclose(message.normal):
ret.append((message, False, ))
continue
ret.append((message, True, ))
pbar.update_absolute(idx)
return list(zip(*ret))

class MIDIFilterNode(JOVBaseNode):
NAME = "MIDI FILTER (JOV) ✳️"
Expand Down Expand Up @@ -205,65 +217,72 @@ def INPUT_TYPES(cls) -> dict:
}
return Lexicon._parse(d, cls)

def __filter(self, data: str, value: float) -> bool:
if not data:
return True
"""
parse string blocks of "numbers" into range(s) to compare. e.g.:
1
5-10
2
def __filter(self, data:int, value:str) -> bool:
"""Parse strings with number ranges into number ranges.
1, 5-10, 2
Would check == 1, == 2 and 5 <= x <= 10
"""
# can you use float for everything to compare?

try:
value = float(value)
except Exception as e:
value = float("nan")
logger.error(str(e))

for line in data.split(','):
if len(a_range := line.split('-')) > 1:
value = value.strip()
if value == "" or len(value) == 0 or value is None:
return True
ranges = value.split(',')
for item in ranges:
item = item.strip()
if '-' in item:
try:
a, b = a_range[:2]
if float(a) <= value <= float(b):
a, b = map(float, item.split('-'))
if a <= data <= b:
return True
except ValueError:
pass
except Exception as e:
logger.error(str(e))

try:
if isclose(value, float(line)):
return True
except Exception as e:
logger.error(str(e))
logger.error(e)
else:
try:
if isclose(data, float(item)):
return True
except ValueError:
pass
except Exception as e:
logger.error(e)
return False

def run(self, **kw) -> Tuple[bool]:
message = parse_param(kw, Lexicon.MIDI, EnumConvertType.ANY, None)[0]
if message is None:
logger.warning('no midi message. connected?')
return (message, False, )

# empty values mean pass-thru (no filter)
val = parse_param(kw, Lexicon.ON, EnumConvertType.STRING, MIDINoteOnFilter.IGNORE.name)[0]
val = MIDINoteOnFilter[val]
if val != MIDINoteOnFilter.IGNORE:
if val == "TRUE" and message.note_on != True:
return (message, False, )
if val == "FALSE" and message.note_on != False:
return (message, False, )

if self.__filter(message.channel, parse_param(kw, Lexicon.CHANNEL, EnumConvertType.BOOLEAN, False)[0]) == False:
return (message, False, )
if self.__filter(message.control, parse_param(kw, Lexicon.CONTROL, EnumConvertType.BOOLEAN, False)[0]) == False:
return (message, False, )
if self.__filter(message.note, parse_param(kw, Lexicon.NOTE, EnumConvertType.BOOLEAN, False)[0]) == False:
return (message, False, )
if self.__filter(message.value, parse_param(kw, Lexicon.VALUE, EnumConvertType.BOOLEAN, False)[0]) == False:
return (message, False, )
if self.__filter(message.normal, parse_param(kw, Lexicon.NORMALIZE, EnumConvertType.BOOLEAN, False)[0]) == False:
return (message, False, )
return (message, True, )
message: MIDIMessage = parse_param(kw, Lexicon.MIDI, EnumConvertType.ANY, None)
note_on = parse_param(kw, Lexicon.ON, EnumConvertType.STRING, MIDINoteOnFilter.IGNORE.name)
chan = parse_param(kw, Lexicon.CHANNEL, EnumConvertType.STRING, "")
ctrl = parse_param(kw, Lexicon.CONTROL, EnumConvertType.STRING, "")
note = parse_param(kw, Lexicon.NOTE, EnumConvertType.STRING, "")
value = parse_param(kw, Lexicon.VALUE, EnumConvertType.STRING, "")
normal = parse_param(kw, Lexicon.NORMALIZE, EnumConvertType.STRING, "")
params = list(zip_longest_fill(message, note_on, chan, ctrl, note, value, normal))
ret = []
pbar = ProgressBar(len(params))
for idx, (message, note_on, chan, ctrl, note, value, normal) in enumerate(params):
message = message[0]
note_on = MIDINoteOnFilter[note_on]
if note_on != MIDINoteOnFilter.IGNORE:
if note_on == "TRUE" and message.note_on != True:
ret.append((message, False, ))
continue
if note_on == "FALSE" and message.note_on != False:
ret.append((message, False, ))
continue
if self.__filter(message.channel, chan) == False:
ret.append((message, False, ))
continue
if self.__filter(message.control, ctrl) == False:
ret.append((message, False, ))
continue
if self.__filter(message.note, note) == False:
ret.append((message, False, ))
continue
if self.__filter(message.value, value) == False:
ret.append((message, False, ))
continue
if self.__filter(message.normal, normal) == False:
ret.append((message, False, ))
continue
ret.append((message, True, ))
pbar.update_absolute(idx)
return list(zip(*ret))
22 changes: 6 additions & 16 deletions core/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,12 @@ def __parse(val) -> str:
if not isinstance(val, (list, tuple, set,)):
val = [val]
for img in val:
#img = tensor2pil(img)
ret.append(f"({'x'.join([str(x) for x in img.shape])}) [{typ}]")
#ret += str(img.size)
#buffered = io.BytesIO()
#img.save(buffered, format="PNG")
#img = base64.b64encode(buffered.getvalue())
#img = "data:image/png;base64," + img.decode("utf-8")
#output["ui"]["b64_images"].append(img)
return ', '.join(ret)
return f"unknown [{typ}]"
return f"{str(val)} [{typ}]"

for x in o:
output["ui"]["text"].append(__parse(x))
#ak = AkashicData(image=output["ui"]["b64_images"], text=output["ui"]["text"] )
#output["result"] = (o, ak)
return output

class ValueGraphNode(JOVBaseNode):
Expand Down Expand Up @@ -442,8 +433,8 @@ def output(extension) -> Path:
class ImageDiffNode(JOVBaseNode):
NAME = "IMAGE DIFF (JOV) 📏"
CATEGORY = f"JOVIMETRIX 🔺🟩🔵/{JOV_CATEGORY}"
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK", "MASK", ) #"FLOAT", )
RETURN_NAMES = (Lexicon.IN_A, Lexicon.IN_B, Lexicon.DIFF, Lexicon.THRESHOLD) #, Lexicon.FLOAT, )
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK", "MASK", )
RETURN_NAMES = (Lexicon.IN_A, Lexicon.IN_B, Lexicon.DIFF, Lexicon.THRESHOLD)
SORT = 90
DESCRIPTION = """
The Image Diff node compares two input images pixel by pixel to identify differences between them. It takes two images as input, labeled as Image A and Image B. The node then calculates the absolute difference between the two images, producing two additional outputs: a difference mask and a threshold mask. The threshold parameter determines the sensitivity of the comparison, with higher values indicating more tolerance for differences. The node returns Image A, Image B, the difference mask, and the threshold mask.
Expand All @@ -470,7 +461,7 @@ def run(self, **kw) -> Tuple[Any, Any]:
for idx, (pA, pB, th) in enumerate(params):
pA = channel_solid(chan=EnumImageType.BGRA) if pA is None else tensor2cv(pA)
pB = channel_solid(chan=EnumImageType.BGRA) if pB is None else tensor2cv(pB)
a, b, d, t, s = image_diff(pA, pB, int(th * 255))
a, b, d, t, _ = image_diff(pA, pB, int(th * 255))
d = image_convert(d, 1)
t = image_convert(t, 1)
results.append([cv2tensor(a), cv2tensor(b), cv2tensor(d), cv2tensor(t)])
Expand Down Expand Up @@ -613,11 +604,12 @@ def run(self, **kw) -> Tuple[int, list]:
class RouteNode(JOVBaseNode):
NAME = "ROUTE (JOV) 🚌"
CATEGORY = f"JOVIMETRIX 🔺🟩🔵/{JOV_CATEGORY}"
RETURN_TYPES = (WILDCARD,)
RETURN_TYPES = ()
SORT = 900
DESCRIPTION = """
Routes the input data from the optional input ports to the output port, preserving the order of inputs. The `PASS_IN` optional input is directly passed through to the output, while other optional inputs are collected and returned as tuples, preserving the order of insertion.
"""
CATEGORY = "JOVIMETRIX 🔺🟩🔵/WIP ☣️💣"

@classmethod
def INPUT_TYPES(cls) -> dict:
Expand All @@ -627,8 +619,6 @@ def INPUT_TYPES(cls) -> dict:
return Lexicon._parse(d, cls)

def run(self, **kw) -> Tuple[Any, ...]:
#passthru = parse_param(kw, Lexicon.PASS_IN, EnumConvertType.ANY, None)
#kw.pop(Lexicon.PASS_IN, None)
return zip(*kw.values())

class SaveOutput(JOVBaseNode):
Expand Down
Loading

0 comments on commit 85fc906

Please sign in to comment.