Beispiel #1
0
def fft_watermark_embed(image, extra_inputs, parameters):
    """ Embeds a watermark image into a host image, using the Fast Fourier Transform

    Arguments:
        *image* (NumPy array) -- the image to be watermarked

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs for the call

            *Watermark* (NumPy array) -- the image to be embedded into the host image

        *parameters* (dictionary) -- a dictionary containing following keys:

            *alpha* (float) -- the embedding strength factor

    Returns:
        list of NumPy array float64 -- list containing the watermarked image (float, but converted to int when saved)
    """
    # Load the extra parameter, the watermark image
    watermark = extra_inputs['Watermark']

    # Parameters extraction
    alpha = parameters['alpha']

    # Resize the watermark image to the shape of the host image
    image_h, image_w = image.shape[:2]
    watermark_h, watermark_w = watermark.shape[:2]

    if watermark_h > image_h or watermark_w > image_w:  # Scale down the watermark image
        watermark = utils.resize_dimension(watermark, image_h, image_w)
    else:
        watermark = utils.resize_dimension(watermark, image_h, image_w)

    watermark_h, watermark_w = watermark.shape[:
                                               2]  # Recompute the watermark dimensions

    # Take the FFT of the host and watermark images and center them
    image_fft = np.fft.fftshift(np.fft.fft2(image))
    watermark_fft = np.fft.fftshift(np.fft.fft2(watermark))

    # Watermark the host image using the frequency domain
    result_fft = image_fft + alpha * watermark_fft

    # Take its Inverse FFT and convert the resulting values into floats
    result_image = np.fft.ifft2(
        np.fft.ifftshift(result_fft))  # dtype = 'complex128'
    result_image = np.real(result_image)

    return result_image
Beispiel #2
0
def cross_stitch(image, extra_inputs, parameters):
    """ Performs color quantization in the input image and generates a template used for cross-stitching.

    Arguments:
        *image* (NumPy array) -- the image to be pixelated

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs for the call (empty)

        *parameters* (dictionary) -- a dictionary containing following keys:

            *target_Height* (int) -- the maximum number of points that the cross-stitch will have on each column

            *target_Width* (int) -- the maximum number of points that the cross-stitch will have on each line

            *colours* (int, optional) -- the number of colours that the cross-stitch will contain; default value is 20

    Returns:
        list of NumPy array uint8 -- list containing the quantized image and the cross-stitch template
    """
    # Parameters extraction
    target_height = parameters['target_Height']
    target_width = parameters['target_Width']

    if 'colours' in parameters:
        colours = parameters['colours']
    else:
        colours = 20

    # Resize the input image, keeping its original aspect ratio
    height, width = image.shape[:2]
    resizing_constant = max(height / target_height, width / target_width)
    resized_height = int(height / resizing_constant)
    resized_width = int(width / resizing_constant)
    resized_image = utils.resize_dimension(image, resized_height,
                                           resized_width)

    # Quantize the resized image
    quantized_image = quantize(resized_image, {}, {'colours': colours})[0]

    # Generate the cross-stitch template and legent
    grid_image, legend_image = _generate_grid_and_legend(quantized_image)

    return [quantized_image, grid_image, legend_image]
