def generate_matted_image(original_image, selected_background_hsvs, tolerance,
                          new_background_image):
    duplicate_image = original_image.copy()
    trimap = np.ones(original_image.shape[0:2]) * 127
    hsv_frame = cv2.cvtColor(original_image, cv2.COLOR_BGR2HSV)
    i_h, i_s, i_v = cv2.split(hsv_frame)
    hb = np.zeros((frame.shape[0], frame.shape[1]))
    for selected_background_hsv in selected_background_hsvs:
        hb = np.logical_or(
            hb,
            np.where((i_h <= selected_background_hsv[0] + tolerance) &
                     (i_h >= selected_background_hsv[0] - tolerance), 1, 0))
    sb = np.where(i_s >= 120, 1, 0)
    background_mask = np.logical_and(hb, sb).astype(np.uint8)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    background_mask = cv2.morphologyEx(background_mask, cv2.MORPH_CLOSE,
                                       kernel)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (21, 21))
    background_mask = cv2.morphologyEx(background_mask,
                                       cv2.MORPH_OPEN,
                                       kernel,
                                       iterations=4)

    foreground_mask = np.where(background_mask == 1, 0, 1).astype(np.uint8)
    foreground_mask = cv2.morphologyEx(foreground_mask,
                                       cv2.MORPH_OPEN,
                                       kernel,
                                       iterations=4)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    foreground_mask = cv2.erode(foreground_mask, kernel, iterations=8)
    background_mask = cv2.erode(background_mask, kernel, iterations=12)
    trimap = np.where(background_mask, 0, trimap)
    trimap = np.where(foreground_mask, 255, trimap)
    trimap = trimap.astype(np.uint8)
    # TODO add  pixels with the most common background colors to background
    background_colors, counts, = np.unique(frame[tuple(
        background_mask.reshape(1, background_mask.shape[0],
                                background_mask.shape[1]) == 1)],
                                           axis=0,
                                           return_counts=True)
    background_colors = background_colors[tuple(counts.reshape(1, -1) > 10000)]
    # counts = counts[tuple(counts.reshape(1, -1) > 10000)]
    # print(background_colors)
    # print(counts)
    # print(background_colors.shape)
    # print(counts.shape)
    match = np.array([
        np.in1d(frame[:, :, 0], background_colors[:, 0]),
        np.in1d(frame[:, :, 1], background_colors[:, 1]),
        np.in1d(frame[:, :, 2], background_colors[:, 2])
    ])
    mask = np.logical_and(np.logical_and(match[0, :], match[1, :]),
                          match[2, :])
    mask = mask.reshape((frame.shape[0], frame.shape[1]))
    background_mask = np.logical_or(background_mask, mask)
    trimap = np.where(mask, 0, trimap)

    alpha_mask = trimap.copy()

    ys, xs = np.where(trimap == 127)
    progress_bar = ProgressBar(len(ys), 20)
    progress = 0
    h = trimap.shape[0]
    w = trimap.shape[1]
    for i in range(0, len(ys)):
        x = xs[i]
        y = ys[i]
        progress_bar.update_progress_bar(progress)
        progress = progress + 1
        start_y = 0 if y - 60 < 0 else y - 60
        end_y = h if y + 60 >= h else y + 60
        start_x = 0 if x - 60 < 0 else x - 60
        end_x = w if x + 60 >= w else x + 60
        local_w = end_x - start_x
        local_h = end_y - start_y
        neighborhood = original_image[start_y:end_y, start_x:end_x]
        local_foreground_mask = foreground_mask[start_y:end_y, start_x:end_x]
        local_foreground_mask = local_foreground_mask.reshape(
            (1, local_h, local_w))
        mean_foreground = np.mean(
            neighborhood[tuple(local_foreground_mask == 1)].reshape(-1, 3),
            axis=0).astype(np.uint8)
        local_background_mask = background_mask[start_y:end_y, start_x:end_x]
        local_background_mask = local_background_mask.reshape(
            (1, local_h, local_w))
        mean_background = np.mean(
            neighborhood[tuple(local_background_mask == 1)].reshape(-1, 3),
            axis=0).astype(np.uint8)

        # Try minimum euclidean distance between the two arrays (fg and bg)
        # foregroundColors = frame[start_y:end_y, start_x:end_x][tuple(local_foreground_mask == 1)].reshape(-1, 3)
        local_background_colors = neighborhood[tuple(
            local_background_mask == 1)].reshape(-1, 3)
        if has_similar_bgr(original_image[y, x].reshape(1, 3),
                           local_background_colors):
            # print("continue")
            alpha_mask[y, x] = np.uint8(0)
            continue

        dist_mean = dist_rgb(mean_foreground, mean_background)

        if dist_mean == 0:
            dist_mean = 0.1

        dist_x = dist_rgb(original_image[y, x], mean_background) / dist_mean

        # 0.16 is the solution of solve(1/(1+exp(-(0-0.5)/s))<0.05)
        sigma = 0.16 * softness_level / 100
        alpha = logistic_cdf(dist_x, 0.5, sigma)
        if 0.99 > alpha > 0.05:
            # foregroundNeighborhood = highlyLikelyForeground[start_y:end_y, start_x:end_x]

            duplicate_image[y, x] = min_dist_color(neighborhood,
                                                   local_foreground_mask,
                                                   original_image[y, x])

        alpha = alpha * 255
        if alpha < 0:
            alpha = 0
        elif alpha > 255:
            alpha = 255
        alpha = round(alpha)
        alpha = np.uint8(alpha)
        # print(alpha)

        alpha_mask[y, x] = alpha

    # highlyLikelyAlphaMask = np.where(alpha_mask == 255, 1, 0)
    alpha_mask = alpha_mask.astype(np.uint8)
    alpha_mask3d = cv2.merge(
        (alpha_mask, alpha_mask, alpha_mask)).astype(np.float)
    # print(alpha_mask)
    cv2.namedWindow("trimap")
    cv2.imshow("trimap", trimap)

    # pivot points for X-Coordinates
    original_value = np.array([0, 50, 100, 150, 200, 255])

    full_range = np.arange(0, 256)
    color_cast_percentage = color_cast_level / 100
    g_curve = np.array([
        0, 50 - color_cast_percentage * 30, 100 - color_cast_percentage * 50,
        150 - color_cast_percentage * 40, 200 - color_cast_percentage * 20, 255
    ])
    g_lut = np.interp(full_range, original_value, g_curve)
    g_channel = duplicate_image[:, :, 1]
    g_channel = cv2.LUT(g_channel, g_lut)
    duplicate_image[:, :, 1] = g_channel

    result = cv2.add(
        cv2.multiply(new_background_image.astype(np.float),
                     (1 - alpha_mask3d / 255)),
        cv2.multiply(duplicate_image.astype(np.float), alpha_mask3d / 255))
    bgra = cv2.cvtColor(duplicate_image, cv2.COLOR_BGR2BGRA)
    bgra[:, :, 3] = alpha_mask
    return result