@@ -1774,6 +1774,84 @@ def imagesfrombatch(self, start_index, num_frames, images=None, masks=None):
17741774 chosen_masks = masks [start_index :end_index ]
17751775
17761776 return (chosen_images , chosen_masks ,)
1777+
1778+ class ImageBatchExtendWithOverlap :
1779+
1780+ RETURN_TYPES = ("IMAGE" , "IMAGE" , "IMAGE" , )
1781+ RETURN_NAMES = ("source_images" , "start_images" , "extended_images" )
1782+ OUTPUT_TOOLTIPS = (
1783+ "The original source images (passthrough)" ,
1784+ "The input images used as the starting point for extension" ,
1785+ "The extended images with overlap, if no new images are provided this will be empty" ,
1786+ )
1787+ FUNCTION = "imagesfrombatch"
1788+ CATEGORY = "KJNodes/image"
1789+ DESCRIPTION = """
1790+ Helper node for video generation extension
1791+ First input source and overlap amount to get the starting frames for the extension.
1792+ Then on another copy of the node provide the newly generated frames and choose how to overlap them.
1793+ """
1794+
1795+ @classmethod
1796+ def INPUT_TYPES (s ):
1797+ return {
1798+ "required" : {
1799+ "source_images" : ("IMAGE" , {"tooltip" : "The source images to extend" }),
1800+ "overlap" : ("INT" , {"default" : 13 ,"min" : 1 , "max" : 4096 , "step" : 1 , "tooltip" : "Number of overlapping frames between source and new images" }),
1801+ "overlap_side" : (["source" , "new_images" ], {"default" : "source" , "tooltip" : "Which side to overlap on" }),
1802+ "overlap_mode" : (["cut" , "linear_blend" , "ease_in_out" ], {"default" : "linear_blend" , "tooltip" : "Method to use for overlapping frames" }),
1803+ },
1804+ "optional" : {
1805+ "new_images" : ("IMAGE" , {"tooltip" : "The new images to extend with" }),
1806+ }
1807+ }
1808+
1809+ def imagesfrombatch (self , source_images , overlap , overlap_side , overlap_mode , new_images = None ):
1810+ if overlap >= len (source_images ):
1811+ return source_images , source_images , source_images
1812+
1813+ if new_images is not None :
1814+ if source_images .shape [1 :3 ] != new_images .shape [1 :3 ]:
1815+ raise ValueError (f"Source and new images must have the same shape: { source_images .shape [1 :3 ]} vs { new_images .shape [1 :3 ]} " )
1816+ # Determine where to place the overlap
1817+ prefix = source_images [:- overlap ]
1818+ if overlap_side == "source" :
1819+ blend_src = source_images [- overlap :]
1820+ blend_dst = new_images [:overlap ]
1821+ elif overlap_side == "new_images" :
1822+ blend_src = new_images [:overlap ]
1823+ blend_dst = source_images [- overlap :]
1824+ suffix = new_images [overlap :]
1825+
1826+ if overlap_mode == "linear_blend" :
1827+ blended_images = [
1828+ crossfade (blend_src [i ], blend_dst [i ], (i + 1 ) / (overlap + 1 ))
1829+ for i in range (overlap )
1830+ ]
1831+ blended_images = torch .stack (blended_images , dim = 0 )
1832+ extended_images = torch .cat ((prefix , blended_images , suffix ), dim = 0 )
1833+ elif overlap_mode == "ease_in_out" :
1834+ blended_images = []
1835+ for i in range (overlap ):
1836+ t = (i + 1 ) / (overlap + 1 )
1837+ eased_t = ease_in_out (t )
1838+ blended_image = crossfade (blend_src [i ], blend_dst [i ], eased_t )
1839+ blended_images .append (blended_image )
1840+ blended_images = torch .stack (blended_images , dim = 0 )
1841+ extended_images = torch .cat ((prefix , blended_images , suffix ), dim = 0 )
1842+
1843+ elif overlap_mode == "cut" :
1844+ extended_images = torch .cat ((prefix , suffix ), dim = 0 )
1845+ if overlap_side == "new_images" :
1846+ extended_images = torch .cat ((source_images , new_images [overlap :]), dim = 0 )
1847+ elif overlap_side == "source" :
1848+ extended_images = torch .cat ((source_images [:- overlap ], new_images ), dim = 0 )
1849+ else :
1850+ extended_images = torch .zeros ((1 , 64 , 64 , 3 ), device = "cpu" )
1851+
1852+ start_images = source_images [- overlap :]
1853+
1854+ return (source_images , start_images , extended_images )
17771855
17781856class GetLatentRangeFromBatch :
17791857
0 commit comments