Beispiel #3
0
def ascii_art(image, extra_inputs, parameters):
    """ Applies an **ASCII Art Filter** onto an image. \n

    Arguments:
        *image* (NumPy array) -- the image to be filtered

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs
        for the call (empty)

        *parameters* (dictionary) -- a dictionary containing following keys:

            *charset* (str, optional) -- the character set to use when
            rendering ASCII art image; possible values are *standard*,
            *alternate* and *full*; default value is *alternate*

    Returns:
        list of NumPy array uint8 -- list containing the filtered image
    """
    # Small, 11 character ramps
    STANDARD_CHARSET = [' ', '.', ',', ':', '-', '=', '+', '*', '#', '%', '@']
    ALTERNATE_CHARSET = [' ', '.', ',', ':', '-', '=', '+', '*', '%', '@', '#']

    # Full, 70 character ramp
    FULL_CHARSET = [
        ' ', '.', '\'', '`', '^', '"', ',', ':', ';', 'I', 'l', '!', 'i', '>',
        '<', '~', '+', '_', '-', '?', ']', '[', '}', '{', '1', ')', '(', '|',
        '\\', '/', 't', 'f', 'j', 'r', 'x', 'n', 'u', 'v', 'c', 'z', 'X', 'Y',
        'U', 'J', 'C', 'L', 'Q', '0', 'O', 'Z', 'm', 'w', 'q', 'p', 'd', 'b',
        'k', 'h', 'a', 'o', '*', '#', 'M', 'W', '&', '8', '%', 'B', '$', '@'
    ]

    if 'charset' in parameters:
        if parameters['charset'] == 'standard':
            CHARS = STANDARD_CHARSET
        elif parameters['charset'] == 'alternate':
            CHARS = ALTERNATE_CHARSET
        else:
            CHARS = FULL_CHARSET
    else:
        CHARS = ALTERNATE_CHARSET

    buckets = 256 / len(CHARS)
    CHARS = CHARS[::-1]  # Reverse the list

    def number_to_char(number):
        return CHARS[int(number // buckets)]

    # Vectorizing this function allows it to be applied on arrays
    number_to_char = np.vectorize(number_to_char)

    # Resize and convert the image to grayscale
    h, w = image.shape[:2]
    original_size = (w, h)
    image = utils.resize_dimension(image, new_width=80)
    if utils.is_color(image):
        image = grayscale(image)[0]

    # Build results as list of lines of text and entire text
    lines = [''.join(number_to_char(row)) for row in list(image)]
    text_spaceless = ''.join(lines)

    # Determine the widest letter, to account for the rectangular aspect ratio of the characters
    font_face = cv2.FONT_HERSHEY_PLAIN
    font_scale = 1
    thickness = 1
    size, base_line = cv2.getTextSize('.', font_face, font_scale, thickness)
    maximum_letter_width = size[0]

    for i in range(len(text_spaceless)):
        letter_width = cv2.getTextSize(text_spaceless[i], font_face,
                                       font_scale, thickness)[0][0]
        if letter_width > maximum_letter_width:
            maximum_letter_width = letter_width

    # Create resulting image as white and write text on it
    number_of_lines = len(lines)
    number_of_cols = len(lines[0]) * maximum_letter_width
    dy = 14  # Vertical offset to account for the characters height
    ascii_image = np.zeros((number_of_lines * dy, number_of_cols), np.uint8)
    ascii_image[:, :] = 255

    for i, line in enumerate(lines):
        y = i * dy
        for j, char in enumerate(line):
            cv2.putText(ascii_image,
                        char, (j * maximum_letter_width, y),
                        font_face,
                        1, (0, 0, 0),
                        1,
                        lineType=cv2.FILLED)

    # Resize resulting image to original size of input image
    ascii_image = cv2.resize(ascii_image,
                             original_size,
                             interpolation=cv2.INTER_AREA)

    return [ascii_image]
Beispiel #4
0
def build_mosaic(image, texture, technique, alpha, resolution, redundancy):
    """ Helper function which actually builds the mosaic """
    if utils.is_grayscale(image):
        image = utils.merge_channels([image, image, image])
    else:
        image = image[:, :, :3]

    # Load the appropriate image library information
    database_path = os.path.join(project_path, 'backend', 'miscellaneous',
                                 'database')
    pickle_name = texture + '_' + resolution + '.pickle'
    pickle_path = os.path.join(database_path, pickle_name)

    with open(pickle_path, 'rb') as p:
        tiles_height, tiles_width = pickle.load(p)
        tiles_averages_dict = pickle.load(p)

    # Initialise necessary variables
    tiles_averages = np.zeros((len(tiles_averages_dict), 3))
    tiles_averages_values = list(tiles_averages_dict.values())
    tiles_averages_keys = list(tiles_averages_dict.keys())
    for i in range(len(tiles_averages_dict)):
        tiles_averages[i] = np.array(tiles_averages_values[i])

    image_height, image_width = image.shape[:2]

    mosaic_lines = int(image_height / tiles_height)
    mosaic_columns = int(image_width / tiles_width)
    mosaic_image = np.zeros(
        (mosaic_lines * tiles_height, mosaic_columns * tiles_width, 3),
        dtype=np.uint8)
    tile_usage_grid = np.ones(
        (mosaic_lines, mosaic_columns), dtype=np.uint8) * -1

    # For each block:
    #    Compute the average r, g, b values of the block and put them in a vector
    #    Compute the distances between the avg vector of the block and each of the avg vectors of the tiles
    #    Replace the current block with the tile located at the shortest distance from the block
    block_averages = np.zeros((1, 3))
    for line in range(mosaic_lines):
        for column in range(mosaic_columns):
            block = image[line * tiles_height:(line + 1) * tiles_height,
                          column * tiles_width:(column + 1) * tiles_width]
            block_averages[0] = np.array(
                (np.mean(block[:, :, 0]), np.mean(block[:, :, 1]),
                 np.mean(block[:, :, 2])))
            distances = cdist(block_averages, tiles_averages)

            # Determine the index of the closest compatible tile
            if redundancy:
                closest_tile_index = np.where(
                    distances == np.min(distances))[1][0]
            else:
                closest_tile_index = get_closest_usable_tile_index(
                    distances, tile_usage_grid, line, column)

            closest_tile_name = tiles_averages_keys[closest_tile_index]
            closest_tile_path = os.path.join(database_path,
                                             texture + '_' + resolution,
                                             closest_tile_name)
            closest_tile = cv2.imread(closest_tile_path, cv2.IMREAD_UNCHANGED)
            mosaic_image[line * tiles_height:(line + 1) * tiles_height,
                         column * tiles_width:(column + 1) *
                         tiles_width] = closest_tile
            tile_usage_grid[line][column] = closest_tile_index

    if technique == 'alternative':
        # Overlay the mosaic on the input image
        image_copy = utils.resize_dimension(image, *mosaic_image.shape[:2])
        mosaic_image = image_copy * (1 - alpha) + mosaic_image * alpha

    return mosaic_image
def visible_watermark(image, extra_inputs, parameters):
    """ Embeds a watermark image over a host image, using the visible
    watermarking technique; the watermark is scaled to the selected size and is
    embedded into the selected location, with the selected transparency

    Arguments:
        *image* (NumPy array) -- the image to be watermarked

        *extra_inputs* (dictionary) -- a dictionary holding any extra inputs
        for the call

            *Watermark* (NumPy array) -- the image to be embedded on top of the
            host image

        *parameters* (dictionary) -- a dictionary containing following keys:

            *transparency* (str, optional) -- specifies how will the watermark
            image be overlayed over the host image; possible values are:
            *opaque*, *transparent* and *very transparent*; default value is
            *transparent*

            *location* (str, optional) -- specifies the location where the
            watermark image will be embedded; possible values are: *bottom
            right*, *bottom left*, *top right*, *top left*, *center*,
            *everywhere*; default value is *bottom right*

            *size* (int, optional) -- specifies the maximum width of the
            watermark image, as a percentage of the width of the host image;
            possible values are: 10 to 90, with increments of 10; default value
            is 20 (%)

    Returns:
        list of NumPy array uint8 -- list containing the watermarked image

    Observations:
        If the watermark image's width is already smaller than the maximum
        watermark width computed from *size*, the watermark image will be left
        unchanged. If the height of the watermark image (after width adjustment)
        is greater than the height of the host image, the watermark image will
        be rescaled as to fit the host image
    """
    # Load the extra parameter, the watermark image
    watermark = extra_inputs['Watermark']

    # Parameters extraction
    if 'transparency' in parameters:
        mode = parameters['transparency']
    else:
        mode = 'transparent'

    if 'location' in parameters:
        location = parameters['location']
    else:
        location = 'bottom right'

    if 'size' in parameters:
        size = parameters['size']
    else:
        size = 20

    # Check if the watermark image needs rescaling
    image_h, image_w = image.shape[:2]
    watermark_h, watermark_w = watermark.shape[:2]
    maximum_watermark_width = int(size / 100 * image_w)

    if watermark_w > maximum_watermark_width:
        # Resize watermark image by new width
        watermark = utils.resize_dimension(watermark,
                                           new_width=maximum_watermark_width)
        # The watermark dimensions need to be updated
        watermark_h, watermark_w = watermark.shape[:2]

    if watermark_h > image_h:
        # Resize watermark image by height of host image
        watermark = utils.resize_dimension(watermark, new_height=image_h)

    # If any of the images are grayscale, convert them to the color equivalent
    if utils.is_grayscale(image):
        image = utils.merge_channels([image, image, image])

    if utils.is_grayscale(watermark):
        watermark = utils.merge_channels([watermark, watermark, watermark])

    # Remove the alpha channel from both images (if present)
    if image.shape[2] == 4:
        image = image[:, :, :3]

    if watermark.shape[2] == 4:
        watermark = watermark[:, :, :3]

    # Apply the watermark over the host image; alpha blending technique is used
    # result = background * (1 - alpha) + foreground * alpha
    watermarked_image = image.copy()

    # Compute the alpha level needed for alpha blending
    if mode == 'opaque':
        alpha = 1
    elif mode == 'transparent':
        alpha = 170 / 255
    else:
        alpha = 85 / 255

    # Compute the region of interest, based on the location specified by user
    if location == 'top left':
        line_start = 10
        line_end = watermark_h + 10
        column_start = 10
        column_end = watermark_w + 10
    elif location == 'top right':
        line_start = 10
        line_end = watermark_h + 10
        column_start = image_w - watermark_w - 10
        column_end = image_w - 10
    elif location == 'bottom left':
        line_start = image_h - watermark_h - 10
        line_end = image_h - 10
        column_start = 10
        column_end = watermark_w + 10
    elif location == 'bottom right':
        line_start = image_h - watermark_h - 10
        line_end = image_h - 10
        column_start = image_w - watermark_w - 10
        column_end = image_w - 10
    elif location == 'center':
        line_start = int(image_h / 2 - watermark_h / 2)
        line_end = int(image_h / 2 + watermark_h / 2)
        column_start = int(image_w / 2 - watermark_w / 2)
        column_end = int(image_w / 2 + watermark_w / 2)
    elif location == 'everywhere':
        # Compute number of horizontal and vertical repeats, respectively
        repeat_x = image_w // watermark_w
        repeat_y = image_h // watermark_h

        for x in range(repeat_x):
            for y in range(repeat_y):
                line_start = watermark_h * y
                line_end = watermark_h * (y + 1)
                column_start = watermark_w * x
                column_end = watermark_w * (x + 1)

                watermarked_image[line_start: line_end, column_start: column_end, : 3] = \
                    image[line_start: line_end, column_start: column_end, : 3] * (1 - alpha) + \
                    watermark[:, :, : 3] * alpha

        return [watermarked_image]
    else:
        raise ValueError("'location' parameter value not allowed")

    # Overlay the watermark on the host image
    watermarked_image[line_start: line_end, column_start: column_end, : 3] = \
        image[line_start: line_end, column_start: column_end, : 3] * (1 - alpha) + \
        watermark[:, :, : 3] * alpha

    return [watermarked_image]