I’ve got this code together for the feathering, but it seems to have issues:
import torch
from PIL import Image
import torchvision.transforms as transforms

# Feather vertically          
def feather_tiles(tensor_list, rows, w_overlap):
    print(len(tensor_list))
    new_tensor_list = []
    if w_overlap > 0:
        for i, tile in enumerate(tensor_list):
            if i % rows[1] != 0:
                lin_mask = torch.linspace(0,1,w_overlap).repeat(tile.size(2),1)
                mask_part = torch.ones(tile.size(2), tile.size(3)-w_overlap)
                mask = torch.cat([lin_mask, mask_part], 1)
                mask = mask.repeat(3,1,1).unsqueeze(0)
                new_tensor_list.append(tile * mask)
            else:
                new_tensor_list.append(tile)
    else:
        new_tensor_list = tensor_list
    return new_tensor_list

# Horizontal feathering
def feather_rows(tensor_list, rows, h_overlap):
    if h_overlap > 0:
        for i, tile in enumerate(tensor_list):
            if i > rows[1]:
                for v in range(3):
                    lin_mask = torch.linspace(0,1,h_overlap).repeat(tile.size(3),1).rot90().rot90().rot90()
                    mask_part = torch.ones(tile.size(2)-h_overlap, tile.size(3))
                    mask = torch.cat((lin_mask, mask_part))	
                    tensor_list[i][0][v] = tensor_list[i][0][v] * mask					
    return tensor_list	

def split_tensor(tensor, tile_size=256):
    tiles = []
    tile_idx = []
    tile_size_y, tile_size_x = tile_size+8, tile_size +5
    h, w = tensor.size(2), tensor.size(3)
    h_range, w_range = int(-(h // -tile_size_y)), int(-(w // -tile_size_x))
	
    for y in range(h_range):       
        for x in range(w_range): 		
            y_val = max(min(tile_size_y*y+tile_size_y, h), 0)
            x_val = max(min(tile_size_x*x+tile_size_x, w), 0)
            ty = tile_size_y*y
            tx = tile_size_x*x

            if abs(tx - x_val) < tile_size_x:
                tx = x_val- tile_size_x
            if abs(ty - y_val) < tile_size_y:
                ty = y_val-tile_size_y


            tiles.append(tensor[:, :, ty:y_val, tx:x_val])
            tile_idx.append([ty, y_val, tx, x_val])
	
    w_overlap = tile_idx[0][3] - tile_idx[1][2]
    h_overlap = tile_idx[0][1] - tile_idx[w_range][0]

    if tensor.is_cuda:
        base_tensor = torch.zeros(tensor.squeeze(0).size(), device=tensor.get_device())
    else: 
        base_tensor = torch.zeros(tensor.squeeze(0).size())
    return tiles, base_tensor.unsqueeze(0), (h_range, w_range), (h_overlap, w_overlap)  

def build_row(tensor_tiles, hxw, w_overlap, bt, tile_size):
    if bt.is_cuda:
        row_base = torch.zeros(bt.size(1),tensor_tiles[0].size(2),bt.size(3), device=bt.get_device()).unsqueeze(0)
    else: 
        row_base = torch.zeros(bt.size(1),tensor_tiles[0].size(2),bt.size(3)).unsqueeze(0)
    row_list = []
    row_list_2 = []
    for v in range(hxw[1]):
      row_list.append(row_base) 
      row_list_2.append(row_base)     
        
    num_tiles = 0
    tile_size_y, tile_size_x = tile_size+8, tile_size +5
    h, w = bt.size(2), bt.size(3)
    h_range, w_range = hxw[0], hxw[1]
    for y in range(h_range):       
        for x in range(w_range):        
            y_val = max(min(tile_size_y*y+tile_size_y, h), 0)
            x_val = max(min(tile_size_x*x+tile_size_x, w), 0)
            ty = tile_size_y*y
            tx = tile_size_x*x

            if abs(tx - x_val) < tile_size_x:
                tx = x_val- tile_size_x
            if abs(ty - y_val) < tile_size_y:
                ty = y_val-tile_size_y
                
            print(row_list[y].size(), tensor_tiles[num_tiles].size())
            if x == 0:
                row_list[y][:, :, :, tx:x_val] = tensor_tiles[num_tiles]
            else:
                row_list_2[y][:, :, :, tx:x_val] = tensor_tiles[num_tiles]
                row_list[y] = row_list[y] + row_list_2[y] / row_list_2[y]               
            num_tiles+=1        
    return row_list

# Test Functions
test_image = 'test_image.jpg'
image_size=(256,202)
Loader = transforms.Compose([transforms.Resize(image_size), transforms.ToTensor()])
input_tensor = Loader(Image.open(test_image).convert('RGB')).unsqueeze(0)

tile_tensors, base_t, hxw, ovlp = split_tensor(input_tensor, 128)
feathered_tiles = feather_tiles(tile_tensors, hxw, ovlp[1])
row_tensors = build_row(feathered_tiles, hxw, ovlp[1], base_t, 128)
feathered_row_tensors = feather_rows(row_tensors , hxw, ovlp[0])


Any help with getting these two functions working properly, would be really appreciated!
This is what the first row looks like after the tiles are run through build_row() function: