nadermx / backgroundremover

Background Remover lets you Remove Background from images and video using AI with a simple command line interface that is free and open source.
https://www.backgroundremoverai.com
MIT License
6.43k stars 534 forks source link

Automating background removal for Images using Python libraries, complete guide, api. #99

Open antichristHater opened 9 months ago

antichristHater commented 9 months ago

NOTE: I didn't want to deal with automating videos as I didn't need them. But you can easily create your own python tool by copying the style of site-packages/backgroundremover/cmd/cli.py for your own usage.

Install those dependincies: `1. A Python version >=3.6 and add it to your path.

  1. pytorch(torch, torchvision, torchaudio) from https://pytorch.org/
  2. ffmpeg 4.4+ and add it to your path.
  3. backgroundremover package: pip install backgroundremover`

Follow these steps: `1. Create a new folder for your project.

  1. Inside that project folder, create two more folders named 'images' and 'outputs'. Images folder will contain Images you want to remove backgrounds from.
  2. Create a python file and copy+paste my command at the bottom.
  3. Run the python file(the first execution of your python file will take couple minutes, it will download necessary modules for background detection. But again, you'll need to install them once.), it will start removing background(the first image will take extra 10~ seconds on every runtime because it'll load modules/model) for every image in 'images' folder and add them _BG_REMOVED suffix then save to 'outputs' folder.
  4. If you need to import this to some project else for manual use, "from backgroundapi import remove_image_background, remove_background_from_every_image_in_folder" the first function will take an input path and an output path and run the api for a single image; the second function will take an input folder and an output folder and bulk remove backgrounds for every image that has a image extension and save them to outputs folder, if no output folders provided it'll generate in the provided image folder.`

This process took 0.8 seconds for each image for me on a 900M series NVIDIA GPU.

Here's the Python code:


import os
from time import perf_counter
from datetime import timedelta

def remove_image_background(input_path, output_path, debugger=False):
    ''' NOTE: Models and modules will take some extra time to load during your first execution.\n
        NOTE: If you want to play around with the default parameters, check https://github.com/nadermx/backgroundremover see what they mean.\n
        Set 'debugger=True' to see execution debugs.\n
    '''

    # Set default parameters for background remover function.
    default_parameters = {
    'model': 'u2net',
    'alpha_matting': False,
    'alpha_matting_foreground_threshold': 240,
    'alpha_matting_background_threshold': 10,
    'alpha_matting_erode_size': 10,
    'alpha_matting_base_size': 1000,
    }

    # Convert paths to absolute paths.
    input_path, output_path = os.path.abspath(input_path), os.path.abspath(output_path)

    # Check if paths exist.
    if not os.path.exists(input_path):
        raise OSError(f'Path {input_path} does not exist.')
    if not os.path.exists(os.path.dirname(output_path)):
        raise OSError(f'Path {output_path} does not exist.')

    # Load modules if running for the first time.
    if 'modules_imported' not in globals():
        global modules_imported, remove, utilities
        if debugger: print("Running app for the first time, importing modules.")
        from backgroundremover import utilities
        from backgroundremover.bg import remove
        modules_imported = ''

    # Open input image.
    with open(input_path, 'rb') as file:
        image = file.read()

    # Load model for the first time.
    if 'model_loaded' not in globals():
        global model_loaded
        if debugger: print('Loading model...')
        model_loaded = ''

    # Remove background from the image and return it as a bytes object.
    t1 = perf_counter()
    img_removed_bg_bytes = remove(image, model_name=default_parameters['model'],alpha_matting=default_parameters['alpha_matting'], alpha_matting_foreground_threshold=default_parameters['alpha_matting_foreground_threshold'],alpha_matting_background_threshold=default_parameters['alpha_matting_background_threshold'],alpha_matting_erode_structure_size=default_parameters['alpha_matting_erode_size'],alpha_matting_base_size=default_parameters['alpha_matting_base_size'])
    if debugger: print(f"{output_path} done. Took {(perf_counter()-t1):003} seconds.")

    # Write bytes object to your output path.
    with open(output_path, 'wb') as file:
        file.write(img_removed_bg_bytes)

def remove_background_from_every_image_in_folder(images_folder = 'images', output_folder = None, output_suffix = '_BG_REMOVED', debugger:bool=True):
    '''BE CAREFUL! If images in your images_folder contains the set output_suffix, they won't have their backgrounds removed.
    '''
    # Failed images
    failed = []

    # Get absolute path
    images_folder = os.path.abspath(images_folder)
    if not os.path.exists(images_folder): os.makedirs(images_folder)

    if not output_folder: output_folder = images_folder
    else:
        if not os.path.exists(os.path.abspath(output_folder)): os.makedirs(os.path.abspath(output_folder))

    # Declare accepted image extensions and add a dot in front of every element.
    image_formats = list(map(lambda x: '.'+x, ["JPEG", "JPG", "PNG", "GIF", "BMP", "TIFF", "TIF","WEBP", "ICO", "EPS", "SVG", "PDF", "RAW", "PCX","XBM", "XPM", "JP2", "EXR", "HDR", "TGA", "PGM","PPM", "PBM", "PAM", "SR", "RAS", "JPE", "JFIF","JIF", "JPE", "JFI", "HEIF", "AVIF", "BPG", "BPGU","FLIF", "JPEG2000", "DJVU", "PGF", "WMF", "EMF","ICNS", "ANI", "MNG", "APNG", "ICO", "CUR","DDS", "PVR", "KTX", "ASTC", "S3TC", "BMP", "ICO","CUR", "WBMP", "XWD", "ART", "PCT", "PLT", "PBM","XPM", "RGB", "RGBA", "BGR", "BGRA", "CMYK", "YCbCr","YUV", "HSV", "LAB", "XYZ", "YCC", "CIE", "LCH","GRAY", "HSL", "HSB", "IPTC", "ICC", "EXIF",]))
    # Extend the list for both upper and lower cases.
    image_formats = list(map(lambda x: x.lower(), image_formats)) + image_formats

    # Get images' paths if their extensions end with an image extension.
    images_paths = [os.path.abspath(os.path.join(images_folder, image)) for image in os.listdir(images_folder) if os.path.splitext(image)[-1] in image_formats and output_suffix not in os.path.splitext(image)[0]]

    # Run script for every image in the set folder.
    for counter, image_path in enumerate(images_paths):
        # Get the file name of an image_path, Split by image name and extension.
        image_name, extension = os.path.splitext(os.path.basename(image_path))

        # Add suffix to base image name
        output_image_name = image_name + output_suffix + '.png'

        # Get directory of the image path, get absolute path of that directory, then join output_image_name to that.
        output_path = os.path.join(os.path.abspath(output_folder), output_image_name)

        # Start a timer for estimation.
        t1 = perf_counter()
        # Run removing function and handle exceptions.
        try:
            remove_image_background(image_path, output_path, debugger=debugger)
        # Exit if KeyboardInterrupt(ctrl+c).
        except Exception as e:
            if e != KeyboardInterrupt:
                print(f"Image content is corrupted or unreadible by this library for: {image_path}")
                failed.append(image_path)
                continue
            else: exit()

        # Print time it took if debugger=True
        estimated_time = (perf_counter()-t1) * (len(images_paths) - (counter+1))
        if debugger: print(f"{counter+1}/{len(images_paths)} OK | ETA: {timedelta(seconds = estimated_time)}", end='\r')

    # Done.
    if failed:
        print(f"\nALL FILES ARE DONE.\n")
        print(f"\nFailed to process following file(s): {failed}                           ")
        input('Enter to exit.')

if __name__ == '__main__': 
    remove_background_from_every_image_in_folder(images_folder='images', output_folder='outputs', output_suffix='_BG_REMOVED', debugger=True)
nadermx commented 7 months ago

Intresting, I feel there is just a simple way to feed this into the cmd.py file so it could be part of the command line

stevens-Ai commented 3 months ago

works great. Thanks

nadermx commented 3 months ago

Damnit, now I might have to find motivation to implement this. Thanks

On Wed, Mar 27, 2024, 2:39 PM stevens-Ai @.***> wrote:

works great. Thanks

— Reply to this email directly, view it on GitHub https://github.com/nadermx/backgroundremover/issues/99#issuecomment-2023944682, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABYACXFAQPXMOLFNOJAPJ5TY2MOAFAVCNFSM6AAAAAA5CMQI5WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRTHE2DINRYGI . You are receiving this because you commented.Message ID: @.***>