glucauze / sd-webui-faceswaplab

Extended faceswap extension for StableDiffusion web-ui with multiple faceswaps, inpainting, checkpoints, ....
https://glucauze.github.io/sd-webui-faceswaplab/
GNU Affero General Public License v3.0
750 stars 101 forks source link

Inpaint with only masked downscale the original image #12

Closed 2blackbar closed 1 year ago

2blackbar commented 1 year ago

When i go to inpaint and choose only masked instead of whole picture, it should preserve resolution of original imave even if its 4k image, and it does when this extension is not in use, but when i enable this extension, it will downscale all images to pixelater versions , its pretty weird and its a serious pipeline bug that degrades all images i run through it . You can test it with images with text, choose onlyu masked and face swap with this extension, resulting image will have pixelated text which is clearly much lower resolution. So default behaviour should be - when i dont mask anything and i use hires picture, faceswaplab should be able to isolate the face from that image , then inpaint just the face, then paste face back into the image without degrading resolution, it does work like this when we mask only the face but it would be much better if faceswaplab did it on its own since it can recognize the face .

glucauze commented 1 year ago

Could put a link to a photo that poses a problem? I'll have to check for myself.

The function that adds the mask to the last part is as follows (in imgutils):

def apply_mask(
    img: PILImage, p: processing.StableDiffusionProcessing, batch_index: int
) -> PILImage:
    """
    Apply mask overlay and color correction to an image if enabled

    Args:
        img: PIL Image objects.
        p : The processing object
        batch_index : the batch index

    Returns:
        PIL Image object
    """
    if isinstance(p, processing.StableDiffusionProcessingImg2Img):
        if p.inpaint_full_res:
            overlays = p.overlay_images
            if overlays is None or batch_index >= len(overlays):
                return img
            overlay: PILImage = overlays[batch_index]

# This is probably the thing that's causing the problem but this is necessary to merge the two images :
            overlay = overlay.resize((img.size), resample=Image.Resampling.LANCZOS)
            img = img.copy()
            img.paste(overlay, (0, 0), overlay)
            return img

        img = processing.apply_overlay(img, p.paste_to, batch_index, p.overlay_images)
        if p.color_corrections is not None and batch_index < len(p.color_corrections):
            img = processing.apply_color_correction(
                p.color_corrections[batch_index], img
            )
    return img

I changed the title of the issue to be a little less dramatic ;) Even if I recognize that the problem is painful. My hunch is that the resize part is not suitable. But we need to find a solution to do this better.

glucauze commented 1 year ago

It seems that resizing is not always necessary. You could try that :

def apply_mask(
    img: PILImage, p: processing.StableDiffusionProcessing, batch_index: int
) -> PILImage:
    """
    Apply mask overlay and color correction to an image if enabled

    Args:
        img: PIL Image objects.
        p : The processing object
        batch_index : the batch index

    Returns:
        PIL Image object
    """
    if isinstance(p, processing.StableDiffusionProcessingImg2Img):
        if p.inpaint_full_res:
            overlays = p.overlay_images
            if overlays is None or batch_index >= len(overlays):
                return img
            overlay: PILImage = overlays[batch_index]
            logger.debug("Overlay size %s, Image size %s", overlay.size, img.size)
            if overlay.size != img.size :
                overlay = overlay.resize((img.size), resample=Image.Resampling.LANCZOS)
            img = img.copy()
            img.paste(overlay, (0, 0), overlay)
            return img

        img = processing.apply_overlay(img, p.paste_to, batch_index, p.overlay_images)
        if p.color_corrections is not None and batch_index < len(p.color_corrections):
            img = processing.apply_color_correction(
                p.color_corrections[batch_index], img
            )
    return img

I wonder if resizing is necessary only when the image dimensions are not exactly multiples of 16. I'd had bugs when I didn't resize. That's why I did it without thinking too much about it. I don't use a lot of hd images.

glucauze commented 1 year ago

Based on my tests, version 1.2.0 should solve this problem.