Skip to content

Commit 125888c

Browse files
committed
added latent tile sampler node
1 parent 8edbc7e commit 125888c

File tree

1 file changed

+113
-22
lines changed

1 file changed

+113
-22
lines changed

mikey_nodes.py

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2570,10 +2570,10 @@ def run(self, seed, base_model, vae, samples, positive_cond_base, negative_cond_
25702570
# common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise=1.0,
25712571
# disable_noise=False, start_step=None, last_step=None, force_full_denoise=False)
25722572
# step 1 run base model low cfg
2573-
sample1 = common_ksampler(base_model, seed, 30, 5, 'dpmpp_3m_sde_gpu', 'exponential', positive_cond_base, negative_cond_base, samples,
2573+
sample1 = common_ksampler(base_model, seed, 30, 4, 'dpmpp_2m_sde_gpu', 'karras', positive_cond_base, negative_cond_base, samples,
25742574
start_step=0, last_step=14, force_full_denoise=False)[0]
25752575
# step 2 run base model high cfg
2576-
sample2 = common_ksampler(base_model, seed+1, 31 + smooth_step, 9.5, 'dpmpp_3m_sde_gpu', 'exponential', positive_cond_base, negative_cond_base, sample1,
2576+
sample2 = common_ksampler(base_model, seed+1, 31 + smooth_step, 6, 'dpmpp_2m_sde_gpu', 'karras', positive_cond_base, negative_cond_base, sample1,
25772577
disable_noise=True, start_step=15, force_full_denoise=True)
25782578
if upscale_by == 0:
25792579
return sample2
@@ -2594,7 +2594,7 @@ def run(self, seed, base_model, vae, samples, positive_cond_base, negative_cond_
25942594
# encode image
25952595
latent = vaeencoder.encode(vae, img)[0]
25962596
# step 3 run base model
2597-
out = common_ksampler(base_model, seed, 31, 9.5, 'dpmpp_3m_sde_gpu', 'exponential', positive_cond_base, negative_cond_base, latent,
2597+
out = common_ksampler(base_model, seed, 31, 6, 'dpmpp_2m_sde_gpu', 'karras', positive_cond_base, negative_cond_base, latent,
25982598
start_step=start_step, force_full_denoise=True)
25992599
return out
26002600

@@ -3149,31 +3149,120 @@ def run(self, seed, base_model, vae, samples, positive_cond_base, negative_cond_
31493149
#final_image = pil2tensor(tiled_image)
31503150
return (tiled_image,)
31513151

3152-
"""
3153-
import cv2
3152+
def split_latent_tensor(latent_tensor, tile_size=1024, scale_factor=8):
3153+
"""Generate tiles for a given latent tensor, considering the scaling factor."""
3154+
latent_tile_size = tile_size // scale_factor # Adjust tile size for latent space
3155+
_, _, height, width = latent_tensor.shape
3156+
3157+
# Determine the number of tiles needed
3158+
num_tiles_x = ceil(width / latent_tile_size)
3159+
num_tiles_y = ceil(height / latent_tile_size)
3160+
3161+
# If width or height is an exact multiple of the tile size, add an additional tile for overlap
3162+
if width % latent_tile_size == 0:
3163+
num_tiles_x += 1
3164+
if height % latent_tile_size == 0:
3165+
num_tiles_y += 1
31543166

3155-
# Load a pre-trained face detection model
3156-
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
3167+
# Calculate the overlap
3168+
overlap_x = (num_tiles_x * latent_tile_size - width) / (num_tiles_x - 1)
3169+
overlap_y = (num_tiles_y * latent_tile_size - height) / (num_tiles_y - 1)
3170+
if overlap_x < 32:
3171+
num_tiles_x += 1
3172+
overlap_x = (num_tiles_x * latent_tile_size - width) / (num_tiles_x - 1)
3173+
if overlap_y < 32:
3174+
num_tiles_y += 1
3175+
overlap_y = (num_tiles_y * latent_tile_size - height) / (num_tiles_y - 1)
31573176

3158-
# Read the image where you want to detect faces
3159-
image_path = 'path_to_your_image.jpg' # Replace with your image path
3160-
image = cv2.imread(image_path)
3177+
tiles = []
31613178

3162-
# Convert the image to grayscale (needed for face detection)
3163-
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
3179+
for i in range(num_tiles_y):
3180+
for j in range(num_tiles_x):
3181+
x_start = j * latent_tile_size - j * overlap_x
3182+
y_start = i * latent_tile_size - i * overlap_y
31643183

3165-
# Detect faces in the image
3166-
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
3184+
# Correct for potential float precision issues
3185+
x_start = round(x_start)
3186+
y_start = round(y_start)
31673187

3168-
# Draw rectangles around each face
3169-
for (x, y, w, h) in faces:
3170-
cv2.rectangle(image, (x, y), (x+w, y+h), (255, 0, 0), 2)
3188+
# Crop the tile from the latent tensor
3189+
tile_tensor = latent_tensor[:, :, y_start:y_start + latent_tile_size, x_start:x_start + latent_tile_size]
3190+
tiles.append(((x_start, y_start, x_start + latent_tile_size, y_start + latent_tile_size), tile_tensor))
31713191

3172-
# Display the output
3173-
cv2.imshow('Face Detection', image)
3174-
cv2.waitKey(0)
3175-
cv2.destroyAllWindows()
3176-
"""
3192+
return tiles
3193+
3194+
def stitch_latent_tensors(original_size, tiles, scale_factor=8):
3195+
"""Stitch tiles together to create the final upscaled latent tensor with overlaps."""
3196+
result = torch.zeros(original_size)
3197+
3198+
# We assume tiles come in the format [(coordinates, tile), ...]
3199+
sorted_tiles = sorted(tiles, key=lambda x: (x[0][1], x[0][0])) # Sort by upper then left
3200+
3201+
# Variables to keep track of the current row's starting point
3202+
current_row_upper = None
3203+
3204+
for (left, upper, right, lower), tile in sorted_tiles:
3205+
3206+
# Check if we're starting a new row
3207+
if current_row_upper != upper:
3208+
current_row_upper = upper
3209+
first_tile_in_row = True
3210+
else:
3211+
first_tile_in_row = False
3212+
3213+
tile_width = right - left
3214+
tile_height = lower - upper
3215+
feather = tile_width // 8 # Assuming feather size is consistent with the example
3216+
3217+
mask = torch.ones(tile.shape[0], tile.shape[1], tile.shape[2], tile.shape[3])
3218+
3219+
if not first_tile_in_row: # Left feathering for tiles other than the first in the row
3220+
for t in range(feather):
3221+
mask[:, :, :, t:t+1] *= (1.0 / feather) * (t + 1)
3222+
3223+
if upper != 0: # Top feathering for all tiles except the first row
3224+
for t in range(feather):
3225+
mask[:, :, t:t+1, :] *= (1.0 / feather) * (t + 1)
3226+
3227+
# Apply the feathering mask
3228+
combined_area = tile * mask + result[:, :, upper:lower, left:right] * (1.0 - mask)
3229+
result[:, :, upper:lower, left:right] = combined_area
3230+
3231+
return result
3232+
3233+
class MikeyLatentTileSampler:
3234+
# receives a latent that is larger than the tile size and resamples it
3235+
@classmethod
3236+
def INPUT_TYPES(s):
3237+
return {"required": {"base_model": ("MODEL",), "samples": ("LATENT",),
3238+
"positive": ("CONDITIONING",), "negative": ("CONDITIONING",),
3239+
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
3240+
"denoise": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.05}),
3241+
"steps": ("INT", {"default": 30, "min": 1, "max": 1000}),
3242+
"cfg": ("FLOAT", {"default": 5, "min": 0.0, "max": 1000.0, "step": 0.1}),
3243+
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
3244+
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, ),
3245+
"tile_size": ("INT", {"default": 1024, "min": 256, "max": 4096, "step": 64})}}
3246+
3247+
RETURN_TYPES = ('LATENT',)
3248+
RETURN_NAMES = ('samples',)
3249+
FUNCTION = 'sample'
3250+
CATEGORY = 'Mikey/Sampling'
3251+
3252+
def sample(self, seed, base_model, samples, positive, negative,
3253+
denoise=0.25, steps=30, cfg=5, sampler_name='dpmpp_2m_sde_gpu', scheduler='karras',
3254+
tile_size=1024):
3255+
latent = samples.copy()
3256+
# split latent into tiles
3257+
latent_samples = latent["samples"]
3258+
tiles = split_latent_tensor(latent_samples, tile_size)
3259+
# resample each tile using ksampler
3260+
start_step = int(steps - (steps * denoise))
3261+
resampled_tiles = [(coords, common_ksampler(base_model, seed, steps, cfg, sampler_name, scheduler,
3262+
positive, negative, {"samples": tile}, start_step=start_step)[0]["samples"]) for coords, tile in tiles]
3263+
# stitch the tiles to get the final upscaled latent tensor
3264+
latent["samples"] = stitch_latent_tensors(latent_samples.shape, resampled_tiles)
3265+
return (latent,)
31773266

31783267
class FaceFixerOpenCV:
31793268
@classmethod
@@ -4983,6 +5072,7 @@ def get_subdirectories(self, directory):
49835072
'Mikey Sampler Base Only Advanced': MikeySamplerBaseOnlyAdvanced,
49845073
'Mikey Sampler Tiled': MikeySamplerTiled,
49855074
'Mikey Sampler Tiled Base Only': MikeySamplerTiledBaseOnly,
5075+
'MikeyLatentTileSampler': MikeyLatentTileSampler,
49865076
'FaceFixerOpenCV': FaceFixerOpenCV,
49875077
'AddMetaData': AddMetaData,
49885078
'SaveMetaData': SaveMetaData,
@@ -5050,6 +5140,7 @@ def get_subdirectories(self, directory):
50505140
'MikeySamplerTiledAdvanced': 'Mikey Sampler Tiled Advanced',
50515141
'MikeySamplerTiledAdvancedBaseOnly': 'Mikey Sampler Tiled Advanced Base Only',
50525142
'Mikey Sampler Tiled Base Only': 'Mikey Sampler Tiled Base Only',
5143+
'MikeyLatentTileSampler': 'Latent Tile Sampler (Mikey)',
50535144
'FaceFixerOpenCV': 'Face Fixer OpenCV (Mikey)',
50545145
'AddMetaData': 'AddMetaData (Mikey)',
50555146
'SaveMetaData': 'SaveMetaData (Mikey)',

0 commit comments

Comments
 (0